Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1176 lines
32 KiB

/*++
Copyright (C) Microsoft Corporation, 1996 - 1999
Module Name:
loader.cxx
Abstract:
Configuration and loading of RPC transports
Revision History:
MarioGo 03-18-96 Cloned from parts of old common.c
MarioGo 10-31-96 Async RPC
--*/
#include <precomp.hxx>
#include <loader.hxx>
#include <trans.hxx>
#include <cotrans.hxx>
#include <dgtrans.hxx>
#include <selbinding.hxx>
extern "C" {
#include <iphlpapi.h>
}
// Globals - see loader.hxx
DWORD gdwComputerNameLength = 0;
RPC_CHAR gpstrComputerName[MAX_COMPUTERNAME_LENGTH + 1];
UINT gPostSize = CO_MIN_RECV;
#ifdef _INTERNAL_RPC_BUILD_
RPCLT_PDU_FILTER_FUNC gpfnFilter = NULL;
#endif
//
// Used to convert numbers to hex strings
//
const RPC_CHAR HexDigits[] =
{
RPC_CONST_CHAR('0'),
RPC_CONST_CHAR('1'),
RPC_CONST_CHAR('2'),
RPC_CONST_CHAR('3'),
RPC_CONST_CHAR('4'),
RPC_CONST_CHAR('5'),
RPC_CONST_CHAR('6'),
RPC_CONST_CHAR('7'),
RPC_CONST_CHAR('8'),
RPC_CONST_CHAR('9'),
RPC_CONST_CHAR('A'),
RPC_CONST_CHAR('B'),
RPC_CONST_CHAR('C'),
RPC_CONST_CHAR('D'),
RPC_CONST_CHAR('E'),
RPC_CONST_CHAR('F')
};
// WARNING: The order of these protocols must be consistent with the
// definition of PROTOCOL_ID.
const
TRANSPORT_TABLE_ENTRY TransportTable[] = {
{
0,
0,
0
},
// TCP/IP
{
TCP_TOWER_ID,
IP_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&TCP_TransportInterface
},
#ifdef SPX_ON
// SPX
{
SPX_TOWER_ID,
IPX_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&SPX_TransportInterface
},
#else
{
0,
0,
NULL
},
#endif
// Named pipes
{
NMP_TOWER_ID,
UNC_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&NMP_TransportInterface
},
#ifdef NETBIOS_ON
// Netbeui
{
NB_TOWER_ID,
NBF_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&NBF_TransportInterface
},
// Netbios over TCP/IP
{
NB_TOWER_ID,
IP_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&NBT_TransportInterface
},
// Netbios over IPX
{
NB_TOWER_ID,
IPX_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&NBI_TransportInterface
},
#else
// Netbeui
{
0,
0,
NULL
},
// Netbios over TCP/IP
{
0,
0,
NULL
},
// Netbios over IPX
{
0,
0,
NULL
},
#endif
#ifdef APPLETALK_ON
// Appletalk Datastream protocol
{
DSP_TOWER_ID,
NBP_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&DSP_TransportInterface
},
#else
// Appletalk Datastream protocol
{
0,
0,
NULL
},
#endif
// Banyan Vines SSP
{
0,
0,
NULL
},
// Hyper-Text Tranfer Protocol (HTTP)
{
HTTP_TOWER_ID,
HTTP_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&HTTP_TransportInterface
},
// UDP/IP
{
UDP_TOWER_ID,
IP_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&UDP_TransportInterface
},
#ifdef IPX_ON
// IPX
{
IPX_TOWER_ID,
IPX_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&IPX_TransportInterface
},
#else
// IPX
{
0,
0,
0
},
#endif
// CDP/UDP/IP
{
CDP_TOWER_ID,
IP_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&CDP_TransportInterface
},
#ifdef NCADG_MQ_ON
// MSMQ (Falcon/RPC)
{
MQ_TOWER_ID,
MQ_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&MQ_TransportInterface
},
#else
// MSMQ (Falcon/RPC)
{
0,
0,
NULL
},
#endif
// TCP over IPv6
{
TCP_TOWER_ID,
IP_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&TCP_TransportInterface
},
// HTTP2 - same as HTTP in terms of contents.
{
HTTP_TOWER_ID,
HTTP_ADDRESS_ID,
(RPC_TRANSPORT_INTERFACE)&HTTP_TransportInterface
}
};
const DWORD cTransportTable = sizeof(TransportTable)/sizeof(TRANSPORT_TABLE_ENTRY);
inline
BOOL CompareProtseqs(
IN const CHAR *p1,
IN const RPC_CHAR *p2)
// Note: protseqs use only ANSI characters so this is ok.
{
while(*p1)
{
if (*p1 != *p2)
{
return FALSE;
}
p1++;
p2++;
}
return(*p2 == 0);
}
PROTOCOL_ID
MapProtseq(
IN const RPC_CHAR *RpcProtocolSequence
)
{
PROTOCOL_ID index;
for(index = 1; index < cTransportTable; index++)
{
if (TransportTable[index].pInfo != NULL)
{
if (RpcpStringCompare(RpcProtocolSequence,
TransportTable[index].pInfo->ProtocolSequence) == 0)
{
return(index);
}
}
}
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "Called with unknown protseq %S\n",
RpcProtocolSequence));
ASSERT(0);
return(0);
}
PROTOCOL_ID
MapProtseq(
IN const CHAR *RpcProtocolSequence
)
{
PROTOCOL_ID index;
for(index = 1; index < cTransportTable; index++)
{
if (TransportTable[index].pInfo != NULL)
{
if (CompareProtseqs(RpcProtocolSequence,
TransportTable[index].pInfo->ProtocolSequence))
{
return(index);
}
}
}
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "Called with unknown protseq %S\n",
RpcProtocolSequence));
ASSERT(0);
return(0);
}
// NB: must be called before RpcCompletionPort is zeroed out, because it is used for comparison
void FreeCompletionPortHashTable(void)
{
DWORD i;
HANDLE hCurrentHandle;
// walk through the table, not closing if there is next entry, and it is the same as this
for (i = 0; i < gNumberOfProcessors * 2; i ++)
{
hCurrentHandle = RpcCompletionPorts[i];
if (hCurrentHandle && (hCurrentHandle != RpcCompletionPort))
{
CloseHandle(hCurrentHandle);
}
}
}
HANDLE GetCompletionPortHandleForThread(void)
{
DWORD i;
DWORD nMinLoad = (DWORD) -1;
int nMinLoadIndex = -1;
for (i = 0; i < gNumberOfProcessors * 2; i ++)
{
if ((DWORD)CompletionPortHandleLoads[i] < nMinLoad)
{
nMinLoadIndex = i;
nMinLoad = CompletionPortHandleLoads[i];
}
}
ASSERT (nMinLoadIndex >= 0);
InterlockedIncrement(&CompletionPortHandleLoads[nMinLoadIndex]);
ASSERT(RpcCompletionPorts[nMinLoadIndex] != 0);
return RpcCompletionPorts[nMinLoadIndex];
}
void ReleaseCompletionPortHandleForThread(HANDLE h)
{
DWORD i;
for (i = 0; i < gNumberOfProcessors * 2; i ++)
{
if (h == RpcCompletionPorts[i])
{
InterlockedDecrement((long *)&CompletionPortHandleLoads[i]);
ASSERT(CompletionPortHandleLoads[i] >= 0);
return;
}
}
ASSERT(0);
}
//
// The firewall table settings:
//
// DoFirewallInit() initializes and updates the data structure.
//
// Syncronization needs are:
// - DoFirewallInit() is not thread-safe and should execute inside a critical section.
// - Access to pFirewallTable should be syncronized against update in DoFirewallInit().
//
// The rules are:
// - The table is initialized during the first UseProtseq*. Subsequent UseProtseq*'s will be noops for
// the initialized table.
// - Update happens on PnP notification. The table gets re-created and replaced during an address change.
// - Access happens on PnP notification and on UseProtseq* path.
// - Access on PnP notification happens after the update to the table. Since a UseProtseq* call must have
// been already made, this path does not have to syncronize against modification by UseProtseq*. Since
// we are doing an access after updating the table, and PnP state change occurs within a critical section,
// access on PnP event does not have to syncronize with anyone.
// - Therefore we only need to syncronize the access on UseProtseq* agains an update on address change.
// The access thread will make a local copy inside a mutex and the update will modify table inside a mutex.
//
FIREWALL_INFO *pFirewallTable = 0;
// Set to TRUE when all interfaces have addresses assigned
// and the table does not have un-initialized addresses.
// Untill all interfaces get addresses, we will monitor for
// address change.
BOOL fFirewallTableFullyInitialized = FALSE;
// The number of interfaces that have addresses assigned.
DWORD FirewallTableNumActiveEntries = 0;
// Has the firewall table been initialized.
// We are protected by the GlobalMutex when we are in DoFirewallInit, so this is OK
BOOL fFirewallInited = FALSE;
typedef DWORD (*PGETIPADDRTABLE)
(
OUT PMIB_IPADDRTABLE pIpAddrTable,
IN OUT PDWORD pdwSize,
IN BOOL bOrder
);
DWORD
GetIpAddrTableHelper(
OUT PMIB_IPADDRTABLE *ppIpAddrTable
)
{
HMODULE hDll;
PGETIPADDRTABLE pGetIpAddrTable = NULL;
DWORD dwSize, dwStatus;
hDll = LoadLibrary(RPC_CONST_SSTRING("iphlpapi.dll"));
if (hDll == 0)
{
return FALSE;
}
pGetIpAddrTable = (PGETIPADDRTABLE)GetProcAddress(hDll, "GetIpAddrTable");
if (pGetIpAddrTable == 0)
{
FreeLibrary(hDll);
return FALSE;
}
// Query for the size
*ppIpAddrTable = NULL;
dwSize = 0;
dwStatus = pGetIpAddrTable(*ppIpAddrTable,
&dwSize,
TRUE);
if (dwStatus != ERROR_INSUFFICIENT_BUFFER)
{
VALIDATE(dwStatus)
{
ERROR_OUTOFMEMORY
} END_VALIDATE;
FreeLibrary(hDll);
return FALSE;
}
*ppIpAddrTable = (PMIB_IPADDRTABLE) new char [dwSize];
if (*ppIpAddrTable == NULL)
{
FreeLibrary(hDll);
return FALSE;
}
// Get the interfaces for the machine
dwStatus = pGetIpAddrTable(*ppIpAddrTable,
&dwSize,
TRUE);
FreeLibrary(hDll);
if (dwStatus != ERROR_SUCCESS)
{
ASSERT(0);
delete [] *ppIpAddrTable;
return FALSE;
}
return TRUE;
}
BOOL
ConvertAndSaveHelper(
IN VER_INDICES_SETTINGS *Settings,
IN PMIB_IPADDRTABLE IpAddrTable
)
{
DWORD IdxToSubnetCnt = 0;
LPDWORD SubnetArray = NULL;
DWORD dwStatus = ERROR_SUCCESS;
SubnetArray = new DWORD[IpAddrTable->dwNumEntries];
if (SubnetArray == NULL)
{
return FALSE;
}
for (DWORD idx = 0; idx < IpAddrTable->dwNumEntries; idx++)
{
PMIB_IPADDRROW pRow = &IpAddrTable->table[idx];
for (DWORD idx2 = 0; idx2 < Settings->dwCount; idx2++)
{
DWORD dwDeviceIndex = pRow->dwIndex;
if (dwDeviceIndex == Settings->dwIndices[idx2])
{
SubnetArray[IdxToSubnetCnt] = pRow->dwAddr & pRow->dwMask;
IdxToSubnetCnt++;
break;
}
}
}
// We have the subnets based off the index settings, commit them as subnet settings
if (IdxToSubnetCnt == 0)
{
(void) DeleteSelectiveBinding();
}
else
{
dwStatus = SetSelectiveBindingSubnets(IdxToSubnetCnt, SubnetArray, TRUE);
VALIDATE(dwStatus)
{
ERROR_SUCCESS,
ERROR_OUTOFMEMORY,
ERROR_ACCESS_DENIED
} END_VALIDATE;
}
delete [] SubnetArray;
return (dwStatus == ERROR_SUCCESS);
}
BOOL
DoFirewallInit (
)
/*++
Routine Description:
Initializes the firewall address table.
Arguments:
Return Value:
TRUE - Initialization suceeded and pFirewallTable has been initialized or updated.
FALSE - Initialization has failed and the table is in the state
it was in prior to the call.
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD dwSize = 1;
LPVOID lpSettings = NULL;
SB_VER sbVer = SB_VER_UNKNOWN;
PMIB_IPADDRTABLE pIpAddrTable = NULL;
FIREWALL_INFO *pNewFirewallTable = 0;
#ifdef MAJOR_TRANS_DEBUG
DbgPrint("RPC: DoFirewallInit\n");
#endif
// If we already initialized, then just return success
if (fFirewallInited)
{
return TRUE;
}
// We will return from this loop if there are unknown settings (return with failure), if there
// are default settings (return with success), or if there is a failure converting the indicies settings
// to subnet settings.
// We will break from this loop only when we have read in subnet settings (which means they were there originially
// or we read in index settings, wrote them back out as subnet settings and then read the new subnet settings in.
for(;;)
{
// Retrieve the selective binding setting from the registry.
dwStatus = GetSelectiveBindingSettings(&sbVer, &dwSize, &lpSettings);
if (dwStatus != ERROR_SUCCESS)
{
VALIDATE(dwStatus)
{
ERROR_OUTOFMEMORY
} END_VALIDATE;
return FALSE;
}
switch (sbVer)
{
case SB_VER_UNKNOWN:
//this means the selective binding settings are corrupt, fatal failure
delete [] pIpAddrTable;
return FALSE;
case SB_VER_DEFAULT:
// pFirewallTable == NULL in the beginning, so there is nothign to be done in that case.
// If we have already initialized the table, we can't delete it now since folks
// may have references to it. We will just ignore the new settings.
fFirewallInited = TRUE;
delete [] pIpAddrTable;
delete lpSettings;
return TRUE;
case SB_VER_INDICES:
// We need to convert this to subnet settings and commit it to the registry
// Get the pIpAddrTable. Note: The pIpAddrTable may not be avaliable if the system
// is being installed, we assume that if there are selective binding settings in the registry
// then the pIpAddrTable is avaliable.
if (!GetIpAddrTableHelper(&pIpAddrTable))
{
delete lpSettings;
return FALSE;
}
if (!ConvertAndSaveHelper((VER_INDICES_SETTINGS *)lpSettings, pIpAddrTable))
{
delete [] pIpAddrTable;
delete lpSettings;
return FALSE;
}
delete [] lpSettings;
continue;
case SB_VER_SUBNETS:
break;
default:
ASSERT(0);
delete [] pIpAddrTable;
return FALSE;
}
break;
}
ASSERT(sbVer == SB_VER_SUBNETS);
ASSERT(lpSettings != NULL);
// If we didn't retrieve the Ip Address Table, get it now. We would have already retrieved it if we originally read in
// index format settings.
if (pIpAddrTable == NULL)
{
if (!GetIpAddrTableHelper(&pIpAddrTable))
{
return FALSE;
}
}
// Allocate the subnet table, for simplicity allocate enough space to fit all the interfaces on the system
// and a flag for each interface. The array of addresses and flags will follow the FIREWALL_INFO structure
// in memory.
// We are using the following procedure to update pFirewallTable:
// Req: If the function fails, pFirewallTable is unchanged.
// Req: If the function suceeds, pFirewallTable is updated to the new copy atomically.
// Rule: Work on a temp copy of the firewall table without touching the original.
// Rule: On sucess update the original.
pNewFirewallTable = (FIREWALL_INFO*) new char[sizeof(FIREWALL_INFO)
+ sizeof(FIREWALL_INFO_ENTRY) * (pIpAddrTable->dwNumEntries - 1)];
if (pNewFirewallTable == NULL)
{
delete [] pIpAddrTable;
delete [] lpSettings;
return FALSE;
}
pNewFirewallTable->NumAddresses = pIpAddrTable->dwNumEntries;
// Step through the interfaces, add enabled interfaces to our firewall table
// for subnets: if admit list: if the interface & mask is in the list of subnets
// it is enabled. for deny list, if it is a member of any of the subnets
// then it not enabled
VER_SUBNETS_SETTINGS *pSubnetSettings = (VER_SUBNETS_SETTINGS*) lpSettings;
for (DWORD idx = 0; idx < pIpAddrTable->dwNumEntries; idx++)
{
PMIB_IPADDRROW pRow = &pIpAddrTable->table[idx];
BOOL bEnabled = !(pSubnetSettings->bAdmit);
pNewFirewallTable->Entries[idx].Address = pRow->dwAddr;
for (DWORD idx2 = 0; idx2 < pSubnetSettings->dwCount; idx2++)
{
DWORD dwSubnet = pRow->dwAddr & pRow->dwMask;
if (dwSubnet == pSubnetSettings->dwSubnets[idx2])
{
bEnabled = pSubnetSettings->bAdmit;
break;
}
}
if (pNewFirewallTable->Entries[idx].Address == 0x0100007F){
// The loopback address must always be enabled, regardless
// of the selective binding settings.
pNewFirewallTable->Entries[idx].fEnabled = TRUE;
}
else {
pNewFirewallTable->Entries[idx].fEnabled = bEnabled;
}
}
// If some interfaces have not yet been initialized and do not have an address,
// mark them as such with the flag.
// Un-initialized interfaces will have a NULL address in the table. This
// may happen if a DHCP address has not yet been assigned to the corresponding NIC.
// If there are no un-initialized interfaces left, the table is now fully initialized
// and we will set fFirewallTableFullyInitialized.
fFirewallTableFullyInitialized = TRUE;
FirewallTableNumActiveEntries = 0;
for (DWORD i = 0; i < pNewFirewallTable->NumAddresses; i++)
{
if (pNewFirewallTable->Entries[i].Address == NULL)
{
pNewFirewallTable->Entries[i].fInitialized = FALSE;
fFirewallTableFullyInitialized = FALSE;
}
else
{
pNewFirewallTable->Entries[i].fInitialized = TRUE;
if (pNewFirewallTable->Entries[i].fEnabled == TRUE)
{
FirewallTableNumActiveEntries++;
}
}
}
#ifdef MAJOR_TRANS_DEBUG
DbgPrint("RPC: DoFirewallInit: pNewFirewallTable = 0x%x pNewFirewallTable->NumAddresses = 0x%x\n",
pNewFirewallTable,
pNewFirewallTable->NumAddresses
);
for (DWORD i = 0; i < pNewFirewallTable->NumAddresses; i++)
{
DbgPrint("RPC: DoFirewallInit: pNewFirewallTable->Entries[0x%x]: Address = 0x%x fInitialized = 0x%x fEnabled = 0x%x fNewAddress = 0x%x\n",
i,
pNewFirewallTable->Entries[i].Address,
pNewFirewallTable->Entries[i].fInitialized,
pNewFirewallTable->Entries[i].fEnabled,
pNewFirewallTable->Entries[i].fNewAddress,
);
}
DbgPrint("RPC: DoFirewallInit: Initialized pNewFirewallTable:\n");
#endif
// Figure out which addresses in the table are new ones.
for (DWORD i = 0; i < pNewFirewallTable->NumAddresses; i++)
{
// If this is a new table then all addresses are new ones.
if (pFirewallTable == NULL)
{
pNewFirewallTable->Entries[i].fNewAddress = TRUE;
}
else
{
// If we are updating the table, see if the address appeared some place
// in the old table.
pNewFirewallTable->Entries[i].fNewAddress = TRUE;
for (DWORD j = 0; j < pFirewallTable->NumAddresses; j++)
{
// If it does, then this entry is not a new one.
if (pNewFirewallTable->Entries[i].Address == pFirewallTable->Entries[j].Address)
{
pNewFirewallTable->Entries[i].fNewAddress = FALSE;
}
}
}
}
// We have completed the initialization of pNewFirewallTable.
// Replace the old copy of the table with the new one.
delete [] pFirewallTable;
pFirewallTable = pNewFirewallTable;
fFirewallInited = TRUE;
delete [] pIpAddrTable;
delete [] lpSettings;
return TRUE;
}
void
DoFirewallUpdate (
)
/*++
Routine Description:
Updates the firewall address table and any addresses that have not
yet been initialized.
This function is called on an address change PnP event.
We re-load the firewall address table and then scan all transport addresses.
We initialize and listen on the addresses that received an IP address.
Arguments:
None
Return Value:
None
--*/
{
BOOL fRes;
BOOL fFirewallInitedSaved;
// We have received a PnP event that signals an address change.
// Re-initialize the firewall address table since it may now
// have addresses assigned to interfaces that previously were
// uninitialized.
// DoFirewallInit() is not thread safe so we will take the global mutex.
RequestGlobalMutex();
// Remember the current state of the table.
fFirewallInitedSaved = fFirewallInited;
// Force an update to the table.
fFirewallInited = FALSE;
fRes = DoFirewallInit();
// If the update has failed, we will continue using the old table, so
// restore the flag. The call should have left the table untouched.
if (fFirewallInitedSaved && !fFirewallInited)
{
fFirewallInited = TRUE;
}
ClearGlobalMutex();
// If we could not update the firewall table, bail out.
// The table should remain with the original entries and
// we will not listen on the new or modified addresses.
if (!fRes)
{
#ifdef DBG
DbgPrint("RPC: Could not update pFirewallTable. The server may not listen on some interfaces.\n");
#endif
return;
}
// Go through the list of the available transport addresses, initialize them,
// and make them listen on the new network address.
GetTransportProtocol(TCP)->InitNewAddresses(TCP);
}
FIREWALL_INFO *
GetFirewallTableCopy (
void
)
/*++
Routine Description:
Allocates and returns a copy of the firewall table.
The caller must free the table after finishing using it.
The function must be protected against racing with DoFirewallInit().
The function must be called after the table has been initialized.
Arguments:
None
Return Value:
Pointer to a copy of the firewall table.
--*/
{
FIREWALL_INFO *pCopyOfFirewallTable = NULL;
DWORD size;
ASSERT(fFirewallInited);
if (pFirewallTable == NULL)
{
return NULL;
}
size = sizeof(FIREWALL_INFO) + sizeof(FIREWALL_INFO_ENTRY) * (pFirewallTable->NumAddresses - 1);
pCopyOfFirewallTable = (FIREWALL_INFO*) new char[size];
if (pCopyOfFirewallTable == NULL)
{
return NULL;
}
RpcpMemoryCopy(pCopyOfFirewallTable, pFirewallTable, size);
return pCopyOfFirewallTable;
}
RPC_TRANSPORT_INTERFACE
TransportLoad (
IN const RPC_CHAR * RpcProtocolSequence
)
{
static fLoaded = FALSE;
RPC_STATUS RpcStatus;
PROTOCOL_ID index;
RPC_STATUS status;
RPC_TRANSPORT_INTERFACE pInfo;
if (fLoaded == FALSE)
{
RpcStatus = InitTransportProtocols();
if (RpcStatus != RPC_S_OK)
return NULL;
//
// Query the computer name - used by most protocols.
//
gdwComputerNameLength = sizeof(gpstrComputerName)/sizeof(RPC_CHAR);
if (!GetComputerName((RPC_SCHAR *)gpstrComputerName, &gdwComputerNameLength))
{
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
"RPCTRANS: GetComputerNameW failed: %d\n",
GetLastError()));
return(0);
}
gdwComputerNameLength++; // Include the null in the count.
// Create initial IO completion port. This saves us from a race
// assigning the global io completion port.
ASSERT(RpcCompletionPort == 0);
RpcCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
0,
0,
0); // PERF REVIEW
if (RpcCompletionPort == 0)
{
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL, RPCTRANS "Failed to create initial completion port: %d\n",
GetLastError()));
return(0);
}
InactiveRpcCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
0,
0,
MAXULONG); // PERF REVIEW
if (InactiveRpcCompletionPort == 0)
{
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL, RPCTRANS "Failed to create initial completion port: %d\n",
GetLastError()));
CloseHandle(RpcCompletionPort);
RpcCompletionPort = 0;
return(0);
}
HANDLE hCurrentCompletionPortHandle;
DWORD i;
BOOL fSuccess = TRUE;
HANDLE hSourceProcessHandle = GetCurrentProcess();
RpcCompletionPorts = new HANDLE[gNumberOfProcessors * 2];
CompletionPortHandleLoads = new long[gNumberOfProcessors * 2];
if ((RpcCompletionPorts == NULL) || (CompletionPortHandleLoads == NULL))
{
CloseHandle(RpcCompletionPort);
RpcCompletionPort = 0;
return 0;
}
for (i = 0; i < gNumberOfProcessors * 2; i ++)
{
RpcCompletionPorts[i] = 0;
CompletionPortHandleLoads[i] = 0;
}
RpcCompletionPorts[0] = RpcCompletionPort;
for (i = 1; i < gNumberOfProcessors * 2; i ++)
{
fSuccess = DuplicateHandle(hSourceProcessHandle, RpcCompletionPort,
hSourceProcessHandle, &hCurrentCompletionPortHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
if (!fSuccess)
break;
ASSERT(hCurrentCompletionPortHandle != 0);
RpcCompletionPorts[i] = hCurrentCompletionPortHandle;
}
if (!fSuccess)
{
FreeCompletionPortHashTable();
CloseHandle(RpcCompletionPort);
RpcCompletionPort = 0;
return 0;
}
//
// Initalize locks, use Rtl* so we don't need to catch exception.
//
NTSTATUS NtStatus;
NtStatus = RtlInitializeCriticalSectionAndSpinCount(&AddressListLock, PREALLOCATE_EVENT_MASK);
if (!NT_SUCCESS(NtStatus))
{
FreeCompletionPortHashTable();
CloseHandle(RpcCompletionPort);
RpcCompletionPort = 0;
return 0;
}
if (gBCacheMode == BCacheModeDirect)
{
// allocate minimum post size. This guarantees that buffer
// will always be at the end.
gPostSize = sizeof(CONN_RPC_HEADER);
}
fLoaded = TRUE;
}
index = MapProtseq(RpcProtocolSequence);
if (!index)
{
return(0);
}
pInfo = 0;
switch (index)
{
case NMP:
pInfo = (RPC_TRANSPORT_INTERFACE) NMP_TransportLoad();
break;
#ifdef NETBIOS_ON
case NBF:
case NBT:
case NBI:
pInfo = (RPC_TRANSPORT_INTERFACE) NB_TransportLoad(index);
break;
#endif
case TCP:
#ifdef SPX_ON
case SPX:
#endif
#ifdef APPLETALK_ON
case DSP:
#endif
case HTTP:
pInfo = (RPC_TRANSPORT_INTERFACE) WS_TransportLoad(index);
break;
#ifdef NCADG_MQ_ON
case MSMQ:
#endif
case CDP:
case UDP:
#ifdef IPX_ON
case IPX:
#endif
pInfo = (RPC_TRANSPORT_INTERFACE) DG_TransportLoad(index);
break;
}
if (pInfo == 0)
{
#ifdef UNICODE
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "Load of %S failed\n",
RpcProtocolSequence));
#else
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "Load of %s failed\n",
RpcProtocolSequence));
#endif
return(0);
}
// When running with corruption injection for the client receives,
// we may overwrite the default transport interfaces with costom Avrf versions.
CORRUPTION_ASSERT(pInfo == TransportTable[index].pInfo);
return(pInfo);
}
void
UnjoinCompletionPort (
void
)
{
DWORD NumberOfBytes;
ULONG_PTR CompletionKey;
LPOVERLAPPED Overlapped;
BOOL b;
// The kernel today doesn't have the functionality to
// unjoin a thread from a completion port. Therefore
// we fake unjoining by joining another completion port which has
// unlimited concurrency called the inactive completion port.
// Thus threads unjoined from the main completion port will not
// affect its concurrency. One undesirable effect is that each
// time a thread joined to the inactive completion port blocks,
// it will try to wake up another thread, and there won't be any
// there, which is a waste of CPU. Ideally, we should have had
// a capability to set KTHREAD::Queue to NULL, but we don't
b = GetQueuedCompletionStatus(InactiveRpcCompletionPort,
&NumberOfBytes,
&CompletionKey,
&Overlapped,
0
);
// this operation should either timeout or fail - it should never
// succeed. If it does, this means somebody has erroneously posted
// an IO on the inactive completion port
ASSERT(b == FALSE);
}
void RPC_CLIENT_IP_ADDRESS::ZeroOut (
void
)
/*++
Routine Description:
Create an empty IP address.
Arguments:
Return Value:
--*/
{
SOCKADDR_IN *EmptyAddr = (SOCKADDR_IN *) Data;
EmptyAddr->sin_family = AF_INET;
EmptyAddr->sin_addr.S_un.S_addr = 0;
EmptyAddr->sin_port = 0;
RpcpMemorySet(&EmptyAddr->sin_zero, 0, sizeof(EmptyAddr->sin_zero));
DataSize = 0;
}
#ifdef _INTERNAL_RPC_BUILD_
void
I_RpcltDebugSetPDUFilter (
IN RPCLT_PDU_FILTER_FUNC pfnFilter
)
{
gpfnFilter = pfnFilter;
}
#endif