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