/*
 * Vince version 1.0.1
 * 
 * 
 * +++++++++++
 *  V I N C E  ---  Vendor Independent Network Control Entity
 * +++++++++++
 * Copyright (c) 1994     Advanced Research Projects Agency (ARPA/CSTO)
 *                                   and the
 *                        Naval Research Laboratory (NRL/CCS)
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the software,
 * derivative works or modified versions, and any portions thereof, and
 * that both notices appear in supporting documentation.
 * 
 * ARPA AND NRL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION
 * AND DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
 * RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * ARPA and NRL request users of this software to return to:
 * 
 *                 vince-distribution@cmf.nrl.navy.mil
 * 
 *                            or
 * 
 *                 Naval Research Laboratory, Code 5590
 *                 Center for Computational Science
 *                 Washington, D.C. 20375-5000
 * 
 * any improvements or extensions that they make and grant ARPA and NRL 
 * the rights to redistribute these changes.
 * 
 */

#include <stdio.h>
#include <string.h>
#include <uni_errors.h>
#include <uni_enums.h>
#include <uni.h>
#include <uni_mesg.h>
#include <uni_debug.h>
#include <uni_message.h>
#include <uni_decode.h>

element aal_params_decoder();
int cell_rate_param_ident();
int cell_rate_param_check();
element traffic_desc_decoder();
element bb_bearer_capab_decoder();
element bb_high_layer_info_decoder();
element bb_low_layer_info_decoder();
element call_state_decoder();
element called_pty_number_decoder();
element called_pty_subaddr_decoder();
element calling_pty_number_decoder();
element calling_pty_subaddr_decoder();
element cause_decoder();
element connection_ident_decoder();
element qos_params_decoder();
element bb_repeat_indic_decoder();
element restart_indic_decoder();
element bb_send_complete_decoder();
element transit_net_select_decoder();
element endpoint_reference_decoder();
/*element endpoint_state_decoder(); */

#define DOG   decode_octet_group(buf+bi, ehdr->tbl_index, og_tbl_idx)

extern uni_message_handler message_handlers[];


/******************************************************************************
 *  element_length_check
 *    The length must either fall within a bound or meet an exact value.
 *****************************************************************************/
int check_element_length(int mesg_tbl_idx, int elem_list_idx, int elem_tbl_idx,
int len)
{
    element_info *e= &(messages_tbl[mesg_tbl_idx].elist[elem_list_idx]);
    /*--------------------------------------------------------------------
    * if min_len in the message tbl is zero that means the max_len is the
    * exact len that must be specified
    *-------------------------------------------------------------------*/
    if (e->min_len == 0) {
        if ((len + UNI_ELEM_HDR_LEN) != e->max_len) {
            printf("\nuni: chk_elem_len - %s message, ",
                                  messages_tbl[mesg_tbl_idx].name);
            printf("%s elem - expected len %d, saw len of %d\n",
             the_elements_tbl[elem_tbl_idx].name, e->max_len, len); 
            return 0;
        } else 
            return 1;
    }
    if (((len + UNI_ELEM_HDR_LEN) < e->min_len ) || 
            ((len + UNI_ELEM_HDR_LEN) > e->max_len)) {
            printf("\nuni: chk_elem_len - %d\n", mesg_tbl_idx);
        printf("\nuni: chk_elem_len - %s message, ",
                                  messages_tbl[mesg_tbl_idx].name);
        printf("%s - exp len b/w %d-%d, saw len %d\n", 
             the_elements_tbl[elem_tbl_idx].name, e->min_len, e->max_len, len);
        return 0;
    } else 
        return 1;
}
  
/******************************************************************************
*  grab_field_value_in_list
******************************************************************************/
u_int grab_field_value_in_list(list l, int pos)
{
    list m;
    field f;
    int j;
    
    mydolist(m, l, pos);
    f = (field) l->first;
    return  (f->val.ival);
}

