--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vpnengine/ikev1lib/src/ikev1private.cpp Thu Dec 17 09:14:51 2009 +0200
@@ -0,0 +1,550 @@
+/*
+* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+* This module contains the private vendor specific extension of IKE.
+* All of the current private extensions are related to Nokia VPN gateway
+* and shall be used ONLY when the EPOC IKE is acting as a Nokia VPN remote
+* access client.
+* The following private extension are implemented:
+*
+* 1) Internal Address payload usage
+* Internal address payload is used to the deliver a secure network
+* adderess and secure network DNS address(es) from VPN gateway to a client.
+* The Internal address payloads are used in the last two IKE main mode
+* messages as follows:
+*
+* Client (initiator) Gateway (responder)
+* .. SA, KE ... --->
+* <--- ..SA, KE ...
+* HDR*, INT_ADDR --->
+* <--- HDR*, INT_ADDR
+*
+* Client sends an INT_ADDR payload with PRI_INTERNAL_ADDRESS attribute
+* Attribute value is 0.0.0.0.
+*
+* Gateway responds with an INT_ADDR payload with PRI_INTERNAL_ADDRESS
+* attribute containing client internal address x.y.z.w
+* Gateway INT_ADDR payload may also contain attributes PRI_INTERNAL_DNS and
+* PRI_INTERNAL_WINS. PRI_INTERNAL_DNS contains a list of DNS IP addresses and
+* PRI_INTERNAL_WINS a list of WINS IP addresses.
+*
+*
+* 2) The NAT Traversal probing
+* The expanded Vendor-Id payload usage for the NAT Traversal probing.
+* The expanded Vendor-Id payloads contains the following information:
+*
+* Client (initiator) Gateway (responder)
+* VID(hash, ip_addr, port) --->
+* <--- VID(hash, detected_ip_addr,
+* detected_port)
+*
+* Client sends a expanded Vendor-Id payload containing the following information:
+* hash = Nokia VPN vendor specific hash data (used to recognize peer)
+* ip_addr = Client IKE own IP address
+* port = Client IKE own port (=500)
+*
+* Gateway responds with expanded Vendor-Id payload containing the following information:
+* hash = Nokia VPN vendor specific hash data (used to recognize peer)
+* detected_ip_addr = Client IP address as detected in received IKE message
+* IP header (=source IP address)
+* detected_port = Client port as detected in received IKE message
+* UDP header (=source port)
+*
+* Both client and gateway do the following examination
+* if ( ip_addr != detected_ip_addr ) || ( port != detected_port )
+* then NAT Traversal shall be used IPSEC ESP traffic between
+* the client and gateway
+*
+* Nokia VPN specific NAT Traversal means that IPSEC ESP traffic shall be
+* capsulated with UDP header.
+* The used UDP port for that purpose is 9872
+*
+*/
+
+#include "ikev1private.h"
+#include "ikev1dialog.h"
+#include "ikev1negotiation.h"
+#include "ikev1isakmpstream.h"
+
+#include "ikepolparser.h"
+
+const TUint8 BASE_VID_DATA[16] = {0x06, 0x3d, 0xf4, 0x13, 0x91, 0xa9, 0x19, 0xa2,
+ 0x5a, 0x61, 0xa8, 0x7c, 0x45, 0x02, 0x5f, 0xaf};
+
+const TUint8 DPD_VID_DATA[16] = {0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, 0xC9,
+ 0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 0x01, 0x00};
+
+TInt BuildVendorIdHash(TUint8 *aICOOKIE, TUint8 *aRCOOKIE, TUint8 *hash_data)
+{
+/*--------------------------------------------------------------------------------
+ *
+ * Build Vendor Id hash data
+ *
+ *------------------------------------------------------------------------*/
+//
+// base = MD5("Network Alchemy, Inc., Version 1.0"); /* ASCII-Z end null included)
+//
+TInt i;
+
+ Mem::Copy(hash_data, &BASE_VID_DATA[0], 16); /* Hash base (MD5) */
+
+ for ( i = 0; i < (ISAKMP_COOKIE_SIZE * 2); i++ ) {
+
+ if ( i < ISAKMP_COOKIE_SIZE )
+ *(hash_data + i) ^= *(aICOOKIE + i);
+ else *(hash_data + i) ^= *(aRCOOKIE + (i - ISAKMP_COOKIE_SIZE));
+
+ }
+
+ return 16;
+}
+
+
+TInt ConstructVendorId(TBool aNATProbe,
+ TUint8 *aICOOKIE,
+ TUint8 *aRCOOKIE,
+ TInetAddr &aLocalAddr,
+ TVendorISAKMP *aVendorPayload)
+{
+/*------------------------------------------------------------------------
+ *
+ * This method constructs a Vendor ID payload. If aNATProbe is TRUE
+ * an expanded format Vendor ID is constructed.
+ * Both Vendor ID formats contains a Nokia VPN vendor specific hash data
+ * which constructed as follows:
+ * base = MD5("Network Alchemy, Inc., Version 1.0"); ASCII-Z end null included)
+ * base = BASE_VID_DATA;
+ * Then the Vendor ID hash is consructed xor:ing ISAKMP cookies to hash as follows:
+ *
+ * for ( i = 0; i < (ISAKMP_COOKIE_SIZE * 2); i++ ) {
+ * if ( i < ISAKMP_COOKIE_SIZE )
+ * base[i] ^= ICOOKIE[i];
+ * else base[i] ^= RCOOKIE[i - ISAKMP_COOKIE_SIZE];
+ * }
+ *
+ * The expanded vendor ID payload looks like so:
+ *
+ * General payload header (next payload is "real" next payload)
+ * General payload header (next payload is "VENDOR_OPTION_NAT_TRAVERSAL")
+ * option hash
+ * General payload header (next payload is "VENDOR_OPTION_VERSION")
+ * option VENDOR_OPTION_NAT_TRAVERSAL
+ * General payload header (next payload is "NULL")
+ * option VENDOR_OPTION_VERSION
+ *
+ * Expanded vendor id format is format is as follows:
+ *
+ * 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! Next Payload ! RESERVED ! Payload Length = 44 !
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! OPTION_NAT_T ! RESERVED ! Hash_lth + 4 = 20 !
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! Nokia VPN Vendor specific hash !
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! OPTION_VERSION! RESERVED ! OPTION_NAT_T_LTH + 4 = 20 !
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! sin_lth ! sin_family ! sin_port !
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! sin_addr !
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! !
+ * . Zero * 2(?) .
+ * ! !
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! 0 ! RESERVED ! OPTION_VERSION + 4 = 8 !
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! MAJOR VERSION ! MINOR VERSION !
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ *------------------------------------------------------------------------*/
+ TVendorISAKMP *nat_vendor_id;
+ TVendorISAKMP *ver_vendor_id;
+ TNATTOption *nat_t_option;
+ TVersionOption *version_option;
+ TUint8 *next_payload;
+ TUint32 vid_lth;
+ TInetAddr own_addr = aLocalAddr;
+
+ next_payload = (TUint8 *)aVendorPayload;
+ *next_payload = ISAKMP_PAYLOAD_NONE; /* zeroe next payload field for sure */
+
+ if ( aNATProbe ) {
+ /*------------------------------------------------------------
+ *
+ * Build expanded Vendor Id payload
+ * Build first VENDOR_OPTION_NAT_TRAVERSAL payload
+ *
+ *-----------------------------------------------------------*/
+ nat_vendor_id = (TVendorISAKMP*)((TUint8*)aVendorPayload +
+ sizeof(TPayloadISAKMP) +
+ sizeof(TPayloadISAKMP) + 16); //bypass hash
+ next_payload = (TUint8 *)nat_vendor_id;
+ *next_payload = VENDOR_OPTION_VERSION;
+ nat_vendor_id->SetReserved(0);
+ nat_vendor_id->SetLength(sizeof(TPayloadISAKMP) + SIN_LTH);
+ nat_t_option = (TNATTOption*)nat_vendor_id->VIDData();
+ nat_t_option->InitOption();
+ nat_t_option->SetPort(500);
+ if ( own_addr.IsV4Mapped() )
+ own_addr.ConvertToV4();
+ nat_t_option->SetAddress(own_addr.Address());
+ /*------------------------------------------------------------
+ *
+ * Build next VENDOR_OPTION_VERSION payload
+ * Set major version X and minor Y.
+ *
+ *-----------------------------------------------------------*/
+ ver_vendor_id = (TVendorISAKMP*)((TUint8*)nat_vendor_id +
+ sizeof(TPayloadISAKMP) + SIN_LTH); //bypass NAT-T
+ next_payload = (TUint8 *)ver_vendor_id;
+ *next_payload = ISAKMP_PAYLOAD_NONE;
+ ver_vendor_id->SetReserved(0);
+ ver_vendor_id->SetLength(sizeof(TPayloadISAKMP) + VERSION_LTH);
+ version_option = (TVersionOption*)ver_vendor_id->VIDData();
+ version_option->SetVersion(MAJOR_VERSION, MINOR_VERSION);
+
+ /*------------------------------------------------------------
+ *
+ * Build "upper" Vendor Id payload general header
+ *
+ *-----------------------------------------------------------*/
+ vid_lth = sizeof(TPayloadISAKMP) + /* "outer" Vendor ID payload */
+ sizeof(TPayloadISAKMP) + 16 + /* VENDOR_OPTION_HASH */
+ sizeof(TPayloadISAKMP) + SIN_LTH + /* VENDOR_OPTION_NAT_TRAVERSAL */
+ sizeof(TPayloadISAKMP) + VERSION_LTH;/* VENDOR_OPTION_VERSION */
+ aVendorPayload->SetLength((TUint16)vid_lth);
+ aVendorPayload->SetReserved(0);
+
+ aVendorPayload = (TVendorISAKMP*)((TUint8*)aVendorPayload + sizeof(TPayloadISAKMP));
+ next_payload = (TUint8 *)aVendorPayload;
+ *next_payload = VENDOR_OPTION_NAT_TRAVERSAL;
+ }
+ else {
+ vid_lth = sizeof(TPayloadISAKMP) + 16;
+ }
+ /*------------------------------------------------------------
+ *
+ * Store Hash data into Vendor Id payload
+ *
+ *-----------------------------------------------------------*/
+ aVendorPayload->SetReserved(0);
+ aVendorPayload->SetLength((TUint16)sizeof(TPayloadISAKMP) + 16);
+
+ BuildVendorIdHash(aICOOKIE, aRCOOKIE,
+ aVendorPayload->VIDData());
+
+ return vid_lth;
+
+}
+
+
+TBool ProcessVendorId(TBool *aFamiliarPeer,
+ TUint8 *aICOOKIE,
+ TUint8 *aRCOOKIE,
+ TInetAddr &aLocalAddr,
+ TVendorISAKMP *aVendorPayload)
+{
+/*-------------------------------------------------------------------------
+ *
+ * Process Vendor Id payload received from peer.
+ * The following actions taken:
+ * -- Check if a Nokia VPN implementation i peer (recognize hash in Vendor Id)
+ * -- If Nokia VPN implementation detected process possible
+ * VENDOR_OPTION_NAT_TRAVERSAL in expanded Vendor Id payload
+ *
+ *------------------------------------------------------------------------*/
+ TBool nokia_vpn_peer = EFalse;
+ TBool nat_t_required = EFalse;
+ TVendorISAKMP *option_payload;
+ TNATTOption *nat_t_option;
+ TInt vid_lth;
+ TInt tmp_lth;
+ TInt hash_lth;
+ TUint16 ptype;
+ TUint16 detected_port;
+ TUint8 ref_hash[20];
+ TInetAddr detected_addr;
+ TInetAddr reference_addr = aLocalAddr;
+
+ vid_lth = aVendorPayload->GetLength() - sizeof(TPayloadISAKMP);
+ if ( vid_lth > 15 ) {
+ /*-------------------------------------------------------
+ *
+ * Check if expanded Vendor Id format
+ *
+ *-------------------------------------------------------*/
+ tmp_lth = vid_lth;
+ ptype = ISAKMP_PAYLOAD_NONE;
+ hash_lth = BuildVendorIdHash(aICOOKIE, aRCOOKIE, ref_hash);
+ option_payload = aVendorPayload;
+ if ( vid_lth > hash_lth ) {
+ /*---------------------------------------------------------------------
+ *
+ * An expanded format Vendor Id, bypass "outer" payload general header
+ * And do sanity check for VENDOR_OPTION_HASH option payload
+ *
+ *--------------------------------------------------------------------*/
+ option_payload = (TVendorISAKMP*)((TUint8*)option_payload + sizeof(TPayloadISAKMP));
+ ptype = option_payload->GetPayload();
+ tmp_lth = option_payload->GetLength();
+ if ( tmp_lth == (sizeof(TPayloadISAKMP) + 16 ) )
+// &&
+// ( option_payload->GetReserved() == 0 ) ) { //Must be always 0
+ tmp_lth -= sizeof(TPayloadISAKMP);
+ else tmp_lth = 0;
+ }
+
+ if ( tmp_lth == hash_lth ) {
+ /*---------------------------------------------
+ *
+ * Check that Vendor Id hash match
+ *
+ *---------------------------------------------*/
+ if ( Mem::Compare(option_payload->VIDData(), tmp_lth, ref_hash, hash_lth) == 0 ) {
+ /*-----------------------------------------------------------
+ *
+ * Process other Vendor Id option payload(s)
+ * In this phase only VENDOR_OPTION_NAT_TRAVERSAL is processed
+ * other options are ignored
+ *
+ *-----------------------------------------------------------*/
+ nokia_vpn_peer = ETrue;
+ tmp_lth += sizeof(TPayloadISAKMP);
+ option_payload = (TVendorISAKMP*)((TUint8*)option_payload + tmp_lth);
+
+ while ( ptype != ISAKMP_PAYLOAD_NONE ) {
+
+ if ( vid_lth <= tmp_lth ) {
+ break;
+ }
+ hash_lth = option_payload->GetLength();
+ tmp_lth += hash_lth;
+ if ( ( hash_lth < (MIN_ISAKMP_PAYLOAD_SIZE + SIN_LTH) ) ) {
+// &&
+// ( option_payload->GetReserved() != 0 ) ) } //Must be always 0
+ break;
+ }
+ if ( ptype == VENDOR_OPTION_NAT_TRAVERSAL ) {
+ if ( reference_addr.IsV4Mapped() )
+ reference_addr.ConvertToV4();
+ hash_lth -= sizeof(TPayloadISAKMP); /* option data length */
+ nat_t_option = (TNATTOption*)((TUint8*)option_payload + sizeof(TPayloadISAKMP));
+ detected_port = nat_t_option->GetPort();
+ detected_addr.SetAddress(nat_t_option->GetAddress());
+ if ( (detected_port != 500) /* Port changed */
+ ||
+ !(detected_addr.Match(reference_addr))) { /* address changed */
+ nat_t_required = ETrue;
+ }
+ break;
+ }
+
+ ptype = option_payload->GetPayload(); //Next payload
+ option_payload = (TVendorISAKMP*)((TUint8*)option_payload + hash_lth);
+
+ }
+ }
+ }
+
+ }
+
+ if ( aFamiliarPeer )
+ *aFamiliarPeer = nokia_vpn_peer;
+
+ return nat_t_required;
+
+}
+
+/**-------------------------------------------------------------------
+ *
+ * Function BuildDPDVendorId()
+ * This method builds a Dead Peer Detection (DPD) related Vendor ID
+ * payload and adds it into the IKE message. The vendor id is
+ * specified in the draft <draft-ietf-ipsec-dpd-04.txt> and its
+ * content is the following:
+ * 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ! !M!M!
+ * ! HASHED_VENDOR_ID !J!N!
+ * ! !R!R!
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Hash data is,
+ * {0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, 0xC9, 0x6B, 0x86, 0x96,
+ * 0xFC, 0x77, 0x57}, and MJR and MNR
+ * MJR = 1 and MNR = 0
+ *
+ *--------------------------------------------------------------------*/
+void BuildDPDVendorId(TIkev1IsakmpStream &aMsg)
+{
+ TInetAddr DummyAddr;
+
+ aMsg.IsakmpVendorId(IETF_NATT_VENDOR_ID,
+ NULL, NULL, DummyAddr, // These parameters has no relevance with IETF_NATT_VID_DATA
+ (TUint8*)DPD_VID_DATA,
+ sizeof(DPD_VID_DATA));
+}
+
+TBool CheckDPDVendorId(const TVendorISAKMP *aVendorPayload)
+{
+/**---------------------------------------------------------------------------------------
+ *
+ * This method checks does the remote end support DPD draft <draft-ietf-ipsec-dpd-04.txt>
+ *
+ *---------------------------------------------------------------------------------------*/
+ TInt vid_lth = aVendorPayload->GetLength() - sizeof(TPayloadISAKMP);
+ if ( vid_lth == sizeof(DPD_VID_DATA) )
+ {
+ if ( Mem::Compare(aVendorPayload->VIDData(), vid_lth, (TUint8*)DPD_VID_DATA, vid_lth) == 0 )
+ return ETrue; // Remote end supports DPD draft
+ }
+ return EFalse;
+}
+
+TInt CheckCredentials(CIkeData *aHostData )
+{
+/*-------------------------------------------------------------------------
+ *
+ * This function is called by CNegotiation::InitNegotiationL() method
+ * when the current IKE proposal defines aggresssive mode exchange with
+ * pre-shared key authentication.
+ * The following special actions are taken:
+ * -- If no pre-shared key data is defined, launch a dialog where
+ * user name and password information is asked from the user.
+ * -- User name information is store to current CIkeData iFQDN field
+ * (represent IKE identification)
+ * -- Password data shall be stored to current CIkeData iPresharedKey field
+ *
+ * This functionality is related to Checkpoint gateway.
+ * To use Aggressive mode exchange and pre-shared key authentication like
+ * this implement kind of "legacy authentication method" for IKE where
+ * client (=initiator) authentication is based on user name/password pair.
+ * User name is sent from initiator (=client) to responder (=Checkpoint GW)
+ * in the IKE ID payload. However, the password data is NOT transmitted in
+ * any payload, but it is used as pre-shared key in both ends.
+ * (= Checkpoint gateway shall use user name data received in IKE ID payload
+ * as a reference to the correct pre-shared key)
+ *
+ *------------------------------------------------------------------------*/
+ if ( !aHostData || aHostData->iPresharedKey.iKey.Length() )
+ return KErrNone;
+
+ aHostData->iPresharedKey.iFormat = STRING_KEY;
+ aHostData->iFQDN.SetLength(0); // Override FQDN in host data with user name
+
+ return CIkev1Dialog::GetSyncUNPWDialog(aHostData->iFQDN, aHostData->iPresharedKey.iKey);
+}
+
+
+CInternalAddress* ProcessIntNetL(TINTNETISAKMP *aIntNetpayload)
+{
+/*-------------------------------------------------------------------------
+ *
+ * Process Internal address payload received (sanity check already done)
+ * Process payload attributes as follows:
+ * -- Parse PRI_INTERNAL_ADDRESS attribute and store value to aInternalAddr
+ * -- Parse PRI_INTERNAL_DNS attributes and build list of DNS addresses
+ * There exists an own attribute for all DNS addresses
+ * -- Ignore other attributes (=PRI_INTERNAL_WINS)
+ *
+ * In this phase only IPv4 Internal addresses are supported by the
+ * Nokia VPN gateway
+ *
+ *------------------------------------------------------------------------*/
+ TInt length = (TInt)aIntNetpayload->GetLength();
+ if ( STATIC_CAST(TUint, length) < sizeof(TINTNETISAKMP) ) {
+ return NULL;
+ }
+
+ length -= sizeof(TINTNETISAKMP); /* Attribute data lengt in payload */
+
+ TUint32 ipv4_addr;
+ TBool internal_address = EFalse;
+ TInetAddr *dns_addr;
+ CInternalAddress *InternalAddr = new (ELeave)CInternalAddress(1);
+ CleanupStack::PushL(InternalAddr);
+ TDataISAKMP *attr = aIntNetpayload->INTNETAttrib();
+
+ while ( length > 0 ) {
+
+ length = length - attr->Size();
+ if ( length < 0 ) {
+ CleanupStack::PopAndDestroy(); /* delete InternalAddr */
+ return NULL;
+ }
+ switch ( attr->Type() ) {
+
+ case PRI_INTERNAL_ADDRESS:
+ /*-----------------------------------------------------------
+ * Internal address received from gateway. If several
+ * Internal address attributes detected use the first address
+ *------------------------------------------------------------*/
+ if ( attr->IsBasic() || ( attr->Length() != 4) ) {
+ CleanupStack::PopAndDestroy(); /* delete InternalAddr */
+ return NULL;
+ }
+ if ( !internal_address ) {
+ internal_address = ETrue;
+ ipv4_addr = GET32(attr->VarValue());
+ ipv4_addr = ByteOrder::Swap32(ipv4_addr); //NOT IN NETWORK ORDER !!!!
+ InternalAddr->iClientIntAddr.SetAddress(ipv4_addr);
+ }
+ break;
+
+ case PRI_INTERNAL_DNS:
+ /*-----------------------------------------------------------
+ * Internal DNS address received from gateway
+ *------------------------------------------------------------*/
+ if ( attr->IsBasic() || ( attr->Length() != 4 ) ) {
+ CleanupStack::PopAndDestroy(); /* delete InternalAddr */
+ return NULL;
+ }
+ ipv4_addr = GET32(attr->VarValue());
+ ipv4_addr = ByteOrder::Swap32(ipv4_addr); //NOT IN NETWORK ORDER !!!!
+ dns_addr = new(ELeave)TInetAddr;
+ CleanupStack::PushL(dns_addr);
+ dns_addr->SetAddress(ipv4_addr);
+ InternalAddr->AppendL(dns_addr);
+ CleanupStack::Pop(); /* delete dns_addr */
+ break;
+
+ default:
+ /*-----------------------------------------------------------
+ * Other attributes (WINS address) are ignored
+ *------------------------------------------------------------*/
+ break;
+ }
+
+ attr = attr->Next();
+ }
+
+ if ( !internal_address ) {
+ /*-----------------------------------------------------
+ * No client internal address defined.
+ * Internal address negotiation failed
+ *----------------------------------------------------*/
+ delete InternalAddr;
+ InternalAddr = NULL;
+ }
+
+ CleanupStack::Pop(); // Remove InternalAddr from cleanup stack
+
+ return InternalAddr;
+}
+
+