/*
 * Note: this file originally auto-generated by mib2c using
 *  $
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <net-snmp/library/cert_util.h>
#include "tlstm-mib.h"
#include "snmpTlstmAddrTable.h"

/** XXX - move these to table_data header? */
#define FATE_NEWLY_CREATED    1
#define FATE_NO_CHANGE        0
#define FATE_DELETE_ME        -1

/***********************************************************************
 *
 * PERSISTENCE
 *
 ***********************************************************************/
    /*
     * structure for undo storage and other vars for set processing 
     */
typedef struct tlstmAddrTable_undo_s {
    char            fate;
    char            copied;
    char            is_consistent;
    netsnmp_request_info *req[TLSTMADDRTABLE_MAX_COLUMN + 1];
    /*
     * undo Column space 
     */
    char       tlstmAddrServerFingerprint[TLSTMADDRSERVERFINGERPRINT_MAX_SIZE];
    size_t          tlstmAddrServerFingerprint_len;
    char            tlstmAddrServerIdentity[TLSTMADDRSERVERIDENTITY_MAX_SIZE];
    size_t          tlstmAddrServerIdentity_len;
    char            tlstmAddrStorageType;
    char            tlstmAddrRowStatus;
    char            hashType;
} tlstmAddrTable_undo;

    /*
     * Typical data structure for a row entry 
     */
typedef struct tlstmAddrTable_entry_s {
    /*
     * Index values 
     */
    char            snmpTargetAddrName[SNMPTARGETADDRNAME_MAX_SIZE];
    size_t          snmpTargetAddrName_len;

    /*
     * Column values 
     */
    char        tlstmAddrServerFingerprint[TLSTMADDRSERVERFINGERPRINT_MAX_SIZE];
    size_t          tlstmAddrServerFingerprint_len;
    char            tlstmAddrServerIdentity[TLSTMADDRSERVERIDENTITY_MAX_SIZE];
    size_t          tlstmAddrServerIdentity_len;
    char            tlstmAddrStorageType;
    char            tlstmAddrRowStatus;
    char            hashType;

    /*
     * used during set processing 
     */
    tlstmAddrTable_undo *undo;

    /*
     * user data
     */
    struct netsnmp_cert_s   *cert;
    char                     addr_flags;

} tlstmAddrTable_entry;

netsnmp_tdata_row *tlstmAddrTable_createEntry(netsnmp_tdata * table_data,
                                              char *snmpTargetAddrName,
                                              size_t snmpTargetAddrName_len);
void tlstmAddrTable_removeEntry(netsnmp_tdata * table_data,
                                netsnmp_tdata_row * row);

static Netsnmp_Node_Handler tlstmAddrTable_handler;
static int _cache_load(netsnmp_cache *cache, netsnmp_tdata *table);
static void _cache_free(netsnmp_cache *cache, netsnmp_tdata *table);
static uint32_t _last_changed = 0;
static int _count_handler(netsnmp_mib_handler *handler,
                          netsnmp_handler_registration *reginfo,
                          netsnmp_agent_request_info *reqinfo,
                          netsnmp_request_info *requests);

static void _tlstmAddr_init_persistence(void);
static void _addrs_add(tlstmAddrTable_entry *entry);
static void _addrs_remove(tlstmAddrTable_entry *entry);
static void _addr_tweak_storage(tlstmAddrTable_entry *entry);
static netsnmp_tdata  *_table_data = NULL;

/***********************************************************************
 *
 * PERSISTENCE
 *
 ***********************************************************************/
/** Initializes the tlstmAddrTable module */
void
init_snmpTlstmAddrTable(void)
{
    oid             reg_oid[] = { SNMP_TLS_TM_ADDR_TABLE };
    const size_t    reg_oid_len = OID_LENGTH(reg_oid);
    netsnmp_handler_registration *reg;
    netsnmp_table_registration_info *table_info;
    netsnmp_cache                   *cache;
    netsnmp_watcher_info            *watcher;

    DEBUGMSGTL(("tlstmAddrTable:init",
                "initializing table tlstmAddrTable\n"));

    reg =
        netsnmp_create_handler_registration("tlstmAddrTable",
                                            tlstmAddrTable_handler,
                                            reg_oid, reg_oid_len,
                                            HANDLER_CAN_RWRITE);

    _table_data = netsnmp_tdata_create_table("tlstmAddrTable", 0);
    if (NULL == _table_data) {
        snmp_log(LOG_ERR, "error creating tdata table for tlstmAddrTable\n");
        return;
    }
    table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
    if (NULL == table_info) {
        snmp_log(LOG_ERR, "error creating table info for tlstmAddrTable\n");
        netsnmp_tdata_delete_table(_table_data);
        _table_data = NULL;
        return;
    }

    /*
     * cache init
     */
    cache = netsnmp_cache_create(30, (NetsnmpCacheLoad*)_cache_load,
                                 (NetsnmpCacheFree*)_cache_free,
                                 reg_oid, reg_oid_len);
    if (NULL == cache) {
        snmp_log(LOG_ERR,"error creating cache for tlstmCertToTSNTable\n");
        netsnmp_tdata_delete_table(_table_data);
        _table_data = NULL;
        return;
    }
    cache->magic = (void *)_table_data;
    cache->flags = NETSNMP_CACHE_DONT_INVALIDATE_ON_SET;

    /*
     * populate index types
     */
    netsnmp_table_helper_add_indexes(table_info,
                                     /* index: snmpTargetAddrName */
                                     ASN_PRIV_IMPLIED_OCTET_STR, 
                                     0);

    table_info->min_column = TLSTMADDRTABLE_MIN_COLUMN;
    table_info->max_column = TLSTMADDRTABLE_MAX_COLUMN;

    netsnmp_tdata_register(reg, _table_data, table_info);

    if (cache) 
        netsnmp_inject_handler_before( reg, netsnmp_cache_handler_get(cache),
                                       "table_container");

    /*
     * register scalars
     */
    reg_oid[10] = 7;
    reg = netsnmp_create_handler_registration("snmpTlstmAddrCount",
                                              _count_handler, reg_oid,
                                              OID_LENGTH(reg_oid),
                                              HANDLER_CAN_RONLY);
    if (NULL == reg)
        snmp_log(LOG_ERR,
                 "could not create handler for snmpTlstmAddrCount\n");
    else {
        netsnmp_register_scalar(reg);
        if (cache) 
            netsnmp_inject_handler_before(reg,
                                          netsnmp_cache_handler_get(cache),
                                          "snmpTlstmAddrCount");
    }
    
    reg_oid[10] = 8;
    reg = netsnmp_create_handler_registration(
        "snmpTlstmAddrTableLastChanged", NULL, reg_oid,
        OID_LENGTH(reg_oid), HANDLER_CAN_RONLY);
    watcher = netsnmp_create_watcher_info((void*)&_last_changed,
                                          sizeof(_last_changed),
                                          ASN_TIMETICKS,
                                          WATCHER_FIXED_SIZE);
    if ((NULL == reg) || (NULL == watcher))
        snmp_log(LOG_ERR,
                 "could not create handler for snmpTlstmAddrTableLastChanged\n");
    else
        netsnmp_register_watched_scalar(reg, watcher);

    /*
     * Initialise the contents of the table here 
     */
    _tlstmAddr_init_persistence();
}

