5861 lines
154 KiB
C++
5861 lines
154 KiB
C++
/*++
|
||
|
||
Copyright (C) Microsoft Corporation, 1996 - 1999
|
||
|
||
Module Name:
|
||
|
||
wstrans.cxx
|
||
|
||
Abstract:
|
||
|
||
Winsock connection transport interface.
|
||
|
||
Author:
|
||
|
||
Mario Goertzel [MarioGo]
|
||
|
||
|
||
Revision History:
|
||
|
||
MarioGo 3/18/1996 Bits 'n pieces
|
||
MarioGo 12/1997 Async and client parts
|
||
KamenM Aug 2/2000 IPv6 Support added - rewrote parts of it
|
||
|
||
--*/
|
||
|
||
#include <precomp.hxx>
|
||
extern "C" {
|
||
#include <iphlpapi.h>
|
||
}
|
||
#include <CharConv.hxx>
|
||
|
||
//
|
||
// Globals
|
||
//
|
||
|
||
BOOL fWinsockLoaded = FALSE;
|
||
|
||
const WS_TRANS_INFO WsTransportTable[] =
|
||
// indexed by protocol ID
|
||
{
|
||
|
||
{
|
||
0
|
||
},
|
||
|
||
// TCP
|
||
{
|
||
AF_INET,
|
||
SOCK_STREAM,
|
||
IPPROTO_TCP,
|
||
sizeof(SOCKADDR_IN),
|
||
FALSE,
|
||
TRUE,
|
||
TRUE, TRUE, FALSE, FALSE
|
||
},
|
||
|
||
#ifdef SPX_ON
|
||
// SPX
|
||
{
|
||
AF_IPX,
|
||
SOCK_STREAM,
|
||
NSPROTO_SPXII,
|
||
sizeof(SOCKADDR_IPX),
|
||
FALSE,
|
||
FALSE,
|
||
FALSE, FALSE, FALSE, FALSE
|
||
},
|
||
#else
|
||
{
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0, 0, 0
|
||
},
|
||
|
||
#endif
|
||
|
||
// NMP - not winsock.
|
||
{
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0, 0, 0
|
||
},
|
||
|
||
#ifdef NETBIOS_ON
|
||
// NBF
|
||
{
|
||
AF_NETBIOS,
|
||
SOCK_SEQPACKET,
|
||
-1, // Protocol is -1*(LANA),
|
||
sizeof(SOCKADDR_NB),
|
||
TRUE,
|
||
FALSE,
|
||
FALSE, FALSE, FALSE, FALSE
|
||
},
|
||
|
||
// NBT
|
||
{
|
||
AF_NETBIOS,
|
||
0,
|
||
-1, // Protocol is -1*(LANA)
|
||
sizeof(SOCKADDR_NB),
|
||
TRUE,
|
||
FALSE,
|
||
FALSE, FALSE, FALSE, FALSE
|
||
},
|
||
|
||
// NBI
|
||
{
|
||
AF_NETBIOS,
|
||
SOCK_SEQPACKET,
|
||
-1, // Protocol is -1*(LANA)
|
||
sizeof(SOCKADDR_NB),
|
||
TRUE,
|
||
FALSE,
|
||
FALSE, FALSE, FALSE, FALSE
|
||
},
|
||
#else
|
||
// NBF
|
||
{
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0, 0, 0
|
||
},
|
||
|
||
// NBT
|
||
{
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0, 0, 0
|
||
},
|
||
|
||
// NBI
|
||
{
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0, 0, 0
|
||
},
|
||
#endif
|
||
|
||
#ifdef APPLETALK_ON
|
||
// DSP
|
||
{
|
||
AF_APPLETALK,
|
||
SOCK_RDM,
|
||
ATPROTO_ADSP,
|
||
sizeof(SOCKADDR_AT),
|
||
FALSE,
|
||
FALSE,
|
||
FALSE, FALSE, FALSE, FALSE
|
||
},
|
||
#else
|
||
// DSP
|
||
{
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0, 0, 0
|
||
},
|
||
#endif
|
||
|
||
// SPP
|
||
{
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0, 0, 0
|
||
},
|
||
|
||
// HTTP
|
||
{
|
||
AF_INET,
|
||
SOCK_STREAM,
|
||
IPPROTO_TCP,
|
||
sizeof(SOCKADDR_IN),
|
||
FALSE,
|
||
TRUE,
|
||
TRUE, TRUE, FALSE, FALSE
|
||
},
|
||
|
||
// UDP
|
||
{
|
||
AF_INET,
|
||
SOCK_DGRAM,
|
||
IPPROTO_UDP,
|
||
sizeof(SOCKADDR_IN),
|
||
FALSE,
|
||
FALSE,
|
||
FALSE, FALSE, FALSE, FALSE
|
||
},
|
||
|
||
#ifdef IPX_ON
|
||
// IPX
|
||
{
|
||
AF_IPX,
|
||
SOCK_DGRAM,
|
||
NSPROTO_IPX,
|
||
sizeof(SOCKADDR_IPX),
|
||
FALSE,
|
||
FALSE,
|
||
FALSE, FALSE, FALSE, FALSE
|
||
},
|
||
#else
|
||
// IPX
|
||
{
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0, 0, 0, 0
|
||
},
|
||
#endif
|
||
|
||
// CDP (Cluster datagram protocol)
|
||
{
|
||
AF_CLUSTER,
|
||
SOCK_DGRAM,
|
||
CLUSPROTO_CDP,
|
||
sizeof(SOCKADDR_CLUSTER),
|
||
FALSE,
|
||
FALSE,
|
||
FALSE, FALSE, FALSE, FALSE
|
||
},
|
||
|
||
// MSMQ - not winsock.
|
||
{
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0, 0, 0
|
||
},
|
||
|
||
// TCP over IPv6
|
||
{
|
||
AF_INET6,
|
||
SOCK_STREAM,
|
||
IPPROTO_TCP,
|
||
sizeof(SOCKADDR_IN6),
|
||
FALSE,
|
||
TRUE,
|
||
TRUE, TRUE, FALSE, FALSE
|
||
},
|
||
|
||
// HTTPv2
|
||
{
|
||
AF_INET,
|
||
SOCK_STREAM,
|
||
IPPROTO_TCP,
|
||
sizeof(SOCKADDR_IN),
|
||
FALSE, // fNetbios
|
||
FALSE, // fCheckShutdowns
|
||
TRUE, // fSetNoDelay
|
||
TRUE, // fSetKeepAlive
|
||
FALSE, // fSetRecvBuffer
|
||
FALSE // fSetSendBuffer
|
||
},
|
||
};
|
||
|
||
const DWORD cWsTransportTable = sizeof(WsTransportTable)/sizeof(WS_TRANS_INFO);
|
||
|
||
const DWORD cTcpTimeoutDefault = 120 * 60 * 1000; // Normal TCP/IP timeout, 120 minutes
|
||
|
||
const UUID WS_ADDRESS::ExtensionFunctionsUuids[] = {WSAID_ACCEPTEX, WSAID_GETACCEPTEXSOCKADDRS};
|
||
const int WS_ADDRESS::AcceptExFunctionId = 0;
|
||
const int WS_ADDRESS::GetAcceptExSockAddressFunctionId = 1;
|
||
|
||
#define FIRST_EXTENSION_FUNCTION_CODE (WS_ADDRESS::AcceptExFunctionId)
|
||
#define LAST_EXTENSION_FUNCTION_CODE (WS_ADDRESS::GetAcceptExSockAddressFunctionId)
|
||
|
||
inline BOOL IsNetbiosProtocol(PROTOCOL_ID id)
|
||
{
|
||
#ifdef NETBIOS_ON
|
||
return ((id == NBT) || (id == NBF) || (id == NBI));
|
||
#else
|
||
return FALSE;
|
||
#endif
|
||
}
|
||
|
||
void
|
||
TCPResolverHint::GetResolverHint (
|
||
OUT BOOL *fIPv4Hint,
|
||
OUT WS_SOCKADDR *sa
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Retrieves the resolver hint from the runtime supplied hint
|
||
(the this object).
|
||
|
||
Arguments:
|
||
|
||
fIPv4Hint - on output true if the store hint was about IPv4
|
||
sa - on output, the IP address is retrieved from the hint
|
||
and stored in sa.
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
SOCKADDR_IN6 *IPv6Address = (SOCKADDR_IN6 *)sa;
|
||
|
||
*fIPv4Hint = fIPv4HintValid;
|
||
if (fIPv4HintValid)
|
||
{
|
||
((SOCKADDR_IN *)sa)->sin_addr.s_addr = u.IPv4Hint;
|
||
}
|
||
else
|
||
{
|
||
IPv6Address->sin6_flowinfo = 0;
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) ) = *((u_long *)(u.IPv6Hint.u.Word) );
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) + 1) = *((u_long *)(u.IPv6Hint.u.Word) + 1);
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) + 2) = *((u_long *)(u.IPv6Hint.u.Word) + 2);
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) + 3) = *((u_long *)(u.IPv6Hint.u.Word) + 3);
|
||
IPv6Address->sin6_scope_id = 0;
|
||
}
|
||
}
|
||
|
||
void
|
||
TCPResolverHint::SetResolverHint (
|
||
IN BOOL fIPv4Hint,
|
||
IN WS_SOCKADDR *sa
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the resolver hint in the runtime supplied hint
|
||
(the this object).
|
||
|
||
Arguments:
|
||
|
||
fIPv4Hint - true if the stored hint is about IPv4
|
||
sa - the IP address is retrieved from sa
|
||
and is stored in the hint.
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
SOCKADDR_IN6 *IPv6Address = (SOCKADDR_IN6 *)sa;
|
||
|
||
fIPv4HintValid = fIPv4Hint;
|
||
if (fIPv4HintValid)
|
||
{
|
||
u.IPv4Hint = ((SOCKADDR_IN *)sa)->sin_addr.s_addr;
|
||
}
|
||
else
|
||
{
|
||
*((u_long *)(u.IPv6Hint.u.Word) ) = *((u_long *)(IPv6Address->sin6_addr.s6_addr) );
|
||
*((u_long *)(u.IPv6Hint.u.Word) + 1) = *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 1);
|
||
*((u_long *)(u.IPv6Hint.u.Word) + 2) = *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 2);
|
||
*((u_long *)(u.IPv6Hint.u.Word) + 3) = *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 3);
|
||
}
|
||
}
|
||
|
||
BOOL WS_ADDRESS::GetExtensionFunctionPointers(SOCKET sock)
|
||
{
|
||
int i;
|
||
|
||
for (i = FIRST_EXTENSION_FUNCTION_CODE; i <= LAST_EXTENSION_FUNCTION_CODE; i ++)
|
||
{
|
||
if (GetExtensionFunctionPointerForFunction(sock, i) == FALSE)
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL WS_ADDRESS::GetExtensionFunctionPointerForFunction(SOCKET sock, int nFunctionCode)
|
||
{
|
||
DWORD dwBytesReturned;
|
||
int retval;
|
||
|
||
ASSERT(nFunctionCode >= FIRST_EXTENSION_FUNCTION_CODE);
|
||
ASSERT(nFunctionCode <= LAST_EXTENSION_FUNCTION_CODE);
|
||
ASSERT(sizeof(ExtensionFunctionPointers)/sizeof(ExtensionFunctionPointers[0]) == (LAST_EXTENSION_FUNCTION_CODE - FIRST_EXTENSION_FUNCTION_CODE + 1));
|
||
|
||
retval = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, (void *) &ExtensionFunctionsUuids[nFunctionCode],
|
||
sizeof(UUID), (void *) &ExtensionFunctionPointers[nFunctionCode], sizeof(void *), &dwBytesReturned,
|
||
NULL, NULL);
|
||
|
||
if (retval == SOCKET_ERROR)
|
||
return FALSE;
|
||
|
||
ASSERT(dwBytesReturned == sizeof(void *));
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
//
|
||
// General winsock interfaces
|
||
//
|
||
|
||
RPC_STATUS WS_CONNECTION::Abort(void)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Closes a connection, will be called only before WS_Close() and
|
||
maybe called by several threads at once. It must also handle
|
||
the case where another thread is about to start IO on the connection.
|
||
|
||
Arguments:
|
||
|
||
Connection - pointer to a server connection object to abort.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
--*/
|
||
|
||
{
|
||
if (InterlockedIncrement(&fAborted) != 1)
|
||
{
|
||
// Another thread beat us to it. Normal during
|
||
// a call to WS_Close.
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
I_RpcLogEvent(SU_TRANS_CONN, EV_ABORT, this, ULongToPtr(GetLastError()), 0, 1, 2);
|
||
|
||
// Wait for any threads which are starting IO to do so.
|
||
while(IsIoStarting())
|
||
Sleep(1);
|
||
|
||
RTL_SOFT_ASSERT(fAborted != 0 && IsIoStarting() == 0);
|
||
|
||
if (type & SERVER)
|
||
{
|
||
ASSERT(pAddress != NULL);
|
||
}
|
||
|
||
if (Conn.Socket)
|
||
{
|
||
closesocket(Conn.Socket);
|
||
Conn.Socket = 0;
|
||
}
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
VOID
|
||
WS_DeactivateAddress (
|
||
IN WS_ADDRESS *pAddress
|
||
)
|
||
/*++
|
||
Function Name:WS_DeactivateAddress
|
||
|
||
Parameters:
|
||
|
||
Note:
|
||
|
||
Doesn't deal with multiple transport addresses/runtime address
|
||
case in netbios or TCP/IP bound to a subset of NICs case. These
|
||
cases currently don't PnP.
|
||
|
||
Description:
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
{
|
||
switch (pAddress->id)
|
||
{
|
||
case TCP:
|
||
case TCP_IPv6:
|
||
case HTTP:
|
||
#ifdef SPX_ON
|
||
case SPX:
|
||
#endif
|
||
#ifdef APPLETALK_ON
|
||
case DSP:
|
||
#endif
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// Don't deactivate the other guys
|
||
//
|
||
#ifdef NETBIOS_ON
|
||
ASSERT((pAddress->id == NMP)
|
||
|| (pAddress->id == NBF)
|
||
|| (pAddress->id == NBT)
|
||
|| (pAddress->id == NBI)
|
||
|| (pAddress->id == CDP)
|
||
);
|
||
#else
|
||
ASSERT((pAddress->id == NMP)
|
||
|| (pAddress->id == CDP)
|
||
);
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
if (InterlockedIncrement(&pAddress->fAborted) != 1)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (pAddress->ListenSocket)
|
||
{
|
||
closesocket(pAddress->ListenSocket);
|
||
pAddress->ListenSocket = 0;
|
||
}
|
||
|
||
if (pAddress->ConnectionSocket)
|
||
{
|
||
closesocket(pAddress->ConnectionSocket);
|
||
pAddress->ConnectionSocket = 0;
|
||
}
|
||
}
|
||
|
||
RPC_STATUS
|
||
WS_ServerListenCommon (
|
||
IN WS_ADDRESS *pAddress
|
||
);
|
||
|
||
USHORT
|
||
WS_GetPortForTCPAddressOnAddressRestart (
|
||
IN WS_ADDRESS *pAddress
|
||
)
|
||
/*++
|
||
Function Name: WS_GetPortForTCPAddressOnAddressRestart
|
||
|
||
Parameters:
|
||
pAddress - the address for which we need to get the port
|
||
|
||
Description:
|
||
When an address is restarted and it happens to be a TCP
|
||
address, we need to call this function to get the port to
|
||
be used. This is necessary so that if we are in a dual
|
||
transport configuration with an active address we can get
|
||
the port from the other address in order to maintain
|
||
consistency
|
||
|
||
Returns:
|
||
the port number or 0 (means no known ports or not a dual
|
||
transport configuration)
|
||
|
||
--*/
|
||
{
|
||
WS_ADDRESS *NextAddress;
|
||
USHORT PortNumber = 0;
|
||
|
||
ASSERT((pAddress->id == TCP) || (pAddress->id == TCP_IPv6));
|
||
ASSERT(pAddress->fDynamicEndpoint);
|
||
|
||
if (pAddress->pFirstAddress != NULL)
|
||
{
|
||
NextAddress = (WS_ADDRESS *)pAddress->pFirstAddress;
|
||
}
|
||
else
|
||
NextAddress = pAddress;
|
||
|
||
|
||
while (NextAddress != NULL)
|
||
{
|
||
ASSERT(NextAddress->fDynamicEndpoint);
|
||
ASSERT((NextAddress->id == TCP) || (NextAddress->id == TCP_IPv6));
|
||
if (!NextAddress->fAborted)
|
||
{
|
||
PortNumber = RpcpGetIpPort(&NextAddress->ListenAddr);
|
||
ASSERT(PortNumber != 0);
|
||
break;
|
||
}
|
||
NextAddress = (WS_ADDRESS *)NextAddress->pNextAddress;
|
||
}
|
||
|
||
return PortNumber;
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
WS_ReactivateAddress (
|
||
IN WS_ADDRESS *pAddress
|
||
)
|
||
/*++
|
||
Function Name:WS_ReactivateAddress
|
||
|
||
Parameters:
|
||
|
||
Description:
|
||
|
||
Returns:
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS Status;
|
||
WS_SOCKADDR *sockaddr = &(pAddress->ListenAddr);
|
||
LIST_ENTRY OldEntry;
|
||
|
||
//
|
||
// If the endpoint is dynamic, clear out the endpoint
|
||
//
|
||
switch (pAddress->id)
|
||
{
|
||
case TCP:
|
||
case TCP_IPv6:
|
||
if (pAddress->fDynamicEndpoint)
|
||
{
|
||
RpcpSetIpPort(sockaddr, WS_GetPortForTCPAddressOnAddressRestart(pAddress));
|
||
}
|
||
break;
|
||
|
||
case HTTP:
|
||
if (pAddress->fDynamicEndpoint)
|
||
{
|
||
RpcpSetIpPort(sockaddr, 0);
|
||
}
|
||
break;
|
||
|
||
#ifdef SPX_ON
|
||
case SPX:
|
||
if (pAddress->fDynamicEndpoint)
|
||
{
|
||
sockaddr->ipxaddr.sa_socket = 0;
|
||
}
|
||
break;
|
||
#endif
|
||
|
||
#ifdef APPLETALK_ON
|
||
case DSP:
|
||
// Don't need to null out the endpoint
|
||
break;
|
||
#endif
|
||
|
||
default:
|
||
VALIDATE(pAddress->id)
|
||
{
|
||
NMP,
|
||
#ifdef NETBIOS_ON
|
||
NBF,
|
||
NBT,
|
||
NBI,
|
||
#endif
|
||
CDP
|
||
} END_VALIDATE;
|
||
//
|
||
// Don't reactivate the other guys
|
||
//
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
// save the old entry, because WS_ServerListenCommon overwrites it
|
||
OldEntry.Flink = pAddress->ObjectList.Flink;
|
||
OldEntry.Blink = pAddress->ObjectList.Blink;
|
||
|
||
Status = WS_ServerListenCommon (pAddress);
|
||
if (Status == RPC_S_OK)
|
||
{
|
||
pAddress->fAborted = 0;
|
||
pAddress->GetExtensionFunctionPointers(pAddress->ListenSocket);
|
||
}
|
||
else if (Status == RPC_P_ADDRESS_FAMILY_INVALID)
|
||
{
|
||
Status = RPC_S_PROTSEQ_NOT_SUPPORTED;
|
||
}
|
||
|
||
// restore the old entry, because WS_ServerListenCommon has overwritten it
|
||
pAddress->ObjectList.Flink = OldEntry.Flink;
|
||
pAddress->ObjectList.Blink = OldEntry.Blink;
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
WS_Close(
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
IN BOOL fDontFlush
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called once when the connection object is to be really deleted.
|
||
At this point there will be no async IO pending on the connection.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - The connection object to close
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
--*/
|
||
|
||
{
|
||
SOCKET s;
|
||
WS_CONNECTION *p = (WS_CONNECTION *)ThisConnection;
|
||
|
||
p->WS_CONNECTION::Abort();
|
||
|
||
if (p->iLastRead)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "Closing connection %p with left over data (%d) %p \n",
|
||
p,
|
||
p->iLastRead,
|
||
p->pReadBuffer));
|
||
}
|
||
|
||
// Wait for the pending receive, if any, to actually complete.
|
||
|
||
if ((p->type & TYPE_MASK) == CLIENT)
|
||
{
|
||
// these operations don't apply to all netbios flavors - they
|
||
// apply to protocols which interop with OSF servers only
|
||
// (because of the check for shutdown)
|
||
if (!IsNetbiosProtocol(p->id))
|
||
{
|
||
PWS_CCONNECTION pcc = (PWS_CCONNECTION)ThisConnection;
|
||
if (pcc->fReceivePending)
|
||
{
|
||
UTIL_WaitForSyncIO(&pcc->Read.ol,
|
||
FALSE,
|
||
INFINITE);
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
// Now free the read buffer
|
||
|
||
TransConnectionFreePacket(ThisConnection, p->pReadBuffer);
|
||
p->pReadBuffer = 0;
|
||
|
||
TransportProtocol::RemoveObjectFromProtocolList((BASE_ASYNC_OBJECT *) ThisConnection);
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
BOOL
|
||
WS_ProtectListeningSocket(
|
||
IN SOCKET sock,
|
||
IN BOOL newValue
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the SO_EXCLUSIVEADDRUSE socket option on the sock parameter. This
|
||
prevents another process from using same port # and stealing our
|
||
connections.
|
||
|
||
Note: This option can only be set by administrators. This routine has no
|
||
affect when called by non-administrator.
|
||
|
||
Arguments:
|
||
|
||
sock - The socket to protect
|
||
newValue - TRUE if the socket is to be protected, FALSE if it is to be unprotected
|
||
|
||
Return Value:
|
||
|
||
0 if it succeeds, 1 if it fails
|
||
|
||
--*/
|
||
{
|
||
int fSocketExclusive = newValue;
|
||
int rval;
|
||
|
||
rval = setsockopt(sock,
|
||
SOL_SOCKET,
|
||
SO_EXCLUSIVEADDRUSE,
|
||
(const char *)&fSocketExclusive,
|
||
sizeof(fSocketExclusive));
|
||
|
||
if (rval != 0)
|
||
{
|
||
// Non-administrators fail with WSAEACCES.
|
||
|
||
if (GetLastError() != WSAEACCES)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"Unable to protect listening socket %d\n",
|
||
GetLastError()));
|
||
}
|
||
}
|
||
|
||
return rval;
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
WS_CheckForShutdowns(
|
||
PWS_CCONNECTION p
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
When a connection is idle OSF DCE 1.1+ machines will try to reduce
|
||
resource usage by shutting down the connection. This is the place
|
||
in the DCE RPC protocol where the servers send data to the client
|
||
asychronously to the client sending a request.
|
||
When a server decides to shutdown a connection it send three
|
||
PDUs of the rpc_shutdown type. Then, if there are no open context
|
||
handles on the connection, it will gracefully close the connection.
|
||
Note: If there are context handles open on the connection then
|
||
the connection is not acutally closed, but the shutdown PDUs are
|
||
still sent.
|
||
|
||
Algorithm:
|
||
|
||
allocate a buffer large enough for a shutdown PDU.
|
||
|
||
loop (i = 1 to 4 step 1)
|
||
submit an async receive on the connection.
|
||
If the receive doesn't complete immediately, return.
|
||
|
||
type = PDU->type.
|
||
|
||
If type != shutdown, save the PDU in the connection and return.
|
||
|
||
goto Loop:
|
||
|
||
If we get here it means we got too many shutdown PDUs. ASSERT and
|
||
close the connection.
|
||
|
||
Arguments:
|
||
|
||
p - The connection to check for shutdowns on.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_P_RECEIVE_FAILED
|
||
RPC_P_SEND_FAILED
|
||
|
||
--*/
|
||
|
||
{
|
||
RPC_STATUS status;
|
||
BUFFER buffer;
|
||
UINT length;
|
||
HANDLE hEvent;
|
||
|
||
if (p->iLastRead == 0)
|
||
{
|
||
// The server may have sent us a shutdown packet while the client
|
||
// was idle. We'll submit a recv now and check the result without
|
||
// waiting.
|
||
//
|
||
// OSF servers will send 3 shutdown packets and then close the
|
||
// connection. We try to submit four receives, this way if the
|
||
// connection has already been closed we can fail here.
|
||
|
||
// Allocate a packet
|
||
p->pReadBuffer = TransConnectionAllocatePacket(p, p->iPostSize);
|
||
|
||
if (NULL == p->pReadBuffer)
|
||
{
|
||
p->WS_CONNECTION::Abort();
|
||
return(RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
p->maxReadBuffer = p->iPostSize;
|
||
}
|
||
else
|
||
{
|
||
ASSERT(p->pReadBuffer);
|
||
}
|
||
|
||
InitReadEvent(p);
|
||
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
status = CO_SubmitSyncRead(p, &buffer, &length);
|
||
|
||
if (status == RPC_S_OK)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "Shutdown check completed!\n"));
|
||
|
||
PCONN_RPC_HEADER phdr = (PCONN_RPC_HEADER)buffer;
|
||
|
||
switch (MessageType(phdr))
|
||
{
|
||
case rpc_shutdown:
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "Received a shutdown\n"));
|
||
|
||
p->fShutdownReceived = TRUE;
|
||
|
||
// Reset the buffers and try again.
|
||
if (p->pReadBuffer == 0)
|
||
{
|
||
p->pReadBuffer = buffer;
|
||
p->maxReadBuffer = p->iPostSize;
|
||
}
|
||
// else
|
||
// it is possible that by now all shutdowns
|
||
// are coalesced in memory - in this case
|
||
// pReadbuffer and maxReadBuffer are already
|
||
// set and there is nothing for us to do here -
|
||
// just loop around and get them
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_P_SEND_FAILED,
|
||
EEInfoDLWSCheckForShutdowns10,
|
||
i);
|
||
break;
|
||
|
||
case rpc_fault:
|
||
// Io pending - don't free the buffer.
|
||
p->fReceivePending = TRUE;
|
||
p->pReadBuffer = buffer;
|
||
p->maxReadBuffer = p->iPostSize;
|
||
|
||
if (((CONN_RPC_FAULT *) buffer)->status == NCA_STATUS_PROTO_ERROR)
|
||
{
|
||
//
|
||
// This can happen if the server is NT 4.0 and it received a cancel
|
||
// after a call completed.
|
||
//
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "Received an out of sequence packet: %p %p\n",
|
||
p,
|
||
phdr));
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_P_SEND_FAILED,
|
||
EEInfoDLWSCheckForShutdowns20,
|
||
(ULONG)((CONN_RPC_FAULT *) buffer)->status,
|
||
(ULONG)i);
|
||
goto Cleanup;
|
||
}
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_P_RECEIVE_COMPLETE,
|
||
EEInfoDLWSCheckForShutdowns30,
|
||
(ULONG)((CONN_RPC_FAULT *) buffer)->status,
|
||
(ULONG)i);
|
||
|
||
return RPC_P_RECEIVE_COMPLETE;
|
||
|
||
default:
|
||
// Got something else - this is probably a protocol error.
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "Received an out of sequence packet: %p %p\n",
|
||
p,
|
||
phdr));
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_P_SEND_FAILED,
|
||
EEInfoDLWSCheckForShutdowns40,
|
||
(ULONG)MessageType(phdr),
|
||
(ULONG)i);
|
||
|
||
ASSERT(0);
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (status == RPC_P_IO_PENDING)
|
||
{
|
||
// Io pending - don't free the buffer.
|
||
p->fReceivePending = TRUE;
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSCheckForShutdowns50,
|
||
i);
|
||
return(status);
|
||
}
|
||
}
|
||
|
||
Cleanup:
|
||
p->WS_CONNECTION::Abort();
|
||
return(RPC_P_SEND_FAILED);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
WS_SyncSend(
|
||
IN RPC_TRANSPORT_CONNECTION Connection,
|
||
IN UINT BufferLength,
|
||
IN BUFFER Buffer,
|
||
IN BOOL fDisableShutdownCheck,
|
||
IN BOOL fDisableCancelCheck,
|
||
ULONG Timeout
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sends a message on the connection. This method must appear
|
||
to be synchronous from the callers perspective.
|
||
|
||
Note: This routine must check for OSF DCE shutdown PDUs
|
||
on TCP/IP to avoid interop problems.
|
||
|
||
Arguments:
|
||
|
||
Connection - The connection of send to.
|
||
BufferLength - The size of the buffer.
|
||
Buffer - The data to sent.
|
||
fDisableShutdownCheck - Normally FALSE, when true this disables
|
||
the transport check for async shutdown PDUs. This is needed
|
||
when sending the third leg.
|
||
|
||
|
||
Return Value:
|
||
|
||
RPC_P_SEND_FAILED - Connection will be closed if this is returned.
|
||
|
||
RPC_S_OK - Data sent
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD bytes;
|
||
RPC_STATUS status;
|
||
|
||
PWS_CCONNECTION p = (PWS_CCONNECTION)Connection;
|
||
ASSERT(!IsNetbiosProtocol(p->id));
|
||
|
||
// Note: this can be called on SERVER connections, too.
|
||
// All references to CLIENT-ONLY members must be guarded with
|
||
// (p->type & CLIENT).
|
||
|
||
//
|
||
// OSF 1.1(+) server's send, asynchronously to the client sending a
|
||
// request, shutdown PDUs on "idle" connections. Here we check for
|
||
// shutdown PDUs in order to allow the client to retry the call on
|
||
// another connection if this one has closed.
|
||
//
|
||
|
||
if ( ((p->type & TYPE_MASK) == CLIENT)
|
||
&& (FALSE == p->fCallStarted)
|
||
&& WsTransportTable[p->id].fCheckShutdowns
|
||
&& (fDisableShutdownCheck == FALSE)
|
||
&& (GetTickCount() - p->dwLastCallTime) > MILLISECONDS_BEFORE_PEEK)
|
||
{
|
||
p->fShutdownReceived = FALSE;
|
||
|
||
status = WS_CheckForShutdowns(p);
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
VALIDATE(status)
|
||
{
|
||
RPC_P_RECEIVE_FAILED,
|
||
RPC_P_CONNECTION_SHUTDOWN,
|
||
RPC_P_SEND_FAILED,
|
||
RPC_S_OUT_OF_MEMORY,
|
||
RPC_P_RECEIVE_COMPLETE
|
||
} END_VALIDATE;
|
||
|
||
if (status == RPC_P_RECEIVE_COMPLETE)
|
||
{
|
||
return status;
|
||
}
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_P_SEND_FAILED,
|
||
EEInfoDLWSSyncSend10);
|
||
return(RPC_P_SEND_FAILED);
|
||
}
|
||
|
||
// There is no need to to this again until SyncRecv is called.
|
||
p->fCallStarted = TRUE;
|
||
}
|
||
|
||
HANDLE hEvent = I_RpcTransGetThreadEvent();
|
||
|
||
p->StartingWriteIO();
|
||
|
||
if (p->fAborted)
|
||
{
|
||
p->WriteIOFinished();
|
||
return(RPC_P_SEND_FAILED);
|
||
}
|
||
|
||
// Setting the low bit of the event indicates that the write
|
||
// completion should NOT be sent to the i/o completion port.
|
||
OVERLAPPED olWrite;
|
||
olWrite.Internal = 0;
|
||
olWrite.InternalHigh = 0;
|
||
olWrite.Offset = 0;
|
||
olWrite.OffsetHigh = 0;
|
||
olWrite.hEvent = (HANDLE) ((ULONG_PTR)hEvent | 0x1);
|
||
|
||
#ifdef _INTERNAL_RPC_BUILD_
|
||
if (gpfnFilter)
|
||
{
|
||
(*gpfnFilter) (Buffer, BufferLength, 0);
|
||
}
|
||
#endif
|
||
|
||
status = p->Send(p->Conn.Handle,
|
||
Buffer,
|
||
BufferLength,
|
||
&bytes,
|
||
&olWrite
|
||
);
|
||
|
||
p->WriteIOFinished();
|
||
|
||
if (status == RPC_S_OK)
|
||
{
|
||
ASSERT(bytes == BufferLength);
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
if (status == ERROR_IO_PENDING)
|
||
{
|
||
// if fDisableCancelCheck, make the thread wait non-alertably,
|
||
// otherwise, make it wait alertably.
|
||
status = UTIL_GetOverlappedResultEx(Connection,
|
||
&olWrite,
|
||
&bytes,
|
||
!fDisableCancelCheck,
|
||
Timeout);
|
||
|
||
if (status == RPC_S_OK)
|
||
{
|
||
ASSERT(bytes == BufferLength);
|
||
return(RPC_S_OK);
|
||
}
|
||
}
|
||
|
||
ASSERT(status != RPC_S_OK);
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSSyncSend20);
|
||
|
||
p->WS_CONNECTION::Abort();
|
||
|
||
if ((status == RPC_S_CALL_CANCELLED) || (status == RPC_P_TIMEOUT))
|
||
{
|
||
// Wait for the write to finish. Since we closed the
|
||
// connection this won't take very long.
|
||
UTIL_WaitForSyncIO(&olWrite,
|
||
FALSE,
|
||
INFINITE);
|
||
}
|
||
else
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
RPC_P_SEND_FAILED,
|
||
EEInfoDLWSSyncSend30);
|
||
|
||
status = RPC_P_SEND_FAILED;
|
||
}
|
||
|
||
return(status);
|
||
}
|
||
|
||
VOID
|
||
WS_P_SetKeepAliveTimeout(
|
||
IN SOCKET Socket,
|
||
IN BOOL OnOff,
|
||
IN UINT KATime,
|
||
IN UINT KAInterval = 5000 OPTIONAL)
|
||
/*++
|
||
|
||
Arguments:
|
||
|
||
Socket - The socket to set the keepalive timeout on
|
||
OnOff - TRUE to turn if on. FALSE to turn it off
|
||
KATime - The timeout in milliseconds for the
|
||
first keepalive packet
|
||
KAInterval - The timeout in milliseconds for the
|
||
subsequent keepalive packets
|
||
|
||
--*/
|
||
{
|
||
int r;
|
||
tcp_keepalive tcpka;
|
||
DWORD t;
|
||
|
||
// make sure we indeed get TRUE of FALSE - we don't know how Winsock will
|
||
// take it otherwise
|
||
ASSERT((OnOff == TRUE) || (OnOff == FALSE));
|
||
|
||
tcpka.onoff = OnOff;
|
||
tcpka.keepalivetime = KATime; // Milliseconds, time to send first KA
|
||
tcpka.keepaliveinterval = KAInterval; // Milliseconds, this is the TCP/IP default.
|
||
|
||
r = WSAIoctl(Socket,
|
||
SIO_KEEPALIVE_VALS,
|
||
(PVOID)&tcpka,
|
||
sizeof(tcpka),
|
||
(PVOID)&tcpka,
|
||
sizeof(tcpka),
|
||
&t,
|
||
0,
|
||
0);
|
||
|
||
if (r != 0)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "setsockopt KEEPALIVE_VALS failed %d\n",
|
||
GetLastError()));
|
||
}
|
||
}
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
WS_TurnOnOffKeepAlives (
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
IN BOOL TurnOn,
|
||
IN BOOL bProtectIO,
|
||
IN KEEPALIVE_TIMEOUT_UNITS Units,
|
||
IN OUT KEEPALIVE_TIMEOUT KATime,
|
||
IN ULONG KAInterval OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Turns on keep alives for Winsock transports supporting keepalives.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - The connection to turn keep alives on on.
|
||
TurnOn - if non-zero, keep alives are turned on. If zero, keep alives
|
||
are turned off.
|
||
KATime - how much to wait before sending first keep alive
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK or RPC_S_* / Win32 errors on failure
|
||
|
||
Note:
|
||
|
||
If we use it on the server, we must protect
|
||
the handle by calling StartingOtherIO
|
||
|
||
--*/
|
||
{
|
||
PWS_CCONNECTION p = (PWS_CCONNECTION)ThisConnection;
|
||
const WS_TRANS_INFO *pInfo = &WsTransportTable[p->id];
|
||
RPC_STATUS RpcStatus = RPC_S_OK;
|
||
|
||
// convert the timeout from runtime scale to transport scale
|
||
if (Units == tuRuntime)
|
||
{
|
||
ASSERT(KATime.RuntimeUnits != RPC_C_BINDING_INFINITE_TIMEOUT);
|
||
KATime.Milliseconds = ConvertRuntimeTimeoutToWSTimeout(KATime.RuntimeUnits);
|
||
}
|
||
|
||
// When the server is turning on keepalives it must protect
|
||
// the operation by calling StartingOtherIO
|
||
if (bProtectIO)
|
||
{
|
||
p->StartingOtherIO();
|
||
}
|
||
|
||
//
|
||
// It is possible that this code is executed before TCP_Open and WS_Open
|
||
// have been called or that unrecoverable failure has been hit.
|
||
// In this case p->id will be invalid or fAborted will be set.
|
||
//
|
||
if (p->id == INVALID_PROTOCOL_ID || p->fAborted)
|
||
{
|
||
RpcStatus = RPC_P_CONNECTION_CLOSED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
ASSERT(pInfo->fSetKeepAlive);
|
||
|
||
WS_P_SetKeepAliveTimeout(
|
||
p->Conn.Socket,
|
||
TurnOn ? TRUE : FALSE,
|
||
KATime.Milliseconds,
|
||
KAInterval);
|
||
|
||
Cleanup:
|
||
if (bProtectIO)
|
||
{
|
||
p->OtherIOFinished();
|
||
}
|
||
return RpcStatus;
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
WS_SyncRecv(
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
OUT BUFFER *pBuffer,
|
||
OUT PUINT pBufferLength,
|
||
IN DWORD dwTimeout
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Receive the next PDU to arrive at the connection.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - The connection to wait on.
|
||
fCancelable - If TRUE, the wait will also include the threads cancel
|
||
event and will timeout after the cancel event is signaled.
|
||
pBuffer - If successful, points to a buffer containing the next PDU.
|
||
BufferSize - If successful, contains the length of the message.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
RPC_P_RECEIVE_FAILED - Connection aborted.
|
||
RPC_P_CONNECTION_SHUTDOWN - Graceful disconnect from server, connection aborted.
|
||
RPC_S_CALL_CANCELLED - Timeout after cancel event, connection aborted.
|
||
|
||
Note:
|
||
|
||
Used only on the client. If we use it on the server, we must protect
|
||
the handle with StartingOtherIO.
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS status;
|
||
DWORD bytes;
|
||
HANDLE hEvent;
|
||
BOOL fReceivePending;
|
||
BOOL fSetKeepAliveVals = FALSE;
|
||
PWS_CCONNECTION p = (PWS_CCONNECTION)ThisConnection;
|
||
DWORD dwActualTimeout;
|
||
BOOL fWaitOnConnectionTimeout;
|
||
|
||
ASSERT((p->type & TYPE_MASK) == CLIENT);
|
||
ASSERT(!IsNetbiosProtocol(p->id));
|
||
|
||
// There maybe a receive already pending from the shutdown check
|
||
// in WS_SyncSend. If so, we want to skip the first submit.
|
||
|
||
fReceivePending = p->fReceivePending;
|
||
p->fReceivePending = FALSE;
|
||
p->fCallStarted = FALSE;
|
||
|
||
// if there's a per operation timeout, use the lesser of the operation
|
||
// and connection timeout
|
||
if (dwTimeout != INFINITE)
|
||
{
|
||
if (dwTimeout <= p->Timeout)
|
||
{
|
||
dwActualTimeout = dwTimeout;
|
||
fWaitOnConnectionTimeout = FALSE;
|
||
}
|
||
else
|
||
{
|
||
dwActualTimeout = p->Timeout;
|
||
fWaitOnConnectionTimeout = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// wait on the connection timeout
|
||
dwActualTimeout = p->Timeout;
|
||
fWaitOnConnectionTimeout = TRUE;
|
||
}
|
||
|
||
ASSERT( (fReceivePending == FALSE)
|
||
|| (p->Read.ol.hEvent == (HANDLE) ((ULONG_PTR)I_RpcTransGetThreadEvent() | 0x1)) );
|
||
|
||
//
|
||
// Keep looping until we have a complete message.
|
||
//
|
||
// Note that SubmitSyncRecv may complete with a whole message read.
|
||
//
|
||
do
|
||
{
|
||
|
||
if (!fReceivePending)
|
||
{
|
||
// Allocate a receive buffer if needed.
|
||
|
||
if (p->pReadBuffer == NULL)
|
||
{
|
||
ASSERT(p->iLastRead == 0);
|
||
|
||
p->pReadBuffer = TransConnectionAllocatePacket(p,
|
||
p->iPostSize);
|
||
if (p->pReadBuffer == NULL)
|
||
{
|
||
p->WS_CONNECTION::Abort();
|
||
return(RPC_P_RECEIVE_FAILED);
|
||
}
|
||
|
||
p->maxReadBuffer = p->iPostSize;
|
||
}
|
||
|
||
InitReadEvent(p);
|
||
|
||
status = CO_SubmitSyncRead(p, pBuffer, pBufferLength);
|
||
|
||
if (status != RPC_P_IO_PENDING)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fReceivePending = FALSE;
|
||
}
|
||
|
||
do
|
||
{
|
||
|
||
//
|
||
// Wait for the pending receive on the connection to complete
|
||
//
|
||
status = UTIL_GetOverlappedResultEx(ThisConnection,
|
||
&p->Read.ol,
|
||
&bytes,
|
||
TRUE, // Alertable
|
||
dwActualTimeout);
|
||
|
||
|
||
if ( status != RPC_S_OK
|
||
|| bytes == 0 )
|
||
{
|
||
|
||
// if we timed out ...
|
||
if (status == RPC_P_TIMEOUT)
|
||
{
|
||
ASSERT(dwActualTimeout != INFINITE);
|
||
|
||
// if we waited on the per connection timeout ...
|
||
if (fWaitOnConnectionTimeout)
|
||
{
|
||
ASSERT(p->Timeout != INFINITE);
|
||
if (dwTimeout == INFINITE)
|
||
{
|
||
// enable keep alives and wait forever
|
||
dwActualTimeout = INFINITE;
|
||
}
|
||
else
|
||
{
|
||
ASSERT(p->Timeout < dwTimeout);
|
||
|
||
// enable keep alives and wait the difference
|
||
dwActualTimeout = dwTimeout - p->Timeout;
|
||
fWaitOnConnectionTimeout = FALSE;
|
||
}
|
||
// Enable aggressive keepalives on the socket if transport
|
||
// supports it
|
||
if (WsTransportTable[p->id].fSetKeepAlive)
|
||
{
|
||
WS_P_SetKeepAliveTimeout(p->Conn.Socket,
|
||
TRUE, // OnOff
|
||
p->Timeout);
|
||
fSetKeepAliveVals = TRUE;
|
||
}
|
||
continue;
|
||
}
|
||
// else we have chosen the per operation timeout and
|
||
// have timed out on that - time to bail out
|
||
}
|
||
|
||
// Normal error path
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSSyncRecv10,
|
||
(status == RPC_S_OK ? bytes : 0));
|
||
|
||
p->WS_CONNECTION::Abort();
|
||
|
||
if ((status == RPC_S_CALL_CANCELLED) || (status == RPC_P_TIMEOUT))
|
||
{
|
||
UTIL_WaitForSyncIO(&p->Read.ol,
|
||
FALSE,
|
||
INFINITE);
|
||
if ((status == RPC_P_TIMEOUT) && fWaitOnConnectionTimeout)
|
||
{
|
||
status = RPC_P_RECEIVE_FAILED;
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSSyncRecv20);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
status = RPC_P_RECEIVE_FAILED;
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSSyncRecv30);
|
||
}
|
||
|
||
return(status);
|
||
}
|
||
}
|
||
while (status == RPC_P_TIMEOUT);
|
||
|
||
status = p->ProcessRead(bytes, pBuffer, pBufferLength);
|
||
|
||
}
|
||
while (status == RPC_P_PARTIAL_RECEIVE);
|
||
|
||
p->dwLastCallTime = GetTickCount();
|
||
|
||
if (fSetKeepAliveVals)
|
||
{
|
||
// Call complete okay, clear keep alives
|
||
WS_P_SetKeepAliveTimeout(p->Conn.Socket,
|
||
FALSE, // OnOff
|
||
0);
|
||
}
|
||
|
||
return(status);
|
||
}
|
||
|
||
void
|
||
RPC_ENTRY
|
||
WS_ServerAbortListen(
|
||
IN RPC_TRANSPORT_ADDRESS Address
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will be called if an error occurs in setting up the
|
||
address between the time that SetupWithEndpoint or SetupUnknownEndpoint
|
||
successfully completed and before the next call into this loadable
|
||
transport module. We need to do any cleanup from Setup*.
|
||
|
||
Arguments:
|
||
|
||
pAddress - The address which is being aborted.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PWS_ADDRESS pList = (PWS_ADDRESS)Address;
|
||
PWS_ADDRESS pLast = 0;
|
||
INT i, retval;
|
||
|
||
delete pList->pAddressVector;
|
||
delete pList->Endpoint;
|
||
|
||
TransportProtocol::RemoveObjectFromProtocolList(pList);
|
||
|
||
while(pList)
|
||
{
|
||
if (pList->ListenSocket)
|
||
{
|
||
closesocket(pList->ListenSocket);
|
||
}
|
||
|
||
if (pList->ConnectionSocket)
|
||
{
|
||
closesocket(pList->ConnectionSocket);
|
||
}
|
||
|
||
|
||
pLast = pList;
|
||
pList = (PWS_ADDRESS) pList->pNextAddress;
|
||
|
||
if (pLast != (PWS_ADDRESS)Address)
|
||
{
|
||
TransportProtocol::RemoveObjectFromProtocolList(pLast);
|
||
delete pLast;
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
WS_SubmitAccept(
|
||
BASE_ADDRESS *Address
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Used to submit an accept on a listen socket. Called once when the
|
||
listen socket is created and again after each client connects.
|
||
|
||
The listen socket must already be added to the completion port.
|
||
|
||
Arguments:
|
||
|
||
Address - The address to accept on.
|
||
->ConnectionSocket - socket to accept on, or zero in
|
||
which case a new socket is allocated and put here.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PWS_ADDRESS pAddress = (PWS_ADDRESS)Address;
|
||
const WS_TRANS_INFO *pInfo = &WsTransportTable[pAddress->id];
|
||
SOCKET sAccept;
|
||
RPC_STATUS status;
|
||
|
||
if (pAddress->ConnectionSocket != 0)
|
||
{
|
||
closesocket(pAddress->ConnectionSocket);
|
||
pAddress->ConnectionSocket = 0;
|
||
}
|
||
|
||
pAddress->ConnectionSocket =
|
||
WSASocketT(pInfo->AddressFamily,
|
||
pInfo->SocketType,
|
||
pInfo->Protocol * GetProtocolMultiplier(pAddress),
|
||
0,
|
||
0,
|
||
WSA_FLAG_OVERLAPPED);
|
||
|
||
if (pAddress->ConnectionSocket == SOCKET_ERROR)
|
||
{
|
||
pAddress->ConnectionSocket = 0;
|
||
COMMON_AddressManager(Address);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// make the handle non-inheritable so it goes away when we close it.
|
||
// join the socket to the completion port
|
||
//
|
||
if (FALSE == SetHandleInformation( (HANDLE) pAddress->ConnectionSocket, HANDLE_FLAG_INHERIT, 0) ||
|
||
RPC_S_OK != COMMON_PrepareNewHandle((HANDLE) pAddress->ConnectionSocket))
|
||
{
|
||
closesocket(pAddress->ConnectionSocket);
|
||
pAddress->ConnectionSocket = 0;
|
||
COMMON_AddressManager(Address);
|
||
return;
|
||
}
|
||
|
||
ASSERT(pAddress->ConnectionSocket != INVALID_SOCKET);
|
||
|
||
DWORD bytes = 0;
|
||
|
||
BOOL b = pAddress->pAcceptExFunction(pAddress->ListenSocket,
|
||
pAddress->ConnectionSocket,
|
||
&pAddress->AcceptBuffer,
|
||
0,
|
||
0,
|
||
sizeof(WS_SOCKADDR) + 16,
|
||
&bytes,
|
||
&pAddress->Listen.ol
|
||
);
|
||
|
||
if (!b && (GetLastError() != ERROR_IO_PENDING))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"AcceptEx failed %p, %d %d\n",
|
||
pAddress,
|
||
pAddress->ConnectionSocket,
|
||
GetLastError()));
|
||
|
||
closesocket(pAddress->ConnectionSocket);
|
||
pAddress->ConnectionSocket = 0;
|
||
COMMON_AddressManager(Address);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
void
|
||
WS_SetSockOptForConnection (
|
||
IN const WS_TRANS_INFO *pInfo,
|
||
IN SOCKET sock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the socket options for the given socket for a server
|
||
side connection socket.
|
||
|
||
Arguments:
|
||
|
||
pInfo - the transport information for the Winsock transport
|
||
sock - the socket on which to set options
|
||
|
||
Return Value:
|
||
|
||
None. Setting the options is a best effort. Failures are ignored.
|
||
|
||
--*/
|
||
{
|
||
int retval = 0;
|
||
|
||
if (pInfo->fSetNoDelay)
|
||
{
|
||
INT NoDelay = TRUE;
|
||
retval = setsockopt(sock,
|
||
pInfo->Protocol,
|
||
TCP_NODELAY,
|
||
(PCHAR)&NoDelay, sizeof(NoDelay)
|
||
);
|
||
}
|
||
|
||
if ( pInfo->fSetKeepAlive
|
||
&& retval == 0)
|
||
{
|
||
INT KeepAlive = TRUE;
|
||
retval = setsockopt(sock,
|
||
pInfo->Protocol,
|
||
SO_KEEPALIVE,
|
||
(PCHAR)&KeepAlive, sizeof(KeepAlive)
|
||
);
|
||
}
|
||
|
||
if (retval != 0)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "setsockopt failed %d, %d\n",
|
||
retval,
|
||
GetLastError()));
|
||
}
|
||
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
WS_NewConnection(
|
||
IN PADDRESS Address,
|
||
OUT PCONNECTION *ppConnection
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called when an AcceptEx completes on an I/O completion thread.
|
||
|
||
Arguments:
|
||
|
||
Address - The address used as context in a previous AcceptEx.
|
||
ppConnection - A place to store the new pConnection. Used
|
||
when a connection been created and then a failure occurs.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S_OUT_OF_MEMORY
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS status;
|
||
BOOL b;
|
||
WS_ADDRESS *pAddress = (WS_ADDRESS *)Address;
|
||
const WS_TRANS_INFO *pInfo = &WsTransportTable[pAddress->id];
|
||
WS_CONNECTION *pConnection;
|
||
UINT fReceiveDirect;
|
||
SOCKET sock = pAddress->ConnectionSocket;
|
||
int retval = 0;
|
||
WS_SOCKADDR *saAddrs;
|
||
WS_SOCKADDR saClient;
|
||
INT adwAddrLen;
|
||
BOOL fSANConnection;
|
||
int LocalAddressLength = 0;
|
||
PSOCKADDR DummyAddr = NULL;
|
||
|
||
ASSERT(sock);
|
||
|
||
pAddress->ConnectionSocket = 0;
|
||
|
||
// First, parse the client address out of the accept
|
||
// since the next accept will reuse the same buffer.
|
||
|
||
pAddress->pGetAcceptExSockaddressFunction(&pAddress->AcceptBuffer,
|
||
0,
|
||
0,
|
||
sizeof(WS_SOCKADDR) + 16,
|
||
&DummyAddr,
|
||
&LocalAddressLength,
|
||
(struct sockaddr **)&saAddrs,
|
||
&adwAddrLen);
|
||
|
||
ASSERT(adwAddrLen <= sizeof(WS_SOCKADDR));
|
||
|
||
// Save the client address before submitting the next accept.
|
||
saClient = *saAddrs;
|
||
|
||
// Submit the next accept.
|
||
WS_SubmitAccept(pAddress);
|
||
|
||
// Now, try process the new connection..
|
||
WS_SetSockOptForConnection(pInfo, sock);
|
||
|
||
/*
|
||
fSANConnection = IsUserModeSocket(sock, &status);
|
||
if (status != RPC_S_OK)
|
||
{
|
||
closesocket(sock);
|
||
return status;
|
||
}
|
||
*/
|
||
fSANConnection = TRUE;
|
||
|
||
//
|
||
// Notes:
|
||
//
|
||
// a. For security reasons, we require the RPC HTTP Servers to send back
|
||
// an identification message.
|
||
//
|
||
// b. This should "really" be done in WS_SubmitAccept(). This is done here
|
||
// for convenience. This is OK if HttpSendIdentifyRespomse() rarely
|
||
// fails.
|
||
//
|
||
if ((pAddress->id == HTTP) &&
|
||
(status = HttpSendIdentifyResponse(sock)))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"HttpSendIdentifyResponse failed %p, %d - %d\n",
|
||
pAddress,
|
||
sock,
|
||
status
|
||
));
|
||
closesocket(sock);
|
||
return(RPC_S_OUT_OF_RESOURCES);
|
||
}
|
||
|
||
BASE_ADDRESS *pRealAddress = pAddress->pFirstAddress;
|
||
|
||
pConnection = (WS_CONNECTION *)
|
||
I_RpcTransServerNewConnection(pRealAddress);
|
||
|
||
*ppConnection = pConnection;
|
||
|
||
if (!pConnection)
|
||
{
|
||
// Abort the connection.
|
||
|
||
INT DontLinger = TRUE;
|
||
// REVIEW: check a protocol flag to do this?
|
||
retval = setsockopt(sock, SOL_SOCKET, SO_DONTLINGER,
|
||
(PCHAR)&DontLinger, sizeof(DontLinger));
|
||
ASSERT(retval == 0);
|
||
closesocket(sock);
|
||
return(RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
// Got a good connection, initialize it..
|
||
|
||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
// This function cannot fail after this point. There is no
|
||
// way to notify the runtime that the connection has been closed.
|
||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
|
||
#ifdef NETBIOS_ON
|
||
if ((pAddress->id == NBF)
|
||
|| (pAddress->id == NBT)
|
||
|| (pAddress->id == NBI))
|
||
{
|
||
if (fSANConnection)
|
||
pConnection = new (pConnection) NB_SAN_CONNECTION;
|
||
else
|
||
pConnection = new (pConnection) NB_CONNECTION;
|
||
}
|
||
else
|
||
#endif
|
||
if (pAddress->id == HTTP)
|
||
{
|
||
pConnection = new (pConnection) WS_HTTP2_INITIAL_CONNECTION;
|
||
}
|
||
else
|
||
{
|
||
if (fSANConnection)
|
||
pConnection = new (pConnection) WS_SAN_CONNECTION;
|
||
else
|
||
pConnection = new (pConnection) WS_CONNECTION;
|
||
}
|
||
|
||
pConnection->type = SERVER | CONNECTION;
|
||
pConnection->id = pAddress->id;
|
||
pConnection->Conn.Socket = sock;
|
||
pConnection->fAborted = 0;
|
||
pConnection->pReadBuffer = 0;
|
||
pConnection->maxReadBuffer = 0;
|
||
pConnection->iLastRead = 0;
|
||
pConnection->iPostSize = gPostSize;
|
||
pConnection->saClientAddress = saClient;
|
||
RpcpMemorySet(&pConnection->Read.ol, 0, sizeof(pConnection->Read.ol));
|
||
pConnection->Read.pAsyncObject = pConnection;
|
||
pConnection->InitIoCounter();
|
||
pConnection->pAddress = pAddress;
|
||
|
||
TransportProtocol::AddObjectToProtocolList((BASE_ASYNC_OBJECT *) *ppConnection);
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
BOOL
|
||
IsUserModeSocket(
|
||
IN SOCKET s,
|
||
OUT RPC_STATUS *pStatus)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a socket, it tells whether this is a true kernel socket or not. This test is based per VadimE's input
|
||
that:
|
||
"Just call getsockopt (SOL_SOCKET, SO_PROTOCOL_INFOW) and check XP1_IFS_HANDLES in
|
||
dwServiceFlags1 of WSAPROTOCOL_INFOW. If flag is not set, the handle is not "TRUE"
|
||
IFS handle and file system calls on them carry performance penalty.
|
||
|
||
Make sure you call after connection is established or information returned may be
|
||
inaccurate."
|
||
|
||
Arguments:
|
||
|
||
s - The socket to be tested
|
||
pStatus - RPC_S_OK if everything is fine. RPC_S_OUT_OF_MEMORY if the test could not be performed. Note
|
||
that in the latter case the return value is undefined and should be disregarded.
|
||
|
||
Return Value:
|
||
|
||
TRUE - the socket is a true kernel socket
|
||
FALSE - the socket is not a kernel socket
|
||
|
||
--*/
|
||
{
|
||
WSAPROTOCOL_INFO protocolInfo;
|
||
int paramSize = sizeof(protocolInfo);
|
||
int retval;
|
||
|
||
// check whether this is a kernel connection. We do this by checking whether this socket is a true
|
||
// IFS_HANDLE. If yes, then this is not a kernel socket. If not, then it is a kernel socket
|
||
retval = getsockopt(s, SOL_SOCKET, SO_PROTOCOL_INFOW, (char *) &protocolInfo, ¶mSize);
|
||
if (retval == SOCKET_ERROR)
|
||
{
|
||
*pStatus = RPC_S_OUT_OF_MEMORY;
|
||
return FALSE;
|
||
}
|
||
|
||
*pStatus = RPC_S_OK;
|
||
|
||
if (protocolInfo.dwServiceFlags1 & XP1_IFS_HANDLES)
|
||
return FALSE;
|
||
else
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
WS_ServerListenCommon (
|
||
IN WS_ADDRESS *pAddress
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does common server address setup.
|
||
|
||
Arguments:
|
||
|
||
pAddress - A pointer to the loadable transport interface address.
|
||
Will contain the newly allocated listen socket when finished.
|
||
|
||
pListenAddr - Initalized socket address to bind to. On output
|
||
it will contain results of the bind.
|
||
|
||
PendingQueueSize - Value specified in use protseq, used
|
||
to set the pending queue size for listens.
|
||
|
||
ReturnValue:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S__CANT_CREATE_ENDPOINT
|
||
|
||
--*/
|
||
{
|
||
SOCKET sock;
|
||
int retval, length;
|
||
RPC_STATUS status;
|
||
const WS_TRANS_INFO *pInfo = &WsTransportTable[pAddress->id];
|
||
WS_SOCKADDR *pListenAddr = &(pAddress->ListenAddr);
|
||
DWORD EndpointFlags = pAddress->EndpointFlags;
|
||
BOOL fRetVal;
|
||
int i;
|
||
DWORD LastError;
|
||
|
||
pAddress->type = ADDRESS;
|
||
pAddress->InAddressList = NotInList;
|
||
pAddress->fAborted = 0;
|
||
pAddress->pNext = 0;
|
||
pAddress->ListenSocket = 0;
|
||
pAddress->ConnectionSocket = 0;
|
||
pAddress->pNextAddress = 0;
|
||
pAddress->pFirstAddress = pAddress;
|
||
memset(&pAddress->Listen, 0, sizeof(BASE_OVERLAPPED));
|
||
pAddress->Listen.pAsyncObject = pAddress;
|
||
for (i = FIRST_EXTENSION_FUNCTION_CODE; i < LAST_EXTENSION_FUNCTION_CODE; i ++)
|
||
{
|
||
pAddress->ExtensionFunctionPointers[i] = NULL;
|
||
}
|
||
RpcpInitializeListHead(&pAddress->ObjectList);
|
||
|
||
// First order of business: get a valid socket
|
||
//
|
||
sock = WSASocketT(pInfo->AddressFamily,
|
||
pInfo->SocketType,
|
||
pInfo->Protocol,
|
||
0,
|
||
0,
|
||
WSA_FLAG_OVERLAPPED);
|
||
|
||
if (sock == INVALID_SOCKET)
|
||
{
|
||
LastError = GetLastError();
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
LastError,
|
||
EEInfoDLWSServerListenCommon10,
|
||
pInfo->AddressFamily,
|
||
pInfo->SocketType,
|
||
pInfo->Protocol);
|
||
|
||
switch(LastError)
|
||
{
|
||
case WSAENETDOWN:
|
||
case WSAEINVAL:
|
||
case WSAEPROTOTYPE:
|
||
case WSAENOPROTOOPT:
|
||
case WSAEPROTONOSUPPORT:
|
||
case WSAESOCKTNOSUPPORT:
|
||
case WSAEPFNOSUPPORT:
|
||
case WSAEADDRNOTAVAIL:
|
||
status = RPC_S_PROTSEQ_NOT_SUPPORTED;
|
||
break;
|
||
|
||
case WSAEAFNOSUPPORT:
|
||
status = RPC_P_ADDRESS_FAMILY_INVALID;
|
||
break;
|
||
|
||
case WSAENOBUFS:
|
||
case WSAEMFILE:
|
||
case WSA_NOT_ENOUGH_MEMORY:
|
||
status = RPC_S_OUT_OF_MEMORY;
|
||
break;
|
||
|
||
default:
|
||
ASSERT(0);
|
||
|
||
// !break
|
||
|
||
case WSAEPROVIDERFAILEDINIT:
|
||
status = RPC_S_OUT_OF_RESOURCES;
|
||
break;
|
||
}
|
||
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
status,
|
||
EEInfoDLWSServerListenCommon30);
|
||
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Make the handle non-inheritable so it goes away when we close it.
|
||
//
|
||
if (FALSE == SetHandleInformation( (HANDLE) sock, HANDLE_FLAG_INHERIT, 0))
|
||
{
|
||
closesocket(sock);
|
||
return RPC_S_OUT_OF_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Protect the socket to prevent another server from using our port.
|
||
//
|
||
|
||
WS_ProtectListeningSocket(sock, TRUE);
|
||
|
||
fRetVal = pAddress->GetExtensionFunctionPointers(sock);
|
||
|
||
if (!fRetVal)
|
||
{
|
||
switch (GetLastError())
|
||
{
|
||
case WSAEFAULT:
|
||
case WSAEINVAL:
|
||
status = RPC_S_INTERNAL_ERROR;
|
||
break;
|
||
|
||
case WSAEOPNOTSUPP:
|
||
status = RPC_S_PROTSEQ_NOT_SUPPORTED;
|
||
break;
|
||
|
||
default:
|
||
status = RPC_S_OUT_OF_RESOURCES;
|
||
}
|
||
closesocket(sock);
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Try to bind to the given port number...
|
||
//
|
||
|
||
pListenAddr->generic.sa_family = pInfo->AddressFamily;
|
||
|
||
// N.B. - we should think how the port allocation will look for TCP/IPv6
|
||
status = WS_Bind(sock, pListenAddr, (pAddress->id == TCP) || (pAddress->id == HTTP), EndpointFlags);
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
closesocket(sock);
|
||
return(status);
|
||
}
|
||
|
||
if(listen(sock, pAddress->QueueSize) == SOCKET_ERROR)
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_S_OUT_OF_RESOURCES,
|
||
EEInfoDLWSServerListenCommon20,
|
||
GetLastError(),
|
||
(ULONGLONG)sock,
|
||
(ULONG)pAddress->QueueSize);
|
||
closesocket(sock);
|
||
return(RPC_S_OUT_OF_RESOURCES);
|
||
}
|
||
|
||
status = COMMON_PrepareNewHandle((HANDLE)sock);
|
||
if (status != RPC_S_OK)
|
||
{
|
||
closesocket(sock);
|
||
return(status);
|
||
}
|
||
|
||
pAddress->ListenSocket = sock;
|
||
|
||
TransportProtocol::AddObjectToProtocolList((BASE_ASYNC_OBJECT *) pAddress);
|
||
|
||
TransportProtocol::FunctionalProtocolDetected(pAddress->id);
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
WS_Initialize_Internal (
|
||
IN PWS_CCONNECTION pConnection
|
||
)
|
||
{
|
||
pConnection->Initialize();
|
||
|
||
pConnection->fCallStarted = FALSE;
|
||
pConnection->fShutdownReceived = FALSE;
|
||
pConnection->fReceivePending = FALSE;
|
||
pConnection->dwLastCallTime = GetTickCount();
|
||
pConnection->pAddress = NULL;
|
||
RpcpInitializeListHead(&pConnection->ObjectList);
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
WS_Initialize (
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
IN RPC_CHAR *NetworkAddress,
|
||
IN RPC_CHAR *NetworkOptions,
|
||
IN BOOL fAsync
|
||
)
|
||
{
|
||
PWS_CCONNECTION p = (PWS_CCONNECTION) ThisConnection;
|
||
|
||
p->id = INVALID_PROTOCOL_ID;
|
||
return WS_Initialize_Internal(p);
|
||
}
|
||
|
||
const UUID ConnectExExtensionFunctionUuid = WSAID_CONNECTEX;
|
||
|
||
|
||
RPC_STATUS
|
||
WS_Open(
|
||
IN PWS_CCONNECTION p,
|
||
IN WS_SOCKADDR *psa,
|
||
IN UINT ConnTimeout,
|
||
IN UINT SendBufferSize,
|
||
IN UINT RecvBufferSize,
|
||
IN ULONG CallTimeout,
|
||
IN BOOL fHTTP2Open
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Common part of opening a winsock connection to a server.
|
||
|
||
Arguments:
|
||
|
||
p - The partially initialized client connection object. If fHTTP2,
|
||
this is a connection object, not client connection object.
|
||
psa - sockaddr with protocol specific part already containing
|
||
this address and port of the server.
|
||
ConnTimeout - Valid for TCP/IP, see SyncRecv error handling.
|
||
{Send,Recv}BufferSize - Used to set the transport buffer
|
||
sizes on some protocols. Currently ignored.
|
||
CallTimeout - call timeout in milliseconds
|
||
fHTTP2Open - the open is an HTTP2 open
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S_OUT_OF_MEMORY
|
||
|
||
RPC_S_SERVER_UNAVAILABLE - failed
|
||
ERROR_RETRY - failed, but another address might work.
|
||
|
||
--*/
|
||
{
|
||
// Initialize common part of the connection object
|
||
|
||
const WS_TRANS_INFO *pInfo = &WsTransportTable[p->id];
|
||
SOCKET sock;
|
||
RPC_STATUS status;
|
||
BOOL fSANConnection;
|
||
DWORD LastError;
|
||
RPC_CLIENT_PROCESS_IDENTIFIER ServerAddress;
|
||
HANDLE hEvent;
|
||
OVERLAPPED ol;
|
||
DWORD dwBytesReturned;
|
||
LPFN_CONNECTEX ConnectEx;
|
||
union
|
||
{
|
||
SOCKADDR_IN sockaddr;
|
||
SOCKADDR_IN6 sockaddr6;
|
||
};
|
||
int NameLen;
|
||
|
||
DWORD Transfer;
|
||
DWORD Flags;
|
||
|
||
// Set if we had already called GetLastErrorand added EEInfo.
|
||
// Adding EEInfo may overwrite the LastError and getting it second
|
||
// time will return 0 and cause an assert.
|
||
BOOL fGetLastErrorCalled = FALSE;
|
||
|
||
if (!fHTTP2Open)
|
||
{
|
||
WS_Initialize_Internal(p);
|
||
}
|
||
|
||
//
|
||
// Open a socket
|
||
//
|
||
|
||
sock = WSASocketT(pInfo->AddressFamily,
|
||
pInfo->SocketType,
|
||
pInfo->Protocol,
|
||
0,
|
||
0,
|
||
WSA_FLAG_OVERLAPPED);
|
||
|
||
if (sock == INVALID_SOCKET)
|
||
{
|
||
LastError = GetLastError();
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
LastError,
|
||
EEInfoDLWSOpen10,
|
||
pInfo->AddressFamily,
|
||
pInfo->SocketType,
|
||
pInfo->Protocol);
|
||
|
||
switch(LastError)
|
||
{
|
||
case WSAEAFNOSUPPORT:
|
||
case WSAEPROTONOSUPPORT:
|
||
case WSAEPROTOTYPE:
|
||
case WSAENETDOWN:
|
||
case WSAESOCKTNOSUPPORT:
|
||
case WSAEINVAL: // when registry is not yet setup.
|
||
status = RPC_S_PROTSEQ_NOT_SUPPORTED;
|
||
break;
|
||
|
||
case ERROR_NOT_ENOUGH_QUOTA:
|
||
case WSAENOBUFS:
|
||
case WSAEMFILE:
|
||
case WSA_NOT_ENOUGH_MEMORY:
|
||
// This failure is possible in low memory conditions
|
||
// or due to fault injection during registry read or
|
||
// notification creation.
|
||
case WSASYSCALLFAILURE:
|
||
status = RPC_S_OUT_OF_MEMORY;
|
||
break;
|
||
|
||
case ERROR_ACCESS_DENIED:
|
||
status = RPC_S_ACCESS_DENIED;
|
||
break;
|
||
|
||
default:
|
||
ASSERT(0);
|
||
// no break - fall through
|
||
|
||
case WSAEPROVIDERFAILEDINIT:
|
||
status = RPC_S_OUT_OF_RESOURCES;
|
||
break;
|
||
}
|
||
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
status,
|
||
EEInfoDLWSOpen30);
|
||
|
||
return (status);
|
||
}
|
||
|
||
//
|
||
// make the handle non-inheritable so it goes away when we close it.
|
||
//
|
||
if (FALSE == SetHandleInformation( (HANDLE) sock, HANDLE_FLAG_INHERIT, 0))
|
||
{
|
||
closesocket(sock);
|
||
return RPC_S_OUT_OF_RESOURCES;
|
||
}
|
||
|
||
p->Conn.Socket = sock;
|
||
|
||
//
|
||
// Set socket options
|
||
//
|
||
// REVIEW: Set loopback socket option? Ask winsock folks.
|
||
|
||
DWORD option;
|
||
int retval = 0;
|
||
|
||
if (pInfo->fSetNoDelay)
|
||
{
|
||
option = TRUE;
|
||
retval = setsockopt( sock, pInfo->Protocol, TCP_NODELAY,
|
||
(PCHAR)&option, sizeof(option) );
|
||
}
|
||
|
||
if (pInfo->fSetKeepAlive && retval == 0)
|
||
{
|
||
option = TRUE;
|
||
retval = setsockopt( sock, pInfo->Protocol, SO_KEEPALIVE,
|
||
(PCHAR)&option, sizeof(option) );
|
||
}
|
||
|
||
if ( pInfo->fSetSendBuffer
|
||
&& SendBufferSize
|
||
&& retval == 0)
|
||
{
|
||
ASSERT(SendBufferSize <= 0xFFFF);
|
||
retval = setsockopt( sock, SOL_SOCKET, SO_SNDBUF,
|
||
(PCHAR)&SendBufferSize, sizeof(UINT) );
|
||
}
|
||
|
||
if ( pInfo->fSetRecvBuffer
|
||
&& RecvBufferSize
|
||
&& retval == 0 )
|
||
{
|
||
ASSERT(RecvBufferSize <= 0xFFFF);
|
||
retval = setsockopt( sock, SOL_SOCKET, SO_RCVBUF,
|
||
(PCHAR)&RecvBufferSize, sizeof(UINT) );
|
||
}
|
||
|
||
|
||
if (retval)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "setsockopt failed %d\n",
|
||
GetLastError()));
|
||
|
||
p->WS_CONNECTION::Abort();
|
||
|
||
return(RPC_S_OUT_OF_RESOURCES);
|
||
}
|
||
|
||
if (!fHTTP2Open)
|
||
{
|
||
//
|
||
// Set timeout
|
||
//
|
||
|
||
if ( WsTransportTable[p->id].fSetKeepAlive
|
||
&& ConnTimeout != RPC_C_BINDING_INFINITE_TIMEOUT)
|
||
{
|
||
ASSERT( ((long)ConnTimeout >= RPC_C_BINDING_MIN_TIMEOUT)
|
||
&& (ConnTimeout <= RPC_C_BINDING_MAX_TIMEOUT));
|
||
|
||
// convert the timeout from runtime scale to transport scale
|
||
p->Timeout = ConvertRuntimeTimeoutToWSTimeout(ConnTimeout);
|
||
}
|
||
else
|
||
{
|
||
p->Timeout = INFINITE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// HTTP specific connect() done in HTTP_Open().
|
||
//
|
||
if (p->id == HTTP)
|
||
{
|
||
//
|
||
// For HTTP, add the new socket to the io completion port
|
||
// now. For TCP, we'll add it later.
|
||
//
|
||
status = COMMON_PrepareNewHandle((HANDLE)sock);
|
||
if (status != RPC_S_OK)
|
||
{
|
||
closesocket(sock);
|
||
return status;
|
||
}
|
||
|
||
return (RPC_S_OK);
|
||
}
|
||
|
||
|
||
//
|
||
// Connect the socket to the server
|
||
//
|
||
|
||
psa->generic.sa_family = pInfo->AddressFamily;
|
||
|
||
if ((CallTimeout == INFINITE) || (CallTimeout == 0)
|
||
#ifdef SPX_ON
|
||
|| (p->id == SPX)
|
||
#endif
|
||
)
|
||
{
|
||
retval = connect(sock, &psa->generic, pInfo->SockAddrSize);
|
||
}
|
||
else
|
||
{
|
||
// we have a specified call timeout. Use ConnectEx instead
|
||
|
||
// first, bind the socket. Unlike connect, ConnectEx doesn't
|
||
// accept unbound sockets
|
||
if (p->id != TCP_IPv6)
|
||
{
|
||
sockaddr.sin_addr.S_un.S_addr = INADDR_ANY;
|
||
sockaddr.sin_family = AF_INET;
|
||
sockaddr.sin_port = 0;
|
||
NameLen = sizeof(sockaddr);
|
||
}
|
||
else
|
||
{
|
||
IN6ADDR_SETANY(&sockaddr6);
|
||
sockaddr6.sin6_scope_id = 0;
|
||
NameLen = sizeof(sockaddr6);
|
||
}
|
||
|
||
retval = bind(sock,
|
||
(SOCKADDR *)&sockaddr,
|
||
NameLen);
|
||
|
||
if (retval == SOCKET_ERROR)
|
||
{
|
||
status = GetLastError();
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSOpen60);
|
||
|
||
fGetLastErrorCalled = TRUE;
|
||
|
||
goto Handle_WS_OpenError;
|
||
}
|
||
|
||
// second retrieve address of ConnectEx
|
||
retval = WSAIoctl(sock,
|
||
SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||
(void *) &ConnectExExtensionFunctionUuid,
|
||
sizeof(UUID),
|
||
(void *) &ConnectEx,
|
||
sizeof(void *),
|
||
&dwBytesReturned,
|
||
NULL, // lpOverlapped
|
||
NULL // lpCompletionRoutine
|
||
);
|
||
|
||
if (retval == SOCKET_ERROR)
|
||
{
|
||
// ConnectEx is not available. We need to default to using connect.
|
||
retval = connect(sock, &psa->generic, pInfo->SockAddrSize);
|
||
}
|
||
else
|
||
{
|
||
// Use ConnectEx.
|
||
|
||
ASSERT(dwBytesReturned == sizeof(void *));
|
||
|
||
hEvent = I_RpcTransGetThreadEvent();
|
||
ASSERT(hEvent);
|
||
|
||
ol.Internal = 0;
|
||
ol.InternalHigh = 0;
|
||
ol.Offset = 0;
|
||
ol.OffsetHigh = 0;
|
||
// There may be a window between winsock's raising the event to signal IO completion
|
||
// and checking if there is a completion port associated with the socket. We
|
||
// need to make sure that the IO completion packet will not be posted to a port if
|
||
// we associate it with the socket after the event is raised but before the packet is posted.
|
||
ol.hEvent = (HANDLE) ((ULONG_PTR)hEvent | 0x1);
|
||
|
||
retval = ConnectEx(sock,
|
||
&psa->generic,
|
||
pInfo->SockAddrSize,
|
||
NULL, // lpSendBuffer
|
||
0, // dwSendDataLength
|
||
NULL, // lpdwBytesSent
|
||
&ol);
|
||
|
||
// N.B. ConnectEx returns the opposite of connect - TRUE for
|
||
// success and FALSE for failure. Since the error handling is
|
||
// common, we must make the return value consistent. We do this
|
||
// by reverting the return value for ConnectEx
|
||
retval = !retval;
|
||
|
||
if (retval != 0)
|
||
{
|
||
LastError = GetLastError();
|
||
if ((LastError != ERROR_IO_PENDING) && (LastError != WSA_IO_PENDING))
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
LastError,
|
||
EEInfoDLWSOpen80);
|
||
|
||
status = LastError;
|
||
fGetLastErrorCalled = TRUE;
|
||
|
||
goto Handle_WS_OpenError;
|
||
}
|
||
|
||
// wait for the result or for timeout
|
||
LastError = WaitForSingleObject(hEvent, CallTimeout);
|
||
if (LastError == WAIT_TIMEOUT)
|
||
{
|
||
// we have hit a timeout. Kill the socket and bail out
|
||
status = RPC_S_CALL_CANCELLED;
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
status,
|
||
EEInfoDLWSOpen50,
|
||
CallTimeout);
|
||
|
||
p->WS_CONNECTION::Abort();
|
||
|
||
// wait for our IO to complete. Should be quick after
|
||
// we closed the socket
|
||
LastError = WaitForSingleObject(hEvent, INFINITE);
|
||
ASSERT(LastError == WAIT_OBJECT_0);
|
||
ASSERT(HasOverlappedIoCompleted(&ol));
|
||
|
||
return status;
|
||
}
|
||
else
|
||
{
|
||
ASSERT(LastError == WAIT_OBJECT_0);
|
||
ASSERT(HasOverlappedIoCompleted(&ol));
|
||
|
||
// Retrieve the overlapped result. No need to wait since the IO has
|
||
// already completed.
|
||
if (!WSAGetOverlappedResult(sock, &ol, &Transfer, FALSE, &Flags))
|
||
{
|
||
// set retval to the WSA error code.
|
||
retval = WSAGetLastError();
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
retval,
|
||
EEInfoDLWSOpen90);
|
||
|
||
status = retval;
|
||
fGetLastErrorCalled = TRUE;
|
||
}
|
||
else
|
||
{
|
||
retval = setsockopt(sock,
|
||
SOL_SOCKET,
|
||
SO_UPDATE_CONNECT_CONTEXT,
|
||
NULL,
|
||
0
|
||
);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
retval = setsockopt(sock,
|
||
SOL_SOCKET,
|
||
SO_UPDATE_CONNECT_CONTEXT,
|
||
NULL,
|
||
0
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (retval == 0)
|
||
{
|
||
//
|
||
// After we're done with connect/ConnectEx, add the socket
|
||
// to the completion port.
|
||
//
|
||
status = COMMON_PrepareNewHandle((HANDLE)sock);
|
||
if (status != RPC_S_OK)
|
||
{
|
||
goto Handle_WS_OpenError;
|
||
}
|
||
|
||
fSANConnection = IsUserModeSocket(sock, &status);
|
||
if (status == RPC_S_OK)
|
||
{
|
||
if (fSANConnection && !fHTTP2Open)
|
||
{
|
||
// reinitialize vtbl
|
||
p = new (p) WS_SAN_CLIENT_CONNECTION;
|
||
}
|
||
TransportProtocol::AddObjectToProtocolList((BASE_ASYNC_OBJECT *) p);
|
||
return(RPC_S_OK);
|
||
}
|
||
}
|
||
|
||
Handle_WS_OpenError:
|
||
|
||
if (!fGetLastErrorCalled)
|
||
{
|
||
status = GetLastError();
|
||
}
|
||
|
||
ServerAddress.ZeroOut();
|
||
if (p->id == TCP)
|
||
{
|
||
ServerAddress.SetIPv4ClientIdentifier(psa->inetaddr.sin_addr.S_un.S_addr, FALSE);
|
||
}
|
||
else if (p->id == TCP_IPv6)
|
||
{
|
||
ServerAddress.SetIPv6ClientIdentifier(&psa->ipaddr, sizeof(psa->ipaddr), FALSE);
|
||
}
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSOpen20,
|
||
(ULONG)ntohs(RpcpGetIpPort(psa)),
|
||
ServerAddress.GetDebugULongLong1(),
|
||
ServerAddress.GetDebugULongLong2());
|
||
|
||
switch(status)
|
||
{
|
||
case WSAENETUNREACH:
|
||
case STATUS_BAD_NETWORK_PATH:
|
||
case STATUS_NETWORK_UNREACHABLE:
|
||
case STATUS_PROTOCOL_UNREACHABLE:
|
||
case WSAEHOSTUNREACH:
|
||
case STATUS_HOST_UNREACHABLE:
|
||
case WSAETIMEDOUT:
|
||
case STATUS_LINK_TIMEOUT:
|
||
case STATUS_IO_TIMEOUT:
|
||
case STATUS_TIMEOUT:
|
||
case WSAEADDRNOTAVAIL:
|
||
case STATUS_INVALID_ADDRESS:
|
||
case STATUS_INVALID_ADDRESS_COMPONENT:
|
||
status = ERROR_RETRY;
|
||
break;
|
||
|
||
case WSAENOBUFS:
|
||
case STATUS_INSUFFICIENT_RESOURCES:
|
||
case STATUS_PAGEFILE_QUOTA:
|
||
case STATUS_COMMITMENT_LIMIT:
|
||
case STATUS_WORKING_SET_QUOTA:
|
||
case STATUS_NO_MEMORY:
|
||
case STATUS_QUOTA_EXCEEDED:
|
||
case STATUS_TOO_MANY_PAGING_FILES:
|
||
case STATUS_REMOTE_RESOURCES:
|
||
case ERROR_NOT_ENOUGH_MEMORY:
|
||
status = RPC_S_OUT_OF_MEMORY;
|
||
break;
|
||
|
||
default:
|
||
VALIDATE(status)
|
||
{
|
||
WSAENETDOWN,
|
||
STATUS_INVALID_NETWORK_RESPONSE,
|
||
STATUS_NETWORK_BUSY,
|
||
STATUS_NO_SUCH_DEVICE,
|
||
STATUS_NO_SUCH_FILE,
|
||
STATUS_OBJECT_PATH_NOT_FOUND,
|
||
STATUS_OBJECT_NAME_NOT_FOUND,
|
||
STATUS_UNEXPECTED_NETWORK_ERROR,
|
||
WSAECONNREFUSED,
|
||
STATUS_REMOTE_NOT_LISTENING,
|
||
STATUS_CONNECTION_REFUSED,
|
||
WSAECONNABORTED,
|
||
STATUS_LOCAL_DISCONNECT,
|
||
STATUS_TRANSACTION_ABORTED,
|
||
STATUS_CONNECTION_ABORTED,
|
||
WSAEADDRINUSE,
|
||
ERROR_CONNECTION_REFUSED
|
||
} END_VALIDATE;
|
||
|
||
status = RPC_S_SERVER_UNAVAILABLE;
|
||
break;
|
||
}
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSOpen40);
|
||
|
||
p->WS_CONNECTION::Abort();
|
||
|
||
return(status);
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////
|
||
//
|
||
// TCP/IP specific stuff
|
||
//
|
||
|
||
RPC_STATUS
|
||
IP_ADDRESS_RESOLVER::NextAddress(
|
||
OUT SOCKADDR_STORAGE *pAddress
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the next IP address associated with the Name
|
||
parameter to the constructor.
|
||
|
||
During the first call if check for loopback and for dotted numeric IP
|
||
address formats. If these fail then it begins a complex lookup
|
||
(WSALookupServiceBegin) and returns the first available address.
|
||
|
||
During successive calls in which a complex lookup was started
|
||
it returns sucessive addressed returned by WSALookupServiceNext().
|
||
|
||
Arguments:
|
||
|
||
pAddress - If successful, the pAddress->sin_addr.s_addr member is set
|
||
to an IP address to try. For all cosClients, if IPvToUse is ipvtuIPAny,
|
||
pAddress->sin_family is set to the actual address family for the
|
||
returned address. This allows client to find out what address was
|
||
returned to them.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK - pAddress contains a new IP address
|
||
|
||
RPC_S_SERVER_UNAVAILABLE - Unable to find any more addresses
|
||
RPC_S_OUT_OF_MEMORY
|
||
|
||
--*/
|
||
{
|
||
int err;
|
||
RPC_STATUS status;
|
||
SOCKADDR_IN6 *IPv6Address = (SOCKADDR_IN6 *)pAddress;
|
||
SOCKADDR_STORAGE addr;
|
||
int ai_flags;
|
||
int i;
|
||
USES_CONVERSION;
|
||
CStackAnsi AnsiName;
|
||
BOOL fValidIPv4;
|
||
BOOL fValidIPv6;
|
||
ADDRINFO *ThisAddrInfo;
|
||
|
||
if (!AddrInfo)
|
||
{
|
||
if (!Name)
|
||
{
|
||
if (cos == cosServer)
|
||
{
|
||
if (IPvToUse == ipvtuIPv6)
|
||
{
|
||
IPv6Address->sin6_flowinfo = 0;
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) ) = 0;
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) + 1) = 0;
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) + 2) = 0;
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) + 3) = 0;
|
||
IPv6Address->sin6_scope_id = 0;
|
||
}
|
||
else
|
||
{
|
||
ASSERT(IPvToUse == ipvtuIPv4);
|
||
((SOCKADDR_IN *)pAddress)->sin_addr.s_addr = INADDR_ANY;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (LoopbacksReturned > 2)
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_S_SERVER_UNAVAILABLE,
|
||
EEInfoDLNextAddress40);
|
||
return RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
|
||
if ((IPvToUse == ipvtuIPv6)
|
||
|| ((IPvToUse == ipvtuIPAny)
|
||
&&
|
||
(LoopbacksReturned == 1)
|
||
)
|
||
)
|
||
{
|
||
IPv6Address->sin6_family = AF_INET6;
|
||
IPv6Address->sin6_flowinfo = 0;
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) ) = 0;
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) + 1) = 0;
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) + 2) = 0;
|
||
*((u_long *)(IPv6Address->sin6_addr.s6_addr) + 3) = 1;
|
||
IPv6Address->sin6_scope_id = 0;
|
||
}
|
||
else if ((IPvToUse == ipvtuIPv4)
|
||
|| ((IPvToUse == ipvtuIPAny)
|
||
&&
|
||
(LoopbacksReturned == 0)
|
||
)
|
||
)
|
||
{
|
||
// Loopback - assign result of htonl(INADDR_LOOPBACK)
|
||
// Little-endian dependence.
|
||
((SOCKADDR_IN *)pAddress)->sin_addr.s_addr = 0x0100007F;
|
||
((SOCKADDR_IN *)pAddress)->sin_family = AF_INET;
|
||
}
|
||
else
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_S_SERVER_UNAVAILABLE,
|
||
EEInfoDLNextAddress40);
|
||
return RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
|
||
LoopbacksReturned ++;
|
||
}
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
if (cos == cosServer)
|
||
ai_flags = AI_PASSIVE;
|
||
else
|
||
ai_flags = 0;
|
||
|
||
switch (IPvToUse)
|
||
{
|
||
case ipvtuIPAny:
|
||
// make a hint for any protocol
|
||
Hint.ai_flags = ai_flags | AI_NUMERICHOST;
|
||
Hint.ai_family = PF_UNSPEC;
|
||
break;
|
||
|
||
case ipvtuIPv4:
|
||
// make a hint for any v4 protocol
|
||
Hint.ai_flags = ai_flags | AI_NUMERICHOST;
|
||
Hint.ai_family = AF_INET;
|
||
break;
|
||
|
||
case ipvtuIPv6:
|
||
// make a hint for TCPv6
|
||
Hint.ai_flags = ai_flags | AI_NUMERICHOST;
|
||
Hint.ai_family = AF_INET6;
|
||
break;
|
||
|
||
default:
|
||
ASSERT((IPvToUse == ipvtuIPAny)
|
||
|| (IPvToUse == ipvtuIPv4)
|
||
|| (IPvToUse == ipvtuIPv6));
|
||
}
|
||
|
||
ATTEMPT_STACK_W2A(AnsiName, Name);
|
||
|
||
err = getaddrinfo(AnsiName,
|
||
NULL,
|
||
&Hint,
|
||
&AddrInfo);
|
||
|
||
if (err)
|
||
{
|
||
ASSERT((err != EAI_BADFLAGS)
|
||
&& (err != EAI_SOCKTYPE));
|
||
|
||
// take down the numeric hosts flag - we'll try
|
||
// to resolve it as a DNS name
|
||
Hint.ai_flags &= ~AI_NUMERICHOST;
|
||
|
||
err = getaddrinfo(AnsiName,
|
||
NULL,
|
||
&Hint,
|
||
&AddrInfo);
|
||
|
||
if (err)
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
err,
|
||
EEInfoDLNextAddress10,
|
||
Name);
|
||
if (err == EAI_MEMORY)
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_S_OUT_OF_MEMORY,
|
||
EEInfoDLNextAddress20);
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
else
|
||
{
|
||
VALIDATE(err)
|
||
{
|
||
EAI_AGAIN,
|
||
EAI_FAMILY,
|
||
EAI_FAIL,
|
||
EAI_NODATA,
|
||
EAI_NONAME,
|
||
EAI_SERVICE
|
||
} END_VALIDATE;
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_S_SERVER_UNAVAILABLE,
|
||
EEInfoDLNextAddress30);
|
||
return RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
// successfully resolved this address
|
||
// just stick it in and we'll let the code below handle it
|
||
CurrentAddrInfo = AddrInfo;
|
||
}
|
||
|
||
ASSERT(AddrInfo != NULL);
|
||
|
||
// get the next value from the cache
|
||
while (CurrentAddrInfo)
|
||
{
|
||
ThisAddrInfo = CurrentAddrInfo;
|
||
CurrentAddrInfo = CurrentAddrInfo->ai_next;
|
||
|
||
fValidIPv4 = FALSE;
|
||
fValidIPv6 = FALSE;
|
||
|
||
if (ThisAddrInfo->ai_family == AF_INET)
|
||
{
|
||
fValidIPv4 = TRUE;
|
||
}
|
||
|
||
if (ThisAddrInfo->ai_family == AF_INET6)
|
||
{
|
||
fValidIPv6 = TRUE;
|
||
}
|
||
|
||
if ((IPvToUse == ipvtuIPv4) && !fValidIPv4)
|
||
continue;
|
||
|
||
if ((IPvToUse == ipvtuIPv6) && !fValidIPv6)
|
||
continue;
|
||
|
||
if ((IPvToUse == ipvtuIPAny) && !fValidIPv4 && !fValidIPv6)
|
||
continue;
|
||
|
||
if (ThisAddrInfo->ai_family == AF_INET)
|
||
{
|
||
ASSERT((IPvToUse == ipvtuIPv4)
|
||
|| (IPvToUse == ipvtuIPAny));
|
||
RpcpCopyIPv4Address((SOCKADDR_IN *)ThisAddrInfo->ai_addr, (SOCKADDR_IN *)pAddress);
|
||
((SOCKADDR_IN *)pAddress)->sin_family = AF_INET;
|
||
}
|
||
else
|
||
{
|
||
ASSERT((IPvToUse == ipvtuIPv6)
|
||
|| (IPvToUse == ipvtuIPAny));
|
||
RpcpCopyIPv6Address((SOCKADDR_IN6 *)ThisAddrInfo->ai_addr, IPv6Address);
|
||
IPv6Address->sin6_family = AF_INET6;
|
||
IPv6Address->sin6_scope_id = ((SOCKADDR_IN6 *)ThisAddrInfo->ai_addr)->sin6_scope_id;
|
||
IPv6Address->sin6_flowinfo = 0;
|
||
}
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_S_SERVER_UNAVAILABLE,
|
||
EEInfoDLNextAddress40);
|
||
|
||
return RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
|
||
IP_ADDRESS_RESOLVER::~IP_ADDRESS_RESOLVER()
|
||
{
|
||
if (AddrInfo)
|
||
freeaddrinfo(AddrInfo);
|
||
}
|
||
|
||
RPC_STATUS
|
||
IP_BuildAddressVector(
|
||
OUT NETWORK_ADDRESS_VECTOR **ppAddressVector,
|
||
IN ULONG NICFlags,
|
||
IN RPC_CHAR *NetworkAddress OPTIONAL,
|
||
IN WS_ADDRESS *Address OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Builds a vector of IP addresses supported by this machine.
|
||
|
||
Arguments:
|
||
|
||
ppAddressVector - A place to store the vector.
|
||
NICFlags - the flags as specified in the RPC_POLICY of the
|
||
RpcServerUse*Protseq* APIs
|
||
NetworkAddess - the network address we were asked to listen
|
||
on. May be NULL.
|
||
Address - in the case of firewall, the addresses we chose to
|
||
listen on.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Figure out all of our IP addresses
|
||
//
|
||
NETWORK_ADDRESS_VECTOR *pVector;
|
||
unsigned i;
|
||
RPC_CHAR *NextAddress;
|
||
int NumberOfAddresses;
|
||
WS_ADDRESS *CurrentAddress;
|
||
|
||
if ((pFirewallTable == 0 || NICFlags == RPC_C_BIND_TO_ALL_NICS) && !NetworkAddress)
|
||
{
|
||
ULONG Ignored;
|
||
|
||
// Get Dns hostname
|
||
pVector = (NETWORK_ADDRESS_VECTOR *)AllocateAndGetComputerName(cnaNew,
|
||
ComputerNameDnsHostname,
|
||
FIELD_OFFSET(NETWORK_ADDRESS_VECTOR, NetworkAddresses[1]),
|
||
FIELD_OFFSET(NETWORK_ADDRESS_VECTOR, NetworkAddresses[1]),
|
||
&Ignored);
|
||
if (pVector == NULL)
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_S_OUT_OF_MEMORY,
|
||
EEInfoDLIPBuildAddressVector10,
|
||
GetLastError());
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
|
||
pVector->Count = 1;
|
||
pVector->NetworkAddresses[0] = (RPC_CHAR*)&pVector->NetworkAddresses[1];
|
||
}
|
||
else if (NetworkAddress)
|
||
{
|
||
// the length of the network address including the terminating NULL
|
||
// (in characters)
|
||
int NetworkAddressLength;
|
||
#if DBG
|
||
{
|
||
// if we have a network address, it must be in IP address notation
|
||
// make an ASSERT to verify that nobody ever passes an dns name here
|
||
ADDRINFO Hint;
|
||
ADDRINFO *AddrInfo;
|
||
USES_CONVERSION;
|
||
CStackAnsi AnsiName;
|
||
int err;
|
||
|
||
RpcpMemorySet(&Hint, 0, sizeof(Hint));
|
||
Hint.ai_flags = AI_NUMERICHOST;
|
||
ATTEMPT_STACK_W2A(AnsiName, NetworkAddress);
|
||
|
||
err = getaddrinfo(AnsiName,
|
||
NULL,
|
||
&Hint,
|
||
&AddrInfo);
|
||
|
||
// this is a numeric address. It should never fail
|
||
ASSERT (!err);
|
||
}
|
||
#endif
|
||
|
||
NetworkAddressLength = RpcpStringLength(NetworkAddress) + 1;
|
||
|
||
pVector = (NETWORK_ADDRESS_VECTOR *)
|
||
I_RpcAllocate( sizeof(NETWORK_ADDRESS_VECTOR)
|
||
+ sizeof(RPC_CHAR *)
|
||
+ (sizeof(RPC_CHAR) * NetworkAddressLength));
|
||
if (pVector == NULL)
|
||
{
|
||
return (RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
pVector->Count = 1;
|
||
|
||
NextAddress = (RPC_CHAR *)&pVector->NetworkAddresses[1];
|
||
|
||
pVector->NetworkAddresses[0] = NextAddress;
|
||
|
||
RpcpMemoryCopy(NextAddress, NetworkAddress, NetworkAddressLength * 2);
|
||
}
|
||
else
|
||
{
|
||
NumberOfAddresses = 0;
|
||
CurrentAddress = Address;
|
||
while (CurrentAddress != NULL)
|
||
{
|
||
NumberOfAddresses ++;
|
||
CurrentAddress = (WS_ADDRESS *)CurrentAddress->pNextAddress;
|
||
}
|
||
|
||
pVector = (NETWORK_ADDRESS_VECTOR *)
|
||
I_RpcAllocate( sizeof(NETWORK_ADDRESS_VECTOR)
|
||
+ (sizeof(RPC_CHAR *)
|
||
+ max(IPv6_MAXIMUM_RAW_NAME, IP_MAXIMUM_RAW_NAME) * sizeof(RPC_CHAR))
|
||
* NumberOfAddresses);
|
||
if (pVector == NULL)
|
||
{
|
||
return (RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
pVector->Count = NumberOfAddresses;
|
||
|
||
RPC_CHAR *NextAddress = (RPC_CHAR*)&pVector->
|
||
NetworkAddresses[NumberOfAddresses];
|
||
IN_ADDR addr;
|
||
SOCKADDR_IN6 *Ipv6Address;
|
||
unsigned j;
|
||
|
||
CurrentAddress = Address;
|
||
for (i = 0; i < NumberOfAddresses; i++)
|
||
{
|
||
pVector->NetworkAddresses[i] = NextAddress;
|
||
|
||
if (CurrentAddress->id != TCP_IPv6)
|
||
{
|
||
addr.s_addr = ((SOCKADDR_IN *)(&CurrentAddress->ListenAddr.inetaddr))->sin_addr.s_addr;
|
||
|
||
swprintf((RPC_SCHAR *)NextAddress, RPC_CONST_SSTRING("%d.%d.%d.%d"),
|
||
addr.s_net, addr.s_host, addr.s_lh, addr.s_impno);
|
||
}
|
||
else
|
||
{
|
||
Ipv6Address = (SOCKADDR_IN6 *)(&CurrentAddress->ListenAddr.inetaddr);
|
||
|
||
|
||
swprintf((RPC_SCHAR *)NextAddress, RPC_CONST_SSTRING("%X:%X:%X:%X:%X:%X:%X:%X"),
|
||
Ipv6Address->sin6_addr.u.Word[0],
|
||
Ipv6Address->sin6_addr.u.Word[1],
|
||
Ipv6Address->sin6_addr.u.Word[2],
|
||
Ipv6Address->sin6_addr.u.Word[3],
|
||
Ipv6Address->sin6_addr.u.Word[4],
|
||
Ipv6Address->sin6_addr.u.Word[5],
|
||
Ipv6Address->sin6_addr.u.Word[6],
|
||
Ipv6Address->sin6_addr.u.Word[7]
|
||
);
|
||
}
|
||
NextAddress += max(IPv6_MAXIMUM_RAW_NAME, IP_MAXIMUM_RAW_NAME);
|
||
CurrentAddress = (WS_ADDRESS *)CurrentAddress->pNextAddress;
|
||
}
|
||
}
|
||
|
||
|
||
*ppAddressVector = pVector;
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
WS_Bind(
|
||
IN SOCKET sock,
|
||
IN OUT WS_SOCKADDR *pListenAddr,
|
||
IN BOOL IpProtocol,
|
||
IN DWORD EndpointFlags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Binds the socket to a port. Takes into account the endpoint flags
|
||
and the value of the port in the WS_SOCKADDR. There is IP specific
|
||
code for firewalls which is keyed off of the EndpointFlags.
|
||
|
||
Arguments:
|
||
|
||
sock - The socket to bind
|
||
pListenAddr - On input the sin_port member is checked. For fixed endpoints
|
||
this is set and is the only address bound to. On output it contains
|
||
the info on the fully bound socket.
|
||
IpProtocol - Whether this is an IP protocol. TRUE for TCP/IP and HTTP.
|
||
EndpointFlags - see RpcTrans.hxx. If non-zero and we're allocating a
|
||
dynamic port then we must call the runtime.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_DUPLICATE_ENDPOINT : when a fixed endpoint is already in use.
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S_CANT_CREATE_ENDPOINT
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS status;
|
||
int Retries = 0;
|
||
BOOL fFirewallPorts = FALSE;
|
||
BOOL fSetSockOptFailed;
|
||
DWORD LastError;
|
||
USHORT Port;
|
||
|
||
if (IpProtocol && (RpcpGetIpPort(pListenAddr) == 0))
|
||
{
|
||
Retries = 8;
|
||
}
|
||
|
||
do
|
||
{
|
||
if (Retries)
|
||
{
|
||
status = I_RpcServerAllocateIpPort(EndpointFlags, &Port);
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
status,
|
||
EEInfoDLWSBind10,
|
||
EndpointFlags);
|
||
break;
|
||
}
|
||
|
||
RpcpSetIpPort(pListenAddr, Port);
|
||
|
||
// Check if any firewall ports are defined. If they are remember
|
||
// that so we can map the error correctly.
|
||
|
||
if (!fFirewallPorts && (RpcpGetIpPort(pListenAddr) == 0))
|
||
{
|
||
Retries = 0;
|
||
}
|
||
else
|
||
{
|
||
Retries--;
|
||
fFirewallPorts = TRUE;
|
||
}
|
||
|
||
RpcpSetIpPort(pListenAddr, htons(RpcpGetIpPort(pListenAddr)));
|
||
}
|
||
else
|
||
{
|
||
status = RPC_S_OK;
|
||
}
|
||
|
||
|
||
WS_Bind_Rebind:
|
||
if ( bind(sock,
|
||
&pListenAddr->generic,
|
||
sizeof(WS_SOCKADDR)) )
|
||
{
|
||
|
||
LastError = GetLastError();
|
||
switch(LastError)
|
||
{
|
||
case WSAEACCES:
|
||
fSetSockOptFailed = WS_ProtectListeningSocket(sock, FALSE);
|
||
// if this failed, we have to bail out, or we'll spin
|
||
if (fSetSockOptFailed)
|
||
{
|
||
status = GetLastError();
|
||
RpcpErrorAddRecord(EEInfoGCRuntime,
|
||
status,
|
||
EEInfoDLWSBind20);
|
||
break;
|
||
}
|
||
|
||
goto WS_Bind_Rebind;
|
||
// no break, because we can't end up here
|
||
|
||
case WSAEADDRINUSE:
|
||
{
|
||
status = RPC_S_DUPLICATE_ENDPOINT;
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSBind45,
|
||
(ULONG)ntohs(RpcpGetIpPort(pListenAddr)));
|
||
break;
|
||
}
|
||
|
||
case WSAENOBUFS:
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
LastError,
|
||
EEInfoDLWSBind40);
|
||
status = RPC_S_OUT_OF_MEMORY;
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
LastError,
|
||
EEInfoDLWSBind50);
|
||
status = RPC_S_CANT_CREATE_ENDPOINT;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
while ( (status == RPC_S_DUPLICATE_ENDPOINT) && Retries);
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
if (fFirewallPorts && status == RPC_S_DUPLICATE_ENDPOINT)
|
||
{
|
||
status = RPC_S_OUT_OF_RESOURCES;
|
||
}
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
status,
|
||
EEInfoDLWSBind30);
|
||
return(status);
|
||
}
|
||
|
||
int length = sizeof(WS_SOCKADDR);
|
||
if (getsockname(sock, &pListenAddr->generic, &length))
|
||
{
|
||
return(RPC_S_OUT_OF_RESOURCES);
|
||
}
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
RPC_STATUS
|
||
CDP_BuildAddressVector(
|
||
OUT NETWORK_ADDRESS_VECTOR **ppAddressVector
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Look up the Cluster node number of this node and build
|
||
the appropriate vector
|
||
|
||
Arguments:
|
||
|
||
ppAddressVector - A place to store the vector.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
|
||
--*/
|
||
{
|
||
HKEY ParamsKey;
|
||
NETWORK_ADDRESS_VECTOR * pVector;
|
||
const RPC_CHAR *ClussvcParams =
|
||
RPC_CONST_STRING("System\\CurrentControlSet\\Services\\ClusSvc\\Parameters");
|
||
RPC_CHAR *ClusRegNodeId = RPC_STRING_LITERAL("NodeId");
|
||
DWORD KeyType;
|
||
RPC_CHAR NodeIdString[ CDP_MAXIMUM_RAW_NAME ];
|
||
DWORD StringLength = CDP_MAXIMUM_RAW_NAME * sizeof( RPC_CHAR );
|
||
DWORD status;
|
||
|
||
//
|
||
// open the Clussvc parameters key and extrace the NodeId
|
||
//
|
||
|
||
status = RegOpenKey( HKEY_LOCAL_MACHINE,
|
||
(const RPC_SCHAR *)ClussvcParams,
|
||
&ParamsKey );
|
||
|
||
if ( status != ERROR_SUCCESS )
|
||
{
|
||
return RPC_S_INVALID_NET_ADDR;
|
||
}
|
||
|
||
status = RegQueryValueEx(ParamsKey,
|
||
(const RPC_SCHAR *)ClusRegNodeId,
|
||
NULL,
|
||
&KeyType,
|
||
(LPBYTE)&NodeIdString,
|
||
&StringLength);
|
||
|
||
RegCloseKey( ParamsKey );
|
||
|
||
if ( status != ERROR_SUCCESS ||
|
||
KeyType != REG_SZ ||
|
||
(( StringLength / sizeof( RPC_CHAR )) > CDP_MAXIMUM_RAW_NAME ))
|
||
{
|
||
return RPC_S_INVALID_NET_ADDR;
|
||
}
|
||
|
||
pVector = (NETWORK_ADDRESS_VECTOR *)
|
||
I_RpcAllocate( sizeof(NETWORK_ADDRESS_VECTOR ) +
|
||
sizeof(RPC_CHAR *) +
|
||
StringLength );
|
||
|
||
if (pVector == NULL)
|
||
{
|
||
return(RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
pVector->Count = 1;
|
||
pVector->NetworkAddresses[0] = (RPC_CHAR *)&pVector->NetworkAddresses[1];
|
||
|
||
RpcpStringCopy( pVector->NetworkAddresses[0], NodeIdString );
|
||
|
||
*ppAddressVector = pVector;
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
typedef struct tagIPVersionSettings
|
||
{
|
||
IPVersionToUse IPVersion;
|
||
BOOL fUseIPv6;
|
||
} IPVersionSettings;
|
||
|
||
const static IPVersionSettings ListenIPVersionSettings[2] = {{ipvtuIPv4, FALSE}, {ipvtuIPv6, TRUE}};
|
||
|
||
typedef enum tagIPVersionSettingsIndexes
|
||
{
|
||
ipvsiIPv4SettingsIndex = 0,
|
||
ipvsiIPv6SettingsIndex
|
||
} IPVersionSettingsIndexes;
|
||
|
||
typedef struct tagListenAddressListElement
|
||
{
|
||
INT ProtocolId;
|
||
IPVersionSettingsIndexes IPVersionSettingsIndex;
|
||
BOOL fSuccessfullyInitialized;
|
||
RPC_STATUS ErrorCode;
|
||
union
|
||
{
|
||
SOCKADDR_IN6 inet6Addr;
|
||
SOCKADDR_IN inetAddr;
|
||
} u;
|
||
} ListenAddressListElement;
|
||
|
||
typedef struct tagTCPServerListenParams
|
||
{
|
||
INT ProtocolId;
|
||
IPVersionSettingsIndexes IPVersionSettingsIndex;
|
||
} TCPServerListenParams;
|
||
|
||
const static TCPServerListenParams HTTPListenParams[1] = {HTTP, ipvsiIPv4SettingsIndex};
|
||
const static TCPServerListenParams TCPListenParams[2] =
|
||
{{TCP, ipvsiIPv4SettingsIndex},
|
||
{TCP_IPv6, ipvsiIPv6SettingsIndex}};
|
||
const int MAX_TCP_SERVER_LISTEN_LOOP_ITERATIONS = 2;
|
||
|
||
RPC_STATUS
|
||
TCP_ServerListenEx(
|
||
IN RPC_TRANSPORT_ADDRESS ThisAddress,
|
||
IN RPC_CHAR *NetworkAddress,
|
||
IN OUT RPC_CHAR * *pEndpoint,
|
||
IN UINT PendingQueueSize,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN ULONG EndpointFlags,
|
||
IN ULONG NICFlags,
|
||
IN BOOL fHttp,
|
||
OUT NETWORK_ADDRESS_VECTOR **ppAddressVector
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allocates a port to receive new client connections.
|
||
If successful a call to COMMON_CompleteListen() will actually allow
|
||
new connection callbacks to the RPC runtime to occur. If the runtime
|
||
is unable to complete then it must abort the address by calling
|
||
WS_ServerAbortListen().
|
||
|
||
Arguments:
|
||
|
||
ThisAddress - A pointer to the loadable transport interface address.
|
||
NetworkAddress - the address to listen on. This can be specified for
|
||
IP only, and it *cannot* be a DNS name. If it is, this function
|
||
will work incorrectly for multihomed/multi IP machines.
|
||
pEndpoint - Optionally, the endpoint (port) to listen on. Set to
|
||
to listened port for dynamically allocated endpoints.
|
||
PendingQueueSize - Count to call listen() with.
|
||
EndpointFlags - Flags that control dynamic port allocation
|
||
NICFlags - Flags that control network (IP) address binding
|
||
SecurityDescriptor - Meaningless for TCP
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S_CANT_CREATE_ENDPOINT
|
||
RPC_S_DUPLICATE_ENDPOINT
|
||
|
||
--*/
|
||
{
|
||
PWS_ADDRESS pAddress = (PWS_ADDRESS)ThisAddress;
|
||
RPC_STATUS status;
|
||
WS_SOCKADDR *sockaddr = &(pAddress->ListenAddr);
|
||
unsigned i;
|
||
PWS_ADDRESS pList, pOld;
|
||
int NeededAddressListSize;
|
||
ListenAddressListElement *ListenAddressList;
|
||
ListenAddressListElement *CurrentListElement;
|
||
int AddressListSize;
|
||
SOCKADDR_IN IPv4Address;
|
||
SOCKADDR_IN6 *IPv6Address;
|
||
int LoopIterations;
|
||
TCPServerListenParams *ParamsToUse;
|
||
IPVersionSettingsIndexes IPVersionSettingsIndex;
|
||
BOOL fAtLeastOneAddressInitialized;
|
||
USHORT PortNumber; // in network byte order!
|
||
BOOL fDynamicEndpoint;
|
||
BOOL fFatalErrorEncountered;
|
||
RPC_STATUS FatalErrorCode;
|
||
WS_ADDRESS *LastSuccessfullyInitializedAddress;
|
||
WS_ADDRESS *NextAddress;
|
||
BOOL fDualTransportConfiguration = FALSE;
|
||
BOOL fLoopbackAddressProcessed;
|
||
|
||
// sizing pass - allocate sufficient memory
|
||
if (pFirewallTable == 0 || NICFlags == RPC_C_BIND_TO_ALL_NICS)
|
||
{
|
||
if (fHttp)
|
||
AddressListSize = 1;
|
||
else
|
||
{
|
||
AddressListSize = 2;
|
||
fDualTransportConfiguration = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddressListSize = pFirewallTable->NumAddresses + 1;
|
||
}
|
||
|
||
NeededAddressListSize = AddressListSize * sizeof(ListenAddressListElement);
|
||
|
||
ListenAddressList = new ListenAddressListElement[NeededAddressListSize];
|
||
if (ListenAddressList == NULL)
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
|
||
fAtLeastOneAddressInitialized = FALSE;
|
||
RpcpMemorySet(ListenAddressList,
|
||
0,
|
||
AddressListSize * sizeof(ListenAddressListElement));
|
||
|
||
// processing pass. Set all the required addresses in the array
|
||
if (pFirewallTable == 0 || NICFlags == RPC_C_BIND_TO_ALL_NICS)
|
||
{
|
||
if (fHttp)
|
||
{
|
||
ASSERT(AddressListSize == 1);
|
||
ParamsToUse = (TCPServerListenParams *)HTTPListenParams;
|
||
LoopIterations = 1;
|
||
}
|
||
else
|
||
{
|
||
ParamsToUse = (TCPServerListenParams *)TCPListenParams;
|
||
LoopIterations = 2;
|
||
}
|
||
|
||
for (i = 0; i < LoopIterations; i ++)
|
||
{
|
||
CurrentListElement = &ListenAddressList[i];
|
||
CurrentListElement->ProtocolId = ParamsToUse[i].ProtocolId;
|
||
IPVersionSettingsIndex = ParamsToUse[i].IPVersionSettingsIndex;
|
||
CurrentListElement->IPVersionSettingsIndex = IPVersionSettingsIndex;
|
||
|
||
IP_ADDRESS_RESOLVER resolver(NetworkAddress,
|
||
cosServer,
|
||
ListenIPVersionSettings[IPVersionSettingsIndex].IPVersion // IP version to use
|
||
);
|
||
|
||
// resolve the address. Since this cannot be a DNS name, we will resolve
|
||
// to one address at most. We choose the ipv6 address, because it has space
|
||
// for both. The actual parameter that determines the type of name resolution
|
||
// to be done is IP version to use passed to the constructor
|
||
status = resolver.NextAddress((SOCKADDR_STORAGE *)&CurrentListElement->u.inet6Addr);
|
||
if (status == RPC_S_OK)
|
||
{
|
||
fAtLeastOneAddressInitialized = TRUE;
|
||
CurrentListElement->fSuccessfullyInitialized = TRUE;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fAtLeastOneAddressInitialized = TRUE;
|
||
fLoopbackAddressProcessed = FALSE;
|
||
|
||
for (i = 0; i < AddressListSize; i++)
|
||
{
|
||
CurrentListElement = &ListenAddressList[i];
|
||
|
||
if (fHttp)
|
||
{
|
||
CurrentListElement->ProtocolId = HTTP;
|
||
}
|
||
else
|
||
{
|
||
CurrentListElement->ProtocolId = TCP;
|
||
}
|
||
|
||
CurrentListElement->IPVersionSettingsIndex = ipvsiIPv4SettingsIndex;
|
||
CurrentListElement->fSuccessfullyInitialized = TRUE;
|
||
|
||
if (i == pFirewallTable->NumAddresses)
|
||
{
|
||
CurrentListElement->u.inetAddr.sin_addr.s_addr = 0x0100007F;
|
||
}
|
||
else
|
||
{
|
||
if (pFirewallTable->Addresses[i] == 0x0100007F)
|
||
fLoopbackAddressProcessed = TRUE;
|
||
CurrentListElement->u.inetAddr.sin_addr.s_addr = pFirewallTable->Addresses[i];
|
||
}
|
||
}
|
||
|
||
// if the loopback address was in the firewall configuration, 'forget' about
|
||
// the last entry we added for the loopback address. Otherwise, we'll have
|
||
// it twice in the list, and this will cause errors
|
||
if (fLoopbackAddressProcessed)
|
||
{
|
||
AddressListSize --;
|
||
// since we added one, and fLoopbackAddressProcessed is set only if
|
||
// we find something in the list, then AddressListSize must still be
|
||
// greater than 0.
|
||
ASSERT(AddressListSize > 0);
|
||
}
|
||
}
|
||
|
||
if (fAtLeastOneAddressInitialized)
|
||
{
|
||
fAtLeastOneAddressInitialized = FALSE;
|
||
}
|
||
else
|
||
{
|
||
// the only place where we can fail so far is name resolution. If this
|
||
// fails, return the status
|
||
delete [] ListenAddressList;
|
||
return status;
|
||
}
|
||
|
||
// Figure out what port to bind to.
|
||
|
||
if (*pEndpoint)
|
||
{
|
||
status = EndpointToPortNumber(*pEndpoint, PortNumber);
|
||
if (status != RPC_S_OK)
|
||
{
|
||
delete [] ListenAddressList;
|
||
return(status);
|
||
}
|
||
|
||
PortNumber = htons(PortNumber);
|
||
fDynamicEndpoint = 0;
|
||
}
|
||
else
|
||
{
|
||
PortNumber = 0;
|
||
fDynamicEndpoint = TRUE;
|
||
}
|
||
|
||
// zoom in through the array address, and listen on all successfully initialized
|
||
// protocols
|
||
pList = pAddress;
|
||
fFatalErrorEncountered = FALSE;
|
||
|
||
for (i = 0; i < AddressListSize; i ++)
|
||
{
|
||
CurrentListElement = &ListenAddressList[i];
|
||
if (!CurrentListElement->fSuccessfullyInitialized)
|
||
continue;
|
||
|
||
if (pList == 0)
|
||
{
|
||
pList = new WS_ADDRESS;
|
||
if (pList == 0)
|
||
{
|
||
fFatalErrorEncountered = TRUE;
|
||
|
||
FatalErrorCode = RPC_S_OUT_OF_MEMORY;
|
||
break;
|
||
}
|
||
|
||
pOld->pNextAddress = pList;
|
||
}
|
||
|
||
sockaddr = &(pList->ListenAddr);
|
||
|
||
RpcpMemorySet(sockaddr, 0, sizeof(*sockaddr));
|
||
|
||
// the port we have set is already in network byte order -
|
||
// no need to change it
|
||
RpcpSetIpPort(sockaddr, PortNumber);
|
||
|
||
pList->fDynamicEndpoint = fDynamicEndpoint;
|
||
|
||
if (ListenIPVersionSettings[CurrentListElement->IPVersionSettingsIndex].fUseIPv6)
|
||
{
|
||
((SOCKADDR_IN6 *)sockaddr)->sin6_flowinfo = 0;
|
||
RpcpCopyIPv6Address(&CurrentListElement->u.inet6Addr, (SOCKADDR_IN6 *)sockaddr);
|
||
}
|
||
else
|
||
{
|
||
RpcpCopyIPv4Address(&CurrentListElement->u.inetAddr, (SOCKADDR_IN *)sockaddr);
|
||
}
|
||
|
||
pList->id = CurrentListElement->ProtocolId;
|
||
pList->NewConnection = WS_NewConnection;
|
||
pList->SubmitListen = WS_SubmitAccept;
|
||
SetProtocolMultiplier(pList, 1);
|
||
pList->pAddressVector = 0;
|
||
pList->Endpoint = 0;
|
||
pList->QueueSize = PendingQueueSize;
|
||
pList->EndpointFlags = EndpointFlags;
|
||
|
||
sockaddr->generic.sa_family = WsTransportTable[CurrentListElement->ProtocolId].AddressFamily;
|
||
|
||
// we must know whether we got WSAEAFNOSUPPORT when we opened the socket
|
||
// if yes, we must record this in CurrentElement, and we must not blow
|
||
// the address. If we got something else, we should abort even in dual
|
||
// transport config. The addresses on the firewall are a separate thing -
|
||
// we ignore the ones we can't listen on.
|
||
|
||
// Actually listen
|
||
status = WS_ServerListenCommon(pList);
|
||
|
||
// the next two are actually initialized in WS_ServerListenCommon as well,
|
||
// so we want to override the seetings WS_ServerListenCommon makes
|
||
pList->pFirstAddress = pAddress;
|
||
pList->pNextAddress = NULL;
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
if ((status == RPC_S_DUPLICATE_ENDPOINT)
|
||
|| (fDualTransportConfiguration && (status != RPC_P_ADDRESS_FAMILY_INVALID)))
|
||
{
|
||
// if either somebody else is listening on our port for this address,
|
||
// or this is a dual transport configuratuon, and we faile to listen
|
||
// on one of the transports for reasons other that it not being
|
||
// installed, bail out
|
||
fFatalErrorEncountered = TRUE;
|
||
FatalErrorCode = status;
|
||
break;
|
||
}
|
||
else if (fDualTransportConfiguration && (status == RPC_P_ADDRESS_FAMILY_INVALID))
|
||
{
|
||
pList->InAddressList = Inactive;
|
||
|
||
// we still need to register the address with PnP
|
||
// make sure it's not already there
|
||
ASSERT(RpcpIsListEmpty(&pList->ObjectList));
|
||
TransportProtocol::AddObjectToProtocolList(pList);
|
||
}
|
||
|
||
CurrentListElement->ErrorCode = status;
|
||
|
||
CurrentListElement->fSuccessfullyInitialized = FALSE;
|
||
}
|
||
else
|
||
{
|
||
if (i == 0)
|
||
{
|
||
PortNumber = RpcpGetIpPort(&pAddress->ListenAddr);
|
||
}
|
||
fAtLeastOneAddressInitialized = TRUE;
|
||
}
|
||
|
||
pOld = pList;
|
||
pList = 0;
|
||
}
|
||
|
||
// compact the list by removing addresses we couldn't successfully listen on
|
||
pList = pAddress;
|
||
for (i = 0; i < AddressListSize; i ++)
|
||
{
|
||
// we may have an early break if a memory allocation failed
|
||
if (pList == NULL)
|
||
break;
|
||
|
||
// if this address has not initialized successfully, and this is not
|
||
// the first address, and either none of the elements initialized
|
||
// successfully or this is not a dual transport configuration with
|
||
// error RPC_P_ADDRESS_FAMILY_INVALID, delete the element
|
||
if (!ListenAddressList[i].fSuccessfullyInitialized && (i > 0) &&
|
||
(!fAtLeastOneAddressInitialized
|
||
|| (!fDualTransportConfiguration
|
||
|| (ListenAddressList[i].ErrorCode != RPC_P_ADDRESS_FAMILY_INVALID)
|
||
)
|
||
))
|
||
{
|
||
ASSERT(pOld != pList);
|
||
pOld->pNextAddress = pList->pNextAddress;
|
||
NextAddress = (WS_ADDRESS *)pList->pNextAddress;
|
||
TransportProtocol::RemoveObjectFromProtocolList(pList);
|
||
delete pList;
|
||
}
|
||
else
|
||
{
|
||
pOld = pList;
|
||
NextAddress = (WS_ADDRESS *)pList->pNextAddress;
|
||
}
|
||
|
||
pList = NextAddress;
|
||
}
|
||
|
||
if (!fAtLeastOneAddressInitialized)
|
||
{
|
||
TransportProtocol::RemoveObjectFromProtocolList(pAddress);
|
||
delete [] ListenAddressList;
|
||
if (status == RPC_P_ADDRESS_FAMILY_INVALID)
|
||
status = RPC_S_PROTSEQ_NOT_SUPPORTED;
|
||
return status;
|
||
}
|
||
|
||
// the attempt to listen on dual protocol configurations may have left EEInfo
|
||
// record in the TEB. If we're here, we have succeeded, and we can delete them
|
||
RpcpPurgeEEInfo();
|
||
|
||
if (!ListenAddressList[0].fSuccessfullyInitialized)
|
||
{
|
||
// here, pOld must be the last successfully initialized element
|
||
// or the last element we haven't given up on (i.e. potentially
|
||
// active through PnP)
|
||
// It cannot be the first element, or we would have bailed out
|
||
// by now. We cannot have only one element either
|
||
ASSERT(pOld->pNextAddress == NULL);
|
||
ASSERT(pAddress->pNextAddress != NULL);
|
||
|
||
// here at least one has succeeded and all non-first failed
|
||
// elements are deleted, though a fatal error may have been
|
||
// encountered. We need to deal with the first element
|
||
// because we don't want to expose elements with failed
|
||
// initialization outside this routine
|
||
if (!(fDualTransportConfiguration && (ListenAddressList[0].ErrorCode == RPC_P_ADDRESS_FAMILY_INVALID)))
|
||
{
|
||
// remove the element we will copy to the first element
|
||
TransportProtocol::RemoveObjectFromProtocolList(pOld);
|
||
|
||
NextAddress = (WS_ADDRESS *)pAddress->pNextAddress;
|
||
RpcpMemoryCopy(pAddress, pOld, sizeof(WS_ADDRESS));
|
||
pAddress->pNextAddress = NextAddress;
|
||
|
||
// find the element we just copied over the first, and free it
|
||
LastSuccessfullyInitializedAddress = pOld;
|
||
pList = pAddress;
|
||
while (pList->pNextAddress != LastSuccessfullyInitializedAddress)
|
||
{
|
||
pList = (WS_ADDRESS *)pList->pNextAddress;
|
||
}
|
||
|
||
delete pList->pNextAddress;
|
||
pList->pNextAddress = NULL;
|
||
|
||
// add the first element back on the list.
|
||
TransportProtocol::AddObjectToProtocolList(pAddress);
|
||
}
|
||
}
|
||
|
||
delete [] ListenAddressList;
|
||
|
||
// by now all elements in the list have listened successfully
|
||
// or are in transport PnP state. However
|
||
// if we encountered a fatal error, we need to abort any way
|
||
if (fFatalErrorEncountered)
|
||
{
|
||
WS_ServerAbortListen(pAddress);
|
||
|
||
// fatal error - bail out
|
||
return FatalErrorCode;
|
||
}
|
||
|
||
// Listened okay
|
||
|
||
// Figure out our network addresses
|
||
status = IP_BuildAddressVector(
|
||
&pAddress->pAddressVector,
|
||
NICFlags,
|
||
NetworkAddress,
|
||
pAddress);
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
WS_ServerAbortListen(pAddress);
|
||
return(status);
|
||
}
|
||
|
||
*ppAddressVector = pAddress->pAddressVector;
|
||
|
||
// Return the dynamic port, if needed.
|
||
|
||
if (!*pEndpoint)
|
||
{
|
||
*pEndpoint = new RPC_CHAR[6]; // 65535 max
|
||
if (!*pEndpoint)
|
||
{
|
||
WS_ServerAbortListen(ThisAddress);
|
||
return(RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
PortNumber = ntohs(PortNumber);
|
||
|
||
PortNumberToEndpoint(PortNumber, *pEndpoint);
|
||
}
|
||
|
||
// Save away the endpoint in the address, if needed, here.
|
||
// (PnP?)
|
||
|
||
return(status);
|
||
}
|
||
|
||
|
||
|
||
|
||
RPC_STATUS
|
||
TCP_ServerListen(
|
||
IN RPC_TRANSPORT_ADDRESS ThisAddress,
|
||
IN RPC_CHAR *NetworkAddress,
|
||
IN OUT RPC_CHAR * *pEndpoint,
|
||
IN UINT PendingQueueSize,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN ULONG EndpointFlags,
|
||
IN ULONG NICFlags,
|
||
OUT NETWORK_ADDRESS_VECTOR **ppAddressVector
|
||
)
|
||
{
|
||
|
||
return (TCP_ServerListenEx(
|
||
ThisAddress,
|
||
NetworkAddress,
|
||
pEndpoint,
|
||
PendingQueueSize,
|
||
SecurityDescriptor,
|
||
EndpointFlags,
|
||
NICFlags,
|
||
FALSE, // Not HTTP
|
||
ppAddressVector
|
||
));
|
||
}
|
||
|
||
RPC_STATUS
|
||
WS_ConvertClientAddress (
|
||
IN const SOCKADDR *ClientAddress,
|
||
IN ULONG ClientAddressType,
|
||
OUT RPC_CHAR **pNetworkAddress
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Converts a given IP address to a RPC network address
|
||
|
||
Arguments:
|
||
|
||
ClientAddress - the client IP address. Can be SOCKADDR_IN6
|
||
for IPv6.
|
||
|
||
ClientAddressType - TCP or TCP_IPv6
|
||
|
||
NetworkAddress - Will contain string on success.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK or other RPC_S_* errors for error
|
||
|
||
--*/
|
||
{
|
||
USES_CONVERSION;
|
||
CNlUnicode nlUnicode;
|
||
int Result;
|
||
int SocketLength;
|
||
int HostLength;
|
||
char *HostName;
|
||
char *ScopeIdSeparator;
|
||
|
||
ASSERT((ClientAddressType == TCP) || (ClientAddressType == TCP_IPv6));
|
||
|
||
if (ClientAddressType == TCP)
|
||
SocketLength = sizeof(SOCKADDR_IN);
|
||
else
|
||
SocketLength = sizeof(SOCKADDR_IN6);
|
||
|
||
// allocate space for the numeric name plus the terminating NULL
|
||
HostLength = max(IP_MAXIMUM_RAW_NAME, IPv6_MAXIMUM_RAW_NAME) + 1;
|
||
HostName = (char *)alloca(HostLength);
|
||
|
||
Result = getnameinfo(ClientAddress,
|
||
SocketLength,
|
||
HostName,
|
||
HostLength,
|
||
NULL,
|
||
0,
|
||
NI_NUMERICHOST);
|
||
|
||
ASSERT(Result == 0);
|
||
|
||
ScopeIdSeparator = strchr(HostName, '%');
|
||
if (ScopeIdSeparator)
|
||
{
|
||
// if there is a scope separator, whack everything after
|
||
// the scope separator (i.e. we don't care about the scope).
|
||
*ScopeIdSeparator = 0;
|
||
}
|
||
|
||
ATTEMPT_NL_A2W(nlUnicode, HostName);
|
||
|
||
*pNetworkAddress = (WCHAR *)nlUnicode;
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
TCP_QueryClientAddress (
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
OUT RPC_CHAR **pNetworkAddress
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the IP address of the client on a connection as a string.
|
||
The clients address is saved when the client connects, so all
|
||
we need to do is format the address.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - The server connection of interest.
|
||
NetworkAddress - Will contain string on success.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK or other RPC_S_* errors for error
|
||
|
||
--*/
|
||
{
|
||
PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
|
||
|
||
return WS_ConvertClientAddress((const SOCKADDR *)&p->saClientAddress.ipaddr,
|
||
p->id,
|
||
pNetworkAddress
|
||
);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
TCP_QueryLocalAddress (
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
IN OUT void *Buffer,
|
||
IN OUT unsigned long *BufferSize,
|
||
OUT unsigned long *AddressFormat
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the local IP address of a connection.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - The server connection of interest.
|
||
|
||
Buffer - The buffer that will receive the output address
|
||
|
||
BufferSize - the size of the supplied Buffer on input. On output the
|
||
number of bytes written to the buffer. If the buffer is too small
|
||
to receive all the output data, ERROR_MORE_DATA is returned,
|
||
nothing is written to the buffer, and BufferSize is set to
|
||
the size of the buffer needed to return all the data.
|
||
|
||
AddressFormat - a constant indicating the format of the returned address.
|
||
Currently supported are RPC_P_ADDR_FORMAT_TCP_IPV4 and
|
||
RPC_P_ADDR_FORMAT_TCP_IPV6. Undefined on failure.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK or other RPC_S_* errors for error
|
||
|
||
--*/
|
||
{
|
||
PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
|
||
int MinimumBufferLength;
|
||
int Result;
|
||
const WS_TRANS_INFO *pInfo = &WsTransportTable[p->id];
|
||
|
||
ASSERT(p->type & SERVER);
|
||
|
||
if ((p->id == TCP) || (p->id == HTTP))
|
||
{
|
||
MinimumBufferLength = sizeof(SOCKADDR_IN);
|
||
*AddressFormat = RPC_P_ADDR_FORMAT_TCP_IPV4;
|
||
}
|
||
else
|
||
{
|
||
ASSERT(p->id == TCP_IPv6);
|
||
MinimumBufferLength = sizeof(SOCKADDR_STORAGE);
|
||
*AddressFormat = RPC_P_ADDR_FORMAT_TCP_IPV6;
|
||
}
|
||
|
||
if (*BufferSize < MinimumBufferLength)
|
||
{
|
||
*BufferSize = MinimumBufferLength;
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
ASSERT(p->pAddress);
|
||
|
||
p->StartingOtherIO();
|
||
|
||
if (p->fAborted)
|
||
{
|
||
p->OtherIOFinished();
|
||
return(RPC_S_NO_CONTEXT_AVAILABLE);
|
||
}
|
||
|
||
Result = setsockopt(p->Conn.Socket,
|
||
SOL_SOCKET,
|
||
SO_UPDATE_ACCEPT_CONTEXT,
|
||
(char *)&p->pAddress->ListenSocket,
|
||
sizeof(p->pAddress->ListenSocket) );
|
||
|
||
if (Result != SOCKET_ERROR)
|
||
{
|
||
Result = getsockname(p->Conn.Socket,
|
||
(sockaddr *)Buffer,
|
||
(int *) BufferSize);
|
||
|
||
// SO_UPDATE_ACCEPT_CONTEXT has the nasty habit of deleting
|
||
// all of our socker options. Restore them
|
||
WS_SetSockOptForConnection(pInfo, p->Conn.Socket);
|
||
|
||
p->OtherIOFinished();
|
||
|
||
if (Result == SOCKET_ERROR)
|
||
{
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_S_OUT_OF_MEMORY,
|
||
EEInfoDLTCP_QueryLocalAddress10,
|
||
(ULONGLONG) p->Conn.Socket,
|
||
GetLastError());
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
p->OtherIOFinished();
|
||
|
||
RpcpErrorAddRecord(EEInfoGCWinsock,
|
||
RPC_S_OUT_OF_MEMORY,
|
||
EEInfoDLTCP_QueryLocalAddress20,
|
||
(ULONGLONG) p->Conn.Socket,
|
||
GetLastError());
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
TCP_QueryClientId(
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
OUT RPC_CLIENT_PROCESS_IDENTIFIER *ClientProcess
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
For secure protocols (which TCP/IP is not) this is suppose to
|
||
give an ID which will be shared by all clients from the same
|
||
process. This prevents one user from grabbing another users
|
||
association group and using their context handles.
|
||
|
||
Since TCP/IP is not secure we return the IP address of the
|
||
client machine. This limits the attacks to other processes
|
||
running on the client machine which is better than nothing.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - Server connection in question.
|
||
ClientProcess - Transport identification of the "client".
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OUT
|
||
|
||
--*/
|
||
{
|
||
PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
|
||
|
||
ASSERT(p->type & SERVER);
|
||
|
||
// Currently, we don't have an efficient way of determining which clients
|
||
// are local, and which remote. Since some clients grant more permissions
|
||
// to local clients, we want to be on the safe side, and return all TCP
|
||
// clients as remote.
|
||
ClientProcess->ZeroOut();
|
||
if (p->id != TCP_IPv6)
|
||
{
|
||
ClientProcess->SetIPv4ClientIdentifier(p->saClientAddress.inetaddr.sin_addr.s_addr,
|
||
FALSE // fLocal
|
||
);
|
||
}
|
||
else
|
||
{
|
||
ClientProcess->SetIPv6ClientIdentifier(&p->saClientAddress.ipaddr,
|
||
sizeof(p->saClientAddress.ipaddr),
|
||
FALSE // fLocal
|
||
);
|
||
}
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
RPC_STATUS
|
||
TCPOrHTTP_Open(
|
||
IN WS_CONNECTION *Connection,
|
||
IN RPC_CHAR * NetworkAddress,
|
||
IN USHORT Endpoint,
|
||
IN UINT ConnTimeout,
|
||
IN UINT SendBufferSize,
|
||
IN UINT RecvBufferSize,
|
||
IN OUT TCPResolverHint *Hint,
|
||
IN BOOL fHintInitialized,
|
||
IN ULONG CallTimeout,
|
||
IN BOOL fHTTP2Open,
|
||
IN I_RpcProxyIsValidMachineFn IsValidMachineFn OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Opens a connection to a server.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - A place to store the connection
|
||
NetworkAddress - The name of the server, either a dot address or DNS name
|
||
Endpoint - the port number in host byte order representation
|
||
ConnTimeout - See RpcMgmtSetComTimeout
|
||
0 - Min
|
||
5 - Default
|
||
9 - Max
|
||
10 - Infinite
|
||
SendBufferSize -
|
||
RecvBufferSize - (Both optional) Specifies the size of the send/recv
|
||
transport buffers.
|
||
ResolverHint - Resolver hint
|
||
fHintInitialized - If TRUE, the ResolveHint contains the IP address
|
||
of the server. If FALSE, do standard name resolution.
|
||
CallTimeout - the call timeout in milliseconds
|
||
fHTTP2Open - non-zero if this is an HTTP2 Open
|
||
IsValidMachineFn - a callback function that is used to validate machine/port
|
||
for access from this process. Used by HTTP only.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S_SERVER_UNAVAILABLE
|
||
RPC_S_INVALID_ENDPOINT_FORMAT
|
||
RPC_S_INVALID_NET_ADDR
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS status;
|
||
WS_SOCKADDR sa;
|
||
PVOID rnrContext;
|
||
BOOL fIPv4Hint;
|
||
USES_CONVERSION;
|
||
CStackAnsi AnsiName;
|
||
BOOL NetworkAddressConverted;
|
||
char *DotName;
|
||
char DotNameBuffer[max(IP_MAXIMUM_RAW_NAME, IPv6_MAXIMUM_RAW_NAME) + 1];
|
||
|
||
ASSERT(NetworkAddress);
|
||
|
||
// All this function needs to is initialize transport specific
|
||
// parts of the connection and sockaddr. This includes resolving
|
||
// the network address into a `raw' address.
|
||
|
||
RpcpSetIpPort(&sa, htons(Endpoint));
|
||
|
||
if (fHTTP2Open)
|
||
{
|
||
Connection->id = HTTPv2;
|
||
NetworkAddressConverted = FALSE;
|
||
}
|
||
else
|
||
Connection->id = TCP;
|
||
|
||
// Two cases, previously saved address or first open
|
||
|
||
if (fHintInitialized)
|
||
{
|
||
Hint->GetResolverHint(&fIPv4Hint, &sa);
|
||
|
||
if (!fIPv4Hint)
|
||
Connection->id = TCP_IPv6;
|
||
|
||
ASSERT(IsValidMachineFn == FALSE);
|
||
|
||
status = WS_Open((PWS_CCONNECTION)Connection,
|
||
&sa,
|
||
ConnTimeout,
|
||
SendBufferSize,
|
||
RecvBufferSize,
|
||
CallTimeout,
|
||
fHTTP2Open
|
||
);
|
||
if (status == ERROR_RETRY)
|
||
{
|
||
status = RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
|
||
return(status);
|
||
}
|
||
|
||
// Prepare to resolve the network address
|
||
|
||
IP_ADDRESS_RESOLVER resolver(NetworkAddress,
|
||
cosClient,
|
||
ipvtuIPAny // IP version to use
|
||
);
|
||
|
||
// Loop until success, fatal failure or we run out of addresses.
|
||
|
||
do
|
||
{
|
||
status = resolver.NextAddress(&sa.ipaddr);
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
break;
|
||
}
|
||
|
||
if (IsValidMachineFn)
|
||
{
|
||
ASSERT(fHTTP2Open != FALSE);
|
||
if (NetworkAddressConverted == FALSE)
|
||
{
|
||
ATTEMPT_STACK_W2A(AnsiName, NetworkAddress);
|
||
NetworkAddressConverted = TRUE;
|
||
}
|
||
|
||
DotName = inet_ntoa(sa.inetaddr.sin_addr);
|
||
strcpy(DotNameBuffer, DotName);
|
||
|
||
status = IsValidMachineFn(AnsiName,
|
||
DotNameBuffer,
|
||
Endpoint
|
||
);
|
||
|
||
// if the server is not allowed for access, continue with next
|
||
if (status != RPC_S_OK)
|
||
continue;
|
||
}
|
||
|
||
if (sa.ipaddr.ss_family == AF_INET6)
|
||
Connection->id = TCP_IPv6;
|
||
else if (Connection->id == TCP_IPv6)
|
||
{
|
||
// if we were at IPv6 and we didn't select IPv6 this time, it must
|
||
// be IPv4
|
||
ASSERT(sa.ipaddr.ss_family == AF_INET);
|
||
Connection->id = TCP;
|
||
}
|
||
|
||
// Call common open function
|
||
status = WS_Open((PWS_CCONNECTION)Connection,
|
||
&sa,
|
||
ConnTimeout,
|
||
SendBufferSize,
|
||
RecvBufferSize,
|
||
CallTimeout,
|
||
fHTTP2Open
|
||
);
|
||
}
|
||
while (status == ERROR_RETRY);
|
||
|
||
if (status == RPC_S_OK)
|
||
{
|
||
Hint->SetResolverHint((Connection->id == TCP) || (Connection->id == HTTPv2), &sa);
|
||
}
|
||
|
||
return(status);
|
||
}
|
||
|
||
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
TCP_Open(
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
IN RPC_CHAR * ProtocolSequence,
|
||
IN RPC_CHAR * NetworkAddress,
|
||
IN RPC_CHAR * Endpoint,
|
||
IN RPC_CHAR * NetworkOptions,
|
||
IN UINT ConnTimeout,
|
||
IN UINT SendBufferSize,
|
||
IN UINT RecvBufferSize,
|
||
IN OUT PVOID ResolverHint,
|
||
IN BOOL fHintInitialized,
|
||
IN ULONG CallTimeout,
|
||
IN ULONG AdditionalTransportCredentialsType, OPTIONAL
|
||
IN void *AdditionalCredentials OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Opens a connection to a server.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - A place to store the connection
|
||
ProtocolSeqeunce - "ncacn_ip_tcp". Ignored in this function
|
||
NetworkAddress - The name of the server, either a dot address or DNS name
|
||
NetworkOptions - Ignored
|
||
ConnTimeout - See RpcMgmtSetComTimeout
|
||
0 - Min
|
||
5 - Default
|
||
9 - Max
|
||
10 - Infinite
|
||
SendBufferSize -
|
||
RecvBufferSize - (Both optional) Specifies the size of the send/recv
|
||
transport buffers.
|
||
ResolverHint - IP address of server, if valid.
|
||
fHintInitialized - If TRUE, the ResolveHint contains the IP address
|
||
of the server. If FALSE, do standard name resolution.
|
||
CallTimeout - the call timeout in milliseconds
|
||
|
||
AdditionalTransportCredentialsType - the type of additional credentials that we were
|
||
given. Not used for TCP.
|
||
|
||
AdditionalCredentials - additional credentials that we were given.
|
||
Not used for TCP.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S_SERVER_UNAVAILABLE
|
||
RPC_S_INVALID_ENDPOINT_FORMAT
|
||
RPC_S_INVALID_NET_ADDR
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS status;
|
||
PWS_CCONNECTION p = (PWS_CCONNECTION)ThisConnection;
|
||
USHORT port;
|
||
|
||
ASSERT(NetworkAddress);
|
||
|
||
if ((AdditionalTransportCredentialsType != 0) || (AdditionalCredentials != NULL))
|
||
return RPC_S_CANNOT_SUPPORT;
|
||
|
||
// use explicit placement to initialize the vtable. We need this to
|
||
// be able to use the virtual functions
|
||
p = new (p) WS_CLIENT_CONNECTION;
|
||
|
||
// All this function needs to is initialize transport specific
|
||
// parts of the connection and sockaddr. This includes resolving
|
||
// the network address into a `raw' address.
|
||
|
||
// Figure out the destination port
|
||
status = EndpointToPortNumber(Endpoint, port);
|
||
if (status != RPC_S_OK)
|
||
{
|
||
return(status);
|
||
}
|
||
|
||
return TCPOrHTTP_Open (p,
|
||
NetworkAddress,
|
||
port,
|
||
ConnTimeout,
|
||
SendBufferSize,
|
||
RecvBufferSize,
|
||
(TCPResolverHint *)ResolverHint,
|
||
fHintInitialized,
|
||
CallTimeout,
|
||
FALSE, // fHTTP2Open
|
||
NULL // IsValidMachineFn
|
||
);
|
||
}
|
||
|
||
#ifdef SPX_ON
|
||
/////////////////////////////////////////////////////////////////////
|
||
//
|
||
// SPX specific functions
|
||
//
|
||
|
||
RPC_STATUS
|
||
SPX_ServerListen(
|
||
IN RPC_TRANSPORT_ADDRESS ThisAddress,
|
||
IN RPC_CHAR *NetworkAddress,
|
||
IN OUT RPC_CHAR * *pEndpoint,
|
||
IN UINT PendingQueueSize,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN ULONG EndpointFlags,
|
||
IN ULONG NICFlags,
|
||
OUT NETWORK_ADDRESS_VECTOR **ppAddressVector
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allocates a new pipe to receive new client connections.
|
||
If successful a call to COMMON_CompleteListen() will actually allow
|
||
new connection callbacks to the RPC runtime to occur. If the runtime
|
||
is unable to complete then it must abort the address by calling
|
||
WS_ServerAbortListen().
|
||
|
||
Arguments:
|
||
|
||
pAddress - A pointer to the loadable transport interface address.
|
||
pEndpoint - Optionally, the endpoint (port) to listen on. Set to
|
||
to listened port for dynamically allocated endpoints.
|
||
PendingQueueSize - Count to call listen() with.
|
||
EndpointFlags - Meaningless for SPX
|
||
NICFlags - Meaningless for SPX
|
||
SecurityDescriptor - Meaningless for SPX
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S_CANT_CREATE_ENDPOINT
|
||
RPC_S_DUPLICATE_ENDPOINT
|
||
|
||
--*/
|
||
{
|
||
PWS_ADDRESS pAddress = (PWS_ADDRESS)ThisAddress;
|
||
RPC_STATUS status;
|
||
WS_SOCKADDR *sockaddr = &(pAddress->ListenAddr);
|
||
INT i;
|
||
USHORT port;
|
||
|
||
pAddress->id = SPX;
|
||
pAddress->NewConnection = WS_NewConnection;
|
||
pAddress->SubmitListen = WS_SubmitAccept;
|
||
SetProtocolMultiplier(pAddress, 1);
|
||
pAddress->pAddressVector = 0;
|
||
pAddress->Endpoint = 0;
|
||
pAddress->QueueSize = PendingQueueSize;
|
||
pAddress->EndpointFlags = EndpointFlags;
|
||
|
||
sockaddr->generic.sa_family = WsTransportTable[SPX].AddressFamily;
|
||
|
||
// Figure out what port to bind to.
|
||
|
||
if (*pEndpoint)
|
||
{
|
||
status = EndpointToPortNumber(*pEndpoint, port);
|
||
if (status != RPC_S_OK)
|
||
{
|
||
return(status);
|
||
}
|
||
|
||
sockaddr->ipxaddr.sa_socket = htons(port);
|
||
pAddress->fDynamicEndpoint = 0;
|
||
}
|
||
else
|
||
{
|
||
pAddress->fDynamicEndpoint = 1;
|
||
sockaddr->ipxaddr.sa_socket = 0;
|
||
}
|
||
|
||
// No need to bind to a specific address for SPX
|
||
memset(sockaddr->ipxaddr.sa_netnum, 0, sizeof(sockaddr->ipxaddr.sa_netnum) );
|
||
memset(sockaddr->ipxaddr.sa_nodenum, 0, sizeof(sockaddr->ipxaddr.sa_nodenum));
|
||
|
||
// Actually listen
|
||
|
||
status = WS_ServerListenCommon(pAddress);
|
||
|
||
|
||
if (status == RPC_S_OK)
|
||
{
|
||
// Listened okay, update local IPX address.
|
||
//
|
||
// Since there is only one addess no lock is required.
|
||
//
|
||
memcpy(IpxAddr.sa_netnum, sockaddr->ipxaddr.sa_netnum, 4);
|
||
memcpy(IpxAddr.sa_nodenum, sockaddr->ipxaddr.sa_nodenum, 6);
|
||
fIpxAddrValid = TRUE;
|
||
|
||
// Figure out our network addresses
|
||
status = IPX_BuildAddressVector(&pAddress->pAddressVector);
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
WS_ServerAbortListen(pAddress);
|
||
return(status);
|
||
}
|
||
|
||
*ppAddressVector = pAddress->pAddressVector;
|
||
|
||
// Return the dynamic port, if needed.
|
||
|
||
if (!*pEndpoint)
|
||
{
|
||
*pEndpoint = new RPC_CHAR[6]; // 65535 max
|
||
if (!*pEndpoint)
|
||
{
|
||
WS_ServerAbortListen(ThisAddress);
|
||
return(RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
port = ntohs(sockaddr->ipxaddr.sa_socket);
|
||
|
||
PortNumberToEndpoint(port, *pEndpoint);
|
||
}
|
||
|
||
TransportProtocol::FunctionalProtocolDetected(pAddress->id);
|
||
|
||
// Save away the endpoint in the address, if needed, here.
|
||
// (PnP?)
|
||
|
||
}
|
||
else if (status == RPC_P_ADDRESS_FAMILY_INVALID)
|
||
{
|
||
status = RPC_S_PROTSEQ_NOT_SUPPORTED;
|
||
}
|
||
|
||
return(status);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
SPX_QueryClientAddress(
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
OUT RPC_CHAR ** pNetworkAddress
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the raw IPX of the client to a connection as a string. The
|
||
clients address is saved when the client connects, so all we need to do is
|
||
format the address.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - The connection of interest.
|
||
pNetworkAddress - Will contain string on success.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
RPC_S_OUT_OF_MEMORY
|
||
|
||
--*/
|
||
{
|
||
WS_CONNECTION *p= (WS_CONNECTION *)ThisConnection;
|
||
ASSERT(p->type & SERVER);
|
||
|
||
RPC_CHAR *Address = new RPC_CHAR[IPX_MAXIMUM_RAW_NAME];
|
||
|
||
if (!Address)
|
||
{
|
||
return(RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
IPX_AddressToName(&p->saClientAddress.ipxaddr, Address);
|
||
|
||
*pNetworkAddress = Address;
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
SPX_QueryClientId(
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
OUT RPC_CLIENT_PROCESS_IDENTIFIER *ClientProcess
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
For secure protocols (which SPX is not) this is suppose to give an ID
|
||
which will be shared by all connections from the same process or security
|
||
identity. This prevents one user from grabbing another users association
|
||
group and using their context handles.
|
||
|
||
Since SPX is not secure we return the IPX node number of the client.
|
||
This limits the attacks to other processes running on the client machine
|
||
which is better than nothing.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - Server connection in question.
|
||
ClientProcess - Transport identification of the "client".
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OUT
|
||
|
||
--*/
|
||
{
|
||
PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
|
||
|
||
ASSERT(p->type & SERVER);
|
||
|
||
// The runtime assumes that any connections with a ClientProcess->FirstPart is
|
||
// zero means the client is local.
|
||
// Currently, we don't have an efficient way of determining which clients
|
||
// are local, and which remote. Since some clients grant more permissions
|
||
// to local clients, we want to be on the safe side, and return all SPX
|
||
// clients as remote.
|
||
|
||
ClientProcess->ZeroOut();
|
||
ClientProcess->SetIPXClientIdentifier(p->saClientAddress.ipxaddr.sa_nodenum,
|
||
sizeof(p->saClientAddress.ipxaddr.sa_nodenum), FALSE);
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
SPX_Open(
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
IN RPC_CHAR * ProtocolSequence,
|
||
IN RPC_CHAR * NetworkAddress,
|
||
IN RPC_CHAR * Endpoint,
|
||
IN RPC_CHAR * NetworkOptions,
|
||
IN UINT ConnTimeout,
|
||
IN UINT SendBufferSize,
|
||
IN UINT RecvBufferSize,
|
||
IN void *ResolverHint,
|
||
IN BOOL fHintInitialized,
|
||
IN ULONG CallTimeout,
|
||
IN ULONG AdditionalTransportCredentialsType, OPTIONAL
|
||
IN void *AdditionalCredentials OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Opens a connection to a server.
|
||
|
||
Arguments:
|
||
|
||
ThisConnection - A place to store the connection
|
||
ProtocolSeqeunce - "ncacn_spx"
|
||
NetworkAddress - The name of the server, either a raw address or pretty name
|
||
NetworkOptions - Ignored
|
||
ConnTimeout - See RpcMgmtSetComTimeout
|
||
0 - Min
|
||
5 - Default
|
||
9 - Max
|
||
10 - Infinite
|
||
SendBufferSize -
|
||
RecvBufferSize - (Both optional) Specifies the size of the send/recv
|
||
transport buffers.
|
||
CallTimeout - call timeout in milliseconds
|
||
|
||
AdditionalTransportCredentialsType - the type of additional credentials that we were
|
||
given. Not used for SPX.
|
||
|
||
AdditionalCredentials - additional credentials that we were given.
|
||
Not used for SPX.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S_SERVER_UNAVAILABLE
|
||
RPC_S_INVALID_ENDPOINT_FORMAT
|
||
RPC_S_INVALID_NET_ADDR
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS status;
|
||
PWS_CCONNECTION p = (PWS_CCONNECTION)ThisConnection;
|
||
SOCKET sock;
|
||
WS_SOCKADDR sa;
|
||
CHAR AnsiPort[IPX_MAXIMUM_ENDPOINT];
|
||
PCHAR AnsiNetworkAddress;
|
||
BOOL fUseCache = TRUE;
|
||
BOOL fFoundInCache = FALSE;
|
||
|
||
if ((AdditionalTransportCredentialsType != 0) || (AdditionalCredentials != NULL))
|
||
return RPC_S_CANNOT_SUPPORT;
|
||
|
||
// use explicit placement to initialize the vtable. We need this to
|
||
// be able to use the virtual functions
|
||
p = new (p) WS_CLIENT_CONNECTION;
|
||
|
||
// All this function needs to is initialize transport specific
|
||
// parts of the connection and sockaddr. This includes resolving
|
||
// the network address into a `raw' address.
|
||
|
||
p->id = SPX;
|
||
|
||
// Figure out the destination port
|
||
USHORT port;
|
||
status = EndpointToPortNumber(Endpoint, port);
|
||
if (status != RPC_S_OK)
|
||
{
|
||
return(status);
|
||
}
|
||
|
||
for (;;)
|
||
{
|
||
// Resolve network address
|
||
|
||
sa.ipxaddr.sa_family = AF_IPX;
|
||
sa.ipxaddr.sa_socket = 0;
|
||
|
||
status = IPX_NameToAddress(NetworkAddress, fUseCache, &sa.ipxaddr);
|
||
|
||
if (status == RPC_P_FOUND_IN_CACHE)
|
||
{
|
||
ASSERT(fUseCache);
|
||
fFoundInCache = TRUE;
|
||
status = RPC_S_OK;
|
||
}
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
if (status == RPC_P_MATCHED_CACHE)
|
||
{
|
||
status = RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
return(status);
|
||
}
|
||
|
||
sa.ipxaddr.sa_socket = htons(port);
|
||
|
||
// Call common open function
|
||
|
||
status = WS_Open(p,
|
||
&sa,
|
||
ConnTimeout,
|
||
SendBufferSize,
|
||
RecvBufferSize,
|
||
CallTimeout,
|
||
FALSE // fHTTP2Open
|
||
);
|
||
|
||
if (status == ERROR_RETRY)
|
||
{
|
||
status = RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
|
||
if ( status == RPC_S_SERVER_UNAVAILABLE
|
||
&& fFoundInCache
|
||
&& fUseCache )
|
||
{
|
||
fUseCache = FALSE;
|
||
continue;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
return(status);
|
||
}
|
||
#endif
|
||
|
||
#ifdef APPLETALK_ON
|
||
|
||
/////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Appletalk data stream protocol (DSP) specific functions
|
||
//
|
||
|
||
RPC_STATUS DSP_GetAppleTalkName(
|
||
OUT CHAR *Buffer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the server's name for appletalk workstations. This value
|
||
defaults to GetComputerName() but can be changed. Mail from JameelH:
|
||
|
||
By default it is the netbios name of the server. It can be overwritten.
|
||
The new name is at:
|
||
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MacFile\Parameters\ServerName.
|
||
|
||
By default this value is not present.
|
||
|
||
Arguments:
|
||
|
||
Buffer - Supplies a buffer (at least 33 bytes) for the name.
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK - The operation completed successfully.
|
||
|
||
RPC_S_OUT_OF_RESOURCES - Unable to get the name for some reasons.
|
||
--*/
|
||
{
|
||
RPC_STATUS Status;
|
||
HKEY hKey;
|
||
DWORD Size = 33;
|
||
DWORD Type;
|
||
|
||
Status =
|
||
RegOpenKeyEx(
|
||
HKEY_LOCAL_MACHINE,
|
||
RPC_CONST_SSTRING("System\\CurrentControlSet\\Services\\MacFile\\Parameters"),
|
||
0,
|
||
KEY_READ,
|
||
&hKey);
|
||
|
||
if ( Status != ERROR_SUCCESS
|
||
&& Status != ERROR_FILE_NOT_FOUND )
|
||
{
|
||
ASSERT(0);
|
||
return(RPC_S_OUT_OF_RESOURCES);
|
||
}
|
||
|
||
if (Status == ERROR_SUCCESS)
|
||
{
|
||
|
||
Status =
|
||
RegQueryValueExA(
|
||
hKey,
|
||
"ServerName",
|
||
0,
|
||
&Type,
|
||
(PBYTE)Buffer,
|
||
&Size);
|
||
}
|
||
|
||
|
||
if ( Status != ERROR_SUCCESS
|
||
&& Status != ERROR_FILE_NOT_FOUND )
|
||
{
|
||
ASSERT(0);
|
||
return(RPC_S_OUT_OF_RESOURCES);
|
||
}
|
||
|
||
if (Status == ERROR_SUCCESS)
|
||
{
|
||
// Found a name in the registry.
|
||
|
||
ASSERT( Type == REG_SZ
|
||
&& Size <= 32
|
||
&& strlen(Buffer) == (Size + 1));
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
// Not in the registry, must be using the computer name.
|
||
|
||
Size = 33;
|
||
|
||
if ( GetComputerNameA(
|
||
Buffer,
|
||
&Size ) == FALSE )
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
"GetComputerNameA failed! %d\n",
|
||
GetLastError()));
|
||
|
||
ASSERT(0);
|
||
return(RPC_S_OUT_OF_RESOURCES);
|
||
}
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
const PCHAR DSP_OBJECTTYPE_PREFIX = "DceDspRpc ";
|
||
|
||
RPC_STATUS
|
||
DSP_ServerListen(
|
||
IN RPC_TRANSPORT_ADDRESS ThisAddress,
|
||
IN RPC_CHAR *NetworkAddress,
|
||
IN OUT RPC_CHAR * *pEndpoint,
|
||
IN UINT PendingQueueSize,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN ULONG EndpointFlags,
|
||
IN ULONG NICFlags,
|
||
OUT NETWORK_ADDRESS_VECTOR **ppAddressVector
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allocates a port to receive new client connections.
|
||
If successful a call to COMMON_CompleteListen() will actually allow
|
||
new connection callbacks to the RPC runtime to occur. If the runtime
|
||
is unable to complete then it must abort the address by calling
|
||
WS_ServerAbortListen().
|
||
|
||
Arguments:
|
||
|
||
pAddress - A pointer to the loadable transport interface address.
|
||
pEndpoint - Optionally, the endpoint (port) to listen on. Set to
|
||
to listened port for dynamically allocated endpoints.
|
||
PendingQueueSize - Count to call listen() with.
|
||
EndpointFlags - Meaningless for DSP
|
||
NICFlags - Meaningless for DSP
|
||
SecurityDescriptor - Meaningless for DSP
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK
|
||
|
||
RPC_S_OUT_OF_MEMORY
|
||
RPC_S_OUT_OF_RESOURCES
|
||
RPC_S_CANT_CREATE_ENDPOINT
|
||
RPC_S_DUPLICATE_ENDPOINT
|
||
|
||
--*/
|
||
{
|
||
PWS_ADDRESS pAddress = (PWS_ADDRESS)ThisAddress;
|
||
RPC_STATUS status;
|
||
WS_SOCKADDR *sockaddr = &(pAddress->ListenAddr);
|
||
USHORT port;
|
||
CHAR AnsiEndpoint[NBP_MAXIMUM_ENDPOINT];
|
||
|
||
pAddress->id = DSP;
|
||
pAddress->NewConnection = WS_NewConnection;
|
||
pAddress->SubmitListen = WS_SubmitAccept;
|
||
SetProtocolMultiplier(pAddress, 1);
|
||
pAddress->pAddressVector = 0;
|
||
pAddress->Endpoint = 0;
|
||
pAddress->QueueSize = PendingQueueSize;
|
||
pAddress->EndpointFlags = EndpointFlags;
|
||
|
||
//
|
||
// For DSP the endpoint is a character string. It is not actually used
|
||
// to allocate the socket. We let DSP allocate a port and then register
|
||
// the port along with our address and endpoint with NBP.
|
||
//
|
||
|
||
if (*pEndpoint)
|
||
{
|
||
// Runtime gave us an endpoint, convert it to ansi
|
||
int nLength;
|
||
*AnsiEndpoint = 0;
|
||
nLength = RpcpStringLength(*pEndpoint);
|
||
if ((nLength <= 0) || (nLength > 22))
|
||
{
|
||
return(RPC_S_INVALID_ENDPOINT_FORMAT);
|
||
}
|
||
PlatformToAnsi(*pEndpoint, AnsiEndpoint);
|
||
pAddress->fDynamicEndpoint = 0;
|
||
}
|
||
else
|
||
{
|
||
static LONG EndpointCount = 0;
|
||
//
|
||
// Create a dynamic endpoint using Use process ID + 16-bit counter.
|
||
//
|
||
*pEndpoint = new RPC_CHAR[7 + 8 + 4 + 1 + 1];
|
||
if (!*pEndpoint)
|
||
{
|
||
return(RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
CHAR buffer[9];
|
||
|
||
strcpy(AnsiEndpoint, "DynEpt ");
|
||
_ltoa(GetCurrentProcessId(), buffer, 16);
|
||
lstrcatA(AnsiEndpoint, buffer);
|
||
buffer[0] = '.';
|
||
LONG t = InterlockedIncrement(&EndpointCount);
|
||
_ltoa( t & 0xFFFF, buffer + 1, 16);
|
||
lstrcatA(AnsiEndpoint, buffer);
|
||
|
||
SimpleAnsiToPlatform(AnsiEndpoint, *pEndpoint);
|
||
pAddress->fDynamicEndpoint = 1;
|
||
}
|
||
|
||
ASSERT(strlen(AnsiEndpoint) > 0 && strlen(AnsiEndpoint) < NBP_MAXIMUM_ENDPOINT);
|
||
|
||
memset(&sockaddr->ataddr, 0, sizeof(WS_SOCKADDR));
|
||
sockaddr->generic.sa_family = WsTransportTable[DSP].AddressFamily;
|
||
|
||
// For DSP we always bind to a transport choosen port. The endpoint
|
||
// only exists in the NBP router as a mapping to our address and port.
|
||
|
||
// Create listen socket
|
||
|
||
status = WS_ServerListenCommon(pAddress);
|
||
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
if (status == RPC_P_ADDRESS_FAMILY_INVALID)
|
||
status = RPC_S_PROTSEQ_NOT_SUPPORTED;
|
||
return(status);
|
||
}
|
||
|
||
// Now, try to register our name and zone.
|
||
//
|
||
// The final format of the name to register is:
|
||
// <ComputerName>@<Zone>@DceDspRpc <Endpoint>
|
||
//
|
||
// The <ComputerName>@<Zone> string is treated as this machines
|
||
// name as far as the runtime cares. The <Endpoint> is used as
|
||
// the endpoint.
|
||
//
|
||
|
||
CHAR ComputerName[NBP_MAXIMUM_NAME];
|
||
|
||
status = DSP_GetAppleTalkName(ComputerName);
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
WS_ServerAbortListen(ThisAddress);
|
||
return(status);
|
||
}
|
||
|
||
// The following code segment is commented out to be consistent with
|
||
// what we did in NT4. In NT4, we had a bug where if the AppleTalk name
|
||
// registered is just the computer name without the zone name appended.
|
||
// In NT5, we achieve the same by commenting out this code which appends
|
||
// the zone name.
|
||
//
|
||
|
||
/*
|
||
INT cZone;
|
||
|
||
PSZ pszT = ComputerName;
|
||
while(*pszT)
|
||
pszT++;
|
||
*pszT++ = '@';
|
||
cZone = 33;
|
||
|
||
// So far we have ComputerName@ and a pointer to just after the @.
|
||
|
||
if (getsockopt(pAddress->ListenSocket,
|
||
SOL_APPLETALK,
|
||
SO_LOOKUP_MYZONE,
|
||
pszT,
|
||
&cZone) != 0)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "Failed to lookup zone: %d\n",
|
||
GetLastError()));
|
||
|
||
// Don't fail, this could be a small network, just register '*' for
|
||
// the broadcast/local zone.
|
||
*pszT++ = '*';
|
||
*pszT++ = '0';
|
||
}
|
||
*/
|
||
|
||
//
|
||
// We need to register our computer name and endpoint next.
|
||
//
|
||
|
||
WSH_REGISTER_NAME AtalkNameToRegister;
|
||
int length;
|
||
|
||
ASSERT(MAX_COMPUTERNAME_LENGTH < MAX_ENTITY + 1);
|
||
|
||
length = strlen(AnsiEndpoint);
|
||
memcpy(AtalkNameToRegister.TypeName, DSP_OBJECTTYPE_PREFIX, 10);
|
||
memcpy(AtalkNameToRegister.TypeName + 10, AnsiEndpoint, length);
|
||
AtalkNameToRegister.TypeNameLen = length + 10;
|
||
|
||
length = strlen(ComputerName);
|
||
memcpy(AtalkNameToRegister.ObjectName, ComputerName, length);
|
||
AtalkNameToRegister.ObjectNameLen = (char) length;
|
||
|
||
AtalkNameToRegister.ZoneName[0] = '*';
|
||
AtalkNameToRegister.ZoneNameLen = 1;
|
||
|
||
// Could do a lookup and connect to see if our name is already registered..
|
||
|
||
if (setsockopt(
|
||
pAddress->ListenSocket,
|
||
SOL_APPLETALK,
|
||
SO_REGISTER_NAME,
|
||
(char *)&AtalkNameToRegister,
|
||
sizeof(AtalkNameToRegister)
|
||
) != 0 )
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "Failed to register name: %d\n",
|
||
GetLastError()));
|
||
|
||
WS_ServerAbortListen(ThisAddress);
|
||
return(RPC_S_CANT_CREATE_ENDPOINT);
|
||
}
|
||
|
||
// All done, build out parameters
|
||
|
||
ASSERT(length == (int) strlen(ComputerName));
|
||
|
||
NETWORK_ADDRESS_VECTOR *pVector;
|
||
|
||
pVector = new( sizeof(RPC_CHAR *)
|
||
+ (length + 2) * sizeof(RPC_CHAR))
|
||
NETWORK_ADDRESS_VECTOR;
|
||
|
||
if (NULL == pVector)
|
||
{
|
||
WS_ServerAbortListen(ThisAddress);
|
||
return(RPC_S_OUT_OF_MEMORY);
|
||
}
|
||
|
||
pVector->Count = 1;
|
||
pVector->NetworkAddresses[0] = (RPC_CHAR*)&pVector->NetworkAddresses[1];
|
||
|
||
AnsiToPlatform(ComputerName, pVector->NetworkAddresses[0]);
|
||
|
||
pAddress->pAddressVector = pVector;
|
||
*ppAddressVector = pVector;
|
||
|
||
return(RPC_S_OK);
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
RPC_ENTRY
|
||
DSP_Open(
|
||
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
||
IN RPC_CHAR * ProtocolSequence,
|
||
IN RPC_CHAR * NetworkAddress,
|
||
IN RPC_CHAR * Endpoint,
|
||
IN RPC_CHAR * NetworkOptions,
|
||
IN UINT ConnTimeout,
|
||
IN UINT SendBufferSize,
|
||
IN UINT RecvBufferSize,
|
||
IN void *ResolverHint,
|
||
IN BOOL fHintInitialized,
|
||
IN ULONG CallTimeout,
|
||
IN ULONG AdditionalTransportCredentialsType, OPTIONAL
|
||
IN void *AdditionalCredentials OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Not supported on Windows NT.
|
||
|
||
--*/
|
||
{
|
||
return(RPC_S_PROTSEQ_NOT_SUPPORTED);
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
RPC_STATUS RPC_ENTRY WS_Abort(IN RPC_TRANSPORT_CONNECTION Connection)
|
||
{
|
||
return ((WS_CONNECTION *)Connection)->WS_CONNECTION::Abort();
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Transport information definitions
|
||
//
|
||
|
||
const RPC_CONNECTION_TRANSPORT
|
||
TCP_TransportInterface =
|
||
{
|
||
RPC_TRANSPORT_INTERFACE_VERSION,
|
||
TCP_TOWER_ID,
|
||
IP_ADDRESS_ID,
|
||
RPC_STRING_LITERAL("ncacn_ip_tcp"),
|
||
"135",
|
||
COMMON_ProcessCalls,
|
||
|
||
COMMON_StartPnpNotifications,
|
||
COMMON_ListenForPNPNotifications,
|
||
|
||
COMMON_TowerConstruct,
|
||
COMMON_TowerExplode,
|
||
COMMON_PostRuntimeEvent,
|
||
FALSE,
|
||
sizeof(WS_ADDRESS),
|
||
max(sizeof(WS_CLIENT_CONNECTION), sizeof(WS_SAN_CLIENT_CONNECTION)),
|
||
max(sizeof(WS_CONNECTION), sizeof(WS_SAN_CONNECTION)),
|
||
sizeof(CO_SEND_CONTEXT),
|
||
sizeof(TCPResolverHint),
|
||
TCP_MAX_SEND,
|
||
WS_Initialize,
|
||
0,
|
||
TCP_Open,
|
||
0, // No SendRecv on winsock
|
||
WS_SyncRecv,
|
||
WS_Abort,
|
||
WS_Close,
|
||
CO_Send,
|
||
CO_Recv,
|
||
WS_SyncSend,
|
||
WS_TurnOnOffKeepAlives,
|
||
TCP_ServerListen,
|
||
WS_ServerAbortListen,
|
||
COMMON_ServerCompleteListen,
|
||
TCP_QueryClientAddress,
|
||
TCP_QueryLocalAddress,
|
||
TCP_QueryClientId,
|
||
0, // Impersonate
|
||
0, // Revert
|
||
0, // FreeResolverHint
|
||
0, // CopyResolverHint
|
||
0, // CompareResolverHint
|
||
0 // SetLastBufferToFree
|
||
};
|
||
|
||
#ifdef SPX_ON
|
||
const RPC_CONNECTION_TRANSPORT
|
||
SPX_TransportInterface =
|
||
{
|
||
RPC_TRANSPORT_INTERFACE_VERSION,
|
||
SPX_TOWER_ID,
|
||
IPX_ADDRESS_ID,
|
||
RPC_STRING_LITERAL("ncacn_spx"),
|
||
"34280",
|
||
COMMON_ProcessCalls,
|
||
|
||
COMMON_StartPnpNotifications,
|
||
COMMON_ListenForPNPNotifications,
|
||
|
||
COMMON_TowerConstruct,
|
||
COMMON_TowerExplode,
|
||
COMMON_PostRuntimeEvent,
|
||
FALSE,
|
||
sizeof(WS_ADDRESS),
|
||
max(sizeof(WS_CLIENT_CONNECTION), sizeof(WS_SAN_CLIENT_CONNECTION)),
|
||
max(sizeof(WS_CONNECTION), sizeof(WS_SAN_CONNECTION)),
|
||
sizeof(CO_SEND_CONTEXT),
|
||
0,
|
||
SPX_MAX_SEND,
|
||
WS_Initialize,
|
||
0,
|
||
SPX_Open,
|
||
0, // No SendRecv on winsock
|
||
WS_SyncRecv,
|
||
WS_Abort,
|
||
WS_Close,
|
||
CO_Send,
|
||
CO_Recv,
|
||
WS_SyncSend,
|
||
0, // turn on/off keep alives
|
||
SPX_ServerListen,
|
||
WS_ServerAbortListen,
|
||
COMMON_ServerCompleteListen,
|
||
SPX_QueryClientAddress,
|
||
0, // query local address
|
||
SPX_QueryClientId,
|
||
0, // Impersonate
|
||
0, // Revert
|
||
0, // FreeResolverHint
|
||
0, // CopyResolverHint
|
||
0, // CompareResolverHint
|
||
0 // SetLastBufferToFree
|
||
};
|
||
#endif
|
||
|
||
#ifdef APPLETALK_ON
|
||
const RPC_CONNECTION_TRANSPORT
|
||
DSP_TransportInterface =
|
||
{
|
||
RPC_TRANSPORT_INTERFACE_VERSION,
|
||
DSP_TOWER_ID,
|
||
NBP_ADDRESS_ID,
|
||
RPC_STRING_LITERAL("ncacn_at_dsp"),
|
||
"Endpoint Mapper",
|
||
COMMON_ProcessCalls,
|
||
COMMON_StartPnpNotifications,
|
||
COMMON_ListenForPNPNotifications,
|
||
COMMON_TowerConstruct,
|
||
COMMON_TowerExplode,
|
||
COMMON_PostRuntimeEvent,
|
||
FALSE,
|
||
sizeof(WS_ADDRESS),
|
||
sizeof(WS_CLIENT_CONNECTION),
|
||
sizeof(WS_CONNECTION),
|
||
sizeof(CO_SEND_CONTEXT),
|
||
0,
|
||
DSP_MAX_SEND,
|
||
WS_Initialize,
|
||
0,
|
||
DSP_Open, // Not really supported.
|
||
0, // No SendRecv on winsock
|
||
WS_SyncRecv,
|
||
WS_Abort,
|
||
WS_Close,
|
||
CO_Send,
|
||
CO_Recv,
|
||
WS_SyncSend,
|
||
0, // turn on/off keep alives
|
||
DSP_ServerListen,
|
||
WS_ServerAbortListen,
|
||
COMMON_ServerCompleteListen,
|
||
0, // DSP_QueryClientAddress,
|
||
0, // DSP_QueryClientId,
|
||
0, // Impersonate
|
||
0, // Revert
|
||
0, // FreeResolverHint
|
||
0, // CopyResolverHint
|
||
0, // CompareResolverHint
|
||
0 // SetLastBufferToFree
|
||
};
|
||
#endif
|
||
|
||
#define RPC_NIC_INDEXES "System\\CurrentControlSet\\Services\\Rpc\\Linkage"
|
||
FIREWALL_INFO *pFirewallTable = 0;
|
||
BOOL fFirewallInited = FALSE;
|
||
typedef DWORD (*PGETIPADDRTABLE)
|
||
(
|
||
OUT PMIB_IPADDRTABLE pIpAddrTable,
|
||
IN OUT PDWORD pdwSize,
|
||
IN BOOL bOrder
|
||
);
|
||
|
||
|
||
DWORD
|
||
NextIndex(
|
||
char **Ptr
|
||
)
|
||
{
|
||
char *Index = *Ptr ;
|
||
if (*Index == 0)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
while (**Ptr) (*Ptr)++ ;
|
||
(*Ptr)++ ;
|
||
|
||
return (DWORD) atoi(Index) ;
|
||
}
|
||
|
||
|
||
BOOL
|
||
GetCardIndexTable (
|
||
DWORD **IndexTable,
|
||
DWORD *NumIndexes
|
||
)
|
||
{
|
||
HKEY hKey;
|
||
int retry;
|
||
DWORD Size ;
|
||
DWORD Type;
|
||
char *Buffer;
|
||
RPC_STATUS Status;
|
||
char *TempBuffer;
|
||
|
||
Status =
|
||
RegOpenKeyExA(
|
||
HKEY_LOCAL_MACHINE,
|
||
RPC_NIC_INDEXES,
|
||
0,
|
||
KEY_READ,
|
||
&hKey);
|
||
|
||
if (Status == ERROR_FILE_NOT_FOUND)
|
||
{
|
||
*IndexTable = NULL;
|
||
return TRUE;
|
||
}
|
||
|
||
if (Status != ERROR_SUCCESS)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
Size = 512 ;
|
||
Buffer = (char *) I_RpcAllocate(Size) ;
|
||
if (Buffer == 0)
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
|
||
while(TRUE)
|
||
{
|
||
Status =
|
||
RegQueryValueExA(
|
||
hKey,
|
||
"Bind",
|
||
0,
|
||
&Type,
|
||
(unsigned char *) Buffer,
|
||
&Size);
|
||
|
||
if (Status == ERROR_SUCCESS)
|
||
{
|
||
break;
|
||
}
|
||
|
||
if (Status == ERROR_MORE_DATA)
|
||
{
|
||
I_RpcFree(Buffer) ;
|
||
Buffer = (char *) I_RpcAllocate(Size) ;
|
||
if (Buffer == 0)
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if (Status == ERROR_FILE_NOT_FOUND)
|
||
{
|
||
I_RpcFree(Buffer) ;
|
||
*IndexTable = NULL;
|
||
RegCloseKey(hKey);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
goto Cleanup;
|
||
}
|
||
|
||
if (*Buffer == 0)
|
||
{
|
||
ASSERT(!"No cards to bind to") ;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// we know this much will be enough
|
||
//
|
||
*IndexTable = (DWORD *) I_RpcAllocate(Size * sizeof(DWORD));
|
||
if (*IndexTable == 0)
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
|
||
*NumIndexes = 0;
|
||
int Index;
|
||
TempBuffer = Buffer;
|
||
while ((Index = NextIndex(&TempBuffer)) != -1)
|
||
{
|
||
(*IndexTable)[*NumIndexes] = Index;
|
||
(*NumIndexes)++;
|
||
}
|
||
|
||
ASSERT(*NumIndexes);
|
||
|
||
I_RpcFree(Buffer);
|
||
RegCloseKey(hKey);
|
||
|
||
return TRUE;
|
||
|
||
Cleanup:
|
||
if (Buffer)
|
||
{
|
||
I_RpcFree(Buffer);
|
||
}
|
||
|
||
RegCloseKey(hKey);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
DoFirewallInit (
|
||
)
|
||
{
|
||
unsigned i;
|
||
unsigned j;
|
||
DWORD NumIndexes;
|
||
DWORD *IndexTable;
|
||
PMIB_IPADDRTABLE pMib;
|
||
HMODULE hDll;
|
||
PGETIPADDRTABLE pGetIpAddrTable;
|
||
RPC_STATUS Status;
|
||
|
||
if (fFirewallInited)
|
||
{
|
||
return TRUE;
|
||
}
|
||
|
||
if (GetCardIndexTable(&IndexTable, &NumIndexes) == FALSE)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
if (IndexTable == 0)
|
||
{
|
||
fFirewallInited = TRUE;
|
||
return TRUE;
|
||
}
|
||
|
||
hDll = LoadLibrary(RPC_CONST_SSTRING("iphlpapi.dll"));
|
||
if (hDll == 0)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
pGetIpAddrTable = (PGETIPADDRTABLE)GetProcAddress(hDll, "GetIpAddrTable");
|
||
if (pGetIpAddrTable == 0)
|
||
{
|
||
FreeLibrary(hDll);
|
||
return FALSE;
|
||
}
|
||
|
||
DWORD Size = 20*sizeof(MIB_IPADDRROW)+sizeof(DWORD);
|
||
|
||
for (i = 0; i < 2; i++)
|
||
{
|
||
pMib = (PMIB_IPADDRTABLE) I_RpcAllocate(Size);
|
||
if (pMib == 0)
|
||
{
|
||
Status = RPC_S_OUT_OF_MEMORY;
|
||
break;
|
||
}
|
||
|
||
Status = pGetIpAddrTable(pMib, &Size, 0);
|
||
if (Status == 0)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
FreeLibrary(hDll);
|
||
|
||
if (Status != 0)
|
||
{
|
||
if (pMib)
|
||
I_RpcFree(pMib);
|
||
return FALSE;
|
||
}
|
||
|
||
pFirewallTable = (FIREWALL_INFO *) I_RpcAllocate(
|
||
(pMib->dwNumEntries+1)*sizeof(DWORD));
|
||
if (pFirewallTable == 0)
|
||
{
|
||
I_RpcFree(pMib);
|
||
return FALSE;
|
||
}
|
||
|
||
pFirewallTable->NumAddresses = 0;
|
||
for (i = 0; i < NumIndexes; i++)
|
||
{
|
||
for (j = 0; j < pMib->dwNumEntries; j++)
|
||
{
|
||
if ((pMib->table[j].dwIndex & 0x00FFFFFF) == IndexTable[i])
|
||
{
|
||
pFirewallTable->Addresses[
|
||
pFirewallTable->NumAddresses] = pMib->table[j].dwAddr;
|
||
pFirewallTable->NumAddresses++;
|
||
}
|
||
}
|
||
}
|
||
|
||
I_RpcFree(pMib);
|
||
I_RpcFree(IndexTable);
|
||
|
||
fFirewallInited = TRUE;
|
||
return TRUE;
|
||
}
|
||
|
||
const RPC_CONNECTION_TRANSPORT *
|
||
WS_TransportLoad (
|
||
IN PROTOCOL_ID index
|
||
)
|
||
{
|
||
RPC_STATUS status;
|
||
|
||
if (fWinsockLoaded == FALSE)
|
||
{
|
||
if (RPC_WSAStartup() == FALSE)
|
||
{
|
||
return 0;
|
||
}
|
||
fWinsockLoaded = TRUE;
|
||
}
|
||
|
||
switch(index)
|
||
{
|
||
case TCP:
|
||
AddressChangeFn = I_RpcServerInqAddressChangeFn();
|
||
|
||
if (AddressChangeFn)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "TCP address change function has been loaded"));
|
||
}
|
||
|
||
if (DoFirewallInit())
|
||
{
|
||
return(&TCP_TransportInterface);
|
||
}
|
||
break;
|
||
|
||
#ifdef SPX_ON
|
||
case SPX:
|
||
AddressChangeFn = I_RpcServerInqAddressChangeFn();
|
||
|
||
if (AddressChangeFn)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "SPX address change function has been loaded"));
|
||
}
|
||
|
||
if (InitializeIpxNameCache() != RPC_S_OK)
|
||
{
|
||
return(0);
|
||
}
|
||
return(&SPX_TransportInterface);
|
||
#endif
|
||
|
||
#ifdef APPLETALK_ON
|
||
case DSP:
|
||
return(&DSP_TransportInterface);
|
||
#endif
|
||
|
||
case HTTP:
|
||
return(&HTTP_TransportInterface);
|
||
|
||
default:
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "WS_TransportLoad called with index: %d\n",
|
||
index));
|
||
|
||
ASSERT(0);
|
||
}
|
||
|
||
return(0);
|
||
}
|
||
|
||
RPC_STATUS SAN_CONNECTION::SANReceive(HANDLE hFile, LPVOID lpBuffer,
|
||
DWORD nNumberOfBytesToRead,
|
||
LPDWORD lpNumberOfBytesRead,
|
||
LPOVERLAPPED lpOverlapped)
|
||
{
|
||
WSABUF wsaBuf;
|
||
int nResult;
|
||
|
||
wsaBuf.len = nNumberOfBytesToRead;
|
||
wsaBuf.buf = (char *)lpBuffer;
|
||
m_dwFlags = 0;
|
||
|
||
nResult = WSARecv((SOCKET)hFile, &wsaBuf, 1, lpNumberOfBytesRead, &m_dwFlags,
|
||
lpOverlapped, NULL);
|
||
if (nResult == SOCKET_ERROR)
|
||
return WSAGetLastError();
|
||
else
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
RPC_STATUS SAN_CONNECTION::SANSend(HANDLE hFile, LPCVOID lpBuffer,
|
||
DWORD nNumberOfBytesToWrite,
|
||
LPDWORD lpNumberOfBytesWritten,
|
||
LPOVERLAPPED lpOverlapped)
|
||
{
|
||
WSABUF wsaBuf;
|
||
int nResult;
|
||
wsaBuf.len = nNumberOfBytesToWrite;
|
||
wsaBuf.buf = (char *)lpBuffer;
|
||
|
||
nResult = WSASend((SOCKET)hFile, &wsaBuf, 1, lpNumberOfBytesWritten, 0,
|
||
lpOverlapped, NULL);
|
||
if (nResult == SOCKET_ERROR)
|
||
return WSAGetLastError();
|
||
else
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
RPC_STATUS WS_CONNECTION::Receive(HANDLE hFile, LPVOID lpBuffer,
|
||
DWORD nNumberOfBytesToRead,
|
||
LPDWORD lpNumberOfBytesRead,
|
||
LPOVERLAPPED lpOverlapped)
|
||
{
|
||
return UTIL_ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
|
||
}
|
||
|
||
RPC_STATUS WS_CONNECTION::Send(HANDLE hFile, LPCVOID lpBuffer,
|
||
DWORD nNumberOfBytesToWrite,
|
||
LPDWORD lpNumberOfBytesWritten,
|
||
LPOVERLAPPED lpOverlapped)
|
||
{
|
||
return UTIL_WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
|
||
}
|
||
|
||
void
|
||
RPC_CLIENT_PROCESS_IDENTIFIER::SetIPv6ClientIdentifier (
|
||
IN void *Buffer,
|
||
IN size_t BufferSize,
|
||
IN BOOL fLocal)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
IPv6 servers can use this to store their IP address in
|
||
the client identifier.
|
||
|
||
Arguments:
|
||
|
||
Buffer - the buffer with the client identifier
|
||
BufferSize - the size of the data in buffer
|
||
fLocal - TRUE if the client is guaranteed to be Local. False otherwise.
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
SOCKADDR_IN6 *IPv6Address;
|
||
ASSERT(BufferSize >= sizeof(SOCKADDR_IN6));
|
||
|
||
this->fLocal = fLocal;
|
||
RpcpMemoryCopy(u.ULongClientId, Buffer, sizeof(SOCKADDR_IN6));
|
||
IPv6Address = (SOCKADDR_IN6 *)u.ULongClientId;
|
||
IPv6Address->sin6_port = 0;
|
||
IPv6Address->sin6_flowinfo = 0;
|
||
IPv6Address->sin6_scope_id = 0;
|
||
}
|
||
|