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.
 
 
 
 
 
 

574 lines
16 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
msext.c
Abstract:
This module contains Microsoft WinSock extension APIs.
Author:
Keith Moore (keithmo) 01-Jul-1996
Revision History:
--*/
#include "winsockp.h"
//
// Private types.
//
typedef struct _SOCK_EXTENSIONS {
GUID Guid;
LPVOID * Function;
} SOCK_EXTENSIONS, *PSOCK_EXTENSIONS;
//
// Private globals.
//
BOOLEAN SockExtensionsInitialized = FALSE;
LPFN_TRANSMITFILE SockTransmitFilePointer;
LPFN_ACCEPTEX SockAcceptExPointer;
LPFN_GETACCEPTEXSOCKADDRS SockGetAcceptExSockaddrsPointer;
SOCK_EXTENSIONS SockExtensions[] =
{
{
WSAID_TRANSMITFILE,
(LPVOID *)&SockTransmitFilePointer
},
{
WSAID_ACCEPTEX,
(LPVOID *)&SockAcceptExPointer
},
{
WSAID_GETACCEPTEXSOCKADDRS,
(LPVOID *)&SockGetAcceptExSockaddrsPointer
}
};
#define NUM_EXTENSIONS ( sizeof(SockExtensions) / sizeof(SockExtensions[0]) )
//
// Private prototypes.
//
INT
SockInitializeExtensions(
SOCKET Socket
);
//
// Public functions.
//
BOOL
TransmitFile (
IN SOCKET hSocket,
IN HANDLE hFile,
IN DWORD nNumberOfBytesToWrite,
IN DWORD nNumberOfBytesPerSend,
IN LPOVERLAPPED lpOverlapped,
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
IN DWORD dwFlags
)
/*++
Routine Description:
Transmits file data over a connected socket handle by interfacing
with the cache manager to retrieve the file data. Use this routine
when high performance file data transfer over sockets is required.
Arguments:
hSocket - a connected socket handle. The socket may be a datagram
socket or a virtual circuit socket.
hFile - an open file handle. Since the file data is read
sequentially, it is recommended that the handle be opened with
FILE_FLAG_SEQUENTIAL_SCAN to improve caching performance.
nNumberOfBytesToWrite - the count of bytes to send from the file.
This request will not complete until the entire requested amount
has been sent, or until an error is encountered. If this
parameter is equal to zero, then the entire file is transmited.
nNumberOfBytesPerSend - informs the sockets layer of the size to use
for each send it performs. The value 0 indicates that an
intelligent default should be used. This is mainly useful for
datagram or message protocols that have limitations of the size
of individual send requests.
lpOverlapped - specify this parameter when the socket handle is
opened as overlapped to achieve overlapped I/O. Note that by
default sockets are opened as overlapped.
Also use this parameter to specify an offset within the file at
which to start the file data transfer. The file pointer is
ignored by this API, so if lpOverlapped is NULL the transmission
always starts at offset 0 in the file.
When lpOverlapped is non-NULL, TransmitFile() may return
ERROR_IO_PENDING to allow the calling function to continue
processing while the operation completes. The event (or hSocket
if hEvent is NULL) will be set to the signalled state upon
completion of the request.
lpTransmitBuffers - an optional pointer to a TRANSMIT_FILE_BUFFERS
structure which contains pointers to data to send before the
file (the head buffer) and after the file (the tail buffer). If
only the file is to be sent, this parameter may be specified as
NULL.
dwFlags - any combination of the following:
TF_DISCONNECT - start a transport-level disconnect after all the
file data has been queued to the transport.
TF_REUSE_SOCKET - prepare the socket handle to be reused. When
the TransmitFile request completes, the socket handle may be
passed to the SuperAccept API. Only valid if TF_DISCONNECT
is also specified.
TF_WRITE_BEHIND - complete the TransmitFile request immediately,
without pending. If this flag is specified and TransmitFile
succeeds, then the data has been accepted by the system but not
necessarily acknowledged by the remote end. Do not use this
setting with the other two settings.
Return Value:
TRUE - The operation was successul.
FALSE - The operation failed. Extended error status is available
using GetLastError().
--*/
{
//
// Initialize the extension table if necessary.
//
if( !SockExtensionsInitialized ) {
INT err;
err = SockInitializeExtensions( hSocket );
if( err != NO_ERROR ) {
SetLastError( err );
return FALSE;
}
}
//
// Let the service provider do the dirty work.
//
WS_ASSERT( SockTransmitFilePointer != NULL );
return SockTransmitFilePointer(
hSocket,
hFile,
nNumberOfBytesToWrite,
nNumberOfBytesPerSend,
lpOverlapped,
lpTransmitBuffers,
dwFlags
);
} // TransmitFile
BOOL
AcceptEx (
IN SOCKET sListenSocket,
IN SOCKET sAcceptSocket,
IN PVOID lpOutputBuffer,
IN DWORD dwReceiveDataLength,
IN DWORD dwLocalAddressLength,
IN DWORD dwRemoteAddressLength,
OUT LPDWORD lpdwBytesReceived,
IN LPOVERLAPPED lpOverlapped
)
/*++
Routine Description:
Combines several socket functions into a single API/kernel
transition. A new connection is accepted, both the local and remote
addresses for the connection are returned, and a receive is done for
the first chunk of data sent by the remote. By combining all these
functions into a sigle call, the performance of connection
acceptance is significantly improved.
Unlike the normal winsock accept() call, AcceptEx() is
asynchronous, which lets it fit better with servers which use thread
pooling to improve scalability. As with all overlapped Win32
functions, either Win32 events or completion ports may be used
as a completion notification mechanism.
Another key difference between this function and the normal accept()
function is that this function requires the caller to specify
both the listening socket AND the socket on which to accept the
connection. The sAcceptSocket must be an opened socket which
is neither bound nor connected. Note that calling TransmitFile()
with both the TF_DISCONNECT and TF_REUSE_SOCKET flags will result
in the specified socket being returned to the "open" state, so
such a socket may be passed to AcceptEx() as sAcceptSocket.
Note that only a single output buffer receives the data as well as
the local and remote socket addresses. Use of a single buffer
improves performance, but the caller should call th
GetAcceptExSockaddrs() function to locate the addresses in the
output buffer.
Because the addresses are written in an internal format, the caller
must specify sisteen more bytes for them than the size of the
SOCKADDR structure for the transport protocol in use. For example,
th size of a SOCKADDR_IN, the address structure for TCP/IP, is 16
bytes, so at least 24 bytes must be specified as the buffer sizes
for the local and remote addresses.
The lpNumberOfBytesTransferred parameters of the
GetQueuedCompletionStatus() and GetOverlappedResult() APIs indicates
the number of bytes received in the request.
When this operation completes successfully, sAcceptHandle may
be used for only the following routines:
ReadFile
WriteFile
send
recv
TransmitFile
closesocket
In order to use it with othr winsock routines, a setsockopt() with
the SO_UPDATE_ACCEPT_CONTEXT option must be performed on the socket.
This socket option initializes some user-mode state on the socket
which allows other winsock routines to access the socket correctly.
sAcceptSocket will not inherit the properties of sListenSocket until
SO_UPDATE_ACCEPT_CONTEXT is set on the socket. When AcceptEx()
returns, sAcceptSocket is in the default state for a connected
socket.
To use the SO_UPDATE_ACCEPT_CONTEXT option, call the setsockopt()
function, specifying sAcceptSocket as the socket handle to setsockopt()
and specify sListenSocket as the option value. For example,
err = setsockopt( sAcceptSocket,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
(char *)&sListenSocket,
sizeof(sListenSocket) );
Arguments:
sListenSocket - a listening socket on which to accept a connection.
The listen() socket API must have been previously called on this
handle.
sAcceptSocket - an open socket handle on which to accept an incoming
connection. This socket must not be bound or connected.
lpOutputBuffer - a pointer to a buffer which receives the first
chunk of data sent on the connection and the local and remote
addresses for the new connection. The receive data is written
to the first part of the buffer (starting at offset 0) and the
addresses are written later in the buffer. This parameter must
be specified.
dwReceiveDataLength - the number of bytes in the buffer that should
be used for receiving data. If this parameter is specified as
0, then no receive operation is performed in conjunction with
the accept--the accept completes as soon as a connection arrives
without waiting for any data to arrive.
dwLocalAddressLength - the number of bytes reserved for the local
address information. This must be at least sixteen bytes more
than the maximum sockaddr length for the transport protocol in
use.
dwRemoteAddressLength - the number of bytes reserved for the remote
address information. This must be at least sixteen bytes more
than the maximum sockaddr length for the transport protocol in
use.
lpdwBytesReceived - points to a DWORD which receives the count of
bytes received. This is only set if the operation completes
synchronously; if it returns ERROR_IO_PENDING and completes
later, then this DWORD is never set and the count of bytes read
may be obtained from the completion notification mechnaism.
lpOverlapped - an OVERLAPPED structure to use in processing the request.
This parameter MUST be specified; it may not be NULL.
Return Value:
TRUE if the operation completed successfully. FALSE if there was an
error, in which case GetLastError() may be called to return extended
error information. If GetLastError() returns ERROR_IO_PENDING then
the operation was successfully initiated and is still in progress.
--*/
{
//
// Initialize the extension table if necessary.
//
if( !SockExtensionsInitialized ) {
INT err;
err = SockInitializeExtensions( sListenSocket );
if( err != NO_ERROR ) {
SetLastError( err );
return FALSE;
}
}
//
// Let the service provider do the dirty work.
//
WS_ASSERT( SockAcceptExPointer != NULL );
return SockAcceptExPointer(
sListenSocket,
sAcceptSocket,
lpOutputBuffer,
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
lpdwBytesReceived,
lpOverlapped
);
} // AcceptEx
VOID
GetAcceptExSockaddrs (
IN PVOID lpOutputBuffer,
IN DWORD dwReceiveDataLength,
IN DWORD dwLocalAddressLength,
IN DWORD dwRemoteAddressLength,
OUT LPSOCKADDR *LocalSockaddr,
OUT LPINT LocalSockaddrLength,
OUT LPSOCKADDR *RemoteSockaddr,
OUT LPINT RemoteSockaddrLength
)
/*++
Routine Description:
Processes the lpOutputBuffer parameter after a successful AcceptEx()
operation. Because AcceptEx() writes address information in an
internal (TDI) format, this routine is required to locate the
SOCKADDR structures in the buffer.
Arguments:
lpOutputBuffer - the same lpOutputBuffer parameter that was passed
to AcceptEx().
dwReceiveDataLength - must be equal to the dwReceiveDataLength
parameter which was passed to AcceptEx().
dwLocalAddressLength - must be equal to the dwLocalAddressLength
parameter which was passed to AcceptEx().
dwRemoteAddressLength - must be equal to the dwRemoteAddressLength
parameter which was passed to AcceptEx().
LocalSockaddr - receives a pointer to the SOCKADDR which describes
the local address of the connection (the same information as
would be returned by getsockname()). This parameter must be
specified.
LocalSockaddrLength - receives the size of the local address. This
parameter must be specified.
RemoteSockaddr - receives a pointer to the SOCKADDR which describes
the remote address of the connection (the same information as
would be returned by getpeername()). This parameter must be
specified.
RemoteSockaddrLength - receives the size of the local address. This
parameter must be specified.
Return Value:
None.
--*/
{
//
// Since GetAcceptExSockaddrs() doesn't take a socket parameter, we
// cannot initialize the extension table here. This is OK, as it
// would be totally lame to call this API before calling AcceptEx().
//
// Also, since GetAcceptExSockaddrs() has no return value, we cannot
// directly indicate an error to the caller, so we'll just NULL out
// everything passed in and let the app explode.
//
if( !SockExtensionsInitialized ) {
LocalSockaddr = NULL;
LocalSockaddrLength = 0;
RemoteSockaddr = NULL;
RemoteSockaddrLength = 0;
return;
}
//
// Let the service provider do the dirty work.
//
WS_ASSERT( SockGetAcceptExSockaddrsPointer != NULL );
SockGetAcceptExSockaddrsPointer(
lpOutputBuffer,
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
LocalSockaddr,
LocalSockaddrLength,
RemoteSockaddr,
RemoteSockaddrLength
);
} // GetAcceptExSockaddrs
INT
SockInitializeExtensions(
SOCKET Socket
)
/*++
Routine Description:
Initializes the MS extension table.
Arguments:
Socket - An open socket handle so we can find the extension pointers.
Return Value:
INT - NO_ERROR if successful, WSAE* if not.
--*/
{
INT i;
INT result;
INT err;
DWORD returned;
//
// Short-circuit this stuff if we're already initialized. This
// avoids acquiring the lock.
//
if( SockExtensionsInitialized ) {
return NO_ERROR;
}
//
// Acquire the lock and recheck for initialization in case another
// thread has already done it.
//
SockAcquireGlobalLockExclusive();
if( SockExtensionsInitialized ) {
SockReleaseGlobalLock();
return NO_ERROR;
}
//
// Loop through the extension table and get the function pointers.
//
err = NO_ERROR;
for( i = 0 ; i < NUM_EXTENSIONS ; i++ ) {
result = WSAIoctl(
Socket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
(LPVOID)&SockExtensions[i].Guid,
sizeof(SockExtensions[i].Guid),
(LPVOID)SockExtensions[i].Function,
sizeof(*SockExtensions[i].Function),
&returned,
NULL,
NULL
);
if( result == SOCKET_ERROR ) {
err = GetLastError();
break;
}
}
SockExtensionsInitialized = ( err == NO_ERROR );
SockReleaseGlobalLock();
return err;
} // SockInitializeExtensions