4624 lines
138 KiB
C++
4624 lines
138 KiB
C++
/*++
|
||
|
||
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;
|
||
|
||
}
|