3377 lines
94 KiB
C++
3377 lines
94 KiB
C++
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1991 - 1999
|
|
|
|
Module Name:
|
|
|
|
binding.cxx
|
|
|
|
Abstract:
|
|
|
|
The implementation of the DCE binding class is contained in this
|
|
file.
|
|
|
|
Author:
|
|
|
|
Michael Montague (mikemon) 04-Nov-1991
|
|
|
|
Revision History:
|
|
Kamen Moutafov (KamenM) Dec 99 - Feb 2000 - Support for cell debugging stuff
|
|
|
|
--*/
|
|
|
|
#include <precomp.hxx>
|
|
#include <epmap.h>
|
|
#include <hndlsvr.hxx>
|
|
#include <sdict2.hxx>
|
|
#include <dispatch.h>
|
|
#include <osfpcket.hxx>
|
|
#include <bitset.hxx>
|
|
#include <ProtBind.hxx>
|
|
#include <osfclnt.hxx>
|
|
#include <osfsvr.hxx>
|
|
#include <rpctrans.hxx>
|
|
|
|
|
|
UUID MgmtIf = { 0xafa8bd80,0x7d8a,0x11c9,
|
|
{0xbe,0xf4,0x08,0x00,0x2b,0x10,0x29,0x89} };
|
|
UUID NullUuid = { 0L, 0, 0, {0,0,0,0,0,0,0,0} };
|
|
|
|
|
|
int
|
|
IsMgmtIfUuid(
|
|
UUID PAPI * IfId
|
|
)
|
|
{
|
|
|
|
if (RpcpMemoryCompare(IfId, &MgmtIf, sizeof(UUID)) == 0)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
RPC_CHAR *
|
|
DuplicateString (
|
|
IN RPC_CHAR PAPI * String
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When this routine is called, it will duplicate the string into a fresh
|
|
string and return it.
|
|
|
|
Arguments, either:
|
|
|
|
String - Supplies the string to be duplicated.
|
|
Ansi String - Supplies the string to be duplicated.
|
|
|
|
Return Value:
|
|
|
|
The duplicated string is returned. If insufficient memory is available
|
|
to allocate a fresh string, zero will be returned.
|
|
|
|
--*/
|
|
{
|
|
RPC_CHAR * FreshString, * FreshStringScan;
|
|
RPC_CHAR PAPI * StringScan;
|
|
unsigned int Length;
|
|
|
|
ASSERT(String);
|
|
|
|
Length = 1;
|
|
StringScan = String;
|
|
while (*StringScan++ != 0)
|
|
Length += 1;
|
|
|
|
FreshString = new RPC_CHAR[Length];
|
|
if (FreshString == 0)
|
|
return(0);
|
|
|
|
for (FreshStringScan = FreshString, StringScan = String;
|
|
*StringScan != 0; FreshStringScan++, StringScan++)
|
|
{
|
|
*FreshStringScan = *StringScan;
|
|
}
|
|
*FreshStringScan = *StringScan;
|
|
|
|
return(FreshString);
|
|
}
|
|
|
|
|
|
DCE_BINDING::DCE_BINDING (
|
|
IN RPC_CHAR PAPI * ObjectUuid OPTIONAL,
|
|
IN RPC_CHAR PAPI * RpcProtocolSequence OPTIONAL,
|
|
IN RPC_CHAR PAPI * NetworkAddress OPTIONAL,
|
|
IN RPC_CHAR PAPI * Endpoint OPTIONAL,
|
|
IN RPC_CHAR PAPI * Options OPTIONAL,
|
|
OUT RPC_STATUS PAPI * Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The constructor creates a DCE_BINDING object based on the pieces of
|
|
the string binding specified.
|
|
|
|
Arguments:
|
|
|
|
ObjectUuid - Optionally supplies the object uuid component of the
|
|
binding.
|
|
|
|
RpcProtocolSequence - Optionally supplies the rpc protocol sequence
|
|
component of the binding.
|
|
|
|
NetworkAddress - Optionally supplies the network address component
|
|
of the binding.
|
|
|
|
Endpoint - Optionally supplies the endpoint component of the binding.
|
|
|
|
Options - Optionally supplies the network options component of the
|
|
binding.
|
|
|
|
Status - Returns the status of the operation. This argument will
|
|
be set to one of the following values.
|
|
|
|
RPC_S_OK - The operation completed successfully.
|
|
|
|
RPC_S_INVALID_STRING_UUID - The specified object uuid does
|
|
not contain the valid string representation of a uuid.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to
|
|
complete the operation.
|
|
|
|
--*/
|
|
{
|
|
ALLOCATE_THIS(DCE_BINDING);
|
|
|
|
*Status = RPC_S_OK;
|
|
|
|
if ( ARGUMENT_PRESENT(ObjectUuid)
|
|
&& (ObjectUuid[0] != 0))
|
|
{
|
|
if (this->ObjectUuid.ConvertFromString(ObjectUuid))
|
|
{
|
|
*Status = RPC_S_INVALID_STRING_UUID;
|
|
this->ObjectUuid.SetToNullUuid();
|
|
}
|
|
}
|
|
else
|
|
this->ObjectUuid.SetToNullUuid();
|
|
|
|
if (ARGUMENT_PRESENT(RpcProtocolSequence))
|
|
{
|
|
this->RpcProtocolSequence = DuplicateString(RpcProtocolSequence);
|
|
if (this->RpcProtocolSequence == 0)
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
this->RpcProtocolSequence = 0;
|
|
|
|
if (ARGUMENT_PRESENT(NetworkAddress))
|
|
{
|
|
this->NetworkAddress = DuplicateString(NetworkAddress);
|
|
if (this->NetworkAddress == 0)
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
this->NetworkAddress = 0;
|
|
|
|
if (ARGUMENT_PRESENT(Endpoint))
|
|
{
|
|
this->Endpoint = DuplicateString(Endpoint);
|
|
if (this->Endpoint == 0)
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
this->Endpoint = 0;
|
|
|
|
if (ARGUMENT_PRESENT(Options))
|
|
{
|
|
this->Options = DuplicateString(Options);
|
|
if (this->Options == 0)
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
this->Options = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*static*/ RPC_CHAR PAPI *
|
|
StringCharSearchWithEscape (
|
|
IN RPC_CHAR PAPI * String,
|
|
IN unsigned int Character
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the same as the library routine, strchr, except that
|
|
the backslash character ('\') is treated as an escape character.
|
|
|
|
Arguments:
|
|
|
|
String - Supplies the string in which to search for the character.
|
|
|
|
Character - Supplies the character to search for in the string.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the first occurance of Character in String is returned.
|
|
If Character does not exist in String, then 0 is returned.
|
|
|
|
--*/
|
|
{
|
|
#ifdef DBCS_ENABLED
|
|
ASSERT(IsDBCSLeadByte((RPC_CHAR)Character) == FALSE);
|
|
ASSERT(IsDBCSLeadByte(RPC_CONST_CHAR('\\')) == FALSE);
|
|
|
|
while(*String != (RPC_CHAR)Character)
|
|
{
|
|
if (*String == 0)
|
|
return(0);
|
|
|
|
if (*String == RPC_CONST_CHAR('\\'))
|
|
{
|
|
String = (RPC_CHAR *)CharNext((LPCSTR)String);
|
|
}
|
|
String = (RPC_CHAR *)CharNext((LPCSTR)String);
|
|
}
|
|
return(String);
|
|
#else
|
|
while (*String != (RPC_CHAR) Character)
|
|
{
|
|
if (*String == RPC_CONST_CHAR('\\'))
|
|
String++;
|
|
if (*String == 0)
|
|
return(0);
|
|
String++;
|
|
}
|
|
return(String);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*static*/ void
|
|
StringCopyWithEscape (
|
|
OUT RPC_CHAR PAPI * Destination,
|
|
IN RPC_CHAR PAPI * Source
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the same as the library routine, strcpy, except that
|
|
the backslash character ('\') is treated as an escape character. When
|
|
a character is escaped, the backslash character is not copied to the
|
|
Destination.
|
|
|
|
Arguments:
|
|
|
|
Destination - Returns a duplicate of the string specified in Source,
|
|
but with out escaped characters escaped.
|
|
|
|
Source - Specifies the string to be copied.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BOOL fLastQuote = FALSE;
|
|
|
|
#ifdef DBCS_ENABLED
|
|
ASSERT(IsDBCSLeadByte('\\') == FALSE);
|
|
#endif
|
|
|
|
|
|
while ((*Destination = *Source) != 0)
|
|
{
|
|
#ifdef DBCS_ENABLED
|
|
if (IsDBCSLeadByte(*Source))
|
|
{
|
|
// Copy the whole DBCS character; don't look for
|
|
// escapes within the character.
|
|
Destination++;
|
|
Source++;
|
|
*Destination = *Source;
|
|
if (*Source == 0)
|
|
{
|
|
ASSERT(0); // Bad string, NULL following a lead byte.
|
|
return;
|
|
}
|
|
Destination++;
|
|
Source++;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if ( *Source != RPC_CONST_CHAR('\\')
|
|
|| fLastQuote == TRUE)
|
|
{
|
|
Destination++;
|
|
fLastQuote = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fLastQuote = TRUE;
|
|
}
|
|
Source++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*static*/ RPC_STATUS
|
|
ParseAndCopyEndpointField (
|
|
OUT RPC_CHAR ** Endpoint,
|
|
IN RPC_CHAR PAPI * String
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses and then copies the endpoint field in String. A
|
|
copy of the field is made into a newly allocated string and returned
|
|
in Endpoint. String is assumed to contain only the endpoint field;
|
|
the terminating ',' or ']' are not included.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - Returns a copy of the endpoint field in a newly allocated
|
|
string.
|
|
|
|
String - Supplies the endpoint field to be parsed and copied.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - The operation completed successfully.
|
|
|
|
RPC_S_OUT_OF_MEMORY - There is no memory available to make a copy
|
|
of the string.
|
|
|
|
RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint field is syntactically
|
|
incorrect. This error code will be returned if the endpoint field
|
|
does not match the following pattern.
|
|
|
|
[ <Endpoint> | "endpoint=" <Endpoint> ]
|
|
|
|
--*/
|
|
{
|
|
// Search will be used to scan along the string to find the end of
|
|
// the endpoint field and the '='.
|
|
|
|
RPC_CHAR PAPI * Search;
|
|
|
|
Search = StringCharSearchWithEscape(String,RPC_CONST_CHAR('='));
|
|
if (Search == 0)
|
|
{
|
|
// This means that we have the <Endpoint> pattern, so we just
|
|
// copy the endpoint field.
|
|
|
|
Search = StringCharSearchWithEscape(String,0);
|
|
*Endpoint = new RPC_CHAR[size_t(Search - String + 1)];
|
|
if (*Endpoint == 0)
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
StringCopyWithEscape(*Endpoint,String);
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
// Otherwise, we have the "endpoint=" pattern. First we need to check
|
|
// that the string before the '=' is in fact "endpoint".
|
|
|
|
*Search = 0;
|
|
if ( RpcpStringCompare(String, RPC_CONST_STRING("endpoint")) != 0 )
|
|
{
|
|
*Search = RPC_CONST_CHAR('=');
|
|
return(RPC_S_INVALID_ENDPOINT_FORMAT);
|
|
}
|
|
*Search = RPC_CONST_CHAR('=');
|
|
String = Search + 1;
|
|
|
|
// Now we just need to allocate a new string and copy the endpoint into
|
|
// it.
|
|
|
|
Search = StringCharSearchWithEscape(String,0);
|
|
*Endpoint = new RPC_CHAR[size_t(Search - String + 1)];
|
|
if (*Endpoint == 0)
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
|
|
StringCopyWithEscape(*Endpoint,String);
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_CHAR *
|
|
AllocateEmptyString (
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates and returns an empty string ("").
|
|
|
|
Return Value:
|
|
|
|
A newly allocated empty string will be returned.
|
|
|
|
--*/
|
|
{
|
|
RPC_CHAR * String;
|
|
|
|
String = new RPC_CHAR[1];
|
|
if (String != 0)
|
|
*String = 0;
|
|
return(String);
|
|
}
|
|
|
|
|
|
DCE_BINDING::DCE_BINDING (
|
|
IN RPC_CHAR PAPI * StringBinding,
|
|
OUT RPC_STATUS PAPI * Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This constructor creates a DCE_BINDING object from a string binding,
|
|
which requires that the string binding be parsed into seperate
|
|
strings and validated.
|
|
|
|
Arguments:
|
|
|
|
StringBinding - Supplies the string being to be parsed.
|
|
|
|
Status - Returns the status of the operation. This parameter will
|
|
take on the following values:
|
|
|
|
RPC_S_OK - The operation completed successfully.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to
|
|
allocate space for the fields of the string binding.
|
|
|
|
RPC_S_INVALID_STRING_BINDING - The string binding is
|
|
syntactically invalid.
|
|
|
|
RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint specified in
|
|
the string binding is syntactically incorrect.
|
|
|
|
RPC_S_INVALID_STRING_UUID - The specified object uuid does not
|
|
contain the valid string representation of a uuid.
|
|
|
|
--*/
|
|
{
|
|
// String will point to the beginning of the field we are trying to
|
|
// parse.
|
|
|
|
RPC_CHAR PAPI * String;
|
|
|
|
// Search will be used to scan along the string to find the end of
|
|
// the field we are trying to parse.
|
|
|
|
RPC_CHAR PAPI * Search;
|
|
|
|
// This will contain the string representation of the object uuid.
|
|
|
|
RPC_CHAR PAPI * ObjectUuidString;
|
|
|
|
ALLOCATE_THIS(DCE_BINDING);
|
|
|
|
// A string binding consists of an optional object uuid, an RPC protocol
|
|
// sequence, a network address, an optional endpoint, and zero or more
|
|
// option fields.
|
|
//
|
|
// [ <Object UUID> "@" ] <RPC Protocol Sequence> ":" <Network Address>
|
|
// [ "[" ( <Endpoint> | "endpoint=" <Endpoint> | ) [","]
|
|
// [ "," <Option Name> "=" <Option Value>
|
|
// ( <Option Name> "=" <Option Value> )* ] "]" ]
|
|
//
|
|
// If an object UUID is specified, then it will be followed by '@'.
|
|
// Likewise, if an endpoint and/or option(s) are specified, they will
|
|
// be in square brackets. Finally, one or more options are specified,
|
|
// then ',' must seperate the optional endpoint from the options. The
|
|
// backslash character '\' is treated as an escape character in all
|
|
// string binding fields.
|
|
|
|
// To begin with, we need to set all of the string pointers to zero.
|
|
// This is necessary so that when we do memory cleanup for error
|
|
// recovery, we know which pointers we allocated a string for.
|
|
|
|
ObjectUuidString = 0;
|
|
RpcProtocolSequence = 0;
|
|
NetworkAddress = 0;
|
|
Endpoint = 0;
|
|
Options = 0;
|
|
|
|
String = StringBinding;
|
|
|
|
|
|
// To begin with, we need to parse off the object UUID from the string
|
|
// if it exists.
|
|
|
|
Search = StringCharSearchWithEscape(String,RPC_CONST_CHAR('@'));
|
|
if (Search == 0)
|
|
{
|
|
// The string binding does not contain an object UUID.
|
|
|
|
ObjectUuid.SetToNullUuid();
|
|
}
|
|
else
|
|
{
|
|
// There is an object UUID in the string.
|
|
|
|
// We need to add one for the terminating zero in the
|
|
// string.
|
|
|
|
ObjectUuidString = (RPC_CHAR PAPI *) RpcpFarAllocate(
|
|
sizeof(RPC_CHAR)*size_t(Search - String + 1));
|
|
|
|
if (ObjectUuidString == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
|
|
// Now copy the string.
|
|
|
|
*Search = 0;
|
|
StringCopyWithEscape(ObjectUuidString,String);
|
|
*Search = RPC_CONST_CHAR('@');
|
|
|
|
// Finally, update String so that we are ready to parse the next
|
|
// field.
|
|
|
|
String = Search + 1;
|
|
|
|
// Now convert the string representation of the object uuid
|
|
// into an actual uuid.
|
|
|
|
if (ObjectUuid.ConvertFromString(ObjectUuidString))
|
|
{
|
|
*Status = RPC_S_INVALID_STRING_UUID;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
|
|
RpcpFarFree(ObjectUuidString);
|
|
ObjectUuidString = 0;
|
|
}
|
|
|
|
// The RPC protocol sequence field comes next; it is terminated by
|
|
// ':'. Both the RPC protocol sequence field and the ':' are required.
|
|
|
|
Search = StringCharSearchWithEscape(String,RPC_CONST_CHAR(':'));
|
|
if (Search == 0)
|
|
{
|
|
// This is an error, because the RPC protocol sequence field is
|
|
// required. We may need to free the string we allocated for
|
|
// the object UUID field.
|
|
|
|
*Status = RPC_S_INVALID_STRING_BINDING;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
else
|
|
{
|
|
// The same comments which applied to copying the object UUID
|
|
// apply here as well.
|
|
|
|
RpcProtocolSequence = new RPC_CHAR[size_t(Search - String + 1)];
|
|
if (RpcProtocolSequence == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
|
|
*Search = 0;
|
|
StringCopyWithEscape(RpcProtocolSequence,String);
|
|
*Search = RPC_CONST_CHAR(':');
|
|
|
|
// Finally, update String so that we are ready to parse the next
|
|
// field.
|
|
|
|
String = Search + 1;
|
|
}
|
|
|
|
// Next comes the network address field which is required. It is
|
|
// terminated by zero or '['.
|
|
|
|
Search = StringCharSearchWithEscape(String,RPC_CONST_CHAR('['));
|
|
if (Search == 0)
|
|
{
|
|
// This means that the network address is the last field, so we
|
|
// just copy it, and set the remaining fields to be empty strings.
|
|
|
|
Search = StringCharSearchWithEscape(String,0);
|
|
NetworkAddress = new RPC_CHAR[size_t(Search - String + 1)];
|
|
if (NetworkAddress == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
StringCopyWithEscape(NetworkAddress,String);
|
|
|
|
Endpoint = AllocateEmptyString();
|
|
if (Endpoint == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
|
|
Options = 0;
|
|
|
|
*Status = RPC_S_OK;
|
|
return;
|
|
}
|
|
|
|
// Otherwise, if we reach here, there is an endpoint and/or options
|
|
// left to parse. But before we parse them, lets copy the network
|
|
// address field.
|
|
|
|
NetworkAddress = new RPC_CHAR [size_t(Search - String + 1)];
|
|
if (NetworkAddress == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
*Search = 0;
|
|
StringCopyWithEscape(NetworkAddress,String);
|
|
*Search = RPC_CONST_CHAR('[');
|
|
|
|
String = Search + 1;
|
|
|
|
// Now we are ready to parse off the endpoint and/or options.
|
|
// To begin with, we check to see if there is a comma.
|
|
|
|
Search = StringCharSearchWithEscape(String,RPC_CONST_CHAR(','));
|
|
if (Search == 0)
|
|
{
|
|
// There is only one token in the string binding. See
|
|
// if its an endpoint, if not, it must be an option.
|
|
// Before we copy the endpoint field, we need to check
|
|
// for the closing square bracket.
|
|
|
|
Search = StringCharSearchWithEscape(String,RPC_CONST_CHAR(']'));
|
|
if (Search == 0)
|
|
{
|
|
// This is an error; the string binding is invalid. We need to
|
|
// clean everything up, and return an error.
|
|
|
|
*Status = RPC_S_INVALID_ENDPOINT_FORMAT;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
|
|
*Search = 0;
|
|
*Status = ParseAndCopyEndpointField(&Endpoint,String);
|
|
*Search = RPC_CONST_CHAR(']');
|
|
|
|
// If the parse succeeded, allocate an empty option.
|
|
if (*Status == RPC_S_OK)
|
|
{
|
|
Options = 0;
|
|
}
|
|
|
|
// If the endpoint parse failed with RPC_S_INVALID_ENDPOINT_FORMAT,
|
|
// the token must be an option.
|
|
else if (*Status == RPC_S_INVALID_ENDPOINT_FORMAT)
|
|
{
|
|
Endpoint = AllocateEmptyString();
|
|
if (Endpoint == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
|
|
Options = new RPC_CHAR [size_t(Search - String + 1)];
|
|
if (Options == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
|
|
*Search = 0;
|
|
StringCopyWithEscape(Options,String);
|
|
*Search = RPC_CONST_CHAR(']');
|
|
|
|
}
|
|
|
|
// Something bad must have happened, clean up.
|
|
else
|
|
goto FreeMemoryAndReturn;
|
|
|
|
*Status = RPC_S_OK;
|
|
return;
|
|
}
|
|
|
|
// When we reach here, we know that there are options. We have
|
|
// to see if there is an endpoint. If there is, copy it and then
|
|
// copy the options. If there isn't, allocate a null endpoint and
|
|
// copy the options.
|
|
|
|
*Search = 0;
|
|
*Status = ParseAndCopyEndpointField(&Endpoint,String);
|
|
*Search = RPC_CONST_CHAR(',');
|
|
|
|
// If there was an endpoint, skip that part of the string.
|
|
// Otherwise treat it as an option.
|
|
if (*Status == RPC_S_OK)
|
|
String = Search + 1;
|
|
else if (*Status != RPC_S_INVALID_ENDPOINT_FORMAT)
|
|
goto FreeMemoryAndReturn;
|
|
|
|
// There was no endpoint, so allocate an empty string.
|
|
else
|
|
{
|
|
Endpoint = AllocateEmptyString();
|
|
if (Endpoint == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
}
|
|
|
|
// Even if the caller did not specify the NetworkOptions argument,
|
|
// we still want to validate the rest of the string binding.
|
|
|
|
Search = StringCharSearchWithEscape(String,RPC_CONST_CHAR(']'));
|
|
if (Search == 0)
|
|
{
|
|
// This is an error; the string binding is invalid. We need
|
|
// to clean everything up, and return an error.
|
|
|
|
*Status = RPC_S_INVALID_STRING_BINDING;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
|
|
// Go ahead and copy the network options field if we reach here.
|
|
|
|
Options = new RPC_CHAR [size_t(Search - String + 1)];
|
|
if (Options == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
goto FreeMemoryAndReturn;
|
|
}
|
|
|
|
*Search = 0;
|
|
StringCopyWithEscape(Options,String);
|
|
*Search = RPC_CONST_CHAR(']');
|
|
|
|
// Everything worked out fine; we just fall through the memory
|
|
// cleanup code and return.
|
|
|
|
*Status = RPC_S_OK;
|
|
|
|
// If an error occured up above, we will have set status to the
|
|
// appropriate error code, and jumped here. We may also arrive
|
|
// here if an error did not occur, hence the check for an error status
|
|
// before we clean up the memory.
|
|
|
|
FreeMemoryAndReturn:
|
|
|
|
if (*Status != RPC_S_OK)
|
|
{
|
|
if (ObjectUuidString != 0)
|
|
RpcpFarFree(ObjectUuidString);
|
|
|
|
delete RpcProtocolSequence;
|
|
delete NetworkAddress;
|
|
delete Endpoint;
|
|
delete Options;
|
|
|
|
ObjectUuidString = 0;
|
|
RpcProtocolSequence = 0;
|
|
NetworkAddress = 0;
|
|
Endpoint = 0;
|
|
Options = 0;
|
|
}
|
|
}
|
|
|
|
|
|
DCE_BINDING::~DCE_BINDING (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We cleaning things up here when a DCE_BINDING is getting deleted.
|
|
This consists of freeing the strings pointed to by the fields of
|
|
the class.
|
|
|
|
--*/
|
|
{
|
|
delete RpcProtocolSequence;
|
|
delete NetworkAddress;
|
|
delete Endpoint;
|
|
delete Options;
|
|
}
|
|
|
|
|
|
/*static*/ int
|
|
StringLengthWithEscape (
|
|
IN RPC_CHAR PAPI * String
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the same as the library routine, strlen, except that
|
|
for that following characters, '@', ':', '\', '[', and ',', are
|
|
counted as two characters (to save space for a \) rather than one.
|
|
|
|
Arguments:
|
|
|
|
String - Supplies a string whose length will be determined.
|
|
|
|
Return Value:
|
|
|
|
The length of the string will be returned including enough space to
|
|
escape certain characters.
|
|
|
|
--*/
|
|
{
|
|
// We use length to keep track of how long the string is so far.
|
|
|
|
int Length;
|
|
|
|
Length = 0;
|
|
while (*String != 0)
|
|
{
|
|
#ifdef DBCS_ENABLED
|
|
if (IsDBCSLeadByte(*String))
|
|
{
|
|
String += 2;
|
|
Length += 2;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if ( (*String == RPC_CONST_CHAR('@'))
|
|
|| (*String == RPC_CONST_CHAR(':'))
|
|
|| (*String == RPC_CONST_CHAR('\\'))
|
|
|| (*String == RPC_CONST_CHAR('['))
|
|
|| (*String == RPC_CONST_CHAR(']'))
|
|
|| (*String == RPC_CONST_CHAR(',')))
|
|
Length += 2;
|
|
else
|
|
Length += 1;
|
|
String += 1;
|
|
}
|
|
}
|
|
return(Length);
|
|
}
|
|
|
|
/*static*/ RPC_CHAR PAPI *
|
|
StringCopyEscapeCharacters (
|
|
OUT RPC_CHAR PAPI * Destination,
|
|
IN RPC_CHAR PAPI * Source
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Source is copied into destination. When coping into destination, the
|
|
following characters are escaped by prefixing them with a '\': '@',
|
|
':', '\', '[', ']', and ','.
|
|
|
|
Arguments:
|
|
|
|
Destination - Returns a copy of Source.
|
|
|
|
Source - Supplies a string to be copied into destination.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the terminating zero in Destination is returned.
|
|
|
|
--*/
|
|
{
|
|
while ((*Destination = *Source) != 0)
|
|
{
|
|
#ifdef DBCS_ENABLED
|
|
if (IsDBCSLeadByte(*Source))
|
|
{
|
|
Destination++;
|
|
Source++;
|
|
*Destination = *Source;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if ( (*Source == RPC_CONST_CHAR('@'))
|
|
|| (*Source == RPC_CONST_CHAR(':'))
|
|
|| (*Source == RPC_CONST_CHAR('\\'))
|
|
|| (*Source == RPC_CONST_CHAR('['))
|
|
|| (*Source == RPC_CONST_CHAR(']'))
|
|
|| (*Source == RPC_CONST_CHAR(',')))
|
|
{
|
|
*Destination++ = RPC_CONST_CHAR('\\');
|
|
*Destination = *Source;
|
|
}
|
|
}
|
|
Destination++;
|
|
Source++;
|
|
}
|
|
*Destination = 0;
|
|
return(Destination);
|
|
}
|
|
|
|
|
|
RPC_CHAR PAPI *
|
|
DCE_BINDING::StringBindingCompose (
|
|
IN RPC_UUID PAPI * Uuid OPTIONAL,
|
|
IN BOOL fStatic
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method creates a string binding from a DCE_BINDING by combining
|
|
the components of a string binding.
|
|
|
|
Arguments:
|
|
|
|
Uuid - Optionally supplies a uuid to use in composing the string
|
|
binding rather than the object uuid contained in the DCE_BINDING.
|
|
|
|
Return Value:
|
|
|
|
String Binding - A newly allocated and created (from the components)
|
|
is returned.
|
|
|
|
0 - Insufficient memory is available to allocate the string binding.
|
|
|
|
--*/
|
|
{
|
|
// We will use the following automatic variable to calculate the
|
|
// required length of the string.
|
|
|
|
int Length;
|
|
|
|
// Copy is used to copy the fields of the string binding into the
|
|
// string binding.
|
|
|
|
RPC_CHAR PAPI * Copy;
|
|
|
|
// StringBinding will contain the string binding we are supposed
|
|
// to be creating here.
|
|
|
|
RPC_CHAR PAPI * StringBinding;
|
|
|
|
// This routine is written as follows. First we need to calculate
|
|
// the amount of space required to hold the string binding. This
|
|
// is not quite straight forward as it seems: we need to escape
|
|
// '@', ':', '\', '[', ']', and ',' characters in the string binding
|
|
// we create. After allocating the string, we copy each piece in,
|
|
// escaping characters as necessary.
|
|
|
|
// Go through and figure out how much space each field of the string
|
|
// binding will take up.
|
|
|
|
if (!ARGUMENT_PRESENT(Uuid))
|
|
Uuid = &ObjectUuid;
|
|
|
|
if (Uuid->IsNullUuid() == 0)
|
|
{
|
|
// The extra plus one is to save space for the '@' which seperates
|
|
// the object UUID field from the RPC protocol sequence field. The
|
|
// length of the string representation of a uuid is always 36
|
|
// characters.
|
|
|
|
Length = 36 + 1;
|
|
}
|
|
else
|
|
{
|
|
Length = 0;
|
|
}
|
|
|
|
if (RpcProtocolSequence != 0)
|
|
{
|
|
Length += StringLengthWithEscape(RpcProtocolSequence);
|
|
}
|
|
|
|
// We need to save space for the ':' seperating the RPC protocol
|
|
// sequence field from the network address field.
|
|
|
|
Length += 1;
|
|
|
|
if (NetworkAddress != 0)
|
|
Length += StringLengthWithEscape(NetworkAddress);
|
|
|
|
if ( (Endpoint != 0)
|
|
&& (Endpoint[0] != 0))
|
|
{
|
|
// The plus two is to save space for the '[' and ']' surrounding
|
|
// the endpoint and options fields.
|
|
|
|
Length += StringLengthWithEscape(Endpoint) + 2;
|
|
|
|
if ( (Options != 0)
|
|
&& (Options[0] != 0))
|
|
{
|
|
// The extra plus one is for the ',' which goes before the
|
|
// options field.
|
|
|
|
Length += StringLengthWithEscape(Options) + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (Options != 0)
|
|
&& (Options[0] != 0))
|
|
{
|
|
// We need to add three to the length to save space for the
|
|
// '[' and ']' which will go around the options, and the ','
|
|
// which goes before the options.
|
|
|
|
Length += StringLengthWithEscape(Options) + 3;
|
|
}
|
|
}
|
|
|
|
// Finally, include space for the terminating zero in the string.
|
|
|
|
Length += 1;
|
|
|
|
// Now we allocate space for the string binding and copy all of the
|
|
// pieces into it.
|
|
|
|
StringBinding = (RPC_CHAR PAPI *)
|
|
RpcpFarAllocate(Length * sizeof(RPC_CHAR));
|
|
if (StringBinding == 0)
|
|
return(0);
|
|
|
|
if (Uuid->IsNullUuid() == 0)
|
|
{
|
|
Copy = Uuid->ConvertToString(StringBinding);
|
|
*Copy++ = RPC_CONST_CHAR('@');
|
|
}
|
|
else
|
|
{
|
|
Copy = StringBinding;
|
|
}
|
|
|
|
if (RpcProtocolSequence != 0)
|
|
{
|
|
Copy = StringCopyEscapeCharacters(Copy, RpcProtocolSequence);
|
|
}
|
|
|
|
*Copy++ = RPC_CONST_CHAR(':');
|
|
|
|
if (NetworkAddress != 0)
|
|
{
|
|
Copy = StringCopyEscapeCharacters(Copy, NetworkAddress);
|
|
}
|
|
|
|
if ( (fStatic == 0)
|
|
&& (Endpoint != 0)
|
|
&& (Endpoint[0] != 0))
|
|
{
|
|
*Copy++ = RPC_CONST_CHAR('[');
|
|
Copy = StringCopyEscapeCharacters(Copy, Endpoint);
|
|
|
|
if ( (Options != 0)
|
|
&& (Options[0] != 0))
|
|
{
|
|
*Copy++ = RPC_CONST_CHAR(',');
|
|
Copy = StringCopyEscapeCharacters(Copy, Options);
|
|
}
|
|
|
|
*Copy++ = RPC_CONST_CHAR(']');
|
|
}
|
|
else
|
|
{
|
|
if ( (Options != 0)
|
|
&& (Options[0] != 0))
|
|
{
|
|
*Copy++ = RPC_CONST_CHAR('[');
|
|
*Copy++ = RPC_CONST_CHAR(',');
|
|
Copy = StringCopyEscapeCharacters(Copy, Options);
|
|
*Copy++ = RPC_CONST_CHAR(']');
|
|
}
|
|
}
|
|
|
|
// And do not forget to terminate the string.
|
|
|
|
*Copy = 0;
|
|
|
|
return(StringBinding);
|
|
}
|
|
|
|
|
|
RPC_CHAR PAPI *
|
|
DCE_BINDING::ObjectUuidCompose (
|
|
OUT RPC_STATUS PAPI * Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method returns a string representation of the object UUID
|
|
component of the DCE_BINDING. The string representation is
|
|
suitable for using as the object UUID component of a string binding.
|
|
|
|
Arguments:
|
|
|
|
Status - Returns the status of the operation if there is insufficient
|
|
memory to allocate for the string to be returned.
|
|
|
|
Return Value:
|
|
|
|
The string representation of the object UUID is returned in a freshly
|
|
allocated string.
|
|
|
|
--*/
|
|
{
|
|
RPC_CHAR PAPI * String;
|
|
|
|
if (ObjectUuid.IsNullUuid() != 0)
|
|
return(AllocateEmptyStringPAPI());
|
|
|
|
// The string representation of a uuid is always 36 characters long
|
|
// (and the extra character is for the terminating zero).
|
|
|
|
String = (RPC_CHAR PAPI *) RpcpFarAllocate(37 * sizeof(RPC_CHAR));
|
|
if (String == 0)
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
else
|
|
{
|
|
ObjectUuid.ConvertToString(String);
|
|
String[36] = 0;
|
|
}
|
|
|
|
return(String);
|
|
}
|
|
|
|
|
|
RPC_CHAR PAPI *
|
|
DCE_BINDING::RpcProtocolSequenceCompose (
|
|
OUT RPC_STATUS PAPI * Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method returns a string representation of the RPC protocol sequence
|
|
component of the DCE_BINDING. The string representation is
|
|
suitable for using as the RPC protocol sequence component of a
|
|
string binding.
|
|
|
|
Arguments:
|
|
|
|
Status - Returns the status of the operation if there is insufficient
|
|
memory to allocate for the string to be returned.
|
|
|
|
Return Value:
|
|
|
|
The string representation of the RPC protocol sequence is returned
|
|
in a freshly allocated string.
|
|
|
|
--*/
|
|
{
|
|
RPC_CHAR PAPI * String;
|
|
|
|
if (RpcProtocolSequence == 0)
|
|
return(AllocateEmptyStringPAPI());
|
|
|
|
String = DuplicateStringPAPI(RpcProtocolSequence);
|
|
if (String == 0)
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
return(String);
|
|
}
|
|
|
|
|
|
RPC_CHAR PAPI *
|
|
DCE_BINDING::NetworkAddressCompose (
|
|
OUT RPC_STATUS PAPI * Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method returns a string representation of the network address
|
|
component of the DCE_BINDING. The string representation is
|
|
suitable for using as the network address component of a string binding.
|
|
|
|
Arguments:
|
|
|
|
Status - Returns the status of the operation if there is insufficient
|
|
memory to allocate for the string to be returned.
|
|
|
|
Return Value:
|
|
|
|
The string representation of the network address is returned in a freshly
|
|
allocated string.
|
|
|
|
--*/
|
|
{
|
|
RPC_CHAR PAPI * String;
|
|
|
|
if (NetworkAddress == 0)
|
|
return(AllocateEmptyStringPAPI());
|
|
|
|
String = DuplicateStringPAPI(NetworkAddress);
|
|
if (String == 0)
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
return(String);
|
|
}
|
|
|
|
|
|
RPC_CHAR PAPI *
|
|
DCE_BINDING::EndpointCompose (
|
|
OUT RPC_STATUS PAPI * Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method returns a string representation of the endpoint
|
|
component of the DCE_BINDING. The string representation is
|
|
suitable for using as the endpoint component of a string binding.
|
|
|
|
Arguments:
|
|
|
|
Status - Returns the status of the operation if there is insufficient
|
|
memory to allocate for the string to be returned.
|
|
|
|
Return Value:
|
|
|
|
The string representation of the endpoint is returned in a freshly
|
|
allocated string.
|
|
|
|
--*/
|
|
{
|
|
RPC_CHAR PAPI * String;
|
|
|
|
if (Endpoint == 0)
|
|
return(AllocateEmptyStringPAPI());
|
|
|
|
String = DuplicateStringPAPI(Endpoint);
|
|
if (String == 0)
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
return(String);
|
|
}
|
|
|
|
|
|
RPC_CHAR PAPI *
|
|
DCE_BINDING::OptionsCompose (
|
|
OUT RPC_STATUS PAPI * Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method returns a string representation of the options
|
|
component of the DCE_BINDING. The string representation is
|
|
suitable for using as the options component of a string binding.
|
|
|
|
Arguments:
|
|
|
|
Status - Returns the status of the operation if there is insufficient
|
|
memory to allocate for the string to be returned.
|
|
|
|
Return Value:
|
|
|
|
The string representation of the options is returned in a freshly
|
|
allocated string.
|
|
|
|
--*/
|
|
{
|
|
RPC_CHAR PAPI * String;
|
|
|
|
if (Options == 0)
|
|
return(AllocateEmptyStringPAPI());
|
|
|
|
String = DuplicateStringPAPI(Options);
|
|
if (String == 0)
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
return(String);
|
|
}
|
|
|
|
|
|
BINDING_HANDLE *
|
|
DCE_BINDING::CreateBindingHandle (
|
|
OUT RPC_STATUS *Status
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
We will create a binding handle specific to the rpc protocol sequence
|
|
specified by the DCE_BINDING object. The object uuid will be
|
|
passed on to the created binding handle. Ownership of this
|
|
passes to this routine. If an error occurs, it will be deleted.
|
|
|
|
Arguments:
|
|
The created binding handle will be returned, or zero if an error
|
|
occured.
|
|
|
|
Return Value:
|
|
RPC_S_OK - We had no trouble allocating the binding handle.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory was available to
|
|
complete the operation.
|
|
|
|
RPC_S_INVALID_RPC_PROTSEQ - The rpc protocol sequence is
|
|
syntactically invalid.
|
|
|
|
RPC_S_PROTSEQ_NOT_SUPPORTED - The requested rpc protocol sequence
|
|
is not supported.
|
|
--*/
|
|
{
|
|
TRANS_INFO *ClientTransInfo ;
|
|
BINDING_HANDLE *BindingHandle ;
|
|
|
|
if ( RpcpMemoryCompare(
|
|
RpcProtocolSequence,
|
|
RPC_CONST_STRING("ncalrpc"),
|
|
8 * sizeof(RPC_CHAR)) == 0 )
|
|
{
|
|
BindingHandle = LrpcCreateBindingHandle();
|
|
|
|
if (BindingHandle == 0)
|
|
{
|
|
delete this;
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
else if ( RpcpMemoryCompare(
|
|
RpcProtocolSequence,
|
|
RPC_CONST_STRING("ncadg_"),
|
|
6*sizeof(RPC_CHAR)) == 0)
|
|
{
|
|
BindingHandle = DgCreateBindingHandle();
|
|
if (BindingHandle == 0)
|
|
{
|
|
delete this;
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
*Status = OsfMapRpcProtocolSequence(0,
|
|
RpcProtocolSequence,
|
|
&ClientTransInfo);
|
|
|
|
if (*Status != RPC_S_OK)
|
|
{
|
|
delete BindingHandle;
|
|
delete this;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
else if ( RpcpMemoryCompare(
|
|
RPC_CONST_STRING("ncacn_"),
|
|
RpcProtocolSequence,
|
|
6 * sizeof(RPC_CHAR)) == 0 )
|
|
{
|
|
BindingHandle = OsfCreateBindingHandle();
|
|
if (BindingHandle == 0)
|
|
{
|
|
delete this;
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
*Status = OsfMapRpcProtocolSequence(0,
|
|
RpcProtocolSequence,
|
|
&ClientTransInfo) ;
|
|
if (*Status != RPC_S_OK)
|
|
{
|
|
delete BindingHandle;
|
|
delete this;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete this;
|
|
*Status = RPC_S_INVALID_RPC_PROTSEQ;
|
|
|
|
return 0;
|
|
}
|
|
|
|
BindingHandle->SetObjectUuid(&ObjectUuid);
|
|
*Status = BindingHandle->PrepareBindingHandle(ClientTransInfo, this);
|
|
if (*Status != RPC_S_OK)
|
|
{
|
|
delete BindingHandle;
|
|
delete this;
|
|
|
|
return 0;
|
|
}
|
|
*Status = RPC_S_OK;
|
|
return BindingHandle;
|
|
}
|
|
|
|
|
|
void
|
|
DCE_BINDING::AddEndpoint(
|
|
IN RPC_CHAR *Endpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine can be used to update the endpoint stored in the DCE_BINDING.
|
|
If the DCE_BINDING already has an endpoint it is deleted.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - The new endpoint to store in this DCE_BINDING. Ownership
|
|
passes to this DCE_BINDING.
|
|
|
|
Return Value:
|
|
|
|
n/a
|
|
|
|
--*/
|
|
{
|
|
if (this->Endpoint)
|
|
delete this->Endpoint;
|
|
|
|
this->Endpoint = Endpoint;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DCE_BINDING::ResolveEndpointIfNecessary (
|
|
IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation,
|
|
IN RPC_UUID * ObjectUuid,
|
|
IN OUT void PAPI * PAPI * EpLookupHandle,
|
|
IN BOOL UseEpMapperEp,
|
|
IN unsigned ConnTimeout,
|
|
IN ULONG CallTimeout,
|
|
IN CLIENT_AUTH_INFO *AuthInfo OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine the endpoint if it is not specified.
|
|
The arguments specifies interface information necessary to resolve
|
|
the endpoint, as well as the object uuid.
|
|
|
|
Arguments:
|
|
|
|
RpcInterfaceInformation - Supplies the interface information necessary
|
|
to resolve the endpoint.
|
|
|
|
ObjectUuid - Supplies the object uuid in the binding.
|
|
|
|
EpLookupHandle - Supplies the current value of the endpoint mapper
|
|
lookup handle for a binding, and returns the new value.
|
|
|
|
ConnTimeout - the connection timeout
|
|
|
|
CallTimeout - the call timeout
|
|
|
|
AuthInfo - optional authentication info to be used when resolving the endpoint
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - The endpoint is fully resolved.
|
|
|
|
RPC_S_NO_ENDPOINT_FOUND - The endpoint can not be resolved.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to resolve
|
|
the endpoint.
|
|
|
|
EPT_S_NOT_REGISTERED - There are no more endpoints to be found
|
|
for the specified combination of interface, network address,
|
|
and lookup handle.
|
|
|
|
EPT_S_CANT_PERFORM_OP - The operation failed due to misc. error e.g.
|
|
unable to bind to the EpMapper.
|
|
|
|
--*/
|
|
{
|
|
unsigned int Index;
|
|
RPC_STATUS RpcStatus;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
if ( (Endpoint == 0)
|
|
|| (Endpoint[0] == 0) )
|
|
{
|
|
|
|
// This binding does not have an endpoint, so we must perform
|
|
// binding resolution to obtain an endpoint. First we look
|
|
// in the interface information to see if an endpoint corresponding
|
|
// to the rpc protocol sequence for this binding is there.
|
|
|
|
for (Index = 0;
|
|
Index < RpcInterfaceInformation->RpcProtseqEndpointCount;
|
|
Index++)
|
|
{
|
|
RpcStatus = AnsiToUnicodeString(
|
|
RpcInterfaceInformation->RpcProtseqEndpoint[
|
|
Index].RpcProtocolSequence, &UnicodeString);
|
|
|
|
if (RpcStatus != RPC_S_OK)
|
|
return(RpcStatus);
|
|
|
|
if ( RpcpStringCompare(RpcProtocolSequence,
|
|
UnicodeString.Buffer) == 0 )
|
|
{
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
|
|
if (Endpoint != 0)
|
|
{
|
|
delete Endpoint;
|
|
Endpoint = 0;
|
|
}
|
|
|
|
RpcStatus = AnsiToUnicodeString(
|
|
RpcInterfaceInformation->RpcProtseqEndpoint[
|
|
Index].Endpoint, &UnicodeString);
|
|
|
|
if (RpcStatus != RPC_S_OK)
|
|
return(RpcStatus);
|
|
|
|
Endpoint = DuplicateString(UnicodeString.Buffer);
|
|
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
|
|
if (Endpoint == 0)
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
}
|
|
|
|
//The endpoint has not been supplied so resolve the endpoint.
|
|
|
|
//CLH 2/17/94 If datagram and forward is required (that is
|
|
//RpcEpResolveBinding has not been called), then simply put
|
|
//the endpoint mapper's endpoint into this binding handles endpoint.
|
|
//The endpoint mapper on the destination node will resolve the
|
|
//endpoint and its runtime will forward the pkt.
|
|
|
|
if (Endpoint != 0)
|
|
{
|
|
delete Endpoint;
|
|
Endpoint = 0;
|
|
}
|
|
|
|
//
|
|
// We cannot allow management interfaces to be resolved if they dont contain
|
|
// an object uuid.
|
|
//
|
|
if ( (IsMgmtIfUuid ((UUID PAPI * )
|
|
&RpcInterfaceInformation->InterfaceId.SyntaxGUID))
|
|
&&( (ObjectUuid == 0) ||
|
|
(RpcpMemoryCompare(ObjectUuid, &NullUuid, sizeof(UUID)) == 0) ) )
|
|
{
|
|
return(RPC_S_BINDING_INCOMPLETE);
|
|
}
|
|
|
|
if ( (RpcpMemoryCompare(RpcProtocolSequence,
|
|
RPC_CONST_STRING("ncadg_"), 6*sizeof(RPC_CHAR)) == 0)
|
|
&& (UseEpMapperEp != 0) )
|
|
{
|
|
RpcStatus = EpGetEpmapperEndpoint(
|
|
((RPC_CHAR * PAPI *) &Endpoint),
|
|
RpcProtocolSequence);
|
|
return((RpcStatus == RPC_S_OK) ?
|
|
RPC_P_EPMAPPER_EP : RpcStatus);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Otherwise, we need to contact the endpoint mapper to
|
|
// resolve the endpoint.
|
|
|
|
return (EpResolveEndpoint((UUID PAPI *) ObjectUuid,
|
|
&RpcInterfaceInformation->InterfaceId,
|
|
&RpcInterfaceInformation->TransferSyntax,
|
|
RpcProtocolSequence,
|
|
NetworkAddress,
|
|
Options,
|
|
EpLookupHandle,
|
|
ConnTimeout,
|
|
CallTimeout,
|
|
AuthInfo,
|
|
(RPC_CHAR * PAPI *) &Endpoint));
|
|
}
|
|
}
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
DCE_BINDING::Compare (
|
|
IN DCE_BINDING * DceBinding,
|
|
OUT BOOL *fOnlyEndpointDiffers
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method compares two DCE_BINDING objects for equality.
|
|
|
|
Arguments:
|
|
|
|
DceBinding - Supplies a DCE_BINDING object to compare with this.
|
|
fOnlyEndpointDiffers - this output variable will be set to TRUE
|
|
if the result is non-zero and only the endpoint is different.
|
|
It will be set to FALSE if the result is non-zero, and there
|
|
is more than the endpoint different. If this function returns
|
|
0, the fOnlyEndpointDiffers argument is undefined.
|
|
|
|
Return Value:
|
|
|
|
Zero will be returned if the specified DCE_BINDING object is the
|
|
same as this. Otherwise, non-zero will be returned.
|
|
|
|
--*/
|
|
{
|
|
int Result;
|
|
|
|
Result = CompareWithoutSecurityOptions(DceBinding,
|
|
fOnlyEndpointDiffers);
|
|
if (Result != 0)
|
|
return Result;
|
|
|
|
if (Options != 0)
|
|
{
|
|
if (DceBinding->Options != 0)
|
|
{
|
|
Result = RpcpStringCompare(DceBinding->Options, Options);
|
|
}
|
|
else
|
|
Result = 1;
|
|
}
|
|
else
|
|
{
|
|
if (DceBinding->Options != 0)
|
|
Result = 1;
|
|
// else - Result has already been set from above
|
|
// Result = 0;
|
|
}
|
|
|
|
if (Result)
|
|
{
|
|
// if we didn't bail out after CompareWithoutSecurityOptions,
|
|
// everything but the security options must have been the same
|
|
// If Result is non-zero, only the security optinos have been
|
|
// different. This means that it is not only the endpoint that
|
|
// is different.
|
|
*fOnlyEndpointDiffers = FALSE;
|
|
}
|
|
|
|
return(Result);
|
|
}
|
|
|
|
DCE_BINDING::CompareWithoutSecurityOptions (
|
|
IN DCE_BINDING * DceBinding,
|
|
OUT BOOL *fOnlyEndpointDiffers
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method compares two DCE_BINDING objects for equality without
|
|
comparing the security options.
|
|
|
|
Arguments:
|
|
|
|
DceBinding - Supplies a DCE_BINDING object to compare with this.
|
|
fOnlyEndpointDiffers - this output variable will be set to TRUE
|
|
if the result is non-zero and only the endpoint is different.
|
|
It will be set to FALSE if the result is non-zero, and there
|
|
is more than the endpoint different. If this function returns
|
|
0, the fOnlyEndpointDiffers argument is undefined.
|
|
|
|
Return Value:
|
|
|
|
Zero will be returned if the specified DCE_BINDING object is the
|
|
same as this. Otherwise, non-zero will be returned.
|
|
|
|
--*/
|
|
{
|
|
int Result;
|
|
|
|
*fOnlyEndpointDiffers = FALSE;
|
|
|
|
Result = RpcpMemoryCompare(&(DceBinding->ObjectUuid), &ObjectUuid, sizeof(UUID));
|
|
if (Result != 0)
|
|
return(Result);
|
|
|
|
if (RpcProtocolSequence != 0)
|
|
{
|
|
if (DceBinding->RpcProtocolSequence != 0)
|
|
{
|
|
Result = RpcpStringCompare(DceBinding->RpcProtocolSequence,
|
|
RpcProtocolSequence);
|
|
if (Result != 0)
|
|
return(Result);
|
|
}
|
|
else
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
if (DceBinding->RpcProtocolSequence != 0)
|
|
return(1);
|
|
}
|
|
|
|
if (NetworkAddress != 0)
|
|
{
|
|
if (DceBinding->NetworkAddress != 0)
|
|
{
|
|
Result = RpcpStringCompare(DceBinding->NetworkAddress,
|
|
NetworkAddress);
|
|
if (Result != 0)
|
|
return(Result);
|
|
}
|
|
else
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
if (DceBinding->NetworkAddress != 0)
|
|
return(1);
|
|
}
|
|
|
|
*fOnlyEndpointDiffers = TRUE;
|
|
|
|
if (Endpoint != 0)
|
|
{
|
|
if (DceBinding->Endpoint != 0)
|
|
{
|
|
Result = RpcpStringCompare(DceBinding->Endpoint, Endpoint);
|
|
if (Result != 0)
|
|
return(Result);
|
|
}
|
|
else
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
if (DceBinding->Endpoint != 0)
|
|
return(1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
DCE_BINDING *
|
|
DCE_BINDING::DuplicateDceBinding (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We duplicate this DCE binding in this method.
|
|
|
|
Return Value:
|
|
|
|
A duplicate DCE_BINDING to this DCE_BINDING will be returned, if
|
|
everthing works correctly. Otherwise, zero will be returned
|
|
indicating an out of memory error.
|
|
|
|
--*/
|
|
{
|
|
DCE_BINDING * DceBinding;
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
RPC_CHAR ObjectUuidString[37];
|
|
|
|
ObjectUuid.ConvertToString(ObjectUuidString);
|
|
ObjectUuidString[36] = 0;
|
|
|
|
DceBinding = new DCE_BINDING(ObjectUuidString,RpcProtocolSequence,
|
|
NetworkAddress,Endpoint,Options,&Status);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
ASSERT(Status == RPC_S_OUT_OF_MEMORY);
|
|
delete DceBinding;
|
|
return(0);
|
|
}
|
|
|
|
return(DceBinding);
|
|
}
|
|
|
|
|
|
void
|
|
DCE_BINDING::MakePartiallyBound (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to make the binding into a partially bound one by setting the
|
|
endpoint to zero. This is really easy to do.
|
|
|
|
--*/
|
|
{
|
|
if (Endpoint != 0)
|
|
{
|
|
delete Endpoint;
|
|
Endpoint = 0;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
DCE_BINDING::MaybeMakePartiallyBound (
|
|
IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation,
|
|
IN RPC_UUID * MyObjectUuid
|
|
)
|
|
/*++
|
|
Function Name:MaybeMakePartiallyBound
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
If the interface can uniquely identify an RPC server on a machine, the
|
|
binding is made partially bound. Otherwise, it is not.
|
|
|
|
Returns:
|
|
|
|
|
|
--*/
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
--*/
|
|
{
|
|
if ((IsMgmtIfUuid ((UUID PAPI * )
|
|
&RpcInterfaceInformation->InterfaceId.SyntaxGUID))
|
|
&&((MyObjectUuid == 0) ||
|
|
(RpcpMemoryCompare(MyObjectUuid, &NullUuid, sizeof(UUID)) == 0)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
MakePartiallyBound();
|
|
return TRUE;
|
|
}
|
|
|
|
RPC_STATUS
|
|
IsRpcProtocolSequenceSupported (
|
|
IN RPC_CHAR PAPI * RpcProtocolSequence
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the specified rpc protocol sequence is
|
|
supported. It will optionally return the parts of the rpc protocol
|
|
sequence (rpc protocol specifier, and address + interface specifiers).
|
|
|
|
Arguments:
|
|
|
|
RpcProtocolSequence - Supplies an rpc protocol sequence to check.
|
|
|
|
RpcProtocolPart - Optionally returns the rpc protocol part of the
|
|
rpc protocol sequence.
|
|
|
|
AddressAndInterfacePart - Optionally returns the address and interface
|
|
parts of the rpc protocol sequence.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - The specified rpc protocol sequence is supported.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to check
|
|
the rpc protocol sequence.
|
|
|
|
RPC_S_PROTSEQ_NOT_SUPPORTED - The specified rpc protocol sequence is not
|
|
supported (but it appears to be valid).
|
|
|
|
RPC_S_INVALID_RPC_PROTSEQ - The specified rpc protocol sequence is
|
|
syntactically invalid.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
TRANS_INFO *ClientTransInfo ;
|
|
size_t ProtSeqLength;
|
|
|
|
ProtSeqLength = RpcpStringLength(RpcProtocolSequence);
|
|
if ( (ProtSeqLength >= 7)
|
|
&&
|
|
(RpcpMemoryCompare(RpcProtocolSequence, RPC_CONST_STRING("ncalrpc"),
|
|
8 * sizeof(RPC_CHAR)) == 0) )
|
|
{
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
else if ( (ProtSeqLength >= 6)
|
|
&& ((RpcpMemoryCompare(RPC_CONST_STRING("ncacn_"),
|
|
RpcProtocolSequence, 6 * sizeof(RPC_CHAR)) == 0 )
|
|
|| ( RpcpMemoryCompare(RPC_CONST_STRING("ncadg_"), RpcProtocolSequence,
|
|
6 * sizeof(RPC_CHAR)) == 0 )) )
|
|
|
|
{
|
|
RPC_PROTSEQ_VECTOR *ProtseqVector;
|
|
unsigned int i;
|
|
|
|
Status = RpcNetworkInqProtseqs(&ProtseqVector);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Status = RPC_S_PROTSEQ_NOT_SUPPORTED;
|
|
|
|
for (i = 0; i < ProtseqVector->Count; i++)
|
|
{
|
|
if (RpcpStringCompare(RpcProtocolSequence, ProtseqVector->Protseq[i]) == 0)
|
|
{
|
|
Status = RPC_S_OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RpcProtseqVectorFree(&ProtseqVector);
|
|
|
|
return(Status);
|
|
}
|
|
else if ( (ProtSeqLength >= 6)
|
|
&&
|
|
(RpcpMemoryCompare(RpcProtocolSequence, RPC_CONST_STRING("mswmsg"),
|
|
7 * sizeof(RPC_CHAR)) == 0) )
|
|
{
|
|
return(RPC_S_PROTSEQ_NOT_SUPPORTED);
|
|
}
|
|
|
|
return(RPC_S_INVALID_RPC_PROTSEQ);
|
|
}
|
|
|
|
|
|
LOADABLE_TRANSPORT::LOADABLE_TRANSPORT (
|
|
IN RPC_TRANSPORT_INTERFACE pTransportInterface,
|
|
IN RPC_CHAR * DllName,
|
|
IN RPC_CHAR PAPI * ProtocolSequence,
|
|
IN DLL *LoadableTransportDll,
|
|
IN FuncGetHandleForThread GetHandleForThread,
|
|
IN FuncReleaseHandleForThread ReleaseHandleForThread,
|
|
OUT RPC_STATUS *Status,
|
|
OUT TRANS_INFO * PAPI *TransInfo
|
|
) : nThreadsAtCompletionPort(0),
|
|
ThreadsDoingLongWait(0)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
To construct the object, all we have got to do is to copy the
|
|
arguments into the object.
|
|
|
|
Arguments:
|
|
|
|
DllName - Supplies the name of the dll from which this transport
|
|
interface was loaded.
|
|
|
|
--*/
|
|
{
|
|
|
|
RpcpStringCopy(this->DllName, DllName) ;
|
|
LoadedDll = LoadableTransportDll;
|
|
|
|
*TransInfo = new TRANS_INFO(pTransportInterface,
|
|
ProtocolSequence,
|
|
this) ;
|
|
if (*TransInfo == 0)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
return ;
|
|
}
|
|
|
|
if (ProtseqDict.Insert(*TransInfo) == -1)
|
|
{
|
|
*Status = RPC_S_OUT_OF_MEMORY;
|
|
return ;
|
|
}
|
|
|
|
ThreadsStarted = 0;
|
|
nActivityValue = 0;
|
|
nOptimalNumberOfThreads = gNumberOfProcessors + 1;
|
|
ProcessCallsFunc = pTransportInterface->ProcessCalls;
|
|
|
|
this->GetHandleForThread = GetHandleForThread;
|
|
this->ReleaseHandleForThread = ReleaseHandleForThread;
|
|
|
|
#ifndef NO_PLUG_AND_PLAY
|
|
PnpListen = pTransportInterface->PnpListen;
|
|
#endif
|
|
|
|
*Status = RPC_S_OK;
|
|
NumThreads = 0;
|
|
}
|
|
|
|
|
|
TRANS_INFO *
|
|
LOADABLE_TRANSPORT::MapProtocol (
|
|
IN RPC_CHAR * DllName,
|
|
IN RPC_CHAR PAPI * ProtocolSequence
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is used to search the dictionary. It compares a
|
|
LOADABLE_TRANSPORT with a transport interface to see if
|
|
they match.
|
|
|
|
Arguments:
|
|
|
|
DllName - Supplies the name of the dll from which this loadable
|
|
transport interface was loaded.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
TRANS_INFO *Protseq ;
|
|
TRANSPORT_LOAD TransportLoad;
|
|
RPC_TRANSPORT_INTERFACE pTransport;
|
|
DictionaryCursor cursor;
|
|
|
|
if (RpcpStringCompare(DllName, this->DllName) != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ProtseqDict.Reset(cursor) ;
|
|
while ((Protseq = ProtseqDict.Next(cursor)) != 0)
|
|
{
|
|
if (Protseq->MatchProtseq(ProtocolSequence))
|
|
{
|
|
return Protseq ;
|
|
}
|
|
}
|
|
|
|
if (GetTransportEntryPoints(LoadedDll, &TransportLoad,
|
|
&GetHandleForThread,
|
|
&ReleaseHandleForThread) == 0)
|
|
return 0;
|
|
|
|
pTransport = (*TransportLoad) (ProtocolSequence);
|
|
|
|
if (pTransport == 0)
|
|
{
|
|
return 0 ;
|
|
}
|
|
|
|
Protseq = new TRANS_INFO(
|
|
pTransport,
|
|
ProtocolSequence,
|
|
this) ;
|
|
if (Protseq == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (ProtseqDict.Insert(Protseq) == -1)
|
|
{
|
|
delete Protseq ;
|
|
return 0;
|
|
}
|
|
|
|
return Protseq ;
|
|
}
|
|
|
|
|
|
TRANS_INFO *
|
|
LOADABLE_TRANSPORT::MatchId (
|
|
IN unsigned short Id
|
|
)
|
|
{
|
|
TRANS_INFO *Protseq ;
|
|
DictionaryCursor cursor;
|
|
|
|
ProtseqDict.Reset(cursor) ;
|
|
while ((Protseq = ProtseqDict.Next(cursor)) != 0)
|
|
{
|
|
if (Protseq->MatchId(Id))
|
|
{
|
|
return Protseq ;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LOADABLE_TRANSPORT_DICT * LoadedLoadableTransports;
|
|
|
|
BOOL GetTransportEntryPoints(IN DLL *LoadableTransportDll, OUT TRANSPORT_LOAD *TransportLoad,
|
|
OUT FuncGetHandleForThread *GetHandleForThread,
|
|
OUT FuncReleaseHandleForThread *ReleaseHandleForThread
|
|
)
|
|
/*++
|
|
Function Name:GetTransportEntryPoints
|
|
|
|
Parameters: IN LoadableTransportDll - the DLL on which to obtain the entry points
|
|
OUT TRANSPORT_LOAD *TransportLoad - the TransportLoad function for this DLL. 0 iff the
|
|
function fails
|
|
OUT FuncGetHandleForThread *GetHandleForThread - the GetHandleForThread function for this DLL
|
|
OUT FuncReleaseHandleForThread *ReleaseHandleForThread - the ReleaseHandleForThread
|
|
function for this DLL
|
|
|
|
Description: Gets the entry points from this transport DLL
|
|
|
|
Returns: TRUE if successful, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
*TransportLoad = (TRANSPORT_LOAD) LoadableTransportDll->GetEntryPoint("TransportLoad");
|
|
|
|
*GetHandleForThread =
|
|
(FuncGetHandleForThread) LoadableTransportDll->GetEntryPoint("GetCompletionPortHandleForThread");
|
|
*ReleaseHandleForThread =
|
|
(FuncReleaseHandleForThread) LoadableTransportDll->GetEntryPoint("ReleaseCompletionPortHandleForThread");
|
|
|
|
if ((*TransportLoad == 0)
|
|
|| (*GetHandleForThread == 0)
|
|
|| (*ReleaseHandleForThread == 0)
|
|
)
|
|
{
|
|
*TransportLoad = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
RPC_STATUS
|
|
LoadableTransportInfo (
|
|
IN RPC_CHAR * DllName,
|
|
IN RPC_CHAR PAPI * RpcProtocolSequence,
|
|
OUT TRANS_INFO * PAPI *pTransInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to return the client information for the loadable transport
|
|
specified by the argument, DllName. This may mean that we need
|
|
to load the transport support dll.
|
|
|
|
Argument:
|
|
|
|
DllName - Supplies the name of the dll which we need to try and
|
|
load to get the appropriate loadable transport interface.
|
|
|
|
RpcProtocolSequence - Supplies the protocol sequence for which
|
|
we are trying to find the appropriate loadable transport
|
|
interface.
|
|
|
|
Status - Returns the specific error code for failure to find/load
|
|
a loadable transport.
|
|
|
|
Return Value:
|
|
|
|
0 - If the specified transport interface can not be loaded for any
|
|
reason: does not exist, out of memory, version mismatch, etc.
|
|
|
|
Otherwise, a pointer to the client information for the requested
|
|
transport interface (loadable transport support) will be returned.
|
|
|
|
--*/
|
|
{
|
|
RPC_TRANSPORT_INTERFACE pTransportInterface;
|
|
LOADABLE_TRANSPORT * LoadableTransport;
|
|
TRANSPORT_LOAD TransportLoad;
|
|
FuncGetHandleForThread GetHandleForThread;
|
|
FuncReleaseHandleForThread ReleaseHandleForThread;
|
|
DLL * LoadableTransportDll;
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
DictionaryCursor cursor;
|
|
|
|
ASSERT(Status == 0);
|
|
|
|
// we can support only up to 4 loadable transports (though today we
|
|
// use only 1 and we don't allow third parties to write their own).
|
|
// This allows us to avoid taking a mutex when browsing the
|
|
// LoadedLoadableTransports dictionary, as we never remove
|
|
// transport from it
|
|
ASSERT(LoadedLoadableTransports->Size() <= INITIALDICTSLOTS);
|
|
|
|
//
|
|
// To begin with, check to see if the transport is already loaded.
|
|
// If so, all we have got to do is to return a pointer to it.
|
|
//
|
|
RequestGlobalMutex();
|
|
LoadedLoadableTransports->Reset(cursor);
|
|
while ((LoadableTransport
|
|
= LoadedLoadableTransports->Next(cursor)) != 0)
|
|
{
|
|
*pTransInfo = LoadableTransport->MapProtocol (
|
|
DllName,
|
|
RpcProtocolSequence) ;
|
|
if (*pTransInfo != 0)
|
|
{
|
|
ClearGlobalMutex();
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we reach here, that means that we need to try and load the
|
|
// specified loadable transport DLL.
|
|
//
|
|
LoadableTransportDll = new DLL(DllName, &Status);
|
|
|
|
if (LoadableTransportDll == 0)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
ClearGlobalMutex();
|
|
delete LoadableTransportDll;
|
|
|
|
VALIDATE(Status)
|
|
{
|
|
RPC_S_OUT_OF_MEMORY,
|
|
RPC_S_INVALID_ARG
|
|
} END_VALIDATE;
|
|
|
|
if ( Status != RPC_S_OUT_OF_MEMORY )
|
|
{
|
|
ASSERT( Status == RPC_S_INVALID_ARG );
|
|
Status = RPC_S_PROTSEQ_NOT_SUPPORTED;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (GetTransportEntryPoints(LoadableTransportDll, &TransportLoad, &GetHandleForThread,
|
|
&ReleaseHandleForThread) == 0)
|
|
{
|
|
ClearGlobalMutex();
|
|
delete LoadableTransportDll;
|
|
|
|
return RPC_S_PROTSEQ_NOT_SUPPORTED;
|
|
}
|
|
|
|
pTransportInterface = (*TransportLoad)(RpcProtocolSequence);
|
|
|
|
if ( pTransportInterface == 0 )
|
|
{
|
|
ClearGlobalMutex();
|
|
delete LoadableTransportDll;
|
|
|
|
return RPC_S_PROTSEQ_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ( pTransportInterface->TransInterfaceVersion
|
|
> RPC_TRANSPORT_INTERFACE_VERSION )
|
|
{
|
|
ClearGlobalMutex();
|
|
delete LoadableTransportDll;
|
|
|
|
return RPC_S_PROTSEQ_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// When we reach here, we have successfully loaded and initialized
|
|
// the loadable transport DLL. Now we need to create the client
|
|
// loadable transport and stick it in the dictionary.
|
|
//
|
|
LoadableTransport = new LOADABLE_TRANSPORT(
|
|
pTransportInterface,
|
|
DllName,
|
|
RpcProtocolSequence,
|
|
LoadableTransportDll,
|
|
GetHandleForThread,
|
|
ReleaseHandleForThread,
|
|
&Status,
|
|
pTransInfo);
|
|
|
|
if ( LoadableTransport == 0 )
|
|
{
|
|
ClearGlobalMutex();
|
|
delete LoadableTransportDll;
|
|
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if ( Status != RPC_S_OK
|
|
|| LoadedLoadableTransports->Insert(LoadableTransport) == -1 )
|
|
{
|
|
ClearGlobalMutex();
|
|
delete LoadableTransportDll;
|
|
delete LoadableTransport;
|
|
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
ClearGlobalMutex();
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
TRANS_INFO PAPI *
|
|
GetLoadedClientTransportInfoFromId(
|
|
IN unsigned short Id
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to return the client information for the loadable transport
|
|
specified by the argument, TransportId. We look into the DICT and see
|
|
if the transport is loaded- it it isnt, tough- we will return an error.
|
|
-this is because we need Protseq and dllname to load a transport and
|
|
all we have is a transport ID.
|
|
|
|
Argument:
|
|
|
|
Id - Transport Id. This is actually the opcode used to encode endpoint
|
|
in a DCE tower. For a listing see DCE spec Chapter 11&12.
|
|
|
|
Status - Returns the error/success code.
|
|
|
|
Return Value:
|
|
|
|
0 - If the specified transport interface can not be loaded for any
|
|
reason: does not exist, out of memory.
|
|
|
|
Otherwise, a pointer to the client information for the requested
|
|
transport interface (loadable transport support) will be returned.
|
|
|
|
--*/
|
|
{
|
|
TRANS_INFO PAPI *TransInfo ;
|
|
LOADABLE_TRANSPORT * LoadableTransport;
|
|
DictionaryCursor cursor;
|
|
|
|
// To begin with, check to see if the transport is already loaded.
|
|
// If so, all we have got to do is to return a pointer to it.
|
|
|
|
RequestGlobalMutex();
|
|
LoadedLoadableTransports->Reset(cursor);
|
|
while ((LoadableTransport
|
|
= LoadedLoadableTransports->Next(cursor)) != 0)
|
|
{
|
|
TransInfo = LoadableTransport->MatchId(Id);
|
|
if (TransInfo != 0)
|
|
{
|
|
ClearGlobalMutex();
|
|
|
|
return(TransInfo);
|
|
}
|
|
}
|
|
|
|
// If we reached here, that means that we are in trouble
|
|
// We assumed that all relevant loadable transports will be
|
|
// loaded for us.... but we are wrong!
|
|
|
|
ClearGlobalMutex();
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
InitializeLoadableTransportClient (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will be called at DLL load time. We do all necessary
|
|
initializations here for this file.
|
|
|
|
Return Value:
|
|
|
|
Zero will be returned if initialization completes successfully;
|
|
otherwise, non-zero will be returned.
|
|
|
|
--*/
|
|
{
|
|
|
|
LoadedLoadableTransports = new LOADABLE_TRANSPORT_DICT;
|
|
if (LoadedLoadableTransports == 0)
|
|
return(1);
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
inline
|
|
BOOL
|
|
ProcessIOEventsWrapper(
|
|
IN LOADABLE_TRANSPORT PAPI *Transport
|
|
)
|
|
/*++
|
|
Function Name:ProcessIOEventsWrapper
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
|
|
Returns:
|
|
TRUE - thread should exit.
|
|
|
|
--*/
|
|
{
|
|
Transport->ProcessIOEvents();
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
RPC_STATUS
|
|
LOADABLE_TRANSPORT::StartServerIfNecessary (
|
|
)
|
|
/*++
|
|
Function Name:StartServerIfNecessary
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
RPC_STATUS Status ;
|
|
int MinimumThreads = GlobalRpcServer->MinimumCallThreads ;
|
|
|
|
if ( ThreadsStarted != 0
|
|
|| InterlockedIncrement(&ThreadsStarted) != 1)
|
|
{
|
|
return RPC_S_OK ;
|
|
}
|
|
|
|
Status = InitializeServerSideCellHeapIfNecessary();
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
ThreadsStarted = 0;
|
|
return Status;
|
|
}
|
|
|
|
for (i = 0; i < MinimumThreads; i++)
|
|
{
|
|
InterlockedIncrement(&NumThreads);
|
|
Status = GlobalRpcServer->CreateThread (
|
|
(THREAD_PROC) &ProcessIOEventsWrapper, this) ;
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
NumThreads = 0;
|
|
ThreadsStarted = 0;
|
|
return Status ;
|
|
}
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LOADABLE_TRANSPORT::CreateThread (void)
|
|
/*++
|
|
Function Name:CreateThread
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
if (NumThreads < 1)
|
|
{
|
|
Status = GlobalRpcServer->CreateThread (
|
|
(THREAD_PROC) &ProcessIOEventsWrapper, this) ;
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
InterlockedIncrement(&NumThreads);
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
inline
|
|
RPC_STATUS
|
|
LOADABLE_TRANSPORT::ProcessCalls (
|
|
IN INT Timeout,
|
|
OUT RPC_TRANSPORT_EVENT *pEvent,
|
|
OUT RPC_STATUS *pEventStatus,
|
|
OUT PVOID *ppEventContext,
|
|
OUT UINT *pBufferLength,
|
|
OUT BUFFER *pBuffer,
|
|
OUT PVOID *ppSourceContext)
|
|
/*++
|
|
Function Name:ProcessCalls
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
return (*ProcessCallsFunc) (
|
|
Timeout,
|
|
pEvent,
|
|
pEventStatus,
|
|
ppEventContext,
|
|
pBufferLength,
|
|
pBuffer,
|
|
ppSourceContext) ;
|
|
}
|
|
|
|
const ULONG MAX_THREAD_TIMEOUT = 660*1000; // 11 minutes
|
|
|
|
void ProcessNewAddressEvent(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus,
|
|
IN PVOID pEventContext,
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer,
|
|
IN PVOID pSourceContext)
|
|
{
|
|
LISTEN_FOR_PNP_NOTIFICATIONS PnpFunc;
|
|
|
|
RpcpPurgeEEInfo();
|
|
|
|
GlobalRpcServer->CreateOrUpdateAddresses();
|
|
|
|
#ifndef NO_PLUG_AND_PLAY
|
|
PnpFunc = pLoadableTransport->PnpListen;
|
|
(*PnpFunc)();
|
|
#endif
|
|
}
|
|
|
|
void ProcessConnectionServerReceivedEvent(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus, // operation status
|
|
IN PVOID pEventContext, // trans conenction
|
|
IN UINT BufferLength, // buffer length
|
|
IN BUFFER Buffer, // buffer
|
|
IN PVOID pSourceContext)
|
|
{
|
|
OSF_SCONNECTION *SConnection = InqTransSConnection(pEventContext);
|
|
|
|
ASSERT(SConnection->InvalidHandle(OSF_SCONNECTION_TYPE) == 0);
|
|
|
|
RpcpPurgeEEInfo();
|
|
|
|
SConnection->ProcessReceiveComplete(EventStatus,
|
|
Buffer,
|
|
BufferLength);
|
|
}
|
|
|
|
void ProcessConnectionServerSendEvent(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus,
|
|
IN PVOID pEventContext,
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer,
|
|
IN PVOID pSourceContext // send context
|
|
)
|
|
{
|
|
OSF_SCALL *SCall = InqTransSCall(pSourceContext);
|
|
|
|
ASSERT(SCall->InvalidHandle(OSF_SCALL_TYPE) == 0);
|
|
|
|
ASSERT(EventStatus != RPC_S_OK
|
|
|| ((rpcconn_common *) Buffer)->frag_length == BufferLength);
|
|
|
|
RpcpPurgeEEInfo();
|
|
|
|
SCall->ProcessSendComplete(EventStatus, Buffer);
|
|
}
|
|
|
|
void ProcessConnectionClientSendEvent(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus, // Operation status
|
|
IN PVOID pEventContext,
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer, // Buffer
|
|
IN PVOID pSourceContext // send context
|
|
)
|
|
{
|
|
REFERENCED_OBJECT *pObj;
|
|
|
|
pObj = (REFERENCED_OBJECT *) *((PVOID *)
|
|
((char *) pSourceContext - sizeof(void *)));
|
|
ASSERT(pObj->InvalidHandle(OSF_CCALL_TYPE | OSF_CCONNECTION_TYPE) == 0);
|
|
|
|
RpcpPurgeEEInfo();
|
|
|
|
pObj->ProcessSendComplete(EventStatus, Buffer);
|
|
}
|
|
|
|
void ProcessConnectionClientReceiveEvent(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus, // operation status
|
|
IN PVOID pEventContext, // trans connection
|
|
IN UINT BufferLength, // buffer length
|
|
IN BUFFER Buffer, // buffer
|
|
IN PVOID pSourceContext)
|
|
{
|
|
OSF_CCONNECTION *CConnection = InqTransCConnection(pEventContext);
|
|
|
|
ASSERT(CConnection->InvalidHandle(OSF_CCONNECTION_TYPE) == 0);
|
|
ASSERT(CConnection->IsExclusive() == FALSE);
|
|
|
|
RpcpPurgeEEInfo();
|
|
|
|
CConnection->ProcessReceiveComplete(
|
|
EventStatus,
|
|
Buffer,
|
|
BufferLength);
|
|
CConnection->RemoveReference();
|
|
}
|
|
|
|
void ProcessDatagramServerReceiveEvent(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus,
|
|
IN PVOID pEventContext,
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer,
|
|
IN PVOID pSourceContext)
|
|
{
|
|
ProcessDgServerPacket( EventStatus,
|
|
pEventContext,
|
|
Buffer,
|
|
BufferLength,
|
|
(DatagramTransportPair *)pSourceContext );
|
|
}
|
|
|
|
void ProcessDatagramClientReceiveEvent(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus,
|
|
IN PVOID pEventContext,
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer,
|
|
IN PVOID pSourceContext)
|
|
{
|
|
ProcessDgClientPacket( EventStatus,
|
|
pEventContext,
|
|
Buffer,
|
|
BufferLength,
|
|
(DatagramTransportPair *)pSourceContext );
|
|
}
|
|
|
|
void ProcessRuntimePostedEvent(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus,
|
|
IN PVOID pEventContext,
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer,
|
|
IN PVOID pSourceContext)
|
|
{
|
|
BOOL IsServer;
|
|
BOOL SendToRuntime;
|
|
RPC_STATUS RpcStatus;
|
|
|
|
RpcpPurgeEEInfo();
|
|
|
|
switch (BufferLength)
|
|
{
|
|
case CO_EVENT_BIND_TO_SERVER:
|
|
|
|
extern void OsfBindToServer( PVOID Context );
|
|
|
|
OsfBindToServer( pEventContext );
|
|
break;
|
|
|
|
case DG_EVENT_CALLBACK_COMPLETE:
|
|
|
|
class DG_SCONNECTION;
|
|
extern void ConvCallCompletedWrapper( PVOID Connection );
|
|
|
|
ConvCallCompletedWrapper(pEventContext);
|
|
break;
|
|
|
|
case CO_EVENT_TICKLE_THREAD:
|
|
#if defined (RPC_GC_AUDIT)
|
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Tickled\n",
|
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
|
|
#endif
|
|
// no-op
|
|
break;
|
|
|
|
case IN_PROXY_IIS_DIRECT_RECV:
|
|
HTTP2IISDirectReceive(pEventContext);
|
|
break;
|
|
|
|
case HTTP2_DIRECT_RECEIVE:
|
|
|
|
// For now we will not inject corruption prior to HTTP2DirectReceive.
|
|
// We will need to query ((HTTP2EndpointReceiver *)pEventContext)->IsServer
|
|
// to tell which kind of buffer this really is before injecting corruption.
|
|
|
|
EventStatus = HTTP2DirectReceive(pEventContext,
|
|
(BYTE **)&Buffer,
|
|
(ULONG *)&BufferLength,
|
|
&pEventContext,
|
|
&IsServer
|
|
);
|
|
|
|
if (EventStatus != RPC_P_PACKET_CONSUMED)
|
|
{
|
|
if (IsServer == FALSE)
|
|
{
|
|
ProcessConnectionClientReceiveEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
else
|
|
{
|
|
ProcessConnectionServerReceivedEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HTTP2_WINHTTP_DIRECT_RECV:
|
|
|
|
EventStatus = HTTP2WinHttpDirectReceive(pEventContext,
|
|
(BYTE **)&Buffer,
|
|
(ULONG *)&BufferLength,
|
|
&pEventContext
|
|
);
|
|
|
|
if (EventStatus != RPC_P_PACKET_CONSUMED)
|
|
{
|
|
ProcessConnectionClientReceiveEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
break;
|
|
|
|
case HTTP2_WINHTTP_DIRECT_SEND:
|
|
EventStatus = HTTP2WinHttpDirectSend(pEventContext,
|
|
(BYTE **)&Buffer,
|
|
&pSourceContext
|
|
);
|
|
|
|
if (EventStatus != RPC_P_PACKET_CONSUMED)
|
|
{
|
|
ProcessConnectionClientSendEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
break;
|
|
|
|
case PLUG_CHANNEL_DIRECT_SEND:
|
|
RpcStatus = HTTP2PlugChannelDirectSend(pEventContext);
|
|
ASSERT(RpcStatus == RPC_S_OK);
|
|
break;
|
|
|
|
case CHANNEL_DATA_ORIGINATOR_DIRECT_SEND:
|
|
EventStatus = HTTP2ChannelDataOriginatorDirectSend(pEventContext,
|
|
&IsServer,
|
|
&pSourceContext,
|
|
&Buffer,
|
|
&BufferLength
|
|
);
|
|
|
|
if (EventStatus != RPC_P_PACKET_CONSUMED)
|
|
{
|
|
if (IsServer == FALSE)
|
|
{
|
|
ProcessConnectionClientSendEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
else
|
|
{
|
|
ProcessConnectionServerSendEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HTTP2_FLOW_CONTROL_DIRECT_SEND:
|
|
EventStatus = HTTP2FlowControlChannelDirectSend(pEventContext,
|
|
&IsServer,
|
|
&SendToRuntime,
|
|
&pSourceContext,
|
|
&Buffer,
|
|
&BufferLength
|
|
);
|
|
|
|
if ((EventStatus != RPC_P_PACKET_CONSUMED) && (SendToRuntime != FALSE))
|
|
{
|
|
if (IsServer == FALSE)
|
|
{
|
|
ProcessConnectionClientSendEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
else
|
|
{
|
|
ProcessConnectionServerSendEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HTTP2_RESCHEDULE_TIMER:
|
|
HTTP2TimerReschedule(pEventContext);
|
|
break;
|
|
|
|
case HTTP2_ABORT_CONNECTION:
|
|
HTTP2AbortConnection(pEventContext);
|
|
break;
|
|
|
|
case HTTP2_RECYCLE_CHANNEL:
|
|
HTTP2RecycleChannel(pEventContext);
|
|
break;
|
|
|
|
default:
|
|
ASSERT( 0 );
|
|
}
|
|
}
|
|
|
|
void ProcessInvalidIOEvent(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus,
|
|
IN PVOID pEventContext,
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer,
|
|
IN PVOID pSourceContext)
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
|
|
void ProcessComplexTSend(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus, // status of the operation
|
|
IN PVOID pEventContext,
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer,
|
|
IN PVOID pSourceContext // send context
|
|
)
|
|
{
|
|
EventStatus = HTTP2ProcessComplexTSend(pSourceContext,
|
|
EventStatus,
|
|
&Buffer
|
|
);
|
|
|
|
if (EventStatus != RPC_P_PACKET_CONSUMED)
|
|
{
|
|
if ((Event & TYPE_MASK) == CLIENT)
|
|
{
|
|
ProcessConnectionClientSendEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ProcessConnectionServerSendEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessComplexTReceive(LOADABLE_TRANSPORT *pLoadableTransport,
|
|
IN RPC_TRANSPORT_EVENT Event,
|
|
IN RPC_STATUS EventStatus, // status of the operation
|
|
IN PVOID pEventContext, // connection
|
|
IN UINT BufferLength,
|
|
IN BUFFER Buffer,
|
|
IN PVOID pSourceContext // bytes received
|
|
)
|
|
{
|
|
ULONG Bytes = PtrToUlong(pSourceContext);
|
|
|
|
EventStatus = HTTP2ProcessComplexTReceive(&pEventContext,
|
|
EventStatus,
|
|
Bytes,
|
|
&Buffer,
|
|
&BufferLength
|
|
);
|
|
|
|
if ((EventStatus != RPC_P_PACKET_CONSUMED)
|
|
&& (EventStatus != RPC_P_PARTIAL_RECEIVE))
|
|
{
|
|
if ((Event & TYPE_MASK) == CLIENT)
|
|
{
|
|
ProcessConnectionClientReceiveEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
else
|
|
{
|
|
ProcessConnectionServerReceivedEvent(pLoadableTransport,
|
|
Event,
|
|
EventStatus,
|
|
pEventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
// note that this array must have correspondence to the constants in rpctrans.hxx
|
|
ProcessIOEventFunc *IOEventDispatchTable[LastRuntimeConstant + 1] =
|
|
{
|
|
// 0 is CONNECTION | CLIENT | SEND
|
|
ProcessConnectionClientSendEvent,
|
|
// 1 is DATAGRAM | CLIENT | SEND
|
|
ProcessInvalidIOEvent,
|
|
// 2 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 3 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 4 is CONNECTION | SERVER | SEND
|
|
ProcessConnectionServerSendEvent,
|
|
// 5 is DATAGRAM | SERVER | SEND
|
|
ProcessInvalidIOEvent,
|
|
// 6 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 7 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 8 is CONNECTION | CLIENT | RECEIVE
|
|
ProcessConnectionClientReceiveEvent,
|
|
// 9 is DATAGRAM | CLIENT | RECEIVE
|
|
ProcessDatagramClientReceiveEvent,
|
|
// 10 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 11 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 12 is CONNECTION | SERVER | RECEIVE
|
|
ProcessConnectionServerReceivedEvent,
|
|
// 13 is DATAGRAM | SERVER | RECEIVE
|
|
ProcessDatagramServerReceiveEvent,
|
|
// 14 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 15 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 16 is COMPLEX_T | CONNECTION | SEND | CLIENT
|
|
ProcessComplexTSend,
|
|
// 17 is RuntimePosted
|
|
ProcessRuntimePostedEvent,
|
|
// 18 is NewAddress
|
|
ProcessNewAddressEvent,
|
|
// 19 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 20 is COMPLEX_T | CONNECTION | SEND | SERVER
|
|
ProcessComplexTSend,
|
|
// 21 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 22 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 23 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 24 is COMPLEX_T | CONNECTION | RECEIVE | CLIENT
|
|
ProcessComplexTReceive,
|
|
// 25 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 26 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 27 is invalid
|
|
ProcessInvalidIOEvent,
|
|
// 28 is COMPLEX_T | CONNECTION | RECEIVE | SERVER
|
|
ProcessComplexTReceive
|
|
};
|
|
|
|
const ULONG UndefinedLocalThreadTimeout = 0;
|
|
|
|
void LOADABLE_TRANSPORT::ProcessIOEvents (
|
|
)
|
|
/*++
|
|
Function Name:ProcessIOEvents
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
|
|
Returns:
|
|
TRUE - the thread should not be cached
|
|
FALSE - the thread should be cached
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status ;
|
|
RPC_TRANSPORT_EVENT Event ;
|
|
RPC_STATUS EventStatus ;
|
|
PVOID EventContext ;
|
|
BUFFER Buffer ;
|
|
UINT BufferLength ;
|
|
PVOID pSourceContext = 0;
|
|
int Timeout = gThreadTimeout;
|
|
unsigned int nLocalActivityValue = 0;
|
|
int nOldActivityValue = nActivityValue;
|
|
HANDLE hCompletionPortHandleForThread = GetHandleForThread();
|
|
THREAD *CurrentThread;
|
|
DebugThreadInfo *ThreadDebugCell;
|
|
BOOL fThreadIsDoingLongWait = FALSE;
|
|
ULONG LocalNumThreads;
|
|
ULONG LocalThreadsDoingLongWait;
|
|
long LocalMaxThreadTimeout;
|
|
#if defined (RPC_GC_AUDIT)
|
|
long Temp;
|
|
#endif
|
|
long ThreadActivationDelay;
|
|
|
|
if (IocThreadStarted == 0)
|
|
{
|
|
IocThreadStarted = 1;
|
|
}
|
|
|
|
nThreadsAtCompletionPort.Increment();
|
|
|
|
if ((gProrateStart > 0) && ((DWORD)nThreadsAtCompletionPort.GetInteger() > gProrateStart))
|
|
{
|
|
ThreadActivationDelay = nThreadsAtCompletionPort.GetInteger() - gProrateStart;
|
|
if (ThreadActivationDelay > 0)
|
|
{
|
|
ThreadActivationDelay *= gProrateFactor;
|
|
|
|
if ((DWORD)ThreadActivationDelay > gProrateMax)
|
|
ThreadActivationDelay = gProrateMax;
|
|
|
|
Sleep(ThreadActivationDelay);
|
|
}
|
|
}
|
|
|
|
CurrentThread = RpcpGetThreadPointer();
|
|
ASSERT(CurrentThread);
|
|
ThreadDebugCell = CurrentThread->DebugCell;
|
|
if (ThreadDebugCell)
|
|
{
|
|
ThreadDebugCell->Status = dtsIdle;
|
|
ThreadDebugCell->LastUpdateTime = NtGetTickCount();
|
|
ThreadDebugCell->Endpoint.CellID = 0;
|
|
ThreadDebugCell->Endpoint.SectionID = 0;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
|
|
EventContext = hCompletionPortHandleForThread;
|
|
Status = ProcessCalls (Timeout,
|
|
&Event,
|
|
&EventStatus,
|
|
&EventContext,
|
|
&BufferLength,
|
|
&Buffer,
|
|
&pSourceContext);
|
|
|
|
|
|
if (Status == RPC_S_OK)
|
|
{
|
|
InterlockedDecrement(&NumThreads);
|
|
|
|
if (fThreadIsDoingLongWait)
|
|
{
|
|
fThreadIsDoingLongWait = FALSE;
|
|
#if defined (RPC_GC_AUDIT)
|
|
Temp = ThreadsDoingLongWait.Decrement();
|
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: is coming back from long wait %d\n",
|
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId(), Temp);
|
|
#else
|
|
ThreadsDoingLongWait.Decrement();
|
|
#endif
|
|
}
|
|
|
|
Timeout = gThreadTimeout;
|
|
|
|
if (ThreadDebugCell)
|
|
{
|
|
ThreadDebugCell->Status = dtsProcessing;
|
|
ThreadDebugCell->LastUpdateTime = NtGetTickCount();
|
|
}
|
|
|
|
// capture the current activity state
|
|
nOldActivityValue = nActivityValue;
|
|
|
|
// indicate to the next thread that there's activity
|
|
nLocalActivityValue ++;
|
|
if ((nLocalActivityValue & 0xFF) == 0)
|
|
nActivityValue ++;
|
|
|
|
// make sure that the io event is within the bounds of the dispatch table
|
|
ASSERT(Event < sizeof(IOEventDispatchTable) / sizeof(IOEventDispatchTable[0]));
|
|
|
|
(*IOEventDispatchTable[Event])(this,
|
|
Event,
|
|
EventStatus,
|
|
EventContext,
|
|
BufferLength,
|
|
Buffer,
|
|
pSourceContext);
|
|
|
|
InterlockedIncrement(&NumThreads);
|
|
|
|
if (ThreadDebugCell)
|
|
{
|
|
ThreadDebugCell->Status = dtsIdle;
|
|
ThreadDebugCell->LastUpdateTime = NtGetTickCount();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
BOOL fKeepThread = FALSE;
|
|
|
|
// N.B. If a thread times out waiting for an Irp, we should
|
|
// let it go, unless any one of the following conditions
|
|
// exist:
|
|
// - it is the last listening thread on the port
|
|
// - there is an Irp pending on it
|
|
// - the port is busy, and we are at or below the optimal
|
|
// number of threads for this number of processors
|
|
|
|
// N.B. The NumThreads and ThreadsDoingLongWait are not
|
|
// changed atomically with respect to each other. This
|
|
// opens a race condition, but the race is benign, if the
|
|
// simple rule below is kept.
|
|
// Whenever we change both NumThreads and
|
|
// ThreadsDoingLongWait, we must do so in a way that errs
|
|
// to less threads doing short wait, rather than more
|
|
// threads doing short wait. Thus we may scare somebody
|
|
// into not doing a long wait, but that's better rather
|
|
// than letting somebody do a long wait, and toasting the
|
|
// garbage collection. For overview of the garbage
|
|
// collection mechanism, see the header in GC.cxx
|
|
ASSERT(Status == RPC_P_TIMEOUT);
|
|
LocalNumThreads = InterlockedDecrement(&NumThreads);
|
|
|
|
PerformGarbageCollection();
|
|
|
|
if (!fThreadIsDoingLongWait)
|
|
{
|
|
// we will be conservative, and we will presume we will be
|
|
// doing a long wait. If we're not, we'll decrement it later
|
|
fThreadIsDoingLongWait = TRUE;
|
|
LocalThreadsDoingLongWait = ThreadsDoingLongWait.Increment();
|
|
}
|
|
else
|
|
{
|
|
// we were already doing a long wait - just grab the current
|
|
// value
|
|
LocalThreadsDoingLongWait = ThreadsDoingLongWait.GetInteger();
|
|
}
|
|
|
|
// if there are no threads on short wait, and either one-time garbage
|
|
// collection was requested (GarbageCollectionRequested), or items
|
|
// with periodic garbage collection are requested
|
|
// (PeriodicGarbageCollectItems > 0), we can't go on a long wait
|
|
if ((LocalNumThreads <= LocalThreadsDoingLongWait)
|
|
&& (GarbageCollectionRequested || (PeriodicGarbageCollectItems > 0)))
|
|
{
|
|
#if defined (RPC_GC_AUDIT)
|
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: garbage collection requested - doing short wait %d, %d, %d, %d\n",
|
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId(), LocalNumThreads,
|
|
LocalThreadsDoingLongWait, GarbageCollectionRequested, PeriodicGarbageCollectItems);
|
|
#endif
|
|
// if garbage collection was requested, and there are
|
|
// no threads doing a short wait, we can't do a long
|
|
// wait - indicate to the code below that gThreadTimeout
|
|
// is the maximum allowed thread timeout and decrement
|
|
// the number of threads doing a long wait (we incremented
|
|
// it above - this decrement restores it)
|
|
ASSERT (fThreadIsDoingLongWait);
|
|
|
|
ThreadsDoingLongWait.Decrement();
|
|
fThreadIsDoingLongWait = FALSE;
|
|
|
|
LocalMaxThreadTimeout = gThreadTimeout;
|
|
}
|
|
else
|
|
{
|
|
// signal the code below that there is no restriction on
|
|
// the timeout applied, and it is free to choose its
|
|
// timeout
|
|
LocalMaxThreadTimeout = UndefinedLocalThreadTimeout;
|
|
}
|
|
|
|
if (LocalNumThreads == 0)
|
|
{
|
|
fKeepThread = TRUE;
|
|
|
|
if (LocalMaxThreadTimeout == UndefinedLocalThreadTimeout)
|
|
{
|
|
#if defined (RPC_GC_AUDIT)
|
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Max thread timeout\n",
|
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId());
|
|
#endif
|
|
ASSERT(fThreadIsDoingLongWait);
|
|
|
|
Timeout = INFINITE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(fThreadIsDoingLongWait == FALSE);
|
|
Timeout = LocalMaxThreadTimeout;
|
|
}
|
|
}
|
|
#ifdef RPC_OLD_IO_PROTECTION
|
|
else if (ThreadSelf()->InqProtectCount() > 1)
|
|
#else
|
|
// the simplest form of timing out threads introduces the following problem
|
|
// On an MP box, if we have N processors executing N threads, we need to keep
|
|
// an extra thread to listen for new requests. However, periodically, it will
|
|
// timeout, die, and then get recreated by one of the executing threads which
|
|
// picks a new call. This wastes cycles. If, on the other hand, on an MP box
|
|
// we keep N+1 threads around, we hurt scalability in the ASP case.
|
|
// We solve this problem by introducing the concept of a busy port. A port is
|
|
// busy if it has served within one timeout period approximately 2048 or more
|
|
// calls. If a port falls into the busy category, we don't let go the N+1th
|
|
// thread on an MP box. If the port has activity, but not enough to get into
|
|
// the busy category, we timeout the extra thread. 2048 is an arbitrary number
|
|
// where we switch trading memory for speed. nOptimalNumberOfThreads is
|
|
// the number of processors + 1 for this implementation.
|
|
// since nLocalActivityValue is updated once per 256 requests (to avoid sloshing)
|
|
// having a difference of 8 is approximately 2048 requests. There is wide
|
|
// margin of error, as it is possible for threads to be anywhere in the 256
|
|
// range and still count as nothing, but that's ok.
|
|
else if ((nThreadsAtCompletionPort.GetInteger() <= nOptimalNumberOfThreads)
|
|
&& ((nOldActivityValue + 8) < nActivityValue))
|
|
#endif
|
|
{
|
|
fKeepThread = TRUE;
|
|
Timeout *= 2;
|
|
|
|
if (LocalMaxThreadTimeout == UndefinedLocalThreadTimeout)
|
|
LocalMaxThreadTimeout = MAX_THREAD_TIMEOUT;
|
|
|
|
// if by doubling we have exceeded the max timeout,
|
|
// drop back to it
|
|
if (Timeout > LocalMaxThreadTimeout)
|
|
{
|
|
Timeout = LocalMaxThreadTimeout;
|
|
}
|
|
// else
|
|
// {
|
|
// We could have checked whether Timeout still falls into
|
|
// the short wait category after doubling, but we know
|
|
// that short wait is gThreadTimeout, and after doubling
|
|
// it will be bigger. Therefore, we don't need to do this
|
|
// check
|
|
// }
|
|
|
|
if ((ULONG)Timeout > gThreadTimeout)
|
|
{
|
|
if (!fThreadIsDoingLongWait)
|
|
{
|
|
#if defined (RPC_GC_AUDIT)
|
|
DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Thread %X: Doing long wait: %d\n",
|
|
GetCurrentProcessId(), GetCurrentProcessId(), GetCurrentThreadId(), Timeout);
|
|
#endif
|
|
fThreadIsDoingLongWait = TRUE;
|
|
ThreadsDoingLongWait.Increment();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(fKeepThread == FALSE);
|
|
}
|
|
|
|
nOldActivityValue = nActivityValue;
|
|
|
|
if (fKeepThread)
|
|
{
|
|
InterlockedIncrement(&NumThreads);
|
|
if (ThreadDebugCell)
|
|
{
|
|
RelocateCellIfPossible((void **) &ThreadDebugCell, &CurrentThread->DebugCellTag);
|
|
CurrentThread->DebugCell = ThreadDebugCell;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fThreadIsDoingLongWait)
|
|
{
|
|
ThreadsDoingLongWait.Decrement();
|
|
}
|
|
else
|
|
{
|
|
// the only way this thread can be here is if
|
|
// all other threads are on long wait
|
|
ASSERT(LocalNumThreads <= LocalThreadsDoingLongWait);
|
|
|
|
// in this case, make a best effort to tickle one
|
|
// of the threads on a long wait. We ignore the result.
|
|
// This is ok, because it will only delay the gc until
|
|
// on of the long wait threads comes back.
|
|
TickleIocThread();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
nThreadsAtCompletionPort.Decrement();
|
|
|
|
if (ThreadDebugCell)
|
|
{
|
|
ThreadDebugCell->Status = dtsAllocated;
|
|
ThreadDebugCell->LastUpdateTime = NtGetTickCount();
|
|
}
|
|
|
|
ReleaseHandleForThread(hCompletionPortHandleForThread);
|
|
}
|