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

759 lines
17 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) Microsoft Corporation, 1997 - 1999
Module Name:
Util.cxx
Abstract:
Transport/protocol independent helper functions.
Author:
Mario Goertzel [MarioGo]
Revision History:
MarioGo 2/21/1997 Bits 'n pieces
--*/
#include <precomp.hxx>
RPC_STATUS
EndpointToPortNumber(
IN RPC_CHAR *endpoint,
OUT USHORT &port)
/*++
Routine Description:
Validates a unicode string which should contain a winsock (USHORT) port
number and converts the string to an integer.
Arguments:
endpoint - The port number as a unicode string
port - If successful, the port number.
Return Value:
RPC_S_OK
RPC_S_INVALID_ENDPOINT_FORMAT
--*/
{
RPC_CHAR *pT;
#ifdef UNICODE
ULONG lport = wcstol(endpoint, &pT, 10);
#else
ULONG lport = ANSI_strtol((const RPC_SCHAR *) endpoint, (RPC_SCHAR **) &pT, 10);
#endif
if (lport == 0 || lport > 0xFFFF || *pT != 0)
{
return(RPC_S_INVALID_ENDPOINT_FORMAT);
}
port = (USHORT)lport;
return(RPC_S_OK);
}
RPC_STATUS
EndpointToPortNumberA(
IN char *endpoint,
OUT USHORT &port)
/*++
Routine Description:
Validates an ANSI string which should contain a winsock (USHORT) port
number and converts the string to an integer.
Arguments:
endpoint - The port number as an ANSI string
port - If successful, the port number.
Return Value:
RPC_S_OK
RPC_S_INVALID_ENDPOINT_FORMAT
--*/
{
char *pT;
ULONG lport = ANSI_strtol(endpoint, &pT, 10);
if (lport == 0 || lport > 0xFFFF || *pT != 0)
{
return(RPC_S_INVALID_ENDPOINT_FORMAT);
}
port = (USHORT)lport;
return(RPC_S_OK);
}
void
PortNumberToEndpoint(
IN USHORT port,
OUT RPC_CHAR *pEndpoint
)
{
UNICODE_STRING UnicodeString;
ASSERT(port);
UnicodeString.Buffer = pEndpoint;
UnicodeString.Length = 0;
UnicodeString.MaximumLength = 6 * sizeof(RPC_CHAR);
NTSTATUS status = RtlIntegerToUnicodeString(port, 0, &UnicodeString);
ASSERT(NT_SUCCESS(status));
}
void
PortNumberToEndpointA(
IN USHORT port,
OUT char *pEndpoint
)
{
NTSTATUS status = RtlIntegerToChar(port,
10, // Base
6 * sizeof(char), // OutputLength
pEndpoint);
ASSERT(NT_SUCCESS(status));
}
inline
UCHAR ConvertHexDigit(UCHAR digit)
/*++
Routine Description:
Converts a character containing '0'-'9', 'a'-'f' or 'A'-'F' into the
equivalent binary value: 0x0 - 0xF.
Arguments:
digit - The character to convert.
Return Value:
The value of digit or zero if the digit is not a hex digit.
--*/
{
UCHAR r;
r = digit - '0';
if (r < 10)
{
return(r);
}
r = digit - 'a';
if (r < 6)
{
return(r + 10);
}
r = digit - 'A';
if (r < 6)
{
return(r + 10);
}
ASSERT(0);
return 0;
}
UCHAR
HexDigitsToBinary(
IN UCHAR high,
IN UCHAR low
)
/*++
Routine Description:
Builds an 8-bit value from two hex digits.
--*/
{
return( ( ConvertHexDigit(high) << 4 ) | ConvertHexDigit(low) );
}
DWORD
UTIL_WaitForSyncIO(
LPOVERLAPPED lpOverlapped,
IN BOOL fAlertable,
IN DWORD dwTimeout
)
/*++
Routine Description:
The special part is that the same event (this's threads event)
maybe used for multiple IOs at once. If another one of those
IOs completes, it is ignored. This only returns when the IO
specified by lpOverlapped finishes or an alert/timeout happens.
Arguments:
lpOverlapped - The status block associated with the IO in question.
fAlertable - If TRUE, the wait is alertable
dwTimeout - Milliseconds to wait for the IO.
Return Value:
Same as WaitForSingleObjectEx()
--*/
{
DWORD status;
for (;;)
{
if (HasOverlappedIoCompleted(lpOverlapped))
{
break;
}
status = WaitForSingleObjectEx(lpOverlapped->hEvent, dwTimeout, fAlertable);
if (status != WAIT_OBJECT_0)
{
ASSERT( (status == WAIT_IO_COMPLETION && fAlertable)
|| (status == WAIT_TIMEOUT && (dwTimeout != INFINITE)));
return(status);
}
if (HasOverlappedIoCompleted(lpOverlapped))
{
break;
}
// Another Io completed, just ignore it for now.
ResetEvent(lpOverlapped->hEvent);
}
return(WAIT_OBJECT_0);
}
DWORD
UTIL_WaitForSyncHTTP2IO(
IN LPOVERLAPPED lpOverlapped,
IN HANDLE hEvent,
IN BOOL fAlertable,
IN DWORD dwTimeout
)
/*++
Routine Description:
The special part is that the same event (this's threads event)
maybe used for multiple IOs at once. If another one of those
IOs completes, it is ignored. This only returns when the IO
specified by lpOverlapped finishes or an alert/timeout happens.
Arguments:
lpOverlapped - The status block associated with the IO in question.
hEvent - the event to wait on
fAlertable - If TRUE, the wait is alertable
dwTimeout - Milliseconds to wait for the IO.
Return Value:
Same as WaitForSingleObjectEx()
--*/
{
DWORD status;
ASSERT(hEvent);
for (;;)
{
if (lpOverlapped->OffsetHigh)
{
break;
}
status = WaitForSingleObjectEx(hEvent, dwTimeout, fAlertable);
if (status != WAIT_OBJECT_0)
{
ASSERT( (status == WAIT_IO_COMPLETION && fAlertable)
|| (status == WAIT_TIMEOUT && (dwTimeout != INFINITE)));
return(status);
}
if (lpOverlapped->OffsetHigh)
{
break;
}
// Another Io completed, just ignore it for now.
ResetEvent(hEvent);
}
return(WAIT_OBJECT_0);
}
RPC_STATUS
UTIL_GetOverlappedResultEx(
RPC_TRANSPORT_CONNECTION ThisConnection,
LPOVERLAPPED lpOverlapped,
LPDWORD lpNumberOfBytesTransferred,
BOOL fAlertable,
DWORD dwTimeout
)
/*++
Routine Description:
Similar to the Win32 API GetOverlappedResult.
Works with thread event based IO. (even with multiple IOs/thread-event)
Allows cancels
Arguments:
ThisConnection - RPC runtime connection associated with this IO.
lpOverlapped - Overlapped structure of the IO in progress.
Must contain a valid hEvent member.
lpNumberOfBytesTransferred - see GetOverlappedResult
fAlertable - If true, RPC cancels are enabled. This means the
wait must be alertable and must follow a protocol to determine
if a call has been cancelled.
dwTimeout - Milliseconds to wait
Return Value:
RPC_S_Ok
RPC_P_TIMEOUT
RPC_S_CALL_CANCELLED
--*/
{
BASE_ASYNC_OBJECT *Connection = (BASE_ASYNC_OBJECT *) ThisConnection;
ASSERT(lpOverlapped->hEvent);
RPC_STATUS status;
DWORD canceltimeout = 0;
for(;;)
{
// Wait for the IO to complete
status = UTIL_WaitForSyncIO(lpOverlapped, fAlertable, dwTimeout);
if (status == WAIT_OBJECT_0)
{
break;
}
if (status == WAIT_TIMEOUT)
{
ASSERT(dwTimeout != INFINITE);
if (canceltimeout)
{
return(RPC_S_CALL_CANCELLED);
}
return(RPC_P_TIMEOUT);
}
ASSERT(status == WAIT_IO_COMPLETION);
if ((Connection->type & TYPE_MASK) == CLIENT)
{
//
// The RPC call may have been cancelled, need to call
// into the runtime to find out.
//
status = I_RpcTransIoCancelled(ThisConnection, &canceltimeout);
switch (status)
{
case RPC_S_OK:
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "RPC cancelled (%p)\n",
lpOverlapped));
if (canceltimeout == 0)
{
return (RPC_S_CALL_CANCELLED);
}
//
// Convert to milliseconds
//
canceltimeout *= 1000;
if (dwTimeout > canceltimeout)
{
dwTimeout = canceltimeout;
}
break;
case RPC_S_NO_CALL_ACTIVE:
//
// ignore and continue
//
break;
default:
return RPC_S_CALL_CANCELLED;
}
}
// Either the call was cancelled and timeout has been updated or
// the call wasn't cancelled and we need to wait again.
}
// IO has completed
ASSERT(HasOverlappedIoCompleted(lpOverlapped));
// IO successful
*lpNumberOfBytesTransferred = ULONG(lpOverlapped->InternalHigh);
if ( NT_SUCCESS(lpOverlapped->Internal) )
{
return(RPC_S_OK);
}
// IO failed
return RtlNtStatusToDosError(ULONG(lpOverlapped->Internal));
}
RPC_STATUS
UTIL_GetOverlappedHTTP2ResultEx(
RPC_TRANSPORT_CONNECTION ThisConnection,
IN LPOVERLAPPED lpOverlapped,
IN HANDLE hEvent,
IN BOOL fAlertable,
IN DWORD dwTimeout
)
/*++
Routine Description:
Similar to the Win32 API GetOverlappedResult.
Works with thread event based IO. (even with multiple IOs/thread-event)
Allows cancels
Arguments:
ThisConnection - RPC runtime connection associated with this IO.
lpOverlapped - Overlapped structure of the IO in progress.
Must contain a valid hEvent member.
hEvent - event to wait on
fAlertable - If true, RPC cancels are enabled. This means the
wait must be alertable and must follow a protocol to determine
if a call has been cancelled.
dwTimeout - Milliseconds to wait
Return Value:
RPC_S_Ok
RPC_P_TIMEOUT
RPC_S_CALL_CANCELLED
--*/
{
BASE_ASYNC_OBJECT *Connection = (BASE_ASYNC_OBJECT *) ThisConnection;
ASSERT(hEvent);
RPC_STATUS status;
DWORD canceltimeout = 0;
for(;;)
{
// Wait for the IO to complete
status = UTIL_WaitForSyncHTTP2IO(lpOverlapped, hEvent, fAlertable, dwTimeout);
if (status == WAIT_OBJECT_0)
{
break;
}
if (status == WAIT_TIMEOUT)
{
ASSERT(dwTimeout != INFINITE);
if (canceltimeout)
{
return(RPC_S_CALL_CANCELLED);
}
return(RPC_P_TIMEOUT);
}
ASSERT(status == WAIT_IO_COMPLETION);
if ((Connection->type & TYPE_MASK) == CLIENT)
{
//
// The RPC call may have been cancelled, need to call
// into the runtime to find out.
//
status = I_RpcTransIoCancelled(ThisConnection, &canceltimeout);
switch (status)
{
case RPC_S_OK:
TransDbgPrint((DPFLTR_RPCPROXY_ID,
DPFLTR_WARNING_LEVEL,
RPCTRANS "RPC cancelled (%p)\n",
lpOverlapped));
if (canceltimeout == 0)
{
return (RPC_S_CALL_CANCELLED);
}
//
// Convert to milliseconds
//
canceltimeout *= 1000;
if (dwTimeout > canceltimeout)
{
dwTimeout = canceltimeout;
}
break;
case RPC_S_NO_CALL_ACTIVE:
//
// ignore and continue
//
break;
default:
return RPC_S_CALL_CANCELLED;
}
}
// Either the call was cancelled and timeout has been updated or
// the call wasn't cancelled and we need to wait again.
}
// IO has completed
ASSERT(lpOverlapped->OffsetHigh);
if (lpOverlapped->Internal == RPC_S_OK)
{
return(RPC_S_OK);
}
if ((lpOverlapped->Internal == RPC_P_CONNECTION_SHUTDOWN)
|| (lpOverlapped->Internal == RPC_P_RECEIVE_FAILED)
|| (lpOverlapped->Internal == RPC_P_SEND_FAILED)
|| (lpOverlapped->Internal == RPC_P_CONNECTION_CLOSED)
|| (lpOverlapped->Internal == RPC_S_OUT_OF_MEMORY)
|| (lpOverlapped->Internal == RPC_S_SERVER_UNAVAILABLE)
|| (lpOverlapped->Internal == RPC_S_PROTOCOL_ERROR) )
{
return lpOverlapped->Internal;
}
else
{
// IO failed
status = RtlNtStatusToDosError(ULONG(lpOverlapped->Internal));
ASSERT(status != ERROR_MR_MID_NOT_FOUND);
return status;
}
}
char * _cdecl
RpcStrTok(
IN char * string,
IN const char * control,
IN OUT char ** ppStrPrev
)
/*++
Routine Description:
Tokenize string with delimiter in control. Similar to C runtime function
strtok() but no state information is maintained. The caller is expected
to give the string from where to tokenize next.
strtok considers the string to consist of a sequence of zero or more
text tokens separated by spans of one or more control chars. The first
call, with string specified, returns a pointer to the first char of the
first token, and will write a null char into string immediately
following the returned token. Subsequent calls with zero for the first
argument (string) will work thru the string until no tokens remain. The
control string may be different from call to call. when no tokens remain
in string a NULL pointer is returned. Remember the control chars with a
bit map, one bit per ascii char. The null char is always a control char.
Arguments:
string - string to tokenize, or NULL to get next token
control - string of characters to use as delimiters
strPrev - string returned from the preceeding call to RpcStrTok().
Note:
a. Works only for ANSI character strings.
b. Cloned from SLM project vctools [crt\crtw32\string\strtok.c].
Return Value:
pointer to first token in string, or if string was NULL, to next token
NULL, when no more tokens remain.
--*/
{
char *str;
const char *ctrl = control;
unsigned char map[32];
int count;
ASSERT(ppStrPrev != NULL);
char * nextoken = *ppStrPrev;
/* Clear control map */
for (count = 0; count < 32; count++)
map[count] = 0;
/* Set bits in delimiter table */
do {
map[*ctrl >> 3] |= (1 << (*ctrl & 7));
} while (*ctrl++);
/* Initialize str. If string is NULL, set str to the saved
* pointer (i.e., continue breaking tokens out of the string
* from the last strtok call) */
if (string)
str = string;
else
str = nextoken;
/* Find beginning of token (skip over leading delimiters). Note that
* there is no token iff this loop sets str to point to the terminal
* null (*str == '\0') */
while ( (map[*str >> 3] & (1 << (*str & 7))) && *str )
str++;
string = str;
/* Find the end of the token. If it is not the end of the string,
* put a null there. */
for ( ; *str ; str++ )
if ( map[*str >> 3] & (1 << (*str & 7)) ) {
*str++ = '\0';
break;
}
/* Update nextoken (or the corresponding field in the per-thread data
* structure). This should update *ppStrPrev. */
nextoken = str;
/* Determine if a token has been found. */
if ( string == str )
return NULL;
else
return string;
}
#if defined(DBG) && defined(TRANSPORT_DLL)
BOOL ValidateError(
IN unsigned int Status,
IN unsigned int Count,
IN const int ErrorList[])
/*++
Routine Description
Tests that 'Status' is one of an expected set of error codes.
Used on debug builds as part of the VALIDATE() macro.
Example:
VALIDATE(EventStatus)
{
RPC_P_CONNECTION_CLOSED,
RPC_P_RECEIVE_FAILED,
RPC_P_CONNECTION_SHUTDOWN
// more error codes here
} END_VALIDATE;
This function is called with the RpcStatus and expected errors codes
as parameters. If RpcStatus is not one of the expected error
codes and it not zero a message will be printed to the debugger
and the function will return false. The VALIDATE macro ASSERT's the
return value.
Arguments:
Status - Status code in question.
Count - number of variable length arguments
... - One or more expected status codes. Terminated with 0 (RPC_S_OK).
Return Value:
TRUE - Status code is in the list or the status is 0.
FALSE - Status code is not in the list.
--*/
{
unsigned i;
for (i = 0; i < Count; i++)
{
if (ErrorList[i] == (int) Status)
{
return TRUE;
}
}
PrintToDebugger("RPC Assertion: unexpected failure %lu (0lx%08x)\n",
(unsigned long)Status, (unsigned long)Status);
return(FALSE);
}
#endif