You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5918 lines
166 KiB
5918 lines
166 KiB
/*++
|
|
|
|
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>
|
|
#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,
|
|
IN BOOL fResetAddressListEntries = TRUE, OPTIONAL
|
|
IN BOOL fAddToProtocolList = TRUE OPTIONAL
|
|
);
|
|
|
|
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,
|
|
IN BOOL fResetAddressListEntries OPTIONAL
|
|
)
|
|
/*++
|
|
Function Name:WS_ReactivateAddress
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
WS_SOCKADDR *sockaddr = &(pAddress->ListenAddr);
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
// Pass in fAddToProtocolList = FALSE since the object is already in the protocol list.
|
|
Status = WS_ServerListenCommon (pAddress, fResetAddressListEntries, FALSE);
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
// we don't know how big the buffer actually is. It may be
|
|
// gPostSize, but it also may be only length (if the
|
|
// previous receive was coalesced). Play it safe and choose
|
|
// what we know - length
|
|
p->pReadBuffer = buffer;
|
|
p->maxReadBuffer = length;
|
|
}
|
|
// 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;
|
|
if (p->pReadBuffer == 0)
|
|
{
|
|
// see comment in buffer resetting logic in rpc_shutdown
|
|
// for explanation of this.
|
|
p->pReadBuffer = buffer;
|
|
p->maxReadBuffer = length;
|
|
}
|
|
|
|
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);
|
|
|
|
CORRUPTION_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 time between the last keep alive
|
|
response and the next probe.
|
|
KAInterval - The timeout in milliseconds for the
|
|
subsequent keepalive packets. The time between
|
|
two consequitive keep alive probes if the first one
|
|
did not receive a response.
|
|
|
|
--*/
|
|
{
|
|
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. The time between the last keep alive
|
|
response and the next probe.
|
|
KAInterval - the time between two consequitive keep alive probes if the first one did not
|
|
receive a response. 5000 milliseconds is the default.
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
WS_SyncRecv_Avrf(
|
|
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
|
OUT BUFFER *pBuffer,
|
|
OUT PUINT pBufferLength,
|
|
IN DWORD dwTimeout
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper for WS_SyncRecv implementing corruption injection
|
|
under the RPC verifier.
|
|
|
|
SyncRecv member of the transport interface may only be called
|
|
by the cliet, hence we inject the corruption for a client receive.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
Status = WS_SyncRecv(
|
|
ThisConnection,
|
|
pBuffer,
|
|
pBufferLength,
|
|
dwTimeout);
|
|
|
|
if (!Status)
|
|
{
|
|
if (gfRPCVerifierEnabled)
|
|
{
|
|
CorruptionInject(ClientReceive,
|
|
pBufferLength,
|
|
(void **)pBuffer);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
#ifdef MAJOR_TRANS_DEBUG
|
|
DbgPrint("WS_SubmitAccept: Address=0x%x ListenAddr=%x\n", pAddress, (DWORD)pAddress->ListenAddr.inetaddr.sin_addr.S_un.S_addr);
|
|
#endif
|
|
|
|
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);
|
|
|
|
#ifdef MAJOR_TRANS_DEBUG
|
|
DbgPrint("WS_SubmitAccept: WSASocketT: pAddress->ConnectionSocket=%x\n", pAddress->ConnectionSocket);
|
|
#endif
|
|
|
|
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
|
|
);
|
|
|
|
#ifdef MAJOR_TRANS_DEBUG
|
|
DbgPrint("WS_SubmitAccept: pAddress->pAcceptExFunction: b=%x GetLastError()=%x\n", b, GetLastError());
|
|
#endif
|
|
|
|
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));
|
|
// setsockopt can fail with something like WSAENOBUFS in low
|
|
// memory conditions. We can't handle this in any special way.
|
|
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,
|
|
IN BOOL fResetAddressListEntries, OPTIONAL
|
|
IN BOOL fAddToProtocolList OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
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;
|
|
|
|
// We may be called on an address that is enlinked in a list
|
|
// and does not want these entries reset.
|
|
if (fResetAddressListEntries)
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (fAddToProtocolList)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
if (fAddToProtocolList)
|
|
{
|
|
TransportProtocol::AddObjectToProtocolList((BASE_ASYNC_OBJECT *) pAddress);
|
|
}
|
|
|
|
TransportProtocol::FunctionalProtocolDetected(pAddress->id);
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
NETWORK_ADDRESS_VECTOR *
|
|
WS_GetNetworkAddressVector (
|
|
IN RPC_TRANSPORT_ADDRESS ThisAddress
|
|
)
|
|
{
|
|
PWS_ADDRESS pAddress = (PWS_ADDRESS)ThisAddress;
|
|
ASSERT(pAddress->pAddressVector);
|
|
#ifdef MAJOR_TRANS_DEBUG
|
|
DbgPrint("WS_GetNetworkAddressVector: pAddress=0x%x pAddressVector=0x%x\n", pAddress, pAddress->pAddressVector);
|
|
#endif
|
|
return pAddress->pAddressVector;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
case WSAECONNREFUSED:
|
|
case STATUS_REMOTE_NOT_LISTENING:
|
|
case STATUS_CONNECTION_REFUSED:
|
|
// for conn refused, we have different logic for IPv4 and IPv6
|
|
// since IPv6 is installed optionally at the time of this writing
|
|
// and is not PnP compliant in the way we need, it will not be
|
|
// picked up by the server by default. Yet the DNS entries will
|
|
// be updated. So if we have an IPv6 address, the server may
|
|
// not have picke dup IPv6 and we need to retry for IPv6 only.
|
|
if (p->id == TCP_IPv6)
|
|
{
|
|
status = ERROR_RETRY;
|
|
}
|
|
else
|
|
{
|
|
status = RPC_S_SERVER_UNAVAILABLE;
|
|
}
|
|
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,
|
|
WSAECONNABORTED,
|
|
STATUS_LOCAL_DISCONNECT,
|
|
STATUS_TRANSACTION_ABORTED,
|
|
STATUS_CONNECTION_ABORTED,
|
|
WSAEADDRINUSE,
|
|
ERROR_CONNECTION_REFUSED,
|
|
WSAECONNRESET
|
|
} 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. If a non-NULL
|
|
pointer is passed in, the memory will be freed before returning
|
|
a new vector. Queries should always specify *ppAddressVector == NULL.
|
|
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, iActive;
|
|
RPC_CHAR *NextAddress;
|
|
int NumberOfAddresses;
|
|
WS_ADDRESS *CurrentAddress;
|
|
|
|
// If we were called with an old address vector, delete it.
|
|
if (*ppAddressVector)
|
|
{
|
|
delete (*ppAddressVector);
|
|
}
|
|
|
|
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))
|
|
* ((WS_ADDRESS *)Address->pFirstAddress)->NumActiveAddresses);
|
|
if (pVector == NULL)
|
|
{
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
// Only populate the vector with addresses that have been initialized.
|
|
// It is possible that the list contains some address object that have
|
|
// not yet had their DHCP address assigned - they should be skipped.
|
|
pVector->Count = ((WS_ADDRESS *)Address->pFirstAddress)->NumActiveAddresses;
|
|
|
|
NextAddress = (RPC_CHAR*)&pVector->NetworkAddresses[NumberOfAddresses];
|
|
IN_ADDR addr;
|
|
SOCKADDR_IN6 *Ipv6Address;
|
|
unsigned j;
|
|
|
|
CurrentAddress = Address;
|
|
|
|
iActive = 0;
|
|
for (i = 0; i < NumberOfAddresses; i++)
|
|
{
|
|
if (CurrentAddress->fAddressInitialized)
|
|
{
|
|
pVector->NetworkAddresses[iActive] = NextAddress;
|
|
iActive++;
|
|
|
|
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;
|
|
}
|
|
|
|
// Protect the socket to prevent another server from using our port.
|
|
if (WS_ProtectListeningSocket(sock, TRUE) != 0)
|
|
{
|
|
return RPC_S_CANT_CREATE_ENDPOINT;
|
|
}
|
|
|
|
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:
|
|
// This error can only be returned when a bind is done for a socket
|
|
// with SO_REUSEADDR following a bind with SO_EXCLUSIVEADDRUSE. Since
|
|
// we never bind with SO_REUSEADDR we should not see this error unless
|
|
// the port is being allocated dynamically and happens to use
|
|
// an already listening port.
|
|
ASSERT(Retries > 0);
|
|
status = RPC_S_DUPLICATE_ENDPOINT;
|
|
break;
|
|
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a port to receive new client connections.
|
|
If successful a call to COMMON_ServerCompleteListen() 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 fPortNumberInitialized;
|
|
BOOL fDynamicEndpoint;
|
|
BOOL fFatalErrorEncountered;
|
|
RPC_STATUS FatalErrorCode;
|
|
WS_ADDRESS *LastSuccessfullyInitializedAddress;
|
|
WS_ADDRESS *NextAddress;
|
|
BOOL fDualTransportConfiguration = FALSE;
|
|
BOOL fLoopbackAddressProcessed;
|
|
FIREWALL_INFO *pCopyOfFirewallTable = NULL;
|
|
|
|
RequestGlobalMutex();
|
|
// Get a copy of pFirewallTable while in the critical section
|
|
// to protect against a race with the PnP table update.
|
|
// We should only access the local copy and remember to delete it.
|
|
pCopyOfFirewallTable = GetFirewallTableCopy();
|
|
|
|
if (pFirewallTable != NULL &&
|
|
pCopyOfFirewallTable == NULL)
|
|
{
|
|
ClearGlobalMutex();
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
ClearGlobalMutex();
|
|
|
|
// sizing pass - allocate sufficient memory
|
|
if (pCopyOfFirewallTable == 0 || NICFlags == RPC_C_BIND_TO_ALL_NICS)
|
|
{
|
|
if (fHttp)
|
|
AddressListSize = 1;
|
|
else
|
|
{
|
|
AddressListSize = 2;
|
|
fDualTransportConfiguration = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddressListSize = pCopyOfFirewallTable->NumAddresses + 1;
|
|
}
|
|
|
|
NeededAddressListSize = AddressListSize * sizeof(ListenAddressListElement);
|
|
|
|
ListenAddressList = new ListenAddressListElement[NeededAddressListSize];
|
|
if (ListenAddressList == NULL)
|
|
{
|
|
delete pCopyOfFirewallTable;
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
fAtLeastOneAddressInitialized = FALSE;
|
|
RpcpMemorySet(ListenAddressList,
|
|
0,
|
|
NeededAddressListSize);
|
|
|
|
// processing pass. Set all the required addresses in the array
|
|
if (pCopyOfFirewallTable == 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 == pCopyOfFirewallTable->NumAddresses)
|
|
{
|
|
CurrentListElement->u.inetAddr.sin_addr.s_addr = 0x0100007F;
|
|
}
|
|
else
|
|
{
|
|
if (pCopyOfFirewallTable->Entries[i].Address == 0x0100007F)
|
|
fLoopbackAddressProcessed = TRUE;
|
|
CurrentListElement->u.inetAddr.sin_addr.s_addr = pCopyOfFirewallTable->Entries[i].Address;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
delete pCopyOfFirewallTable;
|
|
return status;
|
|
}
|
|
|
|
// Figure out what port to bind to.
|
|
|
|
if (*pEndpoint)
|
|
{
|
|
status = EndpointToPortNumber(*pEndpoint, PortNumber);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
delete [] ListenAddressList;
|
|
delete pCopyOfFirewallTable;
|
|
return(status);
|
|
}
|
|
|
|
PortNumber = htons(PortNumber);
|
|
fDynamicEndpoint = 0;
|
|
fPortNumberInitialized = TRUE;
|
|
}
|
|
else
|
|
{
|
|
PortNumber = 0;
|
|
fDynamicEndpoint = TRUE;
|
|
fPortNumberInitialized = FALSE;
|
|
}
|
|
|
|
// zoom in through the array address, and listen on all successfully initialized
|
|
// protocols
|
|
pList = pAddress;
|
|
|
|
pAddress->NumActiveAddresses = 0;
|
|
|
|
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;
|
|
pList->fAddressInitialized = TRUE;
|
|
|
|
pList->pFirstAddress = pAddress;
|
|
pList->pNextAddress = NULL;
|
|
|
|
sockaddr->generic.sa_family = WsTransportTable[CurrentListElement->ProtocolId].AddressFamily;
|
|
|
|
// If we are running with the firewall address table
|
|
// and the address has not yet been initialized or enabled, delay listening on it.
|
|
if (pCopyOfFirewallTable != NULL &&
|
|
(pCopyOfFirewallTable->Entries[i].fInitialized == FALSE
|
|
|| pCopyOfFirewallTable->Entries[i].fEnabled == FALSE
|
|
)
|
|
)
|
|
{
|
|
ASSERT(pCopyOfFirewallTable->NumAddresses > i);
|
|
|
|
pList->InAddressList = Inactive;
|
|
|
|
// We need to register the address to receive
|
|
// PnP notifications. This is necessary to allow
|
|
// the initialization and activation of the address later on
|
|
// when the corresponding interface has been initialized.
|
|
TransportProtocol::AddObjectToProtocolList(pList);
|
|
|
|
pList->type = ADDRESS;
|
|
|
|
// Record that the address has not yet been initialized and
|
|
// keep track of the interface index so that we can later use
|
|
// the pFirewallTable to initialize this address.
|
|
pList->fAddressInitialized = FALSE;
|
|
}
|
|
// Either selective bindings are not used, or the address has been initialized.
|
|
else
|
|
{
|
|
// 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.
|
|
|
|
pAddress->NumActiveAddresses++;
|
|
|
|
// Actually listen.
|
|
// Pass in fResetAddressListEntries = FALSE since the address list
|
|
// fields have already been initialized.
|
|
status = WS_ServerListenCommon(pList, FALSE);
|
|
|
|
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 fail 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 (!fPortNumberInitialized)
|
|
{
|
|
PortNumber = RpcpGetIpPort(&(pList->ListenAddr));
|
|
fPortNumberInitialized = TRUE;
|
|
}
|
|
fAtLeastOneAddressInitialized = TRUE;
|
|
}
|
|
}
|
|
|
|
pOld = pList;
|
|
pList = 0;
|
|
}
|
|
|
|
// If at least one address has initialized then that address should have picked
|
|
// the dynamic endpoint to listen on:
|
|
// fAtLeastOneAddressInitialized -> fPortNumberInitialized
|
|
ASSERT(!fAtLeastOneAddressInitialized || fPortNumberInitialized);
|
|
|
|
// 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 and this is not a
|
|
// delay-initialized address under dynamic binding that has not yet been
|
|
// assigned an IP address, delete the element.
|
|
if (!ListenAddressList[i].fSuccessfullyInitialized && (i > 0)
|
|
&&
|
|
(!fAtLeastOneAddressInitialized
|
|
|| (!fDualTransportConfiguration
|
|
|| (ListenAddressList[i].ErrorCode != RPC_P_ADDRESS_FAMILY_INVALID)
|
|
)
|
|
)
|
|
&&
|
|
!(pCopyOfFirewallTable != NULL && pCopyOfFirewallTable->Entries[i].fInitialized == FALSE)
|
|
)
|
|
{
|
|
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;
|
|
delete pCopyOfFirewallTable;
|
|
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.
|
|
//
|
|
// We will leave the first element alone if it is a delay-initialized address
|
|
// that has not yet been assigned a transport address.
|
|
if (!(fDualTransportConfiguration && (ListenAddressList[0].ErrorCode == RPC_P_ADDRESS_FAMILY_INVALID))
|
|
&&
|
|
!(pCopyOfFirewallTable != NULL && pCopyOfFirewallTable->Entries[0].fInitialized == FALSE))
|
|
{
|
|
// 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;
|
|
delete pCopyOfFirewallTable;
|
|
|
|
// 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
|
|
ASSERT(pAddress->pAddressVector == NULL);
|
|
status = IP_BuildAddressVector(
|
|
&pAddress->pAddressVector,
|
|
NICFlags,
|
|
NetworkAddress,
|
|
pAddress);
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
WS_ServerAbortListen(pAddress);
|
|
return(status);
|
|
}
|
|
|
|
// 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
|
|
)
|
|
{
|
|
|
|
return (TCP_ServerListenEx(
|
|
ThisAddress,
|
|
NetworkAddress,
|
|
pEndpoint,
|
|
PendingQueueSize,
|
|
SecurityDescriptor,
|
|
EndpointFlags,
|
|
NICFlags,
|
|
FALSE // Not HTTP
|
|
));
|
|
}
|
|
|
|
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
|
|
RPC_ENTRY
|
|
TCP_QueryClientIpAddress (
|
|
IN RPC_TRANSPORT_CONNECTION ThisConnection,
|
|
IN OUT RPC_CLIENT_IP_ADDRESS *ClientIpAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the IP address of the client on a connection as a SOCKADDR.
|
|
The clients address is saved when the client connects, so all
|
|
we need to do is copy it.
|
|
|
|
Arguments:
|
|
|
|
ThisConnection - The server connection of interest.
|
|
|
|
ClientIpAddress - the buffer to store the address to.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK or other RPC_S_* errors for error
|
|
|
|
--*/
|
|
{
|
|
ULONG BufferLength;
|
|
PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
|
|
|
|
if (p->id != TCP_IPv6)
|
|
{
|
|
BufferLength = sizeof(SOCKADDR_IN);
|
|
}
|
|
else
|
|
{
|
|
BufferLength = sizeof(SOCKADDR_IN6);
|
|
}
|
|
|
|
ASSERT(BufferLength <= sizeof(ClientIpAddress->Data));
|
|
|
|
ClientIpAddress->DataSize = BufferLength;
|
|
// we know both IPv4 and IPv6 start at the same offset. Just copy the respective
|
|
// size starting at the offset.
|
|
RpcpMemoryCopy (ClientIpAddress->Data, &p->saClientAddress.ipaddr, BufferLength);
|
|
|
|
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;
|
|
RPC_STATUS status2;
|
|
WS_SOCKADDR sa;
|
|
PVOID rnrContext;
|
|
BOOL fIPv4Hint;
|
|
USES_CONVERSION;
|
|
CStackAnsi AnsiName;
|
|
BOOL NetworkAddressConverted;
|
|
char *DotName;
|
|
char *DotNamePtr;
|
|
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);
|
|
|
|
// check the list of valid machines first. If this is not a valid
|
|
// machine, we don't want to return out information on whether the name
|
|
// resolves or not
|
|
if (IsValidMachineFn)
|
|
{
|
|
ASSERT(fHTTP2Open != FALSE);
|
|
if (NetworkAddressConverted == FALSE)
|
|
{
|
|
ATTEMPT_STACK_W2A(AnsiName, NetworkAddress);
|
|
NetworkAddressConverted = TRUE;
|
|
}
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
DotName = inet_ntoa(sa.inetaddr.sin_addr);
|
|
strcpy(DotNameBuffer, DotName);
|
|
DotNamePtr = DotNameBuffer;
|
|
}
|
|
else
|
|
{
|
|
DotNamePtr = NULL;
|
|
}
|
|
|
|
status2 = IsValidMachineFn(AnsiName,
|
|
DotNamePtr,
|
|
Endpoint
|
|
);
|
|
|
|
if (status2 != RPC_S_OK)
|
|
{
|
|
// if we're out of names, no point in continuing
|
|
if (status != RPC_S_OK)
|
|
{
|
|
// move the status from the allowed check to the return value
|
|
status = status2;
|
|
break;
|
|
}
|
|
|
|
// if the server is resolved, but not allowed for access, continue with next
|
|
status = ERROR_RETRY;
|
|
continue;
|
|
}
|
|
else if (status != RPC_S_OK)
|
|
{
|
|
// the server is allowed, but can't be resolved - break where we will return an error
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a new pipe to receive new client connections.
|
|
If successful a call to COMMON_ServerCompleteListen() 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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
(void) RegCloseKey(hKey);
|
|
|
|
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
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a port to receive new client connections.
|
|
If successful a call to COMMON_ServerCompleteListen() 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;
|
|
|
|
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,
|
|
WS_GetNetworkAddressVector,
|
|
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,
|
|
TCP_QueryClientIpAddress,
|
|
0, // Impersonate
|
|
0, // Revert
|
|
0, // FreeResolverHint
|
|
0, // CopyResolverHint
|
|
0, // CompareResolverHint
|
|
0 // SetLastBufferToFree
|
|
};
|
|
|
|
// When the RPC verifier is enabled and we are corrupting client receives,
|
|
// we will use a modified transport interface given below. It will have the sync
|
|
// receive members overwritten.
|
|
RPC_CONNECTION_TRANSPORT *pTCP_TransportInterface_Avrf = NULL;
|
|
|
|
#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,
|
|
WS_GetNetworkAddressVector,
|
|
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, // query client ip address
|
|
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,
|
|
WS_GetNetworkAddressVector,
|
|
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, // query client ip address
|
|
0, // Impersonate
|
|
0, // Revert
|
|
0, // FreeResolverHint
|
|
0, // CopyResolverHint
|
|
0, // CompareResolverHint
|
|
0 // SetLastBufferToFree
|
|
};
|
|
#endif
|
|
|
|
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())
|
|
{
|
|
// Overwrite the SyncSendRecv and SyncRecv members of the transport interfaces if the
|
|
// RPC verifier is enabled.
|
|
if (gfRpcVerifierCorruptionInjectClientReceives)
|
|
{
|
|
// Check if we have previously initialized the Avrf transport interface.
|
|
if (pTCP_TransportInterface_Avrf == NULL)
|
|
{
|
|
// Allocate a transport interface structure to override the default.
|
|
pTCP_TransportInterface_Avrf = new (RPC_CONNECTION_TRANSPORT);
|
|
if (pTCP_TransportInterface_Avrf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
// Initialize the Avrf transport interface with the default values.
|
|
RpcpMemoryCopy(pTCP_TransportInterface_Avrf,
|
|
&TCP_TransportInterface,
|
|
sizeof(RPC_CONNECTION_TRANSPORT));
|
|
// Override the interface function for sync receive.
|
|
ASSERT(pTCP_TransportInterface_Avrf->SyncRecv == WS_SyncRecv);
|
|
pTCP_TransportInterface_Avrf->SyncRecv = WS_SyncRecv_Avrf;
|
|
}
|
|
return(pTCP_TransportInterface_Avrf);
|
|
}
|
|
|
|
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:
|
|
if (DoFirewallInit())
|
|
{
|
|
return(&HTTP_TransportInterface);
|
|
}
|
|
break;
|
|
|
|
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;
|
|
}
|
|
|