/******************************************************************************
*  decode_mesg_hdr
******************************************************************************/
uni_message_header decode_mesg_hdr(char *buf)
{
    int i, j = 0, bi;
    uni_message_header mesg_hdr = 
     (uni_message_header) allocate_memory(sizeof(struct uni_message_header));

    /*
     * check for correct protocol discriminator 
     */
    if ((mesg_hdr->proto_disc = (u_int) buf[0]) != uni) {
        printf("uni: incorrect protocol discriminator\n");
        deallocate_memory(mesg_hdr); return 0;
    } 
    /*
     * pad should be 0, reference value length should be <= 3
     */
    if (((u_int) buf[1] & 0xf0) != 0) {
        printf("uni: bad ref val len of %d\n", mesg_hdr->crv_length);
        deallocate_memory(mesg_hdr); return 0;
    }
    if((mesg_hdr->crv_length = ((u_int) buf[1]) & 0x0f) > 3) {
        printf("uni: bad ref val len of %d\n", mesg_hdr->crv_length);
        deallocate_memory(mesg_hdr); return 0;
    } 
    mesg_hdr->crv_flag = (((u_char) buf[2]) >> 7);
    mesg_hdr->crv = ((u_int) buf[2]) & 0x7f;
    for (i = 1, bi = 3; i < mesg_hdr->crv_length; i++, bi++) {
        mesg_hdr->crv = (mesg_hdr->crv << 8) | 
          ((u_int) buf[bi]);
    }
    bi = 5;
    /*
     * check for a valid message type, this field starts in byte 5 of hdr
     * second byte of message ignored (always 0x80)
     */
    mesg_hdr->type = (int) (u_char) buf[bi]; 
    GET_TBL_IDX(i, mesg_hdr->type, messages_tbl, MAX_MESSAGES);
    if (i >= MAX_MESSAGES) {
        printf("uni: decode_mesg_hdr - unrecognized mesg type: 0x%02x\n",
                       mesg_hdr->type);
        deallocate_memory(mesg_hdr); 
        return 0;
    }
    mesg_hdr->idx = i;
    bi += 2;  /* skip next byte */
    /*
     * decode message length
     */
    mesg_hdr->length = (u_int) buf[bi++];
    mesg_hdr->length = (mesg_hdr->length<<8) | ((u_char)buf[bi]);
    mesg_hdr->retry_timer = 0;
    return mesg_hdr;
}

/******************************************************************************
 * aal_params_decoder
 *     Decode aal_params. Note: I don't handle user defined params the moment. 
 *****************************************************************************/
element aal_params_decoder(char *buf, element_header ehdr)
{
    int i;
    int len = ehdr->length;
    list l;
    int bi = 4;
    int og_tbl_idx = 0;
    u_int temp;
    octet_group og;
    octet_group_desc *ogptr;        /* ptr to og in element table */
    field f;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;

    debug("parse", "uni: aal_params_decoder");
    /*
     * Check AAL type. I'm only dealing with AAL 5 for the moment.
     */ 
    if (buf[bi] != aal_type_5) {
        printf("\nuni: aal_params_decoder - bad aal_type %02x\n", buf[bi]);
        return 0;
    } else {
        if ((og = DOG) != 0) { 
            elem->octet_groups = insert(og, elem->octet_groups);
            og_tbl_idx++; bi++; 
            len -= 1;
        } else
            return 0;
    }
    /*
     * Go through the rest of the octet groups.
     */
    while (len) {            
        /* params must be in order */
        for (i = og_tbl_idx; i < NUM_OGS_AAL_PARAMS; i++) {
            ogptr = &(the_elements_tbl[ehdr->tbl_index].ogs[i]);
             if ((u_char) buf[bi] == (u_char) ogptr->internal_fields[0][2])
                break;
        }
        if (i == NUM_OGS_AAL_PARAMS) {
            printf("\nuni: aal_params_decoder - bad identifier %02x\n",
                   buf[bi]);
            return 0;
        } else 
            og_tbl_idx = i;
        
        if ((og =  DOG) != 0) {
            elem->octet_groups = insert(og, elem->octet_groups);
        } else {
            return 0;
        }    
        len -= og->len;
        bi += og->len;
    }
    return elem;
}


/******************************************************************************
 *  cell_rate_param_ident
 *****************************************************************************/
int cell_rate_param_ident(u_int param, u_int *fwd_traff_param_combo,
u_int *bkw_traff_param_combo, int tagging)
{
    /*debug("parse","uni: cell_rate_param_check - param: %d\n", param));/**/
    if (tagging) {
        if(param && 0x02)
            *fwd_traff_param_combo |= TAGGING;
        if(param && 0x01)
            *bkw_traff_param_combo |= TAGGING;
    } else {
        switch(param) {
            case fwd_peak_cell_rate_0_ident:
                *fwd_traff_param_combo |= PCR_0; break;
            case bkw_peak_cell_rate_0_ident:
                *bkw_traff_param_combo |= PCR_0; break;
            case fwd_peak_cell_rate_1_ident:
                *fwd_traff_param_combo |= PCR_0_1; break;
            case bkw_peak_cell_rate_1_ident:
                *bkw_traff_param_combo |= PCR_0_1; break;
            case fwd_sust_cell_rate_0_ident:
                *fwd_traff_param_combo |= SCR_0; break;
            case bkw_sust_cell_rate_0_ident:
                *bkw_traff_param_combo |= SCR_0; break;
            case fwd_sust_cell_rate_1_ident:
                *fwd_traff_param_combo |= SCR_0_1; break;
            case bkw_sust_cell_rate_1_ident:
                *bkw_traff_param_combo |= SCR_0_1; break;
            case fwd_max_burst_size_0_ident:
                *fwd_traff_param_combo |= MBS_0; break;
            case bkw_max_burst_size_0_ident:
                *bkw_traff_param_combo |= MBS_0; break;
            case fwd_max_burst_size_1_ident:
                *fwd_traff_param_combo |= MBS_0_1; break;
            case bkw_max_burst_size_1_ident:
                *bkw_traff_param_combo |= MBS_0_1; break;
            case best_effort_ident:
                *fwd_traff_param_combo |= BEST_EFFORT;
                *bkw_traff_param_combo |= BEST_EFFORT; break;
            case traffic_mngt_options_ident:
                break;
            default:
                printf("uni: cell_rate_param_check - BAD!\n");
                break;
        }
    }
}

/******************************************************************************
 *  cell_rate_param_check
 *     not doing anything with these params for the moment so
 *     not acting on errors yet and thus always returning success (1)
 *****************************************************************************/
int cell_rate_param_check(int fwd, int bkw) 
{
    return 1; 
}


/******************************************************************************
 * traffic_desc_decoder
 *     Decode traffic_desc element. 
 *****************************************************************************/
element traffic_desc_decoder(char *buf, element_header ehdr)
{

    register int i;
    register int bi = 4;
    register int og_tbl_idx = 0;
    register int len = ehdr->length;
    u_int fwd_traff_param_combo = 0;
    u_int bkw_traff_param_combo = 0;
    u_int temp;
    octet_group og;
    octet_group_desc *ogptr;        /* ptr to og in element table */
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;
 
    debug("parse", "uni: traffic_desc_decoder\n");
    /*--------------------------------------------------------------------
    * Go through the rest of the octet groups.
    *-------------------------------------------------------------------*/
    while ((len) && ((buf[bi] & 0xf0) != 0))    {            
        /* Compare buf with traffic_desc ogs and break when found */
        for (i = og_tbl_idx; i < NUM_OGS_TRAFFIC_DESC; i++) {
            ogptr = &(the_elements_tbl[ehdr->tbl_index].ogs[i]);
             if ((u_char)buf[bi]==(u_char) ogptr->internal_fields[0][2])
                break;
        }
        if (i == NUM_OGS_TRAFFIC_DESC) {
            printf("\nuni: traffic_desc_decoder - bad identifier %d\n",
                   buf[bi]);
            return 0;
        } else {   
            /* 
             * record type of identifier, to check combination later  */
            cell_rate_param_ident(((u_char)buf[bi]), 
                &fwd_traff_param_combo,&bkw_traff_param_combo,DONT_TAGG_CHK);
            og_tbl_idx = i;
        }
        
        if ((og = DOG) != 0) 
            elem->octet_groups = insert(og, elem->octet_groups);
        else
            return 0;
        len -= og->len;
        bi += og->len;
    }
    /*--------------------------------------------------------------------
    * final "tagging" octet included, note: assume rsvd bits are 0's
    *-------------------------------------------------------------------*/
    if (len) {
        og_tbl_idx = NUM_OGS_TRAFFIC_DESC - 1;
        if ((og = DOG) != 0) {
            /* record tagging ident, to check overall combination later */
            cell_rate_param_ident((u_char)buf[bi], 
                &fwd_traff_param_combo, &bkw_traff_param_combo,DO_TAGG_CHK);
            elem->octet_groups = insert(og, elem->octet_groups);
        } else
            return 0;
    }
    if (cell_rate_param_check(bkw_traff_param_combo, bkw_traff_param_combo))
        return elem;
    else {
        printf("\nuni: traffic_desc_dcdr -invalid cell rate params combo\n");
        return 0;
    }
}

    
/******************************************************************************
 *  element bb_bearer_capab_decoder(buf, ehdr)
 *****************************************************************************/
element bb_bearer_capab_decoder(char *buf, element_header ehdr)
{
  
    int len;
    int bi = 4;
    int og_tbl_idx = 0;
    octet_group og;
    element elem = (element) allocate_memory (sizeof (struct element));
    u_int temp;
    
    debug("parse", "uni: bb_bearer_capab_decoder\n");
    elem->octet_groups = 0;
    /*--------------------------------------------------------------------
    * get first octet group and check appropriate fields 
    *-------------------------------------------------------------------*/
    if ((og = DOG) == 0) 
        return 0;
    og_tbl_idx++;
    temp = grab_field_value_in_list(og->fields, 1);
    if (temp == bcob_x) {
        if (ehdr->length != 3) {
             printf("uni: Error - bb_bearer_capab_decoder");
             printf(", BCOC-X class, bad elem length of %d\n",ehdr->length);
             return 0;
        }
        /* 
         * don't insert this og, get again but get full* og, i.e. with
         * traffic type and timing req's
         */
        if ((og = DOG) == 0) 
            return 0;
        og_tbl_idx++;
    }
    else if ((temp == bcob_a) && (temp == bcob_c)) {
        if (ehdr->length != 2) {
             printf("uni: Error - bb_bearer_capab_decoder");
             printf(" ,bad elem length of %d\n", ehdr->length); 
             return 0;
        }
    } else {
        printf("uni: Error - bb_bearer_capab_decode -illegal bearer class\n");
        return 0;
    }
    elem->octet_groups = insert(og, elem->octet_groups);
    bi += og->len;
    /*--------------------------------------------------------------------
    * get last octet group and check appropriate fields 
    *-------------------------------------------------------------------*/
    if ((og = DOG) == 0) 
        return 0;
    temp = grab_field_value_in_list(og->fields, 1);
    if ((temp != not_susceptible) & (temp != susceptible)) {
        printf("uni: Err: bb_bear_capab_decod-illegal suscpt. to clipping\n");
        return 0;
    }
    temp = grab_field_value_in_list(og->fields, 3);
    if (temp != point_to_point) {
        printf("uni: Err: bb_bear_capab_decod-illegal user_pln_call_cofig\n");
        return 0;
    }
    elem->octet_groups = insert(og, elem->octet_groups);
    return elem;
}

/******************************************************************************
 *  element bb_high_layer_info_decoder(buf, ehdr)
 *****************************************************************************/
element bb_high_layer_info_decoder(char *buf, element_header ehdr)
{
    int len;
    u_int temp;
    octet_group og;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;
      
    debug("parse", "uni: bb_high_layer_info_decoder\n");
    if (decode_generic_element(buf, ehdr, elem)) {
        return elem;
    } else {
        return 0;
    }
}


/******************************************************************************
 *  element bb_low_layer_info_decoder(buf, ehdr)
 *****************************************************************************/
element bb_low_layer_info_decoder(char *buf, element_header ehdr)
{
    register bi = 4;
    u_int temp, snap;
    int *t;
    int og_tbl_idx = 0;
    octet_group og;
    notes note;
    element elem = (element) allocate_memory (sizeof (struct element));
    register int len = ehdr->length;   /* length of element */
    elem->octet_groups = 0;
  
    debug("parse", "uni: bb_low_layer_info_decoder\n");

    if ((((u_int) buf[bi] & 0x6f) >> 5) == 1) {            /* layer 1 id */
        if ((og = DOG) != 0) 
            bi += og->len;
        else
            return 0;
        elem->octet_groups = insert(og, elem->octet_groups);
        if (!(len -= og->len)) return elem;
    }
    if ((((u_int) buf[bi] & 0x60) >> 5) == 2) {           /* layer 2 id */
        temp =  ((u_int) buf[bi] & 0x1f);
        if ((temp == hdlc_arm_1)||(temp == hdlc_arm_2)||(temp == hdlc_arm_3)) 
            og_tbl_idx = 3;
        else if (temp == user_specified)
            og_tbl_idx = 2;
        else {
            /*printf("logical lan\n");*/
            og_tbl_idx = 1;
        }
        if ((og = DOG) != 0) 
            bi += og->len;
        else
            return 0;
        elem->octet_groups = insert(og, elem->octet_groups);
        if (!(len -= og->len)) return elem;
    }
    if ((((u_int) buf[bi] & 0x6f) >> 5) == 3) {           /* layer 3 id */
        temp =  ((u_int) buf[bi] & 0x1f);
        if ((temp == itu_x25)||(temp == iso_iec_8208)||(temp == x223_iso_8878))
        {
            printf("layer 3 - x25 stuff\n");
            og_tbl_idx = 7;
            if ((og = DOG) != 0) 
                bi += og->len;
            else
                return 0;
        } else if (temp == user_spec) {
            printf("layer 3 - user_spec\n");
            og_tbl_idx = 6;
            if ((og = DOG) != 0) 
                bi += og->len;
            else
                return 0;
        } else if (temp == iso_iec_tr_9577) {
            /* printf("layer 3 - iso_iec_tr_9577\n");*/
            og_tbl_idx = 5;
            if ((og = DOG) != 0) 
                bi += og->len;
            else
                return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            t = (int *)field_lookup_in_elem(elem, init_proto_ident, flat,0);
            if (*t == snap_ipi)
            {
                /*printf("layer 3 - SNAP\n");*/
                if ((len -= og->len) < 6) 
                    return 0;
                og_tbl_idx = 6;
                og = DOG;
                bi += og->len;
                elem->octet_groups = insert(og, elem->octet_groups); /* snap */
                og_tbl_idx = 7;
                og = DOG;
                bi += og->len;
                elem->octet_groups = insert(og, elem->octet_groups); /* oui */
                og_tbl_idx = 8;
                og = DOG;
                bi += og->len;
                elem->octet_groups = insert(og, elem->octet_groups); /* pid*/
	        }
        } else {
            printf("layer 3 - default\n");
            og_tbl_idx = 4;
            if ((og = DOG) != 0) 
                bi += og->len;
            else
                return 0;
        }
    }
    return elem;
}

/******************************************************************************
 *  element call_state_decoder(buf, ehdr)
 *****************************************************************************/
element call_state_decoder(char *buf,element_header ehdr)
{
    int len;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;

    debug("parse", "uni: call_state_decoder\n");
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element called_pty_number_decoder(buf, ehdr)
 *****************************************************************************/
element called_pty_number_decoder(char *buf, element_header ehdr)
{
    int len;
    u_int temp;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;
    
    debug("parse", "uni: called_pty_number_decoder\n");
    temp = ((u_int) buf[bi] & 0x7f) >> 4; 
    /*
    if ((temp != 0) && (temp != 1))
    {
        printf("\nuni: called_pty_#_decoder - bad type_of_number %d\n",
               buf[bi]);
        return 0;
    }
    temp = (u_int) buf[bi] & 0x0f; 
    if ((temp != 1) && (temp != 2))
    {
        printf("\nuni: called_pty_#_decoder - bad addr_plan_ident %d\n",
               buf[bi]);
        return 0;
    }
    */
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element called_pty_subaddr_decoder(buf, ehdr)
 *****************************************************************************/
element called_pty_subaddr_decoder(char *buf, element_header ehdr)
{
    int len;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;
    
    debug("parse", "uni: called_pty_subaddr_decoder\n");
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element calling_pty_number_decoder(buf, ehdr)
 *****************************************************************************/
element calling_pty_number_decoder(char *buf, element_header ehdr)
{
    int len;
    int temp;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;
    
    debug("parse", "uni: calling_pty_number_decoder\n");
    temp = ((u_int) buf[bi] & 0x7f) >> 4; 
    if ((temp != 0) && (temp != 1))
    {
        printf("\nuni: calling_pty_#_decoder - bad type_of_number %d\n",
                               buf[bi]);
        return 0;
    }
    temp = (u_int) buf[bi] & 0x0f; 
    if ((temp != 1) && (temp != 2))
    {
            printf("\nuni: calling_pty_#_decoder - bad addr_plan_ident %d\n",
               buf[bi]);
            return 0;
    }
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element calling_pty_subaddr_decoder(buf, ehdr)
 *****************************************************************************/
element calling_pty_subaddr_decoder(char *buf, element_header ehdr)
{
    int len;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;
    
    debug("parse", "uni: calling_pty_subaddr_decoder");
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element cause_decoder(buf, ehdr)
 *****************************************************************************/
element cause_decoder(char *buf, element_header ehdr)
{
    register i, bi = 4;
    int og_tbl_idx = 0;
    octet_group og;
    int dummy_mask;
    element_header tehdr;
    element telem;
    element elem = (element) allocate_memory (sizeof (struct element));
    register int len = ehdr->length;   
    elem->octet_groups = 0;

    /* get first octet group (spare and location) */
    if ((og = DOG) == 0)    
        return 0;
    bi += og->len;
    elem->octet_groups = insert(og, elem->octet_groups);
    if (!(len -= og->len)) return elem;

    /* get cause value octet group */
    if ((og = DOG) == 0)
        return 0;
    bi += og->len;
    elem->octet_groups = insert(og, elem->octet_groups);
    if (!(len -= og->len)) return elem;

    /* switch on cause value to parse diagnostics */
    switch (((int) buf[bi-og->len] & 127))  {
        case unassigned_number:                /* unallocated number */
        case no_route_to_dest:    
        case qos_unavailable:
            og_tbl_idx = 2;
            break;
        case call_denied:                      /* call_rejected */
            og_tbl_idx = 3;
            if ((og = DOG) == 0) 
               return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            if (!(len -= og->len)) return elem;
            switch ((buf[bi++] & 0x7c) >> 2) {
                case info_elem_missing:
                case info_elem_content_insufficient:
                    bi++;
                    og_tbl_idx = 3;
                    if ((og = DOG) == 0) 
                        return 0;
                    elem->octet_groups = insert(og, elem->octet_groups);
                    if (!(len -= og->len)) 
                        return elem;
                    else 
                       return 0;       /* element too long */
                case user_specific:   /* treating this like a bunch of bytes */
                    og_tbl_idx = 10;
                    for (; len; len -= og->len, bi += og->len) {
                        if ((og = DOG) == 0) 
                           return 0;
                      elem->octet_groups = insert(og, elem->octet_groups);
                    }
                    return elem;   
            }
            break;
        case number_changed:
	    /* TLB: fixed */
            if (!(tehdr = decode_element_header(buf, 0,&dummy_mask))) {
               print_errors("uni: Err: cause elem diagnostcs:", UNRECOG_ELEM);
               return 0;
            }
            if ((ehdr->code!=called_party_number) |
               (ehdr->code!=transit_net_select)) {
               printf("uni: cause_decoder - invalid IE in diagnostics\n");
               return 0;
            }
            if (tehdr->length <= len) {
               if (telem = (coders[ehdr->tbl_index].decoder_func)(buf, tehdr))
                   telem->hdr = tehdr;
               /* how to store this element now? */
            } else {
               printf("uni: cause_decoder - cause IE too short\n");
               return 0;
            }
            break;
        case access_info_discarded:
        case incompatible_dest:
        case info_elem_nonexist_nonimplemented:
        case invalid_info_elem_contents:
           og_tbl_idx = 4;
           for (; len; len -= og->len, bi += og->len) {   /* IE id(s) */
               if ((og = DOG) == 0)
                       return 0;
               elem->octet_groups = insert(og, elem->octet_groups);
           }
           return elem;
        case traffic_desc_unavailable:
            og_tbl_idx = 5;
        case identified_channel_non_existence:
            og_tbl_idx = 6;             /* vpci */
            if ((og = DOG) == 0)
                return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            og_tbl_idx++;
            if ((og = DOG) == 0)    /* vci */
                return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            return elem;
        case mandatory_info_elem_missing:
            og_tbl_idx = 0;
            break;
        case message_type_nonexist_nonimplemented:
        case mesg_not_compatible_with_call_state:
            og_tbl_idx = 8;
            if ((og = DOG) == 0)             /* message type */
                    return 0;
            elem->octet_groups = insert(og, elem->octet_groups);
            return elem;
        case recovery_or_timer_expire:  /* 3 ia5_char iding timer*/
            og_tbl_idx = 9;
            for (i = 0; len & (i < 3); len -= og->len, bi += og->len, i++) {
               if ((og = DOG) == 0) 
                       return 0;
               elem->octet_groups = insert(og, elem->octet_groups);
            }
            if (i <= 2) return 0;
            return elem;
        default:
            break;
    }
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element connection_ident_decoder(buf, ehdr)
 *****************************************************************************/
element connection_ident_decoder(char *buf, element_header ehdr)
{
    int len;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;

    debug("parse", "uni: connection_ident_decoder\n");
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element qos_params_decoder(buf, ehdr)
 *****************************************************************************/
element qos_params_decoder(char *buf, element_header ehdr)
{
    int len;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));

    elem->octet_groups = 0;
    debug("parse", "uni: qos_params_decoder\n");
    if (((int) buf[bi] < 0) && ((int) buf[bi] > 4))
    {    
        printf("\nuni: qos_params_decoder - bad qos_class_fwd %d\n", 
                   buf[bi]);
        return 0;
    }
    if (((int) buf[bi+1] < 0) && ((int) buf[bi+1] > 4))
    {    
        printf("\nuni: qos_params_decoder - bad qos_class_bwk %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf, ehdr, elem))  
      return elem;
    else
      return 0;
}

/******************************************************************************
 *  element bb_repeat_indic_decoder(buf, ehdr)
 *****************************************************************************/
element bb_repeat_indic_decoder(char *buf, element_header ehdr)
{
    int len;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;

    debug("parse", "uni: bb_repeat_indic_decoder\n");
    if ((((u_char) buf[bi]) & 0x0f) != REPEAT_INDICATOR) {    
        printf("\nuni: bb_repeat_indic_decoder - bad repeat_indic %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf, ehdr, elem))
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element restart_indic_decoder(buf, ehdr)
 *****************************************************************************/
element restart_indic_decoder(char *buf, element_header ehdr)
{
    int len;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;

    debug("parse", "uni: restart_indic_decoder\n");
    if (((((u_char) buf[bi]) & 0x07) != indic_vc)
       && ((((u_char) buf[bi]) & 0x07) != all_vc))
    {    
        printf("\nuni: restart_indic_decoder - bad restart_indic %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element bb_send_complete_decoder(buf, ehdr)
 *****************************************************************************/
element bb_send_complete_decoder(char *buf, element_header ehdr)
{
    int len;
    int bi = 4;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;
    
    debug("parse", "uni: bb_send_complete_decoder\n");
    if ((((u_char) buf[bi]) & 0x7f) != SENDING_COMPLETE_INDIC)
    {    
        printf("\nuni: bb_send_complere_decoder - bad indic %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element transit_net_select_decoder(buf, ehdr)
 *****************************************************************************/
element transit_net_select_decoder(char *buf, element_header ehdr)
{
    int len;
    int bi = 4;
    u_char first_octet = 0xa1; /* TYPE_OF_NET_IDENT, NET_IDENT_PLAN */
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;

    debug("parse", "uni: transit_net_select_decoder\n");
    if (((u_char) buf[bi]) != first_octet)
    {    
        printf("\nuni: transit_net_select_decoder - bad first octet %d\n", 
                   buf[bi]);
        return 0;
    }
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element endpoint_reference_decoder(buf, ehdr)
 *****************************************************************************/
element endpoint_reference_decoder(char *buf, element_header ehdr)
{
    int len;
    int bi = 4;
    u_char first_octet = 0xa1;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;

    debug("parse", "uni: endpoint_reference_decoder\n");

    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}

/******************************************************************************
 *  element endpoint_state_decoder(buf, ehdr)
 *****************************************************************************/
element endpoint_state_decoder(char *buf, element_header ehdr)
{
    int len;
    int bi = 4;
    u_char first_octet = 0xa1;
    element elem = (element) allocate_memory (sizeof (struct element));
    elem->octet_groups = 0;
    
    debug("parse", "uni: endpoint_state_decoder\n");
    if (decode_generic_element(buf, ehdr, elem))  
        return elem;
    else
        return 0;
}


/******************************************************************************
 * decode_octet_group
 *****************************************************************************/
octet_group decode_octet_group(char *buf, int ei, int oi)
/*
 * ei = element table index
 * oi = octet group table index
 *  
 */
{       
    register int i = 1, j = 0, tlen, shift;
    int char_len, done = 0;
    int bi = 0, ifi = 0;            /* ifi = internal fields table index */
    int bptr;                       /* bit position ptr in octet */
    octet_group_desc *ogptr;        /* ptr to og in element table */
    octet_group og;
    field f;

    if (!(og =(octet_group) allocate_memory (sizeof(struct octet_group))))
    {
        printf("uni: decode_octet_group -couldn't alloc mem for octet_group");
        return 0;
    }
    og->len = 0;
    og->fields = 0;
    ogptr = the_elements_tbl[ei].ogs;
    bptr = char_len = (ogptr[oi].encoded == ext) ? CHAR_7 : CHAR;

    while ((og->len < (char_len * ogptr[oi].fixed_length)) & !done) { 
        f = (field) allocate_memory (sizeof(struct field));
        f->code = ogptr[oi].internal_fields[ifi][0];
        f->len = ogptr[oi].internal_fields[ifi][1];
        /*
         * decode an "address" field
         */
        if (ogptr[oi].encoded == addr_coded) {

            f->val.cval = (u_char *) allocate_memory ((f->len / 8) + 1);
            bcopy(buf+bi, f->val.cval, UNI_ADDR_LEN);
        }
        /*
         * decode an encoded field
         */
        else if (ogptr[oi].encoded == ext) {
            f->val.ival = 0;		/* NHH */
            tlen = f->len;
            while (tlen) {
                if (tlen > char_len) 
                    f->val.ival <<= char_len;
                else
                    f->val.ival <<= tlen;
                shift = ((bptr - tlen) < 0) ? 0 : bptr - tlen;
                f->val.ival |= (((u_char) buf[bi]) & BMASK(bptr)) >> shift;
                tlen -= bptr - shift;
                bptr -= bptr - shift;
                if (bptr == 0) {
                    if (((u_char) buf[bi]) >> CHAR_7) 
                        done = 1;
                    bi++;
                    bptr = char_len;
                }
            }
        /*
         * else, decode what has to be a "flat" field
         */
        } else {
            for (i=0, f->val.ival = 0; i < (f->len / CHAR);  i++, bi++) {
                f->val.ival <<= CHAR;
                f->val.ival |= (u_char) buf[bi];
            }   
            bptr = CHAR;
        }     
        /* insert field into octet group's field list */
        og->fields = insert(f, og->fields);
        og->len += f->len;
        ifi++;
    }
    og->len = og->len/char_len;
    return og;        
}


/******************************************************************************
 *  decode_element_header
 *
 *  Decode an element header which is made up of octet groups (og's). The og's
 *  that make up an "element header" are: 1)information_element_identifier,
 *  2) instruction_field, and 3) element length. Then follow the "contents"
 *  octet groups. For the moment, to keep things simple, only the contents 
 *  are considered in octet groups. 
 *****************************************************************************/
element_header decode_element_header(char *buf, int mti, int *maskv)
{
    register int i;
    int elist_idx;           /* element list index */
    int found = 0;
    element_header eh;

    /* 
     * Construct element header */
    eh = (element_header) allocate_memory (sizeof(struct element_header));
    eh->code = (int) buf[0];
    eh->instruction_field = (u_char) buf[1] & 0x7f;
    eh->length = ((u_int) ((u_char) buf[2])) << 8 | ((u_char) buf[3]);  
    GET_TBL_IDX(eh->tbl_index,eh->code, the_elements_tbl, MAX_ELEMENTS);
    /* check if element is valid within the message it is, if not, return 0 */
    for (elist_idx=0; messages_tbl[mti].elist[elist_idx].code; elist_idx++) {
        if (eh->code == messages_tbl[mti].elist[elist_idx].code) {
            found = 1; break;
        }
    }
    if (!found) {
        printf("\nuni: decode_element_hdr - elem %02x not found in mesg\n", 
                eh->code);
        deallocate_memory(eh);
        return 0;
    } else {
        *maskv = 1 << elist_idx;
    }
    /*
     * Check if element length is within the bounds allowed in the 
     * message it is in. */
    if (!(check_element_length(mti, elist_idx, eh->tbl_index, eh->length))) {
        deallocate_memory(eh);
        return 0;
    }
    return eh;
}

/******************************************************************************
 *  int decode_generic_element 
 *****************************************************************************/
int decode_generic_element(char *buf, element_header ehdr, element elem)
{
    int len = ehdr->length;
    int bi = UNI_ELEM_HDR_LEN;
    int og_tbl_idx = 0;
    octet_group og;
    
    while (len) 
    {            
        if ((og = DOG) != 0) 
        {
            elem->octet_groups = insert(og, elem->octet_groups);
            og_tbl_idx++;
        }
        else
            return 0;
        len -= og->len;
        bi += og->len;
    }
    return 1;
}

/******************************************************************************
 *  uni_message decode_mesg(buf, mesg_hdr)
 *****************************************************************************/
uni_message decode_mesg(char *buf, uni_message_header mhdr)
{
    register int i;
    register u_int ie_mask;
    int mask_val, mlen = 0;
    element elem = 0;
    element_header ehdr = 0;
    uni_message mesg =
                    (uni_message)allocate_memory(sizeof(struct uni_message));

    mesg->hdr = mhdr;
    mesg->elements = 0;    
    while (mlen < mhdr->length) 
    {
        /* check if the mesg buf space contains at least an element header,
         * if so, decode element header.
         */
        if ((mlen += UNI_ELEM_HDR_LEN) <= mhdr->length) {
            if (!(ehdr = decode_element_header(buf, mhdr->idx, &mask_val))) {
                print_errors("decode_mesg", UNRECOG_ELEM);
                return 0; 
            }
            /* check if mesg buf is long enough to contain the entire element*/
            if ((mlen += (ehdr->length)) <= mhdr->length) {
                if (ehdr->length <= 0) {/* check for blank IE */ 
                    deallocate_memory(ehdr);
                    buf += UNI_ELEM_HDR_LEN;
		        }
                /* call appropriate element decoder */
                else if (elem=(coders[ehdr->tbl_index].decoder_func)(buf,ehdr))
                {
                    elem->hdr = ehdr;
                    mesg->elements = insert(elem, mesg->elements);
                    ie_mask |= mask_val;
                    buf += (UNI_ELEM_HDR_LEN + ehdr->length);
                } 
                else 
                {
                     /*handle_elem_coding_erro(qp, mhdr, ehdr->code); */
                     printf("uni: error in decoding message\n");
                     return 0;
                }
            } 
            else 
            {
                print_errors("decode_mesg", BUFFER_DEPLETED);
            }
        }
        else 
        {       
             print_errors("decode_mesg", BUFFER_DEPLETED);
             printf("      in element, code: %02x\n", (u_char)buf[0]);
             return 0;
        }

    }
    /* check that all mandatory elements are found in message */
    /*printf("ie_mask: %02x\n", ie_mask); */
    for (i = 0; i<messages_tbl[mhdr->idx].num_of_elems; i++,ie_mask >>=1)  {

        if ((messages_tbl[mhdr->idx].elist[i].direction  == BOTH) &&
            (messages_tbl[mhdr->idx].elist[i].type==MAND)&&(!(ie_mask & 0x01)))
	{
            fprintf(stderr, "uni: decode_mesg - missing MAND IEs %s\n", 
            messages_tbl[mhdr->idx].  name);
            deallocate_uni_mesg(mesg);
            return 0;
	}
    }
    return mesg;
}