/***********************************************************************
 *
 * PERSISTENCE
 *
 ***********************************************************************/
/*
 * create a new row in the table 
 */
netsnmp_tdata_row *
tlstmAddrTable_createEntry(netsnmp_tdata * table_data,
                           char *snmpTargetAddrName,
                           size_t snmpTargetAddrName_len)
{
    tlstmAddrTable_entry *entry;
    netsnmp_tdata_row *row;

    if (snmpTargetAddrName_len > sizeof(entry->snmpTargetAddrName))
        return NULL;

    entry = SNMP_MALLOC_TYPEDEF(tlstmAddrTable_entry);
    if (!entry)
        return NULL;

    row = netsnmp_tdata_create_row();
    if (!row) {
        SNMP_FREE(entry);
        return NULL;
    }
    row->data = entry;

    DEBUGIF("tlstmAddrTable:entry:create") {
        char name[sizeof(entry->snmpTargetAddrName)+1];
        snprintf(name, sizeof(name), "%s", snmpTargetAddrName);
        DEBUGMSGT(("tlstmAddrTable:entry:create", "entry %s %p / row %p\n",
                   name, entry, row));
    }

    /*
     * populate index
     */
    memcpy(entry->snmpTargetAddrName, snmpTargetAddrName,
           snmpTargetAddrName_len);
    entry->snmpTargetAddrName_len = snmpTargetAddrName_len;
    netsnmp_tdata_row_add_index(row, ASN_PRIV_IMPLIED_OCTET_STR,
                                entry->snmpTargetAddrName,
                                snmpTargetAddrName_len);

    /*
     * defaults
     */
    entry->tlstmAddrServerFingerprint[0] = '\0';
    entry->tlstmAddrServerFingerprint_len = 0;
    entry->hashType = 0;
    entry->tlstmAddrServerIdentity[0] = '\0';
    entry->tlstmAddrServerIdentity_len = 0;
    entry->tlstmAddrStorageType = ST_NONVOLATILE;
    entry->tlstmAddrRowStatus = RS_NOTREADY;

    if (table_data) {
        DEBUGMSGTL(("tlstmAddrTable:row:insert", "row %p\n",row));
        netsnmp_tdata_add_row(table_data, row);
    }
    return row;
}

/*
 * allocate undo resources 
 */
static tlstmAddrTable_undo *
_allocUndo(tlstmAddrTable_entry * entry)
{
    if (!entry)
        return NULL;

    entry->undo = SNMP_MALLOC_TYPEDEF(tlstmAddrTable_undo);
    if (!entry->undo)
        return NULL;

    entry->undo->is_consistent = -1;    /* don't know */

    return entry->undo;
}

/*
 * free undo resources 
 */
static void
_freeUndo(tlstmAddrTable_entry * entry)
{
    if (!entry || !entry->undo)
        return;

    SNMP_FREE(entry->undo);
}

/*
 * remove a row from the table 
 */
void
tlstmAddrTable_removeEntry(netsnmp_tdata * table_data,
                           netsnmp_tdata_row * row)
{
    tlstmAddrTable_entry *entry;

    if (!row)
        return;                 /* Nothing to remove */

    entry = (tlstmAddrTable_entry *) row->data;

    if (table_data) {
        DEBUGMSGTL(("tlstmAddrTable:row:remove", "row %p\n",row));
        netsnmp_tdata_remove_and_delete_row(table_data, row);
    }
    else
        netsnmp_tdata_delete_row(row);

    DEBUGIF("tlstmAddrTable:entry:delete") {
        char name[sizeof(entry->snmpTargetAddrName)+1];
        snprintf(name, sizeof(name), "%s", entry->snmpTargetAddrName);
        DEBUGMSGT(("tlstmAddrTable:entry:delete", "entry %s %p / row %p\n",
                   name, entry, row));
    }
    if (entry && entry->undo)
        _freeUndo(entry);
    SNMP_FREE(entry);
}


/***********************************************************************
 *
 * PERSISTENCE
 *
 ***********************************************************************/
