1829 lines
40 KiB
C++
1829 lines
40 KiB
C++
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name :
|
||
|
||
tcputil.cxx
|
||
|
||
Abstract:
|
||
|
||
This module contains common utility routines for the TCP services
|
||
|
||
Author:
|
||
|
||
Johnl 09-Oct-1994 Created.
|
||
--*/
|
||
|
||
|
||
#include "tcpdllp.hxx"
|
||
#include <isplat.h>
|
||
#include <datetime.hxx>
|
||
|
||
LPSTR
|
||
ConvertUnicodeToAnsi(
|
||
IN LPCWSTR lpszUnicode,
|
||
IN LPSTR lpszAnsi,
|
||
IN DWORD cbAnsi
|
||
)
|
||
/*++
|
||
Description:
|
||
Converts given null-terminated string into ANSI in the buffer supplied.
|
||
|
||
Arguments:
|
||
lpszUnicode null-terminated string in Unicode
|
||
lpszAnsi buffer supplied to copy string after conversion.
|
||
if ( lpszAnsi == NULL), then this module allocates space
|
||
using TCP_ALLOC, which should be freed calling TCP_FREE
|
||
by user.
|
||
cbAnsi number of bytes in lpszAnsi if specified
|
||
|
||
Returns:
|
||
pointer to converted ANSI string. NULL on errors.
|
||
|
||
History:
|
||
MuraliK 12-01-1994 Created.
|
||
--*/
|
||
{
|
||
|
||
DWORD cchLen;
|
||
DWORD nBytes;
|
||
LPSTR lpszAlloc = NULL;
|
||
|
||
if ( lpszUnicode == NULL) {
|
||
return (NULL);
|
||
}
|
||
|
||
if ( lpszAnsi == NULL) {
|
||
|
||
//
|
||
// multiply by 2 to accomodate DBCS
|
||
//
|
||
|
||
cchLen = wcslen( lpszUnicode);
|
||
nBytes = (cchLen+1) * sizeof(CHAR) * 2;
|
||
lpszAlloc = (LPSTR ) TCP_ALLOC( nBytes );
|
||
|
||
} else {
|
||
|
||
lpszAlloc = lpszAnsi;
|
||
nBytes = cbAnsi;
|
||
DBG_ASSERT(nBytes > 0);
|
||
}
|
||
|
||
if ( lpszAlloc != NULL) {
|
||
|
||
cchLen = WideCharToMultiByte( CP_ACP,
|
||
WC_COMPOSITECHECK,
|
||
lpszUnicode,
|
||
-1,
|
||
lpszAlloc,
|
||
nBytes,
|
||
NULL, // lpszDefaultChar
|
||
NULL // lpfDefaultUsed
|
||
);
|
||
|
||
DBG_ASSERT(cchLen == (strlen(lpszAlloc)+1) );
|
||
|
||
if ( cchLen == 0 ) {
|
||
|
||
//
|
||
// There was a failure. Free up buffer if need be.
|
||
//
|
||
|
||
DBGPRINTF((DBG_CONTEXT,"WideCharToMultiByte failed with %d\n",
|
||
GetLastError()));
|
||
|
||
if ( lpszAnsi == NULL) {
|
||
TCP_FREE( lpszAlloc);
|
||
lpszAlloc = NULL;
|
||
} else {
|
||
lpszAlloc[cchLen] = '\0';
|
||
}
|
||
|
||
} else {
|
||
|
||
DBG_ASSERT( cchLen <= nBytes );
|
||
DBG_ASSERT(lpszAlloc[cchLen-1] == '\0');
|
||
|
||
lpszAlloc[cchLen-1] = '\0';
|
||
}
|
||
}
|
||
|
||
return ( lpszAlloc);
|
||
|
||
} // ConvertUnicodeToAnsi
|
||
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: ReadRegistryDword
|
||
|
||
SYNOPSIS: Reads a DWORD value from the registry.
|
||
|
||
ENTRY: hkey - Openned registry key to read
|
||
|
||
pszValueName - The name of the value.
|
||
|
||
dwDefaultValue - The default value to use if the
|
||
value cannot be read.
|
||
|
||
RETURNS DWORD - The value from the registry, or dwDefaultValue.
|
||
|
||
********************************************************************/
|
||
DWORD ReadRegistryDwordA( HKEY hkey,
|
||
LPCSTR pszValueName,
|
||
DWORD dwDefaultValue )
|
||
{
|
||
DWORD err;
|
||
DWORD dwBuffer;
|
||
|
||
DWORD cbBuffer = sizeof(dwBuffer);
|
||
DWORD dwType;
|
||
|
||
if( hkey != NULL )
|
||
{
|
||
err = RegQueryValueExA( hkey,
|
||
pszValueName,
|
||
NULL,
|
||
&dwType,
|
||
(LPBYTE)&dwBuffer,
|
||
&cbBuffer );
|
||
|
||
if( ( err == NO_ERROR ) && ( dwType == REG_DWORD ) )
|
||
{
|
||
dwDefaultValue = dwBuffer;
|
||
}
|
||
}
|
||
return dwDefaultValue;
|
||
} // ReadRegistryDwordA()
|
||
|
||
|
||
DWORD
|
||
WriteRegistryDwordA(
|
||
IN HKEY hkey,
|
||
IN LPCSTR pszValueName,
|
||
IN DWORD dwValue)
|
||
/*++
|
||
Description:
|
||
Writes the given DWORD value into registry entry specified
|
||
by hkey\pszValueName
|
||
|
||
Arguments:
|
||
hkey handle to registry key
|
||
pszValueName name of the value
|
||
dwValue new value for write
|
||
|
||
Returns:
|
||
Win32 error codes. NO_ERROR if successful.
|
||
|
||
History:
|
||
MuraliK 12-01-1994 Created.
|
||
--*/
|
||
{
|
||
DWORD err;
|
||
|
||
if ( (hkey == NULL) || (pszValueName == NULL) ) {
|
||
|
||
err = ( ERROR_INVALID_PARAMETER);
|
||
|
||
} else {
|
||
err = RegSetValueExA( hkey,
|
||
pszValueName,
|
||
0,
|
||
REG_DWORD,
|
||
(LPBYTE ) &dwValue,
|
||
sizeof( dwValue));
|
||
}
|
||
|
||
return ( err);
|
||
} // WriteRegistryDwordA()
|
||
|
||
|
||
|
||
|
||
|
||
DWORD
|
||
WriteRegistryStringA(
|
||
IN HKEY hkey,
|
||
IN LPCSTR pszValueName,
|
||
IN LPCSTR pszValue,
|
||
IN DWORD cbValue,
|
||
IN DWORD dwType
|
||
)
|
||
/*++
|
||
Description:
|
||
Writes the given ANSI String into registry entry specified
|
||
by hkey\pszValueName.
|
||
|
||
Arguments:
|
||
hkey handle to registry key
|
||
pszValueName name of the value
|
||
pszValue new value for write
|
||
cbValue count of bytes of value written.
|
||
Should include terminating null characters.
|
||
dwType type of the value being written
|
||
( REG_SZ, REG_MULTI_SZ etc)
|
||
|
||
Returns:
|
||
Win32 error codes. NO_ERROR if successful.
|
||
|
||
--*/
|
||
{
|
||
DWORD err;
|
||
|
||
DBG_ASSERT(dwType != REG_MULTI_SZ);
|
||
DBG_ASSERT( (dwType == REG_SZ) || (dwType == REG_EXPAND_SZ) );
|
||
|
||
if ( (hkey == NULL) ||
|
||
(pszValueName == NULL) ||
|
||
(cbValue == 0) ) {
|
||
|
||
err = ERROR_INVALID_PARAMETER;
|
||
} else {
|
||
|
||
err = RegSetValueExA(
|
||
hkey,
|
||
pszValueName,
|
||
0,
|
||
dwType,
|
||
(LPBYTE ) pszValue,
|
||
cbValue); // + 1 for null character
|
||
}
|
||
|
||
return ( err);
|
||
} // WriteRegistryStringA()
|
||
|
||
|
||
DWORD
|
||
WriteRegistryStringW(
|
||
IN HKEY hkey,
|
||
IN LPCWSTR pszValueName,
|
||
IN LPCWSTR pszValue,
|
||
IN DWORD cbValue,
|
||
IN DWORD dwType)
|
||
/*++
|
||
Description:
|
||
Writes the given ANSI String into registry entry specified
|
||
by hkey\pszValueName.
|
||
|
||
Arguments:
|
||
hkey handle to registry key
|
||
pszValueName name of the value
|
||
pszValue new value for write
|
||
cbValue count of bytes of value written.
|
||
Should include terminating null characters.
|
||
dwType type of the value being written
|
||
( REG_SZ, REG_MULTI_SZ etc)
|
||
|
||
Returns:
|
||
Win32 error codes. NO_ERROR if successful.
|
||
|
||
--*/
|
||
{
|
||
DWORD err;
|
||
|
||
LPSTR ansiValue = NULL;
|
||
LPSTR ansiName = NULL;
|
||
|
||
if ( (hkey == NULL) ||
|
||
(pszValueName == NULL) ||
|
||
(cbValue == 0) ) {
|
||
|
||
err = ERROR_INVALID_PARAMETER;
|
||
} else {
|
||
|
||
//
|
||
// Convert to ansi
|
||
//
|
||
|
||
ansiName = ConvertUnicodeToAnsi( pszValueName, NULL, 0 );
|
||
ansiValue = ConvertUnicodeToAnsi( pszValue, NULL, 0 );
|
||
|
||
if ( (ansiName != NULL) && (ansiValue != NULL) ) {
|
||
|
||
err = WriteRegistryStringA(hkey,
|
||
ansiName,
|
||
ansiValue,
|
||
strlen(ansiValue)+1,
|
||
dwType
|
||
);
|
||
} else {
|
||
err = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
}
|
||
|
||
if ( ansiName != NULL ) {
|
||
TCP_FREE(ansiName);
|
||
}
|
||
|
||
if ( ansiValue != NULL ) {
|
||
TCP_FREE(ansiValue);
|
||
}
|
||
|
||
return ( err);
|
||
} // WriteRegistryStringW()
|
||
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: ReadRegistryString
|
||
|
||
SYNOPSIS: Allocates necessary buffer space for a registry
|
||
string, then reads the string into the buffer.
|
||
|
||
ENTRY: pszValueName - The name of the value.
|
||
|
||
pszDefaultValue - The default value to use if the
|
||
value cannot be read.
|
||
|
||
fExpand - Expand environment strings if TRUE.
|
||
|
||
RETURNS: TCHAR * - The string, NULL if error.
|
||
|
||
NOTES: I always allocate one more character than actually
|
||
necessary. This will ensure that any code expecting
|
||
to read a REG_MULTI_SZ will not explode if the
|
||
registry actually contains a REG_SZ.
|
||
|
||
This function cannot be called until after
|
||
InitializeGlobals().
|
||
|
||
HISTORY:
|
||
KeithMo 15-Mar-1993 Created.
|
||
|
||
********************************************************************/
|
||
TCHAR * ReadRegistryString( HKEY hkey,
|
||
LPCTSTR pszValueName,
|
||
LPCTSTR pszDefaultValue,
|
||
BOOL fExpand )
|
||
{
|
||
TCHAR * pszBuffer1;
|
||
TCHAR * pszBuffer2;
|
||
DWORD cbBuffer;
|
||
DWORD dwType;
|
||
DWORD err;
|
||
|
||
//
|
||
// Determine the buffer size.
|
||
//
|
||
|
||
pszBuffer1 = NULL;
|
||
pszBuffer2 = NULL;
|
||
cbBuffer = 0;
|
||
|
||
if( hkey == NULL )
|
||
{
|
||
//
|
||
// Pretend the key wasn't found.
|
||
//
|
||
|
||
err = ERROR_FILE_NOT_FOUND;
|
||
}
|
||
else
|
||
{
|
||
err = RegQueryValueEx( hkey,
|
||
pszValueName,
|
||
NULL,
|
||
&dwType,
|
||
NULL,
|
||
&cbBuffer );
|
||
|
||
if( ( err == NO_ERROR ) || ( err == ERROR_MORE_DATA ) )
|
||
{
|
||
if( ( dwType != REG_SZ ) &&
|
||
( dwType != REG_MULTI_SZ ) &&
|
||
( dwType != REG_EXPAND_SZ ) )
|
||
{
|
||
//
|
||
// Type mismatch, registry data NOT a string.
|
||
// Use default.
|
||
//
|
||
|
||
err = ERROR_FILE_NOT_FOUND;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Item found, allocate a buffer.
|
||
//
|
||
|
||
pszBuffer1 = (TCHAR *) TCP_ALLOC( cbBuffer+sizeof(TCHAR) );
|
||
|
||
if( pszBuffer1 == NULL )
|
||
{
|
||
err = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Now read the value into the buffer.
|
||
//
|
||
|
||
err = RegQueryValueEx( hkey,
|
||
pszValueName,
|
||
NULL,
|
||
NULL,
|
||
(LPBYTE)pszBuffer1,
|
||
&cbBuffer );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if( err == ERROR_FILE_NOT_FOUND )
|
||
{
|
||
//
|
||
// Item not found, use default value.
|
||
//
|
||
|
||
err = NO_ERROR;
|
||
|
||
if( pszDefaultValue != NULL )
|
||
{
|
||
pszBuffer1 = (TCHAR *)TCP_ALLOC( (_tcslen(pszDefaultValue)+1) * sizeof(TCHAR) );
|
||
|
||
if( pszBuffer1 == NULL )
|
||
{
|
||
err = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
else
|
||
{
|
||
_tcscpy( pszBuffer1, pszDefaultValue );
|
||
}
|
||
}
|
||
}
|
||
|
||
if( err != NO_ERROR )
|
||
{
|
||
//
|
||
// Tragic error reading registry, abort now.
|
||
//
|
||
|
||
goto ErrorCleanup;
|
||
}
|
||
|
||
//
|
||
// pszBuffer1 holds the registry value. Now expand
|
||
// the environment strings if necessary.
|
||
//
|
||
|
||
if( !fExpand || TsIsWindows95() )
|
||
{
|
||
return pszBuffer1;
|
||
}
|
||
|
||
//
|
||
// Returns number of characters
|
||
//
|
||
cbBuffer = ExpandEnvironmentStrings( pszBuffer1,
|
||
NULL,
|
||
0 );
|
||
|
||
//
|
||
// The ExpandEnvironmentStrings() API is kinda poor. In returning the
|
||
// number of characters, we have no clue how large to make the buffer
|
||
// in the case of DBCS characters. Lets assume that each character is
|
||
// 2 bytes.
|
||
//
|
||
|
||
pszBuffer2 = (TCHAR *) TCP_ALLOC( (cbBuffer+1)*sizeof(WCHAR) );
|
||
|
||
if( pszBuffer2 == NULL )
|
||
{
|
||
goto ErrorCleanup;
|
||
}
|
||
|
||
if( ExpandEnvironmentStrings( pszBuffer1,
|
||
pszBuffer2,
|
||
cbBuffer ) > cbBuffer )
|
||
{
|
||
goto ErrorCleanup;
|
||
}
|
||
|
||
//
|
||
// pszBuffer2 now contains the registry value with
|
||
// environment strings expanded.
|
||
//
|
||
|
||
TCP_FREE( pszBuffer1 );
|
||
pszBuffer1 = NULL;
|
||
|
||
return pszBuffer2;
|
||
|
||
ErrorCleanup:
|
||
|
||
//
|
||
// Something tragic happend; free any allocated buffers
|
||
// and return NULL to the caller, indicating failure.
|
||
//
|
||
|
||
if( pszBuffer1 != NULL )
|
||
{
|
||
TCP_FREE( pszBuffer1 );
|
||
pszBuffer1 = NULL;
|
||
}
|
||
|
||
if( pszBuffer2 != NULL )
|
||
{
|
||
TCP_FREE( pszBuffer2 );
|
||
pszBuffer2 = NULL;
|
||
}
|
||
|
||
return NULL;
|
||
|
||
} // ReadRegistryString
|
||
|
||
|
||
//
|
||
// Chicago does not support the REG_MULTI_SZ registry value. As
|
||
// a hack (er, workaround), we'll create *keys* in the registry
|
||
// in place of REG_MULTI_SZ *values*. We'll then use the names
|
||
// of any values under the key as the REG_MULTI_SZ entries. So,
|
||
// instead of this:
|
||
//
|
||
// ..\Control\ServiceProvider
|
||
// ProviderOrder = REG_MULTI_SZ "MSTCP"
|
||
// "NWLINK"
|
||
// "FOOBAR"
|
||
//
|
||
// We'll use this:
|
||
//
|
||
// ..\Control\Service\Provider\ProviderOrder
|
||
// MSTCP = REG_SZ ""
|
||
// NWLINK = REG_SZ ""
|
||
// FOOBAR = REG_SZ ""
|
||
//
|
||
// This function takes an open registry key handle, enumerates
|
||
// the names of values contained within the key, and constructs
|
||
// a REG_MULTI_SZ string from the value names.
|
||
//
|
||
// Note that this function is not multithread safe; if another
|
||
// thread (or process) creates or deletes values under the
|
||
// specified key, the results are indeterminate.
|
||
//
|
||
// This function returns NULL on error. It returns non-NULL
|
||
// on success, even if the resulting REG_MULTI_SZ is empty.
|
||
//
|
||
|
||
TCHAR *
|
||
KludgeMultiSz(
|
||
HKEY hkey,
|
||
LPDWORD lpdwLength
|
||
)
|
||
{
|
||
LONG err;
|
||
DWORD iValue;
|
||
DWORD cchTotal;
|
||
DWORD cchValue;
|
||
TCHAR szValue[MAX_PATH];
|
||
LPTSTR lpMultiSz;
|
||
LPTSTR lpTmp;
|
||
LPTSTR lpEnd;
|
||
|
||
//
|
||
// Enumerate the values and total up the lengths.
|
||
//
|
||
|
||
iValue = 0;
|
||
cchTotal = 0;
|
||
|
||
for( ; ; )
|
||
{
|
||
cchValue = sizeof(szValue)/sizeof(TCHAR);
|
||
|
||
err = RegEnumValue( hkey,
|
||
iValue,
|
||
szValue,
|
||
&cchValue,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL );
|
||
|
||
if( err != NO_ERROR )
|
||
{
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Add the length of the value's name, plus one
|
||
// for the terminator.
|
||
//
|
||
|
||
cchTotal += _tcslen( szValue ) + 1;
|
||
|
||
//
|
||
// Advance to next value.
|
||
//
|
||
|
||
iValue++;
|
||
}
|
||
|
||
//
|
||
// Add one for the final terminating NULL.
|
||
//
|
||
|
||
cchTotal++;
|
||
*lpdwLength = cchTotal;
|
||
|
||
//
|
||
// Allocate the MULTI_SZ buffer.
|
||
//
|
||
|
||
lpMultiSz = (TCHAR *) TCP_ALLOC( cchTotal * sizeof(TCHAR) );
|
||
|
||
if( lpMultiSz == NULL )
|
||
{
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
||
return NULL;
|
||
}
|
||
|
||
memset( lpMultiSz, 0, cchTotal * sizeof(TCHAR) );
|
||
|
||
//
|
||
// Enumerate the values and append to the buffer.
|
||
//
|
||
|
||
iValue = 0;
|
||
lpTmp = lpMultiSz;
|
||
lpEnd = lpMultiSz + cchTotal;
|
||
|
||
for( ; ; )
|
||
{
|
||
cchValue = sizeof(szValue)/sizeof(TCHAR);
|
||
|
||
err = RegEnumValue( hkey,
|
||
iValue,
|
||
szValue,
|
||
&cchValue,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL );
|
||
|
||
if( err != NO_ERROR )
|
||
{
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Compute the length of the value name (including
|
||
// the terminating NULL).
|
||
//
|
||
|
||
cchValue = _tcslen( szValue ) + 1;
|
||
|
||
//
|
||
// Determine if there is room in the array, taking into
|
||
// account the second NULL that terminates the string list.
|
||
//
|
||
|
||
if( ( lpTmp + cchValue + 1 ) > lpEnd )
|
||
{
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Append the value name.
|
||
//
|
||
|
||
_tcscpy( lpTmp, szValue );
|
||
lpTmp += cchValue;
|
||
|
||
//
|
||
// Advance to next value.
|
||
//
|
||
|
||
iValue++;
|
||
}
|
||
|
||
//
|
||
// Success!
|
||
//
|
||
|
||
return (LPTSTR)lpMultiSz;
|
||
|
||
} // KludgeMultiSz
|
||
|
||
|
||
|
||
BOOL
|
||
ReadRegistryStr(
|
||
IN HKEY hkeyReg,
|
||
OUT STR & str,
|
||
IN LPCTSTR lpszValueName,
|
||
IN LPCTSTR lpszDefaultValue,
|
||
IN BOOL fExpand )
|
||
/*++
|
||
|
||
Reads the registry string into the string buffer supplied.
|
||
If there is no value in the registry the default value is set to
|
||
be the value of the string.
|
||
|
||
If an environment expansion is requested, it is also performed.
|
||
|
||
Arguments:
|
||
|
||
hkeyReg handle for registry entry
|
||
str string to contain the result of read operation
|
||
lpszValueName
|
||
pointer to string containing the key name whose
|
||
value needs to be fetched.
|
||
lpszDefaultValue
|
||
pointer to string containing a value which is used if no
|
||
value exists in the registry.
|
||
fExpand boolean flag indicating if an expansion is desired.
|
||
|
||
Returns:
|
||
FALSE if there is any error.
|
||
TRUE when the string is successfully set.
|
||
--*/
|
||
{
|
||
BOOL fReturn = FALSE;
|
||
LPTSTR pszValueAlloc;
|
||
|
||
pszValueAlloc = ReadRegistryString( hkeyReg, lpszValueName,
|
||
lpszDefaultValue, fExpand);
|
||
|
||
if ( pszValueAlloc != NULL) {
|
||
|
||
fReturn = str.Copy( pszValueAlloc);
|
||
TCP_FREE( pszValueAlloc);
|
||
} else {
|
||
|
||
DBG_ASSERT( fReturn == FALSE);
|
||
}
|
||
|
||
if ( !fReturn) {
|
||
|
||
IF_DEBUG( ERROR) {
|
||
|
||
DWORD err = GetLastError();
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
" Error %u in ReadRegistryString( %08x, %s).\n",
|
||
err, hkeyReg, lpszValueName));
|
||
|
||
SetLastError(err);
|
||
}
|
||
}
|
||
|
||
return ( fReturn);
|
||
} // ReadRegistryStr
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: FlipSlashes
|
||
|
||
SYNOPSIS: Flips the Unix-ish forward slashes ('/') into Dos-ish
|
||
back slashes ('\').
|
||
|
||
ENTRY: pszPath - The path to munge.
|
||
|
||
RETURNS: TCHAR * - pszPath.
|
||
|
||
HISTORY:
|
||
KeithMo 04-Jun-1993 Created.
|
||
|
||
********************************************************************/
|
||
TCHAR * FlipSlashes( TCHAR * pszPath )
|
||
{
|
||
TCHAR ch;
|
||
TCHAR * pszScan = pszPath;
|
||
|
||
while( ( ch = *pszScan ) != TEXT('\0') )
|
||
{
|
||
if( ch == TEXT('/') )
|
||
{
|
||
*pszScan = TEXT('\\');
|
||
}
|
||
|
||
pszScan++;
|
||
}
|
||
|
||
return pszPath;
|
||
|
||
} // FlipSlashes
|
||
|
||
|
||
|
||
|
||
|
||
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
i_ntoa.c
|
||
|
||
Abstract:
|
||
|
||
This module implements a routine to convert a numerical IP address
|
||
into a dotted-decimal character string Internet address.
|
||
|
||
Author:
|
||
|
||
Mike Massa (mikemas) Sept 20, 1991
|
||
|
||
Revision History:
|
||
|
||
Who When What
|
||
-------- -------- ----------------------------------------------
|
||
mikemas 9-20-91 created
|
||
davidtr 9-19-95 completely rewritten for performance
|
||
muralik 3-Oct-1995 massaged it for Internet services
|
||
|
||
Notes:
|
||
|
||
Exports:
|
||
InetNtoa()
|
||
|
||
--*/
|
||
|
||
|
||
#define UC(b) (((int)b)&0xff)
|
||
|
||
//
|
||
// This preinitialized array defines the strings to be used for
|
||
// inet_ntoa. The index of each row corresponds to the value for a byte
|
||
// in an IP address. The first three bytes of each row are the
|
||
// char/string value for the byte, and the fourth byte in each row is
|
||
// the length of the string required for the byte. This approach
|
||
// allows a fast implementation with no jumps.
|
||
//
|
||
|
||
static BYTE NToACharStrings[][4] = {
|
||
'0', 'x', 'x', 1,
|
||
'1', 'x', 'x', 1,
|
||
'2', 'x', 'x', 1,
|
||
'3', 'x', 'x', 1,
|
||
'4', 'x', 'x', 1,
|
||
'5', 'x', 'x', 1,
|
||
'6', 'x', 'x', 1,
|
||
'7', 'x', 'x', 1,
|
||
'8', 'x', 'x', 1,
|
||
'9', 'x', 'x', 1,
|
||
'1', '0', 'x', 2,
|
||
'1', '1', 'x', 2,
|
||
'1', '2', 'x', 2,
|
||
'1', '3', 'x', 2,
|
||
'1', '4', 'x', 2,
|
||
'1', '5', 'x', 2,
|
||
'1', '6', 'x', 2,
|
||
'1', '7', 'x', 2,
|
||
'1', '8', 'x', 2,
|
||
'1', '9', 'x', 2,
|
||
'2', '0', 'x', 2,
|
||
'2', '1', 'x', 2,
|
||
'2', '2', 'x', 2,
|
||
'2', '3', 'x', 2,
|
||
'2', '4', 'x', 2,
|
||
'2', '5', 'x', 2,
|
||
'2', '6', 'x', 2,
|
||
'2', '7', 'x', 2,
|
||
'2', '8', 'x', 2,
|
||
'2', '9', 'x', 2,
|
||
'3', '0', 'x', 2,
|
||
'3', '1', 'x', 2,
|
||
'3', '2', 'x', 2,
|
||
'3', '3', 'x', 2,
|
||
'3', '4', 'x', 2,
|
||
'3', '5', 'x', 2,
|
||
'3', '6', 'x', 2,
|
||
'3', '7', 'x', 2,
|
||
'3', '8', 'x', 2,
|
||
'3', '9', 'x', 2,
|
||
'4', '0', 'x', 2,
|
||
'4', '1', 'x', 2,
|
||
'4', '2', 'x', 2,
|
||
'4', '3', 'x', 2,
|
||
'4', '4', 'x', 2,
|
||
'4', '5', 'x', 2,
|
||
'4', '6', 'x', 2,
|
||
'4', '7', 'x', 2,
|
||
'4', '8', 'x', 2,
|
||
'4', '9', 'x', 2,
|
||
'5', '0', 'x', 2,
|
||
'5', '1', 'x', 2,
|
||
'5', '2', 'x', 2,
|
||
'5', '3', 'x', 2,
|
||
'5', '4', 'x', 2,
|
||
'5', '5', 'x', 2,
|
||
'5', '6', 'x', 2,
|
||
'5', '7', 'x', 2,
|
||
'5', '8', 'x', 2,
|
||
'5', '9', 'x', 2,
|
||
'6', '0', 'x', 2,
|
||
'6', '1', 'x', 2,
|
||
'6', '2', 'x', 2,
|
||
'6', '3', 'x', 2,
|
||
'6', '4', 'x', 2,
|
||
'6', '5', 'x', 2,
|
||
'6', '6', 'x', 2,
|
||
'6', '7', 'x', 2,
|
||
'6', '8', 'x', 2,
|
||
'6', '9', 'x', 2,
|
||
'7', '0', 'x', 2,
|
||
'7', '1', 'x', 2,
|
||
'7', '2', 'x', 2,
|
||
'7', '3', 'x', 2,
|
||
'7', '4', 'x', 2,
|
||
'7', '5', 'x', 2,
|
||
'7', '6', 'x', 2,
|
||
'7', '7', 'x', 2,
|
||
'7', '8', 'x', 2,
|
||
'7', '9', 'x', 2,
|
||
'8', '0', 'x', 2,
|
||
'8', '1', 'x', 2,
|
||
'8', '2', 'x', 2,
|
||
'8', '3', 'x', 2,
|
||
'8', '4', 'x', 2,
|
||
'8', '5', 'x', 2,
|
||
'8', '6', 'x', 2,
|
||
'8', '7', 'x', 2,
|
||
'8', '8', 'x', 2,
|
||
'8', '9', 'x', 2,
|
||
'9', '0', 'x', 2,
|
||
'9', '1', 'x', 2,
|
||
'9', '2', 'x', 2,
|
||
'9', '3', 'x', 2,
|
||
'9', '4', 'x', 2,
|
||
'9', '5', 'x', 2,
|
||
'9', '6', 'x', 2,
|
||
'9', '7', 'x', 2,
|
||
'9', '8', 'x', 2,
|
||
'9', '9', 'x', 2,
|
||
'1', '0', '0', 3,
|
||
'1', '0', '1', 3,
|
||
'1', '0', '2', 3,
|
||
'1', '0', '3', 3,
|
||
'1', '0', '4', 3,
|
||
'1', '0', '5', 3,
|
||
'1', '0', '6', 3,
|
||
'1', '0', '7', 3,
|
||
'1', '0', '8', 3,
|
||
'1', '0', '9', 3,
|
||
'1', '1', '0', 3,
|
||
'1', '1', '1', 3,
|
||
'1', '1', '2', 3,
|
||
'1', '1', '3', 3,
|
||
'1', '1', '4', 3,
|
||
'1', '1', '5', 3,
|
||
'1', '1', '6', 3,
|
||
'1', '1', '7', 3,
|
||
'1', '1', '8', 3,
|
||
'1', '1', '9', 3,
|
||
'1', '2', '0', 3,
|
||
'1', '2', '1', 3,
|
||
'1', '2', '2', 3,
|
||
'1', '2', '3', 3,
|
||
'1', '2', '4', 3,
|
||
'1', '2', '5', 3,
|
||
'1', '2', '6', 3,
|
||
'1', '2', '7', 3,
|
||
'1', '2', '8', 3,
|
||
'1', '2', '9', 3,
|
||
'1', '3', '0', 3,
|
||
'1', '3', '1', 3,
|
||
'1', '3', '2', 3,
|
||
'1', '3', '3', 3,
|
||
'1', '3', '4', 3,
|
||
'1', '3', '5', 3,
|
||
'1', '3', '6', 3,
|
||
'1', '3', '7', 3,
|
||
'1', '3', '8', 3,
|
||
'1', '3', '9', 3,
|
||
'1', '4', '0', 3,
|
||
'1', '4', '1', 3,
|
||
'1', '4', '2', 3,
|
||
'1', '4', '3', 3,
|
||
'1', '4', '4', 3,
|
||
'1', '4', '5', 3,
|
||
'1', '4', '6', 3,
|
||
'1', '4', '7', 3,
|
||
'1', '4', '8', 3,
|
||
'1', '4', '9', 3,
|
||
'1', '5', '0', 3,
|
||
'1', '5', '1', 3,
|
||
'1', '5', '2', 3,
|
||
'1', '5', '3', 3,
|
||
'1', '5', '4', 3,
|
||
'1', '5', '5', 3,
|
||
'1', '5', '6', 3,
|
||
'1', '5', '7', 3,
|
||
'1', '5', '8', 3,
|
||
'1', '5', '9', 3,
|
||
'1', '6', '0', 3,
|
||
'1', '6', '1', 3,
|
||
'1', '6', '2', 3,
|
||
'1', '6', '3', 3,
|
||
'1', '6', '4', 3,
|
||
'1', '6', '5', 3,
|
||
'1', '6', '6', 3,
|
||
'1', '6', '7', 3,
|
||
'1', '6', '8', 3,
|
||
'1', '6', '9', 3,
|
||
'1', '7', '0', 3,
|
||
'1', '7', '1', 3,
|
||
'1', '7', '2', 3,
|
||
'1', '7', '3', 3,
|
||
'1', '7', '4', 3,
|
||
'1', '7', '5', 3,
|
||
'1', '7', '6', 3,
|
||
'1', '7', '7', 3,
|
||
'1', '7', '8', 3,
|
||
'1', '7', '9', 3,
|
||
'1', '8', '0', 3,
|
||
'1', '8', '1', 3,
|
||
'1', '8', '2', 3,
|
||
'1', '8', '3', 3,
|
||
'1', '8', '4', 3,
|
||
'1', '8', '5', 3,
|
||
'1', '8', '6', 3,
|
||
'1', '8', '7', 3,
|
||
'1', '8', '8', 3,
|
||
'1', '8', '9', 3,
|
||
'1', '9', '0', 3,
|
||
'1', '9', '1', 3,
|
||
'1', '9', '2', 3,
|
||
'1', '9', '3', 3,
|
||
'1', '9', '4', 3,
|
||
'1', '9', '5', 3,
|
||
'1', '9', '6', 3,
|
||
'1', '9', '7', 3,
|
||
'1', '9', '8', 3,
|
||
'1', '9', '9', 3,
|
||
'2', '0', '0', 3,
|
||
'2', '0', '1', 3,
|
||
'2', '0', '2', 3,
|
||
'2', '0', '3', 3,
|
||
'2', '0', '4', 3,
|
||
'2', '0', '5', 3,
|
||
'2', '0', '6', 3,
|
||
'2', '0', '7', 3,
|
||
'2', '0', '8', 3,
|
||
'2', '0', '9', 3,
|
||
'2', '1', '0', 3,
|
||
'2', '1', '1', 3,
|
||
'2', '1', '2', 3,
|
||
'2', '1', '3', 3,
|
||
'2', '1', '4', 3,
|
||
'2', '1', '5', 3,
|
||
'2', '1', '6', 3,
|
||
'2', '1', '7', 3,
|
||
'2', '1', '8', 3,
|
||
'2', '1', '9', 3,
|
||
'2', '2', '0', 3,
|
||
'2', '2', '1', 3,
|
||
'2', '2', '2', 3,
|
||
'2', '2', '3', 3,
|
||
'2', '2', '4', 3,
|
||
'2', '2', '5', 3,
|
||
'2', '2', '6', 3,
|
||
'2', '2', '7', 3,
|
||
'2', '2', '8', 3,
|
||
'2', '2', '9', 3,
|
||
'2', '3', '0', 3,
|
||
'2', '3', '1', 3,
|
||
'2', '3', '2', 3,
|
||
'2', '3', '3', 3,
|
||
'2', '3', '4', 3,
|
||
'2', '3', '5', 3,
|
||
'2', '3', '6', 3,
|
||
'2', '3', '7', 3,
|
||
'2', '3', '8', 3,
|
||
'2', '3', '9', 3,
|
||
'2', '4', '0', 3,
|
||
'2', '4', '1', 3,
|
||
'2', '4', '2', 3,
|
||
'2', '4', '3', 3,
|
||
'2', '4', '4', 3,
|
||
'2', '4', '5', 3,
|
||
'2', '4', '6', 3,
|
||
'2', '4', '7', 3,
|
||
'2', '4', '8', 3,
|
||
'2', '4', '9', 3,
|
||
'2', '5', '0', 3,
|
||
'2', '5', '1', 3,
|
||
'2', '5', '2', 3,
|
||
'2', '5', '3', 3,
|
||
'2', '5', '4', 3,
|
||
'2', '5', '5', 3
|
||
};
|
||
|
||
|
||
|
||
DWORD
|
||
InetNtoa(
|
||
IN struct in_addr inaddr,
|
||
OUT CHAR * pchBuffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function takes an Internet address structure specified by the
|
||
in parameter. It returns an ASCII string representing the address
|
||
in ".'' notation as "a.b.c.d".
|
||
|
||
Arguments:
|
||
|
||
inaddr - A structure which represents an Internet host address.
|
||
pchBuffer - pointer to at least 16 character buffer for storing
|
||
the result of conversion.
|
||
Return Value:
|
||
|
||
If no error occurs, InetNtoa() returns NO_ERROR with the buffer containing
|
||
the text address in standard "." notation.
|
||
Otherwise, it returns Win32 error code.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PUCHAR p;
|
||
PUCHAR buffer = (PUCHAR ) pchBuffer;
|
||
PUCHAR b = buffer;
|
||
|
||
if ( pchBuffer == NULL) {
|
||
|
||
return ( ERROR_INSUFFICIENT_BUFFER);
|
||
}
|
||
|
||
//
|
||
// We do not check for sufficient length of the buffer yet. !!
|
||
//
|
||
|
||
//
|
||
// In an unrolled loop, calculate the string value for each of the four
|
||
// bytes in an IP address. Note that for values less than 100 we will
|
||
// do one or two extra assignments, but we save a test/jump with this
|
||
// algorithm.
|
||
//
|
||
|
||
p = (PUCHAR) &inaddr;
|
||
|
||
*b = NToACharStrings[*p][0];
|
||
*(b+1) = NToACharStrings[*p][1];
|
||
*(b+2) = NToACharStrings[*p][2];
|
||
b += NToACharStrings[*p][3];
|
||
*b++ = '.';
|
||
|
||
p++;
|
||
*b = NToACharStrings[*p][0];
|
||
*(b+1) = NToACharStrings[*p][1];
|
||
*(b+2) = NToACharStrings[*p][2];
|
||
b += NToACharStrings[*p][3];
|
||
*b++ = '.';
|
||
|
||
p++;
|
||
*b = NToACharStrings[*p][0];
|
||
*(b+1) = NToACharStrings[*p][1];
|
||
*(b+2) = NToACharStrings[*p][2];
|
||
b += NToACharStrings[*p][3];
|
||
*b++ = '.';
|
||
|
||
p++;
|
||
*b = NToACharStrings[*p][0];
|
||
*(b+1) = NToACharStrings[*p][1];
|
||
*(b+2) = NToACharStrings[*p][2];
|
||
b += NToACharStrings[*p][3];
|
||
*b = '\0';
|
||
|
||
return ( NO_ERROR);
|
||
|
||
} // InetNtoa()
|
||
|
||
|
||
BOOL
|
||
TcpSockSend(
|
||
IN SOCKET sock,
|
||
IN LPVOID pBuffer,
|
||
IN DWORD cbBuffer,
|
||
OUT PDWORD pcbTotalSent,
|
||
IN DWORD nTimeout
|
||
)
|
||
/*++
|
||
|
||
Description:
|
||
Do async socket send
|
||
|
||
Arguments:
|
||
sock - socket
|
||
pBuffer - buffer to send
|
||
cbBuffer - size of buffer
|
||
pcbTotalSent - bytes sent
|
||
nTimeout - timeout in seconds to use
|
||
|
||
Returns:
|
||
FALSE if there is any error.
|
||
TRUE otherwise
|
||
|
||
--*/
|
||
{
|
||
INT serr = 0;
|
||
INT cbSent;
|
||
DWORD dwBytesSent = 0;
|
||
ULONG one;
|
||
PCHAR pWin95Buffer = NULL;
|
||
|
||
DBG_ASSERT( pBuffer != NULL );
|
||
|
||
//
|
||
// On windows95, setup i/o handle to blocking mode,
|
||
// as blocking I/O is requested
|
||
//
|
||
|
||
if ( TsIsWindows95() ) {
|
||
|
||
one = 0;
|
||
ioctlsocket( sock, FIONBIO, &one );
|
||
|
||
//
|
||
// Probe for writability, if r/o, copy the buffer.
|
||
// This is a workaround for a win95 bug where static pages
|
||
// are getting dirtied when used for sends.
|
||
//
|
||
|
||
if ( IsBadWritePtr( pBuffer, 1 ) ) {
|
||
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"TcpSockSend RO[%x] detected. Doing copy.\n",pBuffer));
|
||
|
||
pWin95Buffer = (PCHAR)LocalAlloc(LMEM_FIXED, cbBuffer);
|
||
|
||
if ( pWin95Buffer != NULL ) {
|
||
|
||
CopyMemory(pWin95Buffer, pBuffer, cbBuffer);
|
||
pBuffer = pWin95Buffer;
|
||
} else {
|
||
|
||
serr = WSAENOBUFS;
|
||
goto exit;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Loop until there's no more data to send.
|
||
//
|
||
|
||
while( cbBuffer > 0 ) {
|
||
|
||
//
|
||
// Wait for the socket to become writeable.
|
||
//
|
||
|
||
serr = 0;
|
||
|
||
if ( TsIsWindows95() ) {
|
||
|
||
BOOL fWrite = FALSE;
|
||
serr = WaitForSocketWorker(
|
||
INVALID_SOCKET,
|
||
sock,
|
||
NULL,
|
||
&fWrite,
|
||
nTimeout
|
||
);
|
||
}
|
||
|
||
if( serr == 0 ) {
|
||
|
||
//
|
||
// Write a block to the socket.
|
||
//
|
||
|
||
cbSent = send( sock, (CHAR *)pBuffer, (INT)cbBuffer, 0 );
|
||
|
||
if( cbSent < 0 ) {
|
||
|
||
//
|
||
// Socket error.
|
||
//
|
||
|
||
serr = WSAGetLastError();
|
||
DBGPRINTF((DBG_CONTEXT, "TcpSockSend error %d\n",serr));
|
||
|
||
} else {
|
||
|
||
dwBytesSent += (DWORD)cbSent;
|
||
|
||
IF_DEBUG( ERROR ) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"HTTP: Synchronous send %d bytes @%p to socket %d\n",
|
||
cbSent, pBuffer, sock ));
|
||
}
|
||
}
|
||
}
|
||
|
||
if( serr != 0 ) {
|
||
break;
|
||
}
|
||
|
||
pBuffer = (LPVOID)( (LPBYTE)pBuffer + cbSent );
|
||
cbBuffer -= (DWORD)cbSent;
|
||
}
|
||
|
||
exit:
|
||
|
||
if (pcbTotalSent) {
|
||
*pcbTotalSent = dwBytesSent;
|
||
}
|
||
|
||
//
|
||
// Set up i/o handle to non-blocking mode , default for ATQ
|
||
//
|
||
|
||
if ( TsIsWindows95() ) {
|
||
one = 1;
|
||
ioctlsocket( sock, FIONBIO, &one );
|
||
|
||
if ( pWin95Buffer != NULL ) {
|
||
LocalFree(pWin95Buffer);
|
||
}
|
||
}
|
||
|
||
if ( serr == 0 ) {
|
||
return(TRUE);
|
||
} else {
|
||
|
||
IF_DEBUG( ERROR ) {
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"HTTP: Synchronous send socket error %d on socket %d.\n",
|
||
serr, sock));
|
||
}
|
||
|
||
SetLastError(serr);
|
||
return(FALSE);
|
||
}
|
||
|
||
} // SockSend
|
||
|
||
|
||
|
||
BOOL
|
||
TcpSockRecv(
|
||
IN SOCKET sock,
|
||
IN LPVOID pBuffer,
|
||
IN DWORD cbBuffer,
|
||
OUT LPDWORD pbReceived,
|
||
IN DWORD nTimeout
|
||
)
|
||
/*++
|
||
|
||
Description:
|
||
Do async socket recv
|
||
|
||
Arguments:
|
||
sock - The target socket.
|
||
pBuffer - Will receive the data.
|
||
cbBuffer - The size (in bytes) of the buffer.
|
||
pbReceived - Will receive the actual number of bytes
|
||
nTimeout - timeout in seconds
|
||
|
||
Returns:
|
||
TRUE, if successful
|
||
|
||
--*/
|
||
{
|
||
INT serr = 0;
|
||
DWORD cbTotal = 0;
|
||
INT cbReceived;
|
||
DWORD dwBytesRecv = 0;
|
||
|
||
ULONG one;
|
||
BOOL fRead = FALSE;
|
||
|
||
DBG_ASSERT( pBuffer != NULL );
|
||
DBG_ASSERT( pbReceived != NULL );
|
||
|
||
//
|
||
// Set up i/o handle to blocking mode , as blocking I/O is requested
|
||
//
|
||
|
||
if ( TsIsWindows95() ) {
|
||
one = 0;
|
||
ioctlsocket( sock, FIONBIO, &one );
|
||
}
|
||
|
||
//
|
||
// Wait for the socket to become readable.
|
||
//
|
||
|
||
serr = WaitForSocketWorker(
|
||
sock,
|
||
INVALID_SOCKET,
|
||
&fRead,
|
||
NULL,
|
||
nTimeout
|
||
);
|
||
|
||
if( serr == 0 )
|
||
{
|
||
//
|
||
// Read a block from the socket.
|
||
//
|
||
DBG_ASSERT( fRead);
|
||
|
||
cbReceived = recv( sock, (CHAR *)pBuffer, (INT)cbBuffer, 0 );
|
||
|
||
if( cbReceived < 0 )
|
||
{
|
||
//
|
||
// Socket error.
|
||
//
|
||
|
||
serr = WSAGetLastError();
|
||
}
|
||
else {
|
||
cbTotal = cbReceived;
|
||
}
|
||
}
|
||
|
||
if( serr == 0 )
|
||
{
|
||
//
|
||
// Return total byte count to caller.
|
||
//
|
||
|
||
*pbReceived = cbTotal;
|
||
}
|
||
else
|
||
{
|
||
IF_DEBUG( ERROR )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"HTTP: Syncronous rcv socket error %d during recv on socket %d\n",
|
||
serr,
|
||
sock ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set up i/o handle to blocking mode , as blocking I/O is requested
|
||
//
|
||
|
||
if ( TsIsWindows95() ) {
|
||
one = 0;
|
||
ioctlsocket( sock, FIONBIO, &one );
|
||
}
|
||
|
||
if ( serr == 0 ) {
|
||
return(TRUE);
|
||
} else {
|
||
SetLastError(serr);
|
||
return(FALSE);
|
||
}
|
||
|
||
} // SockRecv
|
||
|
||
|
||
INT
|
||
WaitForSocketWorker(
|
||
IN SOCKET sockRead,
|
||
IN SOCKET sockWrite,
|
||
IN LPBOOL pfRead,
|
||
IN LPBOOL pfWrite,
|
||
IN DWORD nTimeout
|
||
)
|
||
/*++
|
||
|
||
Description:
|
||
Wait routine
|
||
NOTES: Any (but not all) sockets may be INVALID_SOCKET. For
|
||
each socket that is INVALID_SOCKET, the corresponding
|
||
pf* parameter may be NULL.
|
||
|
||
Arguments:
|
||
sockRead - The socket to check for readability.
|
||
sockWrite - The socket to check for writeability.
|
||
pfRead - Will receive TRUE if sockRead is readable.
|
||
pfWrite - Will receive TRUE if sockWrite is writeable.
|
||
nTimeout - timeout in seconds
|
||
|
||
Returns:
|
||
SOCKERR - 0 if successful, !0 if not. Will return
|
||
WSAETIMEDOUT if the timeout period expired.
|
||
|
||
--*/
|
||
{
|
||
INT serr = 0;
|
||
TIMEVAL timeout;
|
||
LPTIMEVAL ptimeout;
|
||
fd_set fdsRead;
|
||
fd_set fdsWrite;
|
||
INT res;
|
||
|
||
//
|
||
// Ensure we got valid parameters.
|
||
//
|
||
|
||
if( ( sockRead == INVALID_SOCKET ) &&
|
||
( sockWrite == INVALID_SOCKET ) ) {
|
||
|
||
return WSAENOTSOCK;
|
||
}
|
||
|
||
timeout.tv_sec = (LONG )nTimeout;
|
||
|
||
if( timeout.tv_sec == 0 ) {
|
||
|
||
//
|
||
// If the connection timeout == 0, then we have no timeout.
|
||
// So, we block and wait for the specified conditions.
|
||
//
|
||
|
||
ptimeout = NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// The connectio timeout is > 0, so setup the timeout structure.
|
||
//
|
||
|
||
timeout.tv_usec = 0;
|
||
|
||
ptimeout = &timeout;
|
||
}
|
||
|
||
for( ; ; ) {
|
||
|
||
//
|
||
// Setup our socket sets.
|
||
//
|
||
|
||
FD_ZERO( &fdsRead );
|
||
FD_ZERO( &fdsWrite );
|
||
|
||
if( sockRead != INVALID_SOCKET ) {
|
||
|
||
FD_SET( sockRead, &fdsRead );
|
||
DBG_ASSERT( pfRead != NULL );
|
||
*pfRead = FALSE;
|
||
}
|
||
|
||
if( sockWrite != INVALID_SOCKET ) {
|
||
|
||
FD_SET( sockWrite, &fdsWrite );
|
||
DBG_ASSERT( pfWrite != NULL );
|
||
*pfWrite = FALSE;
|
||
}
|
||
|
||
//
|
||
// Wait for one of the conditions to be met.
|
||
//
|
||
|
||
res = select( 0, &fdsRead, &fdsWrite, NULL, ptimeout );
|
||
|
||
if( res == 0 ) {
|
||
|
||
//
|
||
// Timeout.
|
||
//
|
||
|
||
serr = WSAETIMEDOUT;
|
||
break;
|
||
|
||
} else if( res == SOCKET_ERROR ) {
|
||
|
||
//
|
||
// Bad news.
|
||
//
|
||
|
||
serr = WSAGetLastError();
|
||
break;
|
||
} else {
|
||
|
||
BOOL fSomethingWasSet = FALSE;
|
||
|
||
if( pfRead != NULL ) {
|
||
|
||
*pfRead = FD_ISSET( sockRead, &fdsRead );
|
||
fSomethingWasSet = TRUE;
|
||
}
|
||
|
||
if( pfWrite != NULL ) {
|
||
*pfWrite = FD_ISSET( sockWrite, &fdsWrite );
|
||
fSomethingWasSet = TRUE;
|
||
}
|
||
|
||
if( fSomethingWasSet ) {
|
||
|
||
//
|
||
// Success.
|
||
//
|
||
|
||
serr = 0;
|
||
break;
|
||
} else {
|
||
//
|
||
// select() returned with neither a timeout, nor
|
||
// an error, nor any bits set. This feels bad...
|
||
//
|
||
|
||
DBG_ASSERT( FALSE );
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
return serr;
|
||
|
||
} // WaitForSocketWorker()
|
||
|
||
|
||
BOOL
|
||
TcpSockTest(
|
||
IN SOCKET sock
|
||
)
|
||
/*++
|
||
|
||
Description:
|
||
Test the socket if still connected.
|
||
Use select, and if readable, use recv
|
||
|
||
Arguments:
|
||
sock - socket
|
||
|
||
Returns:
|
||
TRUE if the socket most likely is still connected
|
||
FALSE if the socket is disconnected or an error occured
|
||
|
||
--*/
|
||
{
|
||
TIMEVAL timeout;
|
||
fd_set fdsRead;
|
||
INT res;
|
||
CHAR bOneByte;
|
||
|
||
// select for read with zero timeout
|
||
|
||
FD_ZERO( &fdsRead );
|
||
FD_SET( sock, &fdsRead );
|
||
|
||
timeout.tv_sec = 0;
|
||
timeout.tv_usec = 0;
|
||
|
||
res = select( 0, &fdsRead, NULL, NULL, &timeout );
|
||
|
||
if ( res == 0 ) {
|
||
|
||
// No data to be read --
|
||
// have to assume socket is still connected
|
||
return TRUE;
|
||
|
||
} else if ( res == SOCKET_ERROR ) {
|
||
|
||
// Something went wrong during select -- assume disconnected
|
||
return FALSE;
|
||
}
|
||
|
||
DBG_ASSERT( res == 1 );
|
||
|
||
// recv 1 byte (PEEK)
|
||
// select returning 1 above guarantees recv will not block
|
||
|
||
res = recv( sock, &bOneByte, 1, MSG_PEEK );
|
||
|
||
if ( res == 0 || res == SOCKET_ERROR ) {
|
||
// Socket closed or an error -- socket is disconnected
|
||
return FALSE;
|
||
}
|
||
|
||
DBG_ASSERT( res == 1 );
|
||
|
||
// Read one byte successfully -- assume still connected
|
||
return TRUE;
|
||
|
||
} // SockTest
|
||
|
||
|
||
|
||
BOOL
|
||
DoSynchronousReadFile(
|
||
IN HANDLE hFile,
|
||
IN PCHAR Buffer,
|
||
IN DWORD nBuffer,
|
||
OUT PDWORD nRead,
|
||
IN LPOVERLAPPED Overlapped
|
||
)
|
||
/*++
|
||
|
||
Description:
|
||
Does Asynchronous file reads. Assumes that NT handles are
|
||
opened for OVERLAPPED I/O, win95 handles are not.
|
||
|
||
Arguments:
|
||
hFile - Handle to use for the read
|
||
Buffer - Buffer to read with
|
||
nBuffer - size of buffer
|
||
nRead - returns the number of bytes read
|
||
Overlapped - user supplied overlapped structure
|
||
|
||
Returns:
|
||
TRUE/FALSE
|
||
--*/
|
||
{
|
||
BOOL fNewEvent = FALSE;
|
||
OVERLAPPED ov;
|
||
BOOL fRet = FALSE;
|
||
|
||
if ( Overlapped == NULL ) {
|
||
|
||
Overlapped = &ov;
|
||
ov.Offset = 0;
|
||
ov.OffsetHigh = 0;
|
||
ov.hEvent = IIS_CREATE_EVENT(
|
||
"OVERLAPPED::hEvent",
|
||
&ov,
|
||
TRUE,
|
||
FALSE
|
||
);
|
||
|
||
if ( ov.hEvent == NULL ) {
|
||
DBGPRINTF((DBG_CONTEXT,"CreateEvent failed with %d\n",
|
||
GetLastError()));
|
||
goto ErrorExit;
|
||
}
|
||
|
||
fNewEvent = TRUE;
|
||
}
|
||
|
||
if ( !TsIsWindows95() ) {
|
||
|
||
DWORD err = NO_ERROR;
|
||
|
||
if ( !ReadFile( hFile,
|
||
Buffer,
|
||
nBuffer,
|
||
nRead,
|
||
Overlapped )) {
|
||
|
||
err = GetLastError();
|
||
|
||
if ( (err != ERROR_IO_PENDING) &&
|
||
(err != ERROR_HANDLE_EOF) ) {
|
||
|
||
DBGPRINTF((DBG_CONTEXT,"Error %d in ReadFile\n",
|
||
err));
|
||
|
||
goto ErrorExit;
|
||
}
|
||
}
|
||
|
||
if ( err == ERROR_IO_PENDING ) {
|
||
|
||
if ( !GetOverlappedResult( hFile,
|
||
Overlapped,
|
||
nRead,
|
||
TRUE )) {
|
||
|
||
err = GetLastError();
|
||
|
||
DBGPRINTF((DBG_CONTEXT,"Error %d in GetOverlappedResult\n",
|
||
err));
|
||
|
||
if ( err != ERROR_HANDLE_EOF ) {
|
||
goto ErrorExit;
|
||
}
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// No async file i/o for win95
|
||
//
|
||
|
||
if ( !ReadFile( hFile,
|
||
Buffer,
|
||
nBuffer,
|
||
nRead,
|
||
NULL )) {
|
||
|
||
DBGPRINTF((DBG_CONTEXT,"Error %d in ReadFile\n",
|
||
GetLastError()));
|
||
|
||
goto ErrorExit;
|
||
}
|
||
}
|
||
|
||
fRet = TRUE;
|
||
|
||
ErrorExit:
|
||
|
||
if ( fNewEvent ) {
|
||
DBG_REQUIRE(CloseHandle( ov.hEvent ));
|
||
}
|
||
|
||
return(fRet);
|
||
|
||
} // DoSynchronousReadFile
|
||
|
||
|