//////////////////////////////////////////////////////////////////
//
// New Neighboring System for GNU Gatekeeper
//
// Copyright (c) Citron Network Inc. 2002-2003
//
// This work is published under the GNU Public License (GPL)
// see file COPYING for details.
// We also explicitely grant the right to link this code
// with the OpenH323 library.
//
// initial author: Chih-Wei Huang <cwhuang@linux.org.tw>
// initial version: 05/30/2003
//
//////////////////////////////////////////////////////////////////

#if defined(_WIN32) && (_MSC_VER <= 1200)
#pragma warning(disable:4786) // warning about too long debug symbol off
#pragma warning(disable:4284)
#endif

#include <ptlib.h>
#include <h323pdu.h>
#include <ptclib/cypher.h>
#include "gk_const.h"
#include "stl_supp.h"
#include "GkClient.h"
#include "RasPDU.h"
#include "RasSrv.h"
#include "RasTbl.h"
#include "Routing.h"
#include "sigmsg.h"
#include "cisco.h"
#include "Neighbor.h"

using std::multimap;
using std::make_pair;
using std::find_if;
using std::bind2nd;
using std::equal_to;
using std::mem_fun;
using Routing::Route;

namespace Neighbors {


const char *NeighborSection = "RasSrv::Neighbors";
const char *LRQFeaturesSection = "RasSrv::LRQFeatures";


class OldGK : public Neighbor {
	// override from class Neighbor
	virtual bool SetProfile(const PString &, const PString &);
};

class GnuGK : public Neighbor {
	// override from class Neighbor
	virtual bool OnSendingLRQ(H225_LocationRequest &, const AdmissionRequest &);
	virtual bool OnSendingLRQ(H225_LocationRequest &, const SetupRequest &);
	virtual bool OnSendingLRQ(H225_LocationRequest &, const FacilityRequest &);
	virtual bool IsAcceptable(RasMsg *ras) const;
};

class CiscoGK : public Neighbor {
public:
	// override from class Neighbor
	virtual bool OnSendingLRQ(H225_LocationRequest &, const AdmissionRequest &);
	virtual bool OnSendingLRQ(H225_LocationRequest &, const LocationRequest &);
	virtual bool OnSendingLRQ(H225_LocationRequest &, const SetupRequest &);
	virtual bool CheckReply(RasMsg *msg) const;
};

// stupid Clarent gatekeeper
class ClarentGK : public Neighbor {
	// override from class Neighbor
	virtual bool OnSendingLRQ(H225_LocationRequest &);
};

// a gatekeeper by Korea vendor
class GlonetGK : public Neighbor {
	// override from class Neighbor
	virtual bool OnSendingLRQ(H225_LocationRequest &, const AdmissionRequest &);
	virtual bool OnSendingLRQ(H225_LocationRequest &, const LocationRequest &);
	virtual bool OnSendingLRQ(H225_LocationRequest &, const SetupRequest &);
	virtual bool OnSendingLRQ(H225_LocationRequest &, const FacilityRequest &);

	bool BuildLRQ(H225_LocationRequest &, WORD);
};

namespace { // anonymous namespace
	SimpleCreator<OldGK> OldGKCreator("OldGK");
	SimpleCreator<GnuGK> GnuGKCreator("GnuGK");
	SimpleCreator<CiscoGK> CiscoGKCreator("CiscoGK");
	SimpleCreator<ClarentGK> ClarentGKCreator("ClarentGK");
	SimpleCreator<GlonetGK> GlonetGKCreator("GlonetGK");