/** handles requests for the tlstmAddrTable table */
static int
tlstmAddrTable_handler(netsnmp_mib_handler *handler,
                       netsnmp_handler_registration *reginfo,
                       netsnmp_agent_request_info *reqinfo,
                       netsnmp_request_info *requests)
{

    netsnmp_request_info *request = NULL;
    netsnmp_table_request_info *table_info;
    netsnmp_tdata  *table_data;
    netsnmp_tdata_row *table_row;
    tlstmAddrTable_entry *table_entry;
    int             ret = SNMP_ERR_NOERROR;

    DEBUGMSGTL(("tlstmAddrTable:handler", "Processing mode %s (%d) request\n",
                se_find_label_in_slist("agent_mode", reqinfo->mode),
                reqinfo->mode));

    switch (reqinfo->mode) {
    /** ######################################################### GET #####
     *
     *   Read-support (also covers GetNext requests)
     */
    case MODE_GET:
        for (request = requests; request; request = request->next) {
            if (request->processed)
                continue;

            table_entry = (tlstmAddrTable_entry *)
                netsnmp_tdata_extract_entry(request);
            table_info = netsnmp_extract_table_info(request);
            switch (table_info->colnum) {
            case COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT:
            {
                u_char bin[42], *ptr = bin;
                size_t len = sizeof(bin), offset = 1;
                int    rc;

                if ((table_entry->hashType == 0) ||
                    (table_entry->tlstmAddrServerFingerprint_len ==0))
                    offset = 0;
                else {
                    bin[0] = table_entry->hashType;
                    rc = netsnmp_hex_to_binary(
                        &ptr, &len, &offset, 0,
                        table_entry->tlstmAddrServerFingerprint, NULL);
                    if (1 != rc)
                        ret = SNMP_ERR_GENERR;
                }
                if (ret == SNMP_ERR_NOERROR)
                    snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
                                             bin, offset);
            }
                break;          /* case COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT */
            case COLUMN_SNMPTLSTMADDRSERVERIDENTITY:
                snmp_set_var_typed_value
                    (request->requestvb, ASN_OCTET_STR,
                     (u_char *) table_entry->tlstmAddrServerIdentity,
                     table_entry->tlstmAddrServerIdentity_len);
                break;          /* case COLUMN_SNMPTLSTMADDRSERVERIDENTITY */
            case COLUMN_SNMPTLSTMADDRSTORAGETYPE:
                snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
                                           table_entry->tlstmAddrStorageType);
                break;          /* case COLUMN_SNMPTLSTMADDRSTORAGETYPE */
            case COLUMN_SNMPTLSTMADDRROWSTATUS:
                snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
                                           table_entry->tlstmAddrRowStatus);
                break;          /* case COLUMN_SNMPTLSTMADDRROWSTATUS */
            default:
                netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
                break;
            }                   /* switch colnum */
        }                       /* for requests */
        break;                  /* case MODE_GET */

        /*
         * Write-support
         */
    /** #################################################### RESERVE1 #####
     *
     *   In RESERVE1 we are just checking basic ASN.1 size/type restrictions.
     * You probably don't need to change any of this code. Don't change any
     * of the column values here. Save that for the ACTION phase.
     *
     *   The next phase is RESERVE2 or FREE.
     */
    case MODE_SET_RESERVE1:
        for (request = requests; request; request = request->next) {
            table_entry = (tlstmAddrTable_entry *)
                netsnmp_tdata_extract_entry(request);
            table_info = netsnmp_extract_table_info(request);

            if ((NULL != table_entry) &&
                (ST_READONLY == table_entry->tlstmAddrStorageType)) {
                ret = SNMP_ERR_NOTWRITABLE;
                break;
            }

            switch (table_info->colnum) {
            case COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT:
                ret = netsnmp_check_vb_type_and_max_size
                    (request->requestvb, ASN_OCTET_STR,
                     sizeof(table_entry->tlstmAddrServerFingerprint));
                /** check len/algorithm MIB requirements */
                if (ret == SNMP_ERR_NOERROR)
                    ret = netsnmp_cert_check_vb_fingerprint(request->requestvb);
                break;          /* case COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT */
            case COLUMN_SNMPTLSTMADDRSERVERIDENTITY:
                ret = netsnmp_check_vb_type_and_max_size
                    (request->requestvb, ASN_OCTET_STR,
                     sizeof(table_entry->tlstmAddrServerIdentity));
                break;          /* case COLUMN_SNMPTLSTMADDRSERVERIDENTITY */
            case COLUMN_SNMPTLSTMADDRSTORAGETYPE:
                ret = netsnmp_check_vb_storagetype
                    (request->requestvb,
                     (table_entry ?
                      table_entry->tlstmAddrStorageType : ST_NONE));
                break;          /* case COLUMN_SNMPTLSTMADDRSTORAGETYPE */
            case COLUMN_SNMPTLSTMADDRROWSTATUS:
                ret = netsnmp_check_vb_rowstatus_with_storagetype
                    (request->requestvb,
                     (table_entry ?
                      table_entry->tlstmAddrRowStatus : RS_NONEXISTENT),
                     (table_entry ?
                      table_entry->tlstmAddrStorageType : ST_NONE));
                break;          /* case COLUMN_SNMPTLSTMADDRROWSTATUS */
            default:
                ret = SNMP_ERR_NOTWRITABLE;
            }                   /* switch colnum */

            if (ret != SNMP_ERR_NOERROR)
                break;
        }                       /* for requests */
        break;                  /* case MODE_SET_RESERVE1 */

    /** #################################################### RESERVE2 #####
     *
     *   RESERVE2 is for checking additional restrictions from the MIB.
     * Since these restrictions are often in the description of the object,
     * mib2c can't generate code. It's possible that you need to add
     * additional checks here. However, don't change any of the column
     * values here. Save that for the ACTION phase.
     *
     *   The next phase is ACTION or FREE.
     */
    case MODE_SET_RESERVE2:
        for (request = requests; request; request = request->next) {
            table_entry = (tlstmAddrTable_entry *)
                netsnmp_tdata_extract_entry(request);
            table_data = netsnmp_tdata_extract_table(request);
            table_info = netsnmp_extract_table_info(request);
            /*
             * if no table_row, create one
             */
            if (!table_entry) {
                table_row = tlstmAddrTable_createEntry
                    (table_data, (char*)table_info->indexes->val.string,
                     table_info->indexes->val_len);
                if (!table_row) {
                    ret = SNMP_ERR_RESOURCEUNAVAILABLE;
                    break;
                }
                table_entry = table_row->data;
                _allocUndo(table_entry);
                if (table_entry && !table_entry->undo) {
                    tlstmAddrTable_removeEntry(table_data, table_row);
                    table_row = NULL;
                    ret = SNMP_ERR_RESOURCEUNAVAILABLE;
                    break;
                }
                table_entry->undo->fate = FATE_NEWLY_CREATED;
                /** associate row with requests */
                netsnmp_insert_tdata_row(request, table_row);
            }

            /** allocate undo structure, if needed */
            if (!table_entry->undo) {
                _allocUndo(table_entry);
                if (!table_entry->undo) {
                    ret = SNMP_ERR_RESOURCEUNAVAILABLE;
                    break;
                }
            }

            /** don't allow multiple sets of same column */
            if (table_entry->undo->req[table_info->colnum]) {
                DEBUGMSGT(("tlstmAddrTable:reserve2",
                           "multiple sets to col %d in request\n",
                           table_info->colnum));
                ret = SNMP_ERR_INCONSISTENTNAME;
                break;
            }
            table_entry->undo->req[table_info->colnum] = request;

            if (ret != SNMP_ERR_NOERROR)
                break;
        }                       /* for requests */

        /** make sure rowstatus is used to create rows */
        if (ret == SNMP_ERR_NOERROR) {
            for (request = requests; request; request = request->next) {
                if (request->processed)
                    continue;

                table_entry = (tlstmAddrTable_entry *)
                    netsnmp_tdata_extract_entry(request);
                if ((table_entry->undo->fate != FATE_NEWLY_CREATED) ||
                    (table_entry->undo->req[COLUMN_SNMPTLSTMADDRROWSTATUS]))
                    continue;
                ret = SNMP_ERR_INCONSISTENTNAME;
                break;
            }                   /* for requests / creation */
        }                       /* if no error */
        break;                  /* case MODE_SET_RESERVE2 */

    /** ######################################################## FREE #####
     *
     *   FREE is for cleaning up after a failed request (during either
     * RESERVE1 or RESERVE2). So any allocated resources need to be
     * released.
     *
     *   This the final phase for this path in the state machine.
     */
    case MODE_SET_FREE:
        /*
         * release undo resources,  remove any newly created rows
         */
        for (request = requests; request; request = request->next) {
            table_row = netsnmp_tdata_extract_row(request);
            table_data  =  netsnmp_tdata_extract_table(request);
            table_entry =
                (tlstmAddrTable_entry *) table_row ? table_row->
                data : NULL;

            if (!table_entry || !table_entry->undo)
                continue;

            /** disassociate row with requests */
            netsnmp_remove_tdata_row(request, table_row);

            if (FATE_NEWLY_CREATED == table_entry->undo->fate)
                tlstmAddrTable_removeEntry(table_data, table_row);
            else
                _freeUndo(table_entry);
        }
        break;                  /* case MODE_SET_FREE */

    /** ###################################################### ACTION #####
     *
     *   In the ACTION phase, we perform any sets that can be undone.
     * (Save anything that can't be undone for the COMMIT phase.)
     *
     *   After individual columns have been done, you should check that the
     * row as a whole is consistent.
     *
     * The next phase is UNDO or COMMIT.
     */
    case MODE_SET_ACTION:
        for (request = requests; request; request = request->next) {
            table_entry = (tlstmAddrTable_entry *)
                netsnmp_tdata_extract_entry(request);
            table_info = netsnmp_extract_table_info(request);

            switch (table_info->colnum) {
            case COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT:
            {
                u_char *tmp = (u_char*)table_entry->tlstmAddrServerFingerprint;

                memcpy(table_entry->undo->tlstmAddrServerFingerprint,
                       table_entry->tlstmAddrServerFingerprint,
                       sizeof(table_entry->tlstmAddrServerFingerprint));
                table_entry->undo->tlstmAddrServerFingerprint_len =
                    table_entry->tlstmAddrServerFingerprint_len;
                table_entry->undo->hashType = table_entry->hashType;

                table_entry->hashType = request->requestvb->val.string[0];
                table_entry->tlstmAddrServerFingerprint_len =
                    sizeof(table_entry->tlstmAddrServerFingerprint);
                memset(table_entry->tlstmAddrServerFingerprint, 0,
                       sizeof(table_entry->tlstmAddrServerFingerprint));
                table_entry->tlstmAddrServerFingerprint_len =
                    netsnmp_binary_to_hex(&tmp, &table_entry->tlstmAddrServerFingerprint_len,
                                          0, &request->requestvb->val.string[1],
                                          request->requestvb->val_len - 1);
                if (0 == table_entry->tlstmAddrServerFingerprint_len)
                    ret = SNMP_ERR_GENERR;
            }
                break;          /* case COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT */
            case COLUMN_SNMPTLSTMADDRSERVERIDENTITY:
                memcpy(table_entry->undo->tlstmAddrServerIdentity,
                       table_entry->tlstmAddrServerIdentity,
                       sizeof(table_entry->tlstmAddrServerIdentity));
                table_entry->undo->tlstmAddrServerIdentity_len =
                    table_entry->tlstmAddrServerIdentity_len;
                memset(table_entry->tlstmAddrServerIdentity, 0,
                       sizeof(table_entry->tlstmAddrServerIdentity));
                memcpy(table_entry->tlstmAddrServerIdentity,
                       request->requestvb->val.string,
                       request->requestvb->val_len);
                table_entry->tlstmAddrServerIdentity_len =
                    request->requestvb->val_len;
                break;          /* case COLUMN_SNMPTLSTMADDRSERVERIDENTITY */
            case COLUMN_SNMPTLSTMADDRSTORAGETYPE:
                table_entry->undo->tlstmAddrStorageType =
                    table_entry->tlstmAddrStorageType;
                table_entry->tlstmAddrStorageType =
                    *request->requestvb->val.integer;
                break;          /* case COLUMN_SNMPTLSTMADDRSTORAGETYPE */
            case COLUMN_SNMPTLSTMADDRROWSTATUS:
                table_entry->undo->tlstmAddrRowStatus =
                    table_entry->tlstmAddrRowStatus;
                table_entry->tlstmAddrRowStatus =
                    *request->requestvb->val.integer;
                break;          /* case COLUMN_SNMPTLSTMADDRROWSTATUS */
            }                   /* switch colnum */
            if (ret != SNMP_ERR_NOERROR)
                break;
        }                       /* set values for requests */

        if (ret == SNMP_ERR_NOERROR) {

            /*
             * All columns now have their final values set. check the
             * internal consistency of each row.
             */
            for (request = requests; request; request = request->next) {
                table_entry = (tlstmAddrTable_entry *)
                    netsnmp_tdata_extract_entry(request);
                table_info = netsnmp_extract_table_info(request);

                if (table_entry->undo->is_consistent != -1)
                    continue;   /* already checked */

                /** assume consistency */
                table_entry->undo->is_consistent = 1;

                /*
                 * per mib, can't have empty fingerprint and wildcard id
                 */
                if ( (0 == table_entry->tlstmAddrServerFingerprint_len) &&
                     (1 == table_entry->tlstmAddrServerIdentity_len) &&
                     ('*' == table_entry->tlstmAddrServerIdentity[0]) ) {
                    DEBUGMSGTL(("tlstmAddrTable", "fingerprint must not "
                                "be empty for wildcard (*) identity\n"));
                    table_entry->undo->is_consistent = 0;
                }
                
                if ((RS_IS_ACTIVE(table_entry->tlstmAddrRowStatus)) &&
                    ((!table_entry->undo->req[COLUMN_SNMPTLSTMADDRROWSTATUS]) ||
                     (RS_IS_ACTIVE(table_entry->undo->tlstmAddrRowStatus)))) {
                    /*
                     * check mib restrictions on active rows.
                     */
                    if (table_entry->undo->req[COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT]) {
                        table_entry->undo->is_consistent = 0;
                        request = table_entry->undo->req[COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT];
                    }
                    else if (table_entry->undo->req[COLUMN_SNMPTLSTMADDRSERVERIDENTITY]) {
                        table_entry->undo->is_consistent = 0;
                        request = table_entry->undo->req[COLUMN_SNMPTLSTMADDRSERVERIDENTITY];
                    }
                    else if (table_entry->undo->req[COLUMN_SNMPTLSTMADDRSTORAGETYPE]) {
                        table_entry->undo->is_consistent = 0;
                        request = table_entry->undo->req[COLUMN_SNMPTLSTMADDRSTORAGETYPE];
                    }
                    
                    if (!table_entry->undo->is_consistent)
                        ret = SNMP_ERR_INCONSISTENTVALUE; /* per mib */
                } /* active row */
                else if (RS_IS_GOING_ACTIVE
                         (table_entry->tlstmAddrRowStatus)) {
                    /** if going active, inconsistency is fatal */
                    if (!table_entry->undo->is_consistent) {
                        if (FATE_NEWLY_CREATED == table_entry->undo->fate)
                            ret = SNMP_ERR_INCONSISTENTNAME;
                        else
                            ret = SNMP_ERR_INCONSISTENTVALUE;
                        request = table_entry->undo->req[COLUMN_SNMPTLSTMADDRROWSTATUS];
                    }
                } /* going active */
                else if (RS_DESTROY == table_entry->tlstmAddrRowStatus) {
                    /** can't delete active row */
                    if (RS_IS_ACTIVE(table_entry->undo->tlstmAddrRowStatus)) {
                        ret = SNMP_ERR_INCONSISTENTVALUE;
                        request = table_entry->undo->req[COLUMN_SNMPTLSTMADDRROWSTATUS];
                    }
                }               /* destroy */
                if (ret != SNMP_ERR_NOERROR)
                    break;
            }                   /* consistency for requests */
        }                       /* if no error */
        break;                  /* case MODE_SET_ACTION */

    /** ######################################################## UNDO #####
     *
     *   UNDO is for cleaning up any failed requests that went through the
     * ACTION phase.
     *
     *   This the final phase for this path in the state machine.
     */
    case MODE_SET_UNDO:
        for (request = requests; request; request = request->next) {
            table_row = netsnmp_tdata_extract_row(request);
            table_entry =
                (tlstmAddrTable_entry *) table_row ? table_row->
                data : NULL;
            table_data = netsnmp_tdata_extract_table(request);
            table_info = netsnmp_extract_table_info(request);

            switch (table_info->colnum) {
            case COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT:
                /*
                 * restore tlstmAddrServerFingerprint value 
                 */
                memcpy(table_entry->tlstmAddrServerFingerprint,
                       table_entry->undo->tlstmAddrServerFingerprint,
                       sizeof(table_entry->tlstmAddrServerFingerprint));
                table_entry->tlstmAddrServerFingerprint_len =
                    table_entry->undo->tlstmAddrServerFingerprint_len;
                table_entry->hashType = table_entry->undo->hashType;
                break;          /* case COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT */
            case COLUMN_SNMPTLSTMADDRSERVERIDENTITY:
                /*
                 * restore tlstmAddrServerIdentity value 
                 */
                memcpy(table_entry->tlstmAddrServerIdentity,
                       table_entry->undo->tlstmAddrServerIdentity,
                       sizeof(table_entry->tlstmAddrServerIdentity));
                table_entry->tlstmAddrServerIdentity_len =
                    table_entry->undo->tlstmAddrServerIdentity_len;
                break;          /* case COLUMN_SNMPTLSTMADDRSERVERIDENTITY */
            case COLUMN_SNMPTLSTMADDRSTORAGETYPE:
                /*
                 * restore tlstmAddrStorageType value 
                 */
                table_entry->tlstmAddrStorageType =
                    table_entry->undo->tlstmAddrStorageType;
                break;          /* case COLUMN_SNMPTLSTMADDRSTORAGETYPE */
            case COLUMN_SNMPTLSTMADDRROWSTATUS:
                /*
                 * restore tlstmAddrRowStatus value 
                 */
                table_entry->tlstmAddrRowStatus =
                    table_entry->undo->tlstmAddrRowStatus;
                break;          /* case COLUMN_SNMPTLSTMADDRROWSTATUS */
            }                   /* switch colnum */
        }                       /* for requests */

        /*
         * release undo data
         * or remove any newly created rows
         */
        for (request = requests; request; request = request->next) {
            table_row = netsnmp_tdata_extract_row(request);
            table_entry =
                (tlstmAddrTable_entry *) table_row ? table_row->
                data : NULL;

            if (!table_entry || !table_entry->undo)
                continue;

            /** disassociate row with requests */
            netsnmp_remove_tdata_row(request, table_row);

            if (FATE_NEWLY_CREATED == table_entry->undo->fate)
                tlstmAddrTable_removeEntry(table_data, table_row);
            else
                _freeUndo(table_entry);
        }                       /* for requests */
        break;                  /* case MODE_SET_UNDO */

    /** ###################################################### COMMIT #####
     *
     *   COMMIT is the final success state, when all changes are finalized.
     * There is not recovery state should something faile here.
     *
     *   This the final phase for this path in the state machine.
     */
    case MODE_SET_COMMIT:
        for (request = requests; request; request = request->next) {
            table_row = netsnmp_tdata_extract_row(request);
            table_data = netsnmp_tdata_extract_table(request);
            table_info = netsnmp_extract_table_info(request);
            table_entry = (tlstmAddrTable_entry *)
                netsnmp_tdata_extract_entry(request);

            if (!table_entry || !table_entry->undo)
                continue;

            if ((RS_NOTREADY == table_entry->tlstmAddrRowStatus) &&
                table_entry->undo->is_consistent)
                table_entry->tlstmAddrRowStatus = RS_NOTINSERVICE;
            else if ((RS_NOTINSERVICE == table_entry->tlstmAddrRowStatus) &&
                     (0 == table_entry->undo->is_consistent))
                table_entry->tlstmAddrRowStatus = RS_NOTREADY;

            /** release undo data for requests with no rowstatus */
            if (table_entry->undo &&
                !table_entry->undo->req[COLUMN_SNMPTLSTMADDRROWSTATUS] != 0) {

                _freeUndo(table_entry);

                /** update active addrs */
                if ((0 == table_entry->addr_flags) &&
                    (table_entry->tlstmAddrRowStatus == RS_ACTIVE))
                    _addrs_add(table_entry);
                else if ((0 != table_entry->addr_flags) &&
                         (table_entry->tlstmAddrRowStatus == RS_DESTROY))
                    _addrs_remove(table_entry);
            }

            switch (table_info->colnum) {
                case COLUMN_SNMPTLSTMADDRROWSTATUS:
                    switch (table_entry->tlstmAddrRowStatus) {
                    case RS_CREATEANDGO:
                    /** Fall-through */
                    case RS_ACTIVE:
                        table_entry->tlstmAddrRowStatus = RS_ACTIVE;
                        if (0 == table_entry->addr_flags)
                            _addrs_add(table_entry);
                        break;

                    case RS_CREATEANDWAIT:
                        /** Fall-through */
                    case RS_NOTINSERVICE:
                        /** simply set status based on consistency */
                        if (table_entry->undo->is_consistent)
                            table_entry->tlstmAddrRowStatus =
                                RS_NOTINSERVICE;
                        else
                            table_entry->tlstmAddrRowStatus = RS_NOTREADY;
                        if (0 != table_entry->addr_flags)
                            _addrs_remove(table_entry);
                        break;

                    case RS_DESTROY:
                        if (0 != table_entry->addr_flags)
                            _addrs_remove(table_entry);
                        /** disassociate row with requests */
                        netsnmp_remove_tdata_row(request, table_row);
                        tlstmAddrTable_removeEntry(table_data, table_row);
                        table_row = NULL;
                        table_entry = NULL;
                    }
                    /** release undo data */
                    _freeUndo(table_entry);
                    break;      /* case COLUMN_SNMPTLSTMADDRROWSTATUS */

                case COLUMN_SNMPTLSTMADDRSTORAGETYPE:
                    if (RS_ACTIVE == table_entry->tlstmAddrRowStatus)
                        _addr_tweak_storage(table_entry);
                    break;

                case COLUMN_SNMPTLSTMADDRSERVERFINGERPRINT:
                case COLUMN_SNMPTLSTMADDRSERVERIDENTITY:
                    break;
                }               /* switch colnum */
        }                       /* for requests */

        /** update last changed */
        _last_changed = netsnmp_get_agent_uptime();

        /** set up to save persistent store */
        snmp_store_needed(NULL);

        break;                  /* case MODE_SET_COMMIT */
    }                           /* switch (reqinfo->mode) */

    if (ret != SNMP_ERR_NOERROR)
        netsnmp_set_request_error(reqinfo, request, ret);

    return SNMP_ERR_NOERROR;
}

