Files
m241Neelima/components/ip_net2-6.9/ipdhcpc6/src/ipdhcpc6_authhash.c
T
2026-07-30 11:19:44 +05:30

801 lines
24 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Copyright (C) 2007 Wind River Systems
*
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_clib.h>
#include <ipcom_err.h>
#include <ipcom_list.h>
#include <ipcom_syslog.h>
#include <ipcom_hash.h>
#include <ipcom_os.h>
#include <ipcom_sysvar.h>
#include <ipnet.h>
#include <ipnet_routesock.h>
#include "ipdhcpc6_duid.h"
#include "ipdhcpc6_cvthex.h"
#include "ipdhcpc6_authhash.h"
#define IPDHCPC6_DUID_LL_ACTUALSIZE 10
#define IPDHCPC6_DUID_LLT_ACTUALSIZE 14
#define IPDHCPC6_DUID_EN_ACTUALSIZE 14
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdhcpc6_authdata_obj_hash
*===========================================================================
* Description: Returns a hash value based on an authkey value.
* Parameters: dbentry - The option to return a hash value for.
* Returns:
*
*/
IP_STATIC unsigned
ipdhcpc6_authdb_obj_hash (Ipdhcpc6_authkey_dbentry *dbentry)
{
#ifdef DEBUG_DHCPV6_AUTH
int i;
unsigned char *cptr;
ipcom_printf ("ipdhcpc6_authdb_obj_hash called: ");
cptr = (unsigned char *) &(dbentry->duid.data.ll.link_addr);
for (i = 0 ; i < 6 ; i++)
{ ipcom_printf ("%2x ", cptr [i]);
}
ipcom_printf ("\n");
#endif
return ipcom_hash_update(&(dbentry->duid.data), dbentry->duid.len, 0);
}
/*
*===========================================================================
* ipdhcpc6_authdb_key_hash
*===========================================================================
* Description: Returns a hash value for a DUID key.
* Parameters: key - Pointer to the Ipdhcpc6_authkey_duid structure
* Returns:
*
*/
IP_STATIC unsigned
ipdhcpc6_authdb_key_hash(Ipdhcpc6_duid *key)
{
#ifdef DEBUG_DHCPV6_AUTH
int i;
unsigned char *cptr;
ipcom_printf ("ipdhcpc6_authdb_key_hash called: ");
cptr = (unsigned char *) &(key->data.ll.link_addr);
for (i = 0 ; i < 6 ; i++)
{ ipcom_printf ("%2x ", cptr [i]);
}
ipcom_printf ("\n");
#endif
return ipcom_hash_update(&(key->data), key->len, 0);
}
/*
*===========================================================================
* ipdhcpc6_authdb_hash_cmp
*===========================================================================
* Description: Compares a hash object and a hash key for equality.
* Parameters: dbentry - An authkey_dbentry structure.
* key - A pointer to the search key.
* Returns: IP_TRUE if the search key matches the prefix entry.
*
*/
IP_STATIC Ip_bool
ipdhcpc6_authdb_hash_cmp(Ipdhcpc6_authkey_dbentry *dbentry, Ipdhcpc6_duid *key)
{
#ifdef DEBUG_DHCPV6_AUTH
ipcom_printf ("ipdhcpc6_authdb_hash_cmp called\n");
#endif
if (dbentry->duid.len != key->len)
return IP_FALSE;
return ipcom_memcmp(&(dbentry->duid.data), &(key->data),
key->len) == 0;
}
/*************/
/*
*===========================================================================
* ipdhcpc6_authtrans_obj_hash
*===========================================================================
* Description: Returns a hash value based on an authkey value.
* Parameters: dbentry - The option to return a hash value for.
* Returns:
*
*/
IP_STATIC unsigned
ipdhcpc6_authtrans_obj_hash (Ipdhcpc6_authtrans_dbentry *dbentry)
{
#ifdef DEBUG_DHCPV6_AUTH
int i;
unsigned char *cptr;
ipcom_printf ("ipdhcpc6_authtrans_obj_hash called: ");
cptr = (unsigned char *) &(dbentry->duid.data);
switch (dbentry->duid.data.generic.content)
{ case IPDHCPC6_DUID_LL:
ipcom_printf ("LL content\n");
break;
case IPDHCPC6_DUID_LLT:
ipcom_printf ("LLT content\n");
break;
case IPDHCPC6_DUID_EN:
ipcom_printf ("EN content\n");
break;
default:
ipcom_printf ("default case content\n");
}
for (i = 0 ; i < dbentry->duid.len ; i++)
{ ipcom_printf ("%2x ", cptr [i]);
}
ipcom_printf ("\n");
#endif
return ipcom_hash_update(&(dbentry->duid.data), dbentry->duid.len, 0);
}
/*
*===========================================================================
* ipdhcpc6_authtrans_key_hash
*===========================================================================
* Description: Returns a hash value for a DUID key.
* Parameters: key - Pointer to the Ipdhcpc6_authkey_duid structure
* Returns:
*
*/
IP_STATIC unsigned
ipdhcpc6_authtrans_key_hash(Ipdhcpc6_duid *key)
{
#ifdef DEBUG_DHCPV6_AUTH
int i;
unsigned char *cptr;
ipcom_printf ("ipdhcpc6_authtrans_key_hash called: ");
cptr = (unsigned char *) &(key->data);
switch (key->data.generic.content)
{ case IPDHCPC6_DUID_LL:
ipcom_printf ("LL content\n");
break;
case IPDHCPC6_DUID_LLT:
ipcom_printf ("LLT content\n");
break;
case IPDHCPC6_DUID_EN:
ipcom_printf ("EN content\n");
break;
default:
ipcom_printf ("default case content\n");
}
for (i = 0 ; i < key->len ; i++)
{ ipcom_printf ("%2x ", cptr [i]);
}
ipcom_printf ("\n");
#endif
return ipcom_hash_update(&(key->data), key->len, 0);
}
/*
*===========================================================================
* ipdhcpc6_authtrans_hash_cmp
*===========================================================================
* Description: Compares a hash object and a hash key for equality.
* Parameters: prefix - A prefix structure.
* key - A pointer to the search key.
* Returns: IP_TRUE if the search key matches the prefix entry.
*
*/
IP_STATIC Ip_bool
ipdhcpc6_authtrans_hash_cmp(Ipdhcpc6_authtrans_dbentry *dbentry, Ipdhcpc6_duid *key)
{
#ifdef DEBUG_DHCPV6_AUTH
ipcom_printf ("ipdhcpc6_authtrans_hash_cmp called\n");
#endif
if (dbentry->duid.len != key->len)
return IP_FALSE;
return ipcom_memcmp(&(dbentry->duid.data), &(key->data),
key->len) == 0;
}
/*****************/
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
IP_STATIC Ipcom_hash *server_authkey_db;
IP_STATIC Ipcom_hash *server_transaction_db;
IP_GLOBAL Ipcom_hash *
ipdhcpc6_authdb_hash_create(void)
{
return ipcom_hash_new ((Ipcom_hash_obj_func) ipdhcpc6_authdb_obj_hash,
(Ipcom_hash_key_func) ipdhcpc6_authdb_key_hash,
(Ipcom_hash_cmp_func) ipdhcpc6_authdb_hash_cmp);
}
IP_GLOBAL Ipcom_hash *
ipdhcpc6_authtrans_hash_create(void)
{
return ipcom_hash_new ((Ipcom_hash_obj_func) ipdhcpc6_authtrans_obj_hash,
(Ipcom_hash_key_func) ipdhcpc6_authtrans_key_hash,
(Ipcom_hash_cmp_func) ipdhcpc6_authtrans_hash_cmp);
}
IP_GLOBAL Ipdhcpc6_duid *ipdhcpc6_authdb_create_ll_duid (int len, Ip_u16 hwtype, Ip_u8 *link_addr)
{
Ipdhcpc6_duid *retval = (Ipdhcpc6_duid *)
ipcom_malloc (sizeof (struct Ipdhcpc6_duid_struct));
if (IP_NULL == retval)
return IP_NULL;
retval->len = IPDHCPC6_DUID_LL_ACTUALSIZE;
retval->data.ll.content = IPDHCPC6_DUID_LL;
retval->data.ll.hardware_type = hwtype;
ipcom_memcpy (retval->data.ll.link_addr, link_addr, 6);
return retval;
}
IP_GLOBAL Ip_err ipdhcpc6_add_server_authkey (Ipdhcpc6_duid *server_duid,
Ip_u8 *key, int keylen)
{
Ip_u16 duid_content;
Ipdhcpc6_authkey_dbentry *new_authdb_entry = (Ipdhcpc6_authkey_dbentry *)
ipcom_malloc (sizeof (struct Ipdhcpc6_authkey_dbentry));
if (IP_NULL == new_authdb_entry)
return IPCOM_ERR_FAILED;
new_authdb_entry->secret.key = (Ip_u8 *) ipcom_malloc ((Ip_size_t)keylen);
if (IP_NULL == new_authdb_entry->secret.key)
{ ipcom_free (new_authdb_entry);
#if 0
IPCOM_LOG0 (DEBUG, "ipdhcpc6_add_server_authkey: no memory!\n");
#endif
return IPCOM_ERR_NO_MEMORY;
}
/*
* copy in key
*/
ipcom_memcpy (new_authdb_entry->secret.key, key, (Ip_size_t)keylen);
new_authdb_entry->secret.len = keylen;
/*
* copy in duid data
*/
new_authdb_entry->duid.len = server_duid->len;
duid_content = server_duid->data.ll.content;
switch (duid_content)
{
case IPDHCPC6_DUID_LL:
new_authdb_entry->duid.data.ll.hardware_type = server_duid->data.ll.hardware_type;
new_authdb_entry->duid.data.ll.content = IPDHCPC6_DUID_LL;
ipcom_memcpy (new_authdb_entry->duid.data.ll.link_addr,
server_duid->data.ll.link_addr, 6);
break;
case IPDHCPC6_DUID_EN: /* not yet implemented */
case IPDHCPC6_DUID_LLT: /* not yet implemented */
default:
ipcom_free (new_authdb_entry);
return IPCOM_ERR_FAILED;
}
if (IPCOM_SUCCESS != ipcom_hash_add (server_authkey_db, new_authdb_entry))
{
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
IP_GLOBAL Ipdhcpc6_authtrans_dbentry *
ipdhcpc6_create_server_authtrans_entry (union Ipdhcpc6_duid_data *server, int len)
{
Ip_u16 server_duid_content;
Ipdhcpc6_authtrans_dbentry *server_dbentry = IP_NULL;
server_dbentry = (Ipdhcpc6_authtrans_dbentry *)
ipcom_malloc (sizeof (struct Ipdhcpc6_authtrans_dbentry));
if (IP_NULL == server_dbentry)
return IP_NULL;
server_duid_content = ip_ntohs (server->generic.content);
switch (server_duid_content)
{ case IPDHCPC6_DUID_LLT:
server_dbentry->duid.len = IPDHCPC6_DUID_LLT_ACTUALSIZE;
server_dbentry->duid.data.llt.content = IPDHCPC6_DUID_LLT;
server_dbentry->duid.data.llt.hardware_type = server->llt.hardware_type;
server_dbentry->duid.data.llt.time = server->llt.time;
ipcom_memcpy (server_dbentry->duid.data.llt.link_addr,
server->llt.link_addr, 6);
break;
case IPDHCPC6_DUID_EN:
/*
* FIXME
* using the duid union structure we have, we know we will have
* enough space to copy in the first 8 bytes of the enterprise
* ID. This may or may not be enough to be unique; we'll need
* to revisit this later. Look at "len" to see if we were given
* less than 8 bytes for the ID; if so, only copy the bytes
* given.
*/
server_dbentry->duid.len =
(Ip_u16)(len < IPDHCPC6_DUID_EN_ACTUALSIZE ?
len : IPDHCPC6_DUID_EN_ACTUALSIZE);
server_dbentry->duid.data.en.content = IPDHCPC6_DUID_EN;
server_dbentry->duid.data.en.enterprise_number_1 =
server->en.enterprise_number_1;
server_dbentry->duid.data.en.enterprise_number_2 =
server->en.enterprise_number_2;
ipcom_memcpy (server_dbentry->duid.data.en.id,
server->en.id, (Ip_size_t)(8 - (IPDHCPC6_DUID_EN_ACTUALSIZE - len)));
break;
case IPDHCPC6_DUID_LL:
server_dbentry->duid.len = IPDHCPC6_DUID_LL_ACTUALSIZE;
server_dbentry->duid.data.ll.content = IPDHCPC6_DUID_LL;
server_dbentry->duid.data.ll.hardware_type = server->ll.hardware_type;
ipcom_memcpy (server_dbentry->duid.data.ll.link_addr,
server->llt.link_addr, 6);
break;
#ifdef DEBUG_DHCPV6_AUTH
default:
ipcom_printf ("found %u (%u) duid\n", server_duid_content,
ip_htons (server_duid_content));
#endif
}
/*
* set replay counters
*/
server_dbentry->client_replay1 = 0xdeadbeef;
server_dbentry->client_replay2 = 0x0;
server_dbentry->server_replay1 = 0x0;
server_dbentry->server_replay2 = 0x0;
if (IPCOM_SUCCESS != ipcom_hash_add (server_transaction_db, server_dbentry))
return IP_NULL;
return server_dbentry;
}
IP_GLOBAL Ip_err ipdhcpc6_server_no_auth (Ipdhcpc6_duid *server_dbkey)
{
Ipdhcpc6_authkey_dbentry *server_dbobj = IP_NULL;
server_dbobj = ipcom_hash_get (server_authkey_db, server_dbkey);
if (IP_NULL == server_dbobj)
{ ipcom_printf ("no server_dbobj\n");
return IPCOM_SUCCESS;
}
else
{
ipcom_hash_remove (server_authkey_db, server_dbobj);
return IPCOM_SUCCESS;
}
}
IP_GLOBAL Ipdhcpc6_authtrans_dbentry *
ipdhcpc6_get_server_authtrans_entry (union Ipdhcpc6_duid_data *server, int len)
{
Ip_u16 server_duid_content;
Ipdhcpc6_duid server_dbkey;
Ipdhcpc6_authtrans_dbentry *server_dbobj = IP_NULL;
server_duid_content = ip_ntohs (server->generic.content);
switch (server_duid_content)
{ case IPDHCPC6_DUID_LLT:
server_dbkey.len = IPDHCPC6_DUID_LLT_ACTUALSIZE;
server_dbkey.data.llt.content = IPDHCPC6_DUID_LLT;
server_dbkey.data.llt.hardware_type = server->llt.hardware_type;
server_dbkey.data.llt.time = server->llt.time;
ipcom_memcpy (server_dbkey.data.llt.link_addr,
server->llt.link_addr, 6);
break;
case IPDHCPC6_DUID_EN:
/*
* FIXME
* using the duid union structure we have, we know we will have
* enough space to copy in the first 8 bytes of the enterprise
* ID. This may or may not be enough to be unique; we'll need
* to revisit this later. Look at "len" to see if we were given
* less than 8 bytes for the ID; if so, only copy the bytes
* given.
*/
server_dbkey.len =
(Ip_u16)(len < IPDHCPC6_DUID_EN_ACTUALSIZE ?
len : IPDHCPC6_DUID_EN_ACTUALSIZE);
server_dbkey.data.en.content = IPDHCPC6_DUID_EN;
server_dbkey.data.en.enterprise_number_1 =
server->en.enterprise_number_1;
server_dbkey.data.en.enterprise_number_2 =
server->en.enterprise_number_2;
ipcom_memcpy (server_dbkey.data.en.id,
server->en.id, (Ip_size_t)(8 - (IPDHCPC6_DUID_EN_ACTUALSIZE - len)));
break;
case IPDHCPC6_DUID_LL:
server_dbkey.len = IPDHCPC6_DUID_LL_ACTUALSIZE;
server_dbkey.data.ll.content = IPDHCPC6_DUID_LL;
server_dbkey.data.ll.hardware_type = server->ll.hardware_type;
ipcom_memcpy (server_dbkey.data.ll.link_addr,
server->llt.link_addr, 6);
break;
#ifdef DEBUG_DHCPV6_AUTH
default:
ipcom_printf ("found %u (%u) duid\n", server_duid_content,
ip_htons (server_duid_content));
#endif
}
server_dbobj = ipcom_hash_get (server_transaction_db, &server_dbkey);
if (IP_NULL == server_dbobj)
{ /* no entry yet. Make one. */
return ipdhcpc6_create_server_authtrans_entry (server, len);
}
return server_dbobj;
}
IP_STATIC Ip_u8 authKeyFromSysVars [132];
IP_STATIC Ip_size_t authKeyLenFromSysVars = 0;
IP_STATIC Ip_u8 authKey [132];
IP_STATIC Ip_size_t authKeyLen = 0;
#if 0
IP_STATIC Ip_u8 authRealmFromSysVars [128];
IP_STATIC Ip_size_t authRealmLenFromSysVars = 0;
#endif
IP_STATIC Ip_u8 ipdhcpc6_authRealm [128];
IP_STATIC Ip_size_t ipdhcpc6_authRealmlen = 0;
IP_STATIC int ipdhcpc6_user_authdb_inited = 0;
IP_GLOBAL Ip_err ipdhcpc6_get_default_auth_key (int *len, Ip_u8 **key)
{
*len = (int)authKeyLen;
*key = authKey;
return IPCOM_SUCCESS;
}
IP_GLOBAL Ip_err ipdhcpc6_set_default_auth_key (int len, Ip_u8 *key)
{
if (len <= (int)sizeof (authKey))
{
authKeyLen = (Ip_size_t)len;
ipcom_memcpy (authKey, key, (Ip_size_t)len);
return IPCOM_SUCCESS;
}
else
return IPCOM_ERR_FAILED;
}
IP_STATIC Ip_err ipdhcpc6_set_default_auth_key_from_sysvars (void)
{
authKeyLenFromSysVars = sizeof (authKeyFromSysVars);
if (IP_NULL == ipcom_sysvar_get ("ipdhcpc6.authkey",
(char *) authKeyFromSysVars,
&authKeyLenFromSysVars))
{
authKeyLen = 0;
return IPCOM_ERR_FAILED;
}
/*
* check if the value we read is a valid hex key; convert to
* a key buffer.
*/
if (ipdhcpc6_check_is_hex_string ((char *) authKeyFromSysVars))
{
/* we need at least 4 characters to proceed here; "0x--" or more */
if (authKeyLenFromSysVars >= 4)
{ char *kptr = (char *) authKeyFromSysVars;
kptr += 2; /* get past leading "0x" */
authKeyLen = (authKeyLenFromSysVars - 2) / 2;
if (! ipdhcpc6_cvt_hex_to_membuf (kptr, (unsigned char *) authKey, (int)authKeyLen) )
{ authKeyLen = 0;
return IPCOM_ERR_FAILED;
}
}
}
else
{
authKeyLen = 0;
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
IP_GLOBAL Ip_err ipdhcpc6_get_auth_realm (int *len, Ip_u8 **realm)
{
*len = (int)ipdhcpc6_authRealmlen;
*realm = ipdhcpc6_authRealm;
return IPCOM_SUCCESS;
}
IP_GLOBAL Ip_err ipdhcpc6_set_auth_realm (int len, Ip_u8 *realm)
{
if (len <= (int)sizeof (ipdhcpc6_authRealm))
{
ipdhcpc6_authRealmlen = (Ip_size_t)len;
ipcom_memcpy (ipdhcpc6_authRealm, realm, (Ip_size_t)len);
return IPCOM_SUCCESS;
}
else
return IPCOM_ERR_FAILED;
}
IP_STATIC Ip_err ipdhcpc6_set_auth_realm_from_sysvars (void)
{
ipdhcpc6_authRealmlen = sizeof (ipdhcpc6_authRealm);
if (IP_NULL == ipcom_sysvar_get("ipdhcpc6.authrealm",
(char *) ipdhcpc6_authRealm,
&ipdhcpc6_authRealmlen))
{
ipdhcpc6_authRealmlen = 0;
}
return IPCOM_SUCCESS;
}
IP_GLOBAL void ipdhcpc6_authdb_init (void)
{
server_authkey_db = ipdhcpc6_authdb_hash_create ();
server_transaction_db = ipdhcpc6_authtrans_hash_create ();
ipdhcpc6_set_auth_realm_from_sysvars ();
ipdhcpc6_set_default_auth_key_from_sysvars ();
ipdhcpc6_user_authdb_inited = 0;
/* test_server_specific (); */
};
IP_GLOBAL Ip_err
ipdhcpc6_find_key_for_server (union Ipdhcpc6_duid_data *server,
int duid_len,
Ip_u8 **key, int *key_len)
{
Ip_u16 server_duid_content;
Ipdhcpc6_duid server_dbkey;
Ipdhcpc6_authkey_dbentry *server_dbobj = IP_NULL;
server_duid_content = ip_ntohs (server->generic.content);
switch (server_duid_content)
{ case IPDHCPC6_DUID_LLT:
server_dbkey.len = IPDHCPC6_DUID_LL_ACTUALSIZE;
server_dbkey.data.ll.content = IPDHCPC6_DUID_LL;
server_dbkey.data.ll.hardware_type = server->llt.hardware_type;
ipcom_memcpy (server_dbkey.data.ll.link_addr,
server->llt.link_addr, 6);
break;
case IPDHCPC6_DUID_EN:
/*
* FIXME
* using the duid union structure we have, we know we will have
* enough space to copy in the first 8 bytes of the enterprise
* ID. This may or may not be enough to be unique; we'll need
* to revisit this later. Look at "len" to see if we were given
* less than 8 bytes for the ID; if so, only copy the bytes
* given.
*/
server_dbkey.len =
(Ip_u16)(duid_len < IPDHCPC6_DUID_EN_ACTUALSIZE ?
duid_len : IPDHCPC6_DUID_EN_ACTUALSIZE);
server_dbkey.data.en.content = IPDHCPC6_DUID_EN;
server_dbkey.data.en.enterprise_number_1 =
server->en.enterprise_number_1;
server_dbkey.data.en.enterprise_number_2 =
server->en.enterprise_number_2;
ipcom_memcpy (server_dbkey.data.en.id,
server->en.id, (Ip_size_t)(8 - (IPDHCPC6_DUID_EN_ACTUALSIZE - duid_len)));
break;
case IPDHCPC6_DUID_LL:
server_dbkey.len = IPDHCPC6_DUID_LL_ACTUALSIZE;
server_dbkey.data.ll.content = IPDHCPC6_DUID_LL;
server_dbkey.data.ll.hardware_type = server->ll.hardware_type;
ipcom_memcpy (server_dbkey.data.ll.link_addr,
server->llt.link_addr, 6);
break;
default:
#ifdef DEBUG_DHCPV6_AUTH
ipcom_printf ("find_key_for_server: found %u (%u) duid\n", server_duid_content,
ip_htons (server_duid_content));
#endif
return IPCOM_ERR_FAILED;
}
server_dbobj = ipcom_hash_get (server_authkey_db, &server_dbkey);
if (IP_NULL == server_dbobj)
{
/* use default system auth key */
return IPCOM_ERR_FAILED;
}
else
{
/* use server specific key */
*key_len = server_dbobj->secret.len;
*key = server_dbobj->secret.key;
return IPCOM_SUCCESS;
}
}
IP_GLOBAL void ipdhcpc6_block_on_user_authdb_config (void)
{
while (1)
{ if (ipdhcpc6_user_authdb_inited)
return;
ipcom_millisleep (250);
}
}
IP_GLOBAL void ipdhcpc6_user_authdb_config_finished (void)
{
ipdhcpc6_user_authdb_inited = 1;
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/