	int challenge;
	const char OID_T[] = "0.0.8.235.0.2.5";
}

// if we put nomatch into anonymous namespace,
// stupid VC can't find it, why??
static const PrefixInfo nomatch(-1, 0);


// template class LRQSender
typedef Functor2<PrefixInfo, Neighbor *, WORD> LRQFunctor;

template<class R>
class LRQSender : public LRQFunctor {
public:
	LRQSender(const R & r) : m_r(r) {}
	virtual PrefixInfo operator()(Neighbor *, WORD) const;

private:
	const R & m_r;
};

template<class R>
PrefixInfo LRQSender<R>::operator()(Neighbor *nb, WORD seqnum) const
{
	if (const H225_ArrayOf_AliasAddress *dest = m_r.GetAliases()) {
		H225_ArrayOf_AliasAddress aliases;
		if (PrefixInfo info = nb->GetPrefixInfo(*dest, aliases)) {
			H225_RasMessage lrq_ras;
			H225_LocationRequest & lrq = nb->BuildLRQ(lrq_ras, seqnum, aliases);
			if (nb->OnSendingLRQ(lrq, m_r) && nb->SendLRQ(lrq_ras))
				return info;
		}
	}
	return nomatch;
}

class LRQForwarder : public LRQFunctor {
public:
	LRQForwarder(const LocationRequest & l) : m_lrq(l) {}
	virtual PrefixInfo operator()(Neighbor *, WORD) const;

private:
	const LocationRequest & m_lrq;
};

PrefixInfo LRQForwarder::operator()(Neighbor *nb, WORD /*seqnum*/) const
{
	H225_ArrayOf_AliasAddress aliases;
	if (PrefixInfo info = nb->GetPrefixInfo(m_lrq.GetRequest().m_destinationInfo, aliases)) {
		H225_RasMessage lrq_ras;
		lrq_ras.SetTag(H225_RasMessage::e_locationRequest);
		H225_LocationRequest & lrq = lrq_ras;
		// copy and forward
		lrq = m_lrq.GetRequest();
		lrq.m_destinationInfo = aliases;
		// include hopCount if configured and not already included
		if (nb->GetDefaultHopCount() >= 1
			&& !lrq.HasOptionalField(H225_LocationRequest::e_hopCount)) {
			lrq.IncludeOptionalField(H225_LocationRequest::e_hopCount);
			lrq.m_hopCount = nb->GetDefaultHopCount();
		}
		if (nb->OnSendingLRQ(lrq, m_lrq) && nb->SendLRQ(lrq_ras))
			return info;
	}
	return nomatch;
}

// class Neighbor
Neighbor::Neighbor()
{
	m_rasSrv = RasServer::Instance();
}

Neighbor::~Neighbor()
{
	PTRACE(1, "NB\tDelete neighbor " << m_id);
}

bool Neighbor::SendLRQ(H225_RasMessage & lrq_ras)
{
	return m_rasSrv->SendRas(lrq_ras, GetIP(), m_port);
}

PIPSocket::Address Neighbor::GetIP() const
{
	if (m_dynamic) {
		PIPSocket::ClearNameCache();
		// Retrieve the ip address at this time
		if (!GetTransportAddress(m_name, GK_DEF_UNICAST_RAS_PORT, m_ip, m_port)) {
			PTRACE(1, "NB\tCan't get neighbor ip for " << m_name);
		}
	}
	return m_ip;
}

H225_LocationRequest & Neighbor::BuildLRQ(H225_RasMessage & lrq_ras, WORD seqnum, const H225_ArrayOf_AliasAddress & dest)
{
	lrq_ras.SetTag(H225_RasMessage::e_locationRequest);
	H225_LocationRequest & lrq = lrq_ras;
	lrq.m_requestSeqNum = seqnum;
	lrq.m_destinationInfo = dest;

	// Perform outbound per GK rewrite on the destination of the LRQ
	Toolkit::Instance()->GWRewriteE164(m_id,false,lrq.m_destinationInfo[0]);

	lrq.m_replyAddress = m_rasSrv->GetRasAddress(GetIP());

//	lrq.IncludeOptionalField(H225_LocationRequest::e_gatekeeperIdentifier);
//	lrq.m_gatekeeperIdentifier = Toolkit::GKName();
//	lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData);
//	lrq.m_nonStandardData.m_data.SetValue(m_id);

	lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo);
	lrq.m_sourceInfo.SetSize(1);
	H323SetAliasAddress(Toolkit::GKName(), lrq.m_sourceInfo[0], H225_AliasAddress::e_h323_ID);
	
	m_rasSrv->GetGkClient()->SetNBPassword(lrq, Toolkit::GKName());
	if (m_forwardHopCount >= 1) { // what if set hopCount = 1?
		lrq.IncludeOptionalField(H225_LocationRequest::e_hopCount);
		lrq.m_hopCount = m_forwardHopCount;
	}
	return lrq;
}

bool Neighbor::SetProfile(const PString & id, const PString & type)
{
	PConfig *config = GkConfig();
	PString section("Neighbor::" + (m_id = id));

	m_gkid = config->GetString(section, "GatekeeperIdentifier", id);
	m_name = config->GetString(section, "Host", "");
	m_dynamic = Toolkit::AsBool(config->GetString(section, "Dynamic", "0"));
	if (!m_dynamic && !GetTransportAddress(m_name, GK_DEF_UNICAST_RAS_PORT, m_ip, m_port))
		return false;

	PINDEX i;
	m_sendPrefixes.clear();
	PString sprefix(config->GetString(section, "SendPrefixes", ""));
	PStringArray sprefixes(sprefix.Tokenise(",", false));
	for (i = 0; i < sprefixes.GetSize(); ++i) {
		PStringArray p(sprefixes[i].Tokenise(":=", false));
		m_sendPrefixes[p[0]] = (p.GetSize() > 1) ? p[1].AsInteger() : 1;
	}
	PString aprefix(config->GetString(section, "AcceptPrefixes", "*"));
	m_acceptPrefixes = PStringArray(aprefix.Tokenise(",", false));

	SetForwardedInfo(section);

	PString info = " of type " + type;
	if (!sprefix)
		info = " send=" + sprefix;
	if (!aprefix)
		info += " accept=" + aprefix;
	PTRACE(1, "Set neighbor " << id << '(' << (m_dynamic ? m_name : AsString(m_ip, m_port)) << ')' << info);
	return true;
}

PrefixInfo Neighbor::GetPrefixInfo(const H225_ArrayOf_AliasAddress & aliases, H225_ArrayOf_AliasAddress & dest)
{
	Prefixes::iterator iter, biter = m_sendPrefixes.begin(), eiter = m_sendPrefixes.end();
	for (PINDEX i = 0; i < aliases.GetSize(); ++i) {
		H225_AliasAddress & alias = aliases[i];
		iter = m_sendPrefixes.find(alias.GetTagName());
		if (iter != eiter) {
			dest.SetSize(1);
			dest[0] = alias;
			return PrefixInfo(100, (short)iter->second);
		}
		PString destination(AsString(alias, false));
		while (iter != biter) {
			--iter; // search in reverse order
			const int len = MatchPrefix(destination, iter->first);
			if (len < 0) {
				return nomatch;
			}
			else if (len > 0) {
				dest.SetSize(1);
				dest[0] = alias;
				return PrefixInfo((short)len, (short)iter->second);
			}
		}
	}
	iter = m_sendPrefixes.find("*");
	if (iter == eiter)
		return nomatch;
	dest = aliases;
	return PrefixInfo(0, (short)iter->second);
}