/***********************************************************************
 *
 * PERSISTENCE
 *
 ***********************************************************************/
static int
_count_handler(netsnmp_mib_handler *handler,
               netsnmp_handler_registration *reginfo,
               netsnmp_agent_request_info *reqinfo,
               netsnmp_request_info *requests)
{
    int                val;

    if (MODE_GET != reqinfo->mode) {
        snmp_log(LOG_ERR, "bad mode in RO handler");
        return SNMP_ERR_GENERR;
    }

    if ((NULL == _table_data) || (NULL == _table_data->container))
        val = 0;
    else
        val = CONTAINER_SIZE(_table_data->container);

    snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE,
                             (u_char *) &val, sizeof(val));
   
    if (handler->next && handler->next->access_method)
        return netsnmp_call_next_handler(handler, reginfo, reqinfo,
                                         requests);
    
    return SNMP_ERR_NOERROR;
}

/** **************************************************************************
 *
 * handle cache / interactions with tlstmAddr container in snmplib
 *
 ** *************************************************************************/
static void
_addrs_add(tlstmAddrTable_entry *entry)
{
    netsnmp_container *addrs;
    snmpTlstmAddr     *addr;

    if (NULL == entry)
        return;

    DEBUGMSGTL(("tlstmAddrTable:addrs:add", "name %s, fp %s\n",
                entry->snmpTargetAddrName, entry->tlstmAddrServerFingerprint));

    /** get current active addrs */
    addrs = netsnmp_tlstmAddr_container();
    if (NULL == addrs)
        return;

    addr = netsnmp_tlstmAddr_create(entry->snmpTargetAddrName);
    if (NULL == addr)
        return;

    if (entry->tlstmAddrServerFingerprint_len)
        addr->fingerprint = strdup(entry->tlstmAddrServerFingerprint);
    if (entry->tlstmAddrServerIdentity_len)
        addr->identity = strdup(entry->tlstmAddrServerIdentity);
    addr->hashType = entry->hashType;

    addr->flags = TLSTM_ADDR_FROM_MIB;
    if (entry->tlstmAddrStorageType == ST_NONVOLATILE)
        addr->flags |= TLSTM_ADDR_NONVOLATILE;

    if (CONTAINER_INSERT(addrs, addr) != 0) {
        netsnmp_tlstmAddr_free(addr);
        snmp_log(LOG_ERR, "could not insert new tlstm addr");
    }
}

