/*++ Copyright (c) 1996 Microsoft Corporation Module Name: wspmisc.c Abstract: This module contains miscellaneous routines for the Winsock 2 to Winsock 1.1 Mapper Service Provider. The following routines are exported by this module: WSPCancelBlockingCall() WSPDuplicateSocket() WSPGetOverlappedResult() WSPGetQOSByName() WSPJoinLeaf() Author: Keith Moore (keithmo) 29-May-1996 Revision History: --*/ #include "precomp.h" #pragma hdrstop INT WSPAPI WSPCancelBlockingCall( OUT LPINT lpErrno ) /*++ Routine Description: This routine cancels any outstanding blocking operation for this thread. It is normally used in two situations: 1. A WinSock SPI client is processing a message which has been received while a service provider is implementing pseudo blocking. In this case, WSAIsBlocking() will be true. 2. A blocking call is in progress, and the WinSock service provider has called back to the WinSock SPI client's "blocking hook" function (via the callback function retrieved from WPUQueryBlockingCallback()), which in turn is invoking this function. Such a situation might arise, for instance, in implementing a Cancel option for an operation which require an extended time to complete. In each case, the original blocking call will terminate as soon as possible with the error WSAEINTR. (In (1), the termination will not take place until Windows message scheduling has caused control to revert back to the pseudo blocking routine in WinSock. In (2), the blocking call will be terminated as soon as the blocking hook function completes.) In the case of a blocking WSPConnect() operation, WinSock will terminate the blocking call as soon as possible, but it may not be possible for the socket resources to be released until the connection has completed (and then been reset) or timed out. This is likely to be noticeable only if the WinSock SPI client immediately tries to open a new socket (if no sockets are available), or to WSPConnect() to the same peer. Canceling an WSPAccept() or a WSPSelect() call does not adversely impact the sockets passed to these calls. Only the particular call fails; any operation that was legal before the cancel is legal after the cancel, and the state of the socket is not affected in any way. Canceling any operation other than WSPAccept() and WSPSelect() can leave the socket in an indeterminate state. If a WinSock SPI client cancels a blocking operation on a socket, the only operation that the WinSock SPI client can depend on being able to perform on the socket is a call to WSPCloseSocket(), although other operations may work on some WinSock service providers. If a WinSock SPI client desires maximum portability, it must be careful not to depend on performing operations after a cancel. A WinSock SPI client may reset the connection by setting the timeout on SO_LINGER to 0 and calling WSPCloseSocket(). If a cancel operation compromised the integrity of a SOCK_STREAM's data stream in any way, the WinSock provider will reset the connection and fail all future operations other than WSPCloseSocket() with WSAECONNABORTED. Note it is acceptable for WSPCancelBlockingCall() to return successfully if the blocking network operation completes prior to being canceled. In this case, the blocking operation will return successfully as if WSPCancelBlockingCall() had never been called. The only way for the WinSock SPI client to know with certainty that an operation was actually canceled is to check for a return code of WSAEINTR from the blocking call. Arguments: lpErrno - A pointer to the error code. Return Value: The value returned by WSPCancelBlockingCall() is 0 if the operation was successfully canceled. Otherwise the value SOCKET_ERROR is returned, and a specific error code is available in lpErrno. --*/ { INT err; INT result; PSOCK_TLS_DATA tlsData; SOCK_ENTER( "WSPCancelBlockingCall", lpErrno, NULL, NULL, NULL ); SOCK_ASSERT( lpErrno != NULL ); err = SockEnterApi( TRUE, TRUE ); if( err != NO_ERROR ) { SOCK_EXIT( "WSPCancelBlockingCall", SOCKET_ERROR, TRUE ); *lpErrno = err; return SOCKET_ERROR; } tlsData = SOCK_GET_THREAD_DATA(); SOCK_ASSERT( tlsData != NULL ); // // This call is only valid when we are in a blocking call. // if( !tlsData->IsBlocking ) { SOCK_EXIT( "WSPCancelBlockingCall", SOCKET_ERROR, TRUE ); *lpErrno = WSAEINVAL; return SOCKET_ERROR; } // // The IO should not have been cancelled yet. // SOCK_ASSERT( tlsData->ReentrancyFlag ); SOCK_ASSERT( !tlsData->IoCancelled ); SOCK_ASSERT( tlsData->BlockingSocketInfo != NULL ); // // Cancel it. // result = tlsData->BlockingSocketInfo->Hooker->WSACancelBlockingCall(); if( result == SOCKET_ERROR ) { *lpErrno = tlsData->BlockingSocketInfo->Hooker->WSAGetLastError(); SOCK_EXIT( "WSPCancelBlockingCall", SOCKET_ERROR, TRUE ); return SOCKET_ERROR; } // // Remember that we've cancelled it. // tlsData->IoCancelled = TRUE; SOCK_EXIT( "WSPCancelBlockingCall", NO_ERROR, FALSE ); return NO_ERROR; } // WSPCancelBlockingCall INT WSPAPI WSPDuplicateSocket( IN SOCKET s, IN DWORD dwProcessId, OUT LPWSAPROTOCOL_INFOW lpProtocolInfo, OUT LPINT lpErrno ) /*++ Routine Description: A source process calls WSPDuplicateSocket() to obtain a special WSAPROTOCOL_INFOW structure. It uses some interprocess communications (IPC) mechanism to pass the contents of this structure to a target process, which in turn uses it in a call to WSPSocket() to obtain a descriptor for the duplicated socket. Note that the special WSAPROTOCOL_INFOW structure may only be used once by the target process. It is the service provider's responsibility to perform whatever operations are needed in the source process context and to create a WSAPROTOCOL_INFOW structure that will be recognized when it subsequently appears as a parameter to WSPSocket() in the target processes' context. The provider must then return a socket descriptor that references a common underlying socket. The dwProviderReserved field of the WSAPROTOCOL_INFOW struct is available for the service provider's use, and may be used to store any useful context information, including a duplicated handle. When new socket descriptors are allocated IFS providers must call WPUModifyIFSHandle() and non-IFS providers must call WPUCreateSocketHandle(). The descriptors that reference a shared socket may be used independently as far as I/O is concerned. However, the WinSock interface does not implement any type of access control, so it is up to the processes involved to coordinate their operations on a shared socket. A typical use for shared sockets is to have one process that is responsible for creating sockets and establishing connections, hand off sockets to other processes which are responsible for information exchange. Since what is duplicated are the socket descriptors and not the underlying socket, all of the state associated with a socket is held in common across all the descriptors. For example a WSPSetSockOpt() operation performed using one descriptor is subsequently visible using a WSPGetSockOpt() from any or all descriptors. A process may call WSPClosesocket() on a duplicated socket and the descriptor will become deallocated. The underlying socket, however, will remain open until WSPClosesocket() is called by the last remaining descriptor. Notification on shared sockets is subject to the usual constraints of WSPAsyncSelect() and WSPEventSelect(). Issuing either of these calls using any of the shared descriptors cancels any previous event registration for the socket, regardless of which descriptor was used to make that registration. Thus, for example, a shared socket cannot deliver FD_READ events to process A and FD_WRITE events to process B. For situations when such tight coordination is required, it is suggested that developers use threads instead of separate processes. Arguments: s - Specifies the local socket descriptor. dwProcessId - Specifies the ID of the target process for which the shared socket will be used. lpProtocolInfo - A pointer to a buffer allocated by the client that is large enough to contain a WSAPROTOCOL_INFOW struct. The service provider copies the protocol info struct contents to this buffer. lpErrno - A pointer to the error code. Return Value: If no error occurs, WSPDuplicateSocket() returns zero. Otherwise, the value of SOCKET_ERROR is returned, and a specific error number is available in lpErrno. --*/ { *lpErrno = WSAEINVAL; return SOCKET_ERROR; } // WSPDuplicateSocket BOOL WSPAPI WSPGetOverlappedResult( IN SOCKET s, IN LPWSAOVERLAPPED lpOverlapped, OUT LPDWORD lpcbTransfer, IN BOOL fWait, OUT LPDWORD lpdwFlags, OUT LPINT lpErrno ) /*++ Routine Description: The results reported by the WSPGetOverlappedResult() function are those of the specified socket's last overlapped operation to which the specified WSAOVERLAPPED structure was provided, and for which the operation's results were pending. A pending operation is indicated when the function that started the operation returns FALSE, and the lpErrno is WSA_IO_PENDING. When an I/O operation is pending, the function that started the operation resets the hEvent member of the WSAOVERLAPPED structure to the nonsignaled state. Then when the pending operation has been completed, the system sets the event object to the signaled state. If the fWait parameter is TRUE, WSPGetOverlappedResult() determines whether the pending operation has been completed by blocking and waiting for the event object to be in the signaled state. Arguments: s - Identifies the socket. This is the same socket that was specified when the overlapped operation was started by a call to WSPRecv(), WSPRecvFrom(), WSPSend(), WSPSendTo(), or WSPIoctl(). lpOverlapped - Points to a WSAOVERLAPPED structure that was specified when the overlapped operation was started. lpcbTransfer - Points to a 32-bit variable that receives the number of bytes that were actually transferred by a send or receive operation, or by WSPIoctl(). fWait - Specifies whether the function should wait for the pending overlapped operation to complete. If TRUE, the function does not return until the operation has been completed. If FALSE and the operation is still pending, the function returns FALSE and lpErrno is WSA_IO_INCOMPLETE. lpdwFlags - Points to a 32-bit variable that will receive one or more flags that supplement the completion status. If the overlapped operation was initiated via WSPRecv() or WSPRecvFrom(), this parameter will contain the results value for lpFlags parameter. lpErrno - A pointer to the error code. Return Value: If WSPGetOverlappedResult() succeeds, the return value is TRUE. This means that the overlapped operation has completed successfully and that the value pointed to by lpcbTransfer has been updated. If WSPGetOverlappedResult() returns FALSE, this means that either the overlapped operation has not completed or the overlapped operation completed but with errors, or that completion status could not be determined due to errors in one or more parameters to WSPGetOverlappedResult(). On failure, the value pointed to by lpcbTransfer will not be updated. lpErrno indicates the cause of the failure (either of WSPGetOverlappedResult() or of the associated overlapped operation). --*/ { *lpErrno = WSAEINVAL; return FALSE; } // WSPGetOverlappedResult BOOL WSPAPI WSPGetQOSByName( IN SOCKET s, IN LPWSABUF lpQOSName, OUT LPQOS lpQOS, OUT LPINT lpErrno ) /*++ Routine Description: Clients may use this routine to initialize a QOS structure to a set of known values appropriate for a particular service class or media type. These values are stored in a template which is referenced by a well-known name Arguments: s - A descriptor identifying a socket. lpQOSName - Specifies the QOS template name. lpQOS - A pointer to the QOS structure to be filled. lpErrno - A pointer to the error code. Return Value: If no error occurs, WSPGetQOSByName() returns TRUE. Otherwise, a value of FALSE is returned, and a specific error code is available in lpErrno. --*/ { *lpErrno = WSAEINVAL; return FALSE; } // WSPGetQOSByName SOCKET WSPAPI WSPJoinLeaf( IN SOCKET s, IN const struct sockaddr FAR * name, IN int namelen, IN LPWSABUF lpCallerData, OUT LPWSABUF lpCalleeData, IN LPQOS lpSQOS, IN LPQOS lpGQOS, IN DWORD dwFlags, OUT LPINT lpErrno ) /*++ Routine Description: This routine is used to join a leaf node to a multipoint session, and to perform a number of other ancillary operations that occur at session join time as well. If the socket, s, is unbound, unique values are assigned to the local association by the system, and the socket is marked as bound. WSPJoinLeaf() has the same parameters and semantics as WSPConnect() except that it returns a socket descriptor (as in WSPAccept()), and it has an additional dwFlags parameter. Only multipoint sockets created using WSPSocket() with appropriate multipoint flags set may be used for input parameter s in this routine. If the socket is in the non-blocking mode, the returned socket descriptor will not be useable until after a corresponding FD_CONNECT indication has been received, except that closesocket() may be invoked on this socket descriptor to cancel a pending join operation. A root node in a multipoint session may call WSPJoinLeaf() one or more times in order to add a number of leaf nodes, however at most one multipoint connection request may be outstanding at a time. Refer to section 3.14. Protocol-Independent Multicast and Multipoint for additional information. The socket descriptor returned by WSPJoinLeaf() is different depending on whether the input socket descriptor, s, is a c_root or a c_leaf. When used with a c_root socket, the name parameter designates a particular leaf node to be added and the returned socket descriptor is a c_leaf socket corresponding to the newly added leaf node. The newly created socket has the same properties as s including asynchronous events registered with WSPAsyncSelect() or with WSPEventSelect(), but not including the c_root socket's group ID, if any. It is not intended to be used for exchange of multipoint data, but rather is used to receive network event indications (e.g. FD_CLOSE) for the connection that exists to the particular c_leaf. Some multipoint implementations may also allow this socket to be used for "side chats" between the root and an individual leaf node. An FD_CLOSE indication will be received for this socket if the corresponding leaf node calls WSPCloseSocket() to drop out of the multipoint session. Symmetrically, invoking WSPCloseSocket() on the c_leaf socket returned from WSPJoinLeaf() will cause the socket in the corresponding leaf node to get FD_CLOSE notification. When WSPJoinLeaf() is invoked with a c_leaf socket, the name parameter contains the address of the root node (for a rooted control scheme) or an existing multipoint session (non-rooted control scheme), and the returned socket descriptor is the same as the input socket descriptor. In other words, a new socket descriptor is not allocated. In a rooted control scheme, the root application would put its c_root socket in the listening mode by calling WSPListen(). The standard FD_ACCEPT notification will be delivered when the leaf node requests to join itself to the multipoint session. The root application uses the usual WSPAccept() functions to admit the new leaf node. The value returned from WSPAccept() is also a c_leaf socket descriptor just like those returned from WSPJoinLeaf(). To accommodate multipoint schemes that allow both root-initiated and leaf- initiated joins, it is acceptable for a c_root socket that is already in listening mode to be used as an input to WSPJoinLeaf(). The WinSock SPI client is responsible for allocating any memory space pointed to directly or indirectly by any of the parameters it specifies. The lpCallerData is a value parameter which contains any user data that is to be sent along with the multipoint session join request. If lpCallerData is NULL, no user data will be passed to the peer. The lpCalleeData is a result parameter which will contain any user data passed back from the peer as part of the multipoint session establishment. lpCalleeData->len initially contains the length of the buffer allocated by the WinSock SPI client and pointed to by lpCalleeData->buf. lpCalleeData->len will be set to 0 if no user data has been passed back. The lpCalleeData information will be valid when the multipoint join operation is complete. For blocking sockets, this will be when the WSPJoinLeaf() function returns. For non- blocking sockets, this will be after the FD_CONNECT notification has occurred. If lpCalleeData is NULL, no user data will be passed back. The exact format of the user data is specific to the address family to which the socket belongs and/or the applications involved. At multipoint session establishment time, a WinSock SPI client may use the lpSQOS and/or lpGQOS parameters to override any previous QOS specification made for the socket via WSPIoctl() with either the SIO_SET_QOS or SIO_SET_GROUP_QOS opcodes. lpSQOS specifies the flow specs for socket s, one for each direction, followed by any additional provider-specific parameters. If either the associated transport provider in general or the specific type of socket in particular cannot honor the QOS request, an error will be returned as indicated below. The sending or receiving flow spec values will be ignored, respectively, for any unidirectional sockets. If no provider-specific parameters are supplied, the buf and len fields of lpSQOS->ProviderSpecific should be set to NULL and 0, respectively. A NULL value for lpSQOS indicates no application supplied QOS. lpGQOS specifies the flow specs for the socket group (if applicable), one for each direction, followed by any additional provider-specific parameters. If no provider- specific parameters are supplied, the buf and len fields of lpSQOS->ProviderSpecific should be set to NULL and 0, respectively. A NULL value for lpGQOS indicates no application-supplied group QOS. This parameter will be ignored if s is not the creator of the socket group. The dwFlags parameter is used to indicate whether the socket will be acting only as a sender (JL_SENDER_ONLY), only as a receiver (JL_RECEIVER_ONLY), or both (JL_BOTH). When connected sockets break (i.e. become closed for whatever reason), they should be discarded and recreated. It is safest to assume that when things go awry for any reason on a connected socket, the WinSock SPI client must discard and recreate the needed sockets in order to return to a stable point. Arguments: s - A descriptor identifying a multipoint socket. name - The name of the peer to which the socket is to be joined. namelen - The length of the name. lpCallerData - A pointer to the user data that is to be transferred to the peer during multipoint session establishment. lpCalleeData - A pointer to the user data that is to be transferred back from the peer during multipoint session establishment. lpSQOS - A pointer to the flow specs for socket s, one for each direction. lpGQOS - A pointer to the flow specs for the socket group (if applicable). dwFlags - Flags to indicate that the socket is acting as a sender, receiver, or both. lpErrno - A pointer to the error code. Return Value: If no error occurs, WSPJoinLeaf() returns a value of type SOCKET which is a descriptor for the newly created multipoint socket. Otherwise, a value of INVALID_SOCKET is returned, and a specific error code is available in lpErrno. On a blocking socket, the return value indicates success or failure of the join operation. With a non-blocking socket, successful initiation of a join operation is indicated by a return value of a valid socket descriptor. Subsequently, an FD_CONNECT indication is given when the join operation completes, either successfully or otherwise. Also, until the multipoint session join attempt completes all subsequent calls to WSPJoinLeaf() on the same socket will fail with the error code WSAEALREADY. If the return error code indicates the multipoint session join attempt failed (i.e. WSAECONNREFUSED, WSAENETUNREACH, WSAETIMEDOUT) the WinSock SPI client may call WSPJoinLeaf() again for the same socket. --*/ { *lpErrno = WSAEINVAL; return INVALID_SOCKET; } // WSPJoinLeaf