/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/* (c) Copyright Systems Integration Specialists Company, Inc., */
/* 1999-2002, All Rights Reserved. */
/* */
/* PROPRIETARY AND CONFIDENTIAL */
/* */
/* MODULE NAME : goose.c */
/* PRODUCT(S) : ASN1DE */
/* */
/* MODULE DESCRIPTION : */
/* */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */
/* */
/* MODIFICATION LOG : */
/* Date Who Rev Comments */
/* -------- --- ------ ------------------------------------------- */
/* 12/12/03 JRB 09 u_a_unit_data_ind: memset GOOSE_INFO struct */
/* to all 0 at start of decode. */
/* 03/31/03 JRB 08 asn1r_get_bitstr: add max_bits arg. */
/* 12/11/02 JRB 07 Del mvl_acse.h include. */
/* 03/05/02 JRB 06 Del unused proto. Add braces when initializing*/
/* ASN1R_TAG_CTRL_1 structs. */
/* 01/10/02 JRB 05 No longer need bind_id. */
/* Chg args to _goose_decode_mms. */
/* Add gse_uca_decode */
/* 07/25/00 RKR 04 Reconciled changes from BSD */
/* 12/10/99 JRB 03 Fix mmsl_send_goose & _goose_tx_audt_apdu */
/* to properly check return codes. */
/* 09/10/99 JRB 02 Use asn1r_get_u32 for decoding of SqNum, */
/* StNum, HoldTim, & BackTim (encode is OK). */
/* 06/25/99 MDE 01 Created */
/************************************************************************/
#include "glbtypes.h"
#include "sysincs.h"
#include "asn1r.h"
#include "clnp_usr.h"
#include "clnp_sne.h"
#include "clnp.h" /* for clnpl_decode */
#include "goose.h"
/* For loopback testing use this define */
/* #define GOOSE_TEST */
/************************************************************************/
/************************************************************************/
/* Static functions */
static ST_RET _goose_tx_audt_apdu (GOOSE_INFO *gi, ST_UCHAR *goose_mms_pdu,
ST_INT goose_mms_pdu_len);
static ST_RET _goose_encode_mms (ST_UCHAR *asn1_buf, ST_INT asn1_buf_size,
ST_UCHAR **goose_asn1_out, ST_INT *goose_asn1_len_out,
GOOSE_INFO *gi);
static ST_RET _goose_decode_mms (AUDT_APDU *audt_apdu, GOOSE_INFO *gi);
static ST_VOID _goose_unconf_req (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_info_rpt (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_list_of_var (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_seq_of_varspec (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_named (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_vmd_spec (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_named_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_seq_of_varspec_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_list_of_var_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_list_acc_rslt (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_struct (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_app_id (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_sent_time (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_seqnum (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_sub_seq (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_hold_time (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_back_time (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_phsid (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_dnabits (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_get_usrbits (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_struct_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_list_acc_rslt_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_info_rpt_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_unconf_req_done (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_done_ok (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_done_error (ASN1_DEC_CTXT *ac);
static ST_VOID _goose_error (ASN1_DEC_CTXT *ac, ST_RET err);
/************************************************************************/
/************************************************************************/
/* mmsl_send_goose */
/************************************************************************/
ST_RET mmsl_send_goose (GOOSE_INFO *gi)
{
ST_UCHAR asn1_buf[GOOSE_MAX_PDU_SIZE];
ST_UCHAR *goose_mms_pdu;
ST_INT goose_mms_pdu_len;
ST_RET rc;
#ifdef GOOSE_TEST
GOOSE_INFO goose_rx_info;
#endif
/* Encode the MMS PDU, given the user GOOSE information */
rc = _goose_encode_mms (asn1_buf, GOOSE_MAX_PDU_SIZE,
&goose_mms_pdu, &goose_mms_pdu_len, gi);
/* Finish the a-unit data encoding and transmit the PDU */
if (rc == SD_SUCCESS)
rc = _goose_tx_audt_apdu (gi, goose_mms_pdu, goose_mms_pdu_len);
#ifdef GOOSE_TEST
/* For test, pretend we received it too! */
memset(&goose_rx_info, 0, sizeof (GOOSE_INFO));
memcpy(&goose_rx_info.audtApdu, &gi->audtApdu, sizeof (AUDT_APDU));
rc = _goose_decode_mms (&gi->audtApdu, &goose_rx_info);
if (rc != 0)
printf ("\n Whooops!");
rc = memcmp (gi, &goose_rx_info, sizeof (GOOSE_INFO));
if (rc != 0)
printf ("\n Whoa!");
else
u_mmsl_goose_received (&goose_rx_info);
#endif
return (rc);
}
/************************************************************************/
/* _goose_encode_mms */
/************************************************************************/
static ST_RET _goose_encode_mms (ST_UCHAR *asn1_buf, ST_INT asn1_buf_size,
ST_UCHAR **goose_mms_pdu_out, ST_INT *goose_mms_pdu_len_out,
GOOSE_INFO *gi)
{
ASN1_ENC_CTXT ac;
ST_UCHAR *pdu_start; /* 000725 BSD - Changed from ST_CHAR* to ST_UCHAR* */
ST_INT pdu_len;
asn1r_strt_asn1_bld (&ac, asn1_buf, asn1_buf_size);
asn1r_strt_constr (&ac); /* unconfirmed requests */
asn1r_strt_constr (&ac); /* information report body */
asn1r_strt_constr (&ac); /* access result */
asn1r_strt_constr (&ac); /* GOOSE data is a structure */
asn1r_wr_bitstr (&ac, gi->UserSt, gi->num_usr_bits);
asn1r_fin_prim (&ac, 4, CTX);
asn1r_wr_bitstr (&ac, gi->DNA, gi->num_dna_bits);
asn1r_fin_prim (&ac, 4, CTX);
asn1r_wr_u16 (&ac, gi->PhsID);
asn1r_fin_prim (&ac, 6, CTX);
asn1r_wr_u32 (&ac, gi->BackTim);
asn1r_fin_prim (&ac, 6, CTX);
asn1r_wr_u32 (&ac, gi->HoldTim);
asn1r_fin_prim (&ac, 6, CTX);
asn1r_wr_u32 (&ac, gi->StNum);
asn1r_fin_prim (&ac, 6, CTX);
asn1r_wr_u32 (&ac, gi->SqNum);
asn1r_fin_prim (&ac, 6, CTX);
asn1r_wr_btod (&ac, &gi->t);
asn1r_fin_prim (&ac, 12, CTX);
asn1r_wr_vstr (&ac, gi->SendingIED);
asn1r_fin_prim (&ac, 10, CTX);
asn1r_fin_constr (&ac, 2, CTX, DEF); /* end of GOOSE data struct */
asn1r_fin_constr (&ac, 0, CTX, DEF); /* access result being finished */
asn1r_strt_constr (&ac);
asn1r_strt_constr (&ac); /* variable access specification */
asn1r_strt_constr (&ac); /* object name start */
asn1r_wr_vstr (&ac, "GOOSE");
asn1r_fin_prim (&ac, 0, CTX); /* VMD Specific Name */
asn1r_fin_constr (&ac, 0, CTX, DEF);
asn1r_fin_constr (&ac, SEQ_CODE, UNI, DEF);/* end variable access specification */
asn1r_fin_constr (&ac, 0, CTX, DEF); /* end of information report body */
asn1r_fin_constr (&ac, 0, CTX, DEF);
asn1r_fin_constr (&ac, 3, CTX, DEF); /* end of unconfirmed service */
if (ac.asn1r_encode_overrun) /* Check for encode overrun */
return (SD_FAILURE);
pdu_start = ac.asn1r_field_ptr + 1;
pdu_len = (asn1_buf + asn1_buf_size) - pdu_start;
*goose_mms_pdu_out = pdu_start;
*goose_mms_pdu_len_out = pdu_len;
return (SD_SUCCESS);
}
/************************************************************************/
/* _goose_tx_audt_apdu */
/************************************************************************/
static ST_RET _goose_tx_audt_apdu (GOOSE_INFO *gi, ST_UCHAR *goose_mms_pdu,
ST_INT goose_mms_pdu_len)
{
ST_RET ret;
gi->audtApdu.user_info.ptr = goose_mms_pdu;
gi->audtApdu.user_info.len = goose_mms_pdu_len;
ret = a_unit_data_req (&gi->audtApdu);
return (ret);
}
/************************************************************************/
/************************************************************************/
/* u_a_unit_data_ind */
/* This "user" function processes the A-UNIT-DATA.ind. It is customized */
/* to handle only GOOSE packets. It decodes the GOOSE packet, and then */
/* passes the decoded info to the user by calling "u_mmsl_goose_received.*/
/* If the packet is not a GOOSE, or it can't be decoded, it is ignored. */
/************************************************************************/
ST_VOID u_a_unit_data_ind (AUDT_APDU *audt_apdu)
{
ST_RET rc;
GOOSE_INFO goose_info; /* for u_a_unit_data_ind */
/* Start with clean structure. */
memset (&goose_info, 0, sizeof (goose_info));
/* Decode GOOSE PDU. */
rc = _goose_decode_mms (audt_apdu, &goose_info);
/* Tell the user about the received GOOSE message */
if (rc == SD_SUCCESS)
u_mmsl_goose_received (&goose_info);
else
{
/* SLOGALWAYS0 ("A-UNIT-DATA.ind is not a GOOSE. Ignoring it."); */
}
}
/************************************************************************/
/* _goose_decode_mms */
/************************************************************************/
static ST_RET _goose_decode_mms (AUDT_APDU *audt_apdu, GOOSE_INFO *gi)
{
ASN1_DEC_CTXT asn1_ctxt;
ASN1_DEC_CTXT *ac;
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX | CONSTR, 3), _goose_unconf_req}}};
/* Copy AUDT struct to goose_info so user can access all addressing info.*/
memcpy (&gi->audtApdu, audt_apdu, sizeof (AUDT_APDU));
ac = &asn1_ctxt;
memset (ac, 0, sizeof (ASN1_DEC_CTXT));
ac->usr_info[0] = gi;
ac->asn1r_err_fun = _goose_error;
ac->asn1r_decode_done_fun = _goose_done_error;
ac->asn1r_decode_method = ASN1_TABLE_METHOD;
ac->asn1r_tag_table = &asn1r_tags;
asn1r_decode_asn1 (ac, audt_apdu->user_info.ptr, audt_apdu->user_info.len);
if (ac->asn1r_pdu_dec_err != NO_DECODE_ERR)
return (SD_FAILURE);
return (SD_SUCCESS);
}
/************************************************************************/
/* _goose_unconf_req */
/************************************************************************/
static ST_VOID _goose_unconf_req (ASN1_DEC_CTXT *ac)
{
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL(CTX | CONSTR, 0), _goose_info_rpt}}};
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_info_rpt */
/************************************************************************/
static ST_VOID _goose_info_rpt (ASN1_DEC_CTXT *ac)
{
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX | CONSTR, 0), _goose_list_of_var}}};
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_list_of_var */
/************************************************************************/
static ST_VOID _goose_list_of_var (ASN1_DEC_CTXT *ac)
{
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (UNI | CONSTR, SEQ_CODE), _goose_seq_of_varspec}}};
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_seq_of_varspec */
/************************************************************************/
static ST_VOID _goose_seq_of_varspec (ASN1_DEC_CTXT *ac)
{
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX | CONSTR, 0), _goose_named}}};
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_named */
/************************************************************************/
static ST_VOID _goose_named (ASN1_DEC_CTXT *ac)
{
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 0), _goose_vmd_spec}}};
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_vmd_spec */
/************************************************************************/
static ST_VOID _goose_vmd_spec (ASN1_DEC_CTXT *ac)
{
ST_CHAR *data_ptr;
data_ptr = (ST_CHAR *) ac->asn1r_field_ptr-1;
asn1r_get_vstr (ac, data_ptr);
if (strcmp (data_ptr, "GOOSE") != 0)
asn1r_set_dec_err (ac, GOOSE_NAME_MISMATCH);
ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_named_done;
}
/************************************************************************/
/* _goose_named_done */
/************************************************************************/
static ST_VOID _goose_named_done (ASN1_DEC_CTXT *ac)
{
ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_seq_of_varspec_done;
}
/************************************************************************/
/* _goose_seq_of_varspec_done */
/************************************************************************/
static ST_VOID _goose_seq_of_varspec_done (ASN1_DEC_CTXT *ac)
{
ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_list_of_var_done;
}
/************************************************************************/
/* _goose_list_of_var_done */
/************************************************************************/
static ST_VOID _goose_list_of_var_done (ASN1_DEC_CTXT *ac)
{
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX | CONSTR, 0), _goose_list_acc_rslt}}};
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_list_acc_rslt */
/************************************************************************/
static ST_VOID _goose_list_acc_rslt (ASN1_DEC_CTXT *ac)
{
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX | CONSTR, 2), _goose_struct}}};
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_struct */
/************************************************************************/
static ST_VOID _goose_struct (ASN1_DEC_CTXT *ac)
{
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 10), _goose_get_app_id}}};
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_get_app_id */
/************************************************************************/
static ST_VOID _goose_get_app_id (ASN1_DEC_CTXT *ac)
{
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 12), _goose_get_sent_time}}};
_goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
asn1r_get_vstr (ac, _goose_dec_info->SendingIED);
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_get_sent_time */
/************************************************************************/
static ST_VOID _goose_get_sent_time (ASN1_DEC_CTXT *ac)
{
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_seqnum}}};
_goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
asn1r_get_btod (ac, &_goose_dec_info->t);
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_get_seqnum */
/************************************************************************/
static ST_VOID _goose_get_seqnum (ASN1_DEC_CTXT *ac)
{
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_sub_seq}}};
_goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
asn1r_get_u32 (ac, &_goose_dec_info->SqNum);
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_get_sub_seq */
/************************************************************************/
static ST_VOID _goose_get_sub_seq (ASN1_DEC_CTXT *ac)
{
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_hold_time}}};
_goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
asn1r_get_u32 (ac, &_goose_dec_info->StNum);
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_get_hold_time */
/************************************************************************/
static ST_VOID _goose_get_hold_time (ASN1_DEC_CTXT *ac)
{
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_back_time}}};
_goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
asn1r_get_u32 (ac, &_goose_dec_info->HoldTim);
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_get_back_time */
/************************************************************************/
static ST_VOID _goose_get_back_time (ASN1_DEC_CTXT *ac)
{
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 6), _goose_get_phsid}}};
_goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
asn1r_get_u32 (ac, &_goose_dec_info->BackTim);
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_get_phsid */
/************************************************************************/
static ST_VOID _goose_get_phsid (ASN1_DEC_CTXT *ac)
{
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 4), _goose_get_dnabits}}};
_goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
asn1r_get_u16 (ac, &_goose_dec_info->PhsID);
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_get_dnabits */
/************************************************************************/
static ST_VOID _goose_get_dnabits (ASN1_DEC_CTXT *ac)
{
GOOSE_INFO *_goose_dec_info;
static ASN1R_TAG_CTRL_1 asn1r_tags =
{1, {{ASN1R_TAG_VAL (CTX, 4), _goose_get_usrbits}}};
_goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
asn1r_get_bitstr (ac, _goose_dec_info->DNA, GOOSE_MAX_NUM_DNA_BITS);
_goose_dec_info->num_dna_bits = ac->asn1r_bitcount;
ac->asn1r_tag_table = &asn1r_tags;
}
/************************************************************************/
/* _goose_get_usrbits */
/************************************************************************/
static ST_VOID _goose_get_usrbits (ASN1_DEC_CTXT *ac)
{
GOOSE_INFO *_goose_dec_info;
_goose_dec_info = (GOOSE_INFO *) ac->usr_info[0];
asn1r_get_bitstr (ac, _goose_dec_info->UserSt, GOOSE_MAX_NUM_USR_BITS);
_goose_dec_info->num_usr_bits = ac->asn1r_bitcount;
ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_struct_done;
}
/************************************************************************/
/* _goose_struct_done */
/************************************************************************/
static ST_VOID _goose_struct_done (ASN1_DEC_CTXT *ac)
{
ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_list_acc_rslt_done;
}
/************************************************************************/
/* _goose_list_acc_rslt_done */
/************************************************************************/
static ST_VOID _goose_list_acc_rslt_done (ASN1_DEC_CTXT *ac)
{
ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_info_rpt_done;
}
/************************************************************************/
/* _goose_info_rpt_done */
/************************************************************************/
static ST_VOID _goose_info_rpt_done (ASN1_DEC_CTXT *ac)
{
ac->asn1r_c_done_fun [ac->asn1r_msg_level] = _goose_unconf_req_done;
}
/************************************************************************/
/* _goose_unconf_req_done */
/************************************************************************/
static ST_VOID _goose_unconf_req_done (ASN1_DEC_CTXT *ac)
{
ac->asn1r_decode_done_fun = _goose_done_ok;
ac->asn1r_decode_done = SD_TRUE;
}
/************************************************************************/
/* _goose_done_ok */
/************************************************************************/
static ST_VOID _goose_done_ok (ASN1_DEC_CTXT *ac)
{
}
/************************************************************************/
/* _goose_done_error */
/************************************************************************/
static ST_VOID _goose_done_error (ASN1_DEC_CTXT *ac)
{
asn1r_set_dec_err (ac, GOOSE_DONE_TOO_SOON);
}
/************************************************************************/
/* _goose_error */
/************************************************************************/
static ST_VOID _goose_error (ASN1_DEC_CTXT *ac, ST_RET err)
{
}
/************************************************************************/
/* gse_uca_decode */
/* This function decodes the subnet packet (sn_udt), and puts the */
/* results in the user structure (goose_info). */
/* First it decodes from SN_UNITDATA to N_UNITDATA. */
/* Then it decodes from N_UNITDATA to AUDT_APDU. */
/* Then it decodes from AUDT_APDU to GOOSE_INFO. */
/************************************************************************/
ST_RET gse_uca_decode (SN_UNITDATA *sn_udt, /* input subnet packet */
GOOSE_INFO *goose_info) /* output GOOSE data */
{
ST_RET rc;
N_UNITDATA *pNudt = NULL; /* intermediate decode result */
AUDT_APDU audt; /* intermediate decode result */
/* Decode from SN_UNITDATA to N_UNITDATA. */
/* clnpl_decode allocs a N_UNITDATA structure if this is a N-Unitdata */
/* packet, and changes the value of pNudt to point to it. */
if ((rc = clnpl_decode (sn_udt, &pNudt)) != SD_SUCCESS)
return (rc);
if (pNudt==NULL)
return (SD_FAILURE); /* does this ever happen? */
/* Decode from N_UNITDATA to AUDT_APDU. */
if ((rc = cltp_decode_nsdu_2 (pNudt, &audt)) != SD_SUCCESS)
return (rc);
/* CRITICAL: DON't free N_UNITDATA here, because AUDT_APDU has pointer into it. */
/* Decode from AUDT_APDU to GOOSE_INFO. */
rc = _goose_decode_mms (&audt, goose_info);
/* Now it is safe to free N_UNITDATA. All data in GOOSE_INFO now. */
clnp_free (pNudt);
return (rc);
}