static void
_addrs_remove(tlstmAddrTable_entry *entry)
{
    netsnmp_container *addrs;
    snmpTlstmAddr      addr;

    if (NULL == entry)
        return;

    DEBUGMSGTL(("tlstmAddrTable:addr:remove", "name %s, fp %s\n",
                entry->snmpTargetAddrName, entry->tlstmAddrServerFingerprint));

    /** get current active addrs */
    addrs = netsnmp_tlstmAddr_container();
    if (NULL == addrs)
        return;

    addr.name = entry->snmpTargetAddrName;
    if (CONTAINER_REMOVE(addrs, &addr) != 0) {
        snmp_log(LOG_ERR, "could not remove tlstm addr");
    }
    entry->addr_flags = 0;
}

static void
_addr_tweak_storage(tlstmAddrTable_entry *entry)
{
    netsnmp_container *addrs;
    snmpTlstmAddr     *addr, index;

    if (NULL == entry)
        return;

    DEBUGMSGTL(("tlstmAddrTable:addr:tweak", "name %s, st %d\n",
                entry->snmpTargetAddrName, entry->tlstmAddrStorageType));

    /** get current active addrs */
    addrs = netsnmp_tlstmAddr_container();
    if (NULL == addrs)
        return;

    index.name = entry->snmpTargetAddrName;
    addr = CONTAINER_FIND(addrs, &index);
    if (NULL == addr) {
        DEBUGMSGTL(("tlstmAddrTable:addr:tweak", "couldn't find addr!\n"));
        return;
    }

    if (entry->tlstmAddrStorageType == ST_NONVOLATILE)
        addr->flags |= TLSTM_ADDR_NONVOLATILE;
    else
        addr->flags &= ~TLSTM_ADDR_NONVOLATILE;
}

