Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4623 lines
138 KiB

/*++
Copyright (c) 1998 - 2000 Microsoft Corporation
Module Name:
ldappx.cpp
Abstract:
Defines methods utilized by abstract data types used in LDAP portion of the H.323/LDAP proxy.
LDAP Proxy is designed as an addition to H.323 proxy. The main purpose of the
LDAP proxy is to maintain LDAP Address Translation Table, which is used to map
aliases of H.323 endpoints to their IP addresses. The proxy adds an entry when it
intercepts an LDAP PDU from a client to directory server, and the PDU matches all
predefined criteria.
Author(s): ArlieD, IlyaK 14-Jul-1999
Revision History:
07/14/1999 File creation Arlie Davis (ArlieD)
08/20/1999 Improvement of processing of LDAP Ilya Kleyman (IlyaK)
LDAP SearchRequests
12/20/1999 Added prediction of receive sizes in Ilya Kleyman (IlyaK)
non-interpretative data transfer mode
02/20/2000 Added expiration policy of the entries Ilya Kleyman (IlyaK)
in LDAP Address Translation Table
03/12/2000 Added support for multiple private and Ilya Kleyman (IlyaK)
multiple public interface for RRAS
--*/
///////////////////////////////////////////////////////////////////////////////
// //
// Include files //
// //
///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ber.h"
///////////////////////////////////////////////////////////////////////////////
// //
// Constants //
// //
///////////////////////////////////////////////////////////////////////////////
static const ANSI_STRING LdapText_C = ANSI_STRING_INIT("c");
static const ANSI_STRING LdapText_CN = ANSI_STRING_INIT("cn");
static const ANSI_STRING LdapText_ObjectClass = ANSI_STRING_INIT("objectClass");
static const ANSI_STRING LdapText_O = ANSI_STRING_INIT("o");
static const ANSI_STRING LdapText_OU = ANSI_STRING_INIT("ou");
static const ANSI_STRING LdapText_RTPerson = ANSI_STRING_INIT("RTPerson");
static const ANSI_STRING LdapText_Attribute_sipaddress = ANSI_STRING_INIT("sipaddress");
static const ANSI_STRING LdapText_Attribute_ipAddress = ANSI_STRING_INIT("ipAddress");
static const ANSI_STRING LdapText_Attribute_sttl = ANSI_STRING_INIT("sttl");
static const ANSI_STRING LdapText_Attribute_comment = ANSI_STRING_INIT("comment");
static const ANSI_STRING LdapText_Modify_EntryTTL = ANSI_STRING_INIT("EntryTTL");
static const ANSI_STRING LdapText_GeneratedByTAPI = ANSI_STRING_INIT("Generated by TAPI3");
static const ANSI_STRING LdapText_ModifiedByICS = ANSI_STRING_INIT("Made possible by ICS");
static const ANSI_STRING LdapText_TableSizeExceededMessage = ANSI_STRING_INIT("Resources on proxy used up.");
///////////////////////////////////////////////////////////////////////////////
// //
// Global Variables //
// //
///////////////////////////////////////////////////////////////////////////////
SYNC_COUNTER LdapSyncCounter;
LDAP_CONNECTION_ARRAY LdapConnectionArray;
LDAP_TRANSLATION_TABLE LdapTranslationTable;
LDAP_CODER LdapCoder;
LDAP_ACCEPT LdapAccept;
SOCKADDR_IN LdapListenSocketAddress;
DWORD EnableLocalH323Routing;
// utility functions ------------------------------------------------------------------
#if DBG
static BOOL BerDumpStopFn (VOID)
{
return FALSE;
}
static void BerDumpOutputFn (char * Format, ...)
{
if (DebugLevel > 0) {
va_list Va;
CHAR Text [0x200];
va_start (Va, Format);
_vsnprintf (Text, 0x200, Format, Va);
va_end (Va);
OutputDebugStringA (Text);
}
}
static void BerDump (IN LPBYTE Data, IN DWORD Length)
{
ber_decode (BerDumpOutputFn, BerDumpStopFn, Data,
0, // DECODE_NEST_OCTET_STRINGS,
0, 0, Length, 0);
}
#endif // DBG
// LdapQueryTable queries the LDAP translation table for a given alias.
// The alias was one that was previously registered by a LDAP endpoint.
// We do not care about the type of the alias (h323_ID vs emailID, etc.) --
// the semantics of the alias type are left to the Q.931 code.
//
// returns S_OK on success
// returns S_FALSE if no entry was found
// returns an error code if an actual error occurred.
HRESULT LdapQueryTableByAlias (
IN ANSI_STRING * Alias,
OUT DWORD * ReturnClientAddress) // host order
{
HRESULT Result;
IN_ADDR Address;
assert (Alias);
assert (ReturnClientAddress);
Result = LdapTranslationTable.QueryTableByAlias (Alias, &Address);
if (Result == S_OK) {
*ReturnClientAddress = ntohl (Address.s_addr);
return Result;
}
Result = LdapTranslationTable.QueryTableByCN (Alias, &Address);
if (Result == S_OK) {
*ReturnClientAddress = ntohl (Address.s_addr);
return Result;
}
return Result;
}
HRESULT LdapQueryTableByAliasServer (
IN ANSI_STRING * Alias,
IN SOCKADDR_IN * ServerAddress,
OUT DWORD * ReturnClientAddress) // host order
{
HRESULT Result;
IN_ADDR Address;
assert (Alias);
assert (ReturnClientAddress);
Result = LdapTranslationTable.QueryTableByAliasServer (Alias, ServerAddress, &Address);
if (Result == S_OK) {
*ReturnClientAddress = ntohl (Address.s_addr);
return Result;
}
Result = LdapTranslationTable.QueryTableByCNServer (Alias, ServerAddress, &Address);
if (Result == S_OK) {
*ReturnClientAddress = ntohl (Address.s_addr);
return Result;
}
return Result;
}
#if DBG
void LdapPrintTable (void) {
LdapTranslationTable.PrintTable ();
}
#endif // DBG
static DWORD LdapDeterminePacketBoundary (
IN LDAP_BUFFER * Buffer,
IN DWORD PacketOffset,
OUT DWORD * NextPacketOffset, // Points to the beginning of next packet only if function returns ERROR_SUCCESS
OUT DWORD * NextReceiveSize) // Is only meaningful when function returns any value other than ERROR_SUCCESS
{
DWORD PayloadLength;
DWORD ASNHeaderLength = ASN_MIN_HEADER_LEN;
DWORD PacketSize;
DWORD ByteIndex;
DWORD Length;
LPBYTE Data;
assert (Buffer);
assert (Buffer -> Data.Data);
Length = Buffer -> Data.Length - PacketOffset;
Data = Buffer -> Data.Data;
// Pick reasonable default for the size of
// next receive request. Will be changed if necessary
*NextReceiveSize = LDAP_BUFFER_RECEIVE_SIZE;
if (Length != 0) {
if (Data [PacketOffset] == ASN_SEQUENCE_TAG) {
if (Length >= ASN_MIN_HEADER_LEN) {
if (Data [PacketOffset + 1] & ASN_LONG_HEADER_BIT) {
// Long (more than ASN_MIN_HEADER_LEN bytes) ASN header
// Size of the payload length field is indicated in the
// second nybble of second byte
ASNHeaderLength += Data [PacketOffset + 1] & ~ASN_LONG_HEADER_BIT;
// This is where the limit on payload length is established.
// The test below assures it won't be greater than 2 ^ sizeof (DWORD) (4 GBytes)
if (ASNHeaderLength <= ASN_MIN_HEADER_LEN + sizeof (DWORD)) {
if (Length >= ASNHeaderLength) {
PayloadLength = 0;
for (ByteIndex = ASN_MIN_HEADER_LEN;
ByteIndex < ASNHeaderLength;
ByteIndex++) {
PayloadLength *= 1 << CHAR_BIT;
PayloadLength += (DWORD) Data [PacketOffset + ByteIndex];
}
} else {
// Not enough data to even read the ASN header
return ERROR_MORE_DATA;
}
} else {
DebugF (_T("LDAP: Payload size field (%d bytes) is too big.\n"), ASNHeaderLength - ASN_MIN_HEADER_LEN);
return ERROR_INVALID_DATA;
}
} else {
// Short (Exactly ASN_MIN_HEADER_LEN bytes) ASN header
// Payload length is indicated in the second byte
PayloadLength = (DWORD) Data [PacketOffset + 1];
}
PacketSize = ASNHeaderLength + PayloadLength;
if (Length >= PacketSize) {
*NextPacketOffset = PacketOffset + PacketSize;
return ERROR_SUCCESS;
} else {
*NextReceiveSize = PacketSize - Length;
}
}
} else {
Debug (_T("LDAP: Failed to find ASN sequence tag.\n"));
return ERROR_INVALID_DATA;
}
}
return ERROR_MORE_DATA;
}
static BOOL FindChar (
IN ANSI_STRING * String,
IN CHAR Char,
OUT USHORT * ReturnIndex)
{
LPSTR Pos;
LPSTR End;
assert (String);
assert (ReturnIndex);
Pos = String -> Buffer;
End = String -> Buffer + String -> Length / sizeof (CHAR);
for (; Pos < End; Pos++) {
if (*Pos == Char) {
*ReturnIndex = (USHORT) (Pos - String -> Buffer);
return TRUE;
}
}
return FALSE;
}
static void ParseDirectoryPathElement (
IN ANSI_STRING * Element,
IN OUT LDAP_PATH_ELEMENTS * PathElements)
{
ANSI_STRING Tag;
ANSI_STRING Value;
USHORT Index;
if (FindChar (Element, LDAP_PATH_EQUAL_CHAR, &Index)) {
assert (Index * sizeof (CHAR) < Element -> Length);
Tag.Buffer = Element -> Buffer;
Tag.Length = Index * sizeof (CHAR);
Index++; // step over separator
Value.Buffer = Element -> Buffer + Index;
Value.Length = Element -> Length - Index * sizeof (CHAR);
if (RtlEqualStringConst (&Tag, &LdapText_C, TRUE))
PathElements -> C = Value;
else if (RtlEqualStringConst (&Tag, &LdapText_CN, TRUE))
PathElements -> CN = Value;
else if (RtlEqualStringConst (&Tag, &LdapText_ObjectClass, TRUE))
PathElements -> ObjectClass = Value;
else if (RtlEqualStringConst (&Tag, &LdapText_O, TRUE))
PathElements -> O = Value;
}
}
static void ParseDirectoryPath (
IN ANSI_STRING * DirectoryPath,
OUT LDAP_PATH_ELEMENTS * ReturnData)
{
ANSI_STRING SubString;
USHORT Index;
ANSI_STRING Element;
assert (DirectoryPath);
assert (ReturnData);
assert (DirectoryPath -> Buffer);
ZeroMemory (ReturnData, sizeof (LDAP_PATH_ELEMENTS));
SubString = *DirectoryPath;
while (FindChar (&SubString, LDAP_PATH_SEP_CHAR, &Index)) {
assert (Index * sizeof (CHAR) < SubString.Length);
Element.Buffer = SubString.Buffer;
Element.Length = Index * sizeof (CHAR);
Index++; // step over separator
SubString.Buffer += Index;
SubString.Length -= Index * sizeof (CHAR);
ParseDirectoryPathElement (&Element, ReturnData);
}
ParseDirectoryPathElement (&SubString, ReturnData);
}
static void ParseObjectNameElement (
IN ANSI_STRING * Element,
IN OUT LDAP_OBJECT_NAME_ELEMENTS * ObjectNameElements)
{
ANSI_STRING Tag;
ANSI_STRING Value;
USHORT Index;
if (FindChar (Element, LDAP_PATH_EQUAL_CHAR, &Index)) {
assert (Index * sizeof (CHAR) < Element -> Length);
Tag.Buffer = Element -> Buffer;
Tag.Length = Index * sizeof (CHAR);
Index++; // step over separator
Value.Buffer = Element -> Buffer + Index;
Value.Length = Element -> Length - Index * sizeof (CHAR);
if (RtlEqualStringConst (&Tag, &LdapText_CN, TRUE))
ObjectNameElements -> CN = Value;
else if (RtlEqualStringConst (&Tag, &LdapText_O, TRUE))
ObjectNameElements -> O = Value;
else if (RtlEqualStringConst (&Tag, &LdapText_OU, TRUE))
ObjectNameElements -> OU = Value;
}
}
static void ParseObjectName (
IN ANSI_STRING * ObjectName,
OUT LDAP_OBJECT_NAME_ELEMENTS * ReturnData)
{
ANSI_STRING SubString;
USHORT Index;
ANSI_STRING Element;
assert (ObjectName);
assert (ReturnData);
assert (ObjectName -> Buffer);
ZeroMemory (ReturnData, sizeof (LDAP_OBJECT_NAME_ELEMENTS));
SubString = *ObjectName;
while (FindChar (&SubString, LDAP_PATH_SEP_CHAR, &Index)) {
assert (Index * sizeof (CHAR) < SubString.Length);
Element.Buffer = SubString.Buffer;
Element.Length = Index * sizeof (CHAR);
Index++; // step over separator
SubString.Buffer += Index;
SubString.Length -= Index * sizeof (CHAR);
ParseObjectNameElement (&Element, ReturnData);
}
ParseObjectNameElement (&SubString, ReturnData);
}
// LDAP_TRANSLATION_ENTRY ------------------------------------------------
HRESULT
LDAP_TRANSLATION_ENTRY::IsRegisteredViaInterface (
IN DWORD InterfaceAddress, // host order
OUT BOOL *Result
)
/*++
Routine Description:
Determines whether the entry is registered via the
interface specified
Arguments:
InterfaceAddress - address of the interface for which
the determination is to be made.
Result (out) - TRUE if entry was registered via the interface
FALSE if entry was not registered via the interface
Return Values:
TRUE - if determination succeeded
FALSE - if determination failed
Notes:
--*/
{
DWORD BestInterfaceAddress;
ULONG Error;
Error = GetBestInterfaceAddress (ntohl (ClientAddress.s_addr), &BestInterfaceAddress);
*Result = FALSE;
if (ERROR_SUCCESS == Error) {
*Result = (BestInterfaceAddress == InterfaceAddress);
}
return HRESULT_FROM_WIN32 (Error);
}
// LDAP_TRANSLATION_TABLE ------------------------------------------------
LDAP_TRANSLATION_TABLE::LDAP_TRANSLATION_TABLE (void)
{
IsEnabled = FALSE;
GarbageCollectorTimerHandle = NULL;
}
LDAP_TRANSLATION_TABLE::~LDAP_TRANSLATION_TABLE (void)
{
assert (!IsEnabled);
assert (Array.Length == 0);
}
void LDAP_TRANSLATION_TABLE::Stop (void)
{
HRESULT Result;
Lock();
IsEnabled = FALSE;
if (GarbageCollectorTimerHandle) {
if (DeleteTimerQueueTimer(NATH323_TIMER_QUEUE,
GarbageCollectorTimerHandle,
INVALID_HANDLE_VALUE))
{
DebugF (_T("LDAP: Garbage collection is deactivated.\n"));
}
else {
Result = GetLastError ();
DebugError (Result, _T("LDAP: Could not deactivate garbage collection.\n"));
}
GarbageCollectorTimerHandle = NULL;
}
Array.Free();
Unlock ();
}
HRESULT LDAP_TRANSLATION_TABLE::Start (void)
{
HRESULT Result;
Lock ();
assert (!GarbageCollectorTimerHandle);
if (CreateTimerQueueTimer(&GarbageCollectorTimerHandle,
NATH323_TIMER_QUEUE,
GarbageCollectorCallback,
this,
LDAP_TRANSLATION_TABLE_GARBAGE_COLLECTION_PERIOD,
LDAP_TRANSLATION_TABLE_GARBAGE_COLLECTION_PERIOD, // periodic timer
WT_EXECUTEINIOTHREAD)) {
DebugF (_T("LDAP: Successfully activated garbage collection.\n"));
IsEnabled = TRUE;
Result = S_OK;
}
else {
Result = GetLastError ();
DebugLastError (_T("LDAP: Failed to activate garbage collection.\n"));
}
Unlock ();
return Result;
}
// static
void LDAP_TRANSLATION_TABLE::GarbageCollectorCallback (
PVOID Context,
BOOLEAN TimerOrWaitFired)
{
LDAP_TRANSLATION_TABLE * Table;
Table = (LDAP_TRANSLATION_TABLE *) Context;
Table -> RemoveOldEntries ();
}
HRESULT LDAP_TRANSLATION_TABLE::RefreshEntry (
IN ANSI_STRING * Alias,
IN ANSI_STRING * DirectoryPath,
IN IN_ADDR ClientAddress,
IN SOCKADDR_IN * ServerAddress,
IN DWORD TimeToLive) // in seconds
{
DebugF (_T("LDAP: Refreshing local entry for (%.*S) @ %08X:%04X.\n"),
ANSI_STRING_PRINTF (Alias),
SOCKADDR_IN_PRINTF (ServerAddress));
return InsertEntry (Alias, DirectoryPath, ClientAddress, ServerAddress, TimeToLive);
}
void LDAP_TRANSLATION_TABLE::RemoveOldEntries (void)
{
DWORD CurrentTime;
DWORD Index;
Lock ();
if (IsEnabled) {
CurrentTime = GetTickCount () / 1000;
DebugF (_T("LDAP: Garbage collection commenced at %d.\n"), CurrentTime);
Index = 0;
while (Index < Array.Length) {
if (CurrentTime > Array [Index].TimeStamp) {
DebugF (_T("LDAP: Expiring entry @%d, alias -- (%.*S) from translation table.\n"),
Index, ANSI_STRING_PRINTF (&Array [Index].Alias));
Array[Index].FreeContents ();
Array.DeleteAtPos (Index);
InterfaceArray.StopQ931ReceiveRedirects ();
} else {
Index++;
}
}
DebugF (_T("LDAP: Garbage collection completed.\n"));
}
Unlock ();
}
HRESULT LDAP_TRANSLATION_TABLE::QueryTableByAlias (
IN ANSI_STRING * Alias,
OUT IN_ADDR * ReturnClientAddress)
{
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
DWORD Index;
HRESULT Result;
assert (Alias);
assert (ReturnClientAddress);
Lock();
if (IsEnabled) {
Result = S_FALSE;
Array.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
if (RtlEqualStringConst (&Pos -> Alias, Alias, TRUE)) {
*ReturnClientAddress = Pos -> ClientAddress;
Result = S_OK;
break;
}
}
}
else {
Result = S_FALSE;
}
Unlock();
return Result;
}
HRESULT LDAP_TRANSLATION_TABLE::QueryTableByCN (
IN ANSI_STRING * CN,
OUT IN_ADDR * ReturnClientAddress)
{
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
HRESULT Result;
assert (CN);
assert (ReturnClientAddress);
Lock();
if (IsEnabled) {
Result = S_FALSE;
Array.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
if (RtlEqualStringConst (&Pos -> CN, CN, TRUE)) {
*ReturnClientAddress = Pos -> ClientAddress;
Result = S_OK;
break;
}
}
}
else {
Result = S_FALSE;
}
Unlock();
return Result;
}
HRESULT LDAP_TRANSLATION_TABLE::QueryTableByAliasServer (
IN ANSI_STRING * Alias,
IN SOCKADDR_IN * ServerAddress,
OUT IN_ADDR * ReturnClientAddress)
{
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
DWORD Index;
HRESULT Result;
BOOL ServerIsSame;
BOOL AliasIsSame;
assert (Alias);
assert (ServerAddress);
assert (ReturnClientAddress);
Lock();
if (IsEnabled) {
Result = S_FALSE;
Array.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
AliasIsSame = RtlEqualStringConst (&Pos -> Alias, Alias, TRUE);
ServerIsSame = (ServerAddress -> sin_addr.s_addr == Pos -> ServerAddress.sin_addr.s_addr) // addresses are literally equal
||
( ::NhIsLocalAddress (ServerAddress -> sin_addr.s_addr)
&& ::NhIsLocalAddress (Pos -> ServerAddress.sin_addr.s_addr)); // two addresses of the local machine
if (AliasIsSame && ServerIsSame) {
*ReturnClientAddress = Pos -> ClientAddress;
Result = S_OK;
break;
}
}
}
else {
Result = S_FALSE;
}
Unlock();
return Result;
}
HRESULT LDAP_TRANSLATION_TABLE::QueryTableByCNServer (
IN ANSI_STRING * CN,
IN SOCKADDR_IN * ServerAddress,
OUT IN_ADDR * ReturnClientAddress)
{
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
HRESULT Result;
BOOL ServerIsSame;
BOOL CN_IsSame;
assert (CN);
assert (ServerAddress);
assert (ReturnClientAddress);
Lock();
if (IsEnabled) {
Result = S_FALSE;
Array.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
CN_IsSame = RtlEqualStringConst (&Pos -> CN, CN, TRUE);
ServerIsSame = (ServerAddress -> sin_addr.s_addr == Pos -> ServerAddress.sin_addr.s_addr) // addresses are literally equal
||
( ::NhIsLocalAddress (ServerAddress -> sin_addr.s_addr)
&& ::NhIsLocalAddress (Pos -> ServerAddress.sin_addr.s_addr)); // two addresses of the local machine
if (CN_IsSame && ServerIsSame) {
*ReturnClientAddress = Pos -> ClientAddress;
Result = S_OK;
break;
}
}
}
else {
Result = S_FALSE;
}
Unlock();
return Result;
}
#if DBG
void LDAP_TRANSLATION_TABLE::PrintTable (void)
{
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
DebugF (_T("LDAP: Printing out Address Translation Table.\n"));
Lock();
if (IsEnabled) {
Array.GetExtents (&Pos, &End);
DebugF (_T("\n"));
for (; Pos < End; Pos++) {
DebugF (_T("\tEntry at %x:\n"), Pos);
DebugF (_T ("\t\tAlias - %.*S\n"),
ANSI_STRING_PRINTF (&Pos -> Alias));
DebugF (_T ("\t\tDirectoryPath - %.*S\n"),
ANSI_STRING_PRINTF (&Pos -> DirectoryPath));
DebugF (_T ("\t\tCN - %.*S\n"),
ANSI_STRING_PRINTF (&Pos -> CN));
DebugF (_T("\t\tClientAddress - %x\n"), ntohl(Pos->ClientAddress.s_addr));
DebugF (_T("\t\tServerAddress - %x:%x\n"),
SOCKADDR_IN_PRINTF (&Pos -> ServerAddress));
DebugF (_T("\t\tTimeStamp - %u\n"), Pos -> TimeStamp);
}
DebugF (_T("\n"));
}
Unlock();
}
#endif
HRESULT LDAP_TRANSLATION_TABLE::InsertEntry (
IN ANSI_STRING * Alias,
IN ANSI_STRING * DirectoryPath,
IN IN_ADDR ClientAddress,
IN SOCKADDR_IN * ServerAddress,
IN DWORD TimeToLive) // in seconds
{
HRESULT Result;
assert (Alias);
assert (Alias -> Buffer);
assert (DirectoryPath);
assert (DirectoryPath -> Buffer);
assert (ServerAddress);
Lock();
Result = InsertEntryLocked (Alias, DirectoryPath, ClientAddress, ServerAddress, TimeToLive);
Unlock();
#if DBG
if (DebugLevel > 1)
{
LdapPrintTable ();
}
#endif // DBG
return Result;
}
HRESULT LDAP_TRANSLATION_TABLE::FindEntryByPathServer (
IN ANSI_STRING * DirectoryPath,
IN SOCKADDR_IN * ServerAddress,
OUT LDAP_TRANSLATION_ENTRY ** ReturnTranslationEntry)
{
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
HRESULT Result;
Result = S_FALSE;
Array.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
if (RtlEqualStringConst (&Pos -> DirectoryPath, DirectoryPath, TRUE)
&& IsEqualSocketAddress (&Pos -> ServerAddress, ServerAddress)) {
*ReturnTranslationEntry = Pos;
Result = S_OK;
break;
}
}
return Result;
}
HRESULT LDAP_TRANSLATION_TABLE::FindEntryByAliasServer (
IN ANSI_STRING * Alias,
IN SOCKADDR_IN * ServerAddress,
OUT LDAP_TRANSLATION_ENTRY ** ReturnTranslationEntry)
{
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
HRESULT Result;
Result = S_FALSE;
Array.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
if (RtlEqualStringConst (&Pos -> Alias, Alias, TRUE)
// && IsEqualSocketAddress (&Pos -> ServerAddress, ServerAddress)) {
&& Pos -> ServerAddress.sin_addr.s_addr == ServerAddress -> sin_addr.s_addr) {
*ReturnTranslationEntry = Pos;
Result = S_OK;
break;
}
}
return Result;
}
HRESULT LDAP_TRANSLATION_TABLE::InsertEntryLocked (
IN ANSI_STRING * Alias,
IN ANSI_STRING * DirectoryPath,
IN IN_ADDR ClientAddress,
IN SOCKADDR_IN * ServerAddress,
IN DWORD TimeToLive) // in seconds
{
LDAP_TRANSLATION_ENTRY * TranslationEntry;
LDAP_PATH_ELEMENTS PathElements;
HRESULT Result;
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
assert (Alias);
assert (DirectoryPath);
assert (ServerAddress);
if (!IsEnabled)
return S_FALSE;
// locate any existing entry
// the identity of the entry is determined by the tuple:
// < ServerAddress ClientAlias >
if (FindEntryByAliasServer (Alias, ServerAddress, &TranslationEntry) == S_OK) {
Debug (_T("LDAP: Replacing existing translation entry.\n"));
TranslationEntry -> FreeContents();
}
else {
Debug (_T("LDAP: Allocating new translation entry.\n"));
TranslationEntry = Array.AllocAtEnd();
if (!TranslationEntry) {
Debug (_T("LDAP: Failed to allocate translation entry.\n"));
return E_OUTOFMEMORY;
}
}
TranslationEntry -> ClientAddress = ClientAddress;
TranslationEntry -> ServerAddress = *ServerAddress;
TranslationEntry -> TimeStamp = GetTickCount () / 1000 + TimeToLive;
// copy the strings
CopyAnsiString (Alias, &TranslationEntry -> Alias);
CopyAnsiString (DirectoryPath, &TranslationEntry -> DirectoryPath);
if (TranslationEntry -> DirectoryPath.Buffer) {
ParseDirectoryPath (&TranslationEntry -> DirectoryPath, &PathElements);
if (PathElements.CN.Buffer) {
TranslationEntry -> CN = PathElements.CN;
}
else {
Debug (_T("LDAP: Cannot insert translation entry -- CN is not specified.\n"));
TranslationEntry -> CN.Buffer = NULL;
}
}
else {
TranslationEntry -> CN.Buffer = NULL;
}
// test and make sure all allocation code paths succeeded
if (TranslationEntry -> Alias.Buffer
&& TranslationEntry -> DirectoryPath.Buffer
&& TranslationEntry -> CN.Buffer) {
Result = S_OK;
} else {
Debug (_T("LDAP: Failed to allocate memory (or failed to find CN).\n"));
FreeAnsiString (&TranslationEntry -> Alias);
FreeAnsiString (&TranslationEntry -> DirectoryPath);
Array.DeleteEntry (TranslationEntry);
Result = E_OUTOFMEMORY;
}
return Result;
}
HRESULT LDAP_TRANSLATION_TABLE::RemoveEntry (
IN SOCKADDR_IN * ServerAddress,
IN ANSI_STRING * DirectoryPath)
{
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
HRESULT Result;
Lock();
assert (ServerAddress);
assert (DirectoryPath);
Result = S_FALSE;
Array.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
if (RtlEqualString (DirectoryPath, &Pos -> DirectoryPath, TRUE)
&& Compare_SOCKADDR_IN (ServerAddress, &Pos -> ServerAddress) == 0) {
Pos -> FreeContents();
Array.DeleteEntry (Pos);
InterfaceArray.StopQ931ReceiveRedirects ();
Result = S_OK;
break;
}
}
Unlock();
return Result;
}
HRESULT LDAP_TRANSLATION_TABLE::RemoveEntryByAliasServer (
IN ANSI_STRING * Alias,
IN SOCKADDR_IN * ServerAddress)
{
LDAP_TRANSLATION_ENTRY * Pos;
LDAP_TRANSLATION_ENTRY * End;
HRESULT Result;
BOOL AliasIsSame;
BOOL ServerIsSame;
Lock ();
assert (Alias);
Result = S_FALSE;
Array.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
AliasIsSame = RtlEqualStringConst (&Pos -> Alias, Alias, TRUE);
ServerIsSame = (ServerAddress -> sin_addr.s_addr == Pos -> ServerAddress.sin_addr.s_addr) // addresses are literally equal
||
( ::NhIsLocalAddress (ServerAddress -> sin_addr.s_addr)
&& ::NhIsLocalAddress (Pos -> ServerAddress.sin_addr.s_addr)); // two addresses of the local machine
if (AliasIsSame && ServerIsSame) {
Pos -> FreeContents();
Array.DeleteEntry (Pos);
InterfaceArray.StopQ931ReceiveRedirects ();
Result = S_OK;
break;
}
}
Unlock ();
return Result;
}
void
LDAP_TRANSLATION_TABLE::OnInterfaceShutdown (
IN DWORD InterfaceAddress
)
/*++
Routine Description:
Removes all entries registered by the clients reachable
thorough the interface specified, except for entries registered by
a local client.
Arguments:
InterfaceAddress - address of the interface for which
the determination is to be made.
Return Values:
None
Notes:
--*/
{
DWORD ArrayIndex = 0;
LDAP_TRANSLATION_ENTRY * Entry;
BOOL IsEntryToBeDeleted;
HRESULT Result;
Lock ();
if (IsEnabled) {
DebugF (_T("LDAP: Forcibly removing non-local translation entries registered via %08X.\n"), InterfaceAddress);
while (ArrayIndex < Array.GetLength ()) {
Entry = &Array [ArrayIndex];
Result = Entry -> IsRegisteredViaInterface (InterfaceAddress, &IsEntryToBeDeleted);
// Don't delete the entry if it was registered by a local client. This is because
// the client will still be available for H.323 calls.
IsEntryToBeDeleted = IsEntryToBeDeleted && !::NhIsLocalAddress (Entry -> ClientAddress.s_addr);
if (S_OK == Result) {
if (IsEntryToBeDeleted) {
DebugF (_T("LDAP: Forcibly removing entry (%.*S:%08X) @ %08X:%04X.\n"),
ANSI_STRING_PRINTF (&Entry -> Alias),
ntohl (Entry -> ClientAddress.s_addr),
SOCKADDR_IN_PRINTF (&Entry -> ServerAddress));
Entry -> FreeContents();
Array.DeleteEntry (Entry);
InterfaceArray.StopQ931ReceiveRedirects ();
} else {
ArrayIndex++;
}
} else {
// There probably was something wrong with just this entry. Skip it and continue
// searching for entries registered via the interface
ArrayIndex++;
DebugF (_T("LDAP: Failed to determine whether entry (%.*S:%08X) @ %08X:%04X was registered via interface %08X. Error=0x%x\n"),
ANSI_STRING_PRINTF (&Entry -> Alias),
ntohl (Entry -> ClientAddress.s_addr),
SOCKADDR_IN_PRINTF (&Entry -> ServerAddress),
InterfaceAddress,
Result);
}
}
}
Unlock ();
} // LDAP_TRANSLATION_TABLE::RemoveEntriesForClientsOnInterface
BOOL LDAP_TRANSLATION_TABLE::ReachedMaximumSize (void) {
DWORD NumberOfEntries;
Lock ();
NumberOfEntries = Array.Length;
Unlock ();
return NumberOfEntries >= LDAP_MAX_TRANSLATION_TABLE_SIZE;
}
// LDAP_SOCKET ----------------------------------------------
LDAP_SOCKET::LDAP_SOCKET (
IN LDAP_CONNECTION * ArgLdapConnection,
IN LDAP_PUMP * ArgRecvPump,
IN LDAP_PUMP * ArgSendPump)
{
assert (ArgLdapConnection);
assert (ArgRecvPump);
assert (ArgSendPump);
LdapConnection = ArgLdapConnection;
RecvPump = ArgRecvPump;
SendPump = ArgSendPump;
State = STATE_NONE;
BytesToReceive = LDAP_BUFFER_RECEIVE_SIZE;
Socket = INVALID_SOCKET;
ZeroMemory (&RecvOverlapped, sizeof RecvOverlapped);
RecvOverlapped.Socket = this;
RecvBuffer = NULL;
InitializeListHead (&RecvBufferQueue);
ZeroMemory (&SendOverlapped, sizeof SendOverlapped);
SendOverlapped.Socket = this;
SendBuffer = NULL;
InitializeListHead (&SendBufferQueue);
ConnectEvent = NULL;
ConnectWaitHandle = NULL;
AttemptAnotherConnect = TRUE;
IsNatRedirectActive = FALSE;
}
LDAP_SOCKET::~LDAP_SOCKET (void)
{
DeleteBufferList (&RecvBufferQueue);
DeleteBufferList (&SendBufferQueue);
if (RecvBuffer) {
delete RecvBuffer;
RecvBuffer = NULL;
}
assert (IsListEmpty (&RecvBufferQueue));
assert (IsListEmpty (&SendBufferQueue));
assert (!SendBuffer);
assert (!ConnectEvent);
assert (!ConnectWaitHandle);
}
void LDAP_SOCKET::DeleteBufferList (LIST_ENTRY * ListHead)
{
LIST_ENTRY * ListEntry;
LDAP_BUFFER * Buffer;
while (!IsListEmpty (ListHead)) {
ListEntry = RemoveHeadList (ListHead);
Buffer = CONTAINING_RECORD (ListEntry, LDAP_BUFFER, ListEntry);
delete Buffer;
}
}
BOOL LDAP_SOCKET::RecvRemoveBuffer (
OUT LDAP_BUFFER ** ReturnBuffer)
{
LIST_ENTRY * ListEntry;
assert (ReturnBuffer);
if (IsListEmpty (&RecvBufferQueue))
return FALSE;
else {
ListEntry = RemoveHeadList (&RecvBufferQueue);
*ReturnBuffer = CONTAINING_RECORD (ListEntry, LDAP_BUFFER, ListEntry);
return TRUE;
}
}
void LDAP_SOCKET::RecvBuildBuffer (
IN LPBYTE Data,
IN DWORD Length)
{
LDAP_BUFFER * Buffer;
assert (Data);
AssertLocked();
Buffer = new LDAP_BUFFER;
if (!Buffer) {
Debug (_T("LDAP: RecvBuildBuffer, allocation failure #1.\n"));
return;
}
if (Buffer -> Data.Grow (Length)) {
memcpy (Buffer -> Data.Data, Data, Length);
Buffer -> Data.Length = Length;
InsertTailList (&RecvBufferQueue, &Buffer -> ListEntry);
}
else {
Debug (_T("LDAP: RecvBuildBuffer, allocation failure #2.\n"));
delete Buffer;
}
}
HRESULT LDAP_SOCKET::AcceptSocket (
SOCKET LocalClientSocket)
{
if (State != STATE_NONE) {
Debug (_T("LDAP: Not in a valid state for AcceptSocket (State != STATE_NONE).\n"));
return E_UNEXPECTED;
}
State = STATE_CONNECTED;
Socket = LocalClientSocket;
// notify parent about state change
LdapConnection -> OnStateChange (this, State);
if (!BindIoCompletionCallback ((HANDLE) Socket, LDAP_SOCKET::IoCompletionCallback, 0)) {
DebugLastError (_T("LDAP: Failed to bind I/O completion callback.\n"));
return GetLastErrorAsResult ();
}
return S_OK;
}
HRESULT LDAP_SOCKET::IssueConnect (
SOCKADDR_IN * DestinationAddress)
{
HRESULT Status;
HRESULT Result;
ULONG Error;
INT RealSourceAddrSize = sizeof (SOCKADDR_IN);
DWORD BestInterfaceAddress; // host order
int ConnectError;
BOOL KeepaliveOption;
assert (DestinationAddress);
if (State != STATE_NONE) {
Debug (_T("LDAP: Not in a valid state for IssueConnect (State != STATE_NONE).\n"));
return E_UNEXPECTED;
}
assert (Socket == INVALID_SOCKET);
assert (!ConnectEvent);
assert (!ConnectWaitHandle);
ActualDestinationAddress = *DestinationAddress;
// If ILS runs on a remote (public) machine, we need to determine on which public
// interface we will connect to the server. This is so to override global interface-restricted
// NAT redirect by creating a trivial NAT redirect to the server's address from
// the address of the public interface determined.
//
// If server happens to run on the local machine, then we use loopback address
// as this is the address from where we will be "connecting" to the server.
if (!::NhIsLocalAddress (DestinationAddress -> sin_addr.s_addr)) {
Error = GetBestInterfaceAddress (
ntohl (DestinationAddress -> sin_addr.s_addr),
&BestInterfaceAddress);
if (ERROR_SUCCESS != Error) {
Result = HRESULT_FROM_WIN32 (Error);
DebugErrorF (Error, _T("LDAP: Failed to get best interface address for %08X.\n"),
ntohl (DestinationAddress -> sin_addr.s_addr));
return Result;
}
} else {
BestInterfaceAddress = INADDR_LOOPBACK;
}
RealSourceAddress.sin_family = AF_INET;
RealSourceAddress.sin_addr.s_addr = htonl (BestInterfaceAddress);
RealSourceAddress.sin_port = htons (0);
Socket = WSASocket (AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (Socket == INVALID_SOCKET) {
Result = GetLastErrorAsResult ();
DebugLastError (_T("LDAP: Failed to create destination socket.\n"));
} else {
// At this point we actually start the connect procedures. Everything before that
// was just a preparation, so the socket stayed in the STATE_NONE.
State = STATE_ISSUING_CONNECT;
if (SOCKET_ERROR == bind(Socket, (PSOCKADDR)&RealSourceAddress, RealSourceAddrSize)) {
Result = GetLastErrorAsResult();
DebugLastError (_T("LDAP: Failed to bind destination socket.\n"));
} else {
// Set keepalive on the socket
KeepaliveOption = TRUE;
if (SOCKET_ERROR == setsockopt (Socket, SOL_SOCKET, SO_KEEPALIVE,
(PCHAR) &KeepaliveOption, sizeof (KeepaliveOption)))
{
Result = GetLastErrorAsResult ();
DebugLastError (_T("LDAP: Failed to set keepalive on destination socket.\n"));
} else {
if (getsockname (Socket, (struct sockaddr *)&RealSourceAddress, &RealSourceAddrSize)) {
Result = GetLastErrorAsResult ();
DebugLastError (_T("LDAP: Failed to get name of TCP socket.\n"));
} else {
DebugF (_T("LDAP: 0x%x setting up trivial redirect (%08X:%04X -> %08X:%04X) => (%08X:%04X -> %08X:%04X).\n"),
LdapConnection,
SOCKADDR_IN_PRINTF(&RealSourceAddress), SOCKADDR_IN_PRINTF(DestinationAddress),
SOCKADDR_IN_PRINTF(&RealSourceAddress), SOCKADDR_IN_PRINTF(DestinationAddress));
if( NO_ERROR != NatCreateRedirectEx (
NatHandle,
NatRedirectFlagLoopback,
IPPROTO_TCP,
DestinationAddress -> sin_addr.s_addr,
DestinationAddress -> sin_port,
RealSourceAddress.sin_addr.s_addr,
RealSourceAddress.sin_port,
DestinationAddress -> sin_addr.s_addr,
DestinationAddress -> sin_port,
RealSourceAddress.sin_addr.s_addr,
RealSourceAddress.sin_port,
NULL,
NULL,
NULL,
NULL)) {
Result = GetLastErrorAsResult();
DebugLastErrorF (_T("LDAP: 0x%x failed to create trivial redirect.\n"),
LdapConnection);
} else {
// we have successfully created a redirect
IsNatRedirectActive = TRUE;
do
{
ConnectEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!ConnectEvent) {
Result = GetLastErrorAsResult();
DebugLastErrorF (_T("LDAP: 0x%x failed to create connect-event.\n"),
LdapConnection);
break;
}
Status = WSAEventSelect (Socket, ConnectEvent, FD_CONNECT);
if (Status) {
Result = GetLastErrorAsResult();
DebugLastErrorF (_T("LDAP: 0x%x failed to select events on the socket.\n"),
LdapConnection);
break;
}
LdapConnection -> AddRef ();
if (!RegisterWaitForSingleObject (
&ConnectWaitHandle,
ConnectEvent,
LDAP_SOCKET::OnConnectCompletion,
this,
INFINITE,
WT_EXECUTEDEFAULT)) {
Result = GetLastErrorAsResult();
DebugLastErrorF (_T("LDAP: 0x%x failed to RegisterWaitForSingleObject.\n"),
LdapConnection);
LdapConnection -> Release ();
break;
}
if (connect (Socket, (SOCKADDR *)DestinationAddress, sizeof (SOCKADDR_IN))) {
ConnectError = WSAGetLastError ();
if(ConnectError == WSAEWOULDBLOCK) {
State = STATE_CONNECT_PENDING;
LdapConnection->OnStateChange (this, State);
Result = S_OK;
} else {
// a real error
Result = GetLastErrorAsResult();
DebugLastErrorF (_T("LDAP: 0x%x failed to issue async connect.\n"),
LdapConnection);
FreeConnectResources ();
// If remote server refused to connect, make an attempt to
// connect on a different port. Don't try to do so
// for any other error.
if ((WSAECONNREFUSED == ConnectError || WSAECONNRESET == ConnectError)
&& AttemptAnotherConnect) {
AttemptAnotherConnect = FALSE;
DebugF (_T ("LDAP: 0x%x cancels trivial redirect (%08X:%04X -> %08X:%04X) => (%08X:%04X -> %08X:%04X).\n"),
LdapConnection,
SOCKADDR_IN_PRINTF (&RealSourceAddress),
SOCKADDR_IN_PRINTF (&ActualDestinationAddress),
SOCKADDR_IN_PRINTF (&RealSourceAddress),
SOCKADDR_IN_PRINTF (&ActualDestinationAddress));
NatCancelRedirect (
NatHandle,
IPPROTO_TCP,
ActualDestinationAddress.sin_addr.s_addr,
ActualDestinationAddress.sin_port,
RealSourceAddress.sin_addr.s_addr,
RealSourceAddress.sin_port,
ActualDestinationAddress.sin_addr.s_addr,
ActualDestinationAddress.sin_port,
RealSourceAddress.sin_addr.s_addr,
RealSourceAddress.sin_port);
IsNatRedirectActive = FALSE;
closesocket (Socket);
Socket = INVALID_SOCKET;
State = STATE_NONE;
Result = AttemptAlternateConnect (); // calls IssueConnect internally
}
LdapConnection -> Release ();
}
break;
} else {
// connect completed synchronously
// this should never occur
DebugF (_T("LDAP: 0x%x completed synchronously -- this should never occur.\n"),
LdapConnection);
FreeConnectResources ();
LdapConnection -> Release ();
Result = E_UNEXPECTED;
}
} while(FALSE);
}
}
}
}
}
return Result;
}
// static
void LDAP_SOCKET::IoCompletionCallback (
DWORD Status,
DWORD BytesTransferred,
LPOVERLAPPED Overlapped)
{
LDAP_OVERLAPPED * LdapOverlapped;
LDAP_CONNECTION * Connection;
LdapOverlapped = CONTAINING_RECORD (Overlapped, LDAP_OVERLAPPED, Overlapped);
assert (LdapOverlapped -> Socket);
Connection = LdapOverlapped -> Socket -> LdapConnection;
LdapOverlapped -> Socket -> OnIoComplete (Status, BytesTransferred, LdapOverlapped);
Connection -> Release();
}
void LDAP_SOCKET::OnIoComplete (
DWORD Status,
DWORD BytesTransferred,
LDAP_OVERLAPPED * Overlapped)
{
Lock();
assert (Overlapped -> IsPending);
Overlapped -> IsPending = FALSE;
Overlapped -> BytesTransferred = BytesTransferred;
if (Overlapped == &RecvOverlapped)
OnRecvComplete (Status);
else if (Overlapped == &SendOverlapped)
OnSendComplete (Status);
else {
AssertNeverReached();
}
Unlock();
}
// static
void LDAP_SOCKET::OnConnectCompletion (
PVOID Context,
BOOLEAN TimerOrWaitFired)
{
LDAP_SOCKET * LdapSocket;
assert (Context);
LdapSocket = (LDAP_SOCKET *) Context;
LdapSocket -> Lock ();
LdapSocket -> OnConnectCompletionLocked ();
LdapSocket -> Unlock ();
LdapSocket -> LdapConnection -> Release ();
}
void LDAP_SOCKET::OnRecvComplete (DWORD Status)
{
DWORD StartOffset;
DWORD NextPacketOffset;
DWORD NextReceiveSize = 0;
DWORD Result;
LIST_ENTRY * ListEntry;
LDAP_BUFFER * Buffer;
if (Status != ERROR_SUCCESS) {
if (State != STATE_TERMINATED) {
Terminate();
}
return;
}
if (RecvOverlapped.BytesTransferred == 0) {
#if DBG
if (this == &LdapConnection -> ClientSocket)
{
DebugF (_T("LDAP: 0x%x client has closed transport socket.\n"), LdapConnection);
}
else if (this == &LdapConnection -> ServerSocket)
{
DebugF (_T("LDAP: 0x%x server has closed transport socket.\n"), LdapConnection);
}
else
AssertNeverReached();
#endif
Terminate();
return;
}
assert (RecvBuffer);
assert (RecvBuffer -> Data.Length + RecvOverlapped.BytesTransferred
<= RecvBuffer -> Data.MaxLength);
RecvBuffer -> Data.Length += RecvOverlapped.BytesTransferred;
if (State == STATE_TERMINATED) {
DebugF (_T("LDAP: 0x%x is terminating, no further processing will occur.\n"), LdapConnection);
return;
}
if (RecvPump -> IsActivelyPassingData ()) {
StartOffset = 0;
for (;;) {
assert (StartOffset <= RecvBuffer -> Data.Length);
Result = LdapDeterminePacketBoundary (
RecvBuffer,
StartOffset,
&NextPacketOffset,
&NextReceiveSize);
if (Result == ERROR_SUCCESS) {
RecvBuildBuffer (&RecvBuffer -> Data.Data [StartOffset], NextPacketOffset - StartOffset);
StartOffset = NextPacketOffset;
} else {
RecvBuffer -> Data.DeleteRangeAtPos (0, StartOffset);
if (Result == ERROR_INVALID_DATA) {
RecvPump -> StartPassiveDataTransfer ();
DebugF (_T("LDAP: 0x%x starts non-interpreting data transfer.\n"), LdapConnection);
InsertTailList (&RecvBufferQueue, &RecvBuffer -> ListEntry);
RecvBuffer = NULL;
}
BytesToReceive = NextReceiveSize;
break;
}
}
} else {
LONG PreviousRecvSize;
LONG PredictedRecvSize;
HRESULT QueryResult;
DWORD BytesPreviouslyRequested = BytesToReceive;
QueryResult = RecvSizePredictor.RetrieveOldSample (0, &PreviousRecvSize);
if (ERROR_SUCCESS != RecvSizePredictor.AddSample ((LONG) RecvOverlapped.BytesTransferred)) {
delete RecvBuffer;
RecvBuffer = NULL;
DebugErrorF (Status, _T("LDAP: 0x%x could not add sample to SamplePredictor.\n"), LdapConnection);
Terminate();
return;
}
if (BytesPreviouslyRequested == RecvOverlapped.BytesTransferred) {
// Exact receive
if (ERROR_SUCCESS != QueryResult) {
BytesToReceive = (DWORD) (RecvOverlapped.BytesTransferred * 1.5);
} else {
PredictedRecvSize = RecvSizePredictor.PredictNextSample ();
if (PredictedRecvSize < (LONG) RecvOverlapped.BytesTransferred) {
if ((DWORD) PreviousRecvSize < RecvOverlapped.BytesTransferred) {
BytesToReceive = RecvOverlapped.BytesTransferred * 1000 / (DWORD) PreviousRecvSize *
RecvOverlapped.BytesTransferred / 1000;
} else {
BytesToReceive = (DWORD) PreviousRecvSize;
}
} else {
BytesToReceive = (DWORD) PredictedRecvSize;
}
}
} else {
// Inexact receive
PredictedRecvSize = RecvSizePredictor.PredictNextSample ();
BytesToReceive = (PredictedRecvSize < LDAP_BUFFER_RECEIVE_SIZE) ?
LDAP_BUFFER_RECEIVE_SIZE :
(DWORD) PredictedRecvSize;
}
if (BytesToReceive > LDAP_BUFFER_MAX_RECV_SIZE) {
DebugF (_T("LDAP: 0x%x intended to receive %d bytes. Lowering the number to %d bytes.\n"),
LdapConnection, BytesToReceive, LDAP_BUFFER_MAX_RECV_SIZE);
BytesToReceive = LDAP_BUFFER_MAX_RECV_SIZE;
}
InsertTailList (&RecvBufferQueue, &RecvBuffer -> ListEntry);
RecvBuffer = NULL;
}
while (!IsListEmpty (&RecvBufferQueue)) {
ListEntry = RemoveHeadList (&RecvBufferQueue);
Buffer = CONTAINING_RECORD (ListEntry, LDAP_BUFFER, ListEntry);
RecvPump -> OnRecvBuffer (Buffer);
}
RecvIssue ();
}
void LDAP_SOCKET::OnSendComplete (DWORD Status)
{
assert (SendBuffer);
delete SendBuffer;
SendBuffer = NULL;
// before notifying the owning context, transmit any buffers
// that are queued for send.
if (SendNextBuffer())
return;
SendPump -> OnSendDrain();
}
void LDAP_SOCKET::OnConnectCompletionLocked (void) {
WSANETWORKEVENTS NetworkEvents;
HRESULT Result;
int ConnectError;
AssertLocked();
if (State != STATE_CONNECT_PENDING) {
DebugF (_T("LDAP: 0x%x connect request completed, but socket is no longer interested.\n"), LdapConnection);
return;
}
if (WSAEnumNetworkEvents (Socket, ConnectEvent, &NetworkEvents)) {
DebugLastErrorF (_T("LDAP: 0x%x failed to retrieve network events.\n"), LdapConnection);
Terminate();
return;
}
if (!(NetworkEvents.lNetworkEvents & FD_CONNECT)) {
DebugF (_T("LDAP: 0x%x connect event fired, but event mask does not indicate that connect completed -- internal error.\n"),
LdapConnection);
Terminate();
return;
}
ConnectError = S_OK;
if (NetworkEvents.iErrorCode [FD_CONNECT_BIT]) {
ConnectError = NetworkEvents.iErrorCode [FD_CONNECT_BIT];
DebugErrorF (ConnectError, _T("LDAP: 0x%x failed async connect request. "), LdapConnection);
// If remote host refused to connect, we may attempt
// a connection to an alternate port later, so we don't terminate
// the socket. All other error codes result in termination.
if (WSAECONNRESET != ConnectError && WSAECONNREFUSED != ConnectError) {
Terminate ();
return;
}
}
FreeConnectResources ();
// If first attempt to connect fail, try to connect using an alternate port
if ((WSAECONNREFUSED == ConnectError || WSAECONNRESET == ConnectError)
&& AttemptAnotherConnect) {
AttemptAnotherConnect = FALSE;
DebugF (_T ("LDAP: 0x%x cancels trivial redirect (%08X:%04X -> %08X:%04X) => (%08X:%04X -> %08X:%04X).\n"),
LdapConnection,
SOCKADDR_IN_PRINTF (&RealSourceAddress),
SOCKADDR_IN_PRINTF (&ActualDestinationAddress),
SOCKADDR_IN_PRINTF (&RealSourceAddress),
SOCKADDR_IN_PRINTF (&ActualDestinationAddress));
NatCancelRedirect (
NatHandle,
IPPROTO_TCP,
ActualDestinationAddress.sin_addr.s_addr,
ActualDestinationAddress.sin_port,
RealSourceAddress.sin_addr.s_addr,
RealSourceAddress.sin_port,
ActualDestinationAddress.sin_addr.s_addr,
ActualDestinationAddress.sin_port,
RealSourceAddress.sin_addr.s_addr,
RealSourceAddress.sin_port);
IsNatRedirectActive = FALSE;
closesocket (Socket);
Socket = INVALID_SOCKET;
State = STATE_NONE;
Result = AttemptAlternateConnect ();
if (S_OK != Result) {
Terminate ();
}
return;
}
DebugF (_T("LDAP: 0x%x established connection to server %08X:%04X.\n"), LdapConnection, SOCKADDR_IN_PRINTF (&ActualDestinationAddress));
if (!BindIoCompletionCallback ((HANDLE)Socket, LDAP_SOCKET::IoCompletionCallback, 0)) {
DebugLastErrorF (_T("LDAP: 0x%x failed to bind I/O completion callback.\n"), LdapConnection);
Terminate();
return;
}
// Asynchronous connect succeeded
State = STATE_CONNECTED;
LdapConnection -> OnStateChange (this, State);
}
void LDAP_SOCKET::FreeConnectResources (void) {
// refrain from receiving notifications of further transport events
WSAEventSelect (Socket, ConnectEvent, 0);
assert (ConnectWaitHandle);
UnregisterWaitEx (ConnectWaitHandle, NULL);
ConnectWaitHandle = NULL;
assert (ConnectEvent);
CloseHandle(ConnectEvent);
ConnectEvent = NULL;
}
// assumes that connect resources for previous
// connect attempt were freed
HRESULT LDAP_SOCKET::AttemptAlternateConnect (void) {
HRESULT Result;
// switch connection port to the other alternative
ActualDestinationAddress.sin_port =
(ActualDestinationAddress.sin_port == htons (LDAP_STANDARD_PORT)) ?
htons (LDAP_ALTERNATE_PORT) :
htons (LDAP_STANDARD_PORT);
DebugF (_T("LDAP: 0x%x will try to connect on an alternate address %08X:%04X.\n"),
LdapConnection,
SOCKADDR_IN_PRINTF (&ActualDestinationAddress));
// attempting to connect on an alternate port
Result = IssueConnect (&ActualDestinationAddress);
if (S_OK != Result) {
DebugF (_T("LDAP: 0x%x failed to issue connect on an alternate address %08X:%04X.\n"),
LdapConnection,
SOCKADDR_IN_PRINTF (&ActualDestinationAddress));
}
return Result;
}
void LDAP_SOCKET::Terminate (void)
{
switch (State) {
case STATE_TERMINATED:
// nothing to do
return;
case STATE_NONE:
// a different kind of nothing to do
break;
default:
// in all other states, the socket handle must be set
assert (Socket != INVALID_SOCKET);
State = STATE_TERMINATED;
if (INVALID_SOCKET != Socket) {
closesocket (Socket);
Socket = INVALID_SOCKET;
}
if (IsNatRedirectActive) {
DebugF (_T ("LDAP: 0x%x cancels trivial redirect (%08X:%04X -> %08X:%04X) => (%08X:%04X -> %08X:%04X).\n"),
LdapConnection,
SOCKADDR_IN_PRINTF (&RealSourceAddress),
SOCKADDR_IN_PRINTF (&ActualDestinationAddress),
SOCKADDR_IN_PRINTF (&RealSourceAddress),
SOCKADDR_IN_PRINTF (&ActualDestinationAddress));
NatCancelRedirect (
NatHandle,
IPPROTO_TCP,
ActualDestinationAddress.sin_addr.s_addr,
ActualDestinationAddress.sin_port,
RealSourceAddress.sin_addr.s_addr,
RealSourceAddress.sin_port,
ActualDestinationAddress.sin_addr.s_addr,
ActualDestinationAddress.sin_port,
RealSourceAddress.sin_addr.s_addr,
RealSourceAddress.sin_port);
IsNatRedirectActive = FALSE;
}
if (ConnectWaitHandle) {
if (UnregisterWaitEx (ConnectWaitHandle, NULL)) {
// Take care of the case when the connection was terminated AFTER
// async connect has been issued, but BEFORE the connect was completed.
//
// This should not normally happen.
LdapConnection -> Release ();
}
ConnectWaitHandle = NULL;
}
if (ConnectEvent) {
CloseHandle (ConnectEvent);
ConnectEvent = NULL;
}
SendPump -> Terminate ();
RecvPump -> Terminate ();
break;
}
LdapConnection -> OnStateChange (this, State);
}
HRESULT LDAP_SOCKET::RecvIssue (void)
{
WSABUF BufferArray [1];
DWORD Status;
DWORD BytesRequested;
if (RecvOverlapped.IsPending) {
DebugF (_T("LDAP: 0x%x receive is already pending.\n"), LdapConnection);
return S_OK;
}
if (!RecvPump -> CanIssueRecv()) {
// we gate the rate at which we receive data from the network on the
// rate at which the other network connection consumes it.
// this is how we preserve flow control.
return S_OK;
}
if (!RecvBuffer) {
RecvBuffer = new LDAP_BUFFER;
if (!RecvBuffer) {
DebugF (_T("LDAP: 0x%x RecvIssue allocation failure.\n"), LdapConnection);
Terminate();
return E_OUTOFMEMORY;
}
}
BytesRequested = RecvBuffer -> Data.Length + BytesToReceive;
if (!RecvBuffer -> Data.Grow (BytesRequested)) {
DebugF (_T("LDAP: 0x%x failed to expand receive buffer to %d bytes.\n"),
LdapConnection, BytesRequested);
Terminate();
return E_OUTOFMEMORY;
}
BufferArray [0].len = BytesToReceive;
BufferArray [0].buf = reinterpret_cast <char *>(RecvBuffer -> Data.Data) + RecvBuffer -> Data.Length;
ZeroMemory (&RecvOverlapped.Overlapped, sizeof (OVERLAPPED));
RecvFlags = 0;
LdapConnection -> AddRef ();
if (WSARecv (Socket, BufferArray, 1,
&RecvOverlapped.BytesTransferred, &RecvFlags,
&RecvOverlapped.Overlapped, NULL)) {
Status = WSAGetLastError();
if (Status != WSA_IO_PENDING) {
// a true error, probably a transport failure
LdapConnection -> Release ();
DebugErrorF (Status, _T("LDAP: 0x%x failed to issue receive.\n"), LdapConnection);
return HRESULT_FROM_WIN32 (Status);
}
}
RecvOverlapped.IsPending = TRUE;
return S_OK;
}
void LDAP_SOCKET::SendQueueBuffer (
IN LDAP_BUFFER * Buffer)
{
AssertLocked();
assert (!IsInList (&SendBufferQueue, &Buffer -> ListEntry));
InsertTailList (&SendBufferQueue, &Buffer -> ListEntry);
SendNextBuffer();
}
BOOL LDAP_SOCKET::SendNextBuffer (void)
{
WSABUF BufferArray [1];
LIST_ENTRY * ListEntry;
DWORD Status;
if (SendOverlapped.IsPending) {
assert (SendBuffer);
// Debug (_T("LDAP_SOCKET::SendNextMessage: already sending a message, must wait.\n"));
return FALSE;
}
assert (!SendBuffer);
// remove the next buffer to be sent from the queue
if (IsListEmpty (&SendBufferQueue))
return FALSE;
ListEntry = RemoveHeadList (&SendBufferQueue);
SendBuffer = CONTAINING_RECORD (ListEntry, LDAP_BUFFER, ListEntry);
BufferArray [0].buf = reinterpret_cast<char *> (SendBuffer -> Data.Data);
BufferArray [0].len = SendBuffer -> Data.Length;
ZeroMemory (&SendOverlapped.Overlapped, sizeof (OVERLAPPED));
LdapConnection -> AddRef ();
if (WSASend (Socket, BufferArray, 1,
&SendOverlapped.BytesTransferred, 0,
&SendOverlapped.Overlapped, NULL)) {
Status = WSAGetLastError();
if (Status != WSA_IO_PENDING) {
LdapConnection -> Release ();
DebugError (Status, _T("LDAP: Failed to issue send.\n"));
delete SendBuffer;
SendBuffer = NULL;
Terminate();
// we return TRUE, because we did dequeue a buffer,
// even if that buffer could not be transmitted.
return TRUE;
}
}
SendOverlapped.IsPending = TRUE;
return TRUE;
}
BOOL LDAP_SOCKET::GetLocalAddress (
OUT SOCKADDR_IN * ReturnAddress)
{
INT AddressLength;
AssertLocked();
if (State == STATE_CONNECTED) {
AddressLength = sizeof (SOCKADDR_IN);
if (getsockname (Socket, (SOCKADDR *) ReturnAddress, &AddressLength)) {
DebugLastErrorF (_T("LDAP: 0x%x failed to retrieve socket address.\n"), LdapConnection);
ZeroMemory (&ReturnAddress, sizeof (SOCKADDR_IN));
return FALSE;
}
return TRUE;
}
else {
return FALSE;
}
}
BOOL LDAP_SOCKET::GetRemoteAddress (
OUT SOCKADDR_IN * ReturnAddress)
{
INT AddressLength;
AssertLocked();
if (State == STATE_CONNECTED) {
AddressLength = sizeof (SOCKADDR_IN);
if (getpeername (Socket, (SOCKADDR *) ReturnAddress, &AddressLength)) {
DebugLastErrorF (_T("LDAP: 0x%x failed to retrieve peer address.\n"), LdapConnection);
ZeroMemory (&ReturnAddress, sizeof (SOCKADDR_IN));
return FALSE;
}
return TRUE;
}
else {
return FALSE;
}
}
// LDAP_CONNECTION ---------------------------------------------------
LDAP_CONNECTION::LDAP_CONNECTION (NAT_KEY_SESSION_MAPPING_EX_INFORMATION * RedirectInformation)
: LIFETIME_CONTROLLER (&LdapSyncCounter) ,
ClientSocket (this, &PumpClientToServer, &PumpServerToClient),
ServerSocket (this, &PumpServerToClient, &PumpClientToServer),
PumpClientToServer (this, &ClientSocket, &ServerSocket),
PumpServerToClient (this, &ServerSocket, &ClientSocket)
{
SourceInterfaceAddress = 0;
DestinationInterfaceAddress = 0;
State = STATE_NONE;
DestinationAddress.sin_family = AF_INET;
DestinationAddress.sin_addr.s_addr = RedirectInformation -> DestinationAddress;
DestinationAddress.sin_port = RedirectInformation -> DestinationPort;
SourceAddress.sin_family = AF_INET;
SourceAddress.sin_addr.s_addr = RedirectInformation -> SourceAddress;
SourceAddress.sin_port = RedirectInformation -> SourcePort;
DebugF (_T("LDAP: 0x%x created.\n"), this);
}
HRESULT LDAP_CONNECTION::Initialize (
IN NAT_KEY_SESSION_MAPPING_EX_INFORMATION * RedirectInformation
)
{
HRESULT Result;
Lock ();
Result = InitializeLocked (RedirectInformation);
Unlock ();
return Result;
}
HRESULT LDAP_CONNECTION::InitializeLocked (
IN NAT_KEY_SESSION_MAPPING_EX_INFORMATION * RedirectInformation
)
{
ULONG Error;
DebugF (_T ("LDAP: 0x%x connection accepted on adapter %d.\n"), this, RedirectInformation -> AdapterIndex);
SourceInterfaceAddress = H323MapAdapterToAddress (RedirectInformation -> AdapterIndex);
if (INADDR_NONE == SourceInterfaceAddress) {
DebugF (_T ("LDAP: 0x%x failed to get source interface address (via H323MapAdapterToAddress).\n"), this);
return E_FAIL;
}
Error = GetBestInterfaceAddress (ntohl (DestinationAddress.sin_addr.s_addr), &DestinationInterfaceAddress);
if (ERROR_SUCCESS != Error) {
DebugErrorF (Error, _T ("LDAP: 0x%x failed to get destination interface address.\n"), this);
return HRESULT_FROM_WIN32 (Error);
}
DebugF (_T("LDAP: 0x%x arrived on interface %08X.\n"), this, SourceInterfaceAddress);
return S_OK;
}
LDAP_CONNECTION::~LDAP_CONNECTION (void)
{
DebugF (_T("LDAP: 0x%x destroyed.\n"), this);
}
void LDAP_CONNECTION::StartIo (void)
{
PumpClientToServer.Start ();
PumpServerToClient.Start ();
}
HRESULT LDAP_CONNECTION::AcceptSocket (
IN SOCKET Socket,
IN SOCKADDR_IN * LocalAddress,
IN SOCKADDR_IN * RemoteAddress,
IN SOCKADDR_IN * ArgActualDestinationAddress)
{
HRESULT Result;
Lock();
if (State == STATE_NONE) {
Result = ClientSocket.AcceptSocket (Socket);
if (Result == S_OK) {
Result = ServerSocket.IssueConnect (ArgActualDestinationAddress);
if (Result != S_OK) {
DebugErrorF (Result, _T("LDAP: 0x%x failed to issue async connect to %08X:%04X.\n"),
this,
SOCKADDR_IN_PRINTF (ArgActualDestinationAddress));
Terminate ();
}
}
else {
DebugErrorF (Result, _T("LDAP: 0x%x could not successfully complete accept.\n"), this);
Terminate ();
}
}
else {
DebugF (_T("LDAP: 0x%x is not in a valid state for accept (state != STATE_NONE).\n"), this);
Result = E_UNEXPECTED;
}
Unlock();
return Result;
}
HRESULT LDAP_CONNECTION::CreateOperation (
IN LDAP_OPERATION_TYPE Type,
IN LDAP_MESSAGE_ID MessageID,
IN ANSI_STRING * DirectoryPath,
IN ANSI_STRING * Alias,
IN IN_ADDR ClientAddress,
IN SOCKADDR_IN * ServerAddress,
IN DWORD EntryTimeToLive // in seconds
)
{
LDAP_OPERATION * Operation;
DWORD Index;
HRESULT Result;
if (FindOperationIndexByMessageID (MessageID, &Index)) {
DebugF (_T("LDAP: 0x%x - an operation with message ID (%u) is already pending.\n"),
this,
MessageID);
return E_FAIL;
}
Operation = OperationArray.AllocAtPos (Index);
if (!Operation) {
DebugF (_T("LDAP: 0x%x - CreateOperation allocation failure #1.\n"), this);
return E_OUTOFMEMORY;
}
Operation -> Type = Type;
Operation -> MessageID = MessageID;
Operation -> ClientAddress = ClientAddress;
Operation -> ServerAddress = *ServerAddress;
Operation -> EntryTimeToLive = EntryTimeToLive;
CopyAnsiString (DirectoryPath, &Operation -> DirectoryPath);
CopyAnsiString (Alias, &Operation -> Alias);
if ((Operation -> DirectoryPath.Buffer
&& Operation -> Alias.Buffer)) {
// all is well
Result = S_OK;
}
else {
DebugF (_T("LDAP: 0x%x - CreateOperation allocation failure #2.\n"), this);
FreeAnsiString (&Operation -> DirectoryPath);
FreeAnsiString (&Operation -> Alias);
Result = E_OUTOFMEMORY;
}
return Result;
}
// Processing of LDAP messages ---------------------------------------
BOOL LDAP_CONNECTION::ProcessAddRequest (
IN LDAPMessage * Message)
{
AddRequest * Request;
ANSI_STRING DirectoryPath;
LDAP_PATH_ELEMENTS PathElements;
ANSI_STRING AttributeTag;
IN_ADDR OldClientAddress; // the address the client submitted in AddRequest
IN_ADDR NewClientAddress; // the address we are replacing it with
LDAP_OPERATION * Operation;
DWORD OperationInsertionIndex;
ASN1octetstring_t IPAddressOldValue;
SOCKADDR_IN LocalToServerAddress;
SOCKADDR_IN LocalToClientAddress;
SOCKADDR_IN ServerAddress;
INT AddressLength;
BOOL NeedObjectClass;
ANSI_STRING ClientAlias;
AddRequest_attrs * Iter;
AddRequest_attrs_Seq * Attribute;
AddRequest_attrs_Seq_values * ValueSequence;
AttributeValue * Attribute_Alias;
AttributeValue * Attribute_IPAddress;
AttributeValue * Attribute_ObjectClass;
AttributeValue * Attribute_Comment;
AttributeValue Attribute_Comment_Old;
ANSI_STRING String;
CHAR IPAddressText [0x20];
USHORT IPAddressTextLength;
Request = &Message -> protocolOp.u.addRequest;
// check to see if an existing operation with the same message id is pending.
// if so, the client is in violation of the LDAP spec.
// we'll just ignore the packet in this case.
// at the same time, compute the insertion position for the new operation (for use later).
if (FindOperationIndexByMessageID (Message -> messageID, &OperationInsertionIndex)) {
DebugF (_T("LDAP: 0x%x - client has issued two requests with the same message ID (%u), LDAP protocol violation, packet will not be processed.\n"),
this,
Message -> messageID);
return FALSE;
}
// NetMeeting supplies the objectClass in the directory path.
// TAPI supplies the objectClass in the attribute set.
// Don't you just love standards?
InitializeAnsiString (&DirectoryPath, &Request -> entry);
ParseDirectoryPath (&DirectoryPath, &PathElements);
// make sure that the alias is present
if (!PathElements.CN.Buffer) {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified AddRequest (no alias present).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
ClientAlias = PathElements.CN;
if (PathElements.ObjectClass.Buffer) {
if (RtlEqualStringConst (&PathElements.ObjectClass, &LdapText_RTPerson, TRUE)) {
NeedObjectClass = FALSE;
}
else {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified AddRequest (no object class (1)).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
}
else {
NeedObjectClass = TRUE;
}
// first, determine if the attributes of this object
// match the set of objects we wish to modify.
// scan through the set of attributes
// find interesting data
Attribute_IPAddress = NULL;
Attribute_ObjectClass = NULL;
Attribute_Comment = NULL;
for (Iter = Request -> attrs; Iter; Iter = Iter -> next) {
Attribute = &Iter -> value;
InitializeAnsiString (&AttributeTag, &Attribute -> type);
if (Attribute -> values) {
// we are only concerned with single-value attributes
// if it's one of the attributes that we want,
// then store in local variable
if (RtlEqualStringConst (&AttributeTag, &LdapText_Attribute_sipaddress, TRUE)
|| RtlEqualStringConst (&AttributeTag, &LdapText_Attribute_ipAddress, TRUE))
Attribute_IPAddress = &Attribute -> values -> value;
else if (RtlEqualStringConst (&AttributeTag, &LdapText_ObjectClass, TRUE))
Attribute_ObjectClass = &Attribute -> values -> value;
else if (RtlEqualStringConst (&AttributeTag, &LdapText_Attribute_comment, TRUE))
Attribute_Comment = &Attribute -> values -> value;
// else, we aren't interested in the attribute
}
else {
// else, the attribute has no values
}
}
// make sure that we found an objectClass value.
// make sure that the objectClass = RTPerson
if (NeedObjectClass) {
if (!Attribute_ObjectClass) {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified AddRequest (no object class (2)).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
InitializeAnsiString (&String, Attribute_ObjectClass);
if (!RtlEqualStringConst (&String, &LdapText_RTPerson, TRUE)) {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified AddRequest (not for RTPerson).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
}
// if a comment field is present, and the comment is "Generated by TAPI3"
// modify it so that it says "Generated by TAPI3, modified by ICS"
if (Attribute_Comment) {
Attribute_Comment_Old = *Attribute_Comment;
InitializeAnsiString (&String, Attribute_Comment);
if (RtlEqualStringConst (&String, &LdapText_GeneratedByTAPI, TRUE)) {
Attribute_Comment -> value = (PUCHAR) LdapText_ModifiedByICS.Buffer;
Attribute_Comment -> length = LdapText_ModifiedByICS.Length * sizeof (CHAR);
}
}
// make sure ip address attribute is present
// parse the address, build replacement address
if (!Attribute_IPAddress) {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified AddRequest (IP address not present).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
if (LdapTranslationTable.ReachedMaximumSize ()) {
LDAPMessage AddRequestFailed;
DebugF(_T("LDAP: Size of LDAP Address Translation Table exceeded limit. Sending back AddResponse with an error code.\n"));
AddRequestFailed.messageID = Message -> messageID;
AddRequestFailed.protocolOp.choice = addResponse_choice;
AddRequestFailed.protocolOp.u.addResponse.resultCode = sizeLimitExceeded;
AddRequestFailed.protocolOp.u.addResponse.matchedDN.length = 0;
AddRequestFailed.protocolOp.u.addResponse.matchedDN.value = NULL;
AddRequestFailed.protocolOp.u.addResponse.errorMessage.length = LdapText_TableSizeExceededMessage.Length * sizeof (CHAR);
AddRequestFailed.protocolOp.u.addResponse.errorMessage.value = (PUCHAR) LdapText_TableSizeExceededMessage.Buffer;
PumpServerToClient.EncodeSendMessage (&AddRequestFailed);
} else {
if (!ServerSocket.GetRemoteAddress (&ServerAddress)) {
return FALSE;
}
IPAddressTextLength = min (0x1F, (USHORT) Attribute_IPAddress -> length);
IPAddressText [IPAddressTextLength] = 0;
memcpy (IPAddressText, Attribute_IPAddress -> value, IPAddressTextLength * sizeof (CHAR));
if (RtlCharToInteger (IPAddressText, 10, &OldClientAddress.s_addr) != STATUS_SUCCESS) {
DebugF (_T("LDAP: 0x%x - AddRequest: bogus IP address value (%.*S).\n"),
this,
Attribute_IPAddress -> length,
Attribute_IPAddress -> value);
return FALSE;
}
// If ILS is running locally, we will not modify AddRequest sent by any private client.
// Instead, we will later modify SearchResponse PDU if it turned out that
// the name of the client being searched for is stored in LDAP Address Translation Table, and
// the matching SearchRequest PDU came from a machine external to the client's local subnet.
// The address we will put into the modified SearchResponse PDU will be that of the
// interface on which SearchRequest was received (public interface, or another local interface)
// If SearchRequest came from a private client located on the same subnet as the client we
// are registering here, we won't modify it as those two clients can communicate directly
// and don't require any proxying by the NAT machine.
if (!::NhIsLocalAddress (ServerAddress.sin_addr.s_addr)) {
// get the address that we want to substitute (our external interface address)
if (!ServerSocket.GetLocalAddress (&LocalToServerAddress)) {
DebugF (_T("LDAP: 0x%x failed to get local address to server -- internal error.\n"), this);
return FALSE;
}
// Convoluted code alert!
// NetMeeting stores its IP address as an attribute on an LDAP object on an ILS server.
// Makes sense. The attribute is encoded as a textual string, so they had to convert
// the IP address to text. Any sane person would have chosen the standard dotted-quad
// format, but they chose to interpret the IP address as a 32-bit unsigned integer,
// which is fair enough, and then to convert that integer to a single decimal text string.
// That's all great, that's just fine. But NetMeeting stores the attribute byte-swapped
// -- they used ntohl one too many times. Grrrrrrrr.... The value should have been stored
// without swapping the bytes, since the interpretation was "unsigned integer" and not "octet sequence".
OldClientAddress.s_addr = htonl (ByteSwap (OldClientAddress.s_addr));
NewClientAddress = LocalToServerAddress.sin_addr;
// believe me, this IS CORRECT.
// see the long note above for more info. -- arlied
if (RtlIntegerToChar (ByteSwap (ntohl (NewClientAddress.s_addr)),
10, 0x1F, IPAddressText) != STATUS_SUCCESS) {
DebugF (_T("LDAP: 0x%x failed to convert IP address to text -- internal error.\n"), this);
return FALSE;
}
DebugF (_T("LDAP: 0x%x will register %08X on the ILS.\n"), this, ntohl (NewClientAddress.s_addr));
} else {
DebugF (_T("LDAP: 0x%x will register %08X on the ILS.\n"), this, ntohl (OldClientAddress.s_addr));
}
// allocate and build an LDAP_OPERATION structure.
DebugF (_T("LDAP: 0x%x inserts valid AddRequest into operation table.\n"), this);
CreateOperation (
LDAP_OPERATION_ADD,
Message -> messageID,
&DirectoryPath,
&ClientAlias,
OldClientAddress,
&ServerAddress,
LDAP_TRANSLATION_TABLE_ENTRY_INITIAL_TIME_TO_LIVE);
// the entry is now in the operation array
// later, when the server sends the AddResponse,
// we'll match the response with the request,
// and modify the LDAP_TRANSLATION_TABLE
// now, in-place, we modify the PDU structure,
// reencode it, send it, undo the modification
// (so ASN1Free_AddRequest doesn't act up)
assert (Attribute_IPAddress);
IPAddressOldValue = *Attribute_IPAddress;
Attribute_IPAddress -> value = (PUCHAR) IPAddressText;
Attribute_IPAddress -> length = strlen (IPAddressText);
PumpClientToServer.EncodeSendMessage (Message);
// switch back so we don't a/v when decoder frees pdu
*Attribute_IPAddress = IPAddressOldValue;
if (Attribute_Comment)
*Attribute_Comment = Attribute_Comment_Old;
}
return TRUE;
}
BOOL LDAP_CONNECTION::ProcessModifyRequest (
IN LDAP_MESSAGE_ID MessageID,
IN ModifyRequest * Request)
{
ModifyRequest_modifications * ModificationIterator;
ModifyRequest_modifications_Seq * Modification;
PModifyRequest_modifications_Seq_modification_values ModificationValue;
LPCTSTR Op;
ANSI_STRING ModificationType;
ANSI_STRING TimeToLive;
ANSI_STRING DirectoryPath;
ANSI_STRING ClientAlias;
DWORD OperationInsertionIndex;
LDAP_PATH_ELEMENTS PathElements;
BOOL IsValidRefreshRequest = FALSE;
SOCKADDR_IN ServerAddress;
DWORD EntryTimeToLive = 0;
CHAR EntryTimeToLiveText [11];
USHORT EntryTimeToLiveLength;
// check to see if an existing operation with the same message id is pending.
// if so, the client is in violation of the LDAP spec.
// we'll just ignore the packet in this case.
// at the same time, compute the insertion position for the new operation (for use later).
if (FindOperationIndexByMessageID (MessageID, &OperationInsertionIndex)) {
DebugF (_T("LDAP: 0x%x - ModifyRequest: client has issued two requests with the same message ID (%u), LDAP protocol violation, packet will not be processed.\n"),
this,
MessageID);
return FALSE;
}
// Cover the case when ModifyRequest does not supply baseObject
if (Request -> object.value == NULL || Request -> object.length == 0) {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified ModifyRequest (no base object).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
InitializeAnsiString (&DirectoryPath, &Request -> object);
ParseDirectoryPath (&DirectoryPath, &PathElements);
ClientAlias = PathElements.CN;
for (ModificationIterator = Request -> modifications; ModificationIterator; ModificationIterator = ModificationIterator -> next) {
Modification = &ModificationIterator -> value;
InitializeAnsiString (&ModificationType, &Modification -> modification.type);
if (RtlEqualStringConst (&ModificationType, &LdapText_Modify_EntryTTL, TRUE) && Modification -> operation == replace) {
IsValidRefreshRequest = TRUE;
assert (Modification -> modification.values);
ModificationValue = Modification -> modification.values;
InitializeAnsiString (&TimeToLive, &ModificationValue -> value);
EntryTimeToLiveLength = min (10, (USHORT) ModificationValue -> value.length);
EntryTimeToLiveText [EntryTimeToLiveLength] = 0;
memcpy (EntryTimeToLiveText, ModificationValue -> value.value, EntryTimeToLiveLength * sizeof (CHAR));
if (RtlCharToInteger (&EntryTimeToLiveText [0], 10, &EntryTimeToLive) != STATUS_SUCCESS) {
DebugF (_T("LDAP: 0x%x - ModifyRequest: bogus Time-To-Live value (%.*S).\n"),
this,
EntryTimeToLiveLength,
EntryTimeToLiveText);
return FALSE;
}
DebugF (_T("LDAP: 0x%x %08X requested lifetime increase of %d seconds for (%.*S).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr),
EntryTimeToLive, // in seconds
ANSI_STRING_PRINTF (&ClientAlias));
}
}
// If type of the modification was 'replace', and the attribute to be modified was 'EntryTTL'
// then we have just received a valid refresh request from PhoneDialer
//
if (!IsValidRefreshRequest) {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified ModifyRequest (not a refresh request).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
if (!ClientAlias.Buffer || ClientAlias.Length == 0) {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified ModifyRequest (no client alias in refresh request).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
if (!ServerSocket.GetRemoteAddress (&ServerAddress)) {
DebugF (_T("LDAP: 0x%x ModifyRequest: failed to get server address -- internal error.\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
DebugF (_T("LDAP: 0x%x inserts valid ModifyRequest into operation table.\n"), this);
// Allocate and build an LDAP_OPERATION structure.
CreateOperation (
LDAP_OPERATION_MODIFY,
MessageID,
&DirectoryPath,
&ClientAlias,
SourceAddress.sin_addr,
&ServerAddress,
EntryTimeToLive
);
// The entry is now in the operation array
// later, when the server sends the ModifyResponse,
// we'll check whether it was successful.
// If so, we will find and refresh matching
// entry in the LDAP Address Translation Table
return FALSE;
}
BOOL LDAP_CONNECTION::ProcessDeleteRequest (
IN LDAP_MESSAGE_ID MessageID,
IN DelRequest * Request)
{
ANSI_STRING DirectoryPath;
HRESULT Result = S_FALSE;
LDAP_PATH_ELEMENTS PathElements;
assert (Request);
DirectoryPath.Buffer = (PCHAR) Request -> value;
DirectoryPath.Length = (USHORT) Request -> length;
DirectoryPath.MaximumLength = (USHORT) Request -> length;
ParseDirectoryPath (&DirectoryPath, &PathElements);
if (RtlEqualStringConst (&PathElements.ObjectClass, &LdapText_RTPerson, TRUE)) {
Result = LdapTranslationTable.RemoveEntryByAliasServer (&PathElements.CN, &DestinationAddress);
if (Result == S_OK) {
DebugF (_T("LDAP: 0x%x removed entry (%.*S) from LDAP table.\n"),
this,
ANSI_STRING_PRINTF (&DirectoryPath));
} else {
DebugF (_T("LDAP: 0x%x attempted to remove entry (%.*S) from LDAP table, but it was not there.\n"),
this,
ANSI_STRING_PRINTF (&DirectoryPath));
}
} else {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified DeleteRequest.\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
}
return FALSE;
}
BOOL LDAP_CONNECTION::ProcessSearchRequest (
IN LDAPMessage * Message)
{
SearchRequest * Request;
ANSI_STRING DirectoryPath;
ANSI_STRING AttributeTag;
ANSI_STRING AttributeType;
ANSI_STRING AttributeValue;
ANSI_STRING RequestedForAlias;
LDAP_PATH_ELEMENTS PathElements;
LDAP_OPERATION * Operation;
Filter * SearchFilter;
Filter_and * FilterIterator;
SearchRequest_attributes * Iterator;
AttributeValueAssertion * EqualityAssertion;
BOOL IsRequestForIPAddress = FALSE;
BOOL IsRequestForRTPerson = FALSE;
BOOL IsRequestForSttl = FALSE;
BOOL IsQualifiedRequest = FALSE;
BOOL IsRequestForSpecificAlias = FALSE;
DWORD EntryTimeToLive = 0;
CHAR EntryTimeToLiveText [11];
USHORT EntryTimeToLiveLength;
DWORD OperationInsertionIndex;
Request = &Message -> protocolOp.u.searchRequest;
// check to see if an existing operation with the same message id is pending.
// if so, the client is in violation of the LDAP spec.
// we'll just ignore the packet in this case.
// at the same time, compute the insertion position for the new operation (for use later).
if (FindOperationIndexByMessageID (Message -> messageID, &OperationInsertionIndex)) {
DebugF (_T("LDAP: 0x%x - SearchRequest - client has issued two requests with the same message ID (%u), LDAP protocol violation, packet will not be processed.\n"),
this,
Message -> messageID);
return FALSE;
}
// Cover the case when SearchRequest does not supply baseObject
if (Request -> baseObject.value == NULL || Request -> baseObject.length == 0) {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified SearchRequest (no base object).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
InitializeAnsiString (&DirectoryPath, &Request -> baseObject);
ParseDirectoryPath (&DirectoryPath, &PathElements);
// Determine whether we are interested in this request.
//
// This is what we are interested in from SearchRequests originated by NetMeeting:
// 1. It searches for IP address (query to the server), or
// 2. It searches for Sttl attribute, AND for RTPerson attribute (refresh request)
//
for (Iterator = Request -> attributes; Iterator; Iterator = Iterator -> next) {
InitializeAnsiString (&AttributeValue, &Iterator -> value);
if (RtlEqualStringConst (&AttributeValue, &LdapText_Attribute_sipaddress, TRUE)
|| RtlEqualStringConst (&AttributeValue, &LdapText_Attribute_ipAddress, TRUE)) {
IsRequestForIPAddress = TRUE;
}
// else not interesting attribute
}
// Look closer at the composition of the filter.
//
// NetMeeting specifies the following filter in SearchRequest:
//
// FilterType = AND
// FilterType = EqualityMatch
// AttributeType = objectClass
// AttributeValue = RTPerson
// FilterType = EqualityMatch
// AttributeType = cn
// AttributeValue = <...alias, for which IP address is searched, or refresh is requested...>
//
// NetMeeting may also add the following 'EqualityMatch' clause to the filter
// if a Time-To-Live increase is requested
//
// FilterType = EqualityMatch
// AttributeType = sttl
// AttributeValue = <...increase in Time-To-Live, in minutes...>
//
// Phone Dialer DOES NOT query directory server to determine IP address of the called party.
// It does the determination by some other means (DNS lookup, most certainly).
SearchFilter = &Request -> filter;
switch (SearchFilter -> choice) {
case and_choice:
for (FilterIterator = SearchFilter -> u.and; FilterIterator; FilterIterator = FilterIterator -> next) {
switch (FilterIterator -> value.choice) {
case equalityMatch_choice:
EqualityAssertion = &FilterIterator -> value.u.equalityMatch;
InitializeAnsiString (&AttributeType, &EqualityAssertion -> attributeType);
InitializeAnsiString (&AttributeValue, &EqualityAssertion -> attributeValue);
if (RtlEqualStringConst (&AttributeType, &LdapText_Attribute_sttl, TRUE)) {
IsRequestForSttl = TRUE;
EntryTimeToLiveLength = min (10, (USHORT) AttributeValue.Length);
EntryTimeToLiveText [EntryTimeToLiveLength] = 0;
memcpy (EntryTimeToLiveText, AttributeValue.Buffer, EntryTimeToLiveLength * sizeof (CHAR));
if (RtlCharToInteger (&EntryTimeToLiveText [0], 10, &EntryTimeToLive) != STATUS_SUCCESS) {
DebugF (_T("LDAP: 0x%x - SearchRequest: bogus Time-To-Live value (%.*S).\n"),
this,
EntryTimeToLiveLength,
EntryTimeToLiveText);
return FALSE;
}
}
if (RtlEqualStringConst (&AttributeType, &LdapText_ObjectClass, TRUE)
|| RtlEqualStringConst (&AttributeValue, &LdapText_RTPerson, TRUE)) {
IsRequestForRTPerson = TRUE;
}
if (RtlEqualStringConst (&AttributeType, &LdapText_CN, TRUE)) {
RequestedForAlias = AttributeValue;
IsRequestForSpecificAlias = TRUE;
}
break;
default:
break;
}
}
break;
default:
break;
}
IsQualifiedRequest = IsRequestForIPAddress || (IsRequestForRTPerson && IsRequestForSttl);
if (!IsQualifiedRequest) {
DebugF (_T("LDAP: 0x%x client %08X issued unqualified SearchRequest.\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr));
return FALSE;
}
if (IsRequestForSttl) {
DebugF (_T("LDAP: 0x%x %08X requested lifetime increase of %d seconds for (%.*S).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr),
EntryTimeToLive * 60, // in seconds
ANSI_STRING_PRINTF (&RequestedForAlias));
}
if (IsRequestForIPAddress) {
if (IsRequestForSpecificAlias) {
DebugF (_T("LDAP: 0x%x %08X requested IP address for (%.*S).\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr),
ANSI_STRING_PRINTF (&RequestedForAlias));
} else {
DebugF (_T("LDAP: 0x%x %08X issued unspecified request for IP address.\n"),
this,
ntohl (SourceAddress.sin_addr.s_addr) );
}
}
DebugF (_T("LDAP: 0x%x inserts valid SearchRequest into operation table.\n"), this);
// allocate and build an LDAP_OPERATION structure.
CreateOperation (
LDAP_OPERATION_SEARCH,
Message -> messageID,
&DirectoryPath,
&PathElements.CN,
SourceAddress.sin_addr,
&DestinationAddress,
EntryTimeToLive * 60); // in seconds
// the entry is now in the operation array
// later, when the server sends the SearchResponse,
// we'll match the response with the request,
// and modify the IP address if an entry with
// the matching alias happens to be in the
// LDAP_TRANSLATION_TABLE. This would mean that
// the client running on the proxy machine itself
// wishes to connect to a private subnet client.
PumpClientToServer.EncodeSendMessage (Message);
return TRUE;
}
// server to client messages
void LDAP_CONNECTION::ProcessAddResponse (
IN LDAPMessage * Message)
{
AddResponse * Response;
LDAP_OPERATION * Operation;
Response = &Message -> protocolOp.u.addResponse;
AssertLocked();
if (!FindOperationByMessageID (Message -> messageID, &Operation)) {
return;
}
if (Operation -> Type == LDAP_OPERATION_ADD) {
if (Response -> resultCode == success) {
DebugF (_T("LDAP: 0x%x server has approved AddRequest for (%.*S).\n"),
this, ANSI_STRING_PRINTF (&Operation -> Alias));
assert (Operation -> Alias.Buffer);
assert (Operation -> DirectoryPath.Buffer);
InterfaceArray.StartQ931ReceiveRedirects ();
LdapTranslationTable.InsertEntry (
&Operation -> Alias,
&Operation -> DirectoryPath,
Operation -> ClientAddress,
&Operation -> ServerAddress,
Operation -> EntryTimeToLive);
}
else {
DebugF (_T("LDAP: 0x%x Server has rejected AddRequest, result code (%u).\n"),
this,
Response -> resultCode);
}
}
else {
DebugF (_T("LDAP: 0x%x received AddResponse with message ID (%u), and found matching pending request, but the type of the request does not match (%d).\n"),
this,
Message -> messageID,
Operation -> Type);
}
Operation -> FreeContents ();
OperationArray.DeleteEntry (Operation);
}
void LDAP_CONNECTION::ProcessModifyResponse (
IN LDAP_MESSAGE_ID MessageID,
IN ModifyResponse * Response)
{
LDAP_OPERATION * Operation;
AssertLocked();
if (!FindOperationByMessageID (MessageID, &Operation)) {
return;
}
if (Operation -> Type == LDAP_OPERATION_MODIFY) {
if (Response -> resultCode == success) {
assert (Operation -> Alias.Buffer);
assert (Operation -> DirectoryPath.Buffer);
DebugF (_T("LDAP: 0x%x server %08X has approved increase in lifetime of entry (%.*S) by %d seconds.\n"),
this,
ntohl (Operation -> ServerAddress.sin_addr.s_addr),
ANSI_STRING_PRINTF (&Operation -> Alias),
Operation -> EntryTimeToLive);
LdapTranslationTable.RefreshEntry (&Operation -> Alias,
&Operation -> DirectoryPath,
Operation -> ClientAddress,
&Operation -> ServerAddress,
Operation -> EntryTimeToLive);
}
else {
DebugF (_T("LDAP: 0x%x server %08X has rejected ModifyRequest, result code (%u).\n"),
this,
ntohl (Operation -> ServerAddress.sin_addr.s_addr), Response -> resultCode);
}
}
else {
DebugF (_T("LDAP: 0x%x received with message ID (%u), and found matching pending request, but the type of the request does not match (%d).\n"),
this,
MessageID,
Operation -> Type);
}
Operation -> FreeContents ();
OperationArray.DeleteEntry (Operation);
}
void LDAP_CONNECTION::ProcessDeleteResponse (
IN LDAP_MESSAGE_ID MessageID,
IN DelResponse * Response)
{
}
BOOL LDAP_CONNECTION::ProcessSearchResponse (
IN LDAPMessage * Message)
{
SearchResponse * Response;
ANSI_STRING ObjectName;
LDAP_OBJECT_NAME_ELEMENTS ObjectNameElements = { 0 };
ANSI_STRING AttributeTag;
ASN1octetstring_t IPAddressOldValue;
ANSI_STRING ClientAlias;
ANSI_STRING String;
ANSI_STRING ErrorMessage;
HRESULT TranslationTableLookupResult;
DWORD LookupAddress; // host order
SOCKADDR_IN ServerAddress;
DWORD LocalToRequestedAddress; // host order
DWORD AddressToReport;
SearchResponse_entry_attributes * Iter;
SearchResponse_entry_attributes_Seq_values * ValueSequence;
SearchResponse_entry_attributes_Seq * Attribute;
AttributeValue * Attribute_IPAddress;
AttributeValue * Attribute_Sttl;
CHAR IPAddressText [0x20];
BOOL Result = FALSE;
LDAP_OPERATION * Operation;
SOCKET UDP_Socket = INVALID_SOCKET;
assert (Message);
Response = &Message -> protocolOp.u.searchResponse;
AssertLocked();
if (!FindOperationByMessageID (Message -> messageID, &Operation)) {
return FALSE;
}
switch (Response -> choice) {
case entry_choice:
// Determine address of the server
if (!ServerSocket.GetRemoteAddress (&ServerAddress)) {
return FALSE;
}
if(Response -> choice == entry_choice) {
if (Operation -> Type == LDAP_OPERATION_SEARCH) {
// Parse this object's name to get alias and IP address
InitializeAnsiString (&ObjectName, &Response -> u.entry.objectName);
ParseObjectName (&ObjectName, &ObjectNameElements);
// scan through the set of attributes
// find the ones of interest
Attribute_IPAddress = NULL;
Attribute_Sttl = NULL;
for (Iter = Response -> u.entry.attributes; Iter; Iter = Iter -> next) {
Attribute = &Iter -> value;
InitializeAnsiString (&AttributeTag, &Attribute -> type);
if (Attribute -> values) {
if (RtlEqualStringConst (&AttributeTag, &LdapText_Attribute_sipaddress, TRUE)
|| RtlEqualStringConst (&AttributeTag, &LdapText_Attribute_ipAddress, TRUE)) {
Attribute_IPAddress = &Attribute -> values -> value;
} else if (RtlEqualStringConst (&AttributeTag, &LdapText_Attribute_sttl, TRUE)) {
Attribute_Sttl = &Attribute -> values -> value;
}
// else, we aren't interested in the attribute
}
else {
// else, the attribute has no values
}
}
// make sure that the alias is present
ClientAlias = ObjectNameElements.CN;
if (ClientAlias.Length == 0) {
Result = FALSE;
} else {
if (Attribute_Sttl) {
// NetMeeting refreshes its registrations on an ILS by periodically sending a SearchRequest with
// attribute "sttl"
DebugF (_T ("LDAP: 0x%x server %08X has approved increase in lifetime of entry (%.*S) by %d seconds.\n"),
this,
ntohl (ServerAddress.sin_addr.s_addr),
ANSI_STRING_PRINTF (&ClientAlias),
Operation -> EntryTimeToLive);
LdapTranslationTable.RefreshEntry (&ClientAlias,
&ObjectName,
Operation -> ClientAddress,
&Operation -> ServerAddress,
Operation -> EntryTimeToLive);
} else {
// make sure ip address attribute is present
if (!Attribute_IPAddress) {
Result = FALSE;
} else {
// see whether there is a registration entry in the translation table
// with the same alias
TranslationTableLookupResult = LdapQueryTableByAliasServer (&ClientAlias,
&Operation -> ServerAddress,
&LookupAddress);
// If an entry with the same alias is not in the table,
// then we send the SearchResponse PDU unmodified to the requestor
if (S_OK != TranslationTableLookupResult) {
Result = FALSE;
} else {
// Otherwise, we decide what would be the correct address
// to report to the requestor. We will either report the address
// read from the Address Translation Table, or the address
// of the interface requestor used to reach us. The decision will be
// made based on the interface address requestor used to reach us, and
// address of the interface we would use to reach the requestee.
if (INADDR_NONE == SourceInterfaceAddress) {
DebugF (_T("LDAP: 0x%x failed to get best interface address for the requestor.\n"), this);
return FALSE;
}
// Determine on which interface we would connect to the entity whose address was requested
if (GetBestInterfaceAddress (LookupAddress, &LocalToRequestedAddress)) {
DebugF (_T("LDAP: 0x%x failed to get best interface address for the requestee.\n"), this);
return FALSE;
}
// The default reporting behaviour is to report the address of the best interface to reach the requestor,
// thus shielding the requestor from the knowledge of the network internals.
//
// However, there are three exceptions to this rule, when we report the actual address stored
// in the Address Translation Table. These exceptions are as follows:
//
// 1. If the requestor is local.
// In this case we assume that the requestor can directly reach the requestee, and thus
// an H.323 call between them doesn't have to be routed via us.
//
// 2. If local H323 routing is enabled, AND the requestor and the requestee have the same address
// This is necessary to allow the requestor to do something special with the calls to itself
// (NetMeeting, for example, prohibits such calls).
//
// 3. If local H323 routing is disabled, and requestor and requestee are reachable through the
// same interface.
// In these case we assume that the requestor and the requestee can directly reach one another,
// and thus an H.323 call between them doesn't have to be routed via us.
//
if (::NhIsLocalAddress (SourceAddress.sin_addr.s_addr)) {
AddressToReport = LookupAddress;
} else {
if (EnableLocalH323Routing) {
AddressToReport = (ntohl (SourceAddress.sin_addr.s_addr) == LookupAddress) ?
LookupAddress :
SourceInterfaceAddress;
} else {
AddressToReport = (SourceInterfaceAddress == LocalToRequestedAddress) ?
LookupAddress :
SourceInterfaceAddress;
}
}
assert (Attribute_IPAddress);
if (RtlIntegerToChar (htonl (AddressToReport),
10, 0x1F, IPAddressText) != STATUS_SUCCESS) {
DebugF (_T("LDAP: 0x%x failed to convert IP address to text -- internal error.\n"), this);
Result = FALSE;
} else {
DebugF (_T("LDAP: 0x%x read %08X in table. Reporting %08X to requestor %08X.\n"),
this,
LookupAddress, AddressToReport, ntohl (SourceAddress.sin_addr.s_addr));
// Save the old value of the IP address, - we will need to restore
// the PDU structure later.
IPAddressOldValue = *Attribute_IPAddress;
// now, in-place, we modify the PDU structure,
// reencode it, send it, undo the modification
// (so ASN1Free_SearchResponse doesn't act up)
Attribute_IPAddress -> value = (PUCHAR) IPAddressText;
Attribute_IPAddress -> length = strlen (IPAddressText);
PumpServerToClient.EncodeSendMessage (Message);
// switch back so we don't a/v when decoder frees pdu
*Attribute_IPAddress = IPAddressOldValue;
Result = TRUE;
}
}
}
}
}
} else {
DebugF (_T("LDAP: 0x%x received response with message ID (%u), and found matching pending request, but the type of the request does not match (%d).\n"),
this,
Message -> messageID,
Operation -> Type);
Result = FALSE;
}
}
break;
case resultCode_choice:
// We free the operation and associated memory on SearchResponses containing result
// code, no matter whether the code indicated success or failure
InitializeAnsiString (&ErrorMessage, &Response -> u.resultCode.errorMessage);
DebugF (_T("LDAP: 0x%x result in SearchResponse (%d) (code=%d message=(%*.S)).\n"),
this,
Message -> messageID,
Response -> u.resultCode.resultCode,
ANSI_STRING_PRINTF (&ErrorMessage));
Operation -> FreeContents ();
OperationArray.DeleteEntry (Operation);
break;
default:
AssertNeverReached ();
}
return Result;
}
// this method does not assume ownership of the LdapMessage structure,
// which has scope only of this call stack.
BOOL LDAP_CONNECTION::ProcessLdapMessage (
IN LDAP_PUMP * Pump,
IN LDAPMessage * LdapMessage)
{
assert (Pump);
assert (LdapMessage);
if (Pump == &PumpClientToServer) {
switch (LdapMessage -> protocolOp.choice) {
case addRequest_choice:
DebugF (_T("LDAP: 0x%x received AddRequest (%d).\n"), this, LdapMessage -> messageID);
return ProcessAddRequest (LdapMessage);
case modifyRequest_choice:
DebugF (_T("LDAP: 0x%x received ModifyRequest (%d).\n"), this, LdapMessage -> messageID);
return ProcessModifyRequest (LdapMessage -> messageID, &LdapMessage -> protocolOp.u.modifyRequest);
case delRequest_choice:
DebugF (_T("LDAP: 0x%x received DeleteRequest (%d).\n"), this, LdapMessage -> messageID);
return ProcessDeleteRequest (LdapMessage -> messageID, &LdapMessage -> protocolOp.u.delRequest);
case searchRequest_choice:
DebugF (_T("LDAP: 0x%x received SearchRequest (%d).\n"), this, LdapMessage -> messageID);
return ProcessSearchRequest (LdapMessage);
case bindRequest_choice:
DebugF (_T("LDAP: 0x%x received BindRequest (%d).\n"), this, LdapMessage -> messageID);
return FALSE;
case abandonRequest_choice:
DebugF (_T("LDAP: 0x%x received AbandonRequest (%d).\n"), this, LdapMessage -> messageID);
return FALSE;
case unbindRequest_choice:
DebugF (_T("LDAP: 0x%x received UnbindRequest (%d).\n"), this, LdapMessage -> messageID);
return FALSE;
case modifyRDNRequest_choice:
DebugF (_T("LDAP: 0x%x received ModifyRDNRequest (%d).\n"), this, LdapMessage -> messageID);
return FALSE;
case compareDNRequest_choice:
DebugF (_T("LDAP: 0x%x received CompareDNRequest (%d).\n"), this, LdapMessage -> messageID);
return FALSE;
default:
return FALSE;
}
return FALSE;
}
else if (Pump == &PumpServerToClient) {
switch (LdapMessage -> protocolOp.choice) {
case addResponse_choice:
DebugF (_T("LDAP: 0x%x received AddResponse (%d).\n"), this, LdapMessage -> messageID);
ProcessAddResponse (LdapMessage);
break;
case modifyResponse_choice:
DebugF (_T("LDAP: 0x%x received ModifyResponse (%d).\n"), this, LdapMessage -> messageID);
ProcessModifyResponse (LdapMessage -> messageID, &LdapMessage -> protocolOp.u.modifyResponse);
break;
case delResponse_choice:
DebugF (_T("LDAP: 0x%x received DeleteResponse (%d).\n"), this, LdapMessage -> messageID);
ProcessDeleteResponse (LdapMessage -> messageID, &LdapMessage -> protocolOp.u.delResponse);
break;
case searchResponse_choice:
DebugF (_T("LDAP: 0x%x received SearchResponse (%d).\n"), this, LdapMessage -> messageID);
return ProcessSearchResponse (LdapMessage);
break;
case bindResponse_choice:
DebugF (_T("LDAP: 0x%x received BindResponse (%d).\n"), this, LdapMessage -> messageID);
return FALSE;
case modifyRDNResponse_choice:
DebugF (_T("LDAP: 0x%x received ModifyRDNResponse (%d).\n"), this, LdapMessage -> messageID);
return FALSE;
case compareDNResponse_choice:
DebugF (_T("LDAP: 0x%x received CompareDNResponse (%d).\n"), this, LdapMessage -> messageID);
return FALSE;
default:
break;
}
return FALSE;
}
else {
AssertNeverReached();
return FALSE;
}
}
void LDAP_CONNECTION::ProcessBuffer (
IN LDAP_PUMP * Pump,
IN LDAP_BUFFER * Buffer)
{
ASN1error_e Error;
LDAPMessage * PduStructure;
ASN1decoding_t Decoder;
assert (Pump);
assert (Buffer);
// decode the PDU
Error = ASN1_CreateDecoder (LDAP_Module, &Decoder, Buffer -> Data.Data, Buffer -> Data.Length, NULL);
if (Error == ASN1_SUCCESS) {
PduStructure = NULL;
Error = ASN1_Decode (Decoder, (PVOID *) &PduStructure, LDAPMessage_ID, 0, NULL, 0);
if (ASN1_SUCCEEDED (Error)) {
if (ProcessLdapMessage (Pump, PduStructure)) {
// a TRUE return value indicates that ProcessLdapMessage interpreted
// and acted on the contents of PduStructure. therefore, the
// original PDU is no longer needed, and is destroyed.
delete Buffer;
}
else {
// a FALSE return value indicates that ProcessLdapMessage did NOT
// interpret the contents of PduStructure, and that no data has been
// sent to the other socket. In this case, we forward the original PDU.
Pump -> SendQueueBuffer (Buffer);
}
ASN1_FreeDecoded (Decoder, PduStructure, LDAPMessage_ID);
}
else {
DebugF (_T("LDAP: 0x%x failed to decode pdu, ASN.1 error %d, forwarding pdu without interpreting contents.\n"),
this,
Error);
#if DBG
if (DebugLevel > 1) {
DumpMemory (Buffer -> Data.Data, Buffer -> Data.Length);
BerDump (Buffer -> Data.Data, Buffer -> Data.Length);
ASN1_Decode (Decoder, (PVOID *) &PduStructure, LDAPMessage_ID, 0, Buffer -> Data.Data, Buffer -> Data.Length);
}
#endif
Pump -> SendQueueBuffer (Buffer);
}
ASN1_CloseDecoder (Decoder);
}
else {
DebugF (_T("LDAP: 0x%x failed to create ASN.1 decoder, ASN.1 error %08X, forwarding pdu without interpreting contents.\n"),
this,
Error);
Pump -> SendQueueBuffer (Buffer);
}
}
// static
INT LDAP_CONNECTION::BinarySearchOperationByMessageID (
IN const LDAP_MESSAGE_ID * SearchKey,
IN const LDAP_OPERATION * Comparand)
{
if (*SearchKey < Comparand -> MessageID) return -1;
if (*SearchKey > Comparand -> MessageID) return 1;
return 0;
}
BOOL LDAP_CONNECTION::FindOperationIndexByMessageID (
IN LDAP_MESSAGE_ID MessageID,
OUT DWORD * ReturnIndex)
{
return OperationArray.BinarySearch ((SEARCH_FUNC_LDAP_OPERATION)BinarySearchOperationByMessageID, &MessageID, ReturnIndex);
}
BOOL LDAP_CONNECTION::FindOperationByMessageID (
IN LDAP_MESSAGE_ID MessageID,
OUT LDAP_OPERATION ** ReturnOperation)
{
DWORD Index;
if (OperationArray.BinarySearch ((SEARCH_FUNC_LDAP_OPERATION)BinarySearchOperationByMessageID, &MessageID, &Index)) {
*ReturnOperation = &OperationArray[Index];
return TRUE;
}
else {
*ReturnOperation = NULL;
return FALSE;
}
}
void LDAP_CONNECTION::OnStateChange (
LDAP_SOCKET * ContainedSocket,
LDAP_SOCKET::STATE NewSocketState)
{
assert (ContainedSocket == &ClientSocket || ContainedSocket == &ServerSocket);
AssertLocked();
switch (NewSocketState) {
case LDAP_SOCKET::STATE_CONNECT_PENDING:
// Client socket transitions directly from
// STATE_NONE to STATE_CONNECTED
assert (ContainedSocket != &ClientSocket);
State = STATE_CONNECT_PENDING;
break;
case LDAP_SOCKET::STATE_CONNECTED:
if (ClientSocket.GetState() == LDAP_SOCKET::STATE_CONNECTED
&& ServerSocket.GetState() == LDAP_SOCKET::STATE_CONNECTED) {
State = STATE_CONNECTED;
StartIo();
}
break;
case LDAP_SOCKET::STATE_TERMINATED:
Terminate ();
break;
}
}
void LDAP_CONNECTION::Terminate (void)
{
switch (State) {
case STATE_TERMINATED:
// nothing to do
break;
default:
State = STATE_TERMINATED;
ClientSocket.Terminate();
ServerSocket.Terminate();
PumpClientToServer.Terminate();
PumpServerToClient.Terminate();
LdapConnectionArray.RemoveConnection (this);
break;
}
}
void LDAP_CONNECTION::TerminateExternal (void)
{
Lock ();
Terminate ();
Unlock ();
}
BOOL
LDAP_CONNECTION::IsConnectionThrough (
IN DWORD InterfaceAddress // host order
)
/*++
Routine Description:
Determines whether the connection goes through the
interface specified
Arguments:
InterfaceAddress - address of the interface for which
the determination is to be made.
Return Values:
TRUE - if the connection being proxied goes through the
interface specified
FALSE - if the connection being proxied does not go through the
interface specified
Notes:
--*/
{
BOOL IsThrough;
IsThrough = (InterfaceAddress == SourceInterfaceAddress) ||
(InterfaceAddress == DestinationInterfaceAddress);
return IsThrough;
} // LDAP_CONNECTION::IsConnectionThrough
// LDAP_CONNECTION_ARRAY ----------------------------------------------
LDAP_CONNECTION_ARRAY::LDAP_CONNECTION_ARRAY (void) {
IsEnabled = FALSE;
}
void LDAP_CONNECTION_ARRAY::RemoveConnection (
LDAP_CONNECTION * LdapConnection)
{
LDAP_CONNECTION ** Pos;
LDAP_CONNECTION ** End;
BOOL DoRelease;
Lock();
// linear scan, yick
DoRelease = FALSE;
ConnectionArray.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
if (*Pos == LdapConnection) {
// swap with last entry
// quick way to delete entry from table
*Pos = *(End - 1);
ConnectionArray.Length--;
DoRelease = TRUE;
break;
}
}
Unlock();
if (DoRelease) {
LdapConnection -> Release();
}
else {
// when could this happen?
// perhaps a harmless race condition?
DebugF (_T("LDAP: 0x%x could not be removed from table -- was not in table to begin with.\n"), LdapConnection);
}
}
void LDAP_CONNECTION_ARRAY::OnInterfaceShutdown (
IN DWORD InterfaceAddress) { // host order
DWORD ArrayIndex = 0;
LDAP_CONNECTION * Connection;
LDAP_CONNECTION ** ConnectionHolder = NULL;
DYNAMIC_ARRAY <LDAP_CONNECTION *> TempArray;
Lock ();
while (ArrayIndex < ConnectionArray.GetLength ()) {
Connection = ConnectionArray [ArrayIndex];
if (Connection -> IsConnectionThrough (InterfaceAddress)) {
DebugF (_T("LDAP: 0x%x terminating (killing all connections through %08X).\n"),
Connection, InterfaceAddress);
ConnectionHolder = TempArray.AllocAtEnd ();
if (NULL == ConnectionHolder) {
Debug (_T("LDAP_CONNECTION_ARRAY::OnInterfaceShutdown - unable to grow array.\n"));
} else {
Connection -> AddRef ();
*ConnectionHolder = Connection;
}
}
ArrayIndex++;
}
Unlock ();
ArrayIndex = 0;
while (ArrayIndex < TempArray.GetLength ()) {
Connection = TempArray[ArrayIndex];
Connection -> TerminateExternal ();
Connection -> Release ();
ArrayIndex++;
}
}
void LDAP_CONNECTION_ARRAY::Start (void)
{
Lock ();
IsEnabled = TRUE;
Unlock ();
}
void LDAP_CONNECTION_ARRAY::Stop (void)
{
LDAP_CONNECTION * LdapConnection;
Lock ();
IsEnabled = FALSE;
while (ConnectionArray.GetLength()) {
LdapConnection = ConnectionArray[0];
LdapConnection -> AddRef ();
Unlock ();
LdapConnection -> TerminateExternal ();
Lock ();
LdapConnection -> Release ();
}
ConnectionArray.Free ();
Unlock ();
}
HRESULT LDAP_CONNECTION_ARRAY::InsertConnection (
LDAP_CONNECTION * LdapConnection)
{
LDAP_CONNECTION ** ConnectionHolder = NULL;
HRESULT Result;
Lock ();
if (IsEnabled) {
if (ConnectionArray.Length <= LDAP_MAX_CONNECTIONS) {
ConnectionHolder = ConnectionArray.AllocAtEnd ();
if (NULL == ConnectionHolder) {
Result = E_OUTOFMEMORY;
} else {
LdapConnection -> AddRef ();
*ConnectionHolder = LdapConnection;
Result = S_OK;
}
} else {
return E_ABORT;
}
} else {
Result = E_FAIL;
}
Unlock ();
return Result;
}
// LDAP_ACCEPT ----------------------------------------------
LDAP_ACCEPT::LDAP_ACCEPT (void)
{
}
// static
void
LDAP_ACCEPT::AsyncAcceptFunction (
IN PVOID Context,
IN SOCKET Socket,
IN SOCKADDR_IN * LocalAddress,
IN SOCKADDR_IN * RemoteAddress
)
{
HRESULT Result;
Result = AsyncAcceptFunctionInternal (
Context,
Socket,
LocalAddress,
RemoteAddress
);
if (S_OK != Result) {
if (INVALID_SOCKET != Socket) {
closesocket (Socket);
Socket = INVALID_SOCKET;
}
}
}
// static
HRESULT
LDAP_ACCEPT::AsyncAcceptFunctionInternal (
IN PVOID Context,
IN SOCKET Socket,
IN SOCKADDR_IN * LocalAddress,
IN SOCKADDR_IN * RemoteAddress
)
{
LDAP_CONNECTION * LdapConnection;
HRESULT Result;
NAT_KEY_SESSION_MAPPING_EX_INFORMATION RedirectInformation;
ULONG RedirectInformationLength;
ULONG Error;
DWORD BestInterfaceAddress;
SOCKADDR_IN DestinationAddress;
DebugF (_T("LDAP: ----------------------------------------------------------------------\n"));
#if DBG
ExposeTimingWindow ();
#endif
// a new LDAP connection has been accepted from the network.
// first, we determine the original addresses of the transport connection.
// if the connection was redirected to our socket (due to NAT),
// then the query of the NAT redirect table will yield the original transport addresses.
// if an errant client has connected to our service, well, we really didn't
// intend for that to happen, so we just immediately close the socket.
RedirectInformationLength = sizeof RedirectInformation;
Result = NatLookupAndQueryInformationSessionMapping (
NatHandle,
IPPROTO_TCP,
LocalAddress -> sin_addr.s_addr,
LocalAddress -> sin_port,
RemoteAddress -> sin_addr.s_addr,
RemoteAddress -> sin_port,
&RedirectInformation,
&RedirectInformationLength,
NatKeySessionMappingExInformation);
if (STATUS_SUCCESS != Result) {
DebugErrorF (Result, _T("LDAP: new connection was accepted from (%08X:%04X), but it is not in the NAT redirect table -- rejecting connection.\n"),
ntohl (RemoteAddress -> sin_addr.s_addr),
ntohs (RemoteAddress -> sin_port));
return Result;
}
Error = GetBestInterfaceAddress (ntohl (RedirectInformation.DestinationAddress), &BestInterfaceAddress);
if (ERROR_SUCCESS != Error) {
if (WSAEHOSTUNREACH == Error) {
Error = RasAutoDialSharedConnection ();
if (ERROR_SUCCESS != Error) {
DebugF (_T("LDAP: RasAutoDialSharedConnection failed. Error=%d\n"), Error);
}
} else {
DebugError (Error, _T("LDAP: Failed to get interface address for the destination.\n"));
return HRESULT_FROM_WIN32 (Error);
}
}
#if DBG
BOOL IsPrivateOrLocalSource;
BOOL IsPublicDestination;
Result = ::IsPrivateAddress (ntohl (RedirectInformation.SourceAddress), &IsPrivateOrLocalSource);
if (S_OK != Result) {
return Result;
}
IsPrivateOrLocalSource = IsPrivateOrLocalSource || ::NhIsLocalAddress (RedirectInformation.SourceAddress);
Result = ::IsPublicAddress (ntohl (RedirectInformation.DestinationAddress), &IsPublicDestination);
if (S_OK != Result) {
return Result;
}
if (::NhIsLocalAddress (RedirectInformation.SourceAddress) &&
::NhIsLocalAddress (RedirectInformation.DestinationAddress)) {
Debug (_T("LDAP: New LOCAL connection.\n"));
} else {
if (IsPrivateOrLocalSource && IsPublicDestination) {
Debug (_T("LDAP: New OUTBOUND connection.\n"));
} else {
Debug (_T("LDAP: New INBOUND connection.\n"));
}
}
#endif // DBG
DebugF (_T("LDAP: Connection redirected: (%08X:%04X -> %08X:%04X) => (%08X:%04X -> %08X:%04X).\n"),
ntohl (RedirectInformation.SourceAddress),
ntohs (RedirectInformation.SourcePort),
ntohl (RedirectInformation.DestinationAddress),
ntohs (RedirectInformation.DestinationPort),
ntohl (RedirectInformation.NewSourceAddress),
ntohs (RedirectInformation.NewSourcePort),
ntohl (RedirectInformation.NewDestinationAddress),
ntohs (RedirectInformation.NewDestinationPort));
// Create new LDAP_CONNECTION object
LdapConnection = new LDAP_CONNECTION (&RedirectInformation);
if (!LdapConnection) {
DebugF(_T("LDAP: Failed to allocate LDAP_CONNECTION.\n"));
return E_OUTOFMEMORY;
}
LdapConnection -> AddRef ();
Result = LdapConnection -> Initialize (&RedirectInformation);
if (S_OK == Result) {
DestinationAddress.sin_family = AF_INET;
DestinationAddress.sin_addr.s_addr = RedirectInformation.DestinationAddress;
DestinationAddress.sin_port = RedirectInformation.DestinationPort;
if (S_OK == LdapConnectionArray.InsertConnection (LdapConnection)) {
Result = LdapConnection -> AcceptSocket (Socket,
LocalAddress,
RemoteAddress,
&DestinationAddress);
if (S_OK != Result) {
DebugF (_T("LDAP: 0x%x accepted new LDAP client, but failed to proceed.\n"), LdapConnection);
// Probably there was something wrong with just this
// Accept failure. Continue to accept more LDAP connections.
}
}
} else {
DebugF (_T("LDAP: 0x%x failed to initialize.\n"), LdapConnection);
}
LdapConnection -> Release ();
return Result;
}
HRESULT LDAP_ACCEPT::StartLoopbackNatRedirects (void) {
NTSTATUS Status;
Status = NatCreateDynamicAdapterRestrictedPortRedirect (
NatRedirectFlagLoopback | NatRedirectFlagSendOnly,
IPPROTO_TCP,
htons (LDAP_STANDARD_PORT),
LdapListenSocketAddress.sin_addr.s_addr,
LdapListenSocketAddress.sin_port,
::NhMapAddressToAdapter (htonl (INADDR_LOOPBACK)),
MAX_LISTEN_BACKLOG,
&LoopbackRedirectHandle1);
if (Status != STATUS_SUCCESS) {
LoopbackRedirectHandle1 = NULL;
DebugError (Status, _T("LDAP: Failed to create local dynamic redirect #1.\n"));
return (HRESULT) Status;
}
DebugF (_T ("LDAP: Connections traversing loopback interface to port %04X will be redirected to %08X:%04X.\n"),
LDAP_STANDARD_PORT,
ntohl (LdapListenSocketAddress.sin_addr.s_addr),
ntohs (LdapListenSocketAddress.sin_port));
Status = NatCreateDynamicAdapterRestrictedPortRedirect (
NatRedirectFlagLoopback | NatRedirectFlagSendOnly,
IPPROTO_TCP,
htons (LDAP_ALTERNATE_PORT),
LdapListenSocketAddress.sin_addr.s_addr,
LdapListenSocketAddress.sin_port,
::NhMapAddressToAdapter (htonl (INADDR_LOOPBACK)),
MAX_LISTEN_BACKLOG,
&LoopbackRedirectHandle2);
if (Status != STATUS_SUCCESS) {
NatCancelDynamicRedirect (LoopbackRedirectHandle1);
LoopbackRedirectHandle1 = NULL;
LoopbackRedirectHandle2 = NULL;
DebugError (Status, _T("LDAP: Failed to create local dynamic redirect #2.\n"));
return (HRESULT) Status;
}
DebugF (_T ("LDAP: Connections traversing loopback interface to port %04X will be redirected to %08X:%04X.\n"),
LDAP_ALTERNATE_PORT,
ntohl (LdapListenSocketAddress.sin_addr.s_addr),
ntohs (LdapListenSocketAddress.sin_port));
return (HRESULT) Status;
}
HRESULT LDAP_ACCEPT::CreateBindSocket (
void) {
HRESULT Result;
SOCKADDR_IN SocketAddress;
SocketAddress.sin_family = AF_INET;
SocketAddress.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
SocketAddress.sin_port = htons (0); // request dynamic port
Result = AsyncAcceptContext.StartIo (
&SocketAddress,
AsyncAcceptFunction,
NULL);
if (Result != S_OK) {
DebugError (Result, _T("LDAP: Failed to create and bind socket.\n"));
return Result;
}
DebugF (_T("LDAP: Asynchronous Accept started.\n"));
Result = AsyncAcceptContext.GetListenSocketAddress (&LdapListenSocketAddress);
if (Result != S_OK) {
DebugError (Result, _T("LDAP: Failed to get listen socket address.\n"));
return Result;
}
return S_OK;
}
HRESULT LDAP_ACCEPT::Start (void)
{
HRESULT Result;
Result = CreateBindSocket ();
if (S_OK == Result) {
Result = StartLoopbackNatRedirects ();
if (S_OK == Result) {
return S_OK;
}
CloseSocket ();
}
return Result;
}
void LDAP_ACCEPT::Stop (void) {
StopLoopbackNatRedirects ();
CloseSocket ();
}
void LDAP_ACCEPT::StopLoopbackNatRedirects (void) {
if (LoopbackRedirectHandle1) {
NatCancelDynamicRedirect (LoopbackRedirectHandle1);
LoopbackRedirectHandle1 = NULL;
}
if (LoopbackRedirectHandle2) {
NatCancelDynamicRedirect (LoopbackRedirectHandle2);
LoopbackRedirectHandle2 = NULL;
}
}
void LDAP_ACCEPT::CloseSocket (void) {
ZeroMemory ((PVOID)&LdapListenSocketAddress, sizeof (SOCKADDR_IN));
AsyncAcceptContext.StopWait ();
}
LDAP_BUFFER::LDAP_BUFFER (void)
{
}
LDAP_BUFFER::~LDAP_BUFFER (void)
{
}
// LDAP_CODER ---------------------------------------------------------------------
LDAP_CODER::LDAP_CODER (void)
{
Encoder = NULL;
Decoder = NULL;
LDAP_Module_Startup();
}
LDAP_CODER::~LDAP_CODER (void)
{
Encoder = NULL;
Decoder = NULL;
LDAP_Module_Cleanup();
}
DWORD LDAP_CODER::Start (void)
{
DWORD Status;
ASN1error_e Error;
Lock();
Status = ERROR_SUCCESS;
Error = ASN1_CreateEncoder (LDAP_Module, &Encoder, NULL, 0, NULL);
if (ASN1_FAILED (Error)) {
DebugF (_T("LDAP: Failed to initialize LDAP ASN.1 BER encoder, 0x%08X.\n"), Error);
Encoder = NULL;
Status = ERROR_GEN_FAILURE;
}
Error = ASN1_CreateDecoder (LDAP_Module, &Decoder, NULL, 0, NULL);
if (ASN1_FAILED (Error)) {
DebugF (_T("LDAP: Failed to initialize LDAP ASN.1 BER decoder, 0x%08X.\n"), Error);
Decoder = NULL;
Status = ERROR_GEN_FAILURE;
}
Unlock();
return Status;
}
void LDAP_CODER::Stop (void)
{
Lock();
if (Encoder) {
ASN1_CloseEncoder (Encoder);
Encoder = NULL;
}
if (Decoder) {
ASN1_CloseDecoder (Decoder);
Decoder = NULL;
}
Unlock();
}
ASN1error_e LDAP_CODER::Decode (
IN LPBYTE Data,
IN DWORD Length,
OUT LDAPMessage ** ReturnPduStructure,
OUT DWORD * ReturnIndex)
{
ASN1error_e Error;
assert (Data);
assert (ReturnPduStructure);
assert (ReturnIndex);
Lock();
if (Decoder) {
#if DBG
BerDump (Data, Length);
#endif
Error = ASN1_Decode (
Decoder,
(PVOID *) ReturnPduStructure,
LDAPMessage_ID,
ASN1DECODE_SETBUFFER,
Data,
Length);
switch (Error) {
case ASN1_SUCCESS:
// successfully decoded pdu
*ReturnIndex = Decoder -> len;
assert (*ReturnPduStructure);
DebugF (_T("LDAP: Successfully decoded PDU, submitted buffer length %d, used %d bytes.\n"),
Length,
*ReturnIndex);
break;
case ASN1_ERR_EOD:
// not enough data has been accumulated yet
*ReturnIndex = 0;
*ReturnPduStructure = NULL;
DebugF (_T("LDAP: Cannot yet decode PDU, not enough data submitted (%d bytes in buffer).\n"),
Length);
break;
default:
if (ASN1_FAILED (Error)) {
DebugF (_T("LDAP: Failed to decode PDU, for unknown reasons, 0x%08X.\n"),
Error);
}
else {
DebugF (_T("LDAP: PDU decoded, but with warning code, 0x%08X.\n"),
Error);
*ReturnIndex = Decoder -> len;
}
break;
}
}
else {
Debug (_T("LDAP: cannot decode pdu, because decoder was not initialized.\n"));
Error = ASN1_ERR_INTERNAL;
}
Unlock();
return Error;
}
// LDAP_PUMP --------------------------------------------------------------
LDAP_PUMP::LDAP_PUMP (
IN LDAP_CONNECTION * ArgConnection,
IN LDAP_SOCKET * ArgSource,
IN LDAP_SOCKET * ArgDest)
{
assert (ArgConnection);
assert (ArgSource);
assert (ArgDest);
Connection = ArgConnection;
Source = ArgSource;
Dest = ArgDest;
IsPassiveDataTransfer = FALSE;
}
LDAP_PUMP::~LDAP_PUMP (void)
{
}
void LDAP_PUMP::Terminate (void)
{
}
void LDAP_PUMP::Start (void)
{
Source -> RecvIssue();
}
void LDAP_PUMP::Stop (void)
{
}
// called only by source socket OnRecvComplete
void LDAP_PUMP::OnRecvBuffer (
IN LDAP_BUFFER * Buffer)
{
if (IsActivelyPassingData ()) {
Connection -> ProcessBuffer (this, Buffer);
} else {
SendQueueBuffer (Buffer);
}
}
void LDAP_PUMP::OnSendDrain (void)
{
Source -> RecvIssue();
}
BOOL LDAP_PUMP::CanIssueRecv (void)
{
return !Dest -> SendOverlapped.IsPending;
}
void LDAP_PUMP::SendQueueBuffer (
IN LDAP_BUFFER * Buffer)
{
Dest -> SendQueueBuffer (Buffer);
}
void LDAP_PUMP::EncodeSendMessage (
IN LDAPMessage * Message)
{
LDAP_BUFFER * Buffer;
ASN1encoding_t Encoder;
ASN1error_e Error;
Buffer = new LDAP_BUFFER;
if (!Buffer) {
Debug (_T("LDAP: EncodeSendMessage: allocation failure.\n"));
return;
}
Error = ASN1_CreateEncoder (LDAP_Module, &Encoder, NULL, 0, NULL);
if (ASN1_FAILED (Error)) {
DebugF (_T("LDAP: EncodeSendMessage: failed to create ASN.1 encoder, error 0x%08X.\n"),
Error);
delete Buffer;
return;
}
Error = ASN1_Encode (Encoder, Message, LDAPMessage_ID, ASN1ENCODE_ALLOCATEBUFFER, NULL, 0);
if (ASN1_FAILED (Error)) {
DebugF (_T("LDAP: Failed to encode LDAP message, error 0x%08X.\n"), Error);
ASN1_CloseEncoder (Encoder);
delete Buffer;
return;
}
if (Buffer -> Data.Grow (Encoder -> len)) {
memcpy (Buffer -> Data.Data, Encoder -> buf, Encoder -> len);
Buffer -> Data.Length = Encoder -> len;
ASN1_FreeEncoded (Encoder, Encoder -> buf);
SendQueueBuffer (Buffer);
Buffer = NULL;
}
else {
delete Buffer;
}
ASN1_CloseEncoder (Encoder);
}
BOOL LDAP_PUMP::IsActivelyPassingData (void) const {
return !IsPassiveDataTransfer;
}
void LDAP_PUMP::StartPassiveDataTransfer (void) {
IsPassiveDataTransfer = TRUE;
}