libpri/rose.c

2220 lines
68 KiB
C
Raw Normal View History

/*
* libpri: An implementation of Primary Rate ISDN
*
* Copyright (C) 2009 Digium, Inc.
*
* Richard Mudgett <rmudgett@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*
* In addition, when this program is distributed with Asterisk in
* any form that would qualify as a 'combined work' or as a
* 'derivative work' (but not mere aggregation), you can redistribute
* and/or modify the combination under the terms of the license
* provided with that copy of Asterisk, instead of the license
* terms granted here.
*/
/*!
* \file
* \brief Remote Operations Service Element (ROSE) main controlling functions
*
* \author Richard Mudgett <rmudgett@digium.com>
*/
#include <stdio.h>
#include "compat.h"
#include "libpri.h"
#include "pri_internal.h"
#include "rose.h"
#include "rose_internal.h"
#include "asn1.h"
#include "pri_facility.h"
#define ROSE_TAG_COMPONENT_INVOKE (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1)
#define ROSE_TAG_COMPONENT_RESULT (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2)
#define ROSE_TAG_COMPONENT_ERROR (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3)
#define ROSE_TAG_COMPONENT_REJECT (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4)
/*! \brief Structure to convert a code value to a string */
struct rose_code_strings {
/*! \brief Code value to convert to a string */
int code;
/*! \brief String equivalent of the associated code value */
const char *name;
};
/*! \brief ROSE invoke/result message conversion table entry. */
struct rose_convert_msg {
/*! \brief library encoded operation-value */
enum rose_operation operation;
/*!
* \brief OID prefix values to use when encoding/decoding the operation-value OID
* \note NULL if operation-value is a localValue.
*/
const struct asn1_oid *oid_prefix;
/*! \brief Last OID value or localValue for the encoded operation-value */
u_int16_t value;
/*!
* \brief Encode the ROSE invoke operation-value arguments.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 component.
* \param end End of ASN.1 encoding data buffer.
* \param args Arguments to encode in the buffer.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*
* \note The function pointer is NULL if there are no arguments to encode.
*/
unsigned char *(*encode_invoke_args)(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const union rose_msg_invoke_args *args);
/*!
* \brief Encode the ROSE result operation-value arguments.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 component.
* \param end End of ASN.1 encoding data buffer.
* \param args Arguments to encode in the buffer.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*
* \note The function pointer is NULL if there are no arguments to encode.
*/
unsigned char *(*encode_result_args)(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const union rose_msg_result_args *args);
/*!
* \brief Decode the ROSE invoke operation-value arguments.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param tag Component tag that identified this structure.
* \param pos Starting position of the ASN.1 component length.
* \param end End of ASN.1 decoding data buffer.
* \param args Arguments to fill in from the decoded buffer.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*
* \note The function pointer is NULL if there are no arguments to decode.
*/
const unsigned char *(*decode_invoke_args)(struct pri *ctrl, unsigned tag,
const unsigned char *pos, const unsigned char *end,
union rose_msg_invoke_args *args);
/*!
* \brief Decode the ROSE result operation-value arguments.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param tag Component tag that identified this structure.
* \param pos Starting position of the ASN.1 component length.
* \param end End of ASN.1 decoding data buffer.
* \param args Arguments to fill in from the decoded buffer.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*
* \note The function pointer is NULL if there are no arguments to decode.
*/
const unsigned char *(*decode_result_args)(struct pri *ctrl, unsigned tag,
const unsigned char *pos, const unsigned char *end,
union rose_msg_result_args *args);
};
/*! \brief ROSE error code conversion table entry. */
struct rose_convert_error {
/*! \brief library encoded error-value */
enum rose_error_code code;
/*!
* \brief OID prefix values to use when encoding/decoding the error-value OID
* \note NULL if error-value is a localValue.
*/
const struct asn1_oid *oid_prefix;
/*! \brief Last OID value or localValue for the encoded error-value */
u_int16_t value;
/*!
* \brief Encode the ROSE error parameters.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 component.
* \param end End of ASN.1 encoding data buffer.
* \param args Arguments to encode in the buffer.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*
* \note The function pointer is NULL if there are no arguments to encode.
*/
unsigned char *(*encode_error_args)(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const union rose_msg_error_args *args);
/*!
* \brief Decode the ROSE error parameters.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param tag Component tag that identified this structure.
* \param pos Starting position of the ASN.1 component length.
* \param end End of ASN.1 decoding data buffer.
* \param args Arguments to fill in from the decoded buffer.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*
* \note The function pointer is NULL if there are no arguments to decode.
*/
const unsigned char *(*decode_error_args)(struct pri *ctrl, unsigned tag,
const unsigned char *pos, const unsigned char *end,
union rose_msg_error_args *args);
};
/* ------------------------------------------------------------------- */
/*
* Note the first value in oid.values[] is really the first two
* OID subidentifiers. They are compressed using this formula:
* First_Value = (First_Subidentifier * 40) + Second_Subidentifier
*/
/*! \brief ETSI specific invoke/result encode/decode message table */
static const struct rose_convert_msg rose_etsi_msgs[] = {
/* *INDENT-OFF* */
/*
* operation, oid_prefix, value,
* encode_invoke_args, encode_result_args,
* decode_invoke_args, decode_result_args
*/
/*
* localValue's from Advice-of-Charge-Operations
* {ccitt identified-organization etsi (0) 182 operations-and-errors (1)}
*
* Advice-Of-Charge-at-call-Setup(AOCS)
* Advice-Of-Charge-During-the-call(AOCD)
* Advice-Of-Charge-at-the-End-of-the-call(AOCE)
*/
{
ROSE_ETSI_ChargingRequest, NULL, 30,
rose_enc_etsi_ChargingRequest_ARG, rose_enc_etsi_ChargingRequest_RES,
rose_dec_etsi_ChargingRequest_ARG, rose_dec_etsi_ChargingRequest_RES
},
{
ROSE_ETSI_AOCSCurrency, NULL, 31,
rose_enc_etsi_AOCSCurrency_ARG, NULL,
rose_dec_etsi_AOCSCurrency_ARG, NULL
},
{
ROSE_ETSI_AOCSSpecialArr, NULL, 32,
rose_enc_etsi_AOCSSpecialArr_ARG, NULL,
rose_dec_etsi_AOCSSpecialArr_ARG, NULL
},
{
ROSE_ETSI_AOCDCurrency, NULL, 33,
rose_enc_etsi_AOCDCurrency_ARG, NULL,
rose_dec_etsi_AOCDCurrency_ARG, NULL
},
{
ROSE_ETSI_AOCDChargingUnit, NULL, 34,
rose_enc_etsi_AOCDChargingUnit_ARG, NULL,
rose_dec_etsi_AOCDChargingUnit_ARG, NULL
},
{
ROSE_ETSI_AOCECurrency, NULL, 35,
rose_enc_etsi_AOCECurrency_ARG, NULL,
rose_dec_etsi_AOCECurrency_ARG, NULL
},
{
ROSE_ETSI_AOCEChargingUnit, NULL, 36,
rose_enc_etsi_AOCEChargingUnit_ARG, NULL,
rose_dec_etsi_AOCEChargingUnit_ARG, NULL
},
/* *INDENT-ON* */
};
/*! \brief ETSI specific error-value converion table */
static const struct rose_convert_error rose_etsi_errors[] = {
/* *INDENT-OFF* */
/*
* error-code, oid_prefix, value
* encode_error_args, decode_error_args
*/
/*
* localValue Errors from General-Errors
* {ccitt identified-organization etsi(0) 196 general-errors(2)}
*/
{
ROSE_ERROR_Gen_NotSubscribed, NULL, 0,
NULL, NULL
},
{
ROSE_ERROR_Gen_NotAvailable, NULL, 3,
NULL, NULL
},
{
ROSE_ERROR_Gen_NotImplemented, NULL, 4,
NULL, NULL
},
{
ROSE_ERROR_Gen_InvalidServedUserNr, NULL, 6,
NULL, NULL
},
{
ROSE_ERROR_Gen_InvalidCallState, NULL, 7,
NULL, NULL
},
{
ROSE_ERROR_Gen_BasicServiceNotProvided, NULL, 8,
NULL, NULL
},
{
ROSE_ERROR_Gen_NotIncomingCall, NULL, 9,
NULL, NULL
},
{
ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,NULL, 10,
NULL, NULL
},
{
ROSE_ERROR_Gen_ResourceUnavailable, NULL, 11,
NULL, NULL
},
/*
* localValue Errors from Advice-of-Charge-Operations
* {ccitt identified-organization etsi (0) 182 operations-and-errors (1)}
*/
{
ROSE_ERROR_AOC_NoChargingInfoAvailable, NULL, 26,
NULL, NULL
},
/* *INDENT-ON* */
};
/* ------------------------------------------------------------------- */
/*! \brief Q.SIG specific invoke/result encode/decode message table */
static const struct rose_convert_msg rose_qsig_msgs[] = {
/* *INDENT-OFF* */
/*
* operation, oid_prefix, value,
* encode_invoke_args, encode_result_args,
* decode_invoke_args, decode_result_args
*/
/*
* localValue's from Q.SIG Name-Operations
* { iso(1) standard(0) pss1-name(13868) name-operations(0) }
*/
{
ROSE_QSIG_CallingName, NULL, 0,
rose_enc_qsig_CallingName_ARG, NULL,
rose_dec_qsig_CallingName_ARG, NULL
},
{
ROSE_QSIG_CalledName, NULL, 1,
rose_enc_qsig_CalledName_ARG, NULL,
rose_dec_qsig_CalledName_ARG, NULL
},
{
ROSE_QSIG_ConnectedName, NULL, 2,
rose_enc_qsig_ConnectedName_ARG, NULL,
rose_dec_qsig_ConnectedName_ARG, NULL
},
{
ROSE_QSIG_BusyName, NULL, 3,
rose_enc_qsig_BusyName_ARG, NULL,
rose_dec_qsig_BusyName_ARG, NULL
},
/*
* localValue's from Q.SIG Call-Transfer-Operations
* { iso(1) standard(0) pss1-call-transfer(13869) call-transfer-operations(0) }
*/
{
ROSE_QSIG_CallTransferIdentify, NULL, 7,
rose_enc_qsig_DummyArg_ARG, rose_enc_qsig_CallTransferIdentify_RES,
rose_dec_qsig_DummyArg_ARG, rose_dec_qsig_CallTransferIdentify_RES
},
{
ROSE_QSIG_CallTransferAbandon, NULL, 8,
rose_enc_qsig_DummyArg_ARG, NULL,
rose_dec_qsig_DummyArg_ARG, NULL
},
{
ROSE_QSIG_CallTransferInitiate, NULL, 9,
rose_enc_qsig_CallTransferInitiate_ARG, rose_enc_qsig_DummyRes_RES,
rose_dec_qsig_CallTransferInitiate_ARG, rose_dec_qsig_DummyRes_RES
},
{
ROSE_QSIG_CallTransferSetup, NULL, 10,
rose_enc_qsig_CallTransferSetup_ARG, rose_enc_qsig_DummyRes_RES,
rose_dec_qsig_CallTransferSetup_ARG, rose_dec_qsig_DummyRes_RES
},
{
ROSE_QSIG_CallTransferActive, NULL, 11,
rose_enc_qsig_CallTransferActive_ARG, NULL,
rose_dec_qsig_CallTransferActive_ARG, NULL
},
{
ROSE_QSIG_CallTransferComplete, NULL, 12,
rose_enc_qsig_CallTransferComplete_ARG, NULL,
rose_dec_qsig_CallTransferComplete_ARG, NULL
},
{
ROSE_QSIG_CallTransferUpdate, NULL, 13,
rose_enc_qsig_CallTransferUpdate_ARG, NULL,
rose_dec_qsig_CallTransferUpdate_ARG, NULL
},
{
ROSE_QSIG_SubaddressTransfer, NULL, 14,
rose_enc_qsig_SubaddressTransfer_ARG, NULL,
rose_dec_qsig_SubaddressTransfer_ARG, NULL
},
/*
* NOTE: I do not have the specification needed to fully support this
* message. Fortunately, all I have to do for this message is to switch
* it to the bridged call leg for 2BCT support.
*/
{
ROSE_QSIG_PathReplacement, NULL, 4,
NULL, NULL,
NULL, NULL
},
/*
* localValue's from Q.SIG Call-Diversion-Operations
* { iso(1) standard(0) pss1-call-diversion(13873) call-diversion-operations(0) }
*/
{
ROSE_QSIG_ActivateDiversionQ, NULL, 15,
rose_enc_qsig_ActivateDiversionQ_ARG, rose_enc_qsig_DummyRes_RES,
rose_dec_qsig_ActivateDiversionQ_ARG, rose_dec_qsig_DummyRes_RES
},
{
ROSE_QSIG_DeactivateDiversionQ, NULL, 16,
rose_enc_qsig_DeactivateDiversionQ_ARG, rose_enc_qsig_DummyRes_RES,
rose_dec_qsig_DeactivateDiversionQ_ARG, rose_dec_qsig_DummyRes_RES
},
{
ROSE_QSIG_InterrogateDiversionQ, NULL, 17,
rose_enc_qsig_InterrogateDiversionQ_ARG,rose_enc_qsig_InterrogateDiversionQ_RES,
rose_dec_qsig_InterrogateDiversionQ_ARG,rose_dec_qsig_InterrogateDiversionQ_RES
},
{
ROSE_QSIG_CheckRestriction, NULL, 18,
rose_enc_qsig_CheckRestriction_ARG, rose_enc_qsig_DummyRes_RES,
rose_dec_qsig_CheckRestriction_ARG, rose_dec_qsig_DummyRes_RES
},
{
ROSE_QSIG_CallRerouting, NULL, 19,
rose_enc_qsig_CallRerouting_ARG, rose_enc_qsig_DummyRes_RES,
rose_dec_qsig_CallRerouting_ARG, rose_dec_qsig_DummyRes_RES
},
{
ROSE_QSIG_DivertingLegInformation1, NULL, 20,
rose_enc_qsig_DivertingLegInformation1_ARG,NULL,
rose_dec_qsig_DivertingLegInformation1_ARG,NULL
},
{
ROSE_QSIG_DivertingLegInformation2, NULL, 21,
rose_enc_qsig_DivertingLegInformation2_ARG,NULL,
rose_dec_qsig_DivertingLegInformation2_ARG,NULL
},
{
ROSE_QSIG_DivertingLegInformation3, NULL, 22,
rose_enc_qsig_DivertingLegInformation3_ARG,NULL,
rose_dec_qsig_DivertingLegInformation3_ARG,NULL
},
{
ROSE_QSIG_CfnrDivertedLegFailed, NULL, 23,
rose_enc_qsig_DummyArg_ARG, NULL,
rose_dec_qsig_DummyArg_ARG, NULL
},
/*
* localValue's from Q.SIG SS-MWI-Operations
* { iso(1) standard(0) pss1-message-waiting-indication(15506) message-waiting-operations(0) }
*/
{
ROSE_QSIG_MWIActivate, NULL, 80,
rose_enc_qsig_MWIActivate_ARG, rose_enc_qsig_DummyRes_RES,
rose_dec_qsig_MWIActivate_ARG, rose_dec_qsig_DummyRes_RES
},
{
ROSE_QSIG_MWIDeactivate, NULL, 81,
rose_enc_qsig_MWIDeactivate_ARG, rose_enc_qsig_DummyRes_RES,
rose_dec_qsig_MWIDeactivate_ARG, rose_dec_qsig_DummyRes_RES
},
{
ROSE_QSIG_MWIInterrogate, NULL, 82,
rose_enc_qsig_MWIInterrogate_ARG, rose_enc_qsig_MWIInterrogate_RES,
rose_dec_qsig_MWIInterrogate_ARG, rose_dec_qsig_MWIInterrogate_RES
},
/* *INDENT-ON* */
};
/*! \brief Q.SIG specific error-value converion table */
static const struct rose_convert_error rose_qsig_errors[] = {
/* *INDENT-OFF* */
/*
* error-code, oid_prefix, value
* encode_error_args, decode_error_args
*/
/*
* localValue Errors from General-Error-List
* {ccitt identified-organization q 950 general-error-list(1)}
*/
{
ROSE_ERROR_Gen_NotSubscribed, NULL, 0,
NULL, NULL
},
{
ROSE_ERROR_Gen_RejectedByNetwork, NULL, 1,
NULL, NULL
},
{
ROSE_ERROR_Gen_RejectedByUser, NULL, 2,
NULL, NULL
},
{
ROSE_ERROR_Gen_NotAvailable, NULL, 3,
NULL, NULL
},
{
ROSE_ERROR_Gen_InsufficientInformation, NULL, 5,
NULL, NULL
},
{
ROSE_ERROR_Gen_InvalidServedUserNr, NULL, 6,
NULL, NULL
},
{
ROSE_ERROR_Gen_InvalidCallState, NULL, 7,
NULL, NULL
},
{
ROSE_ERROR_Gen_BasicServiceNotProvided, NULL, 8,
NULL, NULL
},
{
ROSE_ERROR_Gen_NotIncomingCall, NULL, 9,
NULL, NULL
},
{
ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,NULL, 10,
NULL, NULL
},
{
ROSE_ERROR_Gen_ResourceUnavailable, NULL, 11,
NULL, NULL
},
{
ROSE_ERROR_Gen_CallFailure, NULL, 25,
NULL, NULL
},
{
ROSE_ERROR_Gen_ProceduralError, NULL, 43,
NULL, NULL
},
/*
* From various Q.SIG specifications.
* We will ignore the manufacturer specific extension information.
*/
{
ROSE_ERROR_QSIG_Unspecified, NULL, 1008,
NULL, NULL
},
/*
* localValue's from Q.SIG Call-Transfer-Operations
* { iso(1) standard(0) pss1-call-transfer(13869) call-transfer-operations(0) }
*/
{
ROSE_ERROR_QSIG_CT_InvalidReroutingNumber, NULL, 1004,
NULL, NULL
},
{
ROSE_ERROR_QSIG_CT_UnrecognizedCallIdentity,NULL, 1005,
NULL, NULL
},
{
ROSE_ERROR_QSIG_CT_EstablishmentFailure, NULL, 1006,
NULL, NULL
},
/*
* localValue's from Q.SIG Call-Diversion-Operations
* { iso(1) standard(0) pss1-call-diversion(13873) call-diversion-operations(0) }
*/
{
ROSE_ERROR_Div_InvalidDivertedToNr, NULL, 12,
NULL, NULL
},
{
ROSE_ERROR_Div_SpecialServiceNr, NULL, 14,
NULL, NULL
},
{
ROSE_ERROR_Div_DiversionToServedUserNr, NULL, 15,
NULL, NULL
},
{
ROSE_ERROR_Div_NumberOfDiversionsExceeded, NULL, 24,
NULL, NULL
},
{
ROSE_ERROR_QSIG_Div_TemporarilyUnavailable, NULL, 1000,
NULL, NULL
},
{
ROSE_ERROR_QSIG_Div_NotAuthorized, NULL, 1007,
NULL, NULL
},
/*
* localValue's from Q.SIG SS-MWI-Operations
* { iso(1) standard(0) pss1-message-waiting-indication(15506) message-waiting-operations(0) }
*/
{
ROSE_ERROR_QSIG_InvalidMsgCentreId, NULL, 1018,
NULL, NULL
},
/* *INDENT-ON* */
};
/* ------------------------------------------------------------------- */
/*! \brief DMS-100 specific invoke/result encode/decode message table */
static const struct rose_convert_msg rose_dms100_msgs[] = {
/* *INDENT-OFF* */
/*
* operation, oid_prefix, value,
* encode_invoke_args, encode_result_args,
* decode_invoke_args, decode_result_args
*/
{
ROSE_DMS100_RLT_OperationInd, NULL, ROSE_DMS100_RLT_OPERATION_IND,
NULL, rose_enc_dms100_RLT_OperationInd_RES,
NULL, rose_dec_dms100_RLT_OperationInd_RES
},
{
ROSE_DMS100_RLT_ThirdParty, NULL, ROSE_DMS100_RLT_THIRD_PARTY,
rose_enc_dms100_RLT_ThirdParty_ARG, NULL,
rose_dec_dms100_RLT_ThirdParty_ARG, NULL
},
/* *INDENT-ON* */
};
/*! \brief DMS-100 specific error-value converion table */
static const struct rose_convert_error rose_dms100_errors[] = {
/* *INDENT-OFF* */
/*
* error-code, oid_prefix, value
* encode_error_args, decode_error_args
*/
{
ROSE_ERROR_DMS100_RLT_BridgeFail, NULL, 0x10,
NULL, NULL
},
{
ROSE_ERROR_DMS100_RLT_CallIDNotFound, NULL, 0x11,
NULL, NULL
},
{
ROSE_ERROR_DMS100_RLT_NotAllowed, NULL, 0x12,
NULL, NULL
},
{
ROSE_ERROR_DMS100_RLT_SwitchEquipCongs, NULL, 0x13,
NULL, NULL
},
/* *INDENT-ON* */
};
/* ------------------------------------------------------------------- */
/*
* Note the first value in oid.values[] is really the first two
* OID subidentifiers. They are compressed using this formula:
* First_Value = (First_Subidentifier * 40) + Second_Subidentifier
*/
static const struct asn1_oid rose_ni2_oid = {
/* *INDENT-OFF* */
/* { iso(1) member-body(2) usa(840) ansi-t1(10005) operations(0) } */
4, { 42, 840, 10005, 0 }
/* *INDENT-ON* */
};
/*! \brief NI2 specific invoke/result encode/decode message table */
static const struct rose_convert_msg rose_ni2_msgs[] = {
/* *INDENT-OFF* */
/*
* operation, oid_prefix, value,
* encode_invoke_args, encode_result_args,
* decode_invoke_args, decode_result_args
*/
/* NI2 seems to have pirated several Q.SIG messages */
/*
* localValue's from Q.SIG Name-Operations
* { iso(1) standard(0) pss1-name(13868) name-operations(0) }
*/
{
ROSE_QSIG_CallingName, NULL, 0,
rose_enc_qsig_CallingName_ARG, NULL,
rose_dec_qsig_CallingName_ARG, NULL
},
{
ROSE_QSIG_CalledName, NULL, 1,
rose_enc_qsig_CalledName_ARG, NULL,
rose_dec_qsig_CalledName_ARG, NULL
},
{
ROSE_QSIG_ConnectedName, NULL, 2,
rose_enc_qsig_ConnectedName_ARG, NULL,
rose_dec_qsig_ConnectedName_ARG, NULL
},
{
ROSE_QSIG_BusyName, NULL, 3,
rose_enc_qsig_BusyName_ARG, NULL,
rose_dec_qsig_BusyName_ARG, NULL
},
{
ROSE_NI2_InformationFollowing, &rose_ni2_oid, 4,
rose_enc_ni2_InformationFollowing_ARG, NULL,
rose_dec_ni2_InformationFollowing_ARG, NULL
},
/* Also used by PRI_SWITCH_ATT4ESS and PRI_SWITCH_LUCENT5E */
{
ROSE_NI2_InitiateTransfer, &rose_ni2_oid, 8,
rose_enc_ni2_InitiateTransfer_ARG, NULL,
rose_dec_ni2_InitiateTransfer_ARG, NULL
},
/* *INDENT-ON* */
};
/*! \brief NI2 specific error-value converion table */
static const struct rose_convert_error rose_ni2_errors[] = {
/* *INDENT-OFF* */
/*
* error-code, oid_prefix, value
* encode_error_args, decode_error_args
*/
/*
* localValue Errors from General-Error-List
* {ccitt identified-organization q 950 general-error-list(1)}
*/
{
ROSE_ERROR_Gen_NotSubscribed, NULL, 0,
NULL, NULL
},
{
ROSE_ERROR_Gen_RejectedByNetwork, NULL, 1,
NULL, NULL
},
{
ROSE_ERROR_Gen_RejectedByUser, NULL, 2,
NULL, NULL
},
{
ROSE_ERROR_Gen_NotAvailable, NULL, 3,
NULL, NULL
},
{
ROSE_ERROR_Gen_InsufficientInformation, NULL, 5,
NULL, NULL
},
{
ROSE_ERROR_Gen_InvalidServedUserNr, NULL, 6,
NULL, NULL
},
{
ROSE_ERROR_Gen_InvalidCallState, NULL, 7,
NULL, NULL
},
{
ROSE_ERROR_Gen_BasicServiceNotProvided, NULL, 8,
NULL, NULL
},
{
ROSE_ERROR_Gen_NotIncomingCall, NULL, 9,
NULL, NULL
},
{
ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,NULL, 10,
NULL, NULL
},
{
ROSE_ERROR_Gen_ResourceUnavailable, NULL, 11,
NULL, NULL
},
{
ROSE_ERROR_Gen_CallFailure, NULL, 25,
NULL, NULL
},
{
ROSE_ERROR_Gen_ProceduralError, NULL, 43,
NULL, NULL
},
/* *INDENT-ON* */
};
/* ------------------------------------------------------------------- */
/*!
* \internal
* \brief Convert the given code value to a string.
*
* \param code Code value to convert to a string.
* \param arr Array to convert the code to a string.
* \param num_elements Number of elements in the conversion array.
*
* \retval String version of the given code value.
*/
static const char *rose_code2str(int code, const struct rose_code_strings *arr,
unsigned num_elements)
{
static char invalid_code[40];
unsigned index;
for (index = 0; index < num_elements; ++index) {
if (arr[index].code == code) {
return arr[index].name;
}
}
snprintf(invalid_code, sizeof(invalid_code), "Invalid code:%d 0x%X", code, code);
return invalid_code;
}
/*!
* \brief Convert the given operation-value to a string.
*
* \param operation Operation-value to convert to a string.
*
* \retval String version of the given operation-value.
*/
const char *rose_operation2str(enum rose_operation operation)
{
static const struct rose_code_strings arr[] = {
/* *INDENT-OFF* */
{ ROSE_None, "ROSE_None" },
{ ROSE_Unknown, "ROSE_Unknown" },
{ ROSE_ETSI_ChargingRequest, "ROSE_ETSI_ChargingRequest" },
{ ROSE_ETSI_AOCSCurrency, "ROSE_ETSI_AOCSCurrency" },
{ ROSE_ETSI_AOCSSpecialArr, "ROSE_ETSI_AOCSSpecialArr" },
{ ROSE_ETSI_AOCDCurrency, "ROSE_ETSI_AOCDCurrency" },
{ ROSE_ETSI_AOCDChargingUnit, "ROSE_ETSI_AOCDChargingUnit" },
{ ROSE_ETSI_AOCECurrency, "ROSE_ETSI_AOCECurrency" },
{ ROSE_ETSI_AOCEChargingUnit, "ROSE_ETSI_AOCEChargingUnit" },
{ ROSE_QSIG_CallingName, "ROSE_QSIG_CallingName" },
{ ROSE_QSIG_CalledName, "ROSE_QSIG_CalledName" },
{ ROSE_QSIG_ConnectedName, "ROSE_QSIG_ConnectedName" },
{ ROSE_QSIG_BusyName, "ROSE_QSIG_BusyName" },
{ ROSE_QSIG_CallTransferIdentify, "ROSE_QSIG_CallTransferIdentify" },
{ ROSE_QSIG_CallTransferAbandon, "ROSE_QSIG_CallTransferAbandon" },
{ ROSE_QSIG_CallTransferInitiate, "ROSE_QSIG_CallTransferInitiate" },
{ ROSE_QSIG_CallTransferSetup, "ROSE_QSIG_CallTransferSetup" },
{ ROSE_QSIG_CallTransferActive, "ROSE_QSIG_CallTransferActive" },
{ ROSE_QSIG_CallTransferComplete, "ROSE_QSIG_CallTransferComplete" },
{ ROSE_QSIG_CallTransferUpdate, "ROSE_QSIG_CallTransferUpdate" },
{ ROSE_QSIG_SubaddressTransfer, "ROSE_QSIG_SubaddressTransfer" },
{ ROSE_QSIG_PathReplacement, "ROSE_QSIG_PathReplacement" },
{ ROSE_QSIG_ActivateDiversionQ, "ROSE_QSIG_ActivateDiversionQ" },
{ ROSE_QSIG_DeactivateDiversionQ, "ROSE_QSIG_DeactivateDiversionQ" },
{ ROSE_QSIG_InterrogateDiversionQ, "ROSE_QSIG_InterrogateDiversionQ" },
{ ROSE_QSIG_CheckRestriction, "ROSE_QSIG_CheckRestriction" },
{ ROSE_QSIG_CallRerouting, "ROSE_QSIG_CallRerouting" },
{ ROSE_QSIG_DivertingLegInformation1, "ROSE_QSIG_DivertingLegInformation1" },
{ ROSE_QSIG_DivertingLegInformation2, "ROSE_QSIG_DivertingLegInformation2" },
{ ROSE_QSIG_DivertingLegInformation3, "ROSE_QSIG_DivertingLegInformation3" },
{ ROSE_QSIG_CfnrDivertedLegFailed, "ROSE_QSIG_CfnrDivertedLegFailed" },
{ ROSE_QSIG_MWIActivate, "ROSE_QSIG_MWIActivate" },
{ ROSE_QSIG_MWIDeactivate, "ROSE_QSIG_MWIDeactivate" },
{ ROSE_QSIG_MWIInterrogate, "ROSE_QSIG_MWIInterrogate" },
{ ROSE_DMS100_RLT_OperationInd, "ROSE_DMS100_RLT_OperationInd" },
{ ROSE_DMS100_RLT_ThirdParty, "ROSE_DMS100_RLT_ThirdParty" },
{ ROSE_NI2_InformationFollowing, "ROSE_NI2_InformationFollowing" },
{ ROSE_NI2_InitiateTransfer, "ROSE_NI2_InitiateTransfer" },
/* *INDENT-ON* */
};
return rose_code2str(operation, arr, ARRAY_LEN(arr));
}
/*!
* \brief Convert the given error-value to a string.
*
* \param code Error-value to convert to a string.
*
* \retval String version of the given error-value.
*/
const char *rose_error2str(enum rose_error_code code)
{
static const struct rose_code_strings arr[] = {
/* *INDENT-OFF* */
{ ROSE_ERROR_None, "No error occurred" },
{ ROSE_ERROR_Unknown, "Unknown error-value code" },
{ ROSE_ERROR_Gen_NotSubscribed, "General: Not Subscribed" },
{ ROSE_ERROR_Gen_NotAvailable, "General: Not Available" },
{ ROSE_ERROR_Gen_NotImplemented, "General: Not Implemented" },
{ ROSE_ERROR_Gen_InvalidServedUserNr, "General: Invalid Served User Number" },
{ ROSE_ERROR_Gen_InvalidCallState, "General: Invalid Call State" },
{ ROSE_ERROR_Gen_BasicServiceNotProvided, "General: Basic Service Not Provided" },
{ ROSE_ERROR_Gen_NotIncomingCall, "General: Not Incoming Call" },
{ ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,"General: Supplementary Service Interaction Not Allowed" },
{ ROSE_ERROR_Gen_ResourceUnavailable, "General: Resource Unavailable" },
/* Additional Q.950 General-Errors for Q.SIG */
{ ROSE_ERROR_Gen_RejectedByNetwork, "General: Rejected By Network" },
{ ROSE_ERROR_Gen_RejectedByUser, "General: Rejected By User" },
{ ROSE_ERROR_Gen_InsufficientInformation, "General: Insufficient Information" },
{ ROSE_ERROR_Gen_CallFailure, "General: Call Failure" },
{ ROSE_ERROR_Gen_ProceduralError, "General: Procedural Error" },
{ ROSE_ERROR_Div_InvalidDivertedToNr, "Diversion: Invalid Diverted To Number" },
{ ROSE_ERROR_Div_SpecialServiceNr, "Diversion: Special Service Number" },
{ ROSE_ERROR_Div_DiversionToServedUserNr, "Diversion: Diversion To Served User Number" },
{ ROSE_ERROR_Div_NumberOfDiversionsExceeded, "Diversion: Number Of Diversions Exceeded" },
{ ROSE_ERROR_AOC_NoChargingInfoAvailable, "AOC: No Charging Info Available" },
/* Q.SIG specific errors */
{ ROSE_ERROR_QSIG_Unspecified, "Unspecified" },
{ ROSE_ERROR_QSIG_CT_InvalidReroutingNumber, "CT: Invalid Rerouting Number" },
{ ROSE_ERROR_QSIG_CT_UnrecognizedCallIdentity,"CT: Unrecognized Call Identity" },
{ ROSE_ERROR_QSIG_CT_EstablishmentFailure, "CT: Establishment Failure" },
{ ROSE_ERROR_QSIG_Div_TemporarilyUnavailable, "Diversion: Temporarily Unavailable" },
{ ROSE_ERROR_QSIG_Div_NotAuthorized, "Diversion: Not Authorized" },
{ ROSE_ERROR_QSIG_InvalidMsgCentreId, "MWI: Invalid Message Center ID" },
/* DMS-100 specific errors */
{ ROSE_ERROR_DMS100_RLT_BridgeFail, "RLT: Bridge Fail" },
{ ROSE_ERROR_DMS100_RLT_CallIDNotFound, "RLT: Call ID Not Found" },
{ ROSE_ERROR_DMS100_RLT_NotAllowed, "RLT: Not Allowed" },
{ ROSE_ERROR_DMS100_RLT_SwitchEquipCongs, "RLT: Switch Equip Congs" },
/* *INDENT-ON* */
};
return rose_code2str(code, arr, ARRAY_LEN(arr));
}
/*!
* \brief Convert the given reject problem-value to a string.
*
* \param code Reject problem-value to convert to a string.
*
* \retval String version of the given reject problem-value.
*/
const char *rose_reject2str(enum rose_reject_code code)
{
static const struct rose_code_strings arr[] = {
/* *INDENT-OFF* */
{ ROSE_REJECT_None, "No reject occurred" },
{ ROSE_REJECT_Unknown, "Unknown reject code" },
{ ROSE_REJECT_Gen_UnrecognizedComponent, "General: Unrecognized Component" },
{ ROSE_REJECT_Gen_MistypedComponent, "General: Mistyped Component" },
{ ROSE_REJECT_Gen_BadlyStructuredComponent, "General: Badly Structured Component" },
{ ROSE_REJECT_Inv_DuplicateInvocation, "Invoke: Duplicate Invocation" },
{ ROSE_REJECT_Inv_UnrecognizedOperation, "Invoke: Unrecognized Operation" },
{ ROSE_REJECT_Inv_MistypedArgument, "Invoke: Mistyped Argument" },
{ ROSE_REJECT_Inv_ResourceLimitation, "Invoke: Resource Limitation" },
{ ROSE_REJECT_Inv_InitiatorReleasing, "Invoke: Initiator Releasing" },
{ ROSE_REJECT_Inv_UnrecognizedLinkedID, "Invoke: Unrecognized Linked ID" },
{ ROSE_REJECT_Inv_LinkedResponseUnexpected, "Invoke: Linked Response Unexpected" },
{ ROSE_REJECT_Inv_UnexpectedChildOperation, "Invoke: Unexpected Child Operation" },
{ ROSE_REJECT_Res_UnrecognizedInvocation, "Result: Unrecognized Invocation" },
{ ROSE_REJECT_Res_ResultResponseUnexpected, "Result: Result Response Unexpected" },
{ ROSE_REJECT_Res_MistypedResult, "Result: Mistyped Result" },
{ ROSE_REJECT_Err_UnrecognizedInvocation, "Error: Unrecognized Invocation" },
{ ROSE_REJECT_Err_ErrorResponseUnexpected, "Error: Error Response Unexpected" },
{ ROSE_REJECT_Err_UnrecognizedError, "Error: Unrecognized Error" },
{ ROSE_REJECT_Err_UnexpectedError, "Error: Unexpected Error" },
{ ROSE_REJECT_Err_MistypedParameter, "Error: Mistyped Parameter" },
/* *INDENT-ON* */
};
return rose_code2str(code, arr, ARRAY_LEN(arr));
}
/*!
* \internal
* \brief Find an operation message conversion entry using the operation code.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param operation Library operation-value code.
*
* \retval Message conversion entry on success.
* \retval NULL on error.
*/
static const struct rose_convert_msg *rose_find_msg_by_op_code(struct pri *ctrl,
enum rose_operation operation)
{
const struct rose_convert_msg *found;
const struct rose_convert_msg *table;
size_t num_entries;
size_t index;
/* Determine which message conversion table to use */
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_T1:
case PRI_SWITCH_EUROISDN_E1:
table = rose_etsi_msgs;
num_entries = ARRAY_LEN(rose_etsi_msgs);
break;
case PRI_SWITCH_QSIG:
table = rose_qsig_msgs;
num_entries = ARRAY_LEN(rose_qsig_msgs);
break;
case PRI_SWITCH_DMS100:
table = rose_dms100_msgs;
num_entries = ARRAY_LEN(rose_dms100_msgs);
break;
case PRI_SWITCH_ATT4ESS:
case PRI_SWITCH_LUCENT5E:
case PRI_SWITCH_NI2:
table = rose_ni2_msgs;
num_entries = ARRAY_LEN(rose_ni2_msgs);
break;
default:
return NULL;
}
/* Search for the table entry */
found = NULL;
for (index = 0; index < num_entries; ++index) {
if (table[index].operation == operation) {
found = &table[index];
break;
}
}
return found;
}
/*!
* \internal
* \brief Find an operation message conversion entry using the
* operation-value OID value or localValue.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param oid Search for the full OID if not NULL.
* \param local Search for the localValue if OID is NULL.
*
* \retval Message conversion entry on success.
* \retval NULL on error.
*/
static const struct rose_convert_msg *rose_find_msg_by_op_val(struct pri *ctrl,
const struct asn1_oid *oid, unsigned local)
{
const struct rose_convert_msg *found;
const struct rose_convert_msg *table;
size_t num_entries;
size_t index;
int sub_index;
/* Determine which message conversion table to use */
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_T1:
case PRI_SWITCH_EUROISDN_E1:
table = rose_etsi_msgs;
num_entries = ARRAY_LEN(rose_etsi_msgs);
break;
case PRI_SWITCH_QSIG:
table = rose_qsig_msgs;
num_entries = ARRAY_LEN(rose_qsig_msgs);
break;
case PRI_SWITCH_DMS100:
table = rose_dms100_msgs;
num_entries = ARRAY_LEN(rose_dms100_msgs);
break;
case PRI_SWITCH_ATT4ESS:
case PRI_SWITCH_LUCENT5E:
case PRI_SWITCH_NI2:
table = rose_ni2_msgs;
num_entries = ARRAY_LEN(rose_ni2_msgs);
break;
default:
return NULL;
}
/* Search for the table entry */
found = NULL;
if (oid) {
/* Search for an OID entry */
local = oid->value[oid->num_values - 1];
for (index = 0; index < num_entries; ++index) {
if (table[index].value == local && table[index].oid_prefix
&& table[index].oid_prefix->num_values == oid->num_values - 1) {
/* Now lets match the OID prefix subidentifiers */
for (sub_index = oid->num_values - 2; 0 <= sub_index; --sub_index) {
if (oid->value[sub_index]
!= table[index].oid_prefix->value[sub_index]) {
break;
}
}
if (sub_index == -1) {
/* All of the OID subidentifiers matched */
found = &table[index];
break;
}
}
}
} else {
/* Search for a localValue entry */
for (index = 0; index < num_entries; ++index) {
if (table[index].value == local && !table[index].oid_prefix) {
found = &table[index];
break;
}
}
}
return found;
}
/*!
* \internal
* \brief Find an error conversion entry using the error code.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param code Library error-value code.
*
* \retval Error conversion entry on success.
* \retval NULL on error.
*/
static const struct rose_convert_error *rose_find_error_by_op_code(struct pri *ctrl,
enum rose_error_code code)
{
const struct rose_convert_error *found;
const struct rose_convert_error *table;
size_t num_entries;
size_t index;
/* Determine which error conversion table to use */
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_T1:
case PRI_SWITCH_EUROISDN_E1:
table = rose_etsi_errors;
num_entries = ARRAY_LEN(rose_etsi_errors);
break;
case PRI_SWITCH_QSIG:
table = rose_qsig_errors;
num_entries = ARRAY_LEN(rose_qsig_errors);
break;
case PRI_SWITCH_DMS100:
table = rose_dms100_errors;
num_entries = ARRAY_LEN(rose_dms100_errors);
break;
case PRI_SWITCH_ATT4ESS:
case PRI_SWITCH_LUCENT5E:
case PRI_SWITCH_NI2:
table = rose_ni2_errors;
num_entries = ARRAY_LEN(rose_ni2_errors);
break;
default:
return NULL;
}
/* Search for the table entry */
found = NULL;
for (index = 0; index < num_entries; ++index) {
if (table[index].code == code) {
found = &table[index];
break;
}
}
return found;
}
/*!
* \internal
* \brief Find an error conversion entry using the
* error-value OID value or localValue.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param oid Search for the full OID if not NULL.
* \param local Search for the localValue if OID is NULL.
*
* \retval Error conversion entry on success.
* \retval NULL on error.
*/
static const struct rose_convert_error *rose_find_error_by_op_val(struct pri *ctrl,
const struct asn1_oid *oid, unsigned local)
{
const struct rose_convert_error *found;
const struct rose_convert_error *table;
size_t num_entries;
size_t index;
int sub_index;
/* Determine which error conversion table to use */
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_T1:
case PRI_SWITCH_EUROISDN_E1:
table = rose_etsi_errors;
num_entries = ARRAY_LEN(rose_etsi_errors);
break;
case PRI_SWITCH_QSIG:
table = rose_qsig_errors;
num_entries = ARRAY_LEN(rose_qsig_errors);
break;
case PRI_SWITCH_DMS100:
table = rose_dms100_errors;
num_entries = ARRAY_LEN(rose_dms100_errors);
break;
case PRI_SWITCH_ATT4ESS:
case PRI_SWITCH_LUCENT5E:
case PRI_SWITCH_NI2:
table = rose_ni2_errors;
num_entries = ARRAY_LEN(rose_ni2_errors);
break;
default:
return NULL;
}
/* Search for the table entry */
found = NULL;
if (oid) {
/* Search for an OID entry */
local = oid->value[oid->num_values - 1];
for (index = 0; index < num_entries; ++index) {
if (table[index].value == local && table[index].oid_prefix
&& table[index].oid_prefix->num_values == oid->num_values - 1) {
/* Now lets match the OID prefix subidentifiers */
for (sub_index = oid->num_values - 2; 0 <= sub_index; --sub_index) {
if (oid->value[sub_index]
!= table[index].oid_prefix->value[sub_index]) {
break;
}
}
if (sub_index == -1) {
/* All of the OID subidentifiers matched */
found = &table[index];
break;
}
}
}
} else {
/* Search for a localValue entry */
for (index = 0; index < num_entries; ++index) {
if (table[index].value == local && !table[index].oid_prefix) {
found = &table[index];
break;
}
}
}
return found;
}
/*!
* \internal
* \brief Encode the Facility ie component operation-value.
*
* \param pos Starting position to encode the operation-value.
* \param end End of ASN.1 encoding data buffer.
* \param oid_prefix Encode as an OID if not NULL.
* \param local Encode as a localValue if oid_prefix is NULL
* else it is the last OID subidentifier.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *rose_enc_operation_value(unsigned char *pos, unsigned char *end,
const struct asn1_oid *oid_prefix, unsigned local)
{
struct asn1_oid oid;
if (oid_prefix) {
if (ARRAY_LEN(oid_prefix->value) <= oid_prefix->num_values) {
return NULL;
}
oid = *oid_prefix;
oid.value[oid.num_values++] = local;
return asn1_enc_oid(pos, end, ASN1_TYPE_OBJECT_IDENTIFIER, &oid);
} else {
return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, local);
}
}
/*! \brief Mapped to rose_enc_operation_value() */
#define rose_enc_error_value(pos, end, oid_prefix, local) \
rose_enc_operation_value(pos, end, oid_prefix, local)
/*!
* \brief Encode the invoke component for a ROSE message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 message.
* \param end End of ASN.1 encoding data buffer.
* \param msg ROSE invoke message to encode.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
unsigned char *rose_encode_invoke(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const struct rose_msg_invoke *msg)
{
const struct rose_convert_msg *convert;
unsigned char *seq_len;
convert = rose_find_msg_by_op_code(ctrl, msg->operation);
if (!convert) {
return NULL;
}
ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_INVOKE);
ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id));
if (msg->linked_id_present) {
ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0,
msg->linked_id));
}
ASN1_CALL(pos, rose_enc_operation_value(pos, end, convert->oid_prefix,
convert->value));
if (convert->encode_invoke_args) {
ASN1_CALL(pos, convert->encode_invoke_args(ctrl, pos, end, &msg->args));
}
ASN1_CONSTRUCTED_END(seq_len, pos, end);
return pos;
}
/*!
* \brief Encode the result component for a ROSE message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 message.
* \param end End of ASN.1 encoding data buffer.
* \param msg ROSE result message to encode.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
unsigned char *rose_encode_result(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const struct rose_msg_result *msg)
{
const struct rose_convert_msg *convert;
unsigned char *seq_len;
unsigned char *op_seq_len;
ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_RESULT);
ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id));
if (msg->operation != ROSE_None) {
convert = rose_find_msg_by_op_code(ctrl, msg->operation);
if (!convert) {
return NULL;
}
ASN1_CONSTRUCTED_BEGIN(op_seq_len, pos, end, ASN1_TYPE_SEQUENCE);
ASN1_CALL(pos, rose_enc_operation_value(pos, end, convert->oid_prefix,
convert->value));
if (convert->encode_result_args) {
ASN1_CALL(pos, convert->encode_result_args(ctrl, pos, end, &msg->args));
}
ASN1_CONSTRUCTED_END(op_seq_len, pos, end);
}
ASN1_CONSTRUCTED_END(seq_len, pos, end);
return pos;
}
/*!
* \brief Encode the error component for a ROSE message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 message.
* \param end End of ASN.1 encoding data buffer.
* \param msg ROSE error message to encode.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
unsigned char *rose_encode_error(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const struct rose_msg_error *msg)
{
const struct rose_convert_error *convert;
unsigned char *seq_len;
convert = rose_find_error_by_op_code(ctrl, msg->code);
if (!convert) {
return NULL;
}
ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_ERROR);
ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id));
ASN1_CALL(pos, rose_enc_error_value(pos, end, convert->oid_prefix, convert->value));
if (convert->encode_error_args) {
ASN1_CALL(pos, convert->encode_error_args(ctrl, pos, end, &msg->args));
}
ASN1_CONSTRUCTED_END(seq_len, pos, end);
return pos;
}
/*!
* \brief Encode the reject component for a ROSE message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 message.
* \param end End of ASN.1 encoding data buffer.
* \param msg ROSE reject message to encode.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
unsigned char *rose_encode_reject(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const struct rose_msg_reject *msg)
{
unsigned char *seq_len;
unsigned tag;
ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_REJECT);
/* Encode Invoke ID */
if (msg->invoke_id_present) {
ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id));
} else {
ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL));
}
/* Encode the reject problem */
switch (msg->code & ~0xFF) {
case ROSE_REJECT_BASE(ROSE_REJECT_BASE_General):
tag = ASN1_CLASS_CONTEXT_SPECIFIC | 0;
break;
case ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke):
tag = ASN1_CLASS_CONTEXT_SPECIFIC | 1;
break;
case ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result):
tag = ASN1_CLASS_CONTEXT_SPECIFIC | 2;
break;
case ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error):
tag = ASN1_CLASS_CONTEXT_SPECIFIC | 3;
break;
default:
return NULL;
}
ASN1_CALL(pos, asn1_enc_int(pos, end, tag, msg->code & 0xFF));
ASN1_CONSTRUCTED_END(seq_len, pos, end);
return pos;
}
/*!
* \brief Encode the ROSE message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 message.
* \param end End of ASN.1 encoding data buffer.
* \param msg ROSE message to encode.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*
* \note This function only encodes the ROSE contents. It does not include
* the protocol profile, NFE, NPP, and interpretation octets defined in
* a facility ie that may precede the ROSE contents. These header octets
* may already be stored in the encompassing buffer before the starting
* position given here.
*/
unsigned char *rose_encode(struct pri *ctrl, unsigned char *pos, unsigned char *end,
const struct rose_message *msg)
{
switch (msg->type) {
case ROSE_COMP_TYPE_INVOKE:
pos = rose_encode_invoke(ctrl, pos, end, &msg->component.invoke);
break;
case ROSE_COMP_TYPE_RESULT:
pos = rose_encode_result(ctrl, pos, end, &msg->component.result);
break;
case ROSE_COMP_TYPE_ERROR:
pos = rose_encode_error(ctrl, pos, end, &msg->component.error);
break;
case ROSE_COMP_TYPE_REJECT:
pos = rose_encode_reject(ctrl, pos, end, &msg->component.reject);
break;
default:
pos = NULL;
break;
}
return pos;
}
/*!
* \internal
* \brief Encode the NetworkFacilityExtension type.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 component.
* \param end End of ASN.1 encoding data buffer.
* \param nfe Network Facility Extension information to encode.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
static unsigned char *fac_enc_nfe(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const struct facNetworkFacilityExtension *nfe)
{
unsigned char *seq_len;
unsigned char *explicit_len;
ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 10);
ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0,
nfe->source_entity));
if (nfe->source_number.length) {
/* EXPLICIT tag */
ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1);
ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &nfe->source_number));
ASN1_CONSTRUCTED_END(explicit_len, pos, end);
}
ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2,
nfe->destination_entity));
if (nfe->destination_number.length) {
/* EXPLICIT tag */
ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3);
ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &nfe->destination_number));
ASN1_CONSTRUCTED_END(explicit_len, pos, end);
}
ASN1_CONSTRUCTED_END(seq_len, pos, end);
return pos;
}
/*!
* \brief Encode the facility extension header.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode ASN.1 component.
* \param end End of ASN.1 encoding data buffer.
* \param header Facility extension information to encode.
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
unsigned char *fac_enc_extension_header(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const struct fac_extension_header *header)
{
if (header->nfe_present) {
ASN1_CALL(pos, fac_enc_nfe(ctrl, pos, end, &header->nfe));
}
if (header->npp_present) {
ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 18,
header->npp));
}
if (header->interpretation_present) {
ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 11,
header->interpretation));
}
return pos;
}
/*!
* \brief Encode the facility ie contents header.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position to encode the facility ie contents.
* \param end End of facility ie contents encoding data buffer.
* \param header Facility extension header data to encode (NULL if none).
*
* \retval Start of the next ASN.1 component to encode on success.
* \retval NULL on error.
*/
unsigned char *facility_encode_header(struct pri *ctrl, unsigned char *pos,
unsigned char *end, const struct fac_extension_header *header)
{
/* Make sure we have some room. */
if (end < pos + 2) {
return NULL;
}
switch (ctrl->switchtype) {
case PRI_SWITCH_EUROISDN_T1:
case PRI_SWITCH_EUROISDN_E1:
*pos++ = 0x80 | Q932_PROTOCOL_ROSE;
header = NULL;
break;
case PRI_SWITCH_QSIG:
*pos++ = 0x80 | Q932_PROTOCOL_EXTENSIONS;
break;
case PRI_SWITCH_DMS100:
*pos++ = Q932_PROTOCOL_ROSE; /* DON'T set the EXT bit yet. */
*pos++ = 0x80 | ROSE_DMS100_RLT_SERVICE_ID;
header = NULL;
break;
case PRI_SWITCH_ATT4ESS:
case PRI_SWITCH_LUCENT5E:
case PRI_SWITCH_NI2:
if (header) {
*pos++ = 0x80 | Q932_PROTOCOL_EXTENSIONS;
} else {
*pos++ = 0x80 | Q932_PROTOCOL_ROSE;
}
break;
default:
return NULL;
}
if (header) {
ASN1_CALL(pos, fac_enc_extension_header(ctrl, pos, end, header));
}
return pos;
}
/*!
* \internal
* \brief Decode the ROSE invoke message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param tag Component tag that identified this structure.
* \param pos Starting position of the ASN.1 component length.
* \param end End of ASN.1 decoding data buffer.
* \param msg ROSE invoke message data to fill.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*/
static const unsigned char *rose_decode_invoke(struct pri *ctrl, unsigned tag,
const unsigned char *pos, const unsigned char *end, struct rose_msg_invoke *msg)
{
int32_t value;
int length;
int seq_offset;
const unsigned char *seq_end;
const struct rose_convert_msg *convert;
struct asn1_oid oid;
unsigned local;
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, "INVOKE Component %s\n", asn1_tag2str(tag));
}
ASN1_CALL(pos, asn1_dec_length(pos, end, &length));
ASN1_END_SETUP(seq_end, seq_offset, length, pos, end);
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER);
ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value));
msg->invoke_id = value;
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | 0)) {
ASN1_CALL(pos, asn1_dec_int(ctrl, "linkedId", tag, pos, seq_end, &value));
msg->linked_id = value;
msg->linked_id_present = 1;
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
} else {
msg->linked_id_present = 0;
}
/* Decode operation-value */
switch (tag) {
case ASN1_TYPE_INTEGER:
ASN1_CALL(pos, asn1_dec_int(ctrl, "operationValue", tag, pos, seq_end, &value));
local = value;
oid.num_values = 0;
break;
case ASN1_TYPE_OBJECT_IDENTIFIER:
ASN1_CALL(pos, asn1_dec_oid(ctrl, "operationValue", tag, pos, seq_end, &oid));
local = 0;
break;
default:
ASN1_DID_NOT_EXPECT_TAG(ctrl, tag);
return NULL;
}
convert = rose_find_msg_by_op_val(ctrl, (oid.num_values == 0) ? NULL : &oid, local);
if (convert) {
msg->operation = convert->operation;
} else {
msg->operation = ROSE_Unknown;
}
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, " operationValue = %s\n", rose_operation2str(msg->operation));
}
/* Decode any expected invoke arguments */
if (convert && convert->decode_invoke_args) {
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
ASN1_CALL(pos, convert->decode_invoke_args(ctrl, tag, pos, seq_end, &msg->args));
}
ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end);
return pos;
}
/*!
* \internal
* \brief Decode the ROSE result message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param tag Component tag that identified this structure.
* \param pos Starting position of the ASN.1 component length.
* \param end End of ASN.1 decoding data buffer.
* \param msg ROSE result message data to fill.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*/
static const unsigned char *rose_decode_result(struct pri *ctrl, unsigned tag,
const unsigned char *pos, const unsigned char *end, struct rose_msg_result *msg)
{
int32_t value;
int length;
int seq_offset;
int op_seq_offset;
const unsigned char *seq_end;
const unsigned char *op_seq_end;
const struct rose_convert_msg *convert;
struct asn1_oid oid;
unsigned local;
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, "RESULT Component %s\n", asn1_tag2str(tag));
}
ASN1_CALL(pos, asn1_dec_length(pos, end, &length));
ASN1_END_SETUP(seq_end, seq_offset, length, pos, end);
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER);
ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value));
msg->invoke_id = value;
/* Decode optional operation sequence */
if (pos < seq_end && *pos != ASN1_INDEF_TERM) {
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE);
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, " operation %s\n", asn1_tag2str(tag));
}
ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length));
ASN1_END_SETUP(op_seq_end, op_seq_offset, length, pos, seq_end);
/* Decode operation-value */
ASN1_CALL(pos, asn1_dec_tag(pos, op_seq_end, &tag));
switch (tag) {
case ASN1_TYPE_INTEGER:
ASN1_CALL(pos, asn1_dec_int(ctrl, "operationValue", tag, pos, op_seq_end,
&value));
local = value;
oid.num_values = 0;
break;
case ASN1_TYPE_OBJECT_IDENTIFIER:
ASN1_CALL(pos, asn1_dec_oid(ctrl, "operationValue", tag, pos, op_seq_end,
&oid));
local = 0;
break;
default:
ASN1_DID_NOT_EXPECT_TAG(ctrl, tag);
return NULL;
}
convert =
rose_find_msg_by_op_val(ctrl, (oid.num_values == 0) ? NULL : &oid, local);
if (convert) {
msg->operation = convert->operation;
} else {
msg->operation = ROSE_Unknown;
}
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, " operationValue = %s\n",
rose_operation2str(msg->operation));
}
/* Decode any expected result arguments */
if (convert && convert->decode_result_args) {
ASN1_CALL(pos, asn1_dec_tag(pos, op_seq_end, &tag));
ASN1_CALL(pos, convert->decode_result_args(ctrl, tag, pos, op_seq_end,
&msg->args));
}
ASN1_END_FIXUP(ctrl, pos, op_seq_offset, op_seq_end, seq_end);
} else {
msg->operation = ROSE_None;
}
ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end);
return pos;
}
/*!
* \internal
* \brief Decode the ROSE error message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param tag Component tag that identified this structure.
* \param pos Starting position of the ASN.1 component length.
* \param end End of ASN.1 decoding data buffer.
* \param msg ROSE error message data to fill.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*/
static const unsigned char *rose_decode_error(struct pri *ctrl, unsigned tag,
const unsigned char *pos, const unsigned char *end, struct rose_msg_error *msg)
{
int32_t value;
int length;
int seq_offset;
const unsigned char *seq_end;
const struct rose_convert_error *convert;
struct asn1_oid oid;
unsigned local;
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, "ERROR Component %s\n", asn1_tag2str(tag));
}
ASN1_CALL(pos, asn1_dec_length(pos, end, &length));
ASN1_END_SETUP(seq_end, seq_offset, length, pos, end);
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER);
ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value));
msg->invoke_id = value;
/* Decode error-value */
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
switch (tag) {
case ASN1_TYPE_INTEGER:
ASN1_CALL(pos, asn1_dec_int(ctrl, "errorValue", tag, pos, seq_end, &value));
local = value;
oid.num_values = 0;
break;
case ASN1_TYPE_OBJECT_IDENTIFIER:
ASN1_CALL(pos, asn1_dec_oid(ctrl, "errorValue", tag, pos, seq_end, &oid));
local = 0;
break;
default:
ASN1_DID_NOT_EXPECT_TAG(ctrl, tag);
return NULL;
}
convert =
rose_find_error_by_op_val(ctrl, (oid.num_values == 0) ? NULL : &oid, local);
if (convert) {
msg->code = convert->code;
} else {
msg->code = ROSE_ERROR_Unknown;
}
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, " errorValue = %s\n", rose_error2str(msg->code));
}
/* Decode any expected error parameters */
if (convert && convert->decode_error_args) {
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
ASN1_CALL(pos, convert->decode_error_args(ctrl, tag, pos, seq_end, &msg->args));
}
ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end);
return pos;
}
/*!
* \internal
* \brief Decode the ROSE reject message.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param tag Component tag that identified this structure.
* \param pos Starting position of the ASN.1 component length.
* \param end End of ASN.1 decoding data buffer.
* \param msg ROSE reject message data to fill.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*/
static const unsigned char *rose_decode_reject(struct pri *ctrl, unsigned tag,
const unsigned char *pos, const unsigned char *end, struct rose_msg_reject *msg)
{
int32_t value;
int length;
int seq_offset;
const unsigned char *seq_end;
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, "REJECT Component %s\n", asn1_tag2str(tag));
}
ASN1_CALL(pos, asn1_dec_length(pos, end, &length));
ASN1_END_SETUP(seq_end, seq_offset, length, pos, end);
/* Invoke ID choice */
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
switch (tag) {
case ASN1_TYPE_INTEGER:
ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value));
msg->invoke_id = value;
msg->invoke_id_present = 1;
break;
case ASN1_TYPE_NULL:
ASN1_CALL(pos, asn1_dec_null(ctrl, "invokeId", tag, pos, seq_end));
msg->invoke_id_present = 0;
break;
default:
ASN1_DID_NOT_EXPECT_TAG(ctrl, tag);
return NULL;
}
/* Problem choice */
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
switch (tag) {
case ASN1_CLASS_CONTEXT_SPECIFIC | 0:
ASN1_CALL(pos, asn1_dec_int(ctrl, "problemGeneral", tag, pos, seq_end, &value));
msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_General) | (value & 0xFF);
break;
case ASN1_CLASS_CONTEXT_SPECIFIC | 1:
ASN1_CALL(pos, asn1_dec_int(ctrl, "problemInvoke", tag, pos, seq_end, &value));
msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) | (value & 0xFF);
break;
case ASN1_CLASS_CONTEXT_SPECIFIC | 2:
ASN1_CALL(pos, asn1_dec_int(ctrl, "problemResult", tag, pos, seq_end, &value));
msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result) | (value & 0xFF);
break;
case ASN1_CLASS_CONTEXT_SPECIFIC | 3:
ASN1_CALL(pos, asn1_dec_int(ctrl, "problemError", tag, pos, seq_end, &value));
msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) | (value & 0xFF);
break;
default:
ASN1_DID_NOT_EXPECT_TAG(ctrl, tag);
return NULL;
}
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, " problem = %s\n", rose_reject2str(msg->code));
}
ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end);
return pos;
}
/*!
* \brief Decode the ROSE message into the given buffer.
*
* \param ctrl D channel controller for diagnostic messages or global options.
* \param pos Starting position of the ASN.1 component.
* \param end End of ASN.1 decoding data buffer.
* \param msg Decoded ROSE message contents.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*
* \note This function only decodes the ROSE contents. It does not check
* for the protocol profile, NFE, NPP, and interpretation octets defined in
* a facility ie that may preceed the ROSE contents. These header octets
* may already have been consumed from the encompasing buffer before the
* buffer given here.
*/
const unsigned char *rose_decode(struct pri *ctrl, const unsigned char *pos,
const unsigned char *end, struct rose_message *msg)
{
unsigned tag;
ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag));
switch (tag) {
case ROSE_TAG_COMPONENT_INVOKE:
msg->type = ROSE_COMP_TYPE_INVOKE;
ASN1_CALL(pos, rose_decode_invoke(ctrl, tag, pos, end, &msg->component.invoke));
break;
case ROSE_TAG_COMPONENT_RESULT:
msg->type = ROSE_COMP_TYPE_RESULT;
ASN1_CALL(pos, rose_decode_result(ctrl, tag, pos, end, &msg->component.result));
break;
case ROSE_TAG_COMPONENT_ERROR:
msg->type = ROSE_COMP_TYPE_ERROR;
ASN1_CALL(pos, rose_decode_error(ctrl, tag, pos, end, &msg->component.error));
break;
case ROSE_TAG_COMPONENT_REJECT:
msg->type = ROSE_COMP_TYPE_REJECT;
ASN1_CALL(pos, rose_decode_reject(ctrl, tag, pos, end, &msg->component.reject));
break;
default:
msg->type = ROSE_COMP_TYPE_INVALID;
ASN1_DID_NOT_EXPECT_TAG(ctrl, tag);
return NULL;
}
if (pos < end) {
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, " %u byte(s) of trailing data not consumed.\n",
(unsigned) (end - pos));
}
}
return pos;
}
/*!
* \internal
* \brief Decode the NetworkFacilityExtension argument parameters.
*
* \param ctrl D channel controller for any diagnostic messages.
* \param name Field name
* \param tag Component tag that identified this production.
* \param pos Starting position of the ASN.1 component length.
* \param end End of ASN.1 decoding data buffer.
* \param nfe Parameter storage to fill.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*/
static const unsigned char *fac_dec_nfe(struct pri *ctrl, const char *name, unsigned tag,
const unsigned char *pos, const unsigned char *end,
struct facNetworkFacilityExtension *nfe)
{
int length;
int seq_offset;
int explicit_offset;
const unsigned char *seq_end;
const unsigned char *explicit_end;
const unsigned char *save_pos;
int32_t value;
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, " %s NetworkFacilityExtension %s\n", name, asn1_tag2str(tag));
}
ASN1_CALL(pos, asn1_dec_length(pos, end, &length));
ASN1_END_SETUP(seq_end, seq_offset, length, pos, end);
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 0);
ASN1_CALL(pos, asn1_dec_int(ctrl, "sourceEntity", tag, pos, seq_end, &value));
nfe->source_entity = value;
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1)) {
/* Remove EXPLICIT tag */
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag));
}
ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length));
ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end);
ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag));
ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "sourceEntityAddress", tag, pos,
seq_end, &nfe->source_number));
ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end);
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
} else {
nfe->source_number.length = 0;
}
ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2);
ASN1_CALL(pos, asn1_dec_int(ctrl, "destinationEntity", tag, pos, seq_end, &value));
nfe->destination_entity = value;
nfe->destination_number.length = 0;
if (pos < seq_end && *pos != ASN1_INDEF_TERM) {
save_pos = pos;
ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag));
if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3)) {
/* Remove EXPLICIT tag */
if (ctrl->debug & PRI_DEBUG_APDU) {
pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag));
}
ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length));
ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end);
ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag));
ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "destinationEntityAddress", tag,
pos, seq_end, &nfe->destination_number));
ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end);
} else {
pos = save_pos;
}
}
ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end);
return pos;
}
/*!
* \brief Decode the extension header argument parameters.
*
* \param ctrl D channel controller for any diagnostic messages.
* \param pos Starting position of the ASN.1 component.
* \param end End of ASN.1 decoding data buffer.
* \param header Parameter storage to fill.
*
* \retval Start of the next ASN.1 component on success.
* \retval NULL on error.
*/
const unsigned char *fac_dec_extension_header(struct pri *ctrl, const unsigned char *pos,
const unsigned char *end, struct fac_extension_header *header)
{
int32_t value;
unsigned tag;
const unsigned char *save_pos;
/*
* For simplicity we are not checking the order of
* the optional header components.
*/
header->nfe_present = 0;
header->npp_present = 0;
header->interpretation_present = 0;
while (pos < end) {
save_pos = pos;
ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag));
switch (tag) {
case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 10:
ASN1_CALL(pos, fac_dec_nfe(ctrl, "nfe", tag, pos, end, &header->nfe));
header->nfe_present = 1;
break;
case ASN1_CLASS_CONTEXT_SPECIFIC | 18:
ASN1_CALL(pos, asn1_dec_int(ctrl, "networkProtocolProfile", tag, pos, end,
&value));
header->npp = value;
header->npp_present = 1;
break;
case ASN1_CLASS_CONTEXT_SPECIFIC | 11:
ASN1_CALL(pos, asn1_dec_int(ctrl, "interpretation", tag, pos, end, &value));
header->interpretation = value;
header->interpretation_present = 1;
break;
default:
pos = save_pos;
goto cancel_options;
}
}
cancel_options:;
return pos;
}
/*!
* \brief Decode the facility ie contents header.
*
* \param ctrl D channel controller for any diagnostic messages.
* \param pos Starting position of the facility ie contents.
* \param end End of facility ie contents.
* \param header Parameter storage to fill.
*
* \retval Start of the next ASN.1 component on success (ROSE message).
* \retval NULL on error.
*/
const unsigned char *facility_decode_header(struct pri *ctrl, const unsigned char *pos,
const unsigned char *end, struct fac_extension_header *header)
{
/* Make sure we have enough room for the protocol profile ie octet(s) */
if (end < pos + 2) {
return NULL;
}
switch (*pos & Q932_PROTOCOL_MASK) {
case Q932_PROTOCOL_ROSE:
case Q932_PROTOCOL_EXTENSIONS:
break;
default:
return NULL;
}
if (!(*pos & 0x80)) {
/* DMS-100 Service indicator octet - Just ignore for now */
++pos;
}
++pos;
if (ctrl->debug & PRI_DEBUG_APDU) {
asn1_dump(ctrl, pos, end);
}
pos = fac_dec_extension_header(ctrl, pos, end, header);
return pos;
}
/*!
* \brief Decode the facility ie contents for debug purposes.
*
* \param ctrl D channel controller for any diagnostic messages.
* \param buf Buffer containing the facility ie contents.
* \param length Length of facility ie contents.
*
* \return Nothing
*
* \note Should only be called if PRI_DEBUG_APDU is enabled. Otherwise,
* it it does nothing useful.
*/
void facility_decode_dump(struct pri *ctrl, const unsigned char *buf, size_t length)
{
const unsigned char *pos;
const unsigned char *end;
union {
struct fac_extension_header header;
struct rose_message rose;
} discard;
end = buf + length;
pos = facility_decode_header(ctrl, buf, end, &discard.header);
while (pos && pos < end) {
pos = rose_decode(ctrl, pos, end, &discard.rose);
}
}
/* ------------------------------------------------------------------- */
/* end rose.c */