static netsnmp_tdata_row *
_entry_from_addr(snmpTlstmAddr  *addr)
{
    netsnmp_tdata_row *row;
    tlstmAddrTable_entry *entry;

    row = tlstmAddrTable_createEntry(NULL, addr->name, strlen(addr->name));
    if (NULL == row) {
        snmp_log(LOG_ERR, "can create tlstmAddr row entry\n");
        return NULL;
    }
    entry = row->data;

    if (addr->flags & TLSTM_ADDR_FROM_CONFIG)
        entry->tlstmAddrStorageType = ST_PERMANENT;
    else if (! (addr->flags & TLSTM_ADDR_NONVOLATILE))
        entry->tlstmAddrStorageType = ST_VOLATILE;

    entry->tlstmAddrRowStatus = RS_ACTIVE;

    if (addr->fingerprint) {
        entry->tlstmAddrServerFingerprint_len = strlen(addr->fingerprint);
        if (entry->tlstmAddrServerFingerprint_len >
            sizeof(entry->tlstmAddrServerFingerprint))
            entry->tlstmAddrServerFingerprint_len =
                sizeof(entry->tlstmAddrServerFingerprint) - 1;
        memcpy(entry->tlstmAddrServerFingerprint, addr->fingerprint,
               entry->tlstmAddrServerFingerprint_len);
        entry->tlstmAddrServerFingerprint[sizeof(entry->tlstmAddrServerFingerprint) - 1] = 0;
    }

    if (addr->identity) {
        entry->tlstmAddrServerIdentity_len = strlen(addr->identity);
        if (entry->tlstmAddrServerIdentity_len >
            sizeof(entry->tlstmAddrServerIdentity))
            entry->tlstmAddrServerIdentity_len =
                sizeof(entry->tlstmAddrServerIdentity) - 1;
        memcpy(entry->tlstmAddrServerIdentity, addr->identity,
               entry->tlstmAddrServerIdentity_len);
        entry->tlstmAddrServerIdentity[sizeof(entry->tlstmAddrServerIdentity) - 1] = 0;
    }

    entry->hashType = addr->hashType;
    entry->addr_flags = addr->flags;

    return row;
}

static int
_cache_load(netsnmp_cache *cache, netsnmp_tdata *table)
{
    netsnmp_container *addrs;
    netsnmp_iterator  *itr;
    snmpTlstmAddr     *addr;
    netsnmp_tdata_row *row;
    int                rc = 0;

    DEBUGMSGTL(("tlstmAddrTable:cache:load", "called, %" NETSNMP_PRIz "d rows\n",
                CONTAINER_SIZE(table->container)));

    /** get current active rows */
    addrs = netsnmp_tlstmAddr_container();
    if (NULL == addrs)
        return 0;

    DEBUGMSGTL(("tlstmAddrTable:cache:load", "tlstmAddr %" NETSNMP_PRIz "d rows\n",
                CONTAINER_SIZE(addrs)));
    itr = CONTAINER_ITERATOR(addrs);
    if (NULL == itr) {
        DEBUGMSGTL(("tlstmAddrTable:cache:load",
                    "cant get iterator\n"));
        return -1;
    }

    /*
     * insert rows for active addrs into tbl container
     */
    addr = ITERATOR_FIRST(itr);
    for( ; addr; addr = ITERATOR_NEXT(itr)) {

        row = _entry_from_addr(addr);
        if (NULL == row) {
            rc =-1;
            break;
        }

        if (netsnmp_tdata_add_row(table, row) != SNMPERR_SUCCESS) {
            tlstmAddrTable_removeEntry(NULL, row);
            rc = -1;
            break;
        }
    }
    ITERATOR_RELEASE(itr);

    DEBUGMSGTL(("tlstmAddrTable:cache:load", "done, %" NETSNMP_PRIz "d rows\n",
                CONTAINER_SIZE(table->container)));

    return rc;
}

static void
_cache_free(netsnmp_cache *cache, netsnmp_tdata *table)
{
    netsnmp_tdata_row *row;
    netsnmp_iterator   *tbl_itr;
    tlstmAddrTable_entry   *entry;

    DEBUGMSGTL(("tlstmAddrTable:cache:free", "called, %" NETSNMP_PRIz "d rows\n",
                CONTAINER_SIZE(table->container)));

    tbl_itr = CONTAINER_ITERATOR(table->container);
    if (NULL == tbl_itr) {
        DEBUGMSGTL(("tlstmAddrTable:cache:free",
                    "cant get entry iterator\n"));
        return;
    }

    row = ITERATOR_FIRST(tbl_itr);
    for( ; row; row = ITERATOR_NEXT(tbl_itr)) {
        entry = row->data;

        /*
         * remove all active rows (they are in the addrs container kept
         * by the library). Keep inactive ones for next time.
         */
        if (entry->tlstmAddrRowStatus == RS_ACTIVE) {
            tlstmAddrTable_removeEntry(NULL, row);
            ITERATOR_REMOVE(tbl_itr);
            continue;
        }
    }
    ITERATOR_RELEASE(tbl_itr);

    DEBUGMSGTL(("tlstmAddrTable:cache:free", "done, %" NETSNMP_PRIz "d rows\n",
                CONTAINER_SIZE(table->container)));
}

/***********************************************************************
 *
 * PERSISTENCE
 *
 ***********************************************************************/

static int  _tlstmAddrTable_save_rows(int majorID, int minorID,
                                                void *serverarg,
                                                void *clientarg);
static void _tlstmAddrTable_row_restore_mib(const char *token,
                                                       char *buf);
static const char mib_token[] = "snmpTlstmAddrEntry";

/************************************************************
 * *_init_persistence should be called from the main table
 * init routine.
 *
 * If your table depends on rows in another table,
 * you should register your callback after the other table,
 * which should ensure the rows on which you depend are saved
 * (and re-created) before the dependent rows.
 */
static void
_tlstmAddr_init_persistence(void)
{
    int             rc;

    if (NULL == _table_data) {
        snmp_log(LOG_ERR, "no table data for tlstmAddr persistence!\n");
        return;
    }

    register_config_handler(NULL, mib_token,
                            _tlstmAddrTable_row_restore_mib, NULL,
                            NULL);
    rc = snmp_register_callback(SNMP_CALLBACK_LIBRARY,
                                SNMP_CALLBACK_STORE_DATA,
                                _tlstmAddrTable_save_rows,
                                _table_data->container);

    if (rc != SNMP_ERR_NOERROR)
        snmp_log(LOG_ERR, "error registering for STORE_DATA callback "
                 "in _tlstmAddrTable_init_persistence\n");
}

static int
_save_entry(tlstmAddrTable_entry *entry, void *type)
{
    char   buf[SNMP_MAXBUF_SMALL], *hashType;

    hashType = se_find_label_in_slist("cert_hash_alg", entry->hashType);
    if (NULL == hashType) {
        snmp_log(LOG_ERR, "skipping entry unknown hash type %d\n",
                 entry->hashType);
        return SNMP_ERR_GENERR;
    }

    /*
     * build the line
     */
    netsnmp_assert(0 == entry->snmpTargetAddrName[
                       entry->snmpTargetAddrName_len]);
    netsnmp_assert(0 == entry->tlstmAddrServerFingerprint[
                       entry->tlstmAddrServerFingerprint_len]);
    snprintf(buf, sizeof(buf), "%s %s --%s %s %s %d", mib_token,
             entry->snmpTargetAddrName, hashType,
             entry->tlstmAddrServerFingerprint,
             entry->tlstmAddrServerIdentity,
             entry->tlstmAddrRowStatus);
    buf[sizeof(buf)-1] = 0;

    read_config_store(type, buf);
    DEBUGMSGTL(("tlstmAddrTable:row:save", "saving entry '%s'\n", buf));

    return SNMP_ERR_NOERROR;
}

static int
_save_addrs(snmpTlstmAddr *addrs, void *app_type)
{
    char buf[SNMP_MAXBUF_SMALL], *hashType;

    if (NULL == addrs)
        return SNMP_ERR_GENERR;

    hashType = se_find_label_in_slist("cert_hash_alg", addrs->hashType);
    if (NULL == hashType) {
        snmp_log(LOG_ERR, "skipping entry unknown hash type %d\n",
                 addrs->hashType);
        return SNMP_ERR_GENERR;
    }
    snprintf(buf, sizeof(buf), "%s %s --%s %s %s %d", mib_token, addrs->name,
             hashType, addrs->fingerprint, addrs->identity, RS_ACTIVE);

    DEBUGMSGTL(("tlstmAddrTable:addrs:save", "saving addrs '%s'\n",
                buf));
    read_config_store(app_type, buf);

    return SNMP_ERR_NOERROR;
}

static int
_tlstmAddrTable_save_rows(int majorID, int minorID, void *serverarg,
                      void *clientarg)
{
    char            sep[] =
        "##############################################################";
    char            buf[] = "#\n" "# tlstmAddr persistent data\n" "#";
    char           *type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
                                                 NETSNMP_DS_LIB_APPTYPE);

    netsnmp_container *mib_addrs = (netsnmp_container *) clientarg;
    netsnmp_container *active_addrs = netsnmp_tlstmAddr_container();
    netsnmp_iterator  *tbl_itr, *addrs_itr = NULL;
    netsnmp_tdata_row *row;
    snmpTlstmAddr     *addr;
    tlstmAddrTable_entry *entry;

    if (((NULL == mib_addrs) || (CONTAINER_SIZE(mib_addrs) == 0)) &&
        ((NULL == active_addrs) || (CONTAINER_SIZE(active_addrs) == 0)))
        return SNMPERR_SUCCESS;

    read_config_store((char *) type, sep);
    read_config_store((char *) type, buf);

    /*
     * save active rows from addr container
     */
    if (NULL != active_addrs) {
        addrs_itr = CONTAINER_ITERATOR(active_addrs);
        if (NULL == addrs_itr) {
            DEBUGMSGTL(("tlstmAddrTable:save", "cant get addrs iterator\n"));
            addr = NULL;
        }
        else
            addr = ITERATOR_FIRST(addrs_itr);

        for( ; addr; addr = ITERATOR_NEXT(addrs_itr)) {
            /** don't store config rows */
            if ((addr->flags & TLSTM_ADDR_FROM_CONFIG) ||
                ! (addr->flags & TLSTM_ADDR_NONVOLATILE))
                continue;
            _save_addrs(addr, type);
        }
    }
    ITERATOR_RELEASE(addrs_itr);

    /*
     * save inactive rows from mib
     */
    tbl_itr = CONTAINER_ITERATOR(mib_addrs);
    if (NULL == tbl_itr)
        DEBUGMSGTL(("tlstmAddrTable:save", "cant get table iterator\n"));
    else {
        row = ITERATOR_FIRST(tbl_itr);
        for( ; row; row = ITERATOR_NEXT(tbl_itr)) {
            entry = row->data;

            /*
             * skip all active rows (should be in active_addrs and thus saved
             * above) and volatile rows.
             */
            if ((entry->tlstmAddrRowStatus == RS_ACTIVE) ||
                (entry->tlstmAddrStorageType != ST_NONVOLATILE))
                continue;

            _save_entry(entry, type);
        }
        ITERATOR_RELEASE(tbl_itr);
    }

    read_config_store((char *) type, sep);
    read_config_store((char *) type, "\n");

    /*
     * never fails 
     */
    return SNMPERR_SUCCESS;
}

static void
_tlstmAddrTable_row_restore_mib(const char *token, char *buf)
{
    char                   name[SNMPADMINLENGTH + 1], id[SNMPADMINLENGTH + 1],
                           fingerprint[SNMPTLSFINGERPRINT_MAX_LEN + 1];
    size_t                 name_len = sizeof(name), id_len = sizeof(id),
                           fp_len = sizeof(fingerprint);
    u_char                 hashType, rowStatus;
    int                    rc;

    /** need somewhere to save rows */
    netsnmp_assert(_table_data && _table_data->container); 

    rc = netsnmp_tlstmAddr_restore_common(&buf, name, &name_len, id, &id_len,
                                          fingerprint, &fp_len, &hashType);
    if (rc < 0)
        return;

    if (NULL == buf) {
        config_perror("incomplete line");
        return;
    }
    rowStatus = atoi(buf);

    /*
     * if row is active, add it to the addrs container so it is available
     * for use. Do not add it to the table, since it will be added
     * during cache_load.
     */
    if (RS_ACTIVE == rowStatus) {
        snmpTlstmAddr *addr;

        addr = netsnmp_tlstmAddr_create(name);
        if (!addr)
            return;

        if (fp_len)
            addr->fingerprint = strdup(fingerprint);
        if (id_len)
            addr->identity = strdup(id);
        addr->hashType = hashType;
        addr->flags = TLSTM_ADDR_FROM_MIB | TLSTM_ADDR_NONVOLATILE;

        if (netsnmp_tlstmAddr_add(addr) != 0)
            netsnmp_tlstmAddr_free(addr);
    }
    else {
        netsnmp_tdata_row     *row;
        tlstmAddrTable_entry  *entry;

        row = tlstmAddrTable_createEntry(_table_data, name, name_len);
        if (!row)
            return;

        entry = row->data;
        
        entry->hashType = hashType;
        memcpy(entry->tlstmAddrServerFingerprint,fingerprint, fp_len);
        entry->tlstmAddrServerFingerprint_len = fp_len;
        memcpy(entry->tlstmAddrServerIdentity, id, id_len);
        entry->tlstmAddrServerIdentity_len = id_len;
        entry->tlstmAddrStorageType = ST_NONVOLATILE;
        entry->tlstmAddrRowStatus = rowStatus;
    }
}
