vpnengine/ikev1lib/src/ikev1private.cpp
changeset 0 33413c0669b9
--- /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;
+}
+
+