759 lines
17 KiB
C++
759 lines
17 KiB
C++
|
||
/*++
|
||
|
||
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
|
||
|