windows-nt/Source/XPSP1/NT/com/rpc/runtime/mtrt/binding.cxx
2020-09-26 16:20:57 +08:00

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);
}