bool Neighbor::OnSendingLRQ(H225_LocationRequest &)
{
	return true;
}

bool Neighbor::OnSendingLRQ(H225_LocationRequest & lrq, const AdmissionRequest &)
{
	return OnSendingLRQ(lrq);
}

bool Neighbor::OnSendingLRQ(H225_LocationRequest & lrq, const LocationRequest &orig_lrq)
{
	// adjust hopCount to be lesser or equal to the original value
	if( orig_lrq.GetRequest().HasOptionalField(H225_LocationRequest::e_hopCount) )
		if( lrq.HasOptionalField(H225_LocationRequest::e_hopCount) ) {
			if( lrq.m_hopCount > orig_lrq.GetRequest().m_hopCount )
				lrq.m_hopCount = orig_lrq.GetRequest().m_hopCount;
		} else {
			lrq.IncludeOptionalField(H225_LocationRequest::e_hopCount);
			lrq.m_hopCount = orig_lrq.GetRequest().m_hopCount;
		}

	return OnSendingLRQ(lrq);
}

bool Neighbor::OnSendingLRQ(H225_LocationRequest & lrq, const SetupRequest &)
{
	return OnSendingLRQ(lrq);
}

bool Neighbor::OnSendingLRQ(H225_LocationRequest & lrq, const FacilityRequest &)
{
	return OnSendingLRQ(lrq);
}

bool Neighbor::CheckReply(RasMsg *ras) const
{
	if( ras->IsFrom(GetIP(), 0 /*m_port*/) )
		return true;
	else {
		const H225_NonStandardParameter *params = ras->GetNonStandardParam();
		return params
			?(strncmp(m_id, params->m_data.AsString(), m_id.GetLength()) == 0)
			:false;
	}
}

bool Neighbor::IsAcceptable(RasMsg *ras) const
{
	if (ras->IsFrom(GetIP(), 0 /*m_port*/)) {
		// ras must be an LRQ
		H225_LocationRequest & lrq = (*ras)->m_recvRAS;
		PINDEX i, j, sz = m_acceptPrefixes.GetSize();
		H225_ArrayOf_AliasAddress & aliases = lrq.m_destinationInfo;
		for (j = 0; j < aliases.GetSize(); ++j) {
			H225_AliasAddress & alias = aliases[j];
			for (i = 0; i < sz; ++i)
				if (m_acceptPrefixes[i] == alias.GetTagName())
					return true;
			PString destination(AsString(alias, false));
			int maxlen = 0;
			for (i = 0; i < sz; ++i) {
				const PString & prefix = m_acceptPrefixes[i];
				const int len = MatchPrefix(destination, prefix);
				if (len < 0)
					return false;
				else if (len > maxlen)
					maxlen = len;
			}
			if (maxlen > 0)
				return true;
		}
		for (i = 0; i < sz; ++i)
			if (m_acceptPrefixes[i] == "*")
				return true;
	}
	return false;
}

void Neighbor::SetForwardedInfo(const PString & section)
{
	PConfig *config = GkConfig();
	m_forwardHopCount = (WORD)config->GetInteger(section, "ForwardHopCount", 0);
	m_acceptForwarded = Toolkit::AsBool(config->GetString(section, "AcceptForwardedLRQ", "1"));
	m_forwardResponse = Toolkit::AsBool(config->GetString(section, "ForwardResponse", "0"));
	PString forwardto(config->GetString(section, "ForwardLRQ", "0"));
	if (forwardto *= "never")
		m_forwardto = -1;
	else if (forwardto *= "always")
		m_forwardto = 1;
	else
		m_forwardto = 0;
}


// class OldGK
bool OldGK::SetProfile(const PString & id, const PString & args)
{
	m_id = m_gkid = id;
	PStringArray cfg(args.Tokenise(";", true));
	m_name = cfg[0].Trim();
	m_sendPrefixes.clear();
	if (cfg.GetSize() > 1) {
		PStringArray p = cfg[1].Tokenise(",", false);
		for (PINDEX i = 0; i < p.GetSize(); ++i)
			m_sendPrefixes[p[i]] = 1;
	} else
		m_sendPrefixes["*"] = 1;
	m_acceptPrefixes.SetSize(1);
	m_acceptPrefixes[0] = "*";
	if (cfg.GetSize() > 2)
		m_password = cfg[2];
	m_dynamic = (cfg.GetSize() > 3) ? Toolkit::AsBool(cfg[3]) : false;
	if (!m_dynamic && !GetTransportAddress(m_name, GK_DEF_UNICAST_RAS_PORT, m_ip, m_port))
		return false;

	SetForwardedInfo(LRQFeaturesSection);
	if (Toolkit::AsBool(GkConfig()->GetString(LRQFeaturesSection, "AlwaysForwardLRQ", "0")))
		m_forwardto = 1;

	PTRACE(1, "Set neighbor " << m_gkid << '(' << (m_dynamic ? m_name : AsString(m_ip, m_port)) << ')' << (cfg.GetSize() > 1 ? (" for prefix " + cfg[1]) : PString()));
	return true;
}


// class GnuGK
bool GnuGK::OnSendingLRQ(H225_LocationRequest & lrq, const AdmissionRequest & request)
{
	lrq.IncludeOptionalField(H225_LocationRequest::e_gatekeeperIdentifier);
	lrq.m_gatekeeperIdentifier = Toolkit::GKName();
	lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData);
	lrq.m_nonStandardData.m_data.SetValue(m_id);

	const H225_AdmissionRequest & arq = request.GetRequest();
	lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo);
	lrq.m_sourceInfo = arq.m_srcInfo;
	if (arq.HasOptionalField(H225_AdmissionRequest::e_canMapAlias)) {
		lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias);
		lrq.m_canMapAlias = arq.m_canMapAlias;
	}
	return true;
}

bool GnuGK::OnSendingLRQ(H225_LocationRequest & lrq, const SetupRequest & request)
{
	lrq.IncludeOptionalField(H225_LocationRequest::e_gatekeeperIdentifier);
	lrq.m_gatekeeperIdentifier = Toolkit::GKName();
	lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData);
	lrq.m_nonStandardData.m_data.SetValue(m_id);

	const H225_Setup_UUIE & setup = request.GetRequest();
	if (setup.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) {
		lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo);
		lrq.m_sourceInfo = setup.m_sourceAddress;
	}
	
	lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias);
	lrq.m_canMapAlias = TRUE;
	return true;
}

bool GnuGK::OnSendingLRQ(H225_LocationRequest & lrq, const FacilityRequest & /*request*/)
{
	lrq.IncludeOptionalField(H225_LocationRequest::e_gatekeeperIdentifier);
	lrq.m_gatekeeperIdentifier = Toolkit::GKName();
	lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData);
	lrq.m_nonStandardData.m_data.SetValue(m_id);
	
	lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias);
	lrq.m_canMapAlias = TRUE;
	return true;
}

bool GnuGK::IsAcceptable(RasMsg *ras) const
{
	if (Neighbor::IsAcceptable(ras)) {
		if (!m_acceptForwarded) {
			H225_LocationRequest & lrq = (*ras)->m_recvRAS;
			return lrq.HasOptionalField(H225_LocationRequest::e_gatekeeperIdentifier) && lrq.m_gatekeeperIdentifier.GetValue() == m_gkid;
		}
		return true;
	}
	return false;
}


// class CiscoGK
bool CiscoGK::OnSendingLRQ(H225_LocationRequest &lrq, const AdmissionRequest &req)
{
	const H225_AdmissionRequest &arq = req.GetRequest();
	Cisco_LRQnonStandardInfo nonStandardData;
	
	nonStandardData.m_ttl = m_forwardHopCount >= 1 ? m_forwardHopCount : 5;

	nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo);
	nonStandardData.m_gatewaySrcInfo.SetSize(arq.m_srcInfo.GetSize());
	for (PINDEX i = 0; i < arq.m_srcInfo.GetSize(); i++)
		nonStandardData.m_gatewaySrcInfo[i] = arq.m_srcInfo[i];

	if (arq.HasOptionalField(H225_AdmissionRequest::e_callIdentifier)) {
		nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_callIdentifier);
		nonStandardData.m_callIdentifier = arq.m_callIdentifier;
	}
		
	// Cisco GK needs these
	lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData);
	lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard);
	H225_H221NonStandard & h221 = lrq.m_nonStandardData.m_nonStandardIdentifier;
	h221.m_manufacturerCode = 18;
	h221.m_t35CountryCode = 181;
	h221.m_t35Extension = 0;
	
	PPER_Stream buff;
	nonStandardData.Encode(buff);
	buff.CompleteEncoding();
	lrq.m_nonStandardData.m_data = buff;
	
	lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias);
	lrq.m_canMapAlias = TRUE;
	return true;
}

// class CiscoGK
bool CiscoGK::OnSendingLRQ(H225_LocationRequest &lrq, const LocationRequest &req)
{
	if (lrq.HasOptionalField(H225_LocationRequest::e_nonStandardData)
			&& lrq.m_nonStandardData.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) {
		const H225_H221NonStandard &h221 = lrq.m_nonStandardData.m_nonStandardIdentifier;
		if (h221.m_manufacturerCode == 18 && h221.m_t35CountryCode == 181)
			return true;
			
		lrq.RemoveOptionalField(H225_LocationRequest::e_nonStandardData);
	}
	
	Cisco_LRQnonStandardInfo nonStandardData;
	nonStandardData.m_ttl = m_forwardHopCount >= 1 ? m_forwardHopCount : 5;

	if (lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo)) {
		nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo);
		nonStandardData.m_gatewaySrcInfo.SetSize(lrq.m_sourceInfo.GetSize());
		for (PINDEX i = 0; i < lrq.m_sourceInfo.GetSize(); i++)
			nonStandardData.m_gatewaySrcInfo[i] = lrq.m_sourceInfo[i];
	}
	
	lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData);
	lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard);
	H225_H221NonStandard & h221 = lrq.m_nonStandardData.m_nonStandardIdentifier;
	h221.m_manufacturerCode = 18;
	h221.m_t35CountryCode = 181;
	h221.m_t35Extension = 0;
	
	PPER_Stream buff;
	nonStandardData.Encode(buff);
	buff.CompleteEncoding();
	lrq.m_nonStandardData.m_data = buff;
	
	lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias);
	lrq.m_canMapAlias = TRUE;
	return true;
}

// class CiscoGK
bool CiscoGK::OnSendingLRQ(H225_LocationRequest &lrq, const SetupRequest &req)
{
	const Q931 &setup = req.GetWrapper()->GetQ931();
	const H225_Setup_UUIE &setupBody = req.GetRequest();
	Cisco_LRQnonStandardInfo nonStandardData;
	
	nonStandardData.m_ttl = m_forwardHopCount >= 1 ? m_forwardHopCount : 5;

	if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) {
		nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_gatewaySrcInfo);
		nonStandardData.m_gatewaySrcInfo.SetSize(setupBody.m_sourceAddress.GetSize());
		for (PINDEX i = 0; i < setupBody.m_sourceAddress.GetSize(); i++)
			nonStandardData.m_gatewaySrcInfo[i] = setupBody.m_sourceAddress[i];
	}
	if (setupBody.HasOptionalField(H225_Setup_UUIE::e_callIdentifier)) {
		nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_callIdentifier);
		nonStandardData.m_callIdentifier = setupBody.m_callIdentifier;
	}
	
	if (setup.HasIE(Q931::CallingPartyNumberIE)) {
		PBYTEArray data = setup.GetIE(Q931::CallingPartyNumberIE);
		if ((data[0] & 0x80) == 0x80 && data.GetSize() >= 2) {
			nonStandardData.IncludeOptionalField(Cisco_LRQnonStandardInfo::e_callingOctet3a);
			nonStandardData.m_callingOctet3a = data[1];
		}
	}
		
	// Cisco GK needs these
	lrq.IncludeOptionalField(H225_LocationRequest::e_nonStandardData);
	lrq.m_nonStandardData.m_nonStandardIdentifier.SetTag(H225_NonStandardIdentifier::e_h221NonStandard);
	H225_H221NonStandard & h221 = lrq.m_nonStandardData.m_nonStandardIdentifier;
	h221.m_manufacturerCode = 18;
	h221.m_t35CountryCode = 181;
	h221.m_t35Extension = 0;
	
	PPER_Stream buff;
	nonStandardData.Encode(buff);
	buff.CompleteEncoding();
	lrq.m_nonStandardData.m_data = buff;
	
	lrq.IncludeOptionalField(H225_LocationRequest::e_canMapAlias);
	lrq.m_canMapAlias = TRUE;
	return true;
}

bool CiscoGK::CheckReply(RasMsg *msg) const
{
	if (msg->IsFrom(GetIP(), 0))
		return true;
		
	if (msg->GetTag() != H225_RasMessage::e_locationConfirm
			&& msg->GetTag() != H225_RasMessage::e_locationReject)
		return false;

	H225_NonStandardParameter *nonStandardData = msg->GetNonStandardParam();
	if (nonStandardData == NULL)
		return false;
	
	if (nonStandardData->m_nonStandardIdentifier.GetTag() != H225_NonStandardIdentifier::e_h221NonStandard)
		return false;
	
	H225_H221NonStandard &h221 = nonStandardData->m_nonStandardIdentifier;
	if (h221.m_manufacturerCode != 18 || h221.m_t35CountryCode != 181)
		return false;
	
	PPER_Stream strm(nonStandardData->m_data);
	Cisco_LRQnonStandardInfo ciscoNonStandardData;
	if (ciscoNonStandardData.Decode(strm)) {
		// here should go additional checks to match callIdentifier, for example
	} else
		PTRACE(5, "NB\tFailed to decode Cisco nonStandardInfo field");
		
	return true;
}

// class ClarentGK
bool ClarentGK::OnSendingLRQ(H225_LocationRequest & lrq)
{
	// Clarent gatekeeper can't decode nonStandardData, stupid!
	lrq.RemoveOptionalField(H225_LocationRequest::e_nonStandardData);
	return true;
}

// class GlonetGK
bool GlonetGK::OnSendingLRQ(H225_LocationRequest & lrq, const AdmissionRequest & request)
{
	return BuildLRQ(lrq, (WORD)request.GetRequest().m_callReferenceValue);
}

bool GlonetGK::OnSendingLRQ(H225_LocationRequest &, const LocationRequest &)
{
	// not supported, since LRQ doesn't have call reference value
	return false;
}

bool GlonetGK::OnSendingLRQ(H225_LocationRequest & lrq, const SetupRequest & request)
{
	return BuildLRQ(lrq, (WORD)request.GetWrapper()->GetCallReference());
}

bool GlonetGK::OnSendingLRQ(H225_LocationRequest & lrq, const FacilityRequest & request)
{
	return BuildLRQ(lrq, (WORD)request.GetWrapper()->GetCallReference());
}

bool GlonetGK::BuildLRQ(H225_LocationRequest & lrq, WORD crv)
{
	lrq.IncludeOptionalField(H225_LocationRequest::e_sourceInfo);
	lrq.m_sourceInfo.SetSize(2);
	H323SetAliasAddress(Toolkit::GKName(), lrq.m_sourceInfo[0], H225_AliasAddress::e_h323_ID);
	H323SetAliasAddress(PString(crv), lrq.m_sourceInfo[1]);
	return true;
}


// class LRQRequester
class LRQRequester : public RasRequester {
public:
	LRQRequester(const LRQFunctor &);
	~LRQRequester();

	bool Send(NeighborList::List &, Neighbor * = 0);
	int GetReqNumber() const { return m_requests.size(); }
	H225_LocationConfirm *WaitForDestination(int);
	PString GetNeighborUsed() { return m_neighbor_used; }

	// override from class RasRequester
	virtual bool IsExpected(const RasMsg *) const;
	virtual void Process(RasMsg *);
	virtual bool OnTimeout();

private:
	struct Request {
		Request(Neighbor *n) : m_neighbor(n), m_reply(0), m_count(1) {}

		Neighbor *m_neighbor;
		RasMsg *m_reply;
		int m_count;
	};

	typedef multimap<PrefixInfo, Request> Queue;

	Queue m_requests;
	PMutex m_rmutex;
	const LRQFunctor & m_sendto;
	RasMsg *m_result;
	PString m_neighbor_used;
};

LRQRequester::LRQRequester(const LRQFunctor & fun) : m_sendto(fun), m_result(0)
{
	AddFilter(H225_RasMessage::e_locationConfirm);
	AddFilter(H225_RasMessage::e_locationReject);
	m_rasSrv->RegisterHandler(this);
}

LRQRequester::~LRQRequester()
{
	m_rasSrv->UnregisterHandler(this);
}

bool LRQRequester::Send(NeighborList::List & neighbors, Neighbor *requester)
{
	PWaitAndSignal lock(m_rmutex);
	NeighborList::List::iterator iter = neighbors.begin();
	while (iter != neighbors.end()) {
		Neighbor *nb = *iter++;
		if (nb != requester)
			if (PrefixInfo info = m_sendto(nb, m_seqNum))
				m_requests.insert(make_pair(info, nb));
	}
	if (m_requests.empty())
		return false;

	m_retry = 2; // TODO: configurable
	PTRACE(2, "NB\t" << m_requests.size() << " LRQ(s) sent");
	return true;
}

H225_LocationConfirm *LRQRequester::WaitForDestination(int timeout)
{
	while (WaitForResponse(timeout))
		if (m_result)
			break;
		else
			GetReply(); // ignore and increase iterator

	return m_result ? &(H225_LocationConfirm &)(*m_result)->m_recvRAS : 0;
}

bool LRQRequester::IsExpected(const RasMsg *ras) const
{
	return RasHandler::IsExpected(ras) && (ras->GetSeqNum() == m_seqNum);
}

void LRQRequester::Process(RasMsg *ras)
{
	PWaitAndSignal lock(m_rmutex);
	for (Queue::iterator iter = m_requests.begin(); iter != m_requests.end(); ++iter) {
		Request & req = iter->second;
		if (req.m_neighbor->CheckReply(ras)) {
			PTRACE(5,"NB\tReceived "<<ras->GetTagName()<<" message matched"
				<<" pending LRQ for neighbor "<<req.m_neighbor->GetId()
				<<':'<<req.m_neighbor->GetIP()
				);
			unsigned tag = ras->GetTag();
			if (tag == H225_RasMessage::e_requestInProgress) {
				if (H225_NonStandardParameter *params = ras->GetNonStandardParam()) {
					PStringArray param(params->m_data.AsString().Tokenise(":", false));
					if (param.GetSize() > 1)
						req.m_count += param[1].AsInteger();
				}
				RasRequester::Process(ras);
			} else if (tag == H225_RasMessage::e_locationConfirm) {
				--req.m_count;
				// Note: to avoid race condition, the order is important
				if (iter == m_requests.begin()) // the highest priority
					m_result = ras;
				AddReply(req.m_reply = ras);
				m_neighbor_used = req.m_neighbor->GetId(); // record neighbor used
				if (m_result)
					m_sync.Signal();
			} else { // should be H225_RasMessage::e_locationReject
				--req.m_count;
				delete ras;
				if (req.m_count <= 0 && req.m_reply == 0) {
					PTRACE(5,"NB\tLRQ rejected for neighbor "<<req.m_neighbor->GetId()
						<<':'<<req.m_neighbor->GetIP()
						);
					m_requests.erase(iter);
					if (m_requests.empty())
						RasRequester::Stop();
					else if (RasMsg *reply = m_requests.begin()->second.m_reply)
						m_result = reply, RasRequester::Stop();
				}
			}
			return;
		}
	}

	PTRACE(1, "RAS\tUnknown reply " << ras->GetTagName());
	delete ras;
}

bool LRQRequester::OnTimeout()
{
	PWaitAndSignal lock(m_rmutex);
	if (m_requests.empty())
		return false;
	Queue::iterator iter, biter = m_requests.begin(), eiter = m_requests.end();
	for (iter = biter; iter != eiter; ++iter) {
		m_result = iter->second.m_reply;
		if (m_result)
			return false;
	}
	if (m_retry-- == 0)
		return false;
	// re-send LRQs
	for (iter = biter; iter != eiter; ++iter) {
		m_sendto(iter->second.m_neighbor, m_seqNum);
		iter->second.m_count = 1; // reset count
	}
	m_sentTime = PTime();
	PTRACE(2, "NB\t" << m_requests.size() << " LRQ(s) re-sent");
	return true;
}


// class NeighborList
NeighborList::NeighborList()
{
	Factory<Neighbor>::SetDefaultCreator(&OldGKCreator);
	// OnReload is called by holder
}

NeighborList::~NeighborList()
{
	DeleteObjectsInContainer(m_neighbors);
}

void NeighborList::OnReload()
{
	challenge = rand();
	PStringToString cfgs(GkConfig()->GetAllKeyValues(NeighborSection));
	PINDEX i, sz = cfgs.GetSize();
	List::iterator iter = m_neighbors.begin();
	while (iter != m_neighbors.end()) {
		List::iterator it = iter++;
		for (i = 0; i < sz; ++i)
			if ((*it)->GetId() == cfgs.GetKeyAt(i))
				break;
		if (i == sz) {
			Neighbor * r = *it;
			m_neighbors.erase(it);
			delete r;
		}
	}
	for (i = 0; i < sz; ++i) {
		const PString & nbid = cfgs.GetKeyAt(i);
		const PString & type = cfgs.GetDataAt(i);
		iter = find_if(m_neighbors.begin(), m_neighbors.end(),
				compose1(bind2nd(equal_to<PString>(), nbid), mem_fun(&Neighbor::GetId))
			      );
		bool newnb = (iter == m_neighbors.end());
		Neighbor *nb = newnb ? Factory<Neighbor>::Create(type) : *iter;
		if (nb->SetProfile(nbid, type)) {
			if (newnb)
				m_neighbors.push_back(nb);
		} else {
			PTRACE(1, "NB\tCan't get profile for neighbor " << nbid);
			delete nb;
			if (!newnb)
				m_neighbors.erase(iter);
		}
	}
}

bool NeighborList::CheckLRQ(RasMsg *ras) const
{
	return find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsAcceptable), ras)) != m_neighbors.end();
}

bool NeighborList::CheckIP(const PIPSocket::Address & addr) const
{
	return find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsFrom), &addr)) != m_neighbors.end();
}

PString NeighborList::GetNeighborIdBySigAdr(const H225_TransportAddress & sigAd)
{

	PIPSocket::Address ipaddr;

	// Get the Neigbor IP address from the transport address
	if (!GetIPFromTransportAddr(sigAd, ipaddr))
	{
		return PString("");
	}

	return GetNeighborIdBySigAdr(ipaddr);
}

PString NeighborList::GetNeighborIdBySigAdr(const PIPSocket::Address & sigAd)
{

	List::iterator findNeighbor;

	// Attempt to find the neigbor in the list
	findNeighbor = find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsFrom), &sigAd));

	if (findNeighbor == m_neighbors.end())
	{
		return PString("");
	}

	return (*findNeighbor)->GetId();

}


/* Not used currently
H225_CryptoH323Token BuildAccessToken(const H225_TransportAddress & dest, const PIPSocket::Address & addr)
{
	H225_CryptoH323Token token;
	token.SetTag(H225_CryptoH323Token::e_nestedcryptoToken);
	H235_CryptoToken & nestedCryptoToken = token;
	nestedCryptoToken.SetTag(H235_CryptoToken::e_cryptoHashedToken);

	H235_CryptoToken_cryptoHashedToken & cryptoHashedToken = nestedCryptoToken;
	// "T" indicates that the hashed token is used for authentication and integrity
	cryptoHashedToken.m_tokenOID = OID_T;

	H235_ClearToken & clearToken = cryptoHashedToken.m_hashedVals;
	clearToken.IncludeOptionalField(H235_ClearToken::e_timeStamp);
	int timeStamp = time(0);
	clearToken.m_timeStamp = timeStamp;

	DWORD key = addr ^ timeStamp ^ challenge;
	PTEACypher::Key cryptokey;
	memset(&cryptokey, challenge, sizeof(PTEACypher::Key));
	memcpy(&cryptokey, &key, sizeof(DWORD));
	PTEACypher cypher(cryptokey);

	PPER_Stream strm;
	dest.Encode(strm);
	PString hashed(cypher.Encode(strm));

	cryptoHashedToken.m_token.m_hash.SetData(hashed.GetLength() * 8, hashed);
	return token;
}
*/

bool DecodeAccessToken(const H225_CryptoH323Token & token, const PIPSocket::Address & addr, H225_TransportAddress & dest)
{
	if (token.GetTag() != H225_CryptoH323Token::e_nestedcryptoToken)
		return false;
	const H235_CryptoToken & nestedCryptoToken = token;
	if (nestedCryptoToken.GetTag() != H235_CryptoToken::e_cryptoHashedToken)
		return false;
	const H235_CryptoToken_cryptoHashedToken & cryptoHashedToken = nestedCryptoToken;
	if (cryptoHashedToken.m_tokenOID.AsString() != OID_T)
		return false;

	const H235_ClearToken & clearToken = cryptoHashedToken.m_hashedVals;
	if (!clearToken.HasOptionalField(H235_ClearToken::e_timeStamp))
		return false;
	int now = time(0), timeStamp = clearToken.m_timeStamp.GetValue();
	if (timeStamp > now || (now - timeStamp) > 30)
		return false;

	const PASN_BitString & bitstring = cryptoHashedToken.m_token.m_hash;
	PString hashed((const char *)bitstring.GetDataPointer(), bitstring.GetSize() / 8);

	DWORD key = addr ^ timeStamp ^ challenge;
	PTEACypher::Key cryptokey;
	memset(&cryptokey, challenge, sizeof(PTEACypher::Key));
	memcpy(&cryptokey, &key, sizeof(DWORD));
	PTEACypher cypher(cryptokey);

	PPER_Stream strm;
	return cypher.Decode(hashed, strm) && dest.Decode(strm);
}


} // end of namespace Neighbors


namespace Routing {


using namespace Neighbors;

class NeighborPolicy : public Policy {
public:
	NeighborPolicy();

private:
	// override from class Policy
	virtual bool IsActive();

	virtual bool OnRequest(AdmissionRequest &);
	virtual bool OnRequest(LocationRequest &);
	virtual bool OnRequest(SetupRequest &);
	virtual bool OnRequest(FacilityRequest &);

	typedef NeighborList::List List;
	List & m_neighbors;
	int m_neighborTimeout;
};

NeighborPolicy::NeighborPolicy() : m_neighbors(*RasServer::Instance()->GetNeighbors())
{
	m_neighborTimeout = GkConfig()->GetInteger(LRQFeaturesSection, "NeighborTimeout", 5) * 1000;
	m_name = "Neighbor";
}

bool NeighborPolicy::IsActive()
{
	return !m_neighbors.empty();
}

template<class H2250>
inline void CopyCryptoTokens(const H225_LocationConfirm *lcf, H2250 & msg)
{
	// copy access tokens
	if (lcf->HasOptionalField(H225_LocationConfirm::e_cryptoTokens)) {
		msg.IncludeOptionalField(H2250::e_cryptoTokens);
		msg.m_cryptoTokens = lcf->m_cryptoTokens;
	}
}

bool NeighborPolicy::OnRequest(AdmissionRequest & arq_obj)
{
	LRQSender<AdmissionRequest> functor(arq_obj);
	LRQRequester request(functor);
	if (request.Send(m_neighbors)) {
		if (H225_LocationConfirm *lcf = request.WaitForDestination(m_neighborTimeout)) {
			Route route(m_name, lcf->m_callSignalAddress);
			route.m_routeId = request.GetNeighborUsed();
			route.m_flags |= Route::e_toNeighbor;
			arq_obj.AddRoute(route);
			RasMsg *ras = arq_obj.GetWrapper();
			(*ras)->m_replyRAS.SetTag(H225_RasMessage::e_admissionConfirm);
			H225_AdmissionConfirm & acf = (*ras)->m_replyRAS;
			CopyCryptoTokens(lcf, acf);
			return true;
		}
	}
	return false;
}

bool NeighborPolicy::OnRequest(LocationRequest & lrq_obj)
{
	RasMsg *ras = lrq_obj.GetWrapper();
	List::iterator iter = find_if(m_neighbors.begin(), m_neighbors.end(), bind2nd(mem_fun(&Neighbor::IsAcceptable), ras));
	Neighbor *requester = (iter != m_neighbors.end()) ? *iter : 0;
	int hopCount = 0;
	if (requester)
		if (requester->ForwardLRQ() < 0)
			return false;
		else if (requester->ForwardLRQ() > 0)
			hopCount = 1;

	H225_LocationRequest & lrq = (*ras)->m_recvRAS;
	if (lrq.HasOptionalField(H225_LocationRequest::e_hopCount)) {
		hopCount = lrq.m_hopCount - 1;
		if (hopCount)
			lrq.m_hopCount = hopCount;
	}
	if (!hopCount)
		return false;


	if (requester && !requester->ForwardResponse()) {
		LRQForwarder functor(lrq_obj);
		LRQRequester request(functor);
		if (request.Send(m_neighbors, requester)) {
			(*ras)->m_replyRAS.SetTag(H225_RasMessage::e_requestInProgress);
			H225_RequestInProgress & rip = (*ras)->m_replyRAS;
			rip.m_requestSeqNum = ras->GetSeqNum();
			rip.m_delay = m_neighborTimeout;
			if (H225_NonStandardParameter *params = ras->GetNonStandardParam()) {
				PString data = params->m_data.AsString() + ":" + PString(request.GetReqNumber());
				rip.IncludeOptionalField(H225_RequestInProgress::e_nonStandardData);
				rip.m_nonStandardData.m_data.SetValue(data);
			}
			return true;
		}
	} else {
		LRQSender<LocationRequest> functor(lrq_obj);
		LRQRequester request(functor);
		if (request.Send(m_neighbors, requester)) {
			if (H225_LocationConfirm *lcf = request.WaitForDestination(m_neighborTimeout)) {
				Route route(m_name, lcf->m_callSignalAddress);
				route.m_routeId = request.GetNeighborUsed();
				route.m_flags |= Route::e_toNeighbor;
				lrq_obj.AddRoute(route);
				(*ras)->m_replyRAS.SetTag(H225_RasMessage::e_locationConfirm);
				H225_LocationConfirm & nlcf = (*ras)->m_replyRAS;
				CopyCryptoTokens(lcf, nlcf);
				return true;
			}
		}
	}
	return false;
}

bool NeighborPolicy::OnRequest(SetupRequest & setup_obj)
{
	LRQSender<SetupRequest> functor(setup_obj);
	LRQRequester request(functor);
	if (request.Send(m_neighbors)) {
		if (H225_LocationConfirm *lcf = request.WaitForDestination(m_neighborTimeout)) {
			Route route(m_name, lcf->m_callSignalAddress);
			route.m_routeId = request.GetNeighborUsed();
			route.m_flags |= Route::e_toNeighbor;
			setup_obj.AddRoute(route);
			CopyCryptoTokens(lcf, setup_obj.GetRequest());
			return true;
		}
	}
	return false;
}

bool NeighborPolicy::OnRequest(FacilityRequest & facility_obj)
{
	LRQSender<FacilityRequest> functor(facility_obj);
	LRQRequester request(functor);
	if (request.Send(m_neighbors)) {
		if (H225_LocationConfirm *lcf = request.WaitForDestination(m_neighborTimeout)) {
			Route route(m_name, lcf->m_callSignalAddress);
			route.m_routeId = request.GetNeighborUsed();
			route.m_flags |= Route::e_toNeighbor;
			facility_obj.AddRoute(route);
			CopyCryptoTokens(lcf, facility_obj.GetRequest());
			return true;
		}
	}
	return false;
}

namespace {
	SimpleCreator<NeighborPolicy> NeighborPolicyCreator("neighbor");
}


} // end of namespace Routing


syntax highlighted by Code2HTML, v. 0.9.1