mirror of https://github.com/tongzx/nt5src
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.
3089 lines
93 KiB
3089 lines
93 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
wshrm.c
|
|
|
|
Abstract:
|
|
|
|
This module contains necessary routines for the Pragmatic General Multicast
|
|
(PGM) Windows Sockets Helper DLL. This DLL provides the transport-specific
|
|
support necessary for the Windows Sockets DLL to use the PGM Transport.
|
|
|
|
This file is largely a clone of the TCP/IP helper code.
|
|
|
|
Author:
|
|
|
|
Mohammad Shabbir Alam (MAlam) 30-March-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define UNICODE
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
|
|
#include <stdio.h>
|
|
#include <tdi.h>
|
|
|
|
#include <winsock2.h>
|
|
#include <wsahelp.h>
|
|
|
|
#include <nspapi.h>
|
|
#include <nspapip.h>
|
|
|
|
#include <wsRm.h>
|
|
#include <rmcommon.h>
|
|
|
|
|
|
// #define TRACE_ON 1
|
|
|
|
#if defined(DBG) && defined(TRACE_ON)
|
|
#define PgmLog DbgPrint
|
|
#define PgmError DbgPrint
|
|
|
|
#else
|
|
|
|
#if defined(DBG)
|
|
#define PgmError DbgPrint
|
|
#else
|
|
#define PgmError
|
|
#endif // DBG
|
|
|
|
#define PgmLog
|
|
#endif // DBG && TRACE_ON
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Structure and variables to define the triples supported by the
|
|
// Pgm Transport. The first entry of each array is considered
|
|
// the canonical triple for that socket type; the other entries are
|
|
// synonyms for the first.
|
|
//
|
|
|
|
typedef struct _MAPPING_TRIPLE {
|
|
INT AddressFamily;
|
|
INT SocketType;
|
|
INT Protocol;
|
|
} MAPPING_TRIPLE, *PMAPPING_TRIPLE;
|
|
|
|
MAPPING_TRIPLE PgmMappingTriples[] = { AF_INET, SOCK_RDM, IPPROTO_RM,
|
|
AF_INET, SOCK_STREAM, IPPROTO_RM };
|
|
|
|
|
|
//
|
|
// Define valid flags for WSHOpenSocket2().
|
|
//
|
|
#define VALID_PGM_FLAGS (WSA_FLAG_OVERLAPPED | \
|
|
WSA_FLAG_MULTIPOINT_C_LEAF | \
|
|
WSA_FLAG_MULTIPOINT_D_LEAF)
|
|
|
|
#define DD_PGM_DEVICE_NAME L"\\Device\\Pgm"
|
|
|
|
#define PGM_NAME L"WSHRM"
|
|
|
|
#define DEFAULT_RECEIVE_BUFFER_SIZE 8192
|
|
#define DEFAULT_MULTICAST_TTL 1
|
|
#define DEFAULT_MULTICAST_INTERFACE INADDR_ANY
|
|
#define DEFAULT_MULTICAST_LOOPBACK TRUE
|
|
|
|
#define WINSOCK_SPI_VERSION 2
|
|
#define PGM_MESSAGE_SIZE (((ULONG)-1) / 2)
|
|
|
|
#define IS_DGRAM_SOCK(type) (((type) == SOCK_DGRAM) || ((type) == SOCK_RAW))
|
|
|
|
//
|
|
// The GUID identifying this provider.
|
|
//
|
|
GUID PgmProviderGuid = { /* c845f828-500f-4e1e-87c2-5dfca19b5348 */
|
|
0xc845f828,
|
|
0x500f,
|
|
0x4e1e,
|
|
{0x87, 0xc2, 0x5d, 0xfc, 0xa1, 0x9b, 0x53, 0x48}
|
|
};
|
|
|
|
|
|
/* ****
|
|
XP1_CONNECTIONLESS ==> Sock type can only be: SOCK_DGRAM
|
|
SOCK_RAW
|
|
|
|
XP1_MESSAGE_ORIENTED ==> Sock type can only be: SOCK_DGRAM
|
|
SOCK_RAW
|
|
SOCK_RDM
|
|
SOCK_SEQPACKET
|
|
XP1_PSEUDO_STREAM <==> SOCK_STREAM
|
|
**** */
|
|
|
|
WSAPROTOCOL_INFOW Winsock2Protocols[] =
|
|
{
|
|
//
|
|
// PGM RDM (SOCK_RDM cannot be CONNECTIONLESS)
|
|
//
|
|
{
|
|
PGM_RDM_SERVICE_FLAGS, // dwServiceFlags1
|
|
0, // dwServiceFlags2
|
|
0, // dwServiceFlags3
|
|
0, // dwServiceFlags4
|
|
PFL_MATCHES_PROTOCOL_ZERO, // dwProviderFlags
|
|
{ // gProviderId
|
|
0, 0, 0,
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
|
},
|
|
0, // dwCatalogEntryId
|
|
{ // ProtocolChain
|
|
BASE_PROTOCOL, // ChainLen
|
|
{ 0, 0, 0, 0, 0, 0, 0 } // ChainEntries
|
|
},
|
|
WINSOCK_SPI_VERSION, // iVersion
|
|
AF_INET, // iAddressFamily
|
|
sizeof(SOCKADDR_IN), // iMaxSockAddr
|
|
sizeof(SOCKADDR_IN), // iMinSockAddr
|
|
SOCK_RDM, // iSocketType
|
|
IPPROTO_RM, // iProtocol
|
|
0, // iProtocolMaxOffset
|
|
BIGENDIAN, // iNetworkByteOrder
|
|
SECURITY_PROTOCOL_NONE, // iSecurityScheme
|
|
PGM_MESSAGE_SIZE, // dwMessageSize
|
|
0, // dwProviderReserved
|
|
L"MSAFD Pgm (RDM)" // szProtocol
|
|
},
|
|
|
|
//
|
|
// PGM Stream
|
|
//
|
|
{
|
|
PGM_STREAM_SERVICE_FLAGS, // dwServiceFlags1
|
|
0, // dwServiceFlags2
|
|
0, // dwServiceFlags3
|
|
0, // dwServiceFlags4
|
|
PFL_MATCHES_PROTOCOL_ZERO, // dwProviderFlags
|
|
{ // gProviderId
|
|
0, 0, 0,
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
|
},
|
|
0, // dwCatalogEntryId
|
|
{ // ProtocolChain
|
|
BASE_PROTOCOL, // ChainLen
|
|
{ 0, 0, 0, 0, 0, 0, 0 } // ChainEntries
|
|
},
|
|
WINSOCK_SPI_VERSION, // iVersion
|
|
AF_INET, // iAddressFamily
|
|
sizeof(SOCKADDR_IN), // iMaxSockAddr
|
|
sizeof(SOCKADDR_IN), // iMinSockAddr
|
|
SOCK_STREAM, // iSocketType
|
|
IPPROTO_RM, // iProtocol
|
|
0, // iProtocolMaxOffset
|
|
BIGENDIAN, // iNetworkByteOrder
|
|
SECURITY_PROTOCOL_NONE, // iSecurityScheme
|
|
0, // dwMessageSize
|
|
0, // dwProviderReserved
|
|
L"MSAFD Pgm (Stream)" // szProtocol
|
|
},
|
|
};
|
|
|
|
#define NUM_WINSOCK2_PROTOCOLS \
|
|
(sizeof(Winsock2Protocols) / sizeof(Winsock2Protocols[0]))
|
|
|
|
//
|
|
// The socket context structure for this DLL. Each open TCP/IP socket
|
|
// will have one of these context structures, which is used to maintain
|
|
// information about the socket.
|
|
//
|
|
|
|
typedef struct _WSHPGM_SOCKET_CONTEXT {
|
|
INT AddressFamily;
|
|
INT SocketType;
|
|
INT Protocol;
|
|
INT ReceiveBufferSize;
|
|
DWORD Flags;
|
|
INT MulticastTtl;
|
|
tIPADDRESS MulticastOutInterface;
|
|
tIPADDRESS MulticastInInterface;
|
|
tIPADDRESS MultipointTarget;
|
|
USHORT MultipointPort;
|
|
BOOLEAN MulticastLoopback;
|
|
BOOLEAN MultipointLeaf;
|
|
} WSHPGM_SOCKET_CONTEXT, *PWSHPGM_SOCKET_CONTEXT;
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Forward declarations of internal routines.
|
|
//
|
|
|
|
BOOLEAN
|
|
IsTripleInList (
|
|
IN PMAPPING_TRIPLE List,
|
|
IN ULONG ListLength,
|
|
IN INT AddressFamily,
|
|
IN INT SocketType,
|
|
IN INT Protocol
|
|
);
|
|
|
|
INT
|
|
SetTdiInformation (
|
|
IN HANDLE TdiConnectionObjectHandle,
|
|
IN ULONG Ioctl,
|
|
IN PVOID InputBuffer,
|
|
IN ULONG InputBufferLength,
|
|
IN PVOID OutputBuffer,
|
|
IN ULONG OutputBufferLength,
|
|
IN BOOLEAN WaitForCompletion
|
|
);
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
DllInitialize(
|
|
IN PVOID DllHandle,
|
|
IN ULONG Reason,
|
|
IN PVOID Context OPTIONAL
|
|
)
|
|
{
|
|
|
|
PgmLog ("WSHPgm.DllInitialize: Reason=<%x> ...\n", Reason);
|
|
|
|
switch (Reason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
//
|
|
// We don't need to receive thread attach and detach
|
|
// notifications, so disable them to help application
|
|
// performance.
|
|
//
|
|
|
|
DisableThreadLibraryCalls( DllHandle );
|
|
|
|
return TRUE;
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} // DllInitialize
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WSHOpenSocket(
|
|
IN OUT PINT AddressFamily,
|
|
IN OUT PINT SocketType,
|
|
IN OUT PINT Protocol,
|
|
OUT PUNICODE_STRING TransportDeviceName,
|
|
OUT PVOID *HelperDllSocketContext,
|
|
OUT PDWORD NotificationEvents
|
|
)
|
|
{
|
|
INT RetVal;
|
|
|
|
RetVal = WSHOpenSocket2 (AddressFamily,
|
|
SocketType,
|
|
Protocol,
|
|
0, // Group
|
|
0, // Flags
|
|
TransportDeviceName,
|
|
HelperDllSocketContext,
|
|
NotificationEvents);
|
|
|
|
return (RetVal);
|
|
} // WSHOpenSocket
|
|
|
|
|
|
INT
|
|
WSHOpenSocket2(
|
|
IN OUT PINT AddressFamily,
|
|
IN OUT PINT SocketType,
|
|
IN OUT PINT Protocol,
|
|
IN GROUP Group,
|
|
IN DWORD Flags,
|
|
OUT PUNICODE_STRING TransportDeviceName,
|
|
OUT PVOID *HelperDllSocketContext,
|
|
OUT PDWORD NotificationEvents
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does the necessary work for this helper DLL to open a socket and is
|
|
called by the winsock DLL in the socket() routine. This routine
|
|
verifies that the specified triple is valid, determines the NT
|
|
device name of the TDI provider that will support that triple,
|
|
allocates space to hold the socket's context block, and
|
|
canonicalizes the triple.
|
|
|
|
Arguments:
|
|
|
|
AddressFamily - on input, the address family specified in the
|
|
socket() call. On output, the canonicalized value for the
|
|
address family.
|
|
|
|
SocketType - on input, the socket type specified in the socket()
|
|
call. On output, the canonicalized value for the socket type.
|
|
|
|
Protocol - on input, the protocol specified in the socket() call.
|
|
On output, the canonicalized value for the protocol.
|
|
|
|
Group - Identifies the group for the new socket.
|
|
|
|
Flags - Zero or more WSA_FLAG_* flags as passed into WSASocket().
|
|
|
|
TransportDeviceName - receives the name of the TDI provider that
|
|
will support the specified triple.
|
|
|
|
HelperDllSocketContext - receives a context pointer that the winsock
|
|
DLL will return to this helper DLL on future calls involving
|
|
this socket.
|
|
|
|
NotificationEvents - receives a bitmask of those state transitions
|
|
this helper DLL should be notified on.
|
|
|
|
Return Value:
|
|
|
|
INT - a winsock error code indicating the status of the operation, or
|
|
NO_ERROR if the operation succeeded.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSHPGM_SOCKET_CONTEXT context;
|
|
|
|
if (IsTripleInList (PgmMappingTriples,
|
|
sizeof(PgmMappingTriples) / sizeof(PgmMappingTriples[0]),
|
|
*AddressFamily,
|
|
*SocketType,
|
|
*Protocol))
|
|
{
|
|
//
|
|
// It's an Rdm PGM socket. Check the flags.
|
|
//
|
|
if ((Flags & ~VALID_PGM_FLAGS) != 0)
|
|
{
|
|
PgmError ("\tWSHPgm.WSHOpenSocket2: ERROR: Flags=<%x> & VALID_PGM_FLAGS=<%x>\n",
|
|
Flags, (ULONG)~VALID_PGM_FLAGS);
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
//
|
|
// Return the canonical form of a CDP socket triple.
|
|
//
|
|
*AddressFamily = PgmMappingTriples[0].AddressFamily;
|
|
*SocketType = PgmMappingTriples[0].SocketType;
|
|
*Protocol = PgmMappingTriples[0].Protocol;
|
|
|
|
//
|
|
// Indicate the name of the TDI device that will service
|
|
// SOCK_RDM sockets for PGM.
|
|
//
|
|
RtlInitUnicodeString (TransportDeviceName, DD_PGM_DEVICE_NAME);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This should never happen if the registry information about this
|
|
// helper DLL is correct. If somehow this did happen, just return
|
|
// an error.
|
|
//
|
|
PgmError ("\tWSHPgm.WSHOpenSocket2: Invalid Triple AddrFamily=<%d>, SockType=<%d>, Protocol=<%d>!\n",
|
|
*AddressFamily, *SocketType, *Protocol);
|
|
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
//
|
|
// Allocate context for this socket. The Windows Sockets DLL will
|
|
// return this value to us when it asks us to get/set socket options.
|
|
//
|
|
context = RtlAllocateHeap (RtlProcessHeap( ), 0, sizeof(*context));
|
|
if (context == NULL)
|
|
{
|
|
PgmError ("WSHPgm.WSHOpenSocket2: WSAENOBUFS -- <%d> bytes\n", sizeof(*context));
|
|
return WSAENOBUFS;
|
|
}
|
|
RtlZeroMemory (context, sizeof(*context));
|
|
|
|
//
|
|
// Initialize the context for the socket.
|
|
//
|
|
context->AddressFamily = *AddressFamily;
|
|
context->SocketType = *SocketType;
|
|
context->Protocol = *Protocol;
|
|
context->ReceiveBufferSize = DEFAULT_RECEIVE_BUFFER_SIZE;
|
|
context->Flags = Flags;
|
|
|
|
context->MulticastTtl = DEFAULT_MULTICAST_TTL;
|
|
context->MulticastOutInterface = DEFAULT_MULTICAST_INTERFACE;
|
|
context->MulticastInInterface = DEFAULT_MULTICAST_INTERFACE;
|
|
context->MulticastLoopback = DEFAULT_MULTICAST_LOOPBACK;
|
|
context->MultipointLeaf = FALSE;
|
|
|
|
//
|
|
// Tell the Windows Sockets DLL which state transitions we're
|
|
// interested in being notified of. The only times we need to be
|
|
// called is after a connect has completed so that we can turn on
|
|
// the sending of keepalives if SO_KEEPALIVE was set before the
|
|
// socket was connected, when the socket is closed so that we can
|
|
// free context information, and when a connect fails so that we
|
|
// can, if appropriate, dial in to the network that will support the
|
|
// connect attempt.
|
|
//
|
|
if (*SocketType == SOCK_RDM)
|
|
{
|
|
*NotificationEvents = WSH_NOTIFY_LISTEN | WSH_NOTIFY_CONNECT | WSH_NOTIFY_CLOSE
|
|
| WSH_NOTIFY_CONNECT_ERROR | WSH_NOTIFY_BIND;
|
|
}
|
|
else // *SocketType == SOCK_STREAM
|
|
{
|
|
*NotificationEvents = WSH_NOTIFY_LISTEN | WSH_NOTIFY_CONNECT | WSH_NOTIFY_CLOSE
|
|
| WSH_NOTIFY_CONNECT_ERROR;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHOpenSocket2: Succeeded -- %s\n",
|
|
(*SocketType == SOCK_RDM ? "SOCK_RDM" : "SOCK_STREAM"));
|
|
|
|
//
|
|
// Everything worked, return success.
|
|
//
|
|
*HelperDllSocketContext = context;
|
|
return NO_ERROR;
|
|
} // WSHOpenSocket2
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WSHNotify (
|
|
IN PVOID HelperDllSocketContext,
|
|
IN SOCKET SocketHandle,
|
|
IN HANDLE TdiAddressObjectHandle,
|
|
IN HANDLE TdiConnectionObjectHandle,
|
|
IN DWORD NotifyEvent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the winsock DLL after a state transition
|
|
of the socket. Only state transitions returned in the
|
|
NotificationEvents parameter of WSHOpenSocket() are notified here.
|
|
This routine allows a winsock helper DLL to track the state of
|
|
socket and perform necessary actions corresponding to state
|
|
transitions.
|
|
|
|
Arguments:
|
|
|
|
HelperDllSocketContext - the context pointer given to the winsock
|
|
DLL by WSHOpenSocket().
|
|
|
|
SocketHandle - the handle for the socket.
|
|
|
|
TdiAddressObjectHandle - the TDI address object of the socket, if
|
|
any. If the socket is not yet bound to an address, then
|
|
it does not have a TDI address object and this parameter
|
|
will be NULL.
|
|
|
|
TdiConnectionObjectHandle - the TDI connection object of the socket,
|
|
if any. If the socket is not yet connected, then it does not
|
|
have a TDI connection object and this parameter will be NULL.
|
|
|
|
NotifyEvent - indicates the state transition for which we're being
|
|
called.
|
|
|
|
Return Value:
|
|
|
|
INT - a winsock error code indicating the status of the operation, or
|
|
NO_ERROR if the operation succeeded.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSHPGM_SOCKET_CONTEXT context = HelperDllSocketContext;
|
|
INT err;
|
|
|
|
//
|
|
// We should only be called after a connect() completes or when the
|
|
// socket is being closed.
|
|
//
|
|
if (NotifyEvent == WSH_NOTIFY_BIND)
|
|
{
|
|
//
|
|
// Set options for Address handle
|
|
//
|
|
PgmLog ("WSHPgm.WSHNotify[WSH_NOTIFY_BIND]: ...\n");
|
|
if (!context->MultipointLeaf)
|
|
{
|
|
tPGM_MCAST_REQUEST MCastRequest;
|
|
|
|
PgmLog ("WSH_NOTIFY_BIND: Address=<%x>, Connection=<%x>, OutIf=<%x>, InIf=<%x>\n",
|
|
TdiAddressObjectHandle, TdiConnectionObjectHandle, context->MulticastOutInterface,
|
|
context->MulticastInInterface);
|
|
|
|
if (context->MulticastOutInterface)
|
|
{
|
|
MCastRequest.MCastOutIf = context->MulticastOutInterface;
|
|
|
|
err = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_SET_SEND_IF,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (err != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHNotify: Error=<%x> setting MCastIf on Bind\n", err);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (context->MulticastInInterface)
|
|
{
|
|
MCastRequest.MCastInfo.MCastInIf = context->MulticastInInterface;
|
|
|
|
err = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_ADD_RECEIVE_IF,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (err != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHNotify: Error=<%x> setting MCastIf on Bind\n", err);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
context->MultipointLeaf = TRUE;
|
|
}
|
|
}
|
|
else if (NotifyEvent == WSH_NOTIFY_CONNECT)
|
|
{
|
|
//
|
|
// If a connection-object option was set on the socket before
|
|
// it was connected, set the option for real now.
|
|
//
|
|
PgmLog ("WSHPgm.WSHNotify[WSH_NOTIFY_CONNECT]: ...\n");
|
|
}
|
|
else if (NotifyEvent == WSH_NOTIFY_CONNECT_ERROR)
|
|
{
|
|
//
|
|
// Return WSATRY_AGAIN to get wsock32 to attempt the connect
|
|
// again. Any other return code is ignored.
|
|
//
|
|
PgmLog ("WSHPgm.WSHNotify[WSH_NOTIFY_CONNECT_ERROR]: ...\n");
|
|
}
|
|
else if (NotifyEvent == WSH_NOTIFY_LISTEN)
|
|
{
|
|
//
|
|
// If a connection-object option was set on the socket before
|
|
// it was connected, set the option for real now.
|
|
//
|
|
PgmLog ("WSHPgm.WSHNotify[WSH_NOTIFY_LISTEN]: ...\n");
|
|
}
|
|
else if (NotifyEvent == WSH_NOTIFY_CLOSE)
|
|
{
|
|
//
|
|
// Free the socket context.
|
|
//
|
|
PgmLog ("WSHPgm.WSHNotify[WSH_NOTIFY_CONNECT_CLOSE]: ...\n");
|
|
RtlFreeHeap (RtlProcessHeap( ), 0, context);
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHPgm.WSHNotify: Unknown Event: <%x> ...\n", NotifyEvent);
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
|
|
} // WSHNotify
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WSHSetSocketInformation (
|
|
IN PVOID HelperDllSocketContext,
|
|
IN SOCKET SocketHandle,
|
|
IN HANDLE TdiAddressObjectHandle,
|
|
IN HANDLE TdiConnectionObjectHandle,
|
|
IN INT Level,
|
|
IN INT OptionName,
|
|
IN PCHAR OptionValue,
|
|
IN INT OptionLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets information about a socket for those socket
|
|
options supported in this helper DLL. This routine is
|
|
called by the winsock DLL when a level/option name combination is
|
|
passed to setsockopt() that the winsock DLL does not understand.
|
|
|
|
Arguments:
|
|
|
|
HelperDllSocketContext - the context pointer returned from
|
|
WSHOpenSocket().
|
|
|
|
SocketHandle - the handle of the socket for which we're getting
|
|
information.
|
|
|
|
TdiAddressObjectHandle - the TDI address object of the socket, if
|
|
any. If the socket is not yet bound to an address, then
|
|
it does not have a TDI address object and this parameter
|
|
will be NULL.
|
|
|
|
TdiConnectionObjectHandle - the TDI connection object of the socket,
|
|
if any. If the socket is not yet connected, then it does not
|
|
have a TDI connection object and this parameter will be NULL.
|
|
|
|
Level - the level parameter passed to setsockopt().
|
|
|
|
OptionName - the optname parameter passed to setsockopt().
|
|
|
|
OptionValue - the optval parameter passed to setsockopt().
|
|
|
|
OptionLength - the optlen parameter passed to setsockopt().
|
|
|
|
Return Value:
|
|
|
|
INT - a winsock error code indicating the status of the operation, or
|
|
NO_ERROR if the operation succeeded.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSHPGM_SOCKET_CONTEXT context = HelperDllSocketContext;
|
|
PWSHPGM_SOCKET_CONTEXT ParentContext = (PWSHPGM_SOCKET_CONTEXT) OptionValue;
|
|
tPGM_MCAST_REQUEST MCastRequest;
|
|
INT error;
|
|
INT optionValue;
|
|
RM_SEND_WINDOW UNALIGNED *pSetWindowInfo;
|
|
RM_FEC_INFO *pFECInfo;
|
|
|
|
UNREFERENCED_PARAMETER( SocketHandle );
|
|
UNREFERENCED_PARAMETER( TdiAddressObjectHandle );
|
|
|
|
//
|
|
// Check if this is an internal request for context information.
|
|
//
|
|
|
|
if (Level == SOL_INTERNAL && OptionName == SO_CONTEXT)
|
|
{
|
|
//
|
|
// Insure that the context information being passed to us is
|
|
// sufficiently large.
|
|
//
|
|
if (OptionLength < sizeof(*context))
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: WSAEINVAL -- <%d> < <%d>\n",
|
|
OptionLength, sizeof(*context));
|
|
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
if (HelperDllSocketContext == NULL)
|
|
{
|
|
//
|
|
// This is our notification that a socket handle was
|
|
// inherited or duped into this process. Allocate a context
|
|
// structure for the new socket.
|
|
//
|
|
context = RtlAllocateHeap (RtlProcessHeap( ), 0, sizeof(*context));
|
|
if (context == NULL)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: WSAENOBUFS -- <%d> bytes\n", sizeof(*context));
|
|
return WSAENOBUFS;
|
|
}
|
|
|
|
//
|
|
// Copy over information into the context block.
|
|
//
|
|
RtlCopyMemory (context, OptionValue, sizeof(*context) );
|
|
|
|
//
|
|
// Tell the Windows Sockets DLL where our context information is
|
|
// stored so that it can return the context pointer in future
|
|
// calls.
|
|
//
|
|
*(PWSHPGM_SOCKET_CONTEXT *)OptionValue = context;
|
|
|
|
PgmLog ("WSHPgm.WSHSetSocketInformation[SOL_INTERNAL:SO_CONTEXT] Inherited socket handle\n");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// The socket was accept()'ed and it needs to have the same
|
|
// properties as it's parent. The OptionValue buffer
|
|
// contains the context information of this socket's parent.
|
|
//
|
|
|
|
ParentContext = (PWSHPGM_SOCKET_CONTEXT)OptionValue;
|
|
|
|
ASSERT( context->AddressFamily == ParentContext->AddressFamily );
|
|
ASSERT( context->SocketType == ParentContext->SocketType );
|
|
ASSERT( context->Protocol == ParentContext->Protocol );
|
|
|
|
//
|
|
// Record this fact in the leaf socket so we can drop membership
|
|
// when the leaf socket is closed.
|
|
//
|
|
context->MultipointLeaf = ParentContext->MultipointLeaf;
|
|
context->MultipointTarget = ParentContext->MultipointTarget;
|
|
context->MultipointPort = ParentContext->MultipointPort;
|
|
context->MulticastOutInterface = ParentContext->MulticastOutInterface;
|
|
context->MulticastInInterface = ParentContext->MulticastInInterface;
|
|
|
|
PgmLog ("WSHPgm.WSHSetSocketInformation[SOL_INTERNAL:SO_CONTEXT] Accepted socket handle\n");
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// The only other levels we support here are SOL_SOCKET and IPPROTO_RM
|
|
//
|
|
|
|
if (Level != SOL_SOCKET &&
|
|
Level != IPPROTO_RM)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: Unsupported Level=<%d>\n", Level);
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
//
|
|
// Make sure that the option length is sufficient.
|
|
//
|
|
if (OptionLength < sizeof(char))
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: OptionLength=<%d> < <%d>\n", OptionLength, sizeof(char));
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
if (OptionLength >= sizeof (int))
|
|
{
|
|
optionValue = *((INT UNALIGNED *)OptionValue);
|
|
}
|
|
else
|
|
{
|
|
optionValue = (UCHAR)*OptionValue;
|
|
}
|
|
|
|
if (Level == IPPROTO_RM)
|
|
{
|
|
//
|
|
// Act based on the specific option.
|
|
//
|
|
switch (OptionName)
|
|
{
|
|
case RM_RATE_WINDOW_SIZE:
|
|
{
|
|
if ((!TdiAddressObjectHandle) ||
|
|
(OptionLength < sizeof(RM_SEND_WINDOW)))
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
pSetWindowInfo = (RM_SEND_WINDOW UNALIGNED *) OptionValue;
|
|
|
|
MCastRequest.TransmitWindowInfo.RateKbitsPerSec = pSetWindowInfo->RateKbitsPerSec;
|
|
MCastRequest.TransmitWindowInfo.WindowSizeInMSecs = pSetWindowInfo->WindowSizeInMSecs;
|
|
MCastRequest.TransmitWindowInfo.WindowSizeInBytes = pSetWindowInfo->WindowSizeInBytes;
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_SET_WINDOW_SIZE_RATE,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> setting Window Rate Size=<%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[RATE_WINDOW_SIZE]: Set Window Rate Size\n");
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_SEND_WINDOW_ADV_RATE:
|
|
{
|
|
if (!TdiAddressObjectHandle)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
MCastRequest.WindowAdvancePercentage = (ULONG) optionValue;
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_SET_ADVANCE_WINDOW_RATE,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> setting Window Adv. Rate=<%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[SEND_WINDOW_ADV_RATE]: Set Window Adv. Rate\n");
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_LATEJOIN:
|
|
{
|
|
if (!TdiAddressObjectHandle)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
MCastRequest.LateJoinerPercentage = (ULONG) optionValue;
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_SET_LATE_JOINER_PERCENTAGE,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> setting LATEJOIN=<%x>\n",
|
|
error, optionValue);
|
|
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[LATEJOIN]: <%d %%>\n", MCastRequest.LateJoinerPercentage);
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_SET_MESSAGE_BOUNDARY:
|
|
{
|
|
if (!TdiConnectionObjectHandle)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
MCastRequest.NextMessageBoundary = (ULONG) optionValue;
|
|
|
|
error = SetTdiInformation (TdiConnectionObjectHandle,
|
|
IOCTL_PGM_WSH_SET_NEXT_MESSAGE_BOUNDARY,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> setting NextMessageBoundary=<%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[MESSAGE_BOUNDARY]: Set next message boundary = <%d>\n",
|
|
MCastRequest.NextMessageBoundary);
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_SET_SEND_IF:
|
|
{
|
|
//
|
|
// If we have a TDI address object, set this option to
|
|
// the address object. If we don't have a TDI address
|
|
// object then we'll have to wait until after the socket
|
|
// is bound.
|
|
//
|
|
PgmLog ("WSHSetSocketInformation[SEND_IF]: Address=<%x>, Connection=<%x>, OutIf=<%x>\n",
|
|
TdiAddressObjectHandle, TdiConnectionObjectHandle, optionValue);
|
|
|
|
if (TdiAddressObjectHandle != NULL)
|
|
{
|
|
|
|
MCastRequest.MCastOutIf = * ((tIPADDRESS *) &optionValue);
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_SET_SEND_IF,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> setting MCastOutIf=<%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHSetSocketInformation[SEND_IF]: Set MCastIf=<%x>\n",optionValue);
|
|
|
|
context->MulticastOutInterface = optionValue;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Save the Interface for now!
|
|
//
|
|
context->MulticastOutInterface = optionValue;
|
|
PgmError ("WSHPgm.WSHSetSocketInformation[SET_SEND_IF]: WARNING -- NULL Address!\n");
|
|
}
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_ADD_RECEIVE_IF:
|
|
{
|
|
PgmLog ("WSHSetSocketInformation[ADD_RECEIVE_IF]: Address=<%x>, Connection=<%x>, If=<%x>\n",
|
|
TdiAddressObjectHandle, TdiConnectionObjectHandle, optionValue);
|
|
|
|
//
|
|
// If we have a TDI address object, set this option to
|
|
// the address object. If we don't have a TDI address
|
|
// object then we'll have to wait until after the socket
|
|
// is bound.
|
|
//
|
|
if (TdiAddressObjectHandle != NULL)
|
|
{
|
|
MCastRequest.MCastInfo.MCastInIf = * ((tIPADDRESS *) &optionValue);
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_ADD_RECEIVE_IF,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> adding MCastInIf=<%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[ADD_RECEIVE_IF]: Set MCastIf=<%x>\n",optionValue);
|
|
|
|
context->MulticastInInterface = optionValue;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Save the Interface for now!
|
|
//
|
|
context->MulticastInInterface = optionValue;
|
|
PgmError ("WSHPgm.WSHSetSocketInformation[ADD_RECEIVE_IF]: WARNING-- NULL Address!\n");
|
|
}
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_DEL_RECEIVE_IF:
|
|
{
|
|
PgmLog ("WSHSetSocketInformation[DEL_RECEIVE_IF]: Address=<%x>, Connection=<%x>, InIf=<%x>\n",
|
|
TdiAddressObjectHandle, TdiConnectionObjectHandle, optionValue);
|
|
|
|
//
|
|
// If we have a TDI address object, set this option to
|
|
// the address object. If we don't have a TDI address
|
|
// object then we'll have to wait until after the socket
|
|
// is bound.
|
|
//
|
|
if (TdiAddressObjectHandle != NULL)
|
|
{
|
|
MCastRequest.MCastInfo.MCastInIf = * ((tIPADDRESS *) &optionValue);
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_DEL_RECEIVE_IF,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> deleting MCastInIf=<%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[DEL_RECEIVE_IF]: Set MCastIf=<%x>\n",optionValue);
|
|
|
|
context->MulticastInInterface = optionValue;
|
|
}
|
|
else
|
|
{
|
|
if (context->MulticastInInterface == (tIPADDRESS) optionValue)
|
|
{
|
|
context->MulticastInInterface = 0;
|
|
}
|
|
PgmError ("WSHPgm.WSHSetSocketInformation[DEL_RECEIVE_IF]: WARNING-- NULL Address!\n");
|
|
}
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_USE_FEC:
|
|
{
|
|
if ((!TdiAddressObjectHandle) ||
|
|
(OptionLength < sizeof(RM_FEC_INFO)))
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
pFECInfo = (RM_FEC_INFO *) OptionValue;
|
|
|
|
MCastRequest.FECInfo.FECBlockSize = pFECInfo->FECBlockSize;
|
|
MCastRequest.FECInfo.FECProActivePackets = pFECInfo->FECProActivePackets;
|
|
MCastRequest.FECInfo.FECGroupSize = pFECInfo->FECGroupSize;
|
|
MCastRequest.FECInfo.fFECOnDemandParityEnabled = pFECInfo->fFECOnDemandParityEnabled;
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_USE_FEC,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> setting FEC = <%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[RATE_WINDOW_SIZE]: Set FEC Info\n");
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_SET_MCAST_TTL:
|
|
{
|
|
if (!TdiAddressObjectHandle)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
MCastRequest.MCastTtl = (ULONG) optionValue;
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_SET_MCAST_TTL,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> setting MCastTtl=<%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[MESSAGE_BOUNDARY]: Set MCastTtl = <%d>\n",
|
|
MCastRequest.MCastTtl);
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_SENDER_WINDOW_ADVANCE_METHOD:
|
|
{
|
|
if (!TdiAddressObjectHandle)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
MCastRequest.WindowAdvanceMethod = (ULONG) optionValue;
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_SET_WINDOW_ADVANCE_METHOD,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> setting WindowAdvanceMethod=<%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[WINDOW_ADVANCE_METHOD]: Set WindowAdvanceMethod = <%d>\n",
|
|
MCastRequest.WindowAdvanceMethod);
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
default:
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation[IPPROTO_RM]: Unsupported option=<%d>\n",OptionName);
|
|
error = WSAENOPROTOOPT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Handle socket-level options.
|
|
//
|
|
switch (OptionName)
|
|
{
|
|
case SO_RCVBUF:
|
|
{
|
|
//
|
|
// If the receive buffer size is being changed, tell PGM about
|
|
// it. Do nothing if this is a datagram.
|
|
//
|
|
if (context->ReceiveBufferSize == optionValue ||
|
|
IS_DGRAM_SOCK(context->SocketType))
|
|
{
|
|
error = NO_ERROR;
|
|
break;
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[SO_RCVBUF]: Address=<%x>, Connection=<%x>, BuffLen=<%x>\n",
|
|
TdiAddressObjectHandle, TdiConnectionObjectHandle, optionValue);
|
|
|
|
if ((TdiConnectionObjectHandle != NULL) &&
|
|
(OptionLength <= sizeof (int)))
|
|
{
|
|
MCastRequest.RcvBufferLength = optionValue;
|
|
error = SetTdiInformation (TdiConnectionObjectHandle,
|
|
IOCTL_PGM_WSH_SET_RCV_BUFF_LEN,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
if (error != NO_ERROR)
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation: ERROR=<%d> setting SO_RCVBUF=<%x>\n",
|
|
error, optionValue);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
PgmLog ("WSHSetSocketInformation[SOL_SOCKET]: Set SO_RCVBUF=<%x>\n", optionValue);
|
|
context->ReceiveBufferSize = optionValue;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
PgmError ("WSHPgm.WSHSetSocketInformation[SOL_SOCKET]: Unsupported Option=<%d>\n",OptionName);
|
|
error = WSAENOPROTOOPT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return error;
|
|
} // WSHSetSocketInformation
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WSHGetSocketInformation (
|
|
IN PVOID HelperDllSocketContext,
|
|
IN SOCKET SocketHandle,
|
|
IN HANDLE TdiAddressObjectHandle,
|
|
IN HANDLE TdiConnectionObjectHandle,
|
|
IN INT Level,
|
|
IN INT OptionName,
|
|
OUT PCHAR OptionValue,
|
|
OUT PINT OptionLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves information about a socket for those socket
|
|
options supported in this helper DLL. This routine is
|
|
called by the winsock DLL when a level/option name combination is
|
|
passed to getsockopt() that the winsock DLL does not understand.
|
|
|
|
Arguments:
|
|
|
|
HelperDllSocketContext - the context pointer returned from
|
|
WSHOpenSocket().
|
|
|
|
SocketHandle - the handle of the socket for which we're getting
|
|
information.
|
|
|
|
TdiAddressObjectHandle - the TDI address object of the socket, if
|
|
any. If the socket is not yet bound to an address, then
|
|
it does not have a TDI address object and this parameter
|
|
will be NULL.
|
|
|
|
TdiConnectionObjectHandle - the TDI connection object of the socket,
|
|
if any. If the socket is not yet connected, then it does not
|
|
have a TDI connection object and this parameter will be NULL.
|
|
|
|
Level - the level parameter passed to getsockopt().
|
|
|
|
OptionName - the optname parameter passed to getsockopt().
|
|
|
|
OptionValue - the optval parameter passed to getsockopt().
|
|
|
|
OptionLength - the optlen parameter passed to getsockopt().
|
|
|
|
Return Value:
|
|
|
|
INT - a winsock error code indicating the status of the operation, or
|
|
NO_ERROR if the operation succeeded.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSHPGM_SOCKET_CONTEXT context = HelperDllSocketContext;
|
|
tPGM_MCAST_REQUEST MCastRequest;
|
|
RM_SEND_WINDOW UNALIGNED *pSetWindowInfo;
|
|
RM_FEC_INFO UNALIGNED *pFECInfo;
|
|
RM_SENDER_STATS UNALIGNED *pSenderStats;
|
|
RM_RECEIVER_STATS UNALIGNED *pReceiverStats;
|
|
INT error;
|
|
|
|
UNREFERENCED_PARAMETER( HelperDllSocketContext );
|
|
UNREFERENCED_PARAMETER( SocketHandle );
|
|
UNREFERENCED_PARAMETER( TdiAddressObjectHandle );
|
|
UNREFERENCED_PARAMETER( TdiConnectionObjectHandle );
|
|
|
|
//
|
|
// Check if this is an internal request for context information.
|
|
//
|
|
|
|
if (Level == SOL_INTERNAL && OptionName == SO_CONTEXT)
|
|
{
|
|
//
|
|
// The Windows Sockets DLL is requesting context information
|
|
// from us. If an output buffer was not supplied, the Windows
|
|
// Sockets DLL is just requesting the size of our context
|
|
// information.
|
|
//
|
|
if (OptionValue != NULL)
|
|
{
|
|
//
|
|
// Make sure that the buffer is sufficient to hold all the
|
|
// context information.
|
|
//
|
|
if (*OptionLength < sizeof(*context))
|
|
{
|
|
PgmLog ("WSHPgm.WSHGetSocketInformation: OptionLength=<%d> < ContextLength=<%d>\n",
|
|
*OptionLength, sizeof(*context));
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
//
|
|
// Copy in the context information.
|
|
//
|
|
RtlCopyMemory( OptionValue, context, sizeof(*context) );
|
|
}
|
|
|
|
*OptionLength = sizeof(*context);
|
|
|
|
PgmLog ("WSHPgm.WSHGetSocketInformation[SOL_INTERNAL:SO_CONTEXT]: OptionLength=<%d>\n",
|
|
*OptionLength);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// The only other levels we support here are SOL_SOCKET and IPPROTO_RM
|
|
//
|
|
if (Level != SOL_SOCKET &&
|
|
Level != IPPROTO_RM)
|
|
{
|
|
PgmError ("WSHPgm.WSHGetSocketInformation: Unsupported Level=<%d>\n", Level);
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
//
|
|
// Make sure that the output buffer is sufficiently large.
|
|
//
|
|
|
|
if (*OptionLength < sizeof(char))
|
|
{
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
__try
|
|
{
|
|
RtlZeroMemory (OptionValue, *OptionLength);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
if (Level == IPPROTO_RM)
|
|
{
|
|
switch (OptionName)
|
|
{
|
|
case RM_RATE_WINDOW_SIZE:
|
|
{
|
|
if ((!TdiAddressObjectHandle) ||
|
|
(*OptionLength < sizeof(RM_SEND_WINDOW)))
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_QUERY_WINDOW_SIZE_RATE,
|
|
NULL,
|
|
0,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
TRUE);
|
|
if (error == NO_ERROR)
|
|
{
|
|
pSetWindowInfo = (RM_SEND_WINDOW UNALIGNED *) OptionValue;
|
|
|
|
pSetWindowInfo->RateKbitsPerSec = MCastRequest.TransmitWindowInfo.RateKbitsPerSec;
|
|
pSetWindowInfo->WindowSizeInMSecs = MCastRequest.TransmitWindowInfo.WindowSizeInMSecs;
|
|
pSetWindowInfo->WindowSizeInBytes = MCastRequest.TransmitWindowInfo.WindowSizeInBytes;
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHPgm.WSHGetSocketInformation: ERROR=<%d> Querying Window RateSize\n",error);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetSocketInformation[RATE_WINDOW_SIZE]: Get Window Rate Size\n");
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_SEND_WINDOW_ADV_RATE:
|
|
{
|
|
if ((!TdiAddressObjectHandle) ||
|
|
(*OptionLength < sizeof(ULONG)))
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_QUERY_ADVANCE_WINDOW_RATE,
|
|
NULL,
|
|
0,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
TRUE);
|
|
if (error == NO_ERROR)
|
|
{
|
|
* ((PULONG) OptionValue) = MCastRequest.WindowAdvancePercentage;
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHPgm.WSHGetSocketInformation: ERROR=<%d> Querying WindowAdvRate\n", error);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetSocketInformation[WINDOW_ADV_RATE]: %d\n",
|
|
MCastRequest.WindowAdvancePercentage);
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_LATEJOIN:
|
|
{
|
|
if ((!TdiAddressObjectHandle) ||
|
|
(*OptionLength < sizeof(ULONG)))
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_QUERY_LATE_JOINER_PERCENTAGE,
|
|
NULL,
|
|
0,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
TRUE);
|
|
if (error == NO_ERROR)
|
|
{
|
|
* ((PULONG) OptionValue) = MCastRequest.LateJoinerPercentage;
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHGetSocketInformation: ERROR=<%d> Querying LateJoinerPercentage\n", error);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetSocketInformation[LATEJOIN]: <%d>\n",
|
|
MCastRequest.LateJoinerPercentage);
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_SENDER_WINDOW_ADVANCE_METHOD:
|
|
{
|
|
if ((!TdiAddressObjectHandle) ||
|
|
(*OptionLength < sizeof(ULONG)))
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_QUERY_WINDOW_ADVANCE_METHOD,
|
|
NULL,
|
|
0,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
TRUE);
|
|
if (error == NO_ERROR)
|
|
{
|
|
* ((PULONG) OptionValue) = MCastRequest.WindowAdvanceMethod;
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHGetSocketInformation: ERROR=<%d> Querying WindowAdvanceMethod\n", error);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetSocketInformation[WINDOW_ADVANCE_METHOD]: <%d>\n",
|
|
MCastRequest.WindowAdvanceMethod);
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_USE_FEC:
|
|
{
|
|
if ((!TdiAddressObjectHandle) ||
|
|
(*OptionLength < sizeof(RM_FEC_INFO)))
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetSocketInformation[FEC_INFO]: Get FEC_INFO\n");
|
|
|
|
error = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_QUERY_FEC_INFO,
|
|
NULL,
|
|
0,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
TRUE);
|
|
if (error == NO_ERROR)
|
|
{
|
|
pFECInfo = (RM_FEC_INFO UNALIGNED *) OptionValue;
|
|
RtlCopyMemory (pFECInfo, &MCastRequest.FECInfo, sizeof(RM_FEC_INFO));
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHGetSocketInformation: ERROR=<%d> Querying FEC_INFO\n", error);
|
|
return error;
|
|
}
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_SENDER_STATISTICS:
|
|
{
|
|
if ((!TdiConnectionObjectHandle) ||
|
|
(*OptionLength < sizeof(RM_SENDER_STATS)))
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
error = SetTdiInformation (TdiConnectionObjectHandle,
|
|
IOCTL_PGM_WSH_QUERY_SENDER_STATS,
|
|
NULL,
|
|
0,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
TRUE);
|
|
if (error == NO_ERROR)
|
|
{
|
|
pSenderStats = (RM_SENDER_STATS UNALIGNED *) OptionValue;
|
|
RtlCopyMemory (pSenderStats, &MCastRequest.SenderStats, sizeof(RM_SENDER_STATS));
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHGetSocketInformation: ERROR=<%d> Querying SENDER_STATS\n", error);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetSocketInformation[SENDER_STATS]: Get SENDER_STATS\n");
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
case RM_RECEIVER_STATISTICS:
|
|
{
|
|
if ((!TdiConnectionObjectHandle) ||
|
|
(*OptionLength < sizeof(RM_RECEIVER_STATS)))
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
error = SetTdiInformation (TdiConnectionObjectHandle,
|
|
IOCTL_PGM_WSH_QUERY_RECEIVER_STATS,
|
|
NULL,
|
|
0,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
TRUE);
|
|
if (error == NO_ERROR)
|
|
{
|
|
pReceiverStats = (RM_RECEIVER_STATS UNALIGNED *) OptionValue;
|
|
RtlCopyMemory (pReceiverStats, &MCastRequest.ReceiverStats, sizeof(RM_RECEIVER_STATS));
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHGetSocketInformation: ERROR=<%d> Querying RECEIVER_STATS\n", error);
|
|
return error;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetSocketInformation[RECEIVER_STATS]: Get RECEIVER_STATS\n");
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
default:
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
PgmError ("WSHPgm.WSHGetSocketInformation[%s]: Unsupported OptionName=<%d>\n",
|
|
(Level == SOL_SOCKET ? "SOL_SOCKET" : "IPPROTO_RM"), OptionName);
|
|
|
|
return WSAENOPROTOOPT;
|
|
|
|
} // WSHGetSocketInformation
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WSHGetSockaddrType (
|
|
IN PSOCKADDR Sockaddr,
|
|
IN DWORD SockaddrLength,
|
|
OUT PSOCKADDR_INFO SockaddrInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses a sockaddr to determine the type of the
|
|
machine address and endpoint address portions of the sockaddr.
|
|
This is called by the winsock DLL whenever it needs to interpret
|
|
a sockaddr.
|
|
|
|
Arguments:
|
|
|
|
Sockaddr - a pointer to the sockaddr structure to evaluate.
|
|
|
|
SockaddrLength - the number of bytes in the sockaddr structure.
|
|
|
|
SockaddrInfo - a pointer to a structure that will receive information
|
|
about the specified sockaddr.
|
|
|
|
|
|
Return Value:
|
|
|
|
INT - a winsock error code indicating the status of the operation, or
|
|
NO_ERROR if the operation succeeded.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNALIGNED SOCKADDR_IN *sockaddr = (PSOCKADDR_IN)Sockaddr;
|
|
ULONG i;
|
|
|
|
//
|
|
// Make sure that the address family is correct.
|
|
//
|
|
if (sockaddr->sin_family != AF_INET)
|
|
{
|
|
return WSAEAFNOSUPPORT;
|
|
}
|
|
|
|
//
|
|
// Make sure that the length is correct.
|
|
//
|
|
if (SockaddrLength < sizeof(SOCKADDR_IN))
|
|
{
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
//
|
|
// The address passed the tests, looks like a good address.
|
|
// Determine the type of the address portion of the sockaddr.
|
|
//
|
|
|
|
if (sockaddr->sin_addr.s_addr == INADDR_ANY)
|
|
{
|
|
SockaddrInfo->AddressInfo = SockaddrAddressInfoWildcard;
|
|
}
|
|
else if (sockaddr->sin_addr.s_addr == INADDR_BROADCAST)
|
|
{
|
|
SockaddrInfo->AddressInfo = SockaddrAddressInfoBroadcast;
|
|
}
|
|
else if (sockaddr->sin_addr.s_addr == INADDR_LOOPBACK)
|
|
{
|
|
SockaddrInfo->AddressInfo = SockaddrAddressInfoLoopback;
|
|
}
|
|
else
|
|
{
|
|
SockaddrInfo->AddressInfo = SockaddrAddressInfoNormal;
|
|
}
|
|
|
|
//
|
|
// Determine the type of the port (endpoint) in the sockaddr.
|
|
//
|
|
if (sockaddr->sin_port == 0)
|
|
{
|
|
SockaddrInfo->EndpointInfo = SockaddrEndpointInfoWildcard;
|
|
}
|
|
else if (ntohs (sockaddr->sin_port) < 2000)
|
|
{
|
|
SockaddrInfo->EndpointInfo = SockaddrEndpointInfoReserved;
|
|
}
|
|
else
|
|
{
|
|
SockaddrInfo->EndpointInfo = SockaddrEndpointInfoNormal;
|
|
}
|
|
|
|
//
|
|
// Zero out the sin_reserved_mbz part of the address. We silently allow
|
|
// nonzero values in this field.
|
|
//
|
|
for (i = 0; i < sizeof(sockaddr->sin_zero); i++)
|
|
{
|
|
sockaddr->sin_zero[i] = 0;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetSockAddrType: Addr=<%x>=><%x>, Port=<%x>=><%x>\n",
|
|
sockaddr->sin_addr.s_addr, SockaddrInfo->AddressInfo, sockaddr->sin_port, SockaddrInfo->EndpointInfo);
|
|
|
|
return NO_ERROR;
|
|
|
|
} // WSHGetSockaddrType
|
|
|
|
|
|
|
|
INT
|
|
WSHEnumProtocols (
|
|
IN LPINT lpiProtocols,
|
|
IN LPWSTR lpTransportKeyName,
|
|
IN OUT LPVOID lpProtocolBuffer,
|
|
IN OUT LPDWORD lpdwBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enumerates the protocols supported by this helper.
|
|
|
|
Arguments:
|
|
|
|
lpiProtocols - Pointer to a NULL-terminated array of protocol
|
|
identifiers. Only protocols specified in this array will
|
|
be returned by this function. If this pointer is NULL,
|
|
all protocols are returned.
|
|
|
|
lpTransportKeyName -
|
|
|
|
lpProtocolBuffer - Pointer to a buffer to fill with PROTOCOL_INFO
|
|
structures.
|
|
|
|
lpdwBufferLength - Pointer to a variable that, on input, contains
|
|
the size of lpProtocolBuffer. On output, this value will be
|
|
updated with the size of the data actually written to the buffer.
|
|
|
|
Return Value:
|
|
|
|
INT - The number of protocols returned if successful, -1 if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD bytesRequired;
|
|
PPROTOCOL_INFO PgmProtocolInfo;
|
|
BOOL useRM = FALSE;
|
|
DWORD i;
|
|
|
|
UNREFERENCED_PARAMETER(lpTransportKeyName);
|
|
|
|
//
|
|
// Make sure that the caller cares about RM.
|
|
//
|
|
if (ARGUMENT_PRESENT (lpiProtocols))
|
|
{
|
|
for (i = 0; lpiProtocols[i] != 0; i++)
|
|
{
|
|
if (lpiProtocols[i] == IPPROTO_RM)
|
|
{
|
|
useRM = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
useRM = TRUE;
|
|
}
|
|
|
|
if (!useRM)
|
|
{
|
|
*lpdwBufferLength = 0;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Make sure that the caller has specified a sufficiently large
|
|
// buffer.
|
|
//
|
|
bytesRequired = (DWORD)((sizeof(PROTOCOL_INFO) * 1) + ((wcslen (PGM_NAME) + 1) * sizeof(WCHAR)));
|
|
if (bytesRequired > *lpdwBufferLength)
|
|
{
|
|
*lpdwBufferLength = bytesRequired;
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Fill in PGM info
|
|
//
|
|
PgmProtocolInfo = lpProtocolBuffer;
|
|
PgmProtocolInfo->lpProtocol = (LPWSTR) ((PBYTE)lpProtocolBuffer + *lpdwBufferLength -
|
|
((wcslen(PGM_NAME) + 1) * sizeof(WCHAR)));
|
|
PgmProtocolInfo->dwServiceFlags = PGM_RDM_SERVICE_FLAGS;
|
|
PgmProtocolInfo->iAddressFamily = AF_INET;
|
|
PgmProtocolInfo->iSocketType = SOCK_RDM;
|
|
PgmProtocolInfo->iProtocol = IPPROTO_RM;
|
|
PgmProtocolInfo->iMaxSockAddr = sizeof(SOCKADDR_IN);
|
|
PgmProtocolInfo->iMinSockAddr = sizeof(SOCKADDR_IN);
|
|
PgmProtocolInfo->dwMessageSize = PGM_MESSAGE_SIZE;
|
|
wcscpy (PgmProtocolInfo->lpProtocol, PGM_NAME);
|
|
|
|
*lpdwBufferLength = bytesRequired;
|
|
|
|
PgmLog ("WSHPgm.WSHEnumProtocols: ServiceFlags=<%x>, <%x>...\n",
|
|
PGM_RDM_SERVICE_FLAGS, PGM_STREAM_SERVICE_FLAGS);
|
|
|
|
return 1;
|
|
|
|
} // WSHEnumProtocols
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WSHGetWildcardSockaddr (
|
|
IN PVOID HelperDllSocketContext,
|
|
OUT PSOCKADDR Sockaddr,
|
|
OUT PINT SockaddrLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a wildcard socket address. A wildcard address
|
|
is one which will bind the socket to an endpoint of the transport's
|
|
choosing. For TCP/IP, a wildcard address has IP address ==
|
|
0.0.0.0 and port = 0.
|
|
|
|
Arguments:
|
|
|
|
HelperDllSocketContext - the context pointer returned from
|
|
WSHOpenSocket() for the socket for which we need a wildcard
|
|
address.
|
|
|
|
Sockaddr - points to a buffer which will receive the wildcard socket
|
|
address.
|
|
|
|
SockaddrLength - receives the length of the wioldcard sockaddr.
|
|
|
|
Return Value:
|
|
|
|
INT - a winsock error code indicating the status of the operation, or
|
|
NO_ERROR if the operation succeeded.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (*SockaddrLength < sizeof(SOCKADDR_IN))
|
|
{
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
*SockaddrLength = sizeof(SOCKADDR_IN);
|
|
|
|
//
|
|
// Just zero out the address and set the family to AF_INET
|
|
//
|
|
RtlZeroMemory (Sockaddr, sizeof(SOCKADDR_IN));
|
|
Sockaddr->sa_family = AF_INET;
|
|
|
|
PgmLog ("WSHPgm.WSHGetWildcardSockaddr: ...\n");
|
|
return NO_ERROR;
|
|
|
|
} // WSAGetWildcardSockaddr
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
DWORD
|
|
WSHGetWinsockMapping (
|
|
OUT PWINSOCK_MAPPING Mapping,
|
|
IN DWORD MappingLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the list of address family/socket type/protocol triples
|
|
supported by this helper DLL.
|
|
|
|
Arguments:
|
|
|
|
Mapping - receives a pointer to a WINSOCK_MAPPING structure that
|
|
describes the triples supported here.
|
|
|
|
MappingLength - the length, in bytes, of the passed-in Mapping buffer.
|
|
|
|
Return Value:
|
|
|
|
DWORD - the length, in bytes, of a WINSOCK_MAPPING structure for this
|
|
helper DLL. If the passed-in buffer is too small, the return
|
|
value will indicate the size of a buffer needed to contain
|
|
the WINSOCK_MAPPING structure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD mappingLength;
|
|
|
|
mappingLength = sizeof(WINSOCK_MAPPING) - sizeof(MAPPING_TRIPLE) + sizeof(PgmMappingTriples);
|
|
|
|
//
|
|
// If the passed-in buffer is too small, return the length needed
|
|
// now without writing to the buffer. The caller should allocate
|
|
// enough memory and call this routine again.
|
|
//
|
|
if (mappingLength > MappingLength)
|
|
{
|
|
return mappingLength;
|
|
}
|
|
|
|
//
|
|
// Fill in the output mapping buffer with the list of triples
|
|
// supported in this helper DLL.
|
|
//
|
|
Mapping->Rows = sizeof(PgmMappingTriples) / sizeof(PgmMappingTriples[0]);
|
|
Mapping->Columns = sizeof(MAPPING_TRIPLE) / sizeof(DWORD);
|
|
RtlMoveMemory (Mapping->Mapping, PgmMappingTriples, sizeof(PgmMappingTriples));
|
|
|
|
PgmLog ("WSHPgm.WSHGetWinsockMapping: MappingLength=<%d>\n", mappingLength);
|
|
|
|
//
|
|
// Return the number of bytes we wrote.
|
|
//
|
|
return mappingLength;
|
|
|
|
} // WSHGetWinsockMapping
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WINAPI
|
|
WSHAddressToString (
|
|
IN LPSOCKADDR Address,
|
|
IN INT AddressLength,
|
|
IN LPWSAPROTOCOL_INFOW ProtocolInfo,
|
|
OUT LPWSTR AddressString,
|
|
IN OUT LPDWORD AddressStringLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a SOCKADDR to a human-readable form.
|
|
|
|
Arguments:
|
|
|
|
Address - The SOCKADDR to convert.
|
|
|
|
AddressLength - The length of Address.
|
|
|
|
ProtocolInfo - The WSAPROTOCOL_INFOW for a particular provider.
|
|
|
|
AddressString - Receives the formatted address string.
|
|
|
|
AddressStringLength - On input, contains the length of AddressString.
|
|
On output, contains the number of characters actually written
|
|
to AddressString.
|
|
|
|
Return Value:
|
|
|
|
INT - 0 if successful, WinSock error code if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WCHAR string[32];
|
|
INT length;
|
|
LPSOCKADDR_IN addr;
|
|
INT err = NO_ERROR;
|
|
|
|
//
|
|
// Quick sanity checks.
|
|
//
|
|
if( AddressLength < sizeof(SOCKADDR_IN))
|
|
{
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
__try {
|
|
addr = (LPSOCKADDR_IN)Address;
|
|
|
|
if (addr->sin_family != AF_INET)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
//
|
|
// Do the converstion.
|
|
//
|
|
// BUGBUG: We should really use the DavidTr huge-but-fast
|
|
// table based lookup for this, but we already have two copies
|
|
// of that 1K table in the system (one in WSOCK32.DLL, and one it
|
|
// WS2_32.DLL). I really don't want to see Yet Another Copy here.
|
|
//
|
|
length = swprintf (string, L"%d.%d.%d.%d", (addr->sin_addr.s_addr >> 0) & 0xFF,
|
|
(addr->sin_addr.s_addr >> 8) & 0xFF,
|
|
(addr->sin_addr.s_addr >> 16) & 0xFF,
|
|
(addr->sin_addr.s_addr >> 24) & 0xFF);
|
|
|
|
if (addr->sin_port != 0)
|
|
{
|
|
length += swprintf (string + length, L":%u", ntohs (addr->sin_port));
|
|
}
|
|
|
|
length++; // account for terminator
|
|
if (*AddressStringLength >= (DWORD)length)
|
|
{
|
|
RtlCopyMemory (AddressString, string, length * sizeof(WCHAR));
|
|
}
|
|
else
|
|
{
|
|
err = WSAEFAULT;
|
|
}
|
|
|
|
*AddressStringLength = length;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
err = WSAEFAULT;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHAddressToString: status=<%d>\n", err);
|
|
return err;
|
|
|
|
} // WSHAddressToString
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
INT
|
|
WINAPI
|
|
WSHStringToAddress (
|
|
IN LPWSTR AddressString,
|
|
IN DWORD AddressFamily,
|
|
IN LPWSAPROTOCOL_INFOW ProtocolInfo,
|
|
OUT LPSOCKADDR Address,
|
|
IN OUT LPINT AddressLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fills in a SOCKADDR structure by parsing a human-readable string.
|
|
|
|
Arguments:
|
|
|
|
AddressString - Points to the zero-terminated human-readable string.
|
|
|
|
AddressFamily - The address family to which the string belongs.
|
|
|
|
ProtocolInfo - The WSAPROTOCOL_INFOW for a particular provider.
|
|
|
|
Address - Receives the SOCKADDR structure.
|
|
|
|
AddressLength - On input, contains the length of Address. On output,
|
|
contains the number of bytes actually written to Address.
|
|
|
|
Return Value:
|
|
|
|
INT - 0 if successful, WinSock error code if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPWSTR terminator;
|
|
ULONG ipAddress;
|
|
USHORT port;
|
|
LPSOCKADDR_IN addr;
|
|
|
|
__try {
|
|
//
|
|
// Quick sanity checks.
|
|
//
|
|
if (*AddressLength < sizeof(SOCKADDR_IN))
|
|
{
|
|
*AddressLength = sizeof(SOCKADDR_IN);
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
if (AddressFamily != AF_INET)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
//
|
|
// Convert it.
|
|
//
|
|
if (!NT_SUCCESS(RtlIpv4StringToAddressW(AddressString, FALSE, &terminator,
|
|
(IN_ADDR*)&ipAddress))) {
|
|
return WSAEINVAL;
|
|
}
|
|
if (ipAddress == INADDR_NONE)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
if (*terminator == L':')
|
|
{
|
|
WCHAR ch;
|
|
USHORT base;
|
|
|
|
terminator++;
|
|
port = 0;
|
|
base = 10;
|
|
|
|
if (*terminator == L'0')
|
|
{
|
|
base = 8;
|
|
terminator++;
|
|
|
|
if (*terminator == L'x')
|
|
{
|
|
base = 16;
|
|
terminator++;
|
|
}
|
|
}
|
|
|
|
while (ch = *terminator++)
|
|
{
|
|
if (iswdigit(ch))
|
|
{
|
|
port = (port * base) + (ch - L'0');
|
|
}
|
|
else if (base == 16 && iswxdigit(ch))
|
|
{
|
|
port = (port << 4);
|
|
port += ch + 10 - (iswlower(ch) ? L'a' : L'A');
|
|
}
|
|
else
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
}
|
|
}
|
|
else if (*terminator == 0)
|
|
{
|
|
port = 0;
|
|
}
|
|
else
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
//
|
|
// Build the address.
|
|
//
|
|
RtlZeroMemory (Address, sizeof(SOCKADDR_IN));
|
|
|
|
addr = (LPSOCKADDR_IN)Address;
|
|
*AddressLength = sizeof(SOCKADDR_IN);
|
|
|
|
addr->sin_family = AF_INET;
|
|
addr->sin_port = htons (port);
|
|
addr->sin_addr.s_addr = ipAddress;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHStringToAddress:\n");
|
|
return NO_ERROR;
|
|
|
|
} // WSHStringToAddress
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WINAPI
|
|
WSHGetBroadcastSockaddr (
|
|
IN PVOID HelperDllSocketContext,
|
|
OUT PSOCKADDR Sockaddr,
|
|
OUT PINT SockaddrLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a broadcast socket address. A broadcast address
|
|
may be used as a destination for the sendto() API to send a datagram
|
|
to all interested clients.
|
|
|
|
Arguments:
|
|
|
|
HelperDllSocketContext - the context pointer returned from
|
|
WSHOpenSocket() for the socket for which we need a broadcast
|
|
address.
|
|
|
|
Sockaddr - points to a buffer which will receive the broadcast socket
|
|
address.
|
|
|
|
SockaddrLength - receives the length of the broadcast sockaddr.
|
|
|
|
Return Value:
|
|
|
|
INT - a winsock error code indicating the status of the operation, or
|
|
NO_ERROR if the operation succeeded.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPSOCKADDR_IN addr;
|
|
|
|
PgmError ("WSHPgm.WSHGetBroadcastSockaddr: Not Supported!\n");
|
|
|
|
return (WSAEOPNOTSUPP);
|
|
|
|
if (*SockaddrLength < sizeof(SOCKADDR_IN))
|
|
{
|
|
return WSAEFAULT;
|
|
}
|
|
|
|
*SockaddrLength = sizeof(SOCKADDR_IN);
|
|
|
|
//
|
|
// Build the broadcast address.
|
|
//
|
|
addr = (LPSOCKADDR_IN) Sockaddr;
|
|
|
|
RtlZeroMemory (addr, sizeof(*addr));
|
|
|
|
addr->sin_family = AF_INET;
|
|
addr->sin_addr.s_addr = htonl (INADDR_BROADCAST);
|
|
|
|
return NO_ERROR;
|
|
|
|
} // WSAGetBroadcastSockaddr
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WINAPI
|
|
WSHGetProviderGuid (
|
|
IN LPWSTR ProviderName,
|
|
OUT LPGUID ProviderGuid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the GUID identifying the protocols supported by this helper.
|
|
|
|
Arguments:
|
|
|
|
ProviderName - Contains the name of the provider, such as "Pgm".
|
|
|
|
ProviderGuid - Points to a buffer that receives the provider's GUID.
|
|
|
|
Return Value:
|
|
|
|
INT - 0 if successful, WinSock error code if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT RetVal = WSAEINVAL;
|
|
|
|
if (ProviderName == NULL || ProviderGuid == NULL)
|
|
{
|
|
RetVal = WSAEFAULT;
|
|
}
|
|
else if (_wcsicmp (ProviderName, L"Pgm") == 0)
|
|
{
|
|
RtlCopyMemory (ProviderGuid, &PgmProviderGuid, sizeof(GUID));
|
|
RetVal = NO_ERROR;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetProviderGuid: ProviderName=<%ws>, RetVal=<%x>\n", ProviderName, RetVal);
|
|
return (RetVal);
|
|
|
|
} // WSHGetProviderGuid
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WINAPI
|
|
WSHGetWSAProtocolInfo (
|
|
IN LPWSTR ProviderName,
|
|
OUT LPWSAPROTOCOL_INFOW * ProtocolInfo,
|
|
OUT LPDWORD ProtocolInfoEntries
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves a pointer to the WSAPROTOCOL_INFOW structure(s) describing
|
|
the protocol(s) supported by this helper.
|
|
|
|
Arguments:
|
|
|
|
ProviderName - Contains the name of the provider, such as "TcpIp".
|
|
|
|
ProtocolInfo - Receives a pointer to the WSAPROTOCOL_INFOW array.
|
|
|
|
ProtocolInfoEntries - Receives the number of entries in the array.
|
|
|
|
Return Value:
|
|
|
|
INT - 0 if successful, WinSock error code if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT RetVal = WSAEINVAL;
|
|
|
|
if (ProviderName == NULL || ProtocolInfo == NULL || ProtocolInfoEntries == NULL)
|
|
{
|
|
RetVal = WSAEFAULT;
|
|
}
|
|
else if (_wcsicmp (ProviderName, L"RMCast") == 0)
|
|
{
|
|
*ProtocolInfo = Winsock2Protocols;
|
|
*ProtocolInfoEntries = NUM_WINSOCK2_PROTOCOLS;
|
|
|
|
RetVal = NO_ERROR;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHGetWSAProtocolInfo: ProviderName=<%ws>, RetVal=<%x>\n", ProviderName, RetVal);
|
|
return (RetVal);
|
|
|
|
} // WSHGetWSAProtocolInfo
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WINAPI
|
|
WSHIoctl (
|
|
IN PVOID HelperDllSocketContext,
|
|
IN SOCKET SocketHandle,
|
|
IN HANDLE TdiAddressObjectHandle,
|
|
IN HANDLE TdiConnectionObjectHandle,
|
|
IN DWORD IoControlCode,
|
|
IN LPVOID InputBuffer,
|
|
IN DWORD InputBufferLength,
|
|
IN LPVOID OutputBuffer,
|
|
IN DWORD OutputBufferLength,
|
|
OUT LPDWORD NumberOfBytesReturned,
|
|
IN LPWSAOVERLAPPED Overlapped,
|
|
IN LPWSAOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine,
|
|
OUT LPBOOL NeedsCompletion
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs queries & controls on the socket. This is basically an
|
|
"escape hatch" for IOCTLs not supported by MSAFD.DLL. Any unknown
|
|
IOCTLs are routed to the socket's helper DLL for protocol-specific
|
|
processing.
|
|
|
|
Arguments:
|
|
|
|
HelperDllSocketContext - the context pointer returned from
|
|
WSHOpenSocket().
|
|
|
|
SocketHandle - the handle of the socket for which we're controlling.
|
|
|
|
TdiAddressObjectHandle - the TDI address object of the socket, if
|
|
any. If the socket is not yet bound to an address, then
|
|
it does not have a TDI address object and this parameter
|
|
will be NULL.
|
|
|
|
TdiConnectionObjectHandle - the TDI connection object of the socket,
|
|
if any. If the socket is not yet connected, then it does not
|
|
have a TDI connection object and this parameter will be NULL.
|
|
|
|
IoControlCode - Control code of the operation to perform.
|
|
|
|
InputBuffer - Address of the input buffer.
|
|
|
|
InputBufferLength - The length of InputBuffer.
|
|
|
|
OutputBuffer - Address of the output buffer.
|
|
|
|
OutputBufferLength - The length of OutputBuffer.
|
|
|
|
NumberOfBytesReturned - Receives the number of bytes actually written
|
|
to the output buffer.
|
|
|
|
Overlapped - Pointer to a WSAOVERLAPPED structure for overlapped
|
|
operations.
|
|
|
|
CompletionRoutine - Pointer to a completion routine to call when
|
|
the operation is completed.
|
|
|
|
NeedsCompletion - WSAIoctl() can be overlapped, with all the gory
|
|
details that involves, such as setting events, queuing completion
|
|
routines, and posting to IO completion ports. Since the majority
|
|
of the IOCTL codes can be completed quickly "in-line", MSAFD.DLL
|
|
can optionally perform the overlapped completion of the operation.
|
|
|
|
Setting *NeedsCompletion to TRUE (the default) causes MSAFD.DLL
|
|
to handle all of the IO completion details iff this is an
|
|
overlapped operation on an overlapped socket.
|
|
|
|
Setting *NeedsCompletion to FALSE tells MSAFD.DLL to take no
|
|
further action because the helper DLL will perform any necessary
|
|
IO completion.
|
|
|
|
Note that if a helper performs its own IO completion, the helper
|
|
is responsible for maintaining the "overlapped" mode of the socket
|
|
at socket creation time and NOT performing overlapped IO completion
|
|
on non-overlapped sockets.
|
|
|
|
Return Value:
|
|
|
|
INT - 0 if successful, WinSock error code if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT err;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Quick sanity checks.
|
|
//
|
|
if (HelperDllSocketContext == NULL ||
|
|
SocketHandle == INVALID_SOCKET ||
|
|
NumberOfBytesReturned == NULL ||
|
|
NeedsCompletion == NULL)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
*NeedsCompletion = TRUE;
|
|
|
|
switch (IoControlCode)
|
|
{
|
|
default:
|
|
err = WSAEINVAL;
|
|
break;
|
|
}
|
|
|
|
PgmLog ("WSHPgm.WSHIoctl: Returning <%d>\n", err);
|
|
return err;
|
|
|
|
} // WSHIoctl
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
INT
|
|
WINAPI
|
|
WSHJoinLeaf (
|
|
IN PVOID HelperDllSocketContext,
|
|
IN SOCKET SocketHandle,
|
|
IN HANDLE TdiAddressObjectHandle,
|
|
IN HANDLE TdiConnectionObjectHandle,
|
|
IN PVOID LeafHelperDllSocketContext,
|
|
IN SOCKET LeafSocketHandle,
|
|
IN PSOCKADDR Sockaddr,
|
|
IN DWORD SockaddrLength,
|
|
IN LPWSABUF CallerData,
|
|
IN LPWSABUF CalleeData,
|
|
IN LPQOS SocketQOS,
|
|
IN LPQOS GroupQOS,
|
|
IN DWORD Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs the protocol-dependent portion of creating a multicast
|
|
socket.
|
|
|
|
Arguments:
|
|
|
|
The following four parameters correspond to the socket passed into
|
|
the WSAJoinLeaf() API:
|
|
|
|
HelperDllSocketContext - The context pointer returned from
|
|
WSHOpenSocket().
|
|
|
|
SocketHandle - The handle of the socket used to establish the
|
|
multicast "session".
|
|
|
|
TdiAddressObjectHandle - The TDI address object of the socket, if
|
|
any. If the socket is not yet bound to an address, then
|
|
it does not have a TDI address object and this parameter
|
|
will be NULL.
|
|
|
|
TdiConnectionObjectHandle - The TDI connection object of the socket,
|
|
if any. If the socket is not yet connected, then it does not
|
|
have a TDI connection object and this parameter will be NULL.
|
|
|
|
The next two parameters correspond to the newly created socket that
|
|
identifies the multicast "session":
|
|
|
|
LeafHelperDllSocketContext - The context pointer returned from
|
|
WSHOpenSocket().
|
|
|
|
LeafSocketHandle - The handle of the socket that identifies the
|
|
multicast "session".
|
|
|
|
Sockaddr - The name of the peer to which the socket is to be joined.
|
|
|
|
SockaddrLength - The length of Sockaddr.
|
|
|
|
CallerData - Pointer to user data to be transferred to the peer
|
|
during multipoint session establishment.
|
|
|
|
CalleeData - Pointer to user data to be transferred back from
|
|
the peer during multipoint session establishment.
|
|
|
|
SocketQOS - Pointer to the flowspecs for SocketHandle, one in each
|
|
direction.
|
|
|
|
GroupQOS - Pointer to the flowspecs for the socket group, if any.
|
|
|
|
Flags - Flags to indicate if the socket is acting as sender,
|
|
receiver, or both.
|
|
|
|
Return Value:
|
|
|
|
INT - 0 if successful, a WinSock error code if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
tPGM_MCAST_REQUEST MCastRequest;
|
|
INT err;
|
|
BOOL bSet_RM_MULTICAST_IF = FALSE;
|
|
PWSHPGM_SOCKET_CONTEXT context;
|
|
|
|
//
|
|
// Note: at this time we only support non-rooted control schemes,
|
|
// and therefore no leaf socket is created
|
|
//
|
|
//
|
|
// Quick sanity checks.
|
|
//
|
|
if (HelperDllSocketContext == NULL ||
|
|
SocketHandle == INVALID_SOCKET ||
|
|
TdiAddressObjectHandle == NULL ||
|
|
LeafHelperDllSocketContext != NULL ||
|
|
LeafSocketHandle != INVALID_SOCKET ||
|
|
Sockaddr == NULL ||
|
|
Sockaddr->sa_family != AF_INET ||
|
|
SockaddrLength < sizeof(SOCKADDR_IN) ||
|
|
( CallerData != NULL && CallerData->len > 0 ) ||
|
|
( CalleeData != NULL && CalleeData->len > 0 ) ||
|
|
SocketQOS != NULL ||
|
|
GroupQOS != NULL)
|
|
{
|
|
return WSAEINVAL;
|
|
}
|
|
|
|
context = HelperDllSocketContext;
|
|
|
|
MCastRequest.MCastInfo.MCastIpAddr = ((LPSOCKADDR_IN)Sockaddr)->sin_addr.s_addr; // MCast group to join...
|
|
MCastRequest.MCastInfo.MCastInIf = context->MulticastInInterface; // Multicast If to rcv packets on
|
|
MCastRequest.MCastOutIf = context->MulticastOutInterface; // Multicast If to send packets on
|
|
|
|
//
|
|
// Now figure out the local interface. Note that the local interface
|
|
// specified in IP_ADD_MEMBERSHIP applies to that on which you wish
|
|
// to receive datagrams, while the local interface specified in
|
|
// RM_MULTICAST_IF applies to that from which to send multicast
|
|
// packets. If there is >1 local interface then we want to be
|
|
// consistent regarding the send/recv interfaces.
|
|
//
|
|
if (context->MulticastInInterface == DEFAULT_MULTICAST_INTERFACE)
|
|
{
|
|
TDI_REQUEST_QUERY_INFORMATION query;
|
|
char tdiAddressInfo[FIELD_OFFSET (TDI_ADDRESS_INFO, Address)
|
|
+ sizeof (TA_IP_ADDRESS)];
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
//
|
|
// App hasn't set RM_MULTICAST_IF, so retrieve the bound
|
|
// address and use that for the send & recv interfaces
|
|
//
|
|
RtlZeroMemory (&query, sizeof (query));
|
|
query.QueryType = TDI_QUERY_ADDRESS_INFO;
|
|
|
|
status = NtDeviceIoControlFile (TdiAddressObjectHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ioStatusBlock,
|
|
IOCTL_TDI_QUERY_INFORMATION,
|
|
&query,
|
|
sizeof (query),
|
|
tdiAddressInfo,
|
|
sizeof(tdiAddressInfo));
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
PTA_IP_ADDRESS pIpAddress = (PTA_IP_ADDRESS)
|
|
(tdiAddressInfo+FIELD_OFFSET (TDI_ADDRESS_INFO, Address));
|
|
|
|
if (MCastRequest.MCastInfo.MCastInIf != DEFAULT_MULTICAST_INTERFACE)
|
|
{
|
|
bSet_RM_MULTICAST_IF = TRUE;
|
|
MCastRequest.MCastInfo.MCastInIf = pIpAddress->Address[0].Address[0].in_addr;
|
|
context->MulticastInInterface = MCastRequest.MCastInfo.MCastInIf;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHPgm!WSHJoinLeaf: DeviceIoCtrl failed to QueryInformation, status=<%x>\n",
|
|
status);
|
|
}
|
|
}
|
|
|
|
err = NO_ERROR;
|
|
|
|
//
|
|
// If the Flags param indicates that caller is a sender only,
|
|
// then there's no point in actually joining the group (anyone
|
|
// can send to a multicast group, but it's only members of the
|
|
// group who recv the packets). So, just check to see if it
|
|
// is necessary to set the RM_MULTICAST_IF to remain consistent
|
|
// with the bound address.
|
|
//
|
|
// Otherwise, caller is a receiver (possibly a sender too), so
|
|
// we really do want to join the group.
|
|
//
|
|
if (Flags != JL_SENDER_ONLY)
|
|
{
|
|
PgmLog ("WSHJoinLeaf[JL_RECEIVER]: Address=<%x>, Connection=<%x>\n",
|
|
TdiAddressObjectHandle, TdiConnectionObjectHandle);
|
|
|
|
//
|
|
// Add ourselves as a receiver
|
|
//
|
|
MCastRequest.MCastPort = ((LPSOCKADDR_IN)Sockaddr)->sin_port;
|
|
err = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_JOIN_MCAST_LEAF,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
|
|
if (err == NO_ERROR)
|
|
{
|
|
//
|
|
// Record this fact in the leaf socket so we can drop membership
|
|
// when the leaf socket is closed.
|
|
//
|
|
context->MultipointLeaf = TRUE;
|
|
context->MultipointTarget = MCastRequest.MCastInfo.MCastIpAddr;
|
|
context->MultipointPort = ((LPSOCKADDR_IN)Sockaddr)->sin_port;
|
|
|
|
PgmLog ("WSHPgm!WSHJoinLeaf: JoinLeaf to <%x:%x> SUCCEEDed\n",
|
|
MCastRequest.MCastInfo.MCastIpAddr);
|
|
}
|
|
else
|
|
{
|
|
PgmError ("WSHPgm!WSHJoinLeaf: JoinLeaf to <%x> FAILed, err=<%d>\n",
|
|
MCastRequest.MCastInfo.MCastIpAddr, err);
|
|
}
|
|
}
|
|
|
|
if ((TdiConnectionObjectHandle) &&
|
|
(err == NO_ERROR) &&
|
|
(bSet_RM_MULTICAST_IF))
|
|
{
|
|
//
|
|
// If we have a TDI address object, set this option to
|
|
// the address object. If we don't have a TDI address
|
|
// object then we'll have to wait until after the socket
|
|
// is bound.
|
|
//
|
|
PgmLog ("WSHJoinLeaf[bSet_RM_MULTICAST_IF]: Address=<%x>, Connection=<%x>\n",
|
|
TdiAddressObjectHandle, TdiConnectionObjectHandle);
|
|
|
|
err = SetTdiInformation (TdiAddressObjectHandle,
|
|
IOCTL_PGM_WSH_ADD_RECEIVE_IF,
|
|
&MCastRequest,
|
|
sizeof (MCastRequest),
|
|
NULL,
|
|
0,
|
|
TRUE);
|
|
|
|
if (err == NO_ERROR)
|
|
{
|
|
PgmLog ("WSHPgm!WSHJoinLeaf: Set MCastIf=<%x> SUCCEEDed\n", MCastRequest.MCastInfo.MCastInIf);
|
|
context->MulticastInInterface = MCastRequest.MCastInfo.MCastInIf;
|
|
}
|
|
else
|
|
{
|
|
PgmLog ("WSHPgm!WSHJoinLeaf: Set MCastIf=<%x> FAILed, err=<%d>\n",
|
|
MCastRequest.MCastInfo.MCastInIf, err);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
|
|
} // WSHJoinLeaf
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Internal routines
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
BOOLEAN
|
|
IsTripleInList (
|
|
IN PMAPPING_TRIPLE List,
|
|
IN ULONG ListLength,
|
|
IN INT AddressFamily,
|
|
IN INT SocketType,
|
|
IN INT Protocol
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines whether the specified triple has an exact match in the
|
|
list of triples.
|
|
|
|
Arguments:
|
|
|
|
List - a list of triples (address family/socket type/protocol) to
|
|
search.
|
|
|
|
ListLength - the number of triples in the list.
|
|
|
|
AddressFamily - the address family to look for in the list.
|
|
|
|
SocketType - the socket type to look for in the list.
|
|
|
|
Protocol - the protocol to look for in the list.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the triple was found in the list, false if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
//
|
|
// Walk through the list searching for an exact match.
|
|
//
|
|
for (i = 0; i < ListLength; i++)
|
|
{
|
|
//
|
|
// If all three elements of the triple match, return indicating
|
|
// that the triple did exist in the list.
|
|
//
|
|
if (AddressFamily == List[i].AddressFamily &&
|
|
SocketType == List[i].SocketType &&
|
|
Protocol == List[i].Protocol)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The triple was not found in the list.
|
|
//
|
|
|
|
return FALSE;
|
|
} // IsTripleInList
|
|
|
|
//----------------------------------------------------------------------------
|
|
INT
|
|
NtStatusToSocketError (
|
|
IN NTSTATUS Status
|
|
)
|
|
{
|
|
|
|
switch ( Status ) {
|
|
|
|
case STATUS_PENDING:
|
|
ASSERT (FALSE);
|
|
return WSASYSCALLFAILURE;
|
|
|
|
case STATUS_INVALID_HANDLE:
|
|
case STATUS_OBJECT_TYPE_MISMATCH:
|
|
return WSAENOTSOCK;
|
|
|
|
case STATUS_INSUFFICIENT_RESOURCES:
|
|
case STATUS_PAGEFILE_QUOTA:
|
|
case STATUS_COMMITMENT_LIMIT:
|
|
case STATUS_WORKING_SET_QUOTA:
|
|
case STATUS_NO_MEMORY:
|
|
case STATUS_CONFLICTING_ADDRESSES:
|
|
case STATUS_QUOTA_EXCEEDED:
|
|
case STATUS_TOO_MANY_PAGING_FILES:
|
|
case STATUS_REMOTE_RESOURCES:
|
|
case STATUS_TOO_MANY_ADDRESSES:
|
|
return WSAENOBUFS;
|
|
|
|
case STATUS_SHARING_VIOLATION:
|
|
case STATUS_ADDRESS_ALREADY_EXISTS:
|
|
return WSAEADDRINUSE;
|
|
|
|
case STATUS_LINK_TIMEOUT:
|
|
case STATUS_IO_TIMEOUT:
|
|
case STATUS_TIMEOUT:
|
|
return WSAETIMEDOUT;
|
|
|
|
case STATUS_GRACEFUL_DISCONNECT:
|
|
return WSAEDISCON;
|
|
|
|
case STATUS_REMOTE_DISCONNECT:
|
|
case STATUS_CONNECTION_RESET:
|
|
case STATUS_LINK_FAILED:
|
|
case STATUS_CONNECTION_DISCONNECTED:
|
|
case STATUS_PORT_UNREACHABLE:
|
|
return WSAECONNRESET;
|
|
|
|
case STATUS_LOCAL_DISCONNECT:
|
|
case STATUS_TRANSACTION_ABORTED:
|
|
case STATUS_CONNECTION_ABORTED:
|
|
return WSAECONNABORTED;
|
|
|
|
case STATUS_BAD_NETWORK_PATH:
|
|
case STATUS_NETWORK_UNREACHABLE:
|
|
case STATUS_PROTOCOL_UNREACHABLE:
|
|
return WSAENETUNREACH;
|
|
|
|
case STATUS_HOST_UNREACHABLE:
|
|
return WSAEHOSTUNREACH;
|
|
|
|
case STATUS_CANCELLED:
|
|
case STATUS_REQUEST_ABORTED:
|
|
return WSAEINTR;
|
|
|
|
case STATUS_BUFFER_OVERFLOW:
|
|
case STATUS_INVALID_BUFFER_SIZE:
|
|
return WSAEMSGSIZE;
|
|
|
|
case STATUS_BUFFER_TOO_SMALL:
|
|
case STATUS_ACCESS_VIOLATION:
|
|
return WSAEFAULT;
|
|
|
|
case STATUS_DEVICE_NOT_READY:
|
|
case STATUS_REQUEST_NOT_ACCEPTED:
|
|
return WSAEWOULDBLOCK;
|
|
|
|
case STATUS_INVALID_NETWORK_RESPONSE:
|
|
case STATUS_NETWORK_BUSY:
|
|
case STATUS_NO_SUCH_DEVICE:
|
|
case STATUS_NO_SUCH_FILE:
|
|
case STATUS_OBJECT_PATH_NOT_FOUND:
|
|
case STATUS_OBJECT_NAME_NOT_FOUND:
|
|
case STATUS_UNEXPECTED_NETWORK_ERROR:
|
|
return WSAENETDOWN;
|
|
|
|
case STATUS_INVALID_CONNECTION:
|
|
return WSAENOTCONN;
|
|
|
|
case STATUS_REMOTE_NOT_LISTENING:
|
|
case STATUS_CONNECTION_REFUSED:
|
|
return WSAECONNREFUSED;
|
|
|
|
case STATUS_PIPE_DISCONNECTED:
|
|
return WSAESHUTDOWN;
|
|
|
|
case STATUS_INVALID_ADDRESS:
|
|
case STATUS_INVALID_ADDRESS_COMPONENT:
|
|
return WSAEADDRNOTAVAIL;
|
|
|
|
case STATUS_NOT_SUPPORTED:
|
|
case STATUS_NOT_IMPLEMENTED:
|
|
return WSAEOPNOTSUPP;
|
|
|
|
case STATUS_ACCESS_DENIED:
|
|
return WSAEACCES;
|
|
|
|
default:
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
#if DBG
|
|
DbgPrint ("SockNtStatusToSocketError: success status %lx "
|
|
"not mapped\n", Status );
|
|
#endif
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
#if DBG
|
|
DbgPrint ("SockNtStatusToSocketError: unable to map 0x%lX, returning\n", Status );
|
|
#endif
|
|
|
|
return WSAENOBUFS;
|
|
|
|
case STATUS_UNSUCCESSFUL:
|
|
case STATUS_INVALID_PARAMETER:
|
|
case STATUS_ADDRESS_CLOSED:
|
|
case STATUS_CONNECTION_INVALID:
|
|
case STATUS_ADDRESS_ALREADY_ASSOCIATED:
|
|
case STATUS_ADDRESS_NOT_ASSOCIATED:
|
|
case STATUS_CONNECTION_ACTIVE:
|
|
case STATUS_INVALID_DEVICE_STATE:
|
|
case STATUS_INVALID_DEVICE_REQUEST:
|
|
return WSAEINVAL;
|
|
|
|
}
|
|
|
|
} // NtStatusToSocketError
|
|
VOID
|
|
CompleteTdiActionApc (
|
|
IN PVOID ApcContext,
|
|
IN PIO_STATUS_BLOCK IoStatusBlock
|
|
)
|
|
{
|
|
//
|
|
// Just free the heap we allocated to hold the IO status block and
|
|
// the TDI action buffer. There is nothing we can do if the call
|
|
// failed.
|
|
//
|
|
|
|
RtlFreeHeap( RtlProcessHeap( ), 0, ApcContext );
|
|
|
|
} // CompleteTdiActionApc
|
|
|
|
|
|
|
|
INT
|
|
SetTdiInformation (
|
|
IN HANDLE TdiObjectHandle,
|
|
IN ULONG Ioctl,
|
|
IN PVOID InputBuffer,
|
|
IN ULONG InputBufferLength,
|
|
IN PVOID OutputBuffer,
|
|
IN ULONG OutputBufferLength,
|
|
IN BOOLEAN WaitForCompletion
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs a TDI action to the TCP/IP driver. A TDI action translates
|
|
into a streams T_OPTMGMT_REQ.
|
|
|
|
Arguments:
|
|
|
|
TdiConnectionObjectHandle - a TDI connection object on which to perform
|
|
the TDI action.
|
|
|
|
Entity - value to put in the tei_entity field of the TDIObjectID
|
|
structure.
|
|
|
|
Class - value to put in the toi_class field of the TDIObjectID
|
|
structure.
|
|
|
|
Type - value to put in the toi_type field of the TDIObjectID
|
|
structure.
|
|
|
|
Id - value to put in the toi_id field of the TDIObjectID structure.
|
|
|
|
InputBuffer - a pointer to a buffer to set as the information.
|
|
|
|
InputBufferLength - the length of the buffer.
|
|
|
|
WaitForCompletion - TRUE if we should wait for the TDI action to
|
|
complete, FALSE if we're at APC level and cannot do a wait.
|
|
|
|
Return Value:
|
|
|
|
INT - NO_ERROR, or a Windows Sockets error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PVOID completionApc;
|
|
PVOID apcContext;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
IO_STATUS_BLOCK *pIoStatusBlock;
|
|
|
|
if (WaitForCompletion || InputBufferLength>32)
|
|
{
|
|
//
|
|
// Allocate space to hold the TDI set information buffers and the IO
|
|
// status block. These cannot be stack variables in case we must
|
|
// return before the operation is complete.
|
|
//
|
|
pIoStatusBlock = RtlAllocateHeap (RtlProcessHeap( ),
|
|
0,
|
|
sizeof (*pIoStatusBlock));
|
|
if (pIoStatusBlock == NULL)
|
|
{
|
|
return WSAENOBUFS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pIoStatusBlock = (PIO_STATUS_BLOCK) &IoStatusBlock;
|
|
}
|
|
|
|
//
|
|
// If we need to wait for completion of the operation, create an
|
|
// event to wait on. If we can't wait for completion because we
|
|
// are being called at APC level, we'll use an APC routine to
|
|
// free the heap we allocated above.
|
|
//
|
|
|
|
if (WaitForCompletion)
|
|
{
|
|
completionApc = NULL;
|
|
apcContext = NULL;
|
|
}
|
|
else
|
|
{
|
|
completionApc = CompleteTdiActionApc;
|
|
apcContext = pIoStatusBlock;
|
|
}
|
|
|
|
//
|
|
// Make the actual TDI action call. The Streams TDI mapper will
|
|
// translate this into a TPI option management request for us and
|
|
// give it to .
|
|
//
|
|
IoStatusBlock.Status = STATUS_PENDING;
|
|
status = NtDeviceIoControlFile (TdiObjectHandle,
|
|
NULL,
|
|
completionApc,
|
|
apcContext,
|
|
pIoStatusBlock,
|
|
Ioctl,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer, // Use same buffer as output buffer!
|
|
OutputBufferLength);
|
|
|
|
//
|
|
// If the call pended and we were supposed to wait for completion,
|
|
// then wait.
|
|
//
|
|
if ( status == STATUS_PENDING && WaitForCompletion)
|
|
{
|
|
#if DBG
|
|
INT count=0;
|
|
#endif
|
|
|
|
while (pIoStatusBlock->Status==STATUS_PENDING)
|
|
{
|
|
LARGE_INTEGER timeout;
|
|
//
|
|
// Wait one millisecond
|
|
//
|
|
timeout.QuadPart = -1i64*1000i64*10i64;
|
|
NtDelayExecution (FALSE, &timeout);
|
|
#if DBG
|
|
if (count++>10*1000)
|
|
{
|
|
DbgPrint ("WSHPGM: Waiting for PGM IOCTL completion for more than 10 seconds!!!!\n");
|
|
DbgBreakPoint ();
|
|
}
|
|
#endif
|
|
}
|
|
status = pIoStatusBlock->Status;
|
|
}
|
|
|
|
if ((pIoStatusBlock != (PIO_STATUS_BLOCK) &IoStatusBlock) &&
|
|
(WaitForCompletion || !NT_SUCCESS(status)))
|
|
{
|
|
RtlFreeHeap (RtlProcessHeap( ), 0, pIoStatusBlock);
|
|
}
|
|
|
|
if (NT_SUCCESS (status))
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
return NtStatusToSocketError (status);
|
|
}
|
|
|
|
} // SetTdiInformation
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//----------------------------------------------------------------------------
|