=================================================================== --- a/pri_timers.h (.../tags/1.4.10.2) (revision 1357) +++ b/pri_timers.h (.../branches/1.4) (revision 1357) @@ -1,97 +0,0 @@ -/* - * libpri: An implementation of Primary Rate ISDN - * - * Written by Mark Spencer - * - * Copyright (C) 2001, Digium, Inc. - * All Rights Reserved. - */ - -/* - * 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. - */ - -#ifndef _PRI_TIMERS_H -#define _PRI_TIMERS_H - -/* -1 means we dont currently support the timer/counter */ -#define PRI_TIMERS_DEFAULT { \ - 3, /* N200 */ \ - -1, /* N201 */ \ - 3, /* N202 */ \ - 7, /* K */ \ - 1000, /* T200 */ \ - -1, /* T201 */ \ - 10000, /* T202 */ \ - 10000, /* T203 */ \ - -1, /* T300 */ \ - -1, /* T301 */ \ - -1, /* T302 */ \ - -1, /* T303 */ \ - -1, /* T304 */ \ - 30000, /* T305 */ \ - -1, /* T306 */ \ - -1, /* T307 */ \ - 4000, /* T308 */ \ - -1, /* T309 */ \ - -1, /* T310 */ \ - 4000, /* T313 */ \ - -1, /* T314 */ \ - -1, /* T316 */ \ - -1, /* T317 */ \ - -1, /* T318 */ \ - -1, /* T319 */ \ - -1, /* T320 */ \ - -1, /* T321 */ \ - -1, /* T322 */ \ - 2500, /* TM20 - Q.921 Appendix IV */ \ - 3, /* NM20 - Q.921 Appendix IV */ \ - } - -/* XXX Only our default timers are setup now XXX */ -#define PRI_TIMERS_UNKNOWN PRI_TIMERS_DEFAULT -#define PRI_TIMERS_NI2 PRI_TIMERS_DEFAULT -#define PRI_TIMERS_DMS100 PRI_TIMERS_DEFAULT -#define PRI_TIMERS_LUCENT5E PRI_TIMERS_DEFAULT -#define PRI_TIMERS_ATT4ESS PRI_TIMERS_DEFAULT -#define PRI_TIMERS_EUROISDN_E1 PRI_TIMERS_DEFAULT -#define PRI_TIMERS_EUROISDN_T1 PRI_TIMERS_DEFAULT -#define PRI_TIMERS_NI1 PRI_TIMERS_DEFAULT -#define PRI_TIMERS_GR303_EOC PRI_TIMERS_DEFAULT -#define PRI_TIMERS_GR303_TMC PRI_TIMERS_DEFAULT -#define PRI_TIMERS_QSIG PRI_TIMERS_DEFAULT -#define __PRI_TIMERS_GR303_EOC_INT PRI_TIMERS_DEFAULT -#define __PRI_TIMERS_GR303_TMC_INT PRI_TIMERS_DEFAULT - -#define PRI_TIMERS_ALL { PRI_TIMERS_UNKNOWN, \ - PRI_TIMERS_NI2, \ - PRI_TIMERS_DMS100, \ - PRI_TIMERS_LUCENT5E, \ - PRI_TIMERS_ATT4ESS, \ - PRI_TIMERS_EUROISDN_E1, \ - PRI_TIMERS_EUROISDN_T1, \ - PRI_TIMERS_NI1, \ - PRI_TIMERS_QSIG, \ - PRI_TIMERS_GR303_EOC, \ - PRI_TIMERS_GR303_TMC, \ - __PRI_TIMERS_GR303_EOC_INT, \ - __PRI_TIMERS_GR303_TMC_INT, \ - } - -#endif Index: .version =================================================================== --- a/.version (.../tags/1.4.10.2) (revision 1357) +++ b/.version (.../branches/1.4) (revision 1357) @@ -1 +1 @@ -1.4.10.2 +1.4.10.2-r1357 Index: prisched.c =================================================================== --- a/prisched.c (.../tags/1.4.10.2) (revision 1357) +++ b/prisched.c (.../branches/1.4) (revision 1357) @@ -33,25 +33,43 @@ #include "pri_internal.h" +/*! \brief The maximum number of timers that were active at once. */ static int maxsched = 0; /* Scheduler routines */ -int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data) + +/*! + * \brief Start a timer to schedule an event. + * + * \param ctrl D channel controller. + * \param ms Number of milliseconds to scheduled event. + * \param function Callback function to call when timeout. + * \param data Value to give callback function when timeout. + * + * \retval 0 if scheduler table is full and could not schedule the event. + * \retval id Scheduled event id. + */ +int pri_schedule_event(struct pri *ctrl, int ms, void (*function)(void *data), void *data) { int x; struct timeval tv; + /* Scheduling runs on master channels only */ - while (pri->master) - pri = pri->master; - for (x=1;xpri_sched[x].callback) + while (ctrl->master) { + ctrl = ctrl->master; + } + for (x = 0; x < MAX_SCHED; ++x) { + if (!ctrl->pri_sched[x].callback) { break; + } + } if (x == MAX_SCHED) { - pri_error(pri, "No more room in scheduler\n"); - return -1; + pri_error(ctrl, "No more room in scheduler\n"); + return 0; } - if (x > maxsched) - maxsched = x; + if (x >= maxsched) { + maxsched = x + 1; + } gettimeofday(&tv, NULL); tv.tv_sec += ms / 1000; tv.tv_usec += (ms % 1000) * 1000; @@ -59,71 +77,110 @@ tv.tv_usec -= 1000000; tv.tv_sec += 1; } - pri->pri_sched[x].when = tv; - pri->pri_sched[x].callback = function; - pri->pri_sched[x].data = data; - return x; + ctrl->pri_sched[x].when = tv; + ctrl->pri_sched[x].callback = function; + ctrl->pri_sched[x].data = data; + return x + 1; } -struct timeval *pri_schedule_next(struct pri *pri) +/*! + * \brief Determine the time of the next scheduled event to expire. + * + * \param ctrl D channel controller. + * + * \return Time of the next scheduled event to expire or NULL if no timers active. + */ +struct timeval *pri_schedule_next(struct pri *ctrl) { struct timeval *closest = NULL; int x; - /* Check subchannels */ - if (pri->subchannel) - closest = pri_schedule_next(pri->subchannel); - for (x=1;xpri_sched[x].callback && - (!closest || (closest->tv_sec > pri->pri_sched[x].when.tv_sec) || - ((closest->tv_sec == pri->pri_sched[x].when.tv_sec) && - (closest->tv_usec > pri->pri_sched[x].when.tv_usec)))) - closest = &pri->pri_sched[x].when; + + /* Scheduling runs on master channels only */ + while (ctrl->master) { + ctrl = ctrl->master; } + for (x = 0; x < MAX_SCHED; ++x) { + if (ctrl->pri_sched[x].callback && (!closest + || (closest->tv_sec > ctrl->pri_sched[x].when.tv_sec) + || ((closest->tv_sec == ctrl->pri_sched[x].when.tv_sec) + && (closest->tv_usec > ctrl->pri_sched[x].when.tv_usec)))) { + closest = &ctrl->pri_sched[x].when; + } + } return closest; } -static pri_event *__pri_schedule_run(struct pri *pri, struct timeval *tv) +/*! + * \internal + * \brief Run all expired timers or return an event generated by an expired timer. + * + * \param ctrl D channel controller. + * \param tv Current time. + * + * \return Event for upper layer to process or NULL if all expired timers run. + */ +static pri_event *__pri_schedule_run(struct pri *ctrl, struct timeval *tv) { int x; void (*callback)(void *); void *data; - pri_event *e; - if (pri->subchannel) { - if ((e = __pri_schedule_run(pri->subchannel, tv))) { - return e; + + /* Scheduling runs on master channels only */ + while (ctrl->master) { + ctrl = ctrl->master; + } + for (x = 0; x < MAX_SCHED; ++x) { + if (ctrl->pri_sched[x].callback && ((ctrl->pri_sched[x].when.tv_sec < tv->tv_sec) + || ((ctrl->pri_sched[x].when.tv_sec == tv->tv_sec) + && (ctrl->pri_sched[x].when.tv_usec <= tv->tv_usec)))) { + /* This timer has expired. */ + ctrl->schedev = 0; + callback = ctrl->pri_sched[x].callback; + data = ctrl->pri_sched[x].data; + ctrl->pri_sched[x].callback = NULL; + callback(data); + if (ctrl->schedev) { + return &ctrl->ev; + } } } - for (x=1;xpri_sched[x].callback && - ((pri->pri_sched[x].when.tv_sec < tv->tv_sec) || - ((pri->pri_sched[x].when.tv_sec == tv->tv_sec) && - (pri->pri_sched[x].when.tv_usec <= tv->tv_usec)))) { - pri->schedev = 0; - callback = pri->pri_sched[x].callback; - data = pri->pri_sched[x].data; - pri->pri_sched[x].callback = NULL; - pri->pri_sched[x].data = NULL; - callback(data); - if (pri->schedev) - return &pri->ev; - } - } return NULL; } -pri_event *pri_schedule_run(struct pri *pri) +/*! + * \brief Run all expired timers or return an event generated by an expired timer. + * + * \param ctrl D channel controller. + * + * \return Event for upper layer to process or NULL if all expired timers run. + */ +pri_event *pri_schedule_run(struct pri *ctrl) { struct timeval tv; + gettimeofday(&tv, NULL); - return __pri_schedule_run(pri, &tv); + return __pri_schedule_run(ctrl, &tv); } - -void pri_schedule_del(struct pri *pri,int id) +/*! + * \brief Delete a scheduled event. + * + * \param ctrl D channel controller. + * \param id Scheduled event id to delete. + * 0 is a disabled/unscheduled event id that is ignored. + * 1 - MAX_SCHED is a valid event id. + * + * \return Nothing + */ +void pri_schedule_del(struct pri *ctrl, int id) { - while (pri->master) - pri = pri->master; - if ((id >= MAX_SCHED) || (id < 0)) - pri_error(pri, "Asked to delete sched id %d???\n", id); - pri->pri_sched[id].callback = NULL; + /* Scheduling runs on master channels only */ + while (ctrl->master) { + ctrl = ctrl->master; + } + if (0 < id && id <= MAX_SCHED) { + ctrl->pri_sched[id - 1].callback = NULL; + } else if (id) { + pri_error(ctrl, "Asked to delete sched id %d???\n", id); + } } Index: rose_qsig_mwi.c =================================================================== --- a/rose_qsig_mwi.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_qsig_mwi.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,790 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 Q.SIG ROSE SS-MWI-Operations + * + * SS-MWI-Operations ECMA-242 Annex E Table E.1 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the MsgCentreId 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 msg_centre_id + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_MsgCentreId(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct roseQsigMsgCentreId *msg_centre_id) +{ + unsigned char *seq_len; + + switch (msg_centre_id->type) { + case 0: /* integer */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + msg_centre_id->u.integer)); + break; + case 1: /* partyNumber */ + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &msg_centre_id->u.number)); + ASN1_CONSTRUCTED_END(seq_len, pos, end); + break; + case 2: /* numericString */ + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + msg_centre_id->u.str, sizeof(msg_centre_id->u.str) - 1)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown MsgCentreId type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the MWIActivate invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_MWIActivate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigMWIActivateArg *mwi_activate; + unsigned char *seq_len; + unsigned char *explicit_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + mwi_activate = &args->qsig.MWIActivate; + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &mwi_activate->served_user_number)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + mwi_activate->basic_service)); + if (mwi_activate->msg_centre_id_present) { + ASN1_CALL(pos, rose_enc_qsig_MsgCentreId(ctrl, pos, end, + &mwi_activate->msg_centre_id)); + } + if (mwi_activate->number_of_messages_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, + mwi_activate->number_of_messages)); + } + if (mwi_activate->originating_number.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &mwi_activate->originating_number)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (mwi_activate->timestamp_present) { + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_GENERALIZED_TIME, + mwi_activate->timestamp, sizeof(mwi_activate->timestamp) - 1)); + } + if (mwi_activate->priority_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5, + mwi_activate->priority)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the MWIDeactivate invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_MWIDeactivate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigMWIDeactivateArg *mwi_deactivate; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + mwi_deactivate = &args->qsig.MWIDeactivate; + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &mwi_deactivate->served_user_number)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + mwi_deactivate->basic_service)); + if (mwi_deactivate->msg_centre_id_present) { + ASN1_CALL(pos, rose_enc_qsig_MsgCentreId(ctrl, pos, end, + &mwi_deactivate->msg_centre_id)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the MWIInterrogate invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_MWIInterrogate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigMWIInterrogateArg *mwi_interrogate; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + mwi_interrogate = &args->qsig.MWIInterrogate; + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &mwi_interrogate->served_user_number)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + mwi_interrogate->basic_service)); + if (mwi_interrogate->msg_centre_id_present) { + ASN1_CALL(pos, rose_enc_qsig_MsgCentreId(ctrl, pos, end, + &mwi_interrogate->msg_centre_id)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the MWIInterrogateResElt 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param record + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_MWIInterrogateResElt(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseQsigMWIInterrogateResElt *record) +{ + unsigned char *seq_len; + unsigned char *explicit_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, record->basic_service)); + if (record->msg_centre_id_present) { + ASN1_CALL(pos, rose_enc_qsig_MsgCentreId(ctrl, pos, end, + &record->msg_centre_id)); + } + if (record->number_of_messages_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, + record->number_of_messages)); + } + if (record->originating_number.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &record->originating_number)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (record->timestamp_present) { + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_GENERALIZED_TIME, + record->timestamp, sizeof(record->timestamp) - 1)); + } + if (record->priority_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5, + record->priority)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the MWIInterrogate result facility ie 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. + */ +unsigned char *rose_enc_qsig_MWIInterrogate_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + unsigned index; + unsigned char *seq_len; + const struct roseQsigMWIInterrogateRes *mwi_interrogate; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + mwi_interrogate = &args->qsig.MWIInterrogate; + for (index = 0; index < mwi_interrogate->num_records; ++index) { + ASN1_CALL(pos, rose_enc_qsig_MWIInterrogateResElt(ctrl, pos, end, + ASN1_TAG_SEQUENCE, &mwi_interrogate->list[index])); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the MsgCentreId 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 msg_centre_id Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_MsgCentreId(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigMsgCentreId *msg_centre_id) +{ + int32_t value; + size_t str_len; + int length; + int explicit_offset; + const unsigned char *explicit_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s MsgCentreId\n", name); + } + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + msg_centre_id->type = 0; /* integer */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "integer", tag, pos, end, &value)); + msg_centre_id->u.integer = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + msg_centre_id->type = 1; /* partyNumber */ + + /* 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, end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "partyNumber", tag, pos, explicit_end, + &msg_centre_id->u.number)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + msg_centre_id->type = 2; /* numericString */ + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "numericString", tag, pos, end, + sizeof(msg_centre_id->u.str), msg_centre_id->u.str, &str_len)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the Q.SIG MWIActivate invoke argument 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. + */ +const unsigned char *rose_dec_qsig_MWIActivate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + size_t str_len; + int length; + int seq_offset; + int explicit_offset; + const unsigned char *explicit_end; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigMWIActivateArg *mwi_activate; + + mwi_activate = &args->qsig.MWIActivate; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " MWIActivateArg %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_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, + &mwi_activate->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + mwi_activate->basic_service = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + mwi_activate->msg_centre_id_present = 0; + mwi_activate->number_of_messages_present = 0; + mwi_activate->originating_number.length = 0; + mwi_activate->timestamp_present = 0; + mwi_activate->priority_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag & ~ASN1_PC_MASK) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + ASN1_CALL(pos, rose_dec_qsig_MsgCentreId(ctrl, "msgCentreId", tag, pos, + seq_end, &mwi_activate->msg_centre_id)); + mwi_activate->msg_centre_id_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + /* Must not be constructed but we will not check for it for simplicity. */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "nbOfMessages", tag, pos, seq_end, + &value)); + mwi_activate->number_of_messages = value; + mwi_activate->number_of_messages_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + /* Must be constructed but we will not check for it for simplicity. */ + /* 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, "originatingNr", tag, pos, + explicit_end, &mwi_activate->originating_number)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_TYPE_GENERALIZED_TIME: + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "timestamp", tag, pos, end, + sizeof(mwi_activate->timestamp), mwi_activate->timestamp, &str_len)); + mwi_activate->timestamp_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 5: + /* Must not be constructed but we will not check for it for simplicity. */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "priority", tag, pos, seq_end, &value)); + mwi_activate->priority = value; + mwi_activate->priority_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 6: + case ASN1_CLASS_CONTEXT_SPECIFIC | 7: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " argumentExt %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG MWIDeactivate invoke argument 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. + */ +const unsigned char *rose_dec_qsig_MWIDeactivate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigMWIDeactivateArg *mwi_deactivate; + + mwi_deactivate = &args->qsig.MWIDeactivate; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " MWIDeactivateArg %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_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, + &mwi_deactivate->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + mwi_deactivate->basic_service = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + mwi_deactivate->msg_centre_id_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag & ~ASN1_PC_MASK) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + ASN1_CALL(pos, rose_dec_qsig_MsgCentreId(ctrl, "msgCentreId", tag, pos, + seq_end, &mwi_deactivate->msg_centre_id)); + mwi_deactivate->msg_centre_id_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " argumentExt %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG MWIInterrogate invoke argument 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. + */ +const unsigned char *rose_dec_qsig_MWIInterrogate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigMWIInterrogateArg *mwi_interrogate; + + mwi_interrogate = &args->qsig.MWIInterrogate; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " MWIInterrogateArg %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_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, + &mwi_interrogate->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + mwi_interrogate->basic_service = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + mwi_interrogate->msg_centre_id_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag & ~ASN1_PC_MASK) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + ASN1_CALL(pos, rose_dec_qsig_MsgCentreId(ctrl, "msgCentreId", tag, pos, + seq_end, &mwi_interrogate->msg_centre_id)); + mwi_interrogate->msg_centre_id_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " argumentExt %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the MWIInterrogateResElt 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 record Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_MWIInterrogateResElt(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigMWIInterrogateResElt *record) +{ + int32_t value; + size_t str_len; + int length; + int seq_offset; + int explicit_offset; + const unsigned char *explicit_end; + const unsigned char *seq_end; + const unsigned char *save_pos; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " MWIInterrogateResElt %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_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + record->basic_service = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + record->msg_centre_id_present = 0; + record->number_of_messages_present = 0; + record->originating_number.length = 0; + record->timestamp_present = 0; + record->priority_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag & ~ASN1_PC_MASK) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + ASN1_CALL(pos, rose_dec_qsig_MsgCentreId(ctrl, "msgCentreId", tag, pos, + seq_end, &record->msg_centre_id)); + record->msg_centre_id_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + /* Must not be constructed but we will not check for it for simplicity. */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "nbOfMessages", tag, pos, seq_end, + &value)); + record->number_of_messages = value; + record->number_of_messages_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + /* Must be constructed but we will not check for it for simplicity. */ + /* 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, "originatingNr", tag, pos, + explicit_end, &record->originating_number)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_TYPE_GENERALIZED_TIME: + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "timestamp", tag, pos, end, + sizeof(record->timestamp), record->timestamp, &str_len)); + record->timestamp_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 5: + /* Must not be constructed but we will not check for it for simplicity. */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "priority", tag, pos, seq_end, &value)); + record->priority = value; + record->priority_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 6: + case ASN1_CLASS_CONTEXT_SPECIFIC | 7: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " argumentExt %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG MWIInterrogate result argument 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. + */ +const unsigned char *rose_dec_qsig_MWIInterrogate_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigMWIInterrogateRes *mwi_interrogate; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " MWIInterrogateRes %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + mwi_interrogate = &args->qsig.MWIInterrogate; + + mwi_interrogate->num_records = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + if (mwi_interrogate->num_records < ARRAY_LEN(mwi_interrogate->list)) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_qsig_MWIInterrogateResElt(ctrl, "listEntry", tag, + pos, seq_end, &mwi_interrogate->list[mwi_interrogate->num_records])); + ++mwi_interrogate->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + + +/* ------------------------------------------------------------------- */ +/* end rose_qsig_mwi.c */ Property changes on: rose_qsig_mwi.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rosetest.c =================================================================== --- a/rosetest.c (.../tags/1.4.10.2) (revision 0) +++ b/rosetest.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,2473 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 ROSE encode/decode test program + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" + +#include +#include + + +/* ------------------------------------------------------------------- */ + + +static const struct fac_extension_header fac_headers[] = { +/* *INDENT-OFF* */ + { + .nfe_present = 0, + }, + { + .nfe_present = 1, + .nfe.source_entity = 1, + .nfe.destination_entity = 1, + }, + { + .nfe_present = 1, + .nfe.source_entity = 1, + .nfe.source_number.plan = 4, + .nfe.source_number.length = 4, + .nfe.source_number.str = "9834", + .nfe.destination_entity = 1, + .nfe.destination_number.plan = 4, + .nfe.destination_number.length = 4, + .nfe.destination_number.str = "9834", + }, + { + .nfe_present = 1, + .nfe.source_entity = 1, + .nfe.destination_entity = 1, + .npp_present = 1, + .npp = 19, + .interpretation_present = 1, + .interpretation = 2, + }, +/* *INDENT-ON* */ +}; + + +static const struct rose_message rose_etsi_msgs[] = { +/* *INDENT-OFF* */ + /* Error messages */ + { + .type = ROSE_COMP_TYPE_ERROR, + .component.error.invoke_id = 82, + .component.error.code = ROSE_ERROR_Div_SpecialServiceNr, + }, + { + .type = ROSE_COMP_TYPE_ERROR, + .component.error.invoke_id = 8, + .component.error.code = ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, + }, + + /* Reject messages */ + { + .type = ROSE_COMP_TYPE_REJECT, + .component.reject.code = ROSE_REJECT_Gen_BadlyStructuredComponent, + }, + { + .type = ROSE_COMP_TYPE_REJECT, + .component.reject.invoke_id_present = 1, + .component.reject.invoke_id = 10, + .component.reject.code = ROSE_REJECT_Inv_InitiatorReleasing, + }, + { + .type = ROSE_COMP_TYPE_REJECT, + .component.reject.invoke_id_present = 1, + .component.reject.invoke_id = 11, + .component.reject.code = ROSE_REJECT_Res_MistypedResult, + }, + { + .type = ROSE_COMP_TYPE_REJECT, + .component.reject.invoke_id_present = 1, + .component.reject.invoke_id = 12, + .component.reject.code = ROSE_REJECT_Err_ErrorResponseUnexpected, + }, + + /* Anonymous result or result without any arguments. */ + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_None, + .component.result.invoke_id = 9, + }, + + /* Advice Of Charge (AOC) */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_ChargingRequest, + .component.invoke.invoke_id = 98, + .component.invoke.args.etsi.ChargingRequest.charging_case = 2, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ChargingRequest, + .component.result.invoke_id = 99, + .component.result.args.etsi.ChargingRequest.type = 0, + .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 0, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.special_charging_code = 3, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ChargingRequest, + .component.result.invoke_id = 100, + .component.result.args.etsi.ChargingRequest.type = 0, + .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.currency = "Dollars", + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.amount.currency = 7, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.amount.multiplier = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.charging_type = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.time.length = 8, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.time.scale = 4, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ChargingRequest, + .component.result.invoke_id = 101, + .component.result.args.etsi.ChargingRequest.type = 0, + .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.currency = "Dollars", + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.amount.currency = 7, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.amount.multiplier = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.charging_type = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.time.length = 8, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.time.scale = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.granularity_present = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.granularity.length = 20, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.duration.granularity.scale = 3, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ChargingRequest, + .component.result.invoke_id = 102, + .component.result.args.etsi.ChargingRequest.type = 0, + .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 2, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.currency = "Euros", + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.amount.currency = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.amount.multiplier = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ChargingRequest, + .component.result.invoke_id = 103, + .component.result.args.etsi.ChargingRequest.type = 0, + .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 3, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.volume_rate.currency = "Yen", + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.volume_rate.amount.currency = 300, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.volume_rate.amount.multiplier = 5, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.volume_rate.unit = 2, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ChargingRequest, + .component.result.invoke_id = 104, + .component.result.args.etsi.ChargingRequest.type = 0, + .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 2, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 2, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.currency = "Euros", + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.amount.currency = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].u.flat_rate.amount.multiplier = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].charged_item = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].currency_type = 3, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].u.volume_rate.currency = "Yen", + .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].u.volume_rate.amount.currency = 300, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].u.volume_rate.amount.multiplier = 5, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[1].u.volume_rate.unit = 2, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ChargingRequest, + .component.result.invoke_id = 105, + .component.result.args.etsi.ChargingRequest.type = 0, + .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 4, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ChargingRequest, + .component.result.invoke_id = 106, + .component.result.args.etsi.ChargingRequest.type = 0, + .component.result.args.etsi.ChargingRequest.u.currency_info.num_records = 1, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].charged_item = 4, + .component.result.args.etsi.ChargingRequest.u.currency_info.list[0].currency_type = 5, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCSCurrency, + .component.invoke.invoke_id = 107, + .component.invoke.args.etsi.AOCSCurrency.type = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCSCurrency, + .component.invoke.invoke_id = 108, + .component.invoke.args.etsi.AOCSCurrency.type = 1, + .component.invoke.args.etsi.AOCSCurrency.currency_info.num_records = 1, + .component.invoke.args.etsi.AOCSCurrency.currency_info.list[0].charged_item = 3, + .component.invoke.args.etsi.AOCSCurrency.currency_info.list[0].currency_type = 4, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCSSpecialArr, + .component.invoke.invoke_id = 109, + .component.invoke.args.etsi.AOCSSpecialArr.type = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCSSpecialArr, + .component.invoke.invoke_id = 110, + .component.invoke.args.etsi.AOCSSpecialArr.type = 1, + .component.invoke.args.etsi.AOCSSpecialArr.special_arrangement = 9, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDCurrency, + .component.invoke.invoke_id = 111, + .component.invoke.args.etsi.AOCDCurrency.type = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDCurrency, + .component.invoke.invoke_id = 112, + .component.invoke.args.etsi.AOCDCurrency.type = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDCurrency, + .component.invoke.invoke_id = 113, + .component.invoke.args.etsi.AOCDCurrency.type = 2, + .component.invoke.args.etsi.AOCDCurrency.specific.recorded.currency = "Francs", + .component.invoke.args.etsi.AOCDCurrency.specific.recorded.amount.currency = 674, + .component.invoke.args.etsi.AOCDCurrency.specific.recorded.amount.multiplier = 3, + .component.invoke.args.etsi.AOCDCurrency.specific.type_of_charging_info = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDCurrency, + .component.invoke.invoke_id = 114, + .component.invoke.args.etsi.AOCDCurrency.type = 2, + .component.invoke.args.etsi.AOCDCurrency.specific.recorded.currency = "Francs", + .component.invoke.args.etsi.AOCDCurrency.specific.recorded.amount.currency = 674, + .component.invoke.args.etsi.AOCDCurrency.specific.recorded.amount.multiplier = 3, + .component.invoke.args.etsi.AOCDCurrency.specific.type_of_charging_info = 1, + .component.invoke.args.etsi.AOCDCurrency.specific.billing_id_present = 1, + .component.invoke.args.etsi.AOCDCurrency.specific.billing_id = 2, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, + .component.invoke.invoke_id = 115, + .component.invoke.args.etsi.AOCDChargingUnit.type = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, + .component.invoke.invoke_id = 116, + .component.invoke.args.etsi.AOCDChargingUnit.type = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, + .component.invoke.invoke_id = 117, + .component.invoke.args.etsi.AOCDChargingUnit.type = 2, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, + .component.invoke.invoke_id = 118, + .component.invoke.args.etsi.AOCDChargingUnit.type = 2, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 0, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].number_of_units = 8523, + .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.billing_id_present = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.billing_id = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, + .component.invoke.invoke_id = 119, + .component.invoke.args.etsi.AOCDChargingUnit.type = 2, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].type_of_unit_present = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].type_of_unit = 13, + .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, + .component.invoke.invoke_id = 120, + .component.invoke.args.etsi.AOCDChargingUnit.type = 2, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 0, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].number_of_units = 8523, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].type_of_unit_present = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].type_of_unit = 13, + .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCDChargingUnit, + .component.invoke.invoke_id = 121, + .component.invoke.args.etsi.AOCDChargingUnit.type = 2, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.num_records = 2, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[0].not_available = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[1].not_available = 0, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[1].number_of_units = 8523, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[1].type_of_unit_present = 1, + .component.invoke.args.etsi.AOCDChargingUnit.specific.recorded.list[1].type_of_unit = 13, + .component.invoke.args.etsi.AOCDChargingUnit.specific.type_of_charging_info = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCECurrency, + .component.invoke.invoke_id = 122, + .component.invoke.args.etsi.AOCECurrency.type = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCECurrency, + .component.invoke.invoke_id = 123, + .component.invoke.args.etsi.AOCECurrency.type = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCECurrency, + .component.invoke.invoke_id = 124, + .component.invoke.args.etsi.AOCECurrency.type = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association_present = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.type = 0, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.id = -37, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCECurrency, + .component.invoke.invoke_id = 125, + .component.invoke.args.etsi.AOCECurrency.type = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association_present = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.type = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.number.plan = 0, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.number.length = 7, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.number.str = "5551212", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCECurrency, + .component.invoke.invoke_id = 126, + .component.invoke.args.etsi.AOCECurrency.type = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 0, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.currency = "Francs", + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.currency = 674, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.multiplier = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCECurrency, + .component.invoke.invoke_id = 127, + .component.invoke.args.etsi.AOCECurrency.type = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 0, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.currency = "Francs", + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.currency = 674, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.multiplier = 3, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association_present = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.type = 0, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.id = -37, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCECurrency, + .component.invoke.invoke_id = 128, + .component.invoke.args.etsi.AOCECurrency.type = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 0, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.currency = "Francs", + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.currency = 674, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.multiplier = 3, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.billing_id_present = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.billing_id = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCECurrency, + .component.invoke.invoke_id = 129, + .component.invoke.args.etsi.AOCECurrency.type = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.free_of_charge = 0, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.currency = "Francs", + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.currency = 674, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.recorded.amount.multiplier = 3, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.billing_id_present = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.specific.billing_id = 2, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association_present = 1, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.type = 0, + .component.invoke.args.etsi.AOCECurrency.currency_info.charging_association.id = -37, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, + .component.invoke.invoke_id = 130, + .component.invoke.args.etsi.AOCEChargingUnit.type = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, + .component.invoke.invoke_id = 131, + .component.invoke.args.etsi.AOCEChargingUnit.type = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, + .component.invoke.invoke_id = 132, + .component.invoke.args.etsi.AOCEChargingUnit.type = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 0, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.id = -37, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, + .component.invoke.invoke_id = 133, + .component.invoke.args.etsi.AOCEChargingUnit.type = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 0, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].not_available = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, + .component.invoke.invoke_id = 134, + .component.invoke.args.etsi.AOCEChargingUnit.type = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 0, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].not_available = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 0, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.id = -37, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, + .component.invoke.invoke_id = 135, + .component.invoke.args.etsi.AOCEChargingUnit.type = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 0, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].not_available = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id_present = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_AOCEChargingUnit, + .component.invoke.invoke_id = 136, + .component.invoke.args.etsi.AOCEChargingUnit.type = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 0, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0].not_available = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id_present = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.specific.billing_id = 2, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association_present = 1, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.type = 0, + .component.invoke.args.etsi.AOCEChargingUnit.charging_unit.charging_association.id = -37, + }, + + /* Call diversion */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_ActivationDiversion, + .component.invoke.invoke_id = 67, + .component.invoke.linked_id_present = 1, + .component.invoke.linked_id = 27, + .component.invoke.args.etsi.ActivationDiversion.procedure = 2, + .component.invoke.args.etsi.ActivationDiversion.basic_service = 3, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.plan = 4, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.length = 4, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.str = "1803", + .component.invoke.args.etsi.ActivationDiversion.served_user_number.plan = 4, + .component.invoke.args.etsi.ActivationDiversion.served_user_number.length = 4, + .component.invoke.args.etsi.ActivationDiversion.served_user_number.str = "5398", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_ActivationDiversion, + .component.invoke.invoke_id = 68, + .component.invoke.args.etsi.ActivationDiversion.procedure = 1, + .component.invoke.args.etsi.ActivationDiversion.basic_service = 5, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.plan = 4, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.length = 4, + .component.invoke.args.etsi.ActivationDiversion.forwarded_to.number.str = "1803", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_ActivationDiversion, + .component.result.invoke_id = 69, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DeactivationDiversion, + .component.invoke.invoke_id = 70, + .component.invoke.args.etsi.DeactivationDiversion.procedure = 1, + .component.invoke.args.etsi.DeactivationDiversion.basic_service = 5, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_DeactivationDiversion, + .component.result.invoke_id = 71, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_ActivationStatusNotificationDiv, + .component.invoke.invoke_id = 72, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.procedure = 1, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.basic_service = 5, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.forwarded_to.number.plan = 4, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.forwarded_to.number.length = 4, + .component.invoke.args.etsi.ActivationStatusNotificationDiv.forwarded_to.number.str = "1803", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DeactivationStatusNotificationDiv, + .component.invoke.invoke_id = 73, + .component.invoke.args.etsi.DeactivationStatusNotificationDiv.procedure = 1, + .component.invoke.args.etsi.DeactivationStatusNotificationDiv.basic_service = 5, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_InterrogationDiversion, + .component.invoke.invoke_id = 74, + .component.invoke.args.etsi.InterrogationDiversion.procedure = 1, + .component.invoke.args.etsi.InterrogationDiversion.basic_service = 5, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_InterrogationDiversion, + .component.invoke.invoke_id = 75, + .component.invoke.args.etsi.InterrogationDiversion.procedure = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_InterrogationDiversion, + .component.result.invoke_id = 76, + .component.result.args.etsi.InterrogationDiversion.num_records = 2, + .component.result.args.etsi.InterrogationDiversion.list[0].procedure = 2, + .component.result.args.etsi.InterrogationDiversion.list[0].basic_service = 5, + .component.result.args.etsi.InterrogationDiversion.list[0].forwarded_to.number.plan = 4, + .component.result.args.etsi.InterrogationDiversion.list[0].forwarded_to.number.length = 4, + .component.result.args.etsi.InterrogationDiversion.list[0].forwarded_to.number.str = "1803", + .component.result.args.etsi.InterrogationDiversion.list[1].procedure = 1, + .component.result.args.etsi.InterrogationDiversion.list[1].basic_service = 3, + .component.result.args.etsi.InterrogationDiversion.list[1].forwarded_to.number.plan = 4, + .component.result.args.etsi.InterrogationDiversion.list[1].forwarded_to.number.length = 4, + .component.result.args.etsi.InterrogationDiversion.list[1].forwarded_to.number.str = "1903", + .component.result.args.etsi.InterrogationDiversion.list[1].served_user_number.plan = 4, + .component.result.args.etsi.InterrogationDiversion.list[1].served_user_number.length = 4, + .component.result.args.etsi.InterrogationDiversion.list[1].served_user_number.str = "5398", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 77, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 3, + .component.invoke.args.etsi.DiversionInformation.basic_service = 5, + .component.invoke.args.etsi.DiversionInformation.served_user_subaddress.type = 1, + .component.invoke.args.etsi.DiversionInformation.served_user_subaddress.length = 4, + .component.invoke.args.etsi.DiversionInformation.served_user_subaddress.u.nsap = "6492", + .component.invoke.args.etsi.DiversionInformation.calling_present = 1, + .component.invoke.args.etsi.DiversionInformation.calling.presentation = 0, + .component.invoke.args.etsi.DiversionInformation.calling.screened.screening_indicator = 3, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.plan = 4, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.length = 4, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.str = "1803", + .component.invoke.args.etsi.DiversionInformation.original_called_present = 1, + .component.invoke.args.etsi.DiversionInformation.original_called.presentation = 1, + .component.invoke.args.etsi.DiversionInformation.last_diverting_present = 1, + .component.invoke.args.etsi.DiversionInformation.last_diverting.presentation = 2, + .component.invoke.args.etsi.DiversionInformation.last_diverting_reason_present = 1, + .component.invoke.args.etsi.DiversionInformation.last_diverting_reason = 3, + .component.invoke.args.etsi.DiversionInformation.q931ie.length = 5, + .component.invoke.args.etsi.DiversionInformation.q931ie_contents = "79828", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 78, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 3, + .component.invoke.args.etsi.DiversionInformation.basic_service = 5, + .component.invoke.args.etsi.DiversionInformation.calling_present = 1, + .component.invoke.args.etsi.DiversionInformation.calling.presentation = 1, + .component.invoke.args.etsi.DiversionInformation.original_called_present = 1, + .component.invoke.args.etsi.DiversionInformation.original_called.presentation = 2, + .component.invoke.args.etsi.DiversionInformation.last_diverting_present = 1, + .component.invoke.args.etsi.DiversionInformation.last_diverting.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 79, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 2, + .component.invoke.args.etsi.DiversionInformation.basic_service = 3, + .component.invoke.args.etsi.DiversionInformation.calling_present = 1, + .component.invoke.args.etsi.DiversionInformation.calling.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 80, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 3, + .component.invoke.args.etsi.DiversionInformation.basic_service = 5, + .component.invoke.args.etsi.DiversionInformation.calling_present = 1, + .component.invoke.args.etsi.DiversionInformation.calling.presentation = 3, + .component.invoke.args.etsi.DiversionInformation.calling.screened.screening_indicator = 2, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.plan = 4, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.length = 4, + .component.invoke.args.etsi.DiversionInformation.calling.screened.number.str = "1803", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 81, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 2, + .component.invoke.args.etsi.DiversionInformation.basic_service = 4, + .component.invoke.args.etsi.DiversionInformation.q931ie.length = 5, + .component.invoke.args.etsi.DiversionInformation.q931ie_contents = "79828", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DiversionInformation, + .component.invoke.invoke_id = 82, + .component.invoke.args.etsi.DiversionInformation.diversion_reason = 2, + .component.invoke.args.etsi.DiversionInformation.basic_service = 4, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallDeflection, + .component.invoke.invoke_id = 83, + .component.invoke.args.etsi.CallDeflection.deflection.number.plan = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.length = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.str = "1803", + .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1, + .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallDeflection, + .component.invoke.invoke_id = 84, + .component.invoke.args.etsi.CallDeflection.deflection.number.plan = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.length = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.str = "1803", + .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1, + .component.invoke.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallDeflection, + .component.invoke.invoke_id = 85, + .component.invoke.args.etsi.CallDeflection.deflection.number.plan = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.length = 4, + .component.invoke.args.etsi.CallDeflection.deflection.number.str = "1803", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CallDeflection, + .component.result.invoke_id = 86, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallRerouting, + .component.invoke.invoke_id = 87, + .component.invoke.args.etsi.CallRerouting.rerouting_reason = 3, + .component.invoke.args.etsi.CallRerouting.rerouting_counter = 2, + .component.invoke.args.etsi.CallRerouting.called_address.number.plan = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.length = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.str = "1803", + .component.invoke.args.etsi.CallRerouting.q931ie.length = 129, + .component.invoke.args.etsi.CallRerouting.q931ie_contents = + "YEHAW." + " The quick brown fox jumped over the lazy dog test." + " Now is the time for all good men to come to the aid of their country.", + .component.invoke.args.etsi.CallRerouting.last_rerouting.presentation = 1, + .component.invoke.args.etsi.CallRerouting.subscription_option = 2, + .component.invoke.args.etsi.CallRerouting.calling_subaddress.type = 1, + .component.invoke.args.etsi.CallRerouting.calling_subaddress.length = 4, + .component.invoke.args.etsi.CallRerouting.calling_subaddress.u.nsap = "6492", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallRerouting, + .component.invoke.invoke_id = 88, + .component.invoke.args.etsi.CallRerouting.rerouting_reason = 3, + .component.invoke.args.etsi.CallRerouting.rerouting_counter = 2, + .component.invoke.args.etsi.CallRerouting.called_address.number.plan = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.length = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.str = "1803", + .component.invoke.args.etsi.CallRerouting.q931ie.length = 2, + .component.invoke.args.etsi.CallRerouting.q931ie_contents = "RT", + .component.invoke.args.etsi.CallRerouting.last_rerouting.presentation = 1, + .component.invoke.args.etsi.CallRerouting.subscription_option = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_CallRerouting, + .component.invoke.invoke_id = 89, + .component.invoke.args.etsi.CallRerouting.rerouting_reason = 3, + .component.invoke.args.etsi.CallRerouting.rerouting_counter = 2, + .component.invoke.args.etsi.CallRerouting.called_address.number.plan = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.length = 4, + .component.invoke.args.etsi.CallRerouting.called_address.number.str = "1803", + .component.invoke.args.etsi.CallRerouting.q931ie.length = 2, + .component.invoke.args.etsi.CallRerouting.q931ie_contents = "RT", + .component.invoke.args.etsi.CallRerouting.last_rerouting.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_CallRerouting, + .component.result.invoke_id = 90, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_InterrogateServedUserNumbers, + .component.invoke.invoke_id = 91, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_InterrogateServedUserNumbers, + .component.result.invoke_id = 92, + .component.result.args.etsi.InterrogateServedUserNumbers.num_records = 2, + .component.result.args.etsi.InterrogateServedUserNumbers.number[0].plan = 4, + .component.result.args.etsi.InterrogateServedUserNumbers.number[0].length = 4, + .component.result.args.etsi.InterrogateServedUserNumbers.number[0].str = "1803", + .component.result.args.etsi.InterrogateServedUserNumbers.number[1].plan = 4, + .component.result.args.etsi.InterrogateServedUserNumbers.number[1].length = 4, + .component.result.args.etsi.InterrogateServedUserNumbers.number[1].str = "5786", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation1, + .component.invoke.invoke_id = 93, + .component.invoke.args.etsi.DivertingLegInformation1.diversion_reason = 4, + .component.invoke.args.etsi.DivertingLegInformation1.subscription_option = 1, + .component.invoke.args.etsi.DivertingLegInformation1.diverted_to_present = 1, + .component.invoke.args.etsi.DivertingLegInformation1.diverted_to.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation1, + .component.invoke.invoke_id = 94, + .component.invoke.args.etsi.DivertingLegInformation1.diversion_reason = 4, + .component.invoke.args.etsi.DivertingLegInformation1.subscription_option = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation2, + .component.invoke.invoke_id = 95, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_counter = 3, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_reason = 2, + .component.invoke.args.etsi.DivertingLegInformation2.diverting_present = 1, + .component.invoke.args.etsi.DivertingLegInformation2.diverting.presentation = 2, + .component.invoke.args.etsi.DivertingLegInformation2.original_called_present = 1, + .component.invoke.args.etsi.DivertingLegInformation2.original_called.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation2, + .component.invoke.invoke_id = 96, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_counter = 3, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_reason = 2, + .component.invoke.args.etsi.DivertingLegInformation2.original_called_present = 1, + .component.invoke.args.etsi.DivertingLegInformation2.original_called.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation2, + .component.invoke.invoke_id = 97, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_counter = 1, + .component.invoke.args.etsi.DivertingLegInformation2.diversion_reason = 2, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_DivertingLegInformation3, + .component.invoke.invoke_id = 98, + .component.invoke.args.etsi.DivertingLegInformation3.presentation_allowed_indicator = 1, + }, + + /* Explicit Call Transfer (ECT) */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctExecute, + .component.invoke.invoke_id = 54, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_ExplicitEctExecute, + .component.invoke.invoke_id = 55, + .component.invoke.args.etsi.ExplicitEctExecute.link_id = 23, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_RequestSubaddress, + .component.invoke.invoke_id = 56, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_SubaddressTransfer, + .component.invoke.invoke_id = 57, + .component.invoke.args.etsi.SubaddressTransfer.subaddress.type = 1, + .component.invoke.args.etsi.SubaddressTransfer.subaddress.length = 4, + .component.invoke.args.etsi.SubaddressTransfer.subaddress.u.nsap = "6492", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctLinkIdRequest, + .component.invoke.invoke_id = 58, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_EctLinkIdRequest, + .component.result.invoke_id = 59, + .component.result.args.etsi.EctLinkIdRequest.link_id = 76, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 60, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 1, + .component.invoke.args.etsi.EctInform.redirection.presentation = 0, + .component.invoke.args.etsi.EctInform.redirection.number.plan = 8, + .component.invoke.args.etsi.EctInform.redirection.number.length = 4, + .component.invoke.args.etsi.EctInform.redirection.number.str = "6229", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 61, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 1, + .component.invoke.args.etsi.EctInform.redirection.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 62, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 1, + .component.invoke.args.etsi.EctInform.redirection.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 63, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 1, + .component.invoke.args.etsi.EctInform.redirection.presentation = 3, + .component.invoke.args.etsi.EctInform.redirection.number.plan = 8, + .component.invoke.args.etsi.EctInform.redirection.number.length = 4, + .component.invoke.args.etsi.EctInform.redirection.number.str = "3340", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctInform, + .component.invoke.invoke_id = 64, + .component.invoke.args.etsi.EctInform.status = 1, + .component.invoke.args.etsi.EctInform.redirection_present = 0, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_ETSI_EctLoopTest, + .component.invoke.invoke_id = 65, + .component.invoke.args.etsi.EctLoopTest.call_transfer_id = 7, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_ETSI_EctLoopTest, + .component.result.invoke_id = 66, + .component.result.args.etsi.EctLoopTest.loop_result = 2, + }, +/* *INDENT-ON* */ +}; + +static unsigned char rose_etsi_indefinite_len[] = { +/* *INDENT-OFF* */ +/* + * Context Specific/C [1 0x01] Len:24 <80> + * Integer(2 0x02) <02> Len:1 <01> + * <44> + * Integer(2 0x02) <02> Len:1 <01> + * <07> + * Sequence/C(48 0x30) <30> Len:16 <80> + * Enumerated(10 0x0A) <0A> Len:1 <01> + * <01> + * Enumerated(10 0x0A) <0A> Len:1 <01> + * <05> + * Sequence/C(48 0x30) <30> Len:6 <80> + * Context Specific [4 0x04] <84> Len:4 <80> + * <31 38 30 33> + * 0x00, 0x00, + * 0x00, 0x00, + * NULL(5 0x05) <05> Len:0 <00> + * 0x00, 0x00, + * 0x00, 0x00 + */ + 0x91, + 0xA1, 0x80, + 0x02, 0x01, + 0x44, + 0x02, 0x01, + 0x07, + 0x30, 0x80, + 0x0A, 0x01, + 0x01, + 0x0A, 0x01, + 0x05, + 0x30, 0x80, + 0x84, 0x80, + 0x31, 0x38, 0x30, 0x33, + 0x00, 0x00, + 0x00, 0x00, + 0x05, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00 +/* *INDENT-ON* */ +}; + +static unsigned char rose_etsi_unused_indefinite_len[] = { +/* *INDENT-OFF* */ +/* + * Context Specific/C [1 0x01] Len:24 <80> + * Integer(2 0x02) <02> Len:1 <01> + * <44> + * Integer(2 0x02) <02> Len:1 <01> + * <06> -- EctExecute + * Sequence/C(48 0x30) <30> Len:16 <80> + * Enumerated(10 0x0A) <0A> Len:1 <01> + * <01> + * Enumerated(10 0x0A) <0A> Len:1 <01> + * <05> + * Sequence/C(48 0x30) <30> Len:6 <80> + * Context Specific [4 0x04] <84> Len:4 <80> + * <31 38 30 33> + * 0x00, 0x00, + * 0x00, 0x00, + * NULL(5 0x05) <05> Len:0 <00> + * 0x00, 0x00, + * 0x00, 0x00 + */ + 0x91, + 0xA1, 0x80, + 0x02, 0x01, + 0x44, + 0x02, 0x01, + 0x06, + 0x30, 0x80, + 0x0A, 0x01, + 0x01, + 0x0A, 0x01, + 0x05, + 0x30, 0x80, + 0x84, 0x80, + 0x31, 0x38, 0x30, 0x33, + 0x00, 0x00, + 0x00, 0x00, + 0x05, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00 +/* *INDENT-ON* */ +}; + +static unsigned char rose_etsi_unused[] = { +/* *INDENT-OFF* */ +/* + * Context Specific/C [1 0x01] Len:24 <18> + * Integer(2 0x02) <02> Len:1 <01> + * <44> + * Integer(2 0x02) <02> Len:1 <01> + * <06> -- EctExecute + * Sequence/C(48 0x30) <30> Len:16 <10> + * Enumerated(10 0x0A) <0A> Len:1 <01> + * <01> + * Enumerated(10 0x0A) <0A> Len:1 <01> + * <05> + * Sequence/C(48 0x30) <30> Len:6 <06> + * Context Specific [4 0x04] <84> Len:4 <04> + * <31 38 30 33> + * NULL(5 0x05) <05> Len:0 <00> + */ + 0x91, + 0xA1, 0x18, + 0x02, 0x01, + 0x44, + 0x02, 0x01, + 0x06, + 0x30, 0x10, + 0x0A, 0x01, + 0x01, + 0x0A, 0x01, + 0x05, + 0x30, 0x06, + 0x84, 0x04, + 0x31, 0x38, 0x30, 0x33, + 0x05, 0x00, + 0x00, 0x00 +/* *INDENT-ON* */ +}; + +static unsigned char rose_etsi_extra[] = { +/* *INDENT-OFF* */ +/* + * Context Specific/C [1 0x01] Len:24 <18> + * Integer(2 0x02) <02> Len:1 <01> + * <44> + * Integer(2 0x02) <02> Len:1 <01> + * <07> + * Sequence/C(48 0x30) <30> Len:16 <10> + * Enumerated(10 0x0A) <0A> Len:1 <01> + * <01> + * Enumerated(10 0x0A) <0A> Len:1 <01> + * <05> + * Sequence/C(48 0x30) <30> Len:6 <06> + * Context Specific [4 0x04] <84> Len:4 <04> + * <31 38 30 33> + * NULL(5 0x05) <05> Len:0 <00> + */ + 0x91, + 0xA1, 0x18, + 0x02, 0x01, + 0x44, + 0x02, 0x01, + 0x07, + 0x30, 0x10, + 0x0A, 0x01, + 0x01, + 0x0A, 0x01, + 0x05, + 0x30, 0x06, + 0x84, 0x04, + 0x31, 0x38, 0x30, 0x33, + 0x05, 0x00, + 0x00, 0x00 +/* *INDENT-ON* */ +}; + + +static const struct rose_message rose_qsig_msgs[] = { +/* *INDENT-OFF* */ + /* Q.SIG Name-Operations */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallingName, + .component.invoke.invoke_id = 2, + .component.invoke.args.qsig.CallingName.name.presentation = 1, + .component.invoke.args.qsig.CallingName.name.char_set = 1, + .component.invoke.args.qsig.CallingName.name.length = 7, + .component.invoke.args.qsig.CallingName.name.data = "Alphred", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallingName, + .component.invoke.invoke_id = 3, + .component.invoke.args.qsig.CallingName.name.presentation = 1, + .component.invoke.args.qsig.CallingName.name.char_set = 3, + .component.invoke.args.qsig.CallingName.name.length = 7, + .component.invoke.args.qsig.CallingName.name.data = "Alphred", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallingName, + .component.invoke.invoke_id = 4, + .component.invoke.args.qsig.CallingName.name.presentation = 2, + .component.invoke.args.qsig.CallingName.name.char_set = 1, + .component.invoke.args.qsig.CallingName.name.length = 7, + .component.invoke.args.qsig.CallingName.name.data = "Alphred", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallingName, + .component.invoke.invoke_id = 5, + .component.invoke.args.qsig.CallingName.name.presentation = 2, + .component.invoke.args.qsig.CallingName.name.char_set = 3, + .component.invoke.args.qsig.CallingName.name.length = 7, + .component.invoke.args.qsig.CallingName.name.data = "Alphred", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallingName, + .component.invoke.invoke_id = 6, + .component.invoke.args.qsig.CallingName.name.presentation = 3, + .component.invoke.args.qsig.CallingName.name.char_set = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallingName, + .component.invoke.invoke_id = 7, + .component.invoke.args.qsig.CallingName.name.presentation = 4, + .component.invoke.args.qsig.CallingName.name.char_set = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CalledName, + .component.invoke.invoke_id = 8, + .component.invoke.args.qsig.CallingName.name.presentation = 4, + .component.invoke.args.qsig.CallingName.name.char_set = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_ConnectedName, + .component.invoke.invoke_id = 9, + .component.invoke.args.qsig.CallingName.name.presentation = 4, + .component.invoke.args.qsig.CallingName.name.char_set = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_BusyName, + .component.invoke.invoke_id = 10, + .component.invoke.args.qsig.CallingName.name.presentation = 4, + .component.invoke.args.qsig.CallingName.name.char_set = 1, + }, + + /* Q.SIG SS-AOC-Operations */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_ChargeRequest, + .component.invoke.invoke_id = 11, + .component.invoke.args.qsig.ChargeRequest.num_records = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_ChargeRequest, + .component.invoke.invoke_id = 12, + .component.invoke.args.qsig.ChargeRequest.num_records = 1, + .component.invoke.args.qsig.ChargeRequest.advice_mode_combinations[0] = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_ChargeRequest, + .component.invoke.invoke_id = 13, + .component.invoke.args.qsig.ChargeRequest.num_records = 2, + .component.invoke.args.qsig.ChargeRequest.advice_mode_combinations[0] = 4, + .component.invoke.args.qsig.ChargeRequest.advice_mode_combinations[1] = 3, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_ChargeRequest, + .component.result.invoke_id = 14, + .component.result.args.qsig.ChargeRequest.advice_mode_combination = 3, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_GetFinalCharge, + .component.invoke.invoke_id = 15, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocFinal, + .component.invoke.invoke_id = 16, + .component.invoke.args.qsig.AocFinal.type = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocFinal, + .component.invoke.invoke_id = 17, + .component.invoke.args.qsig.AocFinal.type = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocFinal, + .component.invoke.invoke_id = 18, + .component.invoke.args.qsig.AocFinal.type = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocFinal, + .component.invoke.invoke_id = 19, + .component.invoke.args.qsig.AocFinal.type = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", + .component.invoke.args.qsig.AocFinal.specific.billing_id_present = 1, + .component.invoke.args.qsig.AocFinal.specific.billing_id = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocFinal, + .component.invoke.invoke_id = 20, + .component.invoke.args.qsig.AocFinal.type = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", + .component.invoke.args.qsig.AocFinal.charging_association_present = 1, + .component.invoke.args.qsig.AocFinal.charging_association.type = 0, + .component.invoke.args.qsig.AocFinal.charging_association.id = 200, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocFinal, + .component.invoke.invoke_id = 21, + .component.invoke.args.qsig.AocFinal.type = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", + .component.invoke.args.qsig.AocFinal.specific.billing_id_present = 1, + .component.invoke.args.qsig.AocFinal.specific.billing_id = 2, + .component.invoke.args.qsig.AocFinal.charging_association_present = 1, + .component.invoke.args.qsig.AocFinal.charging_association.type = 0, + .component.invoke.args.qsig.AocFinal.charging_association.id = 200, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocFinal, + .component.invoke.invoke_id = 22, + .component.invoke.args.qsig.AocFinal.type = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.currency = 800, + .component.invoke.args.qsig.AocFinal.specific.recorded.amount.multiplier = 2, + .component.invoke.args.qsig.AocFinal.specific.recorded.currency = "Rupies", + .component.invoke.args.qsig.AocFinal.charging_association_present = 1, + .component.invoke.args.qsig.AocFinal.charging_association.type = 1, + .component.invoke.args.qsig.AocFinal.charging_association.number.plan = 4, + .component.invoke.args.qsig.AocFinal.charging_association.number.length = 4, + .component.invoke.args.qsig.AocFinal.charging_association.number.str = "1802", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocInterim, + .component.invoke.invoke_id = 23, + .component.invoke.args.qsig.AocInterim.type = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocInterim, + .component.invoke.invoke_id = 24, + .component.invoke.args.qsig.AocInterim.type = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocInterim, + .component.invoke.invoke_id = 25, + .component.invoke.args.qsig.AocInterim.type = 2, + .component.invoke.args.qsig.AocInterim.specific.recorded.amount.currency = 800, + .component.invoke.args.qsig.AocInterim.specific.recorded.amount.multiplier = 2, + .component.invoke.args.qsig.AocInterim.specific.recorded.currency = "Rupies", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocInterim, + .component.invoke.invoke_id = 26, + .component.invoke.args.qsig.AocInterim.type = 2, + .component.invoke.args.qsig.AocInterim.specific.recorded.amount.currency = 800, + .component.invoke.args.qsig.AocInterim.specific.recorded.amount.multiplier = 2, + .component.invoke.args.qsig.AocInterim.specific.recorded.currency = "Rupies", + .component.invoke.args.qsig.AocInterim.specific.billing_id_present = 1, + .component.invoke.args.qsig.AocInterim.specific.billing_id = 2, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 27, + .component.invoke.args.qsig.AocRate.type = 0, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 28, + .component.invoke.args.qsig.AocRate.type = 1, + .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 0, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.special_charging_code = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 29, + .component.invoke.args.qsig.AocRate.type = 1, + .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.currency = "Dollars", + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.amount.currency = 7, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.amount.multiplier = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.charging_type = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.time.length = 8, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.time.scale = 4, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 30, + .component.invoke.args.qsig.AocRate.type = 1, + .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.currency = "Dollars", + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.amount.currency = 7, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.amount.multiplier = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.charging_type = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.time.length = 8, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.time.scale = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.granularity_present = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.granularity.length = 20, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.duration.granularity.scale = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 31, + .component.invoke.args.qsig.AocRate.type = 1, + .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 2, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.currency = "Euros", + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.amount.currency = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.amount.multiplier = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 32, + .component.invoke.args.qsig.AocRate.type = 1, + .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 3, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.volume_rate.currency = "Yen", + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.volume_rate.amount.currency = 300, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.volume_rate.amount.multiplier = 5, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.volume_rate.unit = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 33, + .component.invoke.args.qsig.AocRate.type = 1, + .component.invoke.args.qsig.AocRate.currency_info.num_records = 2, + .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 2, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.currency = "Euros", + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.amount.currency = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].u.flat_rate.amount.multiplier = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[1].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[1].currency_type = 3, + .component.invoke.args.qsig.AocRate.currency_info.list[1].u.volume_rate.currency = "Yen", + .component.invoke.args.qsig.AocRate.currency_info.list[1].u.volume_rate.amount.currency = 300, + .component.invoke.args.qsig.AocRate.currency_info.list[1].u.volume_rate.amount.multiplier = 5, + .component.invoke.args.qsig.AocRate.currency_info.list[1].u.volume_rate.unit = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 34, + .component.invoke.args.qsig.AocRate.type = 1, + .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 4, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 35, + .component.invoke.args.qsig.AocRate.type = 1, + .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 5, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocRate, + .component.invoke.invoke_id = 36, + .component.invoke.args.qsig.AocRate.type = 1, + .component.invoke.args.qsig.AocRate.currency_info.num_records = 1, + .component.invoke.args.qsig.AocRate.currency_info.list[0].charged_item = 4, + .component.invoke.args.qsig.AocRate.currency_info.list[0].currency_type = 6, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocComplete, + .component.invoke.invoke_id = 37, + .component.invoke.args.qsig.AocComplete.charged_user_number.plan = 4, + .component.invoke.args.qsig.AocComplete.charged_user_number.length = 4, + .component.invoke.args.qsig.AocComplete.charged_user_number.str = "8340", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocComplete, + .component.invoke.invoke_id = 38, + .component.invoke.args.qsig.AocComplete.charged_user_number.plan = 4, + .component.invoke.args.qsig.AocComplete.charged_user_number.length = 4, + .component.invoke.args.qsig.AocComplete.charged_user_number.str = "8340", + .component.invoke.args.qsig.AocComplete.charging_association_present = 1, + .component.invoke.args.qsig.AocComplete.charging_association.type = 0, + .component.invoke.args.qsig.AocComplete.charging_association.id = 8298, + }, + + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_AocComplete, + .component.result.invoke_id = 39, + .component.result.args.qsig.AocComplete.charging_option = 2, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocDivChargeReq, + .component.invoke.invoke_id = 40, + .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.plan = 4, + .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.length = 4, + .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.str = "8340", + .component.invoke.args.qsig.AocDivChargeReq.diversion_type = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_AocDivChargeReq, + .component.invoke.invoke_id = 41, + .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.plan = 4, + .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.length = 4, + .component.invoke.args.qsig.AocDivChargeReq.diverting_user_number.str = "8340", + .component.invoke.args.qsig.AocDivChargeReq.charging_association_present = 1, + .component.invoke.args.qsig.AocDivChargeReq.charging_association.type = 0, + .component.invoke.args.qsig.AocDivChargeReq.charging_association.id = 8298, + .component.invoke.args.qsig.AocDivChargeReq.diversion_type = 3, + }, + + /* Q.SIG Call-Transfer-Operations (CT) */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferIdentify, + .component.invoke.invoke_id = 42, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_CallTransferIdentify, + .component.result.invoke_id = 43, + .component.result.args.qsig.CallTransferIdentify.call_id = "2345", + .component.result.args.qsig.CallTransferIdentify.rerouting_number.plan = 4, + .component.result.args.qsig.CallTransferIdentify.rerouting_number.length = 4, + .component.result.args.qsig.CallTransferIdentify.rerouting_number.str = "8340", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferAbandon, + .component.invoke.invoke_id = 44, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferInitiate, + .component.invoke.invoke_id = 45, + .component.invoke.args.qsig.CallTransferInitiate.call_id = "2345", + .component.invoke.args.qsig.CallTransferInitiate.rerouting_number.plan = 4, + .component.invoke.args.qsig.CallTransferInitiate.rerouting_number.length = 4, + .component.invoke.args.qsig.CallTransferInitiate.rerouting_number.str = "8340", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_CallTransferInitiate, + .component.result.invoke_id = 46, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferSetup, + .component.invoke.invoke_id = 47, + .component.invoke.args.qsig.CallTransferSetup.call_id = "23", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_CallTransferSetup, + .component.result.invoke_id = 48, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferActive, + .component.invoke.invoke_id = 49, + .component.invoke.args.qsig.CallTransferActive.connected.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferActive, + .component.invoke.invoke_id = 50, + .component.invoke.args.qsig.CallTransferActive.connected.presentation = 1, + .component.invoke.args.qsig.CallTransferActive.q931ie.length = 2, + .component.invoke.args.qsig.CallTransferActive.q931ie_contents = "RT", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferActive, + .component.invoke.invoke_id = 51, + .component.invoke.args.qsig.CallTransferActive.connected.presentation = 1, + .component.invoke.args.qsig.CallTransferActive.connected_name_present = 1, + .component.invoke.args.qsig.CallTransferActive.connected_name.presentation = 1, + .component.invoke.args.qsig.CallTransferActive.connected_name.char_set = 1, + .component.invoke.args.qsig.CallTransferActive.connected_name.length = 7, + .component.invoke.args.qsig.CallTransferActive.connected_name.data = "Alphred", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferActive, + .component.invoke.invoke_id = 52, + .component.invoke.args.qsig.CallTransferActive.connected.presentation = 1, + .component.invoke.args.qsig.CallTransferActive.q931ie.length = 2, + .component.invoke.args.qsig.CallTransferActive.q931ie_contents = "RT", + .component.invoke.args.qsig.CallTransferActive.connected_name_present = 1, + .component.invoke.args.qsig.CallTransferActive.connected_name.presentation = 1, + .component.invoke.args.qsig.CallTransferActive.connected_name.char_set = 1, + .component.invoke.args.qsig.CallTransferActive.connected_name.length = 7, + .component.invoke.args.qsig.CallTransferActive.connected_name.data = "Alphred", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferComplete, + .component.invoke.invoke_id = 53, + .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 0, + .component.invoke.args.qsig.CallTransferComplete.redirection.screened.screening_indicator = 3, + .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.plan = 4, + .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.length = 4, + .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.str = "8340", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferComplete, + .component.invoke.invoke_id = 54, + .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferComplete, + .component.invoke.invoke_id = 55, + .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferComplete, + .component.invoke.invoke_id = 56, + .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 3, + .component.invoke.args.qsig.CallTransferComplete.redirection.screened.screening_indicator = 3, + .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.plan = 4, + .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.length = 4, + .component.invoke.args.qsig.CallTransferComplete.redirection.screened.number.str = "8340", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferComplete, + .component.invoke.invoke_id = 57, + .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, + .component.invoke.args.qsig.CallTransferComplete.q931ie.length = 2, + .component.invoke.args.qsig.CallTransferComplete.q931ie_contents = "RT", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferComplete, + .component.invoke.invoke_id = 58, + .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, + .component.invoke.args.qsig.CallTransferComplete.redirection_name_present = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection_name.presentation = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection_name.char_set = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection_name.length = 7, + .component.invoke.args.qsig.CallTransferComplete.redirection_name.data = "Alphred", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferComplete, + .component.invoke.invoke_id = 59, + .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, + .component.invoke.args.qsig.CallTransferComplete.call_status = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferComplete, + .component.invoke.invoke_id = 60, + .component.invoke.args.qsig.CallTransferComplete.end_designation = 1, + .component.invoke.args.qsig.CallTransferComplete.redirection.presentation = 2, + .component.invoke.args.qsig.CallTransferComplete.q931ie.length = 2, + .component.invoke.args.qsig.CallTransferComplete.q931ie_contents = "RT", + .component.invoke.args.qsig.CallTransferComplete.call_status = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferUpdate, + .component.invoke.invoke_id = 61, + .component.invoke.args.qsig.CallTransferUpdate.redirection.presentation = 2, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferUpdate, + .component.invoke.invoke_id = 62, + .component.invoke.args.qsig.CallTransferUpdate.redirection.presentation = 2, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name_present = 1, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name.presentation = 1, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name.char_set = 1, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name.length = 7, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name.data = "Alphred", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferUpdate, + .component.invoke.invoke_id = 63, + .component.invoke.args.qsig.CallTransferUpdate.redirection.presentation = 2, + .component.invoke.args.qsig.CallTransferUpdate.q931ie.length = 2, + .component.invoke.args.qsig.CallTransferUpdate.q931ie_contents = "RT", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallTransferUpdate, + .component.invoke.invoke_id = 64, + .component.invoke.args.qsig.CallTransferUpdate.redirection.presentation = 2, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name_present = 1, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name.presentation = 1, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name.char_set = 1, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name.length = 7, + .component.invoke.args.qsig.CallTransferUpdate.redirection_name.data = "Alphred", + .component.invoke.args.qsig.CallTransferUpdate.q931ie.length = 2, + .component.invoke.args.qsig.CallTransferUpdate.q931ie_contents = "RT", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_SubaddressTransfer, + .component.invoke.invoke_id = 65, + .component.invoke.args.qsig.SubaddressTransfer.redirection_subaddress.type = 1, + .component.invoke.args.qsig.SubaddressTransfer.redirection_subaddress.length = 4, + .component.invoke.args.qsig.SubaddressTransfer.redirection_subaddress.u.nsap = "4356", + }, + + /* Q.SIG Call-Diversion-Operations */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_ActivateDiversionQ, + .component.invoke.invoke_id = 66, + .component.invoke.args.qsig.ActivateDiversionQ.procedure = 1, + .component.invoke.args.qsig.ActivateDiversionQ.basic_service = 3, + .component.invoke.args.qsig.ActivateDiversionQ.diverted_to.number.plan = 4, + .component.invoke.args.qsig.ActivateDiversionQ.diverted_to.number.length = 4, + .component.invoke.args.qsig.ActivateDiversionQ.diverted_to.number.str = "8340", + .component.invoke.args.qsig.ActivateDiversionQ.served_user_number.plan = 4, + .component.invoke.args.qsig.ActivateDiversionQ.served_user_number.length = 4, + .component.invoke.args.qsig.ActivateDiversionQ.served_user_number.str = "8340", + .component.invoke.args.qsig.ActivateDiversionQ.activating_user_number.plan = 4, + .component.invoke.args.qsig.ActivateDiversionQ.activating_user_number.length = 4, + .component.invoke.args.qsig.ActivateDiversionQ.activating_user_number.str = "8340", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_ActivateDiversionQ, + .component.result.invoke_id = 67, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_DeactivateDiversionQ, + .component.invoke.invoke_id = 68, + .component.invoke.args.qsig.DeactivateDiversionQ.procedure = 1, + .component.invoke.args.qsig.DeactivateDiversionQ.basic_service = 3, + .component.invoke.args.qsig.DeactivateDiversionQ.served_user_number.plan = 4, + .component.invoke.args.qsig.DeactivateDiversionQ.served_user_number.length = 4, + .component.invoke.args.qsig.DeactivateDiversionQ.served_user_number.str = "8340", + .component.invoke.args.qsig.DeactivateDiversionQ.deactivating_user_number.plan = 4, + .component.invoke.args.qsig.DeactivateDiversionQ.deactivating_user_number.length = 4, + .component.invoke.args.qsig.DeactivateDiversionQ.deactivating_user_number.str = "8340", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_DeactivateDiversionQ, + .component.result.invoke_id = 69, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_InterrogateDiversionQ, + .component.invoke.invoke_id = 70, + .component.invoke.args.qsig.InterrogateDiversionQ.procedure = 1, + .component.invoke.args.qsig.InterrogateDiversionQ.basic_service = 3, + .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.plan = 4, + .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.length = 4, + .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.str = "8340", + .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.plan = 4, + .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.length = 4, + .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.str = "8340", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_InterrogateDiversionQ, + .component.invoke.invoke_id = 71, + .component.invoke.args.qsig.InterrogateDiversionQ.procedure = 1, + .component.invoke.args.qsig.InterrogateDiversionQ.basic_service = 0,/* default */ + .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.plan = 4, + .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.length = 4, + .component.invoke.args.qsig.InterrogateDiversionQ.served_user_number.str = "8340", + .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.plan = 4, + .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.length = 4, + .component.invoke.args.qsig.InterrogateDiversionQ.interrogating_user_number.str = "8340", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_InterrogateDiversionQ, + .component.result.invoke_id = 72, + .component.result.args.qsig.InterrogateDiversionQ.num_records = 0, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_InterrogateDiversionQ, + .component.result.invoke_id = 73, + .component.result.args.qsig.InterrogateDiversionQ.num_records = 1, + .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.plan = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.length = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.str = "8340", + .component.result.args.qsig.InterrogateDiversionQ.list[0].basic_service = 3, + .component.result.args.qsig.InterrogateDiversionQ.list[0].procedure = 2, + .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.plan = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.length = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.str = "8340", + .component.result.args.qsig.InterrogateDiversionQ.list[0].remote_enabled = 0, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_InterrogateDiversionQ, + .component.result.invoke_id = 74, + .component.result.args.qsig.InterrogateDiversionQ.num_records = 1, + .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.plan = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.length = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.str = "8340", + .component.result.args.qsig.InterrogateDiversionQ.list[0].basic_service = 3, + .component.result.args.qsig.InterrogateDiversionQ.list[0].procedure = 2, + .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.plan = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.length = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.str = "8340", + .component.result.args.qsig.InterrogateDiversionQ.list[0].remote_enabled = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_InterrogateDiversionQ, + .component.result.invoke_id = 75, + .component.result.args.qsig.InterrogateDiversionQ.num_records = 2, + .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.plan = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.length = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].served_user_number.str = "8340", + .component.result.args.qsig.InterrogateDiversionQ.list[0].basic_service = 3, + .component.result.args.qsig.InterrogateDiversionQ.list[0].procedure = 2, + .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.plan = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.length = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[0].diverted_to.number.str = "8340", + .component.result.args.qsig.InterrogateDiversionQ.list[1].served_user_number.plan = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[1].served_user_number.length = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[1].served_user_number.str = "8340", + .component.result.args.qsig.InterrogateDiversionQ.list[1].basic_service = 3, + .component.result.args.qsig.InterrogateDiversionQ.list[1].procedure = 2, + .component.result.args.qsig.InterrogateDiversionQ.list[1].diverted_to.number.plan = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[1].diverted_to.number.length = 4, + .component.result.args.qsig.InterrogateDiversionQ.list[1].diverted_to.number.str = "8340", + .component.result.args.qsig.InterrogateDiversionQ.list[1].remote_enabled = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CheckRestriction, + .component.invoke.invoke_id = 76, + .component.invoke.args.qsig.CheckRestriction.served_user_number.plan = 4, + .component.invoke.args.qsig.CheckRestriction.served_user_number.length = 4, + .component.invoke.args.qsig.CheckRestriction.served_user_number.str = "8340", + .component.invoke.args.qsig.CheckRestriction.basic_service = 3, + .component.invoke.args.qsig.CheckRestriction.diverted_to_number.plan = 4, + .component.invoke.args.qsig.CheckRestriction.diverted_to_number.length = 4, + .component.invoke.args.qsig.CheckRestriction.diverted_to_number.str = "8340", + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_CheckRestriction, + .component.result.invoke_id = 77, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallRerouting, + .component.invoke.invoke_id = 78, + .component.invoke.args.qsig.CallRerouting.rerouting_reason = 3, + .component.invoke.args.qsig.CallRerouting.called.number.plan = 4, + .component.invoke.args.qsig.CallRerouting.called.number.length = 4, + .component.invoke.args.qsig.CallRerouting.called.number.str = "8340", + .component.invoke.args.qsig.CallRerouting.diversion_counter = 5, + .component.invoke.args.qsig.CallRerouting.q931ie.length = 2, + .component.invoke.args.qsig.CallRerouting.q931ie_contents = "RT", + .component.invoke.args.qsig.CallRerouting.last_rerouting.presentation = 1, + .component.invoke.args.qsig.CallRerouting.subscription_option = 2, + .component.invoke.args.qsig.CallRerouting.calling.presentation = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CallRerouting, + .component.invoke.invoke_id = 79, + .component.invoke.args.qsig.CallRerouting.rerouting_reason = 3, + .component.invoke.args.qsig.CallRerouting.original_rerouting_reason_present = 1, + .component.invoke.args.qsig.CallRerouting.original_rerouting_reason = 2, + .component.invoke.args.qsig.CallRerouting.called.number.plan = 4, + .component.invoke.args.qsig.CallRerouting.called.number.length = 4, + .component.invoke.args.qsig.CallRerouting.called.number.str = "8340", + .component.invoke.args.qsig.CallRerouting.diversion_counter = 5, + .component.invoke.args.qsig.CallRerouting.q931ie.length = 2, + .component.invoke.args.qsig.CallRerouting.q931ie_contents = "RT", + .component.invoke.args.qsig.CallRerouting.last_rerouting.presentation = 1, + .component.invoke.args.qsig.CallRerouting.subscription_option = 2, + .component.invoke.args.qsig.CallRerouting.calling_subaddress.type = 1, + .component.invoke.args.qsig.CallRerouting.calling_subaddress.length = 4, + .component.invoke.args.qsig.CallRerouting.calling_subaddress.u.nsap = "3253", + .component.invoke.args.qsig.CallRerouting.calling.presentation = 1, + .component.invoke.args.qsig.CallRerouting.calling_name_present = 1, + .component.invoke.args.qsig.CallRerouting.calling_name.presentation = 4, + .component.invoke.args.qsig.CallRerouting.calling_name.char_set = 1, + .component.invoke.args.qsig.CallRerouting.original_called_present = 1, + .component.invoke.args.qsig.CallRerouting.original_called.presentation = 2, + .component.invoke.args.qsig.CallRerouting.redirecting_name_present = 1, + .component.invoke.args.qsig.CallRerouting.redirecting_name.presentation = 4, + .component.invoke.args.qsig.CallRerouting.redirecting_name.char_set = 1, + .component.invoke.args.qsig.CallRerouting.original_called_name_present = 1, + .component.invoke.args.qsig.CallRerouting.original_called_name.presentation = 4, + .component.invoke.args.qsig.CallRerouting.original_called_name.char_set = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_CallRerouting, + .component.result.invoke_id = 80, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_DivertingLegInformation1, + .component.invoke.invoke_id = 81, + .component.invoke.args.qsig.DivertingLegInformation1.diversion_reason = 3, + .component.invoke.args.qsig.DivertingLegInformation1.subscription_option = 1, + .component.invoke.args.qsig.DivertingLegInformation1.nominated_number.plan = 4, + .component.invoke.args.qsig.DivertingLegInformation1.nominated_number.length = 4, + .component.invoke.args.qsig.DivertingLegInformation1.nominated_number.str = "8340", + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_DivertingLegInformation2, + .component.invoke.invoke_id = 82, + .component.invoke.args.qsig.DivertingLegInformation2.diversion_counter = 6, + .component.invoke.args.qsig.DivertingLegInformation2.diversion_reason = 3, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_DivertingLegInformation2, + .component.invoke.invoke_id = 83, + .component.invoke.args.qsig.DivertingLegInformation2.diversion_counter = 6, + .component.invoke.args.qsig.DivertingLegInformation2.diversion_reason = 3, + .component.invoke.args.qsig.DivertingLegInformation2.original_diversion_reason_present = 1, + .component.invoke.args.qsig.DivertingLegInformation2.original_diversion_reason = 2, + .component.invoke.args.qsig.DivertingLegInformation2.diverting_present = 1, + .component.invoke.args.qsig.DivertingLegInformation2.diverting.presentation = 2, + .component.invoke.args.qsig.DivertingLegInformation2.original_called_present = 1, + .component.invoke.args.qsig.DivertingLegInformation2.original_called.presentation = 2, + .component.invoke.args.qsig.DivertingLegInformation2.redirecting_name_present = 1, + .component.invoke.args.qsig.DivertingLegInformation2.redirecting_name.presentation = 4, + .component.invoke.args.qsig.DivertingLegInformation2.redirecting_name.char_set = 1, + .component.invoke.args.qsig.DivertingLegInformation2.original_called_name_present = 1, + .component.invoke.args.qsig.DivertingLegInformation2.original_called_name.presentation = 4, + .component.invoke.args.qsig.DivertingLegInformation2.original_called_name.char_set = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_DivertingLegInformation3, + .component.invoke.invoke_id = 84, + .component.invoke.args.qsig.DivertingLegInformation3.presentation_allowed_indicator = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_DivertingLegInformation3, + .component.invoke.invoke_id = 85, + .component.invoke.args.qsig.DivertingLegInformation3.presentation_allowed_indicator = 1, + .component.invoke.args.qsig.DivertingLegInformation3.redirection_name_present = 1, + .component.invoke.args.qsig.DivertingLegInformation3.redirection_name.presentation = 4, + .component.invoke.args.qsig.DivertingLegInformation3.redirection_name.char_set = 1, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_CfnrDivertedLegFailed, + .component.invoke.invoke_id = 86, + }, + + /* Q.SIG SS-MWI-Operations */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_MWIActivate, + .component.invoke.invoke_id = 102, + .component.invoke.args.qsig.MWIActivate.served_user_number.plan = 4, + .component.invoke.args.qsig.MWIActivate.served_user_number.length = 4, + .component.invoke.args.qsig.MWIActivate.served_user_number.str = "9838", + .component.invoke.args.qsig.MWIActivate.basic_service = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_MWIActivate, + .component.invoke.invoke_id = 103, + .component.invoke.args.qsig.MWIActivate.served_user_number.plan = 4, + .component.invoke.args.qsig.MWIActivate.served_user_number.length = 4, + .component.invoke.args.qsig.MWIActivate.served_user_number.str = "9838", + .component.invoke.args.qsig.MWIActivate.basic_service = 1, + .component.invoke.args.qsig.MWIActivate.msg_centre_id_present = 1, + .component.invoke.args.qsig.MWIActivate.msg_centre_id.type = 0, + .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.integer = 532, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_MWIActivate, + .component.invoke.invoke_id = 104, + .component.invoke.args.qsig.MWIActivate.served_user_number.plan = 4, + .component.invoke.args.qsig.MWIActivate.served_user_number.length = 4, + .component.invoke.args.qsig.MWIActivate.served_user_number.str = "9838", + .component.invoke.args.qsig.MWIActivate.basic_service = 1, + .component.invoke.args.qsig.MWIActivate.msg_centre_id_present = 1, + .component.invoke.args.qsig.MWIActivate.msg_centre_id.type = 1, + .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.number.plan = 4, + .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.number.length = 4, + .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.number.str = "9838", + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_MWIActivate, + .component.invoke.invoke_id = 105, + .component.invoke.args.qsig.MWIActivate.served_user_number.plan = 4, + .component.invoke.args.qsig.MWIActivate.served_user_number.length = 4, + .component.invoke.args.qsig.MWIActivate.served_user_number.str = "9838", + .component.invoke.args.qsig.MWIActivate.basic_service = 1, + .component.invoke.args.qsig.MWIActivate.msg_centre_id_present = 1, + .component.invoke.args.qsig.MWIActivate.msg_centre_id.type = 2, + .component.invoke.args.qsig.MWIActivate.msg_centre_id.u.str = "123456", + .component.invoke.args.qsig.MWIActivate.number_of_messages_present = 1, + .component.invoke.args.qsig.MWIActivate.number_of_messages = 6548, + .component.invoke.args.qsig.MWIActivate.originating_number.plan = 4, + .component.invoke.args.qsig.MWIActivate.originating_number.length = 4, + .component.invoke.args.qsig.MWIActivate.originating_number.str = "9838", + .component.invoke.args.qsig.MWIActivate.timestamp_present = 1, + .component.invoke.args.qsig.MWIActivate.timestamp = "19970621194530", + .component.invoke.args.qsig.MWIActivate.priority_present = 1, + .component.invoke.args.qsig.MWIActivate.priority = 7, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_MWIActivate, + .component.result.invoke_id = 106, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_MWIDeactivate, + .component.invoke.invoke_id = 107, + .component.invoke.args.qsig.MWIDeactivate.served_user_number.plan = 4, + .component.invoke.args.qsig.MWIDeactivate.served_user_number.length = 4, + .component.invoke.args.qsig.MWIDeactivate.served_user_number.str = "9838", + .component.invoke.args.qsig.MWIDeactivate.basic_service = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_MWIDeactivate, + .component.invoke.invoke_id = 108, + .component.invoke.args.qsig.MWIDeactivate.served_user_number.plan = 4, + .component.invoke.args.qsig.MWIDeactivate.served_user_number.length = 4, + .component.invoke.args.qsig.MWIDeactivate.served_user_number.str = "9838", + .component.invoke.args.qsig.MWIDeactivate.basic_service = 1, + .component.invoke.args.qsig.MWIDeactivate.msg_centre_id_present = 1, + .component.invoke.args.qsig.MWIDeactivate.msg_centre_id.type = 0, + .component.invoke.args.qsig.MWIDeactivate.msg_centre_id.u.integer = 532, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_MWIDeactivate, + .component.result.invoke_id = 109, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_MWIInterrogate, + .component.invoke.invoke_id = 110, + .component.invoke.args.qsig.MWIInterrogate.served_user_number.plan = 4, + .component.invoke.args.qsig.MWIInterrogate.served_user_number.length = 4, + .component.invoke.args.qsig.MWIInterrogate.served_user_number.str = "9838", + .component.invoke.args.qsig.MWIInterrogate.basic_service = 1, + }, + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_QSIG_MWIInterrogate, + .component.invoke.invoke_id = 111, + .component.invoke.args.qsig.MWIInterrogate.served_user_number.plan = 4, + .component.invoke.args.qsig.MWIInterrogate.served_user_number.length = 4, + .component.invoke.args.qsig.MWIInterrogate.served_user_number.str = "9838", + .component.invoke.args.qsig.MWIInterrogate.basic_service = 1, + .component.invoke.args.qsig.MWIInterrogate.msg_centre_id_present = 1, + .component.invoke.args.qsig.MWIInterrogate.msg_centre_id.type = 0, + .component.invoke.args.qsig.MWIInterrogate.msg_centre_id.u.integer = 532, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_MWIInterrogate, + .component.result.invoke_id = 112, + .component.result.args.qsig.MWIInterrogate.num_records = 1, + .component.result.args.qsig.MWIInterrogate.list[0].basic_service = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_QSIG_MWIInterrogate, + .component.result.invoke_id = 113, + .component.result.args.qsig.MWIInterrogate.num_records = 2, + .component.result.args.qsig.MWIInterrogate.list[0].basic_service = 1, + .component.result.args.qsig.MWIInterrogate.list[0].msg_centre_id_present = 1, + .component.result.args.qsig.MWIInterrogate.list[0].msg_centre_id.type = 0, + .component.result.args.qsig.MWIInterrogate.list[0].msg_centre_id.u.integer = 987, + .component.result.args.qsig.MWIInterrogate.list[0].number_of_messages_present = 1, + .component.result.args.qsig.MWIInterrogate.list[0].number_of_messages = 6548, + .component.result.args.qsig.MWIInterrogate.list[0].originating_number.plan = 4, + .component.result.args.qsig.MWIInterrogate.list[0].originating_number.length = 4, + .component.result.args.qsig.MWIInterrogate.list[0].originating_number.str = "9838", + .component.result.args.qsig.MWIInterrogate.list[0].timestamp_present = 1, + .component.result.args.qsig.MWIInterrogate.list[0].timestamp = "19970621194530", + .component.result.args.qsig.MWIInterrogate.list[0].priority_present = 1, + .component.result.args.qsig.MWIInterrogate.list[0].priority = 7, + .component.result.args.qsig.MWIInterrogate.list[1].basic_service = 1, + }, +/* *INDENT-ON* */ +}; + + +static const struct rose_message rose_dms100_msgs[] = { +/* *INDENT-OFF* */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_DMS100_RLT_OperationInd, + .component.invoke.invoke_id = ROSE_DMS100_RLT_OPERATION_IND, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_DMS100_RLT_OperationInd, + .component.result.invoke_id = ROSE_DMS100_RLT_OPERATION_IND, + .component.result.args.dms100.RLT_OperationInd.call_id = 130363, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_DMS100_RLT_ThirdParty, + .component.invoke.invoke_id = ROSE_DMS100_RLT_THIRD_PARTY, + .component.invoke.args.dms100.RLT_ThirdParty.call_id = 120047, + .component.invoke.args.dms100.RLT_ThirdParty.reason = 1, + }, + { + .type = ROSE_COMP_TYPE_RESULT, + .component.result.operation = ROSE_DMS100_RLT_ThirdParty, + .component.result.invoke_id = ROSE_DMS100_RLT_THIRD_PARTY, + }, +/* *INDENT-ON* */ +}; + + +static const struct rose_message rose_ni2_msgs[] = { +/* *INDENT-OFF* */ + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_NI2_InformationFollowing, + .component.invoke.invoke_id = 1, + .component.invoke.args.ni2.InformationFollowing.value = 7, + }, + + { + .type = ROSE_COMP_TYPE_INVOKE, + .component.invoke.operation = ROSE_NI2_InitiateTransfer, + .component.invoke.invoke_id = 2, + .component.invoke.args.ni2.InitiateTransfer.call_reference = 5, + }, +/* *INDENT-ON* */ +}; + +/* ------------------------------------------------------------------- */ + +static void rose_pri_message(struct pri *ctrl, char *stuff) +{ + fprintf(stdout, "%s", stuff); +} + +static void rose_pri_error(struct pri *ctrl, char *stuff) +{ + fprintf(stdout, "%s", stuff); + fprintf(stderr, "%s", stuff); +} + +/*! + * \internal + * \brief Test ROSE encoding and decoding the given message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param index Message number to report. + * \param header Facility message header data to encode. + * \param encode_msg Message data to encode. + * + * \return Nothing + */ +static void rose_test_msg(struct pri *ctrl, unsigned index, + const struct fac_extension_header *header, const struct rose_message *encode_msg) +{ + struct fac_extension_header decoded_header; + struct rose_message decoded_msg; + unsigned char *enc_pos; + unsigned char *enc_end; + const unsigned char *dec_pos; + const unsigned char *dec_end; + + static unsigned char buf[1024]; + + pri_message(ctrl, "\n\n"); + enc_end = buf + sizeof(buf); + enc_pos = facility_encode_header(ctrl, buf, enc_end, header); + if (!enc_pos) { + pri_error(ctrl, "Error: Message:%u failed to encode header\n", index); + } else { + enc_pos = rose_encode(ctrl, enc_pos, enc_end, encode_msg); + if (!enc_pos) { + pri_error(ctrl, "Error: Message:%u failed to encode ROSE\n", index); + } else { + pri_message(ctrl, "Message %u encoded length is %u\n", index, + (unsigned) (enc_pos - buf)); + + /* Clear the decoded message contents for comparison. */ + memset(&decoded_header, 0, sizeof(decoded_header)); + memset(&decoded_msg, 0, sizeof(decoded_msg)); + + dec_end = enc_pos; + dec_pos = facility_decode_header(ctrl, buf, dec_end, &decoded_header); + if (!dec_pos) { + pri_error(ctrl, "Error: Message:%u failed to decode header\n", index); + } else { + dec_pos = rose_decode(ctrl, dec_pos, dec_end, &decoded_msg); + if (!dec_pos) { + pri_error(ctrl, "Error: Message:%u failed to decode ROSE\n", index); + } else { + if (header + && memcmp(header, &decoded_header, sizeof(decoded_header))) { + pri_error(ctrl, "Error: Message:%u Header did not match\n", + index); + } + if (memcmp(encode_msg, &decoded_msg, sizeof(decoded_msg))) { + pri_error(ctrl, "Error: Message:%u ROSE did not match\n", index); + } + } + } + } + } + pri_message(ctrl, "\n\n" + "************************************************************\n"); +} + +/*! + * \internal + * \brief Test ROSE decoding messages of unusual encodings. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param name Test name for the encoded message. + * \param msg_buf Encoded message to decode. + * \param msg_len Length of encoded message buffer. + * + * \return Nothing + */ +static void rose_test_exception(struct pri *ctrl, const char *name, + const unsigned char *msg, size_t msg_len) +{ + const unsigned char *pos; + const unsigned char *end; + struct fac_extension_header header; + struct rose_message decoded_msg; + + pri_message(ctrl, "\n\n" + "%s test: Message encoded length is %u\n", name, (unsigned) msg_len); + + pos = msg; + end = msg + msg_len; + pos = facility_decode_header(ctrl, pos, end, &header); + if (!pos) { + pri_error(ctrl, "Error: %s test: Message failed to decode header\n", name); + } else { + pos = rose_decode(ctrl, pos, end, &decoded_msg); + if (!pos) { + pri_error(ctrl, "Error: %s test: Message failed to decode ROSE\n", name); + } + } + + pri_message(ctrl, "\n\n" + "************************************************************\n"); +} + +/*! + * \brief ROSE encode/decode test program. + * + * \param argc Program argument count. + * \param argv Program argument string array. + * + * \retval 0 on success. + * \retval Nonzero on error. + */ +int main(int argc, char *argv[]) +{ + unsigned index; + unsigned offset; + static struct pri dummy_ctrl; + + pri_set_message(rose_pri_message); + pri_set_error(rose_pri_error); + + memset(&dummy_ctrl, 0, sizeof(dummy_ctrl)); + dummy_ctrl.debug = PRI_DEBUG_APDU; + + offset = 0; + pri_message(&dummy_ctrl, "Encode/decode message(s)\n"); + if (argc <= 1) { + dummy_ctrl.switchtype = PRI_SWITCH_EUROISDN_E1; + for (index = 0; index < ARRAY_LEN(rose_etsi_msgs); ++index) { + rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], + &rose_etsi_msgs[index]); + } + offset += ARRAY_LEN(rose_etsi_msgs); + + dummy_ctrl.switchtype = PRI_SWITCH_QSIG; + for (index = 0; index < ARRAY_LEN(rose_qsig_msgs); ++index) { + rose_test_msg(&dummy_ctrl, index + offset, + &fac_headers[index % ARRAY_LEN(fac_headers)], &rose_qsig_msgs[index]); + } + offset += ARRAY_LEN(rose_qsig_msgs); + + dummy_ctrl.switchtype = PRI_SWITCH_DMS100; + for (index = 0; index < ARRAY_LEN(rose_dms100_msgs); ++index) { + rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], + &rose_dms100_msgs[index]); + } + offset += ARRAY_LEN(rose_dms100_msgs); + + dummy_ctrl.switchtype = PRI_SWITCH_NI2; + for (index = 0; index < ARRAY_LEN(rose_ni2_msgs); ++index) { + rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], + &rose_ni2_msgs[index]); + } + //offset += ARRAY_LEN(rose_ni2_msgs); + } else { + index = atoi(argv[1]); + + if (index < ARRAY_LEN(rose_etsi_msgs)) { + dummy_ctrl.switchtype = PRI_SWITCH_EUROISDN_E1; + rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], + &rose_etsi_msgs[index]); + return 0; + } + offset += ARRAY_LEN(rose_etsi_msgs); + index -= ARRAY_LEN(rose_etsi_msgs); + + if (index < ARRAY_LEN(rose_qsig_msgs)) { + dummy_ctrl.switchtype = PRI_SWITCH_QSIG; + rose_test_msg(&dummy_ctrl, index + offset, + &fac_headers[index % ARRAY_LEN(fac_headers)], &rose_qsig_msgs[index]); + return 0; + } + offset += ARRAY_LEN(rose_qsig_msgs); + index -= ARRAY_LEN(rose_qsig_msgs); + + if (index < ARRAY_LEN(rose_dms100_msgs)) { + dummy_ctrl.switchtype = PRI_SWITCH_DMS100; + rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], + &rose_dms100_msgs[index]); + return 0; + } + offset += ARRAY_LEN(rose_dms100_msgs); + index -= ARRAY_LEN(rose_dms100_msgs); + + if (index < ARRAY_LEN(rose_ni2_msgs)) { + dummy_ctrl.switchtype = PRI_SWITCH_NI2; + rose_test_msg(&dummy_ctrl, index + offset, &fac_headers[0], + &rose_ni2_msgs[index]); + return 0; + } + //offset += ARRAY_LEN(rose_ni2_msgs); + //index -= ARRAY_LEN(rose_ni2_msgs); + + fprintf(stderr, "Invalid option\n"); + return 0; + } + +/* ------------------------------------------------------------------- */ + + pri_message(&dummy_ctrl, "\n\n" + "Decode unusually encoded messages\n"); + + dummy_ctrl.switchtype = PRI_SWITCH_EUROISDN_E1; + + rose_test_exception(&dummy_ctrl, "Extra bytes on end", rose_etsi_extra, + sizeof(rose_etsi_extra)); + + rose_test_exception(&dummy_ctrl, "Indefinite length", rose_etsi_indefinite_len, + sizeof(rose_etsi_indefinite_len) - 2); + rose_test_exception(&dummy_ctrl, "Indefinite length (extra)", + rose_etsi_indefinite_len, sizeof(rose_etsi_indefinite_len)); + + rose_test_exception(&dummy_ctrl, "Unused components (indefinite length)", + rose_etsi_unused_indefinite_len, sizeof(rose_etsi_unused_indefinite_len) - 2); + rose_test_exception(&dummy_ctrl, "Unused components (indefinite length, extra)", + rose_etsi_unused_indefinite_len, sizeof(rose_etsi_unused_indefinite_len)); + + rose_test_exception(&dummy_ctrl, "Unused components", rose_etsi_unused, + sizeof(rose_etsi_unused) - 2); + rose_test_exception(&dummy_ctrl, "Unused components (extra)", rose_etsi_unused, + sizeof(rose_etsi_unused)); + +/* ------------------------------------------------------------------- */ + + pri_message(&dummy_ctrl, "\n\n" + "List of operation codes:\n"); + for (index = 0; index < ROSE_Num_Operation_Codes; ++index) { + pri_message(&dummy_ctrl, "%d: %s\n", index, rose_operation2str(index)); + } + pri_message(&dummy_ctrl, "\n\n" + "************************************************************\n"); + +/* ------------------------------------------------------------------- */ + + pri_message(&dummy_ctrl, "\n\n" + "List of error codes:\n"); + for (index = 0; index < ROSE_ERROR_Num_Codes; ++index) { + pri_message(&dummy_ctrl, "%d: %s\n", index, rose_error2str(index)); + } + pri_message(&dummy_ctrl, "\n\n" + "************************************************************\n"); + +/* ------------------------------------------------------------------- */ + + pri_message(&dummy_ctrl, "\n\n"); + pri_message(&dummy_ctrl, "sizeof(struct rose_message) = %u\n", + (unsigned) sizeof(struct rose_message)); + pri_message(&dummy_ctrl, "sizeof(struct rose_msg_invoke) = %u\n", + (unsigned) sizeof(struct rose_msg_invoke)); + pri_message(&dummy_ctrl, "sizeof(struct rose_msg_result) = %u\n", + (unsigned) sizeof(struct rose_msg_result)); + pri_message(&dummy_ctrl, "sizeof(struct rose_msg_error) = %u\n", + (unsigned) sizeof(struct rose_msg_error)); + pri_message(&dummy_ctrl, "sizeof(struct rose_msg_reject) = %u\n", + (unsigned) sizeof(struct rose_msg_reject)); + pri_message(&dummy_ctrl, "sizeof(union rose_msg_invoke_args) = %u\n", + (unsigned) sizeof(union rose_msg_invoke_args)); + pri_message(&dummy_ctrl, "sizeof(union rose_msg_result_args) = %u\n", + (unsigned) sizeof(union rose_msg_result_args)); + + pri_message(&dummy_ctrl, "\n"); + pri_message(&dummy_ctrl, "sizeof(struct roseQsigForwardingList) = %u\n", + (unsigned) sizeof(struct roseQsigForwardingList)); + + pri_message(&dummy_ctrl, "\n"); + pri_message(&dummy_ctrl, "sizeof(struct roseQsigCallRerouting_ARG) = %u\n", + (unsigned) sizeof(struct roseQsigCallRerouting_ARG)); + pri_message(&dummy_ctrl, "sizeof(struct roseQsigAocRateArg_ARG) = %u\n", + (unsigned) sizeof(struct roseQsigAocRateArg_ARG)); + pri_message(&dummy_ctrl, "sizeof(struct roseQsigMWIInterrogateRes) = %u\n", + (unsigned) sizeof(struct roseQsigMWIInterrogateRes)); + + pri_message(&dummy_ctrl, "\n"); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiForwardingList) = %u\n", + (unsigned) sizeof(struct roseEtsiForwardingList)); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiServedUserNumberList) = %u\n", + (unsigned) sizeof(struct roseEtsiServedUserNumberList)); + + pri_message(&dummy_ctrl, "\n"); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiCallRerouting_ARG) = %u\n", + (unsigned) sizeof(struct roseEtsiCallRerouting_ARG)); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiDiversionInformation_ARG) = %u\n", + (unsigned) sizeof(struct roseEtsiDiversionInformation_ARG)); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiAOCSCurrencyInfoList) = %u\n", + (unsigned) sizeof(struct roseEtsiAOCSCurrencyInfoList)); + +/* ------------------------------------------------------------------- */ + + return 0; +} + +/* ------------------------------------------------------------------- */ +/* end rosetest.c */ Property changes on: rosetest.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose_internal.h =================================================================== --- a/rose_internal.h (.../tags/1.4.10.2) (revision 0) +++ b/rose_internal.h (.../branches/1.4) (revision 1357) @@ -0,0 +1,477 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 Internal definitions and prototypes for ROSE. + * + * \author Richard Mudgett + */ + +#ifndef _LIBPRI_ROSE_INTERNAL_H +#define _LIBPRI_ROSE_INTERNAL_H + +#include "rose.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ------------------------------------------------------------------- */ + + +/* Embedded-Q931-Types */ +unsigned char *rose_enc_Q931ie(struct pri *ctrl, unsigned char *pos, unsigned char *end, + unsigned tag, const struct roseQ931ie *q931ie); + +const unsigned char *rose_dec_Q931ie(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct roseQ931ie *q931ie, + size_t contents_size); + +/* Addressing-Data-Elements */ +unsigned char *rose_enc_PartyNumber(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePartyNumber *party_number); +unsigned char *rose_enc_PartySubaddress(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePartySubaddress *party_subaddress); +unsigned char *rose_enc_Address(struct pri *ctrl, unsigned char *pos, unsigned char *end, + unsigned tag, const struct roseAddress *address); +unsigned char *rose_enc_PresentedNumberUnscreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePresentedNumberUnscreened *party); +unsigned char *rose_enc_NumberScreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseNumberScreened *screened); +unsigned char *rose_enc_PresentedNumberScreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePresentedNumberScreened *party); +unsigned char *rose_enc_AddressScreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseAddressScreened *screened); +unsigned char *rose_enc_PresentedAddressScreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePresentedAddressScreened *party); + +const unsigned char *rose_dec_PartyNumber(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartyNumber *party_number); +const unsigned char *rose_dec_PartySubaddress(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartySubaddress *party_subaddress); +const unsigned char *rose_dec_Address(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct roseAddress *address); +const unsigned char *rose_dec_PresentedNumberUnscreened(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePresentedNumberUnscreened *party); +const unsigned char *rose_dec_NumberScreened(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseNumberScreened *screened); +const unsigned char *rose_dec_PresentedNumberScreened(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePresentedNumberScreened *party); +const unsigned char *rose_dec_AddressScreened(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseAddressScreened *screened); +const unsigned char *rose_dec_PresentedAddressScreened(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePresentedAddressScreened *party); + +/* ETSI Advice-of-Charge (AOC) */ +unsigned char *rose_enc_etsi_ChargingRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_ChargingRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_AOCSCurrency_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_AOCSSpecialArr_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_AOCDCurrency_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_AOCDChargingUnit_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_AOCECurrency_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_AOCEChargingUnit_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_etsi_ChargingRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_ChargingRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_AOCSCurrency_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_AOCSSpecialArr_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_AOCDCurrency_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_AOCDChargingUnit_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_AOCECurrency_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_AOCEChargingUnit_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + +/* ETSI Call Diversion */ +unsigned char *rose_enc_etsi_ActivationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_DeactivationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_DeactivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_InterrogationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_InterrogationDiversion_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_DiversionInformation_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CallDeflection_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_InterrogateServedUserNumbers_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_etsi_ActivationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_DeactivationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_DeactivationStatusNotificationDiv_ARG(struct pri + *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_InterrogationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_InterrogationDiversion_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_DiversionInformation_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CallDeflection_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_CallRerouting_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_InterrogateServedUserNumbers_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + +/* ETSI Explicit Call Transfer (ECT) */ +unsigned char *rose_enc_etsi_ExplicitEctExecute_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_EctLinkIdRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_etsi_EctInform_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_EctLoopTest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); + +const unsigned char *rose_dec_etsi_ExplicitEctExecute_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_SubaddressTransfer_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_EctLinkIdRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_etsi_EctInform_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_EctLoopTest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); + +/* Q.SIG Name-Operations */ +unsigned char *rose_enc_qsig_Name(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct roseQsigName *name); + +const unsigned char *rose_dec_qsig_Name(struct pri *ctrl, const char *fname, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigName *name); + +unsigned char *rose_enc_qsig_CallingName_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CalledName_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_ConnectedName_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_BusyName_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_qsig_CallingName_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CalledName_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_ConnectedName_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_BusyName_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + +/* + * Q.SIG Dummy invoke/result argument used by: + * SS-AOC-Operations, + * Call-Transfer-Operations, + * Call-Diversion-Operations, + * and SS-MWI-Operations. + */ +unsigned char *rose_enc_qsig_DummyArg_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_DummyRes_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); + +const unsigned char *rose_dec_qsig_DummyArg_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_DummyRes_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); + +/* Q.SIG SS-AOC-Operations */ +unsigned char *rose_enc_qsig_ChargeRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_ChargeRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_qsig_AocFinal_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_AocInterim_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_AocRate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_AocComplete_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_AocComplete_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_qsig_AocDivChargeReq_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_qsig_ChargeRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_ChargeRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_qsig_AocFinal_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_AocInterim_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_AocRate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_AocComplete_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_AocComplete_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_qsig_AocDivChargeReq_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + +/* Q.SIG Call-Diversion-Operations */ +unsigned char *rose_enc_qsig_ActivateDiversionQ_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_DeactivateDiversionQ_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_InterrogateDiversionQ_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_InterrogateDiversionQ_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_qsig_CheckRestriction_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_qsig_ActivateDiversionQ_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_DeactivateDiversionQ_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_InterrogateDiversionQ_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_InterrogateDiversionQ_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_qsig_CheckRestriction_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CallRerouting_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + +/* Q.SIG Call-Transfer-Operations (CT) */ +unsigned char *rose_enc_qsig_CallTransferIdentify_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_qsig_CallTransferInitiate_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CallTransferSetup_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CallTransferActive_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CallTransferComplete_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_CallTransferUpdate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_qsig_CallTransferIdentify_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_qsig_CallTransferInitiate_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CallTransferSetup_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CallTransferActive_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CallTransferComplete_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_CallTransferUpdate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_SubaddressTransfer_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + +/* Q.SIG SS-MWI-Operations */ +unsigned char *rose_enc_qsig_MWIActivate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_MWIDeactivate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_MWIInterrogate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_qsig_MWIInterrogate_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); + +const unsigned char *rose_dec_qsig_MWIActivate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_MWIDeactivate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_MWIInterrogate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_qsig_MWIInterrogate_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); + +/* Northern Telecom DMS-100 operations */ +unsigned char *rose_enc_dms100_RLT_OperationInd_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args); +unsigned char *rose_enc_dms100_RLT_ThirdParty_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_dms100_RLT_OperationInd_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args); +const unsigned char *rose_dec_dms100_RLT_ThirdParty_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + +/* National ISDN 2 (NI2) operations */ +unsigned char *rose_enc_ni2_InformationFollowing_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args); +unsigned char *rose_enc_ni2_InitiateTransfer_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args); + +const unsigned char *rose_dec_ni2_InformationFollowing_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); +const unsigned char *rose_dec_ni2_InitiateTransfer_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args); + + +/* ------------------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBPRI_ROSE_INTERNAL_H */ +/* ------------------------------------------------------------------- */ +/* end rose_internal.h */ Property changes on: rose_internal.h ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: pri_q921.h =================================================================== --- a/pri_q921.h (.../tags/1.4.10.2) (revision 1357) +++ b/pri_q921.h (.../branches/1.4) (revision 1357) @@ -192,4 +192,10 @@ extern int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr); +extern int q921_transmit_uiframe(struct pri *pri, void *buf, int len); + +extern pri_event *q921_dchannel_up(struct pri *pri); + +extern pri_event *q921_dchannel_down(struct pri *pri); + #endif Index: pri_facility.c =================================================================== --- a/pri_facility.c (.../tags/1.4.10.2) (revision 1357) +++ b/pri_facility.c (.../branches/1.4) (revision 1357) @@ -33,872 +33,1340 @@ #include "pri_q921.h" #include "pri_q931.h" #include "pri_facility.h" +#include "rose.h" #include #include #include #include -static char *asn1id2text(int id) +static short get_invokeid(struct pri *ctrl) { - static char data[32]; - static char *strings[] = { - "none", - "Boolean", - "Integer", - "Bit String", - "Octet String", - "NULL", - "Object Identifier", - "Object Descriptor", - "External Reference", - "Real Number", - "Enumerated", - "Embedded PDV", - "UTF-8 String", - "Relative Object ID", - "Reserved (0e)", - "Reserved (0f)", - "Sequence", - "Set", - "Numeric String", - "Printable String", - "Tele-Text String", - "IA-5 String", - "UTC Time", - "Generalized Time", - }; - if (id > 0 && id <= 0x18) { - return strings[id]; - } else { - sprintf(data, "Unknown (%02x)", id); - return data; - } + ctrl = PRI_MASTER(ctrl); + return ++ctrl->last_invoke; } -static int asn1_dumprecursive(struct pri *pri, void *comp_ptr, int len, int level) +static int redirectingreason_from_q931(struct pri *ctrl, int redirectingreason) { - unsigned char *vdata = (unsigned char *)comp_ptr; - struct rose_component *comp; - int i = 0; - int j, k, l; - int clen = 0; + int value; - while (len > 0) { - GET_COMPONENT(comp, i, vdata, len); - pri_message(pri, "%*s%02X %04X", 2 * level, "", comp->type, comp->len); - if ((comp->type == 0) && (comp->len == 0)) - return clen + 2; - if ((comp->type & ASN1_PC_MASK) == ASN1_PRIMITIVE) { - for (j = 0; j < comp->len; ++j) - pri_message(pri, " %02X", comp->data[j]); + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + switch (redirectingreason) { + case PRI_REDIR_UNKNOWN: + value = QSIG_DIVERT_REASON_UNKNOWN; + break; + case PRI_REDIR_FORWARD_ON_BUSY: + value = QSIG_DIVERT_REASON_CFB; + break; + case PRI_REDIR_FORWARD_ON_NO_REPLY: + value = QSIG_DIVERT_REASON_CFNR; + break; + case PRI_REDIR_UNCONDITIONAL: + value = QSIG_DIVERT_REASON_CFU; + break; + case PRI_REDIR_DEFLECTION: + case PRI_REDIR_DTE_OUT_OF_ORDER: + case PRI_REDIR_FORWARDED_BY_DTE: + pri_message(ctrl, + "!! Don't know how to convert Q.931 redirection reason %d to Q.SIG\n", + redirectingreason); + /* Fall through */ + default: + value = QSIG_DIVERT_REASON_UNKNOWN; + break; } - if ((comp->type & ASN1_CLAN_MASK) == ASN1_UNIVERSAL) { - switch (comp->type & ASN1_TYPE_MASK) { - case 0: - pri_message(pri, " (none)"); - break; - case ASN1_BOOLEAN: - pri_message(pri, " (BOOLEAN: %d)", comp->data[0]); - break; - case ASN1_INTEGER: - for (k = l = 0; k < comp->len; ++k) - l = (l << 8) | comp->data[k]; - pri_message(pri, " (INTEGER: %d)", l); - break; - case ASN1_BITSTRING: - pri_message(pri, " (BITSTRING:"); - for (k = 0; k < comp->len; ++k) - pri_message(pri, " %02x", comp->data[k]); - pri_message(pri, ")"); - break; - case ASN1_OCTETSTRING: - pri_message(pri, " (OCTETSTRING:"); - for (k = 0; k < comp->len; ++k) - pri_message(pri, " %02x", comp->data[k]); - pri_message(pri, ")"); - break; - case ASN1_NULL: - pri_message(pri, " (NULL)"); - break; - case ASN1_OBJECTIDENTIFIER: - pri_message(pri, " (OBJECTIDENTIFIER:"); - for (k = 0; k < comp->len; ++k) - pri_message(pri, " %02x", comp->data[k]); - pri_message(pri, ")"); - break; - case ASN1_ENUMERATED: - for (k = l = 0; k < comp->len; ++k) - l = (l << 8) | comp->data[k]; - pri_message(pri, " (ENUMERATED: %d)", l); - break; - case ASN1_SEQUENCE: - pri_message(pri, " (SEQUENCE)"); - break; - default: - pri_message(pri, " (component %02x - %s)", comp->type, asn1id2text(comp->type & ASN1_TYPE_MASK)); - break; - } + break; + default: + switch (redirectingreason) { + case PRI_REDIR_UNKNOWN: + value = Q952_DIVERT_REASON_UNKNOWN; + break; + case PRI_REDIR_FORWARD_ON_BUSY: + value = Q952_DIVERT_REASON_CFB; + break; + case PRI_REDIR_FORWARD_ON_NO_REPLY: + value = Q952_DIVERT_REASON_CFNR; + break; + case PRI_REDIR_DEFLECTION: + value = Q952_DIVERT_REASON_CD; + break; + case PRI_REDIR_UNCONDITIONAL: + value = Q952_DIVERT_REASON_CFU; + break; + case PRI_REDIR_DTE_OUT_OF_ORDER: + case PRI_REDIR_FORWARDED_BY_DTE: + pri_message(ctrl, + "!! Don't know how to convert Q.931 redirection reason %d to Q.952\n", + redirectingreason); + /* Fall through */ + default: + value = Q952_DIVERT_REASON_UNKNOWN; + break; } - else if ((comp->type & ASN1_CLAN_MASK) == ASN1_CONTEXT_SPECIFIC) { - pri_message(pri, " (CONTEXT SPECIFIC [%d])", comp->type & ASN1_TYPE_MASK); + break; + } + + return value; +} + +static int redirectingreason_for_q931(struct pri *ctrl, int redirectingreason) +{ + int value; + + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + switch (redirectingreason) { + case QSIG_DIVERT_REASON_UNKNOWN: + value = PRI_REDIR_UNKNOWN; + break; + case QSIG_DIVERT_REASON_CFU: + value = PRI_REDIR_UNCONDITIONAL; + break; + case QSIG_DIVERT_REASON_CFB: + value = PRI_REDIR_FORWARD_ON_BUSY; + break; + case QSIG_DIVERT_REASON_CFNR: + value = PRI_REDIR_FORWARD_ON_NO_REPLY; + break; + default: + pri_message(ctrl, "!! Unknown Q.SIG diversion reason %d\n", + redirectingreason); + value = PRI_REDIR_UNKNOWN; + break; } - else { - pri_message(pri, " (component %02x)", comp->type); + break; + default: + switch (redirectingreason) { + case Q952_DIVERT_REASON_UNKNOWN: + value = PRI_REDIR_UNKNOWN; + break; + case Q952_DIVERT_REASON_CFU: + value = PRI_REDIR_UNCONDITIONAL; + break; + case Q952_DIVERT_REASON_CFB: + value = PRI_REDIR_FORWARD_ON_BUSY; + break; + case Q952_DIVERT_REASON_CFNR: + value = PRI_REDIR_FORWARD_ON_NO_REPLY; + break; + case Q952_DIVERT_REASON_CD: + value = PRI_REDIR_DEFLECTION; + break; + case Q952_DIVERT_REASON_IMMEDIATE: + pri_message(ctrl, + "!! Dont' know how to convert Q.952 diversion reason IMMEDIATE to PRI analog\n"); + value = PRI_REDIR_UNKNOWN; /* ??? */ + break; + default: + pri_message(ctrl, "!! Unknown Q.952 diversion reason %d\n", + redirectingreason); + value = PRI_REDIR_UNKNOWN; + break; } - pri_message(pri, "\n"); - if ((comp->type & ASN1_PC_MASK) == ASN1_CONSTRUCTOR) - j = asn1_dumprecursive(pri, comp->data, (comp->len ? comp->len : INT_MAX), level+1); - else - j = comp->len; - j += 2; - len -= j; - vdata += j; - clen += j; + break; } - return clen; + + return value; } -int asn1_dump(struct pri *pri, void *comp, int len) +/*! + * \brief Convert the Q.931 type-of-number field to facility. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param ton Q.931 ton/plan octet. + * + * \return PartyNumber enumeration value. + */ +static int typeofnumber_from_q931(struct pri *ctrl, int ton) { - return asn1_dumprecursive(pri, comp, len, 0); + int value; + + switch ((ton >> 4) & 0x03) { + default: + pri_message(ctrl, "!! Unsupported Q.931 TypeOfNumber value (%d)\n", ton); + /* fall through */ + case PRI_TON_UNKNOWN: + value = Q932_TON_UNKNOWN; + break; + case PRI_TON_INTERNATIONAL: + value = Q932_TON_INTERNATIONAL; + break; + case PRI_TON_NATIONAL: + value = Q932_TON_NATIONAL; + break; + case PRI_TON_NET_SPECIFIC: + value = Q932_TON_NET_SPECIFIC; + break; + case PRI_TON_SUBSCRIBER: + value = Q932_TON_SUBSCRIBER; + break; + case PRI_TON_ABBREVIATED: + value = Q932_TON_ABBREVIATED; + break; + } + + return value; } -static unsigned char get_invokeid(struct pri *pri) +static int typeofnumber_for_q931(struct pri *ctrl, int ton) { - return ++pri->last_invoke; + int value; + + switch (ton) { + default: + pri_message(ctrl, "!! Invalid TypeOfNumber %d\n", ton); + /* fall through */ + case Q932_TON_UNKNOWN: + value = PRI_TON_UNKNOWN; + break; + case Q932_TON_INTERNATIONAL: + value = PRI_TON_INTERNATIONAL; + break; + case Q932_TON_NATIONAL: + value = PRI_TON_NATIONAL; + break; + case Q932_TON_NET_SPECIFIC: + value = PRI_TON_NET_SPECIFIC; + break; + case Q932_TON_SUBSCRIBER: + value = PRI_TON_SUBSCRIBER; + break; + case Q932_TON_ABBREVIATED: + value = PRI_TON_ABBREVIATED; + break; + } + + return value << 4; } -struct addressingdataelements_presentednumberunscreened { - char partyaddress[21]; - char partysubaddress[21]; - int npi; /* Numbering Plan Indicator */ - int ton; /* Type Of Number */ - int pres; /* Presentation */ -}; +/*! + * \internal + * \brief Convert the Q.931 numbering plan field to facility. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param plan Q.931 ton/plan octet. + * + * \return PartyNumber enumeration value. + */ +static int numbering_plan_from_q931(struct pri *ctrl, int plan) +{ + int value; -struct addressingdataelements_presentednumberscreened { - char partyaddress[21]; - char partysubaddress[21]; - int npi; /* Numbering Plan Indicator */ - int ton; /* Type Of Number */ - int pres; /* Presentation */ - int scrind; /* Screening Indicator */ -}; + switch (plan & 0x0F) { + default: + pri_message(ctrl, "!! Unsupported Q.931 numbering plan value (%d)\n", plan); + /* fall through */ + case PRI_NPI_UNKNOWN: + value = 0; /* unknown */ + break; + case PRI_NPI_E163_E164: + value = 1; /* public */ + break; + case PRI_NPI_X121: + value = 3; /* data */ + break; + case PRI_NPI_F69: + value = 4; /* telex */ + break; + case PRI_NPI_NATIONAL: + value = 8; /* nationalStandard */ + break; + case PRI_NPI_PRIVATE: + value = 5; /* private */ + break; + } -#define PRI_CHECKOVERFLOW(size) \ - if (msgptr - message + (size) >= sizeof(message)) { \ - *msgptr = '\0'; \ - pri_message(pri, "%s", message); \ - msgptr = message; \ - } + return value; +} -static void dump_apdu(struct pri *pri, unsigned char *c, int len) +/*! + * \internal + * \brief Convert the PartyNumber numbering plan to Q.931 plan field value. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param plan PartyNumber enumeration value. + * + * \return Q.931 plan field value. + */ +static int numbering_plan_for_q931(struct pri *ctrl, int plan) { - #define MAX_APDU_LENGTH 255 - static char hexs[16] = "0123456789ABCDEF"; - int i; - char message[(2 + MAX_APDU_LENGTH * 3 + 6 + MAX_APDU_LENGTH + 3)] = ""; /* please adjust here, if you make changes below! */ - char *msgptr; - - msgptr = message; - *msgptr++ = ' '; - *msgptr++ = '['; - for (i=0; i> 4) & 0x0f]; - *msgptr++ = hexs[(c[i]) & 0x0f]; + int value; + + switch (plan) { + default: + pri_message(ctrl, + "!! Unsupported PartyNumber to Q.931 numbering plan value (%d)\n", plan); + /* fall through */ + case 0: /* unknown */ + value = PRI_NPI_UNKNOWN; + break; + case 1: /* public */ + value = PRI_NPI_E163_E164; + break; + case 3: /* data */ + value = PRI_NPI_X121; + break; + case 4: /* telex */ + value = PRI_NPI_F69; + break; + case 5: /* private */ + value = PRI_NPI_PRIVATE; + break; + case 8: /* nationalStandard */ + value = PRI_NPI_NATIONAL; + break; } - PRI_CHECKOVERFLOW(6); - strcpy(msgptr, " ] - ["); - msgptr += strlen(msgptr); - for (i=0; i '~')) ? '.' : c[i]; + + return value; +} + +/*! + * \internal + * \brief Convert the Q.931 number presentation field to facility. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param presentation Q.931 presentation/screening octet. + * \param number_present Non-zero if the number is available. + * + * \return Presented enumeration value. + */ +static int presentation_from_q931(struct pri *ctrl, int presentation, int number_present) +{ + int value; + + switch (presentation & PRI_PRES_RESTRICTION) { + case PRI_PRES_ALLOWED: + value = 0; /* presentationAllowed */ + break; + default: + pri_message(ctrl, "!! Unsupported Q.931 number presentation value (%d)\n", + presentation); + /* fall through */ + case PRI_PRES_RESTRICTED: + if (number_present) { + value = 3; /* presentationRestricted */ + } else { + value = 1; /* presentationRestricted */ + } + break; + case PRI_PRES_UNAVAILABLE: + value = 2; /* numberNotAvailableDueToInterworking */ + break; } - PRI_CHECKOVERFLOW(2); - *msgptr++ = ']'; - *msgptr++ = '\n'; - *msgptr = '\0'; - pri_message(pri, "%s", message); + + return value; } -#undef PRI_CHECKOVERFLOW -int redirectingreason_from_q931(struct pri *pri, int redirectingreason) +/*! + * \internal + * \brief Convert the Presented presentation + * to Q.931 presentation field value. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param presentation Presented value. + * + * \return Q.931 presentation field value. + */ +static int presentation_for_q931(struct pri *ctrl, int presentation) { - switch(pri->switchtype) { - case PRI_SWITCH_QSIG: - switch(redirectingreason) { - case PRI_REDIR_UNKNOWN: - return QSIG_DIVERT_REASON_UNKNOWN; - case PRI_REDIR_FORWARD_ON_BUSY: - return QSIG_DIVERT_REASON_CFB; - case PRI_REDIR_FORWARD_ON_NO_REPLY: - return QSIG_DIVERT_REASON_CFNR; - case PRI_REDIR_UNCONDITIONAL: - return QSIG_DIVERT_REASON_CFU; - case PRI_REDIR_DEFLECTION: - case PRI_REDIR_DTE_OUT_OF_ORDER: - case PRI_REDIR_FORWARDED_BY_DTE: - pri_message(pri, "!! Don't know how to convert Q.931 redirection reason %d to Q.SIG\n", redirectingreason); - /* Fall through */ - default: - return QSIG_DIVERT_REASON_UNKNOWN; - } - default: - switch(redirectingreason) { - case PRI_REDIR_UNKNOWN: - return Q952_DIVERT_REASON_UNKNOWN; - case PRI_REDIR_FORWARD_ON_BUSY: - return Q952_DIVERT_REASON_CFB; - case PRI_REDIR_FORWARD_ON_NO_REPLY: - return Q952_DIVERT_REASON_CFNR; - case PRI_REDIR_DEFLECTION: - return Q952_DIVERT_REASON_CD; - case PRI_REDIR_UNCONDITIONAL: - return Q952_DIVERT_REASON_CFU; - case PRI_REDIR_DTE_OUT_OF_ORDER: - case PRI_REDIR_FORWARDED_BY_DTE: - pri_message(pri, "!! Don't know how to convert Q.931 redirection reason %d to Q.952\n", redirectingreason); - /* Fall through */ - default: - return Q952_DIVERT_REASON_UNKNOWN; - } + int value; + + switch (presentation) { + case 0: /* presentationAllowed */ + value = PRI_PRES_ALLOWED; + break; + default: + pri_message(ctrl, + "!! Unsupported Presented to Q.931 value (%d)\n", + presentation); + /* fall through */ + case 1: /* presentationRestricted */ + case 3: /* presentationRestricted */ + value = PRI_PRES_RESTRICTED; + break; + case 2: /* numberNotAvailableDueToInterworking */ + value = PRI_PRES_UNAVAILABLE; + break; } + + return value; } -static int redirectingreason_for_q931(struct pri *pri, int redirectingreason) +/*! + * \internal + * \brief Convert the Q.931 number presentation field to Q.SIG name presentation. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param presentation Q.931 presentation/screening octet. + * \param name_present Non-zero if the name is available. + * + * \return Name presentation enumeration value. + */ +static int qsig_name_presentation_from_q931(struct pri *ctrl, int presentation, int name_present) { - switch(pri->switchtype) { - case PRI_SWITCH_QSIG: - switch(redirectingreason) { - case QSIG_DIVERT_REASON_UNKNOWN: - return PRI_REDIR_UNKNOWN; - case QSIG_DIVERT_REASON_CFU: - return PRI_REDIR_UNCONDITIONAL; - case QSIG_DIVERT_REASON_CFB: - return PRI_REDIR_FORWARD_ON_BUSY; - case QSIG_DIVERT_REASON_CFNR: - return PRI_REDIR_FORWARD_ON_NO_REPLY; - default: - pri_message(pri, "!! Unknown Q.SIG diversion reason %d\n", redirectingreason); - return PRI_REDIR_UNKNOWN; - } - default: - switch(redirectingreason) { - case Q952_DIVERT_REASON_UNKNOWN: - return PRI_REDIR_UNKNOWN; - case Q952_DIVERT_REASON_CFU: - return PRI_REDIR_UNCONDITIONAL; - case Q952_DIVERT_REASON_CFB: - return PRI_REDIR_FORWARD_ON_BUSY; - case Q952_DIVERT_REASON_CFNR: - return PRI_REDIR_FORWARD_ON_NO_REPLY; - case Q952_DIVERT_REASON_CD: - return PRI_REDIR_DEFLECTION; - case Q952_DIVERT_REASON_IMMEDIATE: - pri_message(pri, "!! Dont' know how to convert Q.952 diversion reason IMMEDIATE to PRI analog\n"); - return PRI_REDIR_UNKNOWN; /* ??? */ - default: - pri_message(pri, "!! Unknown Q.952 diversion reason %d\n", redirectingreason); - return PRI_REDIR_UNKNOWN; - } + int value; + + switch (presentation & PRI_PRES_RESTRICTION) { + case PRI_PRES_ALLOWED: + if (name_present) { + value = 1; /* presentation_allowed */ + } else { + value = 4; /* name_not_available */ + } + break; + default: + pri_message(ctrl, "!! Unsupported Q.931 number presentation value (%d)\n", + presentation); + /* fall through */ + case PRI_PRES_RESTRICTED: + if (name_present) { + value = 2; /* presentation_restricted */ + } else { + value = 3; /* presentation_restricted_null */ + } + break; + case PRI_PRES_UNAVAILABLE: + value = 4; /* name_not_available */ + break; } + + return value; } -int typeofnumber_from_q931(struct pri *pri, int ton) +/*! + * \internal + * \brief Convert the Q.SIG name presentation to Q.931 presentation field value. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param presentation Q.SIG name presentation value. + * + * \return Q.931 presentation field value. + */ +static int qsig_name_presentation_for_q931(struct pri *ctrl, int presentation) { - switch(ton) { - case PRI_TON_INTERNATIONAL: - return Q932_TON_INTERNATIONAL; - case PRI_TON_NATIONAL: - return Q932_TON_NATIONAL; - case PRI_TON_NET_SPECIFIC: - return Q932_TON_NET_SPECIFIC; - case PRI_TON_SUBSCRIBER: - return Q932_TON_SUBSCRIBER; - case PRI_TON_ABBREVIATED: - return Q932_TON_ABBREVIATED; - case PRI_TON_RESERVED: - default: - pri_message(pri, "!! Unsupported Q.931 TypeOfNumber value (%d)\n", ton); - /* fall through */ - case PRI_TON_UNKNOWN: - return Q932_TON_UNKNOWN; + int value; + + switch (presentation) { + case 1: /* presentation_allowed */ + value = PRI_PRES_ALLOWED; + break; + default: + pri_message(ctrl, + "!! Unsupported Q.SIG name presentation to Q.931 value (%d)\n", + presentation); + /* fall through */ + case 2: /* presentation_restricted */ + case 3: /* presentation_restricted_null */ + value = PRI_PRES_RESTRICTED; + break; + case 0: /* optional_name_not_present */ + case 4: /* name_not_available */ + value = PRI_PRES_UNAVAILABLE; + break; } + + return value; } -static int typeofnumber_for_q931(struct pri *pri, int ton) +/*! + * \internal + * \brief Convert number presentation to Q.SIG diversion subscription notification. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param presentation Number presentation value. + * + * \return Q.SIG diversion subscription notification value. + */ +static int presentation_to_subscription(struct pri *ctrl, int presentation) { - switch (ton) { - case Q932_TON_UNKNOWN: - return PRI_TON_UNKNOWN; - case Q932_TON_INTERNATIONAL: - return PRI_TON_INTERNATIONAL; - case Q932_TON_NATIONAL: - return PRI_TON_NATIONAL; - case Q932_TON_NET_SPECIFIC: - return PRI_TON_NET_SPECIFIC; - case Q932_TON_SUBSCRIBER: - return PRI_TON_SUBSCRIBER; - case Q932_TON_ABBREVIATED: - return PRI_TON_ABBREVIATED; - default: - pri_message(pri, "!! Invalid Q.932 TypeOfNumber %d\n", ton); - return PRI_TON_UNKNOWN; + /* derive subscription value from presentation value */ + + switch (presentation & PRI_PRES_RESTRICTION) { + case PRI_PRES_ALLOWED: + return QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR; + case PRI_PRES_RESTRICTED: + return QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR; + case PRI_PRES_UNAVAILABLE: /* Number not available due to interworking */ + return QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR; /* ?? QSIG_NO_NOTIFICATION */ + default: + pri_message(ctrl, "!! Unknown Q.SIG presentationIndicator 0x%02x\n", + presentation); + return QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR; } } -int asn1_name_decode(void * data, int len, char *namebuf, int buflen) +/*! + * \internal + * \brief Copy the given rose party number to the q931_party_number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_number Q.931 party number structure + * \param rose_number ROSE party number structure + * + * \note It is assumed that the q931_number has been initialized before calling. + * + * \return Nothing + */ +static void rose_copy_number_to_q931(struct pri *ctrl, + struct q931_party_number *q931_number, const struct rosePartyNumber *rose_number) { - struct rose_component *comp = (struct rose_component*)data; - int datalen = 0, res = 0; + //q931_party_number_init(q931_number); + libpri_copy_string(q931_number->str, (char *) rose_number->str, + sizeof(q931_number->str)); + q931_number->plan = numbering_plan_for_q931(ctrl, rose_number->plan) + | typeofnumber_for_q931(ctrl, rose_number->ton); + q931_number->valid = 1; +} - if (comp->len == ASN1_LEN_INDEF) { - datalen = strlen((char *)comp->data); - res = datalen + 2; - } else - datalen = res = comp->len; +/*! + * \internal + * \brief Copy the given rose subaddress to the q931_party_subaddress. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_subaddress Q.931 party subaddress structure + * \param rose_subaddress ROSE subaddress structure + * + * \note It is assumed that the q931_subaddress has been initialized before calling. + * + * \return Nothing + */ +static void rose_copy_subaddress_to_q931(struct pri *ctrl, + struct q931_party_subaddress *q931_subaddress, + const struct rosePartySubaddress *rose_subaddress) +{ + //q931_party_subaddress_init(q931_subaddress); + if (!rose_subaddress->length) { + /* Subaddress is not present. */ + return; + } - if (datalen > buflen) { - /* Truncate */ - datalen = buflen; + switch (rose_subaddress->type) { + case 0:/* UserSpecified */ + q931_subaddress->type = 2;/* user_specified */ + q931_subaddress->valid = 1; + q931_subaddress->length = rose_subaddress->length; + if (sizeof(q931_subaddress->data) <= q931_subaddress->length) { + q931_subaddress->length = sizeof(q931_subaddress->data) - 1; + } + memcpy(q931_subaddress->data, rose_subaddress->u.user_specified.information, + q931_subaddress->length); + q931_subaddress->data[q931_subaddress->length] = '\0'; + if (rose_subaddress->u.user_specified.odd_count_present) { + q931_subaddress->odd_even_indicator = + rose_subaddress->u.user_specified.odd_count; + } + break; + case 1:/* NSAP */ + q931_subaddress->type = 0;/* nsap */ + q931_subaddress->valid = 1; + libpri_copy_string((char *) q931_subaddress->data, + (char *) rose_subaddress->u.nsap, sizeof(q931_subaddress->data)); + q931_subaddress->length = strlen((char *) q931_subaddress->data); + break; + default: + /* Don't know how to encode so assume it is not present. */ + break; } - memcpy(namebuf, comp->data, datalen); - return res + 2; } -int asn1_string_encode(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_len) +/*! + * \internal + * \brief Copy the given rose address to the q931_party_id address. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_address Q.931 party id structure to fill address + * \param rose_address ROSE address structure + * + * \note It is assumed that the q931_address has been initialized before calling. + * + * \return Nothing + */ +static void rose_copy_address_to_q931(struct pri *ctrl, + struct q931_party_id *q931_address, const struct roseAddress *rose_address) { - struct rose_component *comp = NULL; - - if (len < 2 + src_len) - return -1; + rose_copy_number_to_q931(ctrl, &q931_address->number, &rose_address->number); + rose_copy_subaddress_to_q931(ctrl, &q931_address->subaddress, + &rose_address->subaddress); +} - if (max_len && (src_len > max_len)) - src_len = max_len; +/*! + * \internal + * \brief Copy the given rose presented screened party number to the q931_party_number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_number Q.931 party number structure + * \param rose_presented ROSE presented screened party number structure + * + * \return Nothing + */ +static void rose_copy_presented_number_screened_to_q931(struct pri *ctrl, + struct q931_party_number *q931_number, + const struct rosePresentedNumberScreened *rose_presented) +{ + q931_party_number_init(q931_number); + q931_number->valid = 1; + q931_number->presentation = presentation_for_q931(ctrl, rose_presented->presentation); + switch (rose_presented->presentation) { + case 0: /* presentationAllowedNumber */ + case 3: /* presentationRestrictedNumber */ + q931_number->presentation |= + (rose_presented->screened.screening_indicator & PRI_PRES_NUMBER_TYPE); + rose_copy_number_to_q931(ctrl, q931_number, + &rose_presented->screened.number); + break; + default: + q931_number->presentation |= PRI_PRES_USER_NUMBER_UNSCREENED; + break; + } +} - comp = (struct rose_component *)data; - comp->type = asn1_type; - comp->len = src_len; - memcpy(comp->data, src, src_len); - - return 2 + src_len; +/*! + * \internal + * \brief Copy the given rose presented unscreened party number to the q931_party_number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_number Q.931 party number structure + * \param rose_presented ROSE presented unscreened party number structure + * + * \return Nothing + */ +static void rose_copy_presented_number_unscreened_to_q931(struct pri *ctrl, + struct q931_party_number *q931_number, + const struct rosePresentedNumberUnscreened *rose_presented) +{ + q931_party_number_init(q931_number); + q931_number->valid = 1; + q931_number->presentation = presentation_for_q931(ctrl, + rose_presented->presentation) | PRI_PRES_USER_NUMBER_UNSCREENED; + switch (rose_presented->presentation) { + case 0: /* presentationAllowedNumber */ + case 3: /* presentationRestrictedNumber */ + rose_copy_number_to_q931(ctrl, q931_number, &rose_presented->number); + break; + default: + break; + } } -int asn1_copy_string(char * buf, int buflen, struct rose_component *comp) +/*! + * \internal + * \brief Copy the given rose presented screened party address to the q931_party_number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param q931_address Q.931 party id structure to fill the address + * \param rose_presented ROSE presented screened party address structure + * + * \return Nothing + */ +static void rose_copy_presented_address_screened_to_q931(struct pri *ctrl, + struct q931_party_id *q931_address, + const struct rosePresentedAddressScreened *rose_presented) { - int res; - int datalen; + q931_party_number_init(&q931_address->number); + q931_party_subaddress_init(&q931_address->subaddress); + q931_address->number.valid = 1; + q931_address->number.presentation = presentation_for_q931(ctrl, + rose_presented->presentation); + switch (rose_presented->presentation) { + case 0: /* presentationAllowedAddress */ + case 3: /* presentationRestrictedAddress */ + q931_address->number.presentation |= + (rose_presented->screened.screening_indicator & PRI_PRES_NUMBER_TYPE); + rose_copy_number_to_q931(ctrl, &q931_address->number, + &rose_presented->screened.number); + rose_copy_subaddress_to_q931(ctrl, &q931_address->subaddress, + &rose_presented->screened.subaddress); + break; + default: + q931_address->number.presentation |= PRI_PRES_USER_NUMBER_UNSCREENED; + break; + } +} - if ((comp->len > buflen) && (comp->len != ASN1_LEN_INDEF)) - return -1; +/*! + * \internal + * \brief Copy the given rose party name to the q931_party_name + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param qsig_name Q.SIG party name structure + * \param rose_name Q.SIG ROSE party name structure + * + * \return Nothing + */ +static void rose_copy_name_to_q931(struct pri *ctrl, + struct q931_party_name *qsig_name, const struct roseQsigName *rose_name) +{ + //q931_party_name_init(qsig_name); + qsig_name->valid = 1; + qsig_name->presentation = qsig_name_presentation_for_q931(ctrl, + rose_name->presentation); + qsig_name->char_set = rose_name->char_set; + libpri_copy_string(qsig_name->str, (char *) rose_name->data, sizeof(qsig_name->str)); +} - if (comp->len == ASN1_LEN_INDEF) { - datalen = strlen((char*)comp->data); - res = datalen + 2; - } else - res = datalen = comp->len; +/*! + * \internal + * \brief Copy the given q931_party_number to the rose party number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_number ROSE party number structure + * \param q931_number Q.931 party number structure + * + * \return Nothing + */ +static void q931_copy_number_to_rose(struct pri *ctrl, + struct rosePartyNumber *rose_number, const struct q931_party_number *q931_number) +{ + rose_number->plan = numbering_plan_from_q931(ctrl, q931_number->plan); + rose_number->ton = typeofnumber_from_q931(ctrl, q931_number->plan); + /* Truncate the q931_number->str if necessary. */ + libpri_copy_string((char *) rose_number->str, q931_number->str, + sizeof(rose_number->str)); + rose_number->length = strlen((char *) rose_number->str); +} - memcpy(buf, comp->data, datalen); - buf[datalen] = 0; +/*! + * \internal + * \brief Copy the given q931_party_subaddress to the rose subaddress. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_subaddress ROSE subaddress structure + * \param q931_subaddress Q.931 party subaddress structure + * + * \return Nothing + */ +static void q931_copy_subaddress_to_rose(struct pri *ctrl, + struct rosePartySubaddress *rose_subaddress, + const struct q931_party_subaddress *q931_subaddress) +{ + if (!q931_subaddress->valid) { + /* Subaddress is not present. */ + rose_subaddress->length = 0; + return; + } - return res; + switch (q931_subaddress->type) { + case 0: /* NSAP */ + rose_subaddress->type = 1;/* NSAP */ + libpri_copy_string((char *) rose_subaddress->u.nsap, + (char *) q931_subaddress->data, sizeof(rose_subaddress->u.nsap)); + rose_subaddress->length = strlen((char *) rose_subaddress->u.nsap); + break; + case 2: /* user_specified */ + rose_subaddress->type = 0;/* UserSpecified */ + rose_subaddress->length = q931_subaddress->length; + if (sizeof(rose_subaddress->u.user_specified.information) + <= rose_subaddress->length) { + rose_subaddress->length = + sizeof(rose_subaddress->u.user_specified.information) - 1; + } else { + if (q931_subaddress->odd_even_indicator) { + rose_subaddress->u.user_specified.odd_count_present = 1; + rose_subaddress->u.user_specified.odd_count = 1; + } + } + memcpy(rose_subaddress->u.user_specified.information, q931_subaddress->data, + rose_subaddress->length); + rose_subaddress->u.user_specified.information[rose_subaddress->length] = '\0'; + break; + default: + /* Don't know how to encode so assume it is not present. */ + rose_subaddress->length = 0; + break; + } } -static int rose_number_digits_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) +/*! + * \internal + * \brief Copy the given q931_party_id address to the rose address. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_address ROSE address structure + * \param q931_address Q.931 party id structure to give address + * + * \return Nothing + */ +static void q931_copy_address_to_rose(struct pri *ctrl, struct roseAddress *rose_address, + const struct q931_party_id *q931_address) { - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - int datalen = 0; - int res = 0; + q931_copy_number_to_rose(ctrl, &rose_address->number, &q931_address->number); + q931_copy_subaddress_to_rose(ctrl, &rose_address->subaddress, + &q931_address->subaddress); +} - do { - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_NUMERICSTRING, "Don't know what to do with PublicPartyNumber ROSE component type 0x%x\n"); - if(comp->len > 20 && comp->len != ASN1_LEN_INDEF) { - pri_message(pri, "!! Oversized NumberDigits component (%d)\n", comp->len); - return -1; - } - if (comp->len == ASN1_LEN_INDEF) { - datalen = strlen((char *)comp->data); - res = datalen + 2; - } else - res = datalen = comp->len; - - memcpy(value->partyaddress, comp->data, datalen); - value->partyaddress[datalen] = '\0'; +/*! + * \internal + * \brief Copy the given q931_party_number to the rose presented screened party number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_presented ROSE presented screened party number structure + * \param q931_number Q.931 party number structure + * + * \return Nothing + */ +static void q931_copy_presented_number_screened_to_rose(struct pri *ctrl, + struct rosePresentedNumberScreened *rose_presented, + const struct q931_party_number *q931_number) +{ + if (q931_number->valid) { + rose_presented->presentation = + presentation_from_q931(ctrl, q931_number->presentation, q931_number->str[0]); + rose_presented->screened.screening_indicator = + q931_number->presentation & PRI_PRES_NUMBER_TYPE; + q931_copy_number_to_rose(ctrl, &rose_presented->screened.number, q931_number); + } else { + rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ + } +} - return res + 2; +/*! + * \internal + * \brief Copy the given q931_party_number to the rose presented unscreened party number + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_presented ROSE presented unscreened party number structure + * \param q931_number Q.931 party number structure + * + * \return Nothing + */ +static void q931_copy_presented_number_unscreened_to_rose(struct pri *ctrl, + struct rosePresentedNumberUnscreened *rose_presented, + const struct q931_party_number *q931_number) +{ + if (q931_number->valid) { + rose_presented->presentation = + presentation_from_q931(ctrl, q931_number->presentation, q931_number->str[0]); + q931_copy_number_to_rose(ctrl, &rose_presented->number, q931_number); + } else { + rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ } - while(0); - - return -1; } -static int rose_public_party_number_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) +#if 0 /* In case it is needed in the future */ +/*! + * \internal + * \brief Copy the given q931_party_number to the rose presented screened party address + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_presented ROSE presented screened party address structure + * \param q931_address Q.931 party id structure to get the address + * + * \return Nothing + */ +static void q931_copy_presented_address_screened_to_rose(struct pri *ctrl, + struct rosePresentedAddressScreened *rose_presented, + const struct q931_party_id *q931_address) { - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - int ton; - int res = 0; + if (q931_address->number.valid) { + rose_presented->presentation = + presentation_from_q931(ctrl, q931_address->number.presentation, + q931_address->number.str[0]); + rose_presented->screened.screening_indicator = + q931_address->number.presentation & PRI_PRES_NUMBER_TYPE; + q931_copy_number_to_rose(ctrl, &rose_presented->screened.number, + &q931_address->number); + q931_copy_subaddress_to_rose(ctrl, &rose_presented->screened.subaddress, + &q931_address->subaddress); + } else { + rose_presented->presentation = 2;/* numberNotAvailableDueToInterworking */ + } +} +#endif /* In case it is needed in the future */ - if (len < 2) - return -1; +/*! + * \internal + * \brief Copy the given q931_party_name to the rose party name + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param rose_name Q.SIG ROSE party name structure + * \param qsig_name Q.SIG party name structure + * + * \return Nothing + */ +static void q931_copy_name_to_rose(struct pri *ctrl, + struct roseQsigName *rose_name, const struct q931_party_name *qsig_name) +{ + if (qsig_name->valid) { + rose_name->presentation = qsig_name_presentation_from_q931(ctrl, + qsig_name->presentation, qsig_name->str[0]); + rose_name->char_set = qsig_name->char_set; + /* Truncate the qsig_name->str if necessary. */ + libpri_copy_string((char *) rose_name->data, qsig_name->str, sizeof(rose_name->data)); + rose_name->length = strlen((char *) rose_name->data); + } else { + rose_name->presentation = 4;/* name_not_available */ + } +} - do { - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Don't know what to do with PublicPartyNumber ROSE component type 0x%x\n"); - ASN1_GET_INTEGER(comp, ton); - NEXT_COMPONENT(comp, i); - ton = typeofnumber_for_q931(pri, ton); +/*! + * \internal + * \brief Encode the Q.SIG DivertingLegInformation1 invoke message. + * + * \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 call Call leg from which to encode diversion leg 1. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_diverting_leg_information1(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; - res = rose_number_digits_decode(pri, call, &vdata[i], len-i, value); - if (res < 0) - return -1; - value->ton = ton; + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } - return res + 3; + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_DivertingLegInformation1; + msg.invoke_id = get_invokeid(ctrl); + msg.args.qsig.DivertingLegInformation1.diversion_reason = + redirectingreason_from_q931(ctrl, call->redirecting.reason); - } while(0); - return -1; + /* subscriptionOption is the redirecting.to.number.presentation */ + msg.args.qsig.DivertingLegInformation1.subscription_option = + presentation_to_subscription(ctrl, call->redirecting.to.number.presentation); + + /* nominatedNr is the redirecting.to.number */ + q931_copy_number_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation1.nominated_number, + &call->redirecting.to.number); + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; } -static int rose_private_party_number_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) +/*! + * \internal + * \brief Encode the ETSI DivertingLegInformation1 invoke message. + * + * \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 call Call leg from which to encode diversion leg 1. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_diverting_leg_information1(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) { - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - int ton; - int res = 0; + struct rose_msg_invoke msg; - if (len < 2) - return -1; + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } - do { - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Don't know what to do with PrivatePartyNumber ROSE component type 0x%x\n"); - ASN1_GET_INTEGER(comp, ton); - NEXT_COMPONENT(comp, i); - ton = typeofnumber_for_q931(pri, ton); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_DivertingLegInformation1; + msg.invoke_id = get_invokeid(ctrl); + msg.args.etsi.DivertingLegInformation1.diversion_reason = + redirectingreason_from_q931(ctrl, call->redirecting.reason); - res = rose_number_digits_decode(pri, call, &vdata[i], len-i, value); - if (res < 0) - return -1; - value->ton = ton; + if (call->redirecting.to.number.valid) { + msg.args.etsi.DivertingLegInformation1.subscription_option = 2; - return res + 3; + /* divertedToNumber is the redirecting.to.number */ + msg.args.etsi.DivertingLegInformation1.diverted_to_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.DivertingLegInformation1.diverted_to, + &call->redirecting.to.number); + } else { + msg.args.etsi.DivertingLegInformation1.subscription_option = 1; + } + pos = rose_encode_invoke(ctrl, pos, end, &msg); - } while(0); - return -1; + return pos; } -static int rose_address_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) +/*! + * \brief Encode and queue the DivertingLegInformation1 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode diversion leg 1. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rose_diverting_leg_information1_encode(struct pri *ctrl, q931_call *call) { - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - int res = 0; + unsigned char buffer[256]; + unsigned char *end; - do { - GET_COMPONENT(comp, i, vdata, len); + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = enc_etsi_diverting_leg_information1(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + case PRI_SWITCH_QSIG: + end = enc_qsig_diverting_leg_information1(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + default: + return -1; + } + if (!end) { + return -1; + } - switch(comp->type) { - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0): /* [0] unknownPartyNumber */ - res = rose_number_digits_decode(pri, call, comp->data, comp->len, value); - if (res < 0) - return -1; - value->npi = PRI_NPI_UNKNOWN; - value->ton = PRI_TON_UNKNOWN; - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0): /* [0] unknownPartyNumber */ - res = asn1_copy_string(value->partyaddress, sizeof(value->partyaddress), comp); - if (res < 0) - return -1; - value->npi = PRI_NPI_UNKNOWN; - value->ton = PRI_TON_UNKNOWN; - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1): /* [1] publicPartyNumber */ - res = rose_public_party_number_decode(pri, call, comp->data, comp->len, value); - if (res < 0) - return -1; - value->npi = PRI_NPI_E163_E164; - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_2): /* [2] nsapEncodedNumber */ - pri_message(pri, "!! NsapEncodedNumber isn't handled\n"); - return -1; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_3): /* [3] dataPartyNumber */ - if(rose_number_digits_decode(pri, call, comp->data, comp->len, value)) - return -1; - value->npi = PRI_NPI_X121 /* ??? */; - value->ton = PRI_TON_UNKNOWN /* ??? */; - pri_message(pri, "!! dataPartyNumber isn't handled\n"); - return -1; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_4): /* [4] telexPartyNumber */ - res = rose_number_digits_decode(pri, call, comp->data, comp->len, value); - if (res < 0) - return -1; - value->npi = PRI_NPI_F69 /* ??? */; - value->ton = PRI_TON_UNKNOWN /* ??? */; - pri_message(pri, "!! telexPartyNumber isn't handled\n"); - return -1; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_5): /* [5] priavePartyNumber */ - res = rose_private_party_number_decode(pri, call, comp->data, comp->len, value); - if (res < 0) - return -1; - value->npi = PRI_NPI_PRIVATE; - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_8): /* [8] nationalStandardPartyNumber */ - res = rose_number_digits_decode(pri, call, comp->data, comp->len, value); - if (res < 0) - return -1; - value->npi = PRI_NPI_NATIONAL; - value->ton = PRI_TON_NATIONAL; - break; - default: - pri_message(pri, "!! Unknown Party number component received 0x%X\n", comp->type); - return -1; + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} + +/*! + * \internal + * \brief Encode the Q.SIG DivertingLegInformation2 invoke message. + * + * \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 call Call leg from which to encode diversion leg 2. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_diverting_leg_information2(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_DivertingLegInformation2; + msg.invoke_id = get_invokeid(ctrl); + + /* diversionCounter is the redirecting.count */ + msg.args.qsig.DivertingLegInformation2.diversion_counter = call->redirecting.count; + + msg.args.qsig.DivertingLegInformation2.diversion_reason = + redirectingreason_from_q931(ctrl, call->redirecting.reason); + + /* divertingNr is the redirecting.from.number */ + msg.args.qsig.DivertingLegInformation2.diverting_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation2.diverting, + &call->redirecting.from.number); + + /* redirectingName is the redirecting.from.name */ + if (call->redirecting.from.name.valid) { + msg.args.qsig.DivertingLegInformation2.redirecting_name_present = 1; + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation2.redirecting_name, + &call->redirecting.from.name); + } + + if (1 < call->redirecting.count) { + /* originalCalledNr is the redirecting.orig_called.number */ + msg.args.qsig.DivertingLegInformation2.original_called_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation2.original_called, + &call->redirecting.orig_called.number); + + msg.args.qsig.DivertingLegInformation2.original_diversion_reason_present = 1; + if (call->redirecting.orig_called.number.valid) { + msg.args.qsig.DivertingLegInformation2.original_diversion_reason = + redirectingreason_from_q931(ctrl, call->redirecting.orig_reason); + } else { + msg.args.qsig.DivertingLegInformation2.original_diversion_reason = + QSIG_DIVERT_REASON_UNKNOWN; } - ASN1_FIXUP_LEN(comp, res); - NEXT_COMPONENT(comp, i); - if(i < len) - pri_message(pri, "!! not all information is handled from Address component\n"); - return res + 2; + + /* originalCalledName is the redirecting.orig_called.name */ + if (call->redirecting.orig_called.name.valid) { + msg.args.qsig.DivertingLegInformation2.original_called_name_present = 1; + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation2.original_called_name, + &call->redirecting.orig_called.name); + } } - while (0); - return -1; + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; } -static int rose_presented_number_unscreened_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) +/*! + * \internal + * \brief Encode the ETSI DivertingLegInformation2 invoke message. + * + * \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 call Call leg from which to encode diversion leg 2. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_diverting_leg_information2(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) { - int i = 0; - int size = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; + struct rose_msg_invoke msg; - /* Fill in default values */ - value->ton = PRI_TON_UNKNOWN; - value->npi = PRI_NPI_E163_E164; - value->pres = -1; /* Data is not available */ + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } - do { - GET_COMPONENT(comp, i, vdata, len); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_DivertingLegInformation2; + msg.invoke_id = get_invokeid(ctrl); - switch(comp->type) { - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0): /* [0] presentationAllowedNumber */ - value->pres = PRES_ALLOWED_USER_NUMBER_NOT_SCREENED; - size = rose_address_decode(pri, call, comp->data, comp->len, value); - ASN1_FIXUP_LEN(comp, size); - return size + 2; - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1): /* [1] IMPLICIT presentationRestricted */ - if (comp->len != 0) { /* must be NULL */ - pri_error(pri, "!! Invalid PresentationRestricted component received (len != 0)\n"); - return -1; - } - value->pres = PRES_PROHIB_USER_NUMBER_NOT_SCREENED; - return 2; - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2): /* [2] IMPLICIT numberNotAvailableDueToInterworking */ - if (comp->len != 0) { /* must be NULL */ - pri_error(pri, "!! Invalid NumberNotAvailableDueToInterworking component received (len != 0)\n"); - return -1; - } - value->pres = PRES_NUMBER_NOT_AVAILABLE; - return 2; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_3): /* [3] presentationRestrictedNumber */ - value->pres = PRES_PROHIB_USER_NUMBER_NOT_SCREENED; - size = rose_address_decode(pri, call, comp->data, comp->len, value) + 2; - ASN1_FIXUP_LEN(comp, size); - return size + 2; - default: - pri_message(pri, "Invalid PresentedNumberUnscreened component 0x%X\n", comp->type); - } - return -1; + /* diversionCounter is the redirecting.count */ + msg.args.etsi.DivertingLegInformation2.diversion_counter = call->redirecting.count; + + msg.args.etsi.DivertingLegInformation2.diversion_reason = + redirectingreason_from_q931(ctrl, call->redirecting.reason); + + /* divertingNr is the redirecting.from.number */ + msg.args.etsi.DivertingLegInformation2.diverting_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.DivertingLegInformation2.diverting, + &call->redirecting.from.number); + + if (1 < call->redirecting.count) { + /* originalCalledNr is the redirecting.orig_called.number */ + msg.args.etsi.DivertingLegInformation2.original_called_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.DivertingLegInformation2.original_called, + &call->redirecting.orig_called.number); } - while (0); - return -1; + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; } -static int rose_diverting_leg_information2_decode(struct pri *pri, q931_call *call, struct rose_component *sequence, int len) +/*! + * \internal + * \brief Encode and queue the DivertingLegInformation2 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode diversion leg 2. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_diverting_leg_information2_encode(struct pri *ctrl, q931_call *call) { - int i = 0; - int diversion_counter; - int diversion_reason; - char origcalledname[50] = "", redirectingname[50] = ""; - struct addressingdataelements_presentednumberunscreened divertingnr; - struct addressingdataelements_presentednumberunscreened originalcallednr; - struct rose_component *comp = NULL; - unsigned char *vdata = sequence->data; - int res = 0; - memset(&divertingnr, 0, sizeof(divertingnr)); - memset(&originalcallednr, 0, sizeof(originalcallednr)); + unsigned char buffer[256]; + unsigned char *end; - /* Data checks */ - if (sequence->type != (ASN1_CONSTRUCTOR | ASN1_SEQUENCE)) { /* Constructed Sequence */ - pri_message(pri, "Invalid DivertingLegInformation2Type argument\n"); + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = enc_etsi_diverting_leg_information2(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + case PRI_SWITCH_QSIG: + end = enc_qsig_diverting_leg_information2(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + default: return -1; } + if (!end) { + return -1; + } - if (sequence->len == ASN1_LEN_INDEF) { - len -= 4; /* For the 2 extra characters at the end - * and two characters of header */ - } else - len -= 2; + return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL); +} - do { - /* diversionCounter stuff */ - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do it diversionCounter is of type 0x%x\n"); - ASN1_GET_INTEGER(comp, diversion_counter); - NEXT_COMPONENT(comp, i); +/*! + * \internal + * \brief Encode the Q.SIG DivertingLegInformation3 invoke message. + * + * \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 call Call leg from which to encode diversion leg 3. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_diverting_leg_information3(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; - /* diversionReason stuff */ - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Invalid diversionReason type 0x%X of ROSE divertingLegInformation2 component received\n"); - ASN1_GET_INTEGER(comp, diversion_reason); - NEXT_COMPONENT(comp, i); + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } - diversion_reason = redirectingreason_for_q931(pri, diversion_reason); - - if(pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " Redirection reason: %d, total diversions: %d\n", diversion_reason, diversion_counter); - pri_message(NULL, "Length of message is %d\n", len); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_DivertingLegInformation3; + msg.invoke_id = get_invokeid(ctrl); - for(; i < len; NEXT_COMPONENT(comp, i)) { - GET_COMPONENT(comp, i, vdata, len); - switch(comp->type) { - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0): - call->origredirectingreason = redirectingreason_for_q931(pri, comp->data[0]); - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " Received reason for original redirection %d\n", call->origredirectingreason); - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1): - res = rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &divertingnr); - /* TODO: Fix indefinite length form hacks */ - ASN1_FIXUP_LEN(comp, res); - comp->len = res; - if (res < 0) - return -1; - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, " Received divertingNr '%s'\n", divertingnr.partyaddress); - pri_message(pri, " ton = %d, pres = %d, npi = %d\n", divertingnr.ton, divertingnr.pres, divertingnr.npi); - } - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_2): - res = rose_presented_number_unscreened_decode(pri, call, comp->data, comp->len, &originalcallednr); - if (res < 0) - return -1; - ASN1_FIXUP_LEN(comp, res); - comp->len = res; - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, " Received originalcallednr '%s'\n", originalcallednr.partyaddress); - pri_message(pri, " ton = %d, pres = %d, npi = %d\n", originalcallednr.ton, originalcallednr.pres, originalcallednr.npi); - } - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_3): - res = asn1_name_decode(comp->data, comp->len, redirectingname, sizeof(redirectingname)); - if (res < 0) - return -1; - ASN1_FIXUP_LEN(comp, res); - comp->len = res; - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " Received RedirectingName '%s'\n", redirectingname); - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_4): - res = asn1_name_decode(comp->data, comp->len, origcalledname, sizeof(origcalledname)); - if (res < 0) - return -1; - ASN1_FIXUP_LEN(comp, res); - comp->len = res; - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " Received Originally Called Name '%s'\n", origcalledname); - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_5): - pri_message(pri, "!! Ignoring DivertingLegInformation2 component 0x%X\n", comp->type); - break; - default: - if (comp->type == 0 && comp->len == 0) { - break; /* Found termination characters */ - } - pri_message(pri, "!! Invalid DivertingLegInformation2 component received 0x%X\n", comp->type); - return -1; - } - } + /* redirecting.to.number.presentation also indicates if name presentation is allowed */ + if ((call->redirecting.to.number.presentation & PRI_PRES_RESTRICTION) == PRI_PRES_ALLOWED) { + msg.args.qsig.DivertingLegInformation3.presentation_allowed_indicator = 1; /* TRUE */ - if (divertingnr.pres >= 0) { - call->redirectingplan = divertingnr.npi; - call->redirectingpres = divertingnr.pres; - call->redirectingreason = diversion_reason; - libpri_copy_string(call->redirectingnum, divertingnr.partyaddress, sizeof(call->redirectingnum)); - pri_message(pri, " Received redirectingnum '%s' (%d)\n", call->redirectingnum, (int)call->redirectingnum[0]); + /* redirectionName is the redirecting.to.name */ + if (call->redirecting.to.name.valid) { + msg.args.qsig.DivertingLegInformation3.redirection_name_present = 1; + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.DivertingLegInformation3.redirection_name, + &call->redirecting.to.name); } - if (originalcallednr.pres >= 0) { - call->origcalledplan = originalcallednr.npi; - call->origcalledpres = originalcallednr.pres; - libpri_copy_string(call->origcallednum, originalcallednr.partyaddress, sizeof(call->origcallednum)); - pri_message(pri, " Received origcallednum '%s' (%d)\n", call->origcallednum, (int)call->origcallednum[0]); - } - libpri_copy_string(call->redirectingname, redirectingname, sizeof(call->redirectingname)); - libpri_copy_string(call->origcalledname, origcalledname, sizeof(call->origcalledname)); - return 0; } - while (0); - return -1; + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; } - -static int rose_diverting_leg_information2_encode(struct pri *pri, q931_call *call) + +/*! + * \internal + * \brief Encode the ETSI DivertingLegInformation3 invoke message. + * + * \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 call Call leg from which to encode diversion leg 3. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_diverting_leg_information3(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call) { - int i = 0, j, compsp = 0; - struct rose_component *comp, *compstk[10]; - unsigned char buffer[256]; - int len = 253; - -#if 0 /* This is not required by specifications */ - if (!strlen(call->callername)) { - return -1; + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; } -#endif - buffer[i] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - i++; - /* Interpretation component */ - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0x00 /* Discard unrecognized invokes */); - - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - - ASN1_PUSH(compstk, compsp, comp); - /* Invoke component contents */ - /* Invoke ID */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); - /* Operation Tag */ - - /* ROSE operationId component */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, ROSE_DIVERTING_LEG_INFORMATION2); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_DivertingLegInformation3; + msg.invoke_id = get_invokeid(ctrl); - /* ROSE ARGUMENT component */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - /* ROSE DivertingLegInformation2.diversionCounter component */ - /* Always is 1 because other isn't available in the current design */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, 1); - - /* ROSE DivertingLegInformation2.diversionReason component */ - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, redirectingreason_from_q931(pri, call->redirectingreason)); - - /* ROSE DivertingLegInformation2.divertingNr component */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); - - ASN1_PUSH(compstk, compsp, comp); - /* Redirecting information always not screened */ - - switch(call->redirectingpres) { - case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: - case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: - if (call->redirectingnum && strlen(call->redirectingnum)) { - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - /* NPI of redirected number is not supported in the current design */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4)); - j = asn1_string_encode(ASN1_NUMERICSTRING, &buffer[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum)); - if (j < 0) - return -1; - - i += j; - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - break; - } - /* fall through */ - case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: - case PRES_PROHIB_USER_NUMBER_NOT_SCREENED: - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); - break; - /* Don't know how to handle this */ - case PRES_ALLOWED_NETWORK_NUMBER: - case PRES_PROHIB_NETWORK_NUMBER: - case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: - case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); - break; - default: - pri_message(pri, "!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres); - case PRES_NUMBER_NOT_AVAILABLE: - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i); - break; + if ((call->redirecting.to.number.presentation & PRI_PRES_RESTRICTION) == PRI_PRES_ALLOWED) { + msg.args.etsi.DivertingLegInformation3.presentation_allowed_indicator = 1; /* TRUE */ } - ASN1_FIXUP(compstk, compsp, buffer, i); - /* ROSE DivertingLegInformation2.originalCalledNr component */ - /* This information isn't supported by current design - duplicate divertingNr */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_2), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - /* Redirecting information always not screened */ - switch(call->redirectingpres) { - case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: - case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: - if (call->redirectingnum && strlen(call->redirectingnum)) { - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4)); - - j = asn1_string_encode(ASN1_NUMERICSTRING, &buffer[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum)); - if (j < 0) - return -1; - - i += j; - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - break; - } - /* fall through */ - case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: - case PRES_PROHIB_USER_NUMBER_NOT_SCREENED: - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); - break; - /* Don't know how to handle this */ - case PRES_ALLOWED_NETWORK_NUMBER: - case PRES_PROHIB_NETWORK_NUMBER: - case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: - case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); - break; - default: - pri_message(pri, "!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres); - case PRES_NUMBER_NOT_AVAILABLE: - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i); - break; + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \brief Encode and queue the DivertingLegInformation3 invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode diversion leg 3. + * \param messagetype Q.931 message type to add facility ie to. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rose_diverting_leg_information3_encode(struct pri *ctrl, q931_call *call, + int messagetype) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = enc_etsi_diverting_leg_information3(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + case PRI_SWITCH_QSIG: + end = enc_qsig_diverting_leg_information3(ctrl, buffer, buffer + sizeof(buffer), + call); + break; + default: + return -1; } - ASN1_FIXUP(compstk, compsp, buffer, i); - - /* Fix length of stacked components */ - while(compsp > 0) { - ASN1_FIXUP(compstk, compsp, buffer, i); + if (!end) { + return -1; } - - if (pri_call_apdu_queue(call, Q931_SETUP, buffer, i, NULL, NULL)) - return -1; - - return 0; + + return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL); } -/* Send the rltThirdParty: Invoke */ -int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2) +/*! + * \internal + * \brief Encode the rltThirdParty invoke message. + * + * \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 callwithid Call-ID information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_dms100_rlt_initiate_transfer(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const q931_call *callwithid) { - int i = 0; + struct rose_msg_invoke msg; + + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_DMS100_RLT_ThirdParty; + msg.invoke_id = ROSE_DMS100_RLT_THIRD_PARTY; + msg.args.dms100.RLT_ThirdParty.call_id = callwithid->rlt_call_id & 0xFFFFFF; + msg.args.dms100.RLT_ThirdParty.reason = 0; /* unused, set to 129 */ + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \brief Send the rltThirdParty: Invoke. + * + * \note For PRI_SWITCH_DMS100 only. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param c1 Q.931 call leg 1 + * \param c2 Q.931 call leg 2 + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rlt_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) +{ unsigned char buffer[256]; - struct rose_component *comp = NULL, *compstk[10]; - const unsigned char rlt_3rd_pty = RLT_THIRD_PARTY; - q931_call *callwithid = NULL, *apdubearer = NULL; - int compsp = 0; + unsigned char *end; + q931_call *apdubearer; + q931_call *callwithid; if (c2->transferable) { apdubearer = c1; @@ -906,279 +1374,415 @@ } else if (c1->transferable) { apdubearer = c2; callwithid = c1; - } else + } else { return -1; + } - buffer[i++] = (Q932_PROTOCOL_ROSE); - buffer[i++] = (0x80 | RLT_SERVICE_ID); /* Service Identifier octet */ + end = + enc_dms100_rlt_initiate_transfer(ctrl, buffer, buffer + sizeof(buffer), + callwithid); + if (!end) { + return -1; + } - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); - - /* Invoke ID is set to the operation ID */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_3rd_pty); - - /* Operation Tag */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_3rd_pty); - - /* Additional RLT invoke info - Octet 12 */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - - ASN1_ADD_WORDCOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, callwithid->rlt_call_id & 0xFFFFFF); /* Length is 3 octets */ - /* Reason for redirect - unused, set to 129 */ - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i, 0); - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - - if (pri_call_apdu_queue(apdubearer, Q931_FACILITY, buffer, i, NULL, NULL)) + if (pri_call_apdu_queue(apdubearer, Q931_FACILITY, buffer, end - buffer, NULL)) { return -1; + } if (q931_facility(apdubearer->pri, apdubearer)) { - pri_message(pri, "Could not schedule facility message for call %d\n", apdubearer->cr); + pri_message(ctrl, "Could not schedule facility message for call %d\n", + apdubearer->cr); return -1; } return 0; } -static int add_dms100_transfer_ability_apdu(struct pri *pri, q931_call *c) +/*! + * \internal + * \brief Encode the rltOperationInd invoke message. + * + * \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. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_dms100_rlt_transfer_ability(struct pri *ctrl, + unsigned char *pos, unsigned char *end) { - int i = 0; - unsigned char buffer[256]; - struct rose_component *comp = NULL, *compstk[10]; - const unsigned char rlt_op_ind = RLT_OPERATION_IND; - int compsp = 0; + struct rose_msg_invoke msg; - buffer[i++] = (Q932_PROTOCOL_ROSE); /* Note to self: DON'T set the EXT bit */ - buffer[i++] = (0x80 | RLT_SERVICE_ID); /* Service Identifier octet */ + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_DMS100_RLT_OperationInd; + msg.invoke_id = ROSE_DMS100_RLT_OPERATION_IND; + pos = rose_encode_invoke(ctrl, pos, end, &msg); - /* Invoke ID is set to the operation ID */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_op_ind); - - /* Operation Tag - basically the same as the invoke ID tag */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, rlt_op_ind); - ASN1_FIXUP(compstk, compsp, buffer, i); - - if (pri_call_apdu_queue(c, Q931_SETUP, buffer, i, NULL, NULL)) - return -1; - else - return 0; + return pos; } -/* Sending callername information functions */ -static int add_callername_facility_ies(struct pri *pri, q931_call *c, int cpe) +/*! + * \internal + * \brief Send the rltOperationInd: Invoke. + * + * \note For PRI_SWITCH_DMS100 only. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Q.931 call leg + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int add_dms100_transfer_ability_apdu(struct pri *ctrl, q931_call *call) { - int res = 0; - int i = 0; unsigned char buffer[256]; - unsigned char namelen = 0; - struct rose_component *comp = NULL, *compstk[10]; - int compsp = 0; - int mymessage = 0; - static unsigned char op_tag[] = { - 0x2a, /* informationFollowing 42 */ - 0x86, - 0x48, - 0xce, - 0x15, - 0x00, - 0x04 - }; - - if (!strlen(c->callername)) { + unsigned char *end; + + end = enc_dms100_rlt_transfer_ability(ctrl, buffer, buffer + sizeof(buffer)); + if (!end) { return -1; } - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - /* Interpretation component */ + return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL); +} - if (pri->switchtype == PRI_SWITCH_QSIG) { - ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); - ASN1_FIXUP(compstk, compsp, buffer, i); +/*! + * \internal + * \brief Encode the NI2 InformationFollowing invoke message. + * + * \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. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_ni2_information_following(struct pri *ctrl, unsigned char *pos, + unsigned char *end) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; } - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_NI2_InformationFollowing; + msg.invoke_id = get_invokeid(ctrl); + msg.args.ni2.InformationFollowing.value = 0; + pos = rose_encode_invoke(ctrl, pos, end, &msg); - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); - /* Invoke ID */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + return pos; +} - /* Operation Tag */ - res = asn1_string_encode(ASN1_OBJECTIDENTIFIER, &buffer[i], sizeof(buffer)-i, sizeof(op_tag), op_tag, sizeof(op_tag)); - if (res < 0) - return -1; - i += res; +/*! + * \internal + * \brief Encode the Q.SIG CallingName invoke message. + * + * \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 name Name data which to encode name. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_calling_name(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct q931_party_name *name) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 0); - ASN1_FIXUP(compstk, compsp, buffer, i); - - if (!cpe) { - if (pri_call_apdu_queue(c, Q931_SETUP, buffer, i, NULL, NULL)) - return -1; + memset(&header, 0, sizeof(header)); + if (ctrl->switchtype == PRI_SWITCH_QSIG) { + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ } + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_CallingName; + msg.invoke_id = get_invokeid(ctrl); - /* Now the APDU that contains the information that needs sent. - * We can reuse the buffer since the queue function doesn't - * need it. */ + /* CallingName */ + q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallingName.name, name); - i = 0; - namelen = strlen(c->callername); - if (namelen > 50) { - namelen = 50; /* truncate the name */ - } + pos = rose_encode_invoke(ctrl, pos, end, &msg); - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - /* Interpretation component */ + return pos; +} - if (pri->switchtype == PRI_SWITCH_QSIG) { - ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); - ASN1_FIXUP(compstk, compsp, buffer, i); +/*! + * \internal + * \brief Send caller name information. + * + * \note For PRI_SWITCH_NI2 and PRI_SWITCH_QSIG. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode name. + * \param cpe TRUE if we are the CPE side. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int add_callername_facility_ies(struct pri *ctrl, q931_call *call, int cpe) +{ + unsigned char buffer[256]; + unsigned char *end; + int mymessage; + + if (!call->local_id.name.valid) { + return 0; } - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); + if (ctrl->switchtype == PRI_SWITCH_NI2 && !cpe) { + end = enc_ni2_information_following(ctrl, buffer, buffer + sizeof(buffer)); + if (!end) { + return -1; + } - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); + if (pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL)) { + return -1; + } - /* Invoke ID */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + /* + * We can reuse the buffer since the queue function doesn't + * need it. + */ + } - /* Operation ID: Calling name */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, SS_CNID_CALLINGNAME); - - res = asn1_string_encode((ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), &buffer[i], sizeof(buffer)-i, 50, c->callername, namelen); - if (res < 0) + /* CallingName is the local_id.name */ + end = enc_qsig_calling_name(ctrl, buffer, buffer + sizeof(buffer), + &call->local_id.name); + if (!end) { return -1; - i += res; - ASN1_FIXUP(compstk, compsp, buffer, i); + } - if (cpe) + if (cpe) { mymessage = Q931_SETUP; - else + } else { mymessage = Q931_FACILITY; + } - if (pri_call_apdu_queue(c, mymessage, buffer, i, NULL, NULL)) - return -1; - - return 0; + return pri_call_apdu_queue(call, mymessage, buffer, end - buffer, NULL); } /* End Callername */ /* MWI related encode and decode functions */ -static void mwi_activate_encode_cb(void *data) + +/*! + * \internal + * \brief Encode the Q.SIG MWIActivate invoke message. + * + * \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 req Served user setup request information. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_mwi_activate_message(struct pri *ctrl, unsigned char *pos, + unsigned char *end, struct pri_sr *req) { - return; + struct fac_extension_header header; + struct rose_msg_invoke msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_MWIActivate; + msg.invoke_id = get_invokeid(ctrl); + + /* The called.number is the served user */ + q931_copy_number_to_rose(ctrl, &msg.args.qsig.MWIActivate.served_user_number, + &req->called.number); + /* + * For now, we will just force the numbering plan to unknown to preserve + * the original behaviour. + */ + msg.args.qsig.MWIActivate.served_user_number.plan = 0; /* unknown */ + + msg.args.qsig.MWIActivate.basic_service = 1; /* speech */ + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; } -int mwi_message_send(struct pri* pri, q931_call *call, struct pri_sr *req, int activate) +/*! + * \internal + * \brief Encode the Q.SIG MWIDeactivate invoke message. + * + * \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 req Served user setup request information. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_mwi_deactivate_message(struct pri *ctrl, + unsigned char *pos, unsigned char *end, struct pri_sr *req) { - int i = 0; - unsigned char buffer[255] = ""; - int destlen = strlen(req->called); - struct rose_component *comp = NULL, *compstk[10]; - int compsp = 0; - int res; + struct fac_extension_header header; + struct rose_msg_invoke msg; - if (destlen <= 0) { - return -1; - } else if (destlen > 20) - destlen = 20; /* Destination number cannot be greater then 20 digits */ + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - /* Interpretation component */ + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_MWIDeactivate; + msg.invoke_id = get_invokeid(ctrl); - ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); - ASN1_FIXUP(compstk, compsp, buffer, i); + /* The called.number is the served user */ + q931_copy_number_to_rose(ctrl, &msg.args.qsig.MWIDeactivate.served_user_number, + &req->called.number); + /* + * For now, we will just force the numbering plan to unknown to preserve + * the original behaviour. + */ + msg.args.qsig.MWIDeactivate.served_user_number.plan = 0; /* unknown */ - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); + msg.args.qsig.MWIDeactivate.basic_service = 1; /* speech */ - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); + pos = rose_encode_invoke(ctrl, pos, end, &msg); - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + return pos; +} - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, (activate) ? SS_MWI_ACTIVATE : SS_MWI_DEACTIVATE); - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - /* PartyNumber */ - res = asn1_string_encode((ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), &buffer[i], sizeof(buffer)-i, destlen, req->called, destlen); - - if (res < 0) +/*! + * \brief Encode and queue the Q.SIG MWIActivate/MWIDeactivate invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg to queue message. + * \param req Served user setup request information. + * \param activate Nonzero to do the activate message. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int mwi_message_send(struct pri *ctrl, q931_call *call, struct pri_sr *req, int activate) +{ + unsigned char buffer[255]; + unsigned char *end; + + if (!req->called.number.valid || !req->called.number.str[0]) { return -1; - i += res; + } - /* Enumeration: basicService */ - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 1 /* contents: Voice */); - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); + if (activate) { + end = enc_qsig_mwi_activate_message(ctrl, buffer, buffer + sizeof(buffer), req); + } else { + end = + enc_qsig_mwi_deactivate_message(ctrl, buffer, buffer + sizeof(buffer), req); + } + if (!end) { + return -1; + } - return pri_call_apdu_queue(call, Q931_SETUP, buffer, i, mwi_activate_encode_cb, NULL); + return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL); } /* End MWI */ /* EECT functions */ -int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2) +/*! + * \internal + * \brief Encode the NI2 InitiateTransfer invoke message. + * + * \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 call Call leg from which to encode transfer information. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_ni2_initiate_transfer(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call) { - int i = 0; - int res = 0; - unsigned char buffer[255] = ""; - short call_reference = c2->cr ^ 0x8000; /* Let's do the trickery to make sure the flag is correct */ - struct rose_component *comp = NULL, *compstk[10]; - int compsp = 0; - static unsigned char op_tag[] = { - 0x2A, - 0x86, - 0x48, - 0xCE, - 0x15, - 0x00, - 0x08, - }; + struct rose_msg_invoke msg; - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_ROSE); + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_NI2_InitiateTransfer; + msg.invoke_id = get_invokeid(ctrl); + /* Let's do the trickery to make sure the flag is correct */ + msg.args.ni2.InitiateTransfer.call_reference = call->cr ^ 0x8000; + pos = rose_encode_invoke(ctrl, pos, end, &msg); - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + return pos; +} - res = asn1_string_encode(ASN1_OBJECTIDENTIFIER, &buffer[i], sizeof(buffer)-i, sizeof(op_tag), op_tag, sizeof(op_tag)); - if (res < 0) +/*! + * \brief Start a 2BCT + * + * \note Called for PRI_SWITCH_NI2, PRI_SWITCH_LUCENT5E, and PRI_SWITCH_ATT4ESS + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param c1 Q.931 call leg 1 + * \param c2 Q.931 call leg 2 + * + * \retval 0 on success. + * \retval -1 on error. + */ +int eect_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) +{ + unsigned char buffer[255]; + unsigned char *end; + + end = enc_ni2_initiate_transfer(ctrl, buffer, buffer + sizeof(buffer), c2); + if (!end) { return -1; - i += res; + } - ASN1_ADD_SIMPLE(comp, (ASN1_SEQUENCE | ASN1_CONSTRUCTOR), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_WORDCOMP(comp, ASN1_INTEGER, buffer, i, call_reference); - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - - res = pri_call_apdu_queue(c1, Q931_FACILITY, buffer, i, NULL, NULL); - if (res) { - pri_message(pri, "Could not queue APDU in facility message\n"); + if (pri_call_apdu_queue(c1, Q931_FACILITY, buffer, end - buffer, NULL)) { + pri_message(ctrl, "Could not queue APDU in facility message\n"); return -1; } /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ - res = q931_facility(c1->pri, c1); - if (res) { - pri_message(pri, "Could not schedule facility message for call %d\n", c1->cr); + if (q931_facility(c1->pri, c1)) { + pri_message(ctrl, "Could not schedule facility message for call %d\n", c1->cr); return -1; } @@ -1187,1494 +1791,2316 @@ /* End EECT */ /* QSIG CF CallRerouting */ -int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const char* original, const char* reason) +/*! + * \internal + * \brief Encode the Q.SIG CallRerouting invoke message. + * + * \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 call Q.931 call leg. + * \param calling Call rerouting/deflecting updated caller data. + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_call_rerouting(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, const struct q931_party_id *calling, + const struct q931_party_redirecting *deflection, int subscription_option) { -/*CallRerouting ::= OPERATION - -- Sent from the Served User PINX to the Rerouting PINX - ARGUMENT SEQUENCE - { reroutingReason DiversionReason, - originalReroutingReason [0] IMPLICIT DiversionReason OPTIONAL, - calledAddress Address, - diversionCounter INTEGER (1..15), - pSS1InfoElement PSS1InformationElement, - -- The basic call information elements Bearer capability, High layer compatibility, Low - -- layer compatibity, Progress indicator and Party category can be embedded in the - -- pSS1InfoElement in accordance with 6.5.3.1.5 - lastReroutingNr [1] PresentedNumberUnscreened, - subscriptionOption [2] IMPLICIT SubscriptionOption, + struct fac_extension_header header; + struct rose_msg_invoke msg; + unsigned char *q931ie_pos; - callingPartySubaddress [3] PartySubaddress OPTIONAL, + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 2; /* rejectAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } - callingNumber [4] PresentedNumberScreened, + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_CallRerouting; + msg.invoke_id = get_invokeid(ctrl); - callingName [5] Name OPTIONAL, - originalCalledNr [6] PresentedNumberUnscreened OPTIONAL, - redirectingName [7] Name OPTIONAL, - originalCalledName [8] Name OPTIONAL, - extension CHOICE { - [9] IMPLICIT Extension , - [10] IMPLICIT SEQUENCE OF Extension } OPTIONAL } -*/ + msg.args.qsig.CallRerouting.rerouting_reason = + redirectingreason_from_q931(ctrl, deflection->reason); - int i = 0, j; - int res = 0; - unsigned char buffer[255] = ""; - int len = 253; - struct rose_component *comp = NULL, *compstk[10]; - int compsp = 0; - static unsigned char op_tag[] = { - 0x13, - }; + /* calledAddress is the passed in deflection->to address */ + q931_copy_address_to_rose(ctrl, &msg.args.qsig.CallRerouting.called, &deflection->to); - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - /* Interpretation component */ + msg.args.qsig.CallRerouting.diversion_counter = deflection->count; - ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); - ASN1_FIXUP(compstk, compsp, buffer, i); + /* pSS1InfoElement */ + q931ie_pos = msg.args.qsig.CallRerouting.q931ie_contents; + *q931ie_pos++ = 0x04; /* Bearer Capability IE */ + *q931ie_pos++ = 0x03; /* len */ + *q931ie_pos++ = 0x80 | call->transcapability; /* Rxed transfer capability. */ + *q931ie_pos++ = 0x90; /* circuit mode, 64kbit/s */ + *q931ie_pos++ = 0xa3; /* level1 protocol, a-law */ + *q931ie_pos++ = 0x95; /* locking shift to codeset 5 (national use) */ + *q931ie_pos++ = 0x32; /* Unknown ie */ + *q931ie_pos++ = 0x01; /* Unknown ie len */ + *q931ie_pos++ = 0x81; /* Unknown ie body */ + msg.args.qsig.CallRerouting.q931ie.length = q931ie_pos + - msg.args.qsig.CallRerouting.q931ie_contents; - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 2); /* reject - to get feedback from QSIG switch */ + /* lastReroutingNr is the passed in deflection->from.number */ + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.qsig.CallRerouting.last_rerouting, &deflection->from.number); - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); + msg.args.qsig.CallRerouting.subscription_option = subscription_option; - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); + /* callingNumber is the passed in calling->number */ + q931_copy_presented_number_screened_to_rose(ctrl, + &msg.args.qsig.CallRerouting.calling, &calling->number); - res = asn1_string_encode(ASN1_INTEGER, &buffer[i], sizeof(buffer)-i, sizeof(op_tag), op_tag, sizeof(op_tag)); - if (res < 0) - return -1; - i += res; + /* callingPartySubaddress is the passed in calling->subaddress if valid */ + q931_copy_subaddress_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling_subaddress, + &calling->subaddress); - /* call rerouting argument */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); + /* callingName is the passed in calling->name if valid */ + if (calling->name.valid) { + msg.args.qsig.CallRerouting.calling_name_present = 1; + q931_copy_name_to_rose(ctrl, &msg.args.qsig.CallRerouting.calling_name, + &calling->name); + } - /* reroutingReason DiversionReason */ + if (1 < deflection->count) { + /* originalCalledNr is the deflection->orig_called.number */ + msg.args.qsig.CallRerouting.original_called_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.qsig.CallRerouting.original_called, + &deflection->orig_called.number); - if (reason) { - if (!strcasecmp(reason, "cfu")) - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 1); /* cfu */ - else if (!strcasecmp(reason, "cfb")) - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 2); /* cfb */ - else if (!strcasecmp(reason, "cfnr")) - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 3); /* cfnr */ - } else { - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 0); /* unknown */ + msg.args.qsig.CallRerouting.original_rerouting_reason_present = 1; + if (deflection->orig_called.number.valid) { + msg.args.qsig.CallRerouting.original_rerouting_reason = + redirectingreason_from_q931(ctrl, deflection->orig_reason); + } else { + msg.args.qsig.CallRerouting.original_rerouting_reason = + QSIG_DIVERT_REASON_UNKNOWN; + } + + /* originalCalledName is the deflection->orig_called.name */ + if (deflection->orig_called.name.valid) { + msg.args.qsig.CallRerouting.original_called_name_present = 1; + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.CallRerouting.original_called_name, + &deflection->orig_called.name); + } } + pos = rose_encode_invoke(ctrl, pos, end, &msg); - /* calledAddress Address */ - /* explicit sequence tag for Address */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - /* implicit choice public party number tag */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - /* type of public party number = unknown */ - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 0); - /* NumberDigits of public party number */ - j = asn1_string_encode(ASN1_NUMERICSTRING, &buffer[i], len - i, 20, (char*)dest, strlen(dest)); - if (j < 0) - return -1; + return pos; +} - i += j; - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); +/*! + * \internal + * \brief Encode the ETSI CallRerouting invoke message. + * + * \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 call Q.931 call leg. + * \param calling Call rerouting/deflecting updated caller data. + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_call_rerouting(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, const struct q931_party_id *calling, + const struct q931_party_redirecting *deflection, int subscription_option) +{ + struct rose_msg_invoke msg; + unsigned char *q931ie_pos; - /* diversionCounter INTEGER (1..15) */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, 1); + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } - /* pSS1InfoElement */ - ASN1_ADD_SIMPLE(comp, (ASN1_APPLICATION | ASN1_TAG_0 ), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - buffer[i++] = (0x04); /* Bearer Capability IE */ - buffer[i++] = (0x03); /* len */ - buffer[i++] = (0x80); /* ETSI Standard, Speech */ - buffer[i++] = (0x90); /* circuit mode, 64kbit/s */ - buffer[i++] = (0xa3); /* level1 protocol, a-law */ - buffer[i++] = (0x95); /* locking shift to codeset 5 (national use) */ - buffer[i++] = (0x32); /* Unknown ie */ - buffer[i++] = (0x01); /* Unknown ie len */ - buffer[i++] = (0x81); /* Unknown ie body */ - ASN1_FIXUP(compstk, compsp, buffer, i); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_CallRerouting; + msg.invoke_id = get_invokeid(ctrl); - /* lastReroutingNr [1]*/ - /* implicit optional lastReroutingNr tag */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); - ASN1_PUSH(compstk, compsp, comp); + msg.args.etsi.CallRerouting.rerouting_reason = + redirectingreason_from_q931(ctrl, deflection->reason); - /* implicit choice presented number unscreened tag */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0), buffer, i); - ASN1_PUSH(compstk, compsp, comp); + /* calledAddress is the passed in deflection->to address */ + q931_copy_address_to_rose(ctrl, &msg.args.etsi.CallRerouting.called_address, + &deflection->to); - /* implicit choice public party number tag */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - /* type of public party number = unknown */ - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 0); - j = asn1_string_encode(ASN1_NUMERICSTRING, &buffer[i], len - i, 20, original?(char*)original:c->callednum, original?strlen(original):strlen(c->callednum)); - if (j < 0) - return -1; + msg.args.etsi.CallRerouting.rerouting_counter = deflection->count; - i += j; - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); + /* q931InfoElement */ + q931ie_pos = msg.args.etsi.CallRerouting.q931ie_contents; + *q931ie_pos++ = 0x04; /* Bearer Capability IE */ + *q931ie_pos++ = 0x03; /* len */ + *q931ie_pos++ = 0x80 | call->transcapability; /* Rxed transfer capability. */ + *q931ie_pos++ = 0x90; /* circuit mode, 64kbit/s */ + *q931ie_pos++ = 0xa3; /* level1 protocol, a-law */ + msg.args.etsi.CallRerouting.q931ie.length = q931ie_pos + - msg.args.etsi.CallRerouting.q931ie_contents; - /* subscriptionOption [2]*/ - /* implicit optional lastReroutingNr tag */ - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); /* noNotification */ + /* lastReroutingNr is the passed in deflection->from.number */ + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.CallRerouting.last_rerouting, &deflection->from.number); - /* callingNumber [4]*/ - /* implicit optional callingNumber tag */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_4), buffer, i); - ASN1_PUSH(compstk, compsp, comp); + msg.args.etsi.CallRerouting.subscription_option = subscription_option; - /* implicit choice presented number screened tag */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0), buffer, i); - ASN1_PUSH(compstk, compsp, comp); + /* callingPartySubaddress is the passed in calling->subaddress if valid */ + q931_copy_subaddress_to_rose(ctrl, &msg.args.etsi.CallRerouting.calling_subaddress, + &calling->subaddress); - /* implicit choice presentationAllowedAddress tag */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - /* type of public party number = subscriber number */ - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 4); - j = asn1_string_encode(ASN1_NUMERICSTRING, &buffer[i], len - i, 20, c->callernum, strlen(c->callernum)); - if (j < 0) - return -1; + pos = rose_encode_invoke(ctrl, pos, end, &msg); - i += j; - ASN1_FIXUP(compstk, compsp, buffer, i); + return pos; +} - /* Screeening Indicator network provided */ - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 3); +/*! + * \internal + * \brief Encode the ETSI CallDeflection invoke message. + * + * \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 call Q.931 call leg. + * \param deflection Call deflection address. + * + * \note + * deflection is the new called number and must always be present. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_call_deflection(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, const struct q931_party_id *deflection) +{ + struct rose_msg_invoke msg; - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } - /**/ + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_CallDeflection; + msg.invoke_id = get_invokeid(ctrl); - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); + /* deflectionAddress is the passed in deflection->to address */ + q931_copy_address_to_rose(ctrl, &msg.args.etsi.CallDeflection.deflection, + deflection); - res = pri_call_apdu_queue(c, Q931_FACILITY, buffer, i, NULL, NULL); - if (res) { - pri_message(pri, "Could not queue ADPU in facility message\n"); + msg.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present = 1; + switch (deflection->number.presentation & PRI_PRES_RESTRICTION) { + case PRI_PRES_ALLOWED: + msg.args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user = 1; + break; + default: + case PRI_PRES_UNAVAILABLE: + case PRI_PRES_RESTRICTED: + break; + } + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue the CallRerouting/CallDeflection message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.) + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_reroute_request_encode(struct pri *ctrl, q931_call *call, + const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, + int subscription_option) +{ + unsigned char buffer[256]; + unsigned char *end; + + if (!caller) { + /* + * We are deflecting an incoming call back to the network. + * Therefore, the Caller-ID is the remote party. + */ + caller = &call->remote_id; + } + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (q931_is_ptmp(ctrl)) { + end = + enc_etsi_call_deflection(ctrl, buffer, buffer + sizeof(buffer), call, + &deflection->to); + } else { + end = + enc_etsi_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call, + caller, deflection, subscription_option); + } + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call, caller, + deflection, subscription_option); + break; + default: return -1; } + if (!end) { + return -1; + } - /* Remember that if we queue a facility IE for a facility message we - * have to explicitly send the facility message ourselves */ + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} - res = q931_facility(c->pri, c); - if (res) { - pri_message(pri, "Could not schedule facility message for call %d\n", c->cr); +/*! + * \brief Send the CallRerouting/CallDeflection message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.) + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + * + * \retval 0 on success. + * \retval -1 on error. + */ +int send_reroute_request(struct pri *ctrl, q931_call *call, + const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, + int subscription_option) +{ + if (!deflection->to.number.str[0]) { + /* Must have a deflect to number. That is the point of deflection. */ return -1; } + if (rose_reroute_request_encode(ctrl, call, caller, deflection, subscription_option) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for CallRerouting/CallDeflection message.\n"); + return -1; + } return 0; } + +/*! + * \brief Send the Q.SIG CallRerouting invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Q.931 call leg. + * \param dest Destination number. + * \param original Original called number. + * \param reason Rerouting reason: cfu, cfb, cfnr + * + * \retval 0 on success. + * \retval -1 on error. + */ +int qsig_cf_callrerouting(struct pri *ctrl, q931_call *call, const char *dest, + const char *original, const char *reason) +{ + struct q931_party_redirecting reroute; + + q931_party_redirecting_init(&reroute); + + /* Rerouting to the dest number. */ + reroute.to.number.valid = 1; + reroute.to.number.plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + reroute.to.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + libpri_copy_string(reroute.to.number.str, dest, sizeof(reroute.to.number.str)); + + /* Rerouting from the original number. */ + if (original) { + reroute.from.number.valid = 1; + reroute.from.number.plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + libpri_copy_string(reroute.from.number.str, original, sizeof(reroute.from.number.str)); + } else { + q931_party_address_to_id(&reroute.from, &call->called); + } + reroute.from.number.presentation = PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + + /* Decode the rerouting reason. */ + reroute.reason = PRI_REDIR_UNKNOWN; + if (!reason) { + /* No reason for rerouting given. */ + } else if (!strcasecmp(reason, "cfu")) { + reroute.reason = PRI_REDIR_UNCONDITIONAL; + } else if (!strcasecmp(reason, "cfb")) { + reroute.reason = PRI_REDIR_FORWARD_ON_BUSY; + } else if (!strcasecmp(reason, "cfnr")) { + reroute.reason = PRI_REDIR_FORWARD_ON_NO_REPLY; + } + + reroute.count = (call->redirecting.count < PRI_MAX_REDIRECTS) + ? call->redirecting.count + 1 : PRI_MAX_REDIRECTS; + + if (!call->redirecting.orig_called.number.valid) { + /* + * Since we do not already have an originally called party, we + * must either be the first redirected to party or this call + * has not been redirected before. + * + * Preserve who redirected to us as the originally called party. + */ + reroute.orig_called = call->redirecting.from; + reroute.orig_reason = call->redirecting.reason; + } else { + reroute.orig_called = call->redirecting.orig_called; + reroute.orig_reason = call->redirecting.orig_reason; + } + + return send_reroute_request(ctrl, call, NULL, &reroute, 0 /* noNotification */); +} /* End QSIG CC-CallRerouting */ -static int anfpr_pathreplacement_respond(struct pri *pri, q931_call *call, q931_ie *ie) +/* + * From Mantis issue 7778 description: (ETS 300 258, ISO 13863) + * After both legs of the call are setup and Asterisk has a successful "tromboned" or bridged call ... + * Asterisk sees both 'B' channels (from trombone) are on same PRI/technology and initiates "Path Replacement" events + * a. Asterisk sends "Transfer Complete" messages to both call legs + * b. QSIG Switch sends "PathReplacement" message on one of the legs (random 1-10sec timer expires - 1st leg to send is it!) + * c. Asterisk rebroadcasts "PathReplacement" message to other call leg + * d. QSIG Switch sends "Disconnect" message on one of the legs (same random timer sequence as above) + * e. Asterisk rebroadcasts "Disconnect" message to other call leg + * f. QSIG Switch disconnects Asterisk call legs - callers are now within QSIG switch + * + * Just need to resend the message to the other tromboned leg of the call. + */ +static int anfpr_pathreplacement_respond(struct pri *ctrl, q931_call *call, q931_ie *ie) { int res; - - res = pri_call_apdu_queue_cleanup(call->bridged_call); - if (res) { - pri_message(pri, "Could not Clear queue ADPU\n"); - return -1; - } - + + pri_call_apdu_queue_cleanup(call->bridged_call); + /* Send message */ - res = pri_call_apdu_queue(call->bridged_call, Q931_FACILITY, ie->data, ie->len, NULL, NULL); + res = pri_call_apdu_queue(call->bridged_call, Q931_FACILITY, ie->data, ie->len, NULL); if (res) { - pri_message(pri, "Could not queue ADPU in facility message\n"); - return -1; + pri_message(ctrl, "Could not queue ADPU in facility message\n"); + return -1; } - + /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ - + res = q931_facility(call->bridged_call->pri, call->bridged_call); if (res) { - pri_message(pri, "Could not schedule facility message for call %d\n", call->bridged_call->cr); + pri_message(ctrl, "Could not schedule facility message for call %d\n", + call->bridged_call->cr); return -1; } return 0; } + /* AFN-PR */ -int anfpr_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2) +/*! + * \brief Start a Q.SIG path replacement. + * + * \note Called for PRI_SWITCH_QSIG + * + * \note Did all the tests to see if we're on the same PRI and + * are on a compatible switchtype. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param c1 Q.931 call leg 1 + * \param c2 Q.931 call leg 2 + * + * \retval 0 on success. + * \retval -1 on error. + */ +int anfpr_initiate_transfer(struct pri *ctrl, q931_call *c1, q931_call *c2) { - /* Did all the tests to see if we're on the same PRI and - * are on a compatible switchtype */ - /* TODO */ - int i = 0; - int res = 0; - unsigned char buffer[255] = ""; - unsigned short call_reference = c2->cr; - struct rose_component *comp = NULL, *compstk[10]; - unsigned char buffer2[255] = ""; - int compsp = 0; - static unsigned char op_tag[] = { - 0x0C, - }; - - /* Channel 1 */ - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - - ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer, i, 0); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); - ASN1_FIXUP(compstk, compsp, buffer, i); - - /* Interpretation component */ - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 2); /* reject - to get feedback from QSIG switch */ - - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); - - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, get_invokeid(pri)); - - res = asn1_string_encode(ASN1_INTEGER, &buffer[i], sizeof(buffer)-i, sizeof(op_tag), op_tag, sizeof(op_tag)); - if (res < 0) + unsigned char buffer[255]; + unsigned char *pos; + unsigned char *end; + int res; + struct fac_extension_header header; + struct rose_msg_invoke msg; + + end = buffer + sizeof(buffer); + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 2; /* rejectAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, buffer, end, &header); + if (!pos) { return -1; - i += res; - - ASN1_ADD_SIMPLE(comp, (ASN1_SEQUENCE | ASN1_CONSTRUCTOR), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - buffer[i++] = (0x0a);/* Enumeration endDesignation */ - buffer[i++] = (0x01);/* Len */ - buffer[i++] = (0x00);/* primaryEnd */ - buffer[i++] = (0x81);/* redirectionNumber = presentationRestricted */ - buffer[i++] = (0x00);/* Len */ - buffer[i++] = (0x0a);/* Enumeration callStatus */ - buffer[i++] = (0x01);/* Len */ - buffer[i++] = (0x01);/* alerting */ + } - /* - * Where does this element come from? It is not in Q.SIG ECMA-178. - * We send this but we will not accept it. - * This seems to be a cut and paste error from eect_initiate_transfer(). - */ - ASN1_ADD_WORDCOMP(comp, ASN1_INTEGER, buffer, i, call_reference); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_CallTransferComplete; + msg.invoke_id = get_invokeid(ctrl); + msg.args.qsig.CallTransferComplete.end_designation = 0; /* primaryEnd */ + msg.args.qsig.CallTransferComplete.redirection.presentation = 1; /* presentationRestricted */ + msg.args.qsig.CallTransferComplete.call_status = 1; /* alerting */ + pos = rose_encode_invoke(ctrl, pos, end, &msg); + if (!pos) { + return -1; + } - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - - res = pri_call_apdu_queue(c1, Q931_FACILITY, buffer, i, NULL, NULL); + res = pri_call_apdu_queue(c1, Q931_FACILITY, buffer, pos - buffer, NULL); if (res) { - pri_message(pri, "Could not queue ADPU in facility message\n"); + pri_message(ctrl, "Could not queue ADPU in facility message\n"); return -1; } - + /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ - + res = q931_facility(c1->pri, c1); if (res) { - pri_message(pri, "Could not schedule facility message for call %d\n", c1->cr); + pri_message(ctrl, "Could not schedule facility message for call %d\n", c1->cr); return -1; } - - /* Channel 2 */ - i = 0; - res = 0; - compsp = 0; - - buffer2[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - - ASN1_ADD_SIMPLE(comp, COMP_TYPE_NFE, buffer2, i); - ASN1_PUSH(compstk, compsp, comp); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_0), buffer2, i, 0); - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer2, i, 0); - ASN1_FIXUP(compstk, compsp, buffer2, i); - - /* Interpretation component */ - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer2, i, 2); /* reject */ - - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer2, i); - ASN1_PUSH(compstk, compsp, comp); - - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer2, i, get_invokeid(pri)); - - res = asn1_string_encode(ASN1_INTEGER, &buffer2[i], sizeof(buffer2)-i, sizeof(op_tag), op_tag, sizeof(op_tag)); - if (res < 0) + + /* Reuse the previous message header */ + pos = facility_encode_header(ctrl, buffer, end, &header); + if (!pos) { return -1; - i += res; - - ASN1_ADD_SIMPLE(comp, (ASN1_SEQUENCE | ASN1_CONSTRUCTOR), buffer2, i); - ASN1_PUSH(compstk, compsp, comp); - buffer2[i++] = (0x0a);/* Enumeration endDesignation */ - buffer2[i++] = (0x01);/* Len */ - buffer2[i++] = (0x01);/* secondaryEnd */ - buffer2[i++] = (0x81);/* redirectionNumber = presentationRestricted */ - buffer2[i++] = (0x00);/* Len */ - buffer2[i++] = (0x0a);/* Enumeration callStatus */ - buffer2[i++] = (0x01);/* Len */ - buffer2[i++] = (0x01);/* alerting */ + } - /* - * Where does this element come from? It is not in Q.SIG ECMA-178. - * We send this but we will not accept it. - * This seems to be a cut and paste error from eect_initiate_transfer(). - */ - ASN1_ADD_WORDCOMP(comp, ASN1_INTEGER, buffer2, i, call_reference); + /* Update the previous message */ + msg.invoke_id = get_invokeid(ctrl); + msg.args.qsig.CallTransferComplete.end_designation = 1; /* secondaryEnd */ + pos = rose_encode_invoke(ctrl, pos, end, &msg); + if (!pos) { + return -1; + } - ASN1_FIXUP(compstk, compsp, buffer2, i); - ASN1_FIXUP(compstk, compsp, buffer2, i); - - - res = pri_call_apdu_queue(c2, Q931_FACILITY, buffer2, i, NULL, NULL); + res = pri_call_apdu_queue(c2, Q931_FACILITY, buffer, pos - buffer, NULL); if (res) { - pri_message(pri, "Could not queue ADPU in facility message\n"); + pri_message(ctrl, "Could not queue ADPU in facility message\n"); return -1; } - + /* Remember that if we queue a facility IE for a facility message we * have to explicitly send the facility message ourselves */ - + res = q931_facility(c2->pri, c2); if (res) { - pri_message(pri, "Could not schedule facility message for call %d\n", c1->cr); + pri_message(ctrl, "Could not schedule facility message for call %d\n", c2->cr); return -1; } - + return 0; } /* End AFN-PR */ /* AOC */ -static int aoc_aoce_charging_request_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) +/*! + * \internal + * \brief Encode the ETSI AOCEChargingUnit invoke message. + * + * \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 chargedunits Number of units charged to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_aoce_charging_unit(struct pri *ctrl, unsigned char *pos, + unsigned char *end, long chargedunits) { - int chargingcase = -1; - unsigned char *vdata = data; - struct rose_component *comp = NULL; - int pos1 = 0; + struct rose_msg_invoke msg; - if (pri->debug & PRI_DEBUG_AOC) - dump_apdu (pri, data, len); + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } - do { - GET_COMPONENT(comp, pos1, vdata, len); - CHECK_COMPONENT(comp, ASN1_ENUMERATED, "!! Invalid AOC Charging Request argument. Expected Enumerated (0x0A) but Received 0x%02X\n"); - ASN1_GET_INTEGER(comp, chargingcase); - if (chargingcase >= 0 && chargingcase <= 2) { - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "Channel %d/%d, Call %d - received AOC charging request - charging case: %i\n", - call->ds1no, call->channelno, call->cr, chargingcase); - } else { - pri_message(pri, "!! unkown AOC ChargingCase: 0x%02X", chargingcase); - chargingcase = -1; - } - NEXT_COMPONENT(comp, pos1); - } while (pos1 < len); - if (pos1 < len) { - pri_message(pri, "!! Only reached position %i in %i bytes long AOC-E structure:", pos1, len ); - dump_apdu (pri, data, len); - return -1; /* Aborted before */ + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_AOCEChargingUnit; + msg.invoke_id = get_invokeid(ctrl); + msg.args.etsi.AOCEChargingUnit.type = 1; /* charging_unit */ + if (chargedunits <= 0) { + msg.args.etsi.AOCEChargingUnit.charging_unit.free_of_charge = 1; + } else { + msg.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.num_records = 1; + msg.args.etsi.AOCEChargingUnit.charging_unit.specific.recorded.list[0]. + number_of_units = chargedunits; } - return 0; + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; } - -static int aoc_aoce_charging_unit_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) +/*! + * \internal + * \brief Send the ETSI AOCEChargingUnit invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode AOC. + * \param chargedunits Number of units charged to encode. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int aoc_aoce_charging_unit_encode(struct pri *ctrl, q931_call *call, + long chargedunits) { - long chargingunits = 0, chargetype = -1, temp, chargeIdentifier = -1; - unsigned char *vdata = data; - struct rose_component *comp1 = NULL, *comp2 = NULL, *comp3 = NULL; - int pos1 = 0, pos2, pos3, sublen2, sublen3; - struct addressingdataelements_presentednumberunscreened chargednr; + unsigned char buffer[255]; + unsigned char *end; - if (pri->debug & PRI_DEBUG_AOC) - dump_apdu (pri, data, len); + /* sample data: [ 91 a1 12 02 02 3a 78 02 01 24 30 09 30 07 a1 05 30 03 02 01 01 ] */ - do { - GET_COMPONENT(comp1, pos1, vdata, len); /* AOCEChargingUnitInfo */ - CHECK_COMPONENT(comp1, ASN1_SEQUENCE, "!! Invalid AOC-E Charging Unit argument. Expected Sequence (0x30) but Received 0x%02X\n"); - SUB_COMPONENT(comp1, pos1); - GET_COMPONENT(comp1, pos1, vdata, len); - switch (comp1->type) { - case (ASN1_SEQUENCE | ASN1_CONSTRUCTOR): /* specificChargingUnits */ - sublen2 = comp1->len; - pos2 = pos1; - comp2 = comp1; - SUB_COMPONENT(comp2, pos2); - do { - GET_COMPONENT(comp2, pos2, vdata, len); - switch (comp2->type) { - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1): /* RecordedUnitsList (0xA1) */ - SUB_COMPONENT(comp2, pos2); - GET_COMPONENT(comp2, pos2, vdata, len); - CHECK_COMPONENT(comp2, ASN1_SEQUENCE, "!! Invalid AOC-E Charging Unit argument. Expected Sequence (0x30) but received 0x02%X\n"); /* RecordedUnits */ - sublen3 = pos2 + comp2->len; - pos3 = pos2; - comp3 = comp2; - SUB_COMPONENT(comp3, pos3); - do { - GET_COMPONENT(comp3, pos3, vdata, len); - switch (comp3->type) { - case ASN1_INTEGER: /* numberOfUnits */ - ASN1_GET_INTEGER(comp3, temp); - chargingunits += temp; - case ASN1_NULL: /* notAvailable */ - break; - default: - pri_message(pri, "!! Don't know how to handle 0x%02X in AOC-E RecordedUnits\n", comp3->type); - } - NEXT_COMPONENT(comp3, pos3); - } while (pos3 < sublen3); - if (pri->debug & PRI_DEBUG_AOC) - pri_message(pri, "Channel %d/%d, Call %d - received AOC-E charging: %i unit%s\n", - call->ds1no, call->channelno, call->cr, chargingunits, (chargingunits == 1) ? "" : "s"); - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_2): /* AOCEBillingID (0xA2) */ - SUB_COMPONENT(comp2, pos2); - GET_COMPONENT(comp2, pos2, vdata, len); - ASN1_GET_INTEGER(comp2, chargetype); - pri_message(pri, "!! not handled: Channel %d/%d, Call %d - received AOC-E billing ID: %i\n", - call->ds1no, call->channelno, call->cr, chargetype); - break; - default: - pri_message(pri, "!! Don't know how to handle 0x%02X in AOC-E RecordedUnitsList\n", comp2->type); - } - NEXT_COMPONENT(comp2, pos2); - } while (pos2 < sublen2); - break; - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1): /* freeOfCharge (0x81) */ - if (pri->debug & PRI_DEBUG_AOC) - pri_message(pri, "Channel %d/%d, Call %d - received AOC-E free of charge\n", call->ds1no, call->channelno, call->cr); - chargingunits = 0; - break; - default: - pri_message(pri, "!! Invalid AOC-E specificChargingUnits. Expected Sequence (0x30) or Object Identifier (0x81/0x01) but received 0x%02X\n", comp1->type); - } - NEXT_COMPONENT(comp1, pos1); - GET_COMPONENT(comp1, pos1, vdata, len); /* get optional chargingAssociation. will 'break' when reached end of structure */ - switch (comp1->type) { - /* TODO: charged number is untested - please report! */ - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0): /* chargedNumber (0xA0) */ - if(rose_presented_number_unscreened_decode(pri, call, comp1->data, comp1->len, &chargednr) != 0) - return -1; - pri_message(pri, "!! not handled: Received ChargedNr '%s' \n", chargednr.partyaddress); - pri_message(pri, " ton = %d, pres = %d, npi = %d\n", chargednr.ton, chargednr.pres, chargednr.npi); - break; - case ASN1_INTEGER: - ASN1_GET_INTEGER(comp1, chargeIdentifier); - break; - default: - pri_message(pri, "!! Invalid AOC-E chargingAssociation. Expected Object Identifier (0xA0) or Integer (0x02) but received 0x%02X\n", comp1->type); - } - NEXT_COMPONENT(comp1, pos1); - } while (pos1 < len); + end = + enc_etsi_aoce_charging_unit(ctrl, buffer, buffer + sizeof(buffer), chargedunits); + if (!end) { + return -1; + } - if (pos1 < len) { - pri_message(pri, "!! Only reached position %i in %i bytes long AOC-E structure:", pos1, len ); - dump_apdu (pri, data, len); - return -1; /* oops - aborted before */ + /* Remember that if we queue a facility IE for a facility message we + * have to explicitly send the facility message ourselves */ + if (pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL) + || q931_facility(call->pri, call)) { + pri_message(ctrl, "Could not schedule facility message for call %d\n", call->cr); + return -1; } - call->aoc_units = chargingunits; - + return 0; } +/* End AOC */ -static int aoc_aoce_charging_unit_encode(struct pri *pri, q931_call *c, long chargedunits) +/* ===== Call Transfer Supplementary Service (ECMA-178) ===== */ + +/*! + * \internal + * \brief Encode the Q.SIG CallTransferComplete invoke message. + * + * \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 call Call leg from which to encode call transfer. + * \param call_status TRUE if call is alerting. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_call_transfer_complete(struct pri *ctrl, + unsigned char *pos, unsigned char *end, q931_call *call, int call_status) { - /* sample data: [ 91 a1 12 02 02 3a 78 02 01 24 30 09 30 07 a1 05 30 03 02 01 01 ] */ - int i = 0, res = 0, compsp = 0; - unsigned char buffer[255] = ""; - struct rose_component *comp = NULL, *compstk[10]; + struct fac_extension_header header; + struct rose_msg_invoke msg; - /* ROSE protocol (0x91)*/ - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_ROSE); + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } - /* ROSE Component (0xA1,len)*/ - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_CallTransferComplete; + msg.invoke_id = get_invokeid(ctrl); + msg.args.qsig.CallTransferComplete.end_designation = 0; /* primaryEnd */ - /* ROSE invokeId component (0x02,len,id)*/ - ASN1_ADD_WORDCOMP(comp, INVOKE_IDENTIFIER, buffer, i, ++pri->last_invoke); + /* redirectionNumber is the local_id.number */ + q931_copy_presented_number_screened_to_rose(ctrl, + &msg.args.qsig.CallTransferComplete.redirection, &call->local_id.number); - /* ROSE operationId component (0x02,0x01,0x24)*/ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, ROSE_AOC_AOCE_CHARGING_UNIT); + /* redirectionName is the local_id.name */ + if (call->local_id.name.valid) { + msg.args.qsig.CallTransferComplete.redirection_name_present = 1; + q931_copy_name_to_rose(ctrl, + &msg.args.qsig.CallTransferComplete.redirection_name, + &call->local_id.name); + } - /* AOCEChargingUnitInfo (0x30,len) */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); + if (call_status) { + msg.args.qsig.CallTransferComplete.call_status = 1; /* alerting */ + } + pos = rose_encode_invoke(ctrl, pos, end, &msg); - if (chargedunits > 0) { - /* SpecificChargingUnits (0x30,len) */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); + return pos; +} - /* RecordedUnitsList (0xA1,len) */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - - /* RecordedUnits (0x30,len) */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - - /* NumberOfUnits (0x02,len,charge) */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, chargedunits); +/*! + * \internal + * \brief Encode the ETSI EctInform invoke message. + * + * \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 call Call leg from which to encode inform message. + * \param call_status TRUE if call is alerting. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_ect_inform(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int call_status) +{ + struct rose_msg_invoke msg; - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - } else { - /* freeOfCharge (0x81,0) */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; } - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - - if (pri->debug & PRI_DEBUG_AOC) - dump_apdu (pri, buffer, i); - - /* code below is untested */ - res = pri_call_apdu_queue(c, Q931_FACILITY, buffer, i, NULL, NULL); - if (res) { - pri_message(pri, "Could not queue APDU in facility message\n"); - return -1; + + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_ETSI_EctInform; + msg.invoke_id = get_invokeid(ctrl); + + if (!call_status) { + msg.args.etsi.EctInform.status = 1;/* active */ + + /* + * EctInform(active) contains the redirectionNumber + * redirectionNumber is the local_id.number + */ + msg.args.etsi.EctInform.redirection_present = 1; + q931_copy_presented_number_unscreened_to_rose(ctrl, + &msg.args.etsi.EctInform.redirection, &call->local_id.number); } - /* Remember that if we queue a facility IE for a facility message we - * have to explicitly send the facility message ourselves */ - res = q931_facility(c->pri, c); - if (res) { - pri_message(pri, "Could not schedule facility message for call %d\n", c->cr); + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue the CallTransferComplete/EctInform invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode call transfer. + * \param call_status TRUE if call is alerting. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_call_transfer_complete_encode(struct pri *ctrl, q931_call *call, + int call_status) +{ + unsigned char buffer[256]; + unsigned char *end; + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = + enc_etsi_ect_inform(ctrl, buffer, buffer + sizeof(buffer), call, call_status); + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_call_transfer_complete(ctrl, buffer, buffer + sizeof(buffer), call, + call_status); + break; + default: return -1; } + if (!end) { + return -1; + } - return 0; + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); } -/* End AOC */ -static int rose_calling_name_decode(struct pri *pri, q931_call *call, struct rose_component *choice, int len) +/* ===== End Call Transfer Supplementary Service (ECMA-178) ===== */ + +/*! + * \internal + * \brief Encode the Q.SIG CalledName invoke message. + * + * \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 name Name data which to encode name. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_called_name(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct q931_party_name *name) { - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = choice->data; - int characterSet = 1; - switch (choice->type) { - case ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE: - memcpy(call->callername, choice->data, choice->len); - call->callername[choice->len] = 0; - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " Received simple calling name '%s'\n", call->callername); - return 0; + struct fac_extension_header header; + struct rose_msg_invoke msg; - case ROSE_NAME_PRESENTATION_ALLOWED_EXTENDED: - do { - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_OCTETSTRING, "Don't know what to do if nameData is of type 0x%x\n"); - memcpy(call->callername, comp->data, comp->len); - call->callername[comp->len] = 0; - NEXT_COMPONENT(comp, i); + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do if CharacterSet is of type 0x%x\n"); - ASN1_GET_INTEGER(comp, characterSet); - } - while (0); + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_CalledName; + msg.invoke_id = get_invokeid(ctrl); - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " Received extended calling name '%s', characterset %d\n", call->callername, characterSet); - return 0; - case ROSE_NAME_PRESENTATION_RESTRICTED_SIMPLE: - case ROSE_NAME_PRESENTATION_RESTRICTED_EXTENDED: - case ROSE_NAME_PRESENTATION_RESTRICTED_NULL: - case ROSE_NAME_NOT_AVAIL: - default: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "Do not handle argument of type 0x%X\n", choice->type); - return -1; - } + /* CalledName */ + q931_copy_name_to_rose(ctrl, &msg.args.qsig.CalledName.name, name); + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; } -/* ===== Call Transfer Supplementary Service (ECMA-178) ===== */ -static int rose_party_number_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) +/*! + * \internal + * \brief Encode and queue the Q.SIG CalledName invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode name. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rose_called_name_encode(struct pri *ctrl, q931_call *call, int messagetype) { - int i = 0; - int size = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; + unsigned char buffer[256]; + unsigned char *end; + /* CalledName is the local_id.name */ + end = enc_qsig_called_name(ctrl, buffer, buffer + sizeof(buffer), + &call->local_id.name); + if (!end) { + return -1; + } - do { - GET_COMPONENT(comp, i, vdata, len); + return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL); +} - switch(comp->type) { - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0): /* [0] IMPLICIT NumberDigits -- default: unknownPartyNumber */ - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " PartyNumber: UnknownPartyNumber len=%d\n", len); - size = rose_number_digits_decode(pri, call, comp->data, comp->len, value); - if (size < 0) - return -1; - value->npi = PRI_NPI_UNKNOWN; - value->ton = PRI_TON_UNKNOWN; - break; +/*! + * \internal + * \brief Encode the Q.SIG ConnectedName invoke message. + * + * \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 name Name data which to encode name. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_connected_name(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct q931_party_name *name) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_1): /* [1] IMPLICIT PublicPartyNumber */ - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " PartyNumber: PublicPartyNumber len=%d\n", len); - size = rose_public_party_number_decode(pri, call, comp->data, comp->len, value); - if (size < 0) - return -1; - value->npi = PRI_NPI_E163_E164; - break; + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_3): /* [3] IMPLICIT NumberDigits -- not used: dataPartyNumber */ - pri_message(pri, "!! PartyNumber: dataPartyNumber is reserved!\n"); - size = rose_number_digits_decode(pri, call, comp->data, comp->len, value); - if (size < 0) - return -1; - value->npi = PRI_NPI_X121 /* ??? */; - value->ton = PRI_TON_UNKNOWN /* ??? */; - break; + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_ConnectedName; + msg.invoke_id = get_invokeid(ctrl); - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_4): /* [4] IMPLICIT NumberDigits -- not used: telexPartyNumber */ - pri_message(pri, "!! PartyNumber: telexPartyNumber is reserved!\n"); - size = rose_number_digits_decode(pri, call, comp->data, comp->len, value); - if (size < 0) - return -1; - value->npi = PRI_NPI_F69 /* ??? */; - value->ton = PRI_TON_UNKNOWN /* ??? */; - break; + /* ConnectedName */ + q931_copy_name_to_rose(ctrl, &msg.args.qsig.ConnectedName.name, name); - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_5): /* [5] IMPLICIT PrivatePartyNumber */ - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " PartyNumber: PrivatePartyNumber len=%d\n", len); - size = rose_private_party_number_decode(pri, call, comp->data, comp->len, value); - if (size < 0) - return -1; - value->npi = PRI_NPI_PRIVATE; - break; + pos = rose_encode_invoke(ctrl, pos, end, &msg); - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_8): /* [8] IMPLICIT NumberDigits -- not used: nationalStandatdPartyNumber */ - pri_message(pri, "!! PartyNumber: nationalStandardPartyNumber is reserved!\n"); - size = rose_number_digits_decode(pri, call, comp->data, comp->len, value); - if (size < 0) - return -1; - value->npi = PRI_NPI_NATIONAL; - value->ton = PRI_TON_NATIONAL; - break; + return pos; +} - default: - pri_message(pri, "Invalid PartyNumber component 0x%X\n", comp->type); - return -1; - } - ASN1_FIXUP_LEN(comp, size); - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " PartyNumber: '%s' size=%d len=%d\n", value->partyaddress, size, len); - return size; +/*! + * \internal + * \brief Encode and queue the Q.SIG ConnectedName invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode name. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int rose_connected_name_encode(struct pri *ctrl, q931_call *call, int messagetype) +{ + unsigned char buffer[256]; + unsigned char *end; + + /* ConnectedName is the local_id.name */ + end = enc_qsig_connected_name(ctrl, buffer, buffer + sizeof(buffer), + &call->local_id.name); + if (!end) { + return -1; } - while (0); - return -1; + return pri_call_apdu_queue(call, messagetype, buffer, end - buffer, NULL); } - -static int rose_number_screened_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberscreened *value) +/*! + * \brief Put the APDU on the call queue. + * + * \param call Call to enqueue message. + * \param messagetype Q.931 message type. + * \param apdu Facility ie contents buffer. + * \param apdu_len Length of the contents buffer. + * \param response Sender supplied information to handle APDU response messages. + * NULL if don't care about responses. + * + * \note + * Only APDU messages with an invoke component can supply a response pointer. + * If any other APDU messages supply a response pointer then aliasing of the + * invoke_id can occur. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *apdu, int apdu_len, struct apdu_callback_data *response) { - int i = 0; - int size = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; + struct apdu_event *cur = NULL; + struct apdu_event *new_event = NULL; - int scrind = -1; - - do { - /* Party Number */ - GET_COMPONENT(comp, i, vdata, len); - size = rose_party_number_decode(pri, call, (u_int8_t *)comp, comp->len + 2, (struct addressingdataelements_presentednumberunscreened*) value); - if (size < 0) + if (!call || !messagetype || !apdu + || apdu_len < 1 || sizeof(new_event->apdu) < apdu_len) { + return -1; + } + switch (messagetype) { + case Q931_FACILITY: + break; + default: + if (q931_is_dummy_call(call)) { + pri_error(call->pri, "!! Cannot send %s message on dummy call reference.\n", + msg2str(messagetype)); return -1; - comp->len = size; - NEXT_COMPONENT(comp, i); + } + break; + } - /* Screening Indicator */ - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Don't know what to do with NumberScreened ROSE component type 0x%x\n"); - ASN1_GET_INTEGER(comp, scrind); - // Todo: scrind = screeningindicator_for_q931(pri, scrind); - NEXT_COMPONENT(comp, i); + new_event = calloc(1, sizeof(*new_event)); + if (!new_event) { + pri_error(call->pri, "!! Malloc failed!\n"); + return -1; + } - value->scrind = scrind; + /* Fill in the APDU event */ + new_event->message = messagetype; + if (response) { + new_event->response = *response; + } + new_event->call = call; + new_event->apdu_len = apdu_len; + memcpy(new_event->apdu, apdu, apdu_len); - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " NumberScreened: '%s' ScreeningIndicator=%d i=%d len=%d\n", value->partyaddress, scrind, i, len); - - return i-2; // We do not have a sequence header here. + /* Append APDU event to the end of the list. */ + if (call->apdus) { + for (cur = call->apdus; cur->next; cur = cur->next) { + } + cur->next = new_event; + } else { + call->apdus = new_event; } - while (0); - return -1; + return 0; } - -static int rose_presented_number_screened_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberscreened *value) +/* Used by q931.c to cleanup the apdu queue upon destruction of a call */ +void pri_call_apdu_queue_cleanup(q931_call *call) { - int i = 0; - int size = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; + struct apdu_event *cur_event; + struct apdu_event *free_event; - /* Fill in default values */ - value->ton = PRI_TON_UNKNOWN; - value->npi = PRI_NPI_UNKNOWN; - value->pres = -1; /* Data is not available */ + if (call) { + cur_event = call->apdus; + call->apdus = NULL; + while (cur_event) { + if (cur_event->response.callback) { + /* Indicate to callback that the APDU is being cleaned up. */ + cur_event->response.callback(APDU_CALLBACK_REASON_CLEANUP, call->pri, + call, cur_event, NULL); - do { - GET_COMPONENT(comp, i, vdata, len); + /* Stop any response timeout. */ + pri_schedule_del(call->pri, cur_event->timer); + } + free_event = cur_event; + cur_event = cur_event->next; + free(free_event); + } + } +} - switch(comp->type) { - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_0): /* [0] IMPLICIT presentationAllowedNumber */ - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " PresentedNumberScreened: presentationAllowedNumber comp->len=%d\n", comp->len); - value->pres = PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN; - size = rose_number_screened_decode(pri, call, comp->data, comp->len, value); - if (size < 0) - return -1; - ASN1_FIXUP_LEN(comp, size); - return size + 2; +/*! + * \internal + * \brief Find an outstanding APDU with the given invoke id. + * + * \param call Call to find APDU. + * \param invoke_id Invoke id to match outstanding APDUs in queue. + * + * \retval apdu_event if found. + * \retval NULL if not found. + */ +static struct apdu_event *pri_call_apdu_find(struct q931_call *call, int invoke_id) +{ + struct apdu_event *apdu; - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1): /* [1] IMPLICIT presentationRestricted */ - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " PresentedNumberScreened: presentationRestricted comp->len=%d\n", comp->len); - if (comp->len != 0) { /* must be NULL */ - pri_error(pri, "!! Invalid PresentationRestricted component received (len != 0)\n"); - return -1; - } - value->pres = PRES_PROHIB_USER_NUMBER_PASSED_SCREEN; - return 2; + for (apdu = call->apdus; apdu; apdu = apdu->next) { + /* + * Note: The APDU cannot be sent and still in the queue without a + * callback and timeout timer active. Therefore, an invoke_id of + * zero is valid and not just the result of a memset(). + */ + if (apdu->response.invoke_id == invoke_id && apdu->sent) { + break; + } + } + return apdu; +} - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2): /* [2] IMPLICIT numberNotAvailableDueToInterworking */ - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " PresentedNumberScreened: NumberNotAvailableDueToInterworking comp->len=%d\n", comp->len); - if (comp->len != 0) { /* must be NULL */ - pri_error(pri, "!! Invalid NumberNotAvailableDueToInterworking component received (len != 0)\n"); - return -1; - } - value->pres = PRES_NUMBER_NOT_AVAILABLE; - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " PresentedNumberScreened: numberNotAvailableDueToInterworking Type=0x%X i=%d len=%d size=%d\n", comp->type, i, len); - return 2; +/*! + * \brief Delete the given APDU event from the given call. + * + * \param call Call to remove the APDU. + * \param doomed APDU event to delete. + * + * \return Nothing + */ +void pri_call_apdu_delete(struct q931_call *call, struct apdu_event *doomed) +{ + struct apdu_event **prev; + struct apdu_event *cur; - case (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTOR | ASN1_TAG_3): /* [3] IMPLICIT presentationRestrictedNumber */ - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " PresentedNumberScreened: presentationRestrictedNumber comp->len=%d\n", comp->len); - value->pres = PRES_PROHIB_USER_NUMBER_PASSED_SCREEN; - size = rose_number_screened_decode(pri, call, comp->data, comp->len, value); - if (size < 0) - return -1; - ASN1_FIXUP_LEN(comp, size); - return size + 2; + /* Find APDU in list. */ + for (prev = &call->apdus, cur = call->apdus; + cur; + prev = &cur->next, cur = cur->next) { + if (cur == doomed) { + /* Stop any response timeout. */ + pri_schedule_del(call->pri, cur->timer); - default: - pri_message(pri, "Invalid PresentedNumberScreened component 0x%X\n", comp->type); + /* Remove APDU from list. */ + *prev = cur->next; + free(cur); + break; } - return -1; } - while (0); - - return -1; } - -static int rose_call_transfer_complete_decode(struct pri *pri, q931_call *call, struct rose_component *sequence, int len) +/*! \note Only called when sending the SETUP message. */ +int pri_call_add_standard_apdus(struct pri *ctrl, q931_call *call) { - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = sequence->data; - int res = 0; + if (!ctrl->sendfacility) { + return 0; + } - int end_designation = 0; - struct addressingdataelements_presentednumberscreened redirection_number; - char redirection_name[50] = ""; - int call_status = 0; - redirection_number.partyaddress[0] = 0; - redirection_number.partysubaddress[0] = 0; - call->callername[0] = 0; - call->callernum[0] = 0; + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (q931_is_ptmp(ctrl)) { + /* PTMP mode */ + break; + } + /* PTP mode */ + if (call->redirecting.count) { + rose_diverting_leg_information2_encode(ctrl, call); + /* + * Expect a DivertingLegInformation3 to update the COLR of the + * redirecting-to party we are attempting to call now. + */ + call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; + } + break; + case PRI_SWITCH_QSIG: + /* For Q.SIG it does network and cpe operations */ + if (call->redirecting.count) { + rose_diverting_leg_information2_encode(ctrl, call); - /* Data checks */ - if (sequence->type != (ASN1_CONSTRUCTOR | ASN1_SEQUENCE)) { /* Constructed Sequence */ - pri_message(pri, "Invalid callTransferComplete argument. (Not a sequence)\n"); - return -1; + /* + * Expect a DivertingLegInformation3 to update the COLR of the + * redirecting-to party we are attempting to call now. + */ + call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; + } + add_callername_facility_ies(ctrl, call, 1); + break; + case PRI_SWITCH_NI2: + add_callername_facility_ies(ctrl, call, (ctrl->localtype == PRI_CPE)); + break; + case PRI_SWITCH_DMS100: + if (ctrl->localtype == PRI_CPE) { + add_dms100_transfer_ability_apdu(ctrl, call); + } + break; + default: + break; } - if (sequence->len == ASN1_LEN_INDEF) { - len -= 4; /* For the 2 extra characters at the end - * and two characters of header */ - } else - len -= 2; + return 0; +} - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: len=%d\n", len); +/*! + * \brief Send the CallTransferComplete/EctInform invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode call transfer. + * \param call_status TRUE if call is alerting. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int send_call_transfer_complete(struct pri *ctrl, q931_call *call, int call_status) +{ + if (rose_call_transfer_complete_encode(ctrl, call, call_status) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for call transfer completed.\n"); + return -1; + } - do { - /* End Designation */ - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Invalid endDesignation type 0x%X of ROSE callTransferComplete component received\n"); - ASN1_GET_INTEGER(comp, end_designation); - NEXT_COMPONENT(comp, i); - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: Received endDesignation=%d\n", end_designation); + return 0; +} +/*! + * \internal + * \brief Encode a plain facility ETSI error code. + * + * \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 call Call leg from which to encode error message response. + * \param invoke_id Invoke id to put in error message response. + * \param code Error code to put in error message response. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_error(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int invoke_id, enum rose_error_code code) +{ + struct rose_msg_error msg; - /* Redirection Number */ - GET_COMPONENT(comp, i, vdata, len); - res = rose_presented_number_screened_decode(pri, call, (u_int8_t *)comp, comp->len + 2, &redirection_number); - if (res < 0) - return -1; - comp->len = res; - if (res > 2) { - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: Received redirectionNumber=%s\n", redirection_number.partyaddress); - strncpy(call->callernum, redirection_number.partyaddress, 20); - call->callernum[20] = 0; - } - NEXT_COMPONENT(comp, i); + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke_id; + msg.code = code; -#if 0 /* This one is optional. How do we check if it is there? */ - /* Basic Call Info Elements */ - GET_COMPONENT(comp, i, vdata, len); - NEXT_COMPONENT(comp, i); -#endif + pos = rose_encode_error(ctrl, pos, end, &msg); + return pos; +} - /* Redirection Name */ - GET_COMPONENT(comp, i, vdata, len); - res = asn1_name_decode((u_int8_t *)comp, comp->len + 2, redirection_name, sizeof(redirection_name)); - if (res < 0) - return -1; - memcpy(call->callername, comp->data, comp->len); - call->callername[comp->len] = 0; - ASN1_FIXUP_LEN(comp, res); - comp->len = res; - NEXT_COMPONENT(comp, i); - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: Received redirectionName '%s'\n", redirection_name); +/*! + * \internal + * \brief Encode a plain facility Q.SIG error code. + * + * \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 call Call leg from which to encode error message response. + * \param invoke_id Invoke id to put in error message response. + * \param code Error code to put in error message response. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_error(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int invoke_id, enum rose_error_code code) +{ + struct fac_extension_header header; + struct rose_msg_error msg; + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; + } - /* Call Status */ - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_ENUMERATED, "Invalid callStatus type 0x%X of ROSE callTransferComplete component received\n"); - ASN1_GET_INTEGER(comp, call_status); - NEXT_COMPONENT(comp, i); - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: Received callStatus=%d\n", call_status); + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke_id; + msg.code = code; + pos = rose_encode_error(ctrl, pos, end, &msg); - /* Argument Extension */ -#if 0 /* Not supported */ - GET_COMPONENT(comp, i, vdata, len); - switch (comp->type) { - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_9): /* [9] IMPLICIT Extension */ - res = rose_extension_decode(pri, call, comp->data, comp->len, &redirection_number); - if (res < 0) - return -1; - ASN1_FIXUP_LEN(comp, res); - comp->len = res; + return pos; +} - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_10): /* [10] IMPLICIT SEQUENCE OF Extension */ - res = rose_sequence_of_extension_decode(pri, call, comp->data, comp->len, &redirection_number); - if (res < 0) - return -1; - ASN1_FIXUP_LEN(comp, res); - comp->len = res; +/*! + * \internal + * \brief Encode and queue a plain facility error code. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode error message response. + * \param invoke_id Invoke id to put in error message response. + * \param code Error code to put in error message response. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_facility_error_encode(struct pri *ctrl, q931_call *call, int invoke_id, + enum rose_error_code code) +{ + unsigned char buffer[256]; + unsigned char *end; - default: - pri_message(pri, " CT-Complete: !! Unknown argumentExtension received 0x%X\n", comp->type); - return -1; - } -#else - GET_COMPONENT(comp, i, vdata, len); - ASN1_FIXUP_LEN(comp, res); - NEXT_COMPONENT(comp, i); -#endif + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = + enc_etsi_error(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id, code); + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_error(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id, code); + break; + default: + return -1; + } + if (!end) { + return -1; + } - if(i < len) - pri_message(pri, " CT-Complete: !! not all information is handled !! i=%d / len=%d\n", i, len); + return pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL); +} - return 0; +/*! + * \brief Encode and send a plain facility error code. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode error message response. + * \param invoke_id Invoke id to put in error message response. + * \param code Error code to put in error message response. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_facility_error(struct pri *ctrl, q931_call *call, int invoke_id, + enum rose_error_code code) +{ + if (rose_facility_error_encode(ctrl, call, invoke_id, code) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for error message.\n"); + return -1; } - while (0); - return -1; + return 0; } - -static int rose_call_transfer_update_decode(struct pri *pri, q931_call *call, struct rose_component *sequence, int len) +/*! + * \internal + * \brief Encode a plain facility ETSI result ok. + * + * \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 call Call leg from which to encode result ok message response. + * \param invoke_id Invoke id to put in result ok message response. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_etsi_result_ok(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int invoke_id) { - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = sequence->data; - int res = 0; + struct rose_msg_result msg; - struct addressingdataelements_presentednumberscreened redirection_number; - redirection_number.partyaddress[0] = 0; - redirection_number.partysubaddress[0] = 0; - char redirection_name[50] = ""; - call->callername[0] = 0; - call->callernum[0] = 0; + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; + } + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke_id; + msg.operation = ROSE_None; - /* Data checks */ - if (sequence->type != (ASN1_CONSTRUCTOR | ASN1_SEQUENCE)) { /* Constructed Sequence */ - pri_message(pri, "Invalid callTransferComplete argument. (Not a sequence)\n"); - return -1; + pos = rose_encode_result(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode a plain facility Q.SIG result ok. + * + * \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 call Call leg from which to encode result ok message response. + * \param invoke_id Invoke id to put in result ok message response. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *enc_qsig_result_ok(struct pri *ctrl, unsigned char *pos, + unsigned char *end, q931_call *call, int invoke_id) +{ + struct fac_extension_header header; + struct rose_msg_result msg; + + memset(&header, 0, sizeof(header)); + header.nfe_present = 1; + header.nfe.source_entity = 0; /* endPINX */ + header.nfe.destination_entity = 0; /* endPINX */ + header.interpretation_present = 1; + header.interpretation = 0; /* discardAnyUnrecognisedInvokePdu */ + pos = facility_encode_header(ctrl, pos, end, &header); + if (!pos) { + return NULL; } - if (sequence->len == ASN1_LEN_INDEF) { - len -= 4; /* For the 2 extra characters at the end - * and two characters of header */ - } else - len -= 2; + memset(&msg, 0, sizeof(msg)); + msg.invoke_id = invoke_id; + msg.operation = ROSE_None; - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: len=%d\n", len); + pos = rose_encode_result(ctrl, pos, end, &msg); - do { - /* Redirection Number */ - GET_COMPONENT(comp, i, vdata, len); - res = rose_presented_number_screened_decode(pri, call, (u_int8_t *)comp, comp->len + 2, &redirection_number); - if (res < 0) - return -1; - comp->len = res; - if (res > 2) { - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: Received redirectionNumber=%s\n", redirection_number.partyaddress); - strncpy(call->callernum, redirection_number.partyaddress, 20); - call->callernum[20] = 0; - } - NEXT_COMPONENT(comp, i); + return pos; +} - /* Redirection Name */ - GET_COMPONENT(comp, i, vdata, len); - res = asn1_name_decode((u_int8_t *)comp, comp->len + 2, redirection_name, sizeof(redirection_name)); - if (res < 0) - return -1; - memcpy(call->callername, comp->data, comp->len); - call->callername[comp->len] = 0; - ASN1_FIXUP_LEN(comp, res); - comp->len = res; - NEXT_COMPONENT(comp, i); - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: Received redirectionName '%s'\n", redirection_name); +/*! + * \internal + * \brief Encode and queue a plain ROSE result ok. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode result ok message response. + * \param msgtype Q.931 message type to put facility ie in. + * \param invoke_id Invoke id to put in result ok message response. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int rose_result_ok_encode(struct pri *ctrl, q931_call *call, int msgtype, int invoke_id) +{ + unsigned char buffer[256]; + unsigned char *end; + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + end = + enc_etsi_result_ok(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id); + break; + case PRI_SWITCH_QSIG: + end = + enc_qsig_result_ok(ctrl, buffer, buffer + sizeof(buffer), call, invoke_id); + break; + default: + return -1; + } + if (!end) { + return -1; + } -#if 0 /* This one is optional. How do we check if it is there? */ - /* Basic Call Info Elements */ - GET_COMPONENT(comp, i, vdata, len); - NEXT_COMPONENT(comp, i); -#endif + return pri_call_apdu_queue(call, msgtype, buffer, end - buffer, NULL); +} +/*! + * \brief Encode and send a FACILITY message with a plain ROSE result ok. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode result ok message response. + * \param invoke_id Invoke id to put in result ok message response. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_facility_result_ok(struct pri *ctrl, q931_call *call, int invoke_id) +{ + if (rose_result_ok_encode(ctrl, call, Q931_FACILITY, invoke_id) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for result OK message.\n"); + return -1; + } - /* Argument Extension */ -#if 0 /* Not supported */ - GET_COMPONENT(comp, i, vdata, len); - switch (comp->type) { - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_9): /* [9] IMPLICIT Extension */ - res = rose_extension_decode(pri, call, comp->data, comp->len, &redirection_number); - if (res < 0) - return -1; - ASN1_FIXUP_LEN(comp, res); - comp->len = res; + return 0; +} - case (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_10): /* [10] IMPLICIT SEQUENCE OF Extension */ - res = rose_sequence_of_extension_decode(pri, call, comp->data, comp->len, &redirection_number); - if (res < 0) - return -1; - ASN1_FIXUP_LEN(comp, res); - comp->len = res; +int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code) +{ + enum rose_error_code rose_err; - default: - pri_message(pri, " CT-Complete: !! Unknown argumentExtension received 0x%X\n", comp->type); - return -1; - } -#else - GET_COMPONENT(comp, i, vdata, len); - ASN1_FIXUP_LEN(comp, res); - NEXT_COMPONENT(comp, i); -#endif + if (!ctrl || !call) { + return -1; + } - if(i < len) - pri_message(pri, " CT-Complete: !! not all information is handled !! i=%d / len=%d\n", i, len); + /* Convert the public rerouting response code to an error code or result ok. */ + rose_err = ROSE_ERROR_Gen_ResourceUnavailable; + switch (code) { + case PRI_REROUTING_RSP_OK_CLEAR: + return rose_result_ok_encode(ctrl, call, Q931_DISCONNECT, invoke_id); + case PRI_REROUTING_RSP_OK_RETAIN: + return send_facility_result_ok(ctrl, call, invoke_id); + case PRI_REROUTING_RSP_NOT_SUBSCRIBED: + rose_err = ROSE_ERROR_Gen_NotSubscribed; + break; + case PRI_REROUTING_RSP_NOT_AVAILABLE: + rose_err = ROSE_ERROR_Gen_NotAvailable; + break; + case PRI_REROUTING_RSP_NOT_ALLOWED: + rose_err = ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed; + break; + case PRI_REROUTING_RSP_INVALID_NUMBER: + rose_err = ROSE_ERROR_Div_InvalidDivertedToNr; + break; + case PRI_REROUTING_RSP_SPECIAL_SERVICE_NUMBER: + rose_err = ROSE_ERROR_Div_SpecialServiceNr; + break; + case PRI_REROUTING_RSP_DIVERSION_TO_SELF: + rose_err = ROSE_ERROR_Div_DiversionToServedUserNr; + break; + case PRI_REROUTING_RSP_MAX_DIVERSIONS_EXCEEDED: + rose_err = ROSE_ERROR_Div_NumberOfDiversionsExceeded; + break; + case PRI_REROUTING_RSP_RESOURCE_UNAVAILABLE: + rose_err = ROSE_ERROR_Gen_ResourceUnavailable; + break; + } + return send_facility_error(ctrl, call, invoke_id, rose_err); +} - return 0; +/*! + * \brief Handle the ROSE reject message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. + * \param ie Raw ie contents. + * \param header Decoded facility header before ROSE. + * \param reject Decoded ROSE reject message contents. + * + * \return Nothing + */ +void rose_handle_reject(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, + const struct fac_extension_header *header, const struct rose_msg_reject *reject) +{ + struct apdu_event *apdu; + union apdu_msg_data msg; + + /* Gripe to the user about getting rejected. */ + pri_error(ctrl, "ROSE REJECT:\n"); + if (reject->invoke_id_present) { + pri_error(ctrl, "\tINVOKE ID: %d\n", reject->invoke_id); } - while (0); + pri_error(ctrl, "\tPROBLEM: %s\n", rose_reject2str(reject->code)); - return -1; + switch (ctrl->switchtype) { + case PRI_SWITCH_DMS100: + /* The DMS-100 switch apparently handles invoke_id as an invoke operation. */ + return; + default: + break; + } + + if (!reject->invoke_id_present) { + /* + * No invoke id to look up so we cannot match it to any outstanding APDUs. + * This REJECT is apparently meant for someone monitoring the link. + */ + return; + } + apdu = pri_call_apdu_find(call, reject->invoke_id); + if (!apdu) { + return; + } + msg.reject = reject; + if (apdu->response.callback(APDU_CALLBACK_REASON_MSG_REJECT, ctrl, call, apdu, &msg)) { + pri_call_apdu_delete(call, apdu); + } } +/*! + * \brief Handle the ROSE error message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. + * \param ie Raw ie contents. + * \param header Decoded facility header before ROSE. + * \param error Decoded ROSE error message contents. + * + * \return Nothing + */ +void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, + const struct fac_extension_header *header, const struct rose_msg_error *error) +{ + const char *dms100_operation; + struct apdu_event *apdu; + union apdu_msg_data msg; -/* ===== End Call Transfer Supplementary Service (ECMA-178) ===== */ + /* Gripe to the user about getting an error. */ + pri_error(ctrl, "ROSE RETURN ERROR:\n"); + switch (ctrl->switchtype) { + case PRI_SWITCH_DMS100: + switch (error->invoke_id) { + case ROSE_DMS100_RLT_OPERATION_IND: + dms100_operation = "RLT_OPERATION_IND"; + break; + case ROSE_DMS100_RLT_THIRD_PARTY: + dms100_operation = "RLT_THIRD_PARTY"; + break; + default: + dms100_operation = NULL; + break; + } + if (dms100_operation) { + pri_error(ctrl, "\tOPERATION: %s\n", dms100_operation); + break; + } + /* fall through */ + default: + pri_error(ctrl, "\tINVOKE ID: %d\n", error->invoke_id); + break; + } + pri_error(ctrl, "\tERROR: %s\n", rose_error2str(error->code)); + switch (ctrl->switchtype) { + case PRI_SWITCH_DMS100: + /* The DMS-100 switch apparently handles invoke_id as an invoke operation. */ + return; + default: + break; + } + apdu = pri_call_apdu_find(call, error->invoke_id); + if (!apdu) { + return; + } + msg.error = error; + if (apdu->response.callback(APDU_CALLBACK_REASON_MSG_ERROR, ctrl, call, apdu, &msg)) { + pri_call_apdu_delete(call, apdu); + } +} -int rose_reject_decode(struct pri *pri, q931_call *call, q931_ie *ie, unsigned char *data, int len) +/*! + * \brief Handle the ROSE result message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. + * \param ie Raw ie contents. + * \param header Decoded facility header before ROSE. + * \param result Decoded ROSE result message contents. + * + * \return Nothing + */ +void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, + const struct fac_extension_header *header, const struct rose_msg_result *result) { - int i = 0; - int problemtag = -1; - int problem = -1; - int invokeidvalue = -1; - unsigned char *vdata = data; - struct rose_component *comp = NULL; - char *problemtagstr, *problemstr; - - do { - /* Invoke ID stuff */ - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); - ASN1_GET_INTEGER(comp, invokeidvalue); - NEXT_COMPONENT(comp, i); + struct apdu_event *apdu; + union apdu_msg_data msg; - GET_COMPONENT(comp, i, vdata, len); - problemtag = comp->type; - problem = comp->data[0]; - - if (pri->switchtype == PRI_SWITCH_DMS100) { - switch (problemtag) { - case 0x80: - problemtagstr = "General problem"; + switch (ctrl->switchtype) { + case PRI_SWITCH_DMS100: + /* The DMS-100 switch apparently handles invoke_id as an invoke operation. */ + switch (result->invoke_id) { + case ROSE_DMS100_RLT_OPERATION_IND: + if (result->operation != ROSE_DMS100_RLT_OperationInd) { + pri_message(ctrl, "Invalid Operation value in return result! %s\n", + rose_operation2str(result->operation)); break; - case 0x81: - problemtagstr = "Invoke problem"; - break; - case 0x82: - problemtagstr = "Return result problem"; - break; - case 0x83: - problemtagstr = "Return error problem"; - break; - default: - problemtagstr = "Unknown"; } - switch (problem) { - case 0x00: - problemstr = "Unrecognized component"; - break; - case 0x01: - problemstr = "Mistyped component"; - break; - case 0x02: - problemstr = "Badly structured component"; - break; - default: - problemstr = "Unknown"; + /* We have enough data to transfer the call */ + call->rlt_call_id = result->args.dms100.RLT_OperationInd.call_id; + call->transferable = 1; + break; + case ROSE_DMS100_RLT_THIRD_PARTY: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "Successfully completed RLT transfer!\n"); } - - pri_error(pri, "ROSE REJECT:\n"); - pri_error(pri, "\tINVOKE ID: 0x%X\n", invokeidvalue); - pri_error(pri, "\tPROBLEM TYPE: %s (0x%x)\n", problemtagstr, problemtag); - pri_error(pri, "\tPROBLEM: %s (0x%x)\n", problemstr, problem); - - return 0; - } else { - pri_message(pri, "Unable to handle reject on switchtype %d!\n", pri->switchtype); - return -1; + break; + default: + pri_message(ctrl, "Could not parse invoke of type %d!\n", result->invoke_id); + break; } + return; + default: + break; + } - } while(0); - - return -1; + apdu = pri_call_apdu_find(call, result->invoke_id); + if (!apdu) { + return; + } + msg.result = result; + if (apdu->response.callback(APDU_CALLBACK_REASON_MSG_RESULT, ctrl, call, apdu, &msg)) { + pri_call_apdu_delete(call, apdu); + } } -int rose_return_error_decode(struct pri *pri, q931_call *call, q931_ie *ie, unsigned char *data, int len) + +/*! + * \brief Handle the ROSE invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which the message came. + * \param msgtype Q.931 message type ie is in. + * \param ie Raw ie contents. + * \param header Decoded facility header before ROSE. + * \param invoke Decoded ROSE invoke message contents. + * + * \return Nothing + */ +void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, + const struct fac_extension_header *header, const struct rose_msg_invoke *invoke) { - int i = 0; - int errorvalue = -1; - int invokeidvalue = -1; - unsigned char *vdata = data; - struct rose_component *comp = NULL; - char *invokeidstr, *errorstr; - - do { - /* Invoke ID stuff */ - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); - ASN1_GET_INTEGER(comp, invokeidvalue); - NEXT_COMPONENT(comp, i); + struct pri_subcommand *subcmd; + struct q931_party_id party_id; + struct q931_party_redirecting deflection; - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do if second component in return error is 0x%x\n"); - ASN1_GET_INTEGER(comp, errorvalue); + switch (invoke->operation) { +#if 0 /* Not handled yet */ + case ROSE_ETSI_ActivationDiversion: + break; + case ROSE_ETSI_DeactivationDiversion: + break; + case ROSE_ETSI_ActivationStatusNotificationDiv: + break; + case ROSE_ETSI_DeactivationStatusNotificationDiv: + break; + case ROSE_ETSI_InterrogationDiversion: + break; + case ROSE_ETSI_DiversionInformation: + break; +#endif /* Not handled yet */ + case ROSE_ETSI_CallDeflection: + if (!PRI_MASTER(ctrl)->deflection_support) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + break; + } + if (!q931_master_pass_event(ctrl, call, msgtype)) { + /* Some other user is further along to connecting than this call. */ + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Div_IncomingCallAccepted); + break; + } + if (call->master_call->deflection_in_progress) { + /* Someone else is already doing a call deflection. */ + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Div_RequestAlreadyAccepted); + break; + } + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + /* + * ROSE_ERROR_Gen_ResourceUnavailable was not in the list of allowed codes, + * but we will send it anyway. + */ + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_ResourceUnavailable); + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } - if (pri->switchtype == PRI_SWITCH_DMS100) { - switch (invokeidvalue) { - case RLT_OPERATION_IND: - invokeidstr = "RLT_OPERATION_IND"; - break; - case RLT_THIRD_PARTY: - invokeidstr = "RLT_THIRD_PARTY"; - break; - default: - invokeidstr = "Unknown"; - } + call->master_call->deflection_in_progress = 1; - switch (errorvalue) { - case 0x10: - errorstr = "RLT Bridge Fail"; - break; - case 0x11: - errorstr = "RLT Call ID Not Found"; - break; - case 0x12: - errorstr = "RLT Not Allowed"; - break; - case 0x13: - errorstr = "RLT Switch Equip Congs"; - break; - default: - errorstr = "Unknown"; - } + q931_party_redirecting_init(&deflection); - pri_error(pri, "ROSE RETURN ERROR:\n"); - pri_error(pri, "\tOPERATION: %s\n", invokeidstr); - pri_error(pri, "\tERROR: %s\n", errorstr); + /* Deflecting from the called address. */ + q931_party_address_to_id(&deflection.from, &call->called); + if (invoke->args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user_present) { + deflection.from.number.presentation = + invoke->args.etsi.CallDeflection.presentation_allowed_to_diverted_to_user + ? PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED + : PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } else { + deflection.from.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } - return 0; + /* Deflecting to the new address. */ + rose_copy_address_to_q931(ctrl, &deflection.to, + &invoke->args.etsi.CallDeflection.deflection); + deflection.to.number.presentation = deflection.from.number.presentation; + + deflection.count = (call->redirecting.count < PRI_MAX_REDIRECTS) + ? call->redirecting.count + 1 : PRI_MAX_REDIRECTS; + deflection.reason = PRI_REDIR_DEFLECTION; + if (deflection.count == 1) { + deflection.orig_called = deflection.from; + deflection.orig_reason = deflection.reason; } else { - pri_message(pri, "Unable to handle return error on switchtype %d!\n", pri->switchtype); + deflection.orig_called = call->redirecting.orig_called; + deflection.orig_reason = call->redirecting.orig_reason; } - } while(0); - - return -1; -} + subcmd->cmd = PRI_SUBCMD_REROUTING; + subcmd->u.rerouting.invoke_id = invoke->invoke_id; + subcmd->u.rerouting.subscription_option = 3;/* notApplicable */ + q931_party_id_copy_to_pri(&subcmd->u.rerouting.caller, &call->local_id); + q931_party_redirecting_copy_to_pri(&subcmd->u.rerouting.deflection, + &deflection); + break; + case ROSE_ETSI_CallRerouting: + if (!PRI_MASTER(ctrl)->deflection_support) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + break; + } + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_ResourceUnavailable); + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } -int rose_return_result_decode(struct pri *pri, q931_call *call, q931_ie *ie, unsigned char *data, int len) -{ - int i = 0; - int operationidvalue = -1; - int invokeidvalue = -1; - unsigned char *vdata = data; - struct rose_component *comp = NULL; - - do { - /* Invoke ID stuff */ - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); - ASN1_GET_INTEGER(comp, invokeidvalue); - NEXT_COMPONENT(comp, i); + q931_party_redirecting_init(&deflection); - if (pri->switchtype == PRI_SWITCH_DMS100) { - switch (invokeidvalue) { - case RLT_THIRD_PARTY: - if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Successfully completed RLT transfer!\n"); - return 0; - case RLT_OPERATION_IND: - if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Received RLT_OPERATION_IND\n"); - /* Have to take out the rlt_call_id */ - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_SEQUENCE, "Protocol error detected in parsing RLT_OPERATION_IND return result!\n"); + /* Rerouting from the last address. */ + rose_copy_presented_number_unscreened_to_q931(ctrl, &deflection.from.number, + &invoke->args.etsi.CallRerouting.last_rerouting); - /* Traverse the contents of this sequence */ - /* First is the Operation Value */ - SUB_COMPONENT(comp, i); - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_INTEGER, "RLT_OPERATION_IND should be of type ASN1_INTEGER!\n"); - ASN1_GET_INTEGER(comp, operationidvalue); + /* Rerouting to the new address. */ + rose_copy_address_to_q931(ctrl, &deflection.to, + &invoke->args.etsi.CallRerouting.called_address); + switch (invoke->args.etsi.CallRerouting.subscription_option) { + default: + case 0: /* noNotification */ + case 1: /* notificationWithoutDivertedToNr */ + deflection.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + case 2: /* notificationWithDivertedToNr */ + deflection.to.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + } - if (operationidvalue != RLT_OPERATION_IND) { - pri_message(pri, "Invalid Operation ID value (0x%x) in return result!\n", operationidvalue); - return -1; - } + /* Calling party subaddress update. */ + party_id = call->local_id; - /* Next is the Call ID */ - NEXT_COMPONENT(comp, i); - GET_COMPONENT(comp, i, vdata, len); - CHECK_COMPONENT(comp, ASN1_TAG_0, "Error check failed on Call ID!\n"); - ASN1_GET_INTEGER(comp, call->rlt_call_id); - /* We have enough data to transfer the call */ - call->transferable = 1; + deflection.count = invoke->args.etsi.CallRerouting.rerouting_counter; + deflection.reason = redirectingreason_for_q931(ctrl, + invoke->args.etsi.CallRerouting.rerouting_reason); + if (deflection.count == 1) { + deflection.orig_called = deflection.from; + deflection.orig_reason = deflection.reason; + } else { + deflection.orig_called = call->redirecting.orig_called; + deflection.orig_reason = call->redirecting.orig_reason; + } - return 0; - - default: - pri_message(pri, "Could not parse invoke of type 0x%x!\n", invokeidvalue); - return -1; + subcmd->cmd = PRI_SUBCMD_REROUTING; + subcmd->u.rerouting.invoke_id = invoke->invoke_id; + subcmd->u.rerouting.subscription_option = + invoke->args.etsi.CallRerouting.subscription_option; + q931_party_id_copy_to_pri(&subcmd->u.rerouting.caller, &party_id); + q931_party_redirecting_copy_to_pri(&subcmd->u.rerouting.deflection, + &deflection); + break; +#if 0 /* Not handled yet */ + case ROSE_ETSI_InterrogateServedUserNumbers: + break; +#endif /* Not handled yet */ + case ROSE_ETSI_DivertingLegInformation1: + if (invoke->args.etsi.DivertingLegInformation1.diverted_to_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, &party_id.number, + &invoke->args.etsi.DivertingLegInformation1.diverted_to); + /* + * We set the presentation value since the sender cannot know the + * presentation value preference of the destination party. + */ + if (party_id.number.str[0]) { + party_id.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + } else { + party_id.number.presentation = + PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED; } - } else if (pri->switchtype == PRI_SWITCH_QSIG) { - switch (invokeidvalue) { - case 0x13: - if (pri->debug & PRI_DEBUG_APDU) pri_message(pri, "Successfully completed QSIG CF callRerouting!\n"); - return 0; - } } else { - pri_message(pri, "Unable to handle return result on switchtype %d!\n", pri->switchtype); - return -1; + q931_party_number_init(&party_id.number); + party_id.number.valid = 1; } - } while(0); - - return -1; -} + /* + * Unless otherwise indicated by CONNECT, the divertedToNumber will be + * the remote_id.number. + */ + if (!call->connected_number_in_message) { + call->remote_id.number = party_id.number; + } -int rose_invoke_decode(struct pri *pri, q931_call *call, q931_ie *ie, unsigned char *data, int len) -{ - int i = 0; - int res = 0; - int operation_tag; - unsigned char *vdata = data; - struct rose_component *comp = NULL, *invokeid = NULL, *operationid = NULL; - - do { - /* Invoke ID stuff */ - GET_COMPONENT(comp, i, vdata, len); -#if 0 - CHECK_COMPONENT(comp, INVOKE_IDENTIFIER, "Don't know what to do if first ROSE component is of type 0x%x\n"); -#endif - invokeid = comp; - NEXT_COMPONENT(comp, i); + /* divertedToNumber is put in redirecting.to.number */ + switch (invoke->args.etsi.DivertingLegInformation1.subscription_option) { + default: + case 0: /* noNotification */ + case 1: /* notificationWithoutDivertedToNr */ + q931_party_number_init(&call->redirecting.to.number); + call->redirecting.to.number.valid = 1; + call->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + case 2: /* notificationWithDivertedToNr */ + call->redirecting.to.number = party_id.number; + break; + } - /* Operation Tag */ - GET_COMPONENT(comp, i, vdata, len); -#if 0 - CHECK_COMPONENT(comp, ASN1_INTEGER, "Don't know what to do if second ROSE component is of type 0x%x\n"); -#endif - operationid = comp; - ASN1_GET_INTEGER(comp, operation_tag); - NEXT_COMPONENT(comp, i); + call->redirecting.reason = redirectingreason_for_q931(ctrl, + invoke->args.etsi.DivertingLegInformation1.diversion_reason); + if (call->redirecting.count < PRI_MAX_REDIRECTS) { + ++call->redirecting.count; + } + call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; + break; + case ROSE_ETSI_DivertingLegInformation2: + call->redirecting.state = Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3; + call->redirecting.count = + invoke->args.etsi.DivertingLegInformation2.diversion_counter; + if (!call->redirecting.count) { + /* To be safe, make sure that the count is non-zero. */ + call->redirecting.count = 1; + } + call->redirecting.reason = redirectingreason_for_q931(ctrl, + invoke->args.etsi.DivertingLegInformation2.diversion_reason); - /* No argument - return with error */ - if (i >= len) - return -1; + /* divertingNr is put in redirecting.from.number */ + if (invoke->args.etsi.DivertingLegInformation2.diverting_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->redirecting.from.number, + &invoke->args.etsi.DivertingLegInformation2.diverting); + } else if (!call->redirecting_number_in_message) { + q931_party_number_init(&call->redirecting.from.number); + call->redirecting.from.number.valid = 1; + } - /* Arguement Tag */ - GET_COMPONENT(comp, i, vdata, len); - if (!comp->type) - return -1; + call->redirecting.orig_reason = PRI_REDIR_UNKNOWN; - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " [ Handling operation %d ]\n", operation_tag); - switch (operation_tag) { - case SS_CNID_CALLINGNAME: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " Handle Name display operation\n"); - return rose_calling_name_decode(pri, call, comp, len-i); - case ROSE_CALL_TRANSFER_IDENTIFY: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "ROSE %i: CallTransferIdentify - not handled!\n", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - return -1; - case ROSE_CALL_TRANSFER_ABANDON: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "ROSE %i: CallTransferAbandon - not handled!\n", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - return -1; - case ROSE_CALL_TRANSFER_INITIATE: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "ROSE %i: CallTransferInitiate - not handled!\n", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - return -1; - case ROSE_CALL_TRANSFER_SETUP: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "ROSE %i: CallTransferSetup - not handled!\n", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - return -1; - case ROSE_CALL_TRANSFER_ACTIVE: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "ROSE %i: CallTransferActive - not handled!\n", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - return -1; - case ROSE_CALL_TRANSFER_COMPLETE: - if (pri->debug & PRI_DEBUG_APDU) - { - pri_message(pri, "ROSE %i: Handle CallTransferComplete\n", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + /* originalCalledNr is put in redirecting.orig_called.number */ + if (invoke->args.etsi.DivertingLegInformation2.original_called_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->redirecting.orig_called.number, + &invoke->args.etsi.DivertingLegInformation2.original_called); + } else { + q931_party_number_init(&call->redirecting.orig_called.number); + } + break; + case ROSE_ETSI_DivertingLegInformation3: + /* + * Unless otherwise indicated by CONNECT, this will be the + * remote_id.number.presentation. + */ + if (!invoke->args.etsi.DivertingLegInformation3.presentation_allowed_indicator) { + call->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + if (!call->connected_number_in_message) { + call->remote_id.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; } - return rose_call_transfer_complete_decode(pri, call, comp, len-i); - case ROSE_CALL_TRANSFER_UPDATE: - if (pri->debug & PRI_DEBUG_APDU) - { - pri_message(pri, "ROSE %i: Handle CallTransferUpdate\n", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + } + + switch (call->redirecting.state) { + case Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3: + call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; } - return rose_call_transfer_update_decode(pri, call, comp, len-i); - case ROSE_SUBADDRESS_TRANSFER: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "ROSE %i: SubaddressTransfer - not handled!\n", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - return -1; - case ROSE_DIVERTING_LEG_INFORMATION2: - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "ROSE %i: Handle CallingName\n", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - } - return rose_diverting_leg_information2_decode(pri, call, comp, len-i); - case ROSE_AOC_NO_CHARGING_INFO_AVAILABLE: - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "ROSE %i: AOC No Charging Info Available - not handled!", operation_tag); - dump_apdu (pri, comp->data, comp->len); - } - return -1; - case ROSE_AOC_CHARGING_REQUEST: - return aoc_aoce_charging_request_decode(pri, call, (u_int8_t *)comp, comp->len + 2); - case ROSE_AOC_AOCS_CURRENCY: - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "ROSE %i: AOC-S Currency - not handled!", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - } - return -1; - case ROSE_AOC_AOCS_SPECIAL_ARR: - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "ROSE %i: AOC-S Special Array - not handled!", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - } - return -1; - case ROSE_AOC_AOCD_CURRENCY: - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "ROSE %i: AOC-D Currency - not handled!", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - } - return -1; - case ROSE_AOC_AOCD_CHARGING_UNIT: - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "ROSE %i: AOC-D Charging Unit - not handled!", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - } - return -1; - case ROSE_AOC_AOCE_CURRENCY: - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "ROSE %i: AOC-E Currency - not handled!", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - } - return -1; - case ROSE_AOC_AOCE_CHARGING_UNIT: - return aoc_aoce_charging_unit_decode(pri, call, (u_int8_t *)comp, comp->len + 2); - if (0) { /* the following function is currently not used - just to make the compiler happy */ - aoc_aoce_charging_unit_encode(pri, call, call->aoc_units); /* use this function to forward the aoc-e on a bridged channel */ - return 0; - } - case ROSE_AOC_IDENTIFICATION_OF_CHARGE: - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "ROSE %i: AOC Identification Of Charge - not handled!", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); - } - return -1; - case SS_ANFPR_PATHREPLACEMENT: - /* Clear Queue */ - res = pri_call_apdu_queue_cleanup(call->bridged_call); - if (res) { - pri_message(pri, "Could not Clear queue ADPU\n"); - return -1; - } - anfpr_pathreplacement_respond(pri, call, ie); - break; + /* Setup redirecting subcommand */ + subcmd->cmd = PRI_SUBCMD_REDIRECTING; + q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, + &call->redirecting); + break; default: - if (pri->debug & PRI_DEBUG_APDU) { - pri_message(pri, "!! Unable to handle ROSE operation %d", operation_tag); - dump_apdu (pri, (u_int8_t *)comp, comp->len + 2); + break; + } + break; + case ROSE_ETSI_ChargingRequest: + /* Ignore messsage */ + break; +#if 0 /* Not handled yet */ + case ROSE_ETSI_AOCSCurrency: + break; + case ROSE_ETSI_AOCSSpecialArr: + break; + case ROSE_ETSI_AOCDCurrency: + break; + case ROSE_ETSI_AOCDChargingUnit: + break; + case ROSE_ETSI_AOCECurrency: + break; +#endif /* Not handled yet */ + case ROSE_ETSI_AOCEChargingUnit: + call->aoc_units = 0; + if (invoke->args.etsi.AOCEChargingUnit.type == 1 + && !invoke->args.etsi.AOCEChargingUnit.charging_unit.free_of_charge) { + unsigned index; + + for (index = + invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded. + num_records; index--;) { + if (!invoke->args.etsi.AOCEChargingUnit.charging_unit.specific.recorded. + list[index].not_available) { + call->aoc_units += + invoke->args.etsi.AOCEChargingUnit.charging_unit.specific. + recorded.list[index].number_of_units; + } } - return -1; } - } while(0); - - return -1; -} + /* the following function is currently not used - just to make the compiler happy */ + if (0) { + /* use this function to forward the aoc-e on a bridged channel */ + aoc_aoce_charging_unit_encode(ctrl, call, call->aoc_units); + } + break; +#if 0 /* Not handled yet */ + case ROSE_ITU_IdentificationOfCharge: + break; +#endif /* Not handled yet */ +#if 0 /* Not handled yet */ + case ROSE_ETSI_EctExecute: + break; + case ROSE_ETSI_ExplicitEctExecute: + break; +#endif /* Not handled yet */ + case ROSE_ETSI_RequestSubaddress: + /* Ignore since we are not handling subaddresses yet. */ + break; +#if 0 /* Not handled yet */ + case ROSE_ETSI_SubaddressTransfer: + break; + case ROSE_ETSI_EctLinkIdRequest: + break; +#endif /* Not handled yet */ + case ROSE_ETSI_EctInform: + /* redirectionNumber is put in remote_id.number */ + if (invoke->args.etsi.EctInform.redirection_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->remote_id.number, &invoke->args.etsi.EctInform.redirection); + } + if (!invoke->args.etsi.EctInform.status) { + /* The remote party for the transfer has not answered yet. */ + call->incoming_ct_state = INCOMING_CT_STATE_EXPECT_CT_ACTIVE; + } else { + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; + } + break; +#if 0 /* Not handled yet */ + case ROSE_ETSI_EctLoopTest: + break; +#endif /* Not handled yet */ + case ROSE_QSIG_CallingName: + /* CallingName is put in remote_id.name */ + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.CallingName.name); + break; + case ROSE_QSIG_CalledName: + /* CalledName is put in remote_id.name */ + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.CalledName.name); -int pri_call_apdu_queue(q931_call *call, int messagetype, void *apdu, int apdu_len, void (*function)(void *data), void *data) -{ - struct apdu_event *cur = NULL; - struct apdu_event *new_event = NULL; + /* Setup connected line subcommand */ + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } + subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; + q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &call->remote_id); + break; + case ROSE_QSIG_ConnectedName: + /* ConnectedName is put in remote_id.name */ + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.ConnectedName.name); + break; +#if 0 /* Not handled yet */ + case ROSE_QSIG_BusyName: + break; +#endif /* Not handled yet */ +#if 0 /* Not handled yet */ + case ROSE_QSIG_ChargeRequest: + break; + case ROSE_QSIG_GetFinalCharge: + break; + case ROSE_QSIG_AocFinal: + break; + case ROSE_QSIG_AocInterim: + break; + case ROSE_QSIG_AocRate: + break; + case ROSE_QSIG_AocComplete: + break; + case ROSE_QSIG_AocDivChargeReq: + break; +#endif /* Not handled yet */ +#if 0 /* Not handled yet */ + case ROSE_QSIG_CallTransferIdentify: + break; + case ROSE_QSIG_CallTransferAbandon: + break; + case ROSE_QSIG_CallTransferInitiate: + break; + case ROSE_QSIG_CallTransferSetup: + break; +#endif /* Not handled yet */ + case ROSE_QSIG_CallTransferActive: + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; - if (!call || !messagetype || !apdu || (apdu_len < 1) || (apdu_len > 255)) - return -1; + /* connectedAddress is put in remote_id */ + rose_copy_presented_address_screened_to_q931(ctrl, &call->remote_id, + &invoke->args.qsig.CallTransferActive.connected); - if (!(new_event = calloc(1, sizeof(*new_event)))) { - pri_error(call->pri, "!! Malloc failed!\n"); - return -1; - } + /* connectedName is put in remote_id.name */ + if (invoke->args.qsig.CallTransferActive.connected_name_present) { + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.CallTransferActive.connected_name); + } + break; + case ROSE_QSIG_CallTransferComplete: + /* redirectionNumber is put in remote_id.number */ + rose_copy_presented_number_screened_to_q931(ctrl, &call->remote_id.number, + &invoke->args.qsig.CallTransferComplete.redirection); - new_event->message = messagetype; - new_event->callback = function; - new_event->data = data; - memcpy(new_event->apdu, apdu, apdu_len); - new_event->apdu_len = apdu_len; - - if (call->apdus) { - cur = call->apdus; - while (cur->next) { - cur = cur->next; + /* redirectionName is put in remote_id.name */ + if (invoke->args.qsig.CallTransferComplete.redirection_name_present) { + rose_copy_name_to_q931(ctrl, &call->remote_id.name, + &invoke->args.qsig.CallTransferComplete.redirection_name); } - cur->next = new_event; - } else - call->apdus = new_event; - return 0; -} + if (invoke->args.qsig.CallTransferComplete.call_status == 1) { + /* The remote party for the transfer has not answered yet. */ + call->incoming_ct_state = INCOMING_CT_STATE_EXPECT_CT_ACTIVE; + } else { + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; + } + break; + case ROSE_QSIG_CallTransferUpdate: + party_id = call->remote_id; -int pri_call_apdu_queue_cleanup(q931_call *call) -{ - struct apdu_event *cur_event = NULL, *free_event = NULL; + /* redirectionNumber is put in party_id.number */ + rose_copy_presented_number_screened_to_q931(ctrl, &party_id.number, + &invoke->args.qsig.CallTransferUpdate.redirection); - if (call && call->apdus) { - cur_event = call->apdus; - while (cur_event) { - /* TODO: callbacks, some way of giving return res on status of apdu */ - free_event = cur_event; - cur_event = cur_event->next; - free(free_event); + /* redirectionName is put in party_id.name */ + if (invoke->args.qsig.CallTransferUpdate.redirection_name_present) { + rose_copy_name_to_q931(ctrl, &party_id.name, + &invoke->args.qsig.CallTransferUpdate.redirection_name); } - call->apdus = NULL; - } - return 0; -} - -int pri_call_add_standard_apdus(struct pri *pri, q931_call *call) -{ - if (!pri->sendfacility) - return 0; - - if (pri->switchtype == PRI_SWITCH_QSIG) { /* For Q.SIG it does network and cpe operations */ - if (call->redirectingnum[0]) - rose_diverting_leg_information2_encode(pri, call); - add_callername_facility_ies(pri, call, 1); - return 0; - } - -#if 0 - if (pri->localtype == PRI_NETWORK) { - switch (pri->switchtype) { - case PRI_SWITCH_NI2: - add_callername_facility_ies(pri, call, 0); + if (q931_party_id_cmp(&party_id, &call->remote_id)) { + /* The remote_id data has changed. */ + call->remote_id = party_id; + switch (call->incoming_ct_state) { + case INCOMING_CT_STATE_IDLE: + call->incoming_ct_state = INCOMING_CT_STATE_POST_CONNECTED_LINE; break; default: break; + } } - return 0; - } else if (pri->localtype == PRI_CPE) { - switch (pri->switchtype) { - case PRI_SWITCH_NI2: - add_callername_facility_ies(pri, call, 1); - break; - default: - break; + break; +#if 0 /* Not handled yet */ + case ROSE_QSIG_SubaddressTransfer: + break; +#endif /* Not handled yet */ + case ROSE_QSIG_PathReplacement: + anfpr_pathreplacement_respond(ctrl, call, ie); + break; +#if 0 /* Not handled yet */ + case ROSE_QSIG_ActivateDiversionQ: + break; + case ROSE_QSIG_DeactivateDiversionQ: + break; + case ROSE_QSIG_InterrogateDiversionQ: + break; + case ROSE_QSIG_CheckRestriction: + break; +#endif /* Not handled yet */ + case ROSE_QSIG_CallRerouting: + if (!PRI_MASTER(ctrl)->deflection_support) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_NotSubscribed); + break; } - return 0; - } -#else - if (pri->switchtype == PRI_SWITCH_NI2) - add_callername_facility_ies(pri, call, (pri->localtype == PRI_CPE)); -#endif + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + send_facility_error(ctrl, call, invoke->invoke_id, + ROSE_ERROR_Gen_ResourceUnavailable); + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } - if ((pri->switchtype == PRI_SWITCH_DMS100) && (pri->localtype == PRI_CPE)) { - add_dms100_transfer_ability_apdu(pri, call); - } + q931_party_redirecting_init(&deflection); + /* Rerouting from the last address. */ + rose_copy_presented_number_unscreened_to_q931(ctrl, &deflection.from.number, + &invoke->args.qsig.CallRerouting.last_rerouting); + if (invoke->args.qsig.CallRerouting.redirecting_name_present) { + rose_copy_name_to_q931(ctrl, &deflection.from.name, + &invoke->args.qsig.CallRerouting.redirecting_name); + } + /* Rerouting to the new address. */ + rose_copy_address_to_q931(ctrl, &deflection.to, + &invoke->args.qsig.CallRerouting.called); + switch (invoke->args.qsig.CallRerouting.subscription_option) { + default: + case 0: /* noNotification */ + case 1: /* notificationWithoutDivertedToNr */ + deflection.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + case 2: /* notificationWithDivertedToNr */ + deflection.to.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + } - return 0; + /* Calling party update. */ + party_id = call->local_id; + rose_copy_presented_number_screened_to_q931(ctrl, &party_id.number, + &invoke->args.qsig.CallRerouting.calling); + if (invoke->args.qsig.CallRerouting.calling_name_present) { + rose_copy_name_to_q931(ctrl, &party_id.name, + &invoke->args.qsig.CallRerouting.calling_name); + } + + deflection.count = invoke->args.qsig.CallRerouting.diversion_counter; + deflection.reason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.CallRerouting.rerouting_reason); + + /* Original called party update. */ + if (deflection.count == 1) { + deflection.orig_called = deflection.from; + deflection.orig_reason = deflection.reason; + } else { + deflection.orig_called = call->redirecting.orig_called; + deflection.orig_reason = call->redirecting.orig_reason; + } + if (invoke->args.qsig.CallRerouting.original_called_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &deflection.orig_called.number, + &invoke->args.qsig.CallRerouting.original_called); + } + if (invoke->args.qsig.CallRerouting.original_called_name_present) { + rose_copy_name_to_q931(ctrl, &deflection.orig_called.name, + &invoke->args.qsig.CallRerouting.original_called_name); + } + if (invoke->args.qsig.CallRerouting.original_rerouting_reason_present) { + deflection.orig_reason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.CallRerouting.original_rerouting_reason); + } + + subcmd->cmd = PRI_SUBCMD_REROUTING; + subcmd->u.rerouting.invoke_id = invoke->invoke_id; + subcmd->u.rerouting.subscription_option = + invoke->args.qsig.CallRerouting.subscription_option; + q931_party_id_copy_to_pri(&subcmd->u.rerouting.caller, &party_id); + q931_party_redirecting_copy_to_pri(&subcmd->u.rerouting.deflection, + &deflection); + break; + case ROSE_QSIG_DivertingLegInformation1: + q931_party_number_init(&party_id.number); + rose_copy_number_to_q931(ctrl, &party_id.number, + &invoke->args.qsig.DivertingLegInformation1.nominated_number); + if (party_id.number.str[0]) { + party_id.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + + /* + * Unless otherwise indicated by CONNECT, the nominatedNr will be + * the remote_id.number. + */ + if (!call->connected_number_in_message) { + call->remote_id.number = party_id.number; + } + + /* nominatedNr is put in redirecting.to.number */ + switch (invoke->args.qsig.DivertingLegInformation1.subscription_option) { + default: + case QSIG_NO_NOTIFICATION: + case QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR: + q931_party_number_init(&call->redirecting.to.number); + call->redirecting.to.number.valid = 1; + call->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + break; + case QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR: + call->redirecting.to.number = party_id.number; + break; + } + + call->redirecting.reason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation1.diversion_reason); + if (call->redirecting.count < PRI_MAX_REDIRECTS) { + ++call->redirecting.count; + } + call->redirecting.state = Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3; + break; + case ROSE_QSIG_DivertingLegInformation2: + call->redirecting.state = Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3; + call->redirecting.count = + invoke->args.qsig.DivertingLegInformation2.diversion_counter; + if (!call->redirecting.count) { + /* To be safe, make sure that the count is non-zero. */ + call->redirecting.count = 1; + } + call->redirecting.reason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.diversion_reason); + + /* divertingNr is put in redirecting.from.number */ + if (invoke->args.qsig.DivertingLegInformation2.diverting_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->redirecting.from.number, + &invoke->args.qsig.DivertingLegInformation2.diverting); + } else if (!call->redirecting_number_in_message) { + q931_party_number_init(&call->redirecting.from.number); + call->redirecting.from.number.valid = 1; + } + + /* redirectingName is put in redirecting.from.name */ + if (invoke->args.qsig.DivertingLegInformation2.redirecting_name_present) { + rose_copy_name_to_q931(ctrl, &call->redirecting.from.name, + &invoke->args.qsig.DivertingLegInformation2.redirecting_name); + } else { + q931_party_name_init(&call->redirecting.from.name); + } + + call->redirecting.orig_reason = PRI_REDIR_UNKNOWN; + if (invoke->args.qsig.DivertingLegInformation2.original_diversion_reason_present) { + call->redirecting.orig_reason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.original_diversion_reason); + } + + /* originalCalledNr is put in redirecting.orig_called.number */ + if (invoke->args.qsig.DivertingLegInformation2.original_called_present) { + rose_copy_presented_number_unscreened_to_q931(ctrl, + &call->redirecting.orig_called.number, + &invoke->args.qsig.DivertingLegInformation2.original_called); + } else { + q931_party_number_init(&call->redirecting.orig_called.number); + } + + /* originalCalledName is put in redirecting.orig_called.name */ + if (invoke->args.qsig.DivertingLegInformation2.original_called_name_present) { + rose_copy_name_to_q931(ctrl, &call->redirecting.orig_called.name, + &invoke->args.qsig.DivertingLegInformation2.original_called_name); + } else { + q931_party_name_init(&call->redirecting.orig_called.name); + } + break; + case ROSE_QSIG_DivertingLegInformation3: + /* + * Unless otherwise indicated by CONNECT, this will be the + * remote_id.number.presentation. + */ + if (!invoke->args.qsig.DivertingLegInformation3.presentation_allowed_indicator) { + call->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + if (!call->connected_number_in_message) { + call->remote_id.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + } + + /* redirectionName is put in redirecting.to.name */ + if (invoke->args.qsig.DivertingLegInformation3.redirection_name_present) { + rose_copy_name_to_q931(ctrl, &call->redirecting.to.name, + &invoke->args.qsig.DivertingLegInformation3.redirection_name); + if (!invoke->args.qsig.DivertingLegInformation3.presentation_allowed_indicator) { + call->redirecting.to.name.presentation = PRI_PRES_RESTRICTED; + } + } else { + q931_party_name_init(&call->redirecting.to.name); + } + + switch (call->redirecting.state) { + case Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3: + call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; + subcmd = q931_alloc_subcommand(ctrl); + if (!subcmd) { + pri_error(ctrl, "ERROR: Too many facility subcommands\n"); + break; + } + /* Setup redirecting subcommand */ + subcmd->cmd = PRI_SUBCMD_REDIRECTING; + q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, + &call->redirecting); + break; + default: + break; + } + break; +#if 0 /* Not handled yet */ + case ROSE_QSIG_CfnrDivertedLegFailed: + break; +#endif /* Not handled yet */ +#if 0 /* Not handled yet */ + case ROSE_QSIG_MWIActivate: + break; + case ROSE_QSIG_MWIDeactivate: + break; + case ROSE_QSIG_MWIInterrogate: + break; +#endif /* Not handled yet */ + default: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "!! ROSE invoke operation not handled! %s\n", + rose_operation2str(invoke->operation)); + } + break; + } } - Index: pri_facility.h =================================================================== --- a/pri_facility.h (.../tags/1.4.10.2) (revision 1357) +++ b/pri_facility.h (.../branches/1.4) (revision 1357) @@ -31,123 +31,21 @@ #define _PRI_FACILITY_H #include "pri_q931.h" +/* Forward declare some structs */ +struct fac_extension_header; +struct rose_msg_invoke; +struct rose_msg_result; +struct rose_msg_error; +struct rose_msg_reject; + /* Protocol Profile field */ +#define Q932_PROTOCOL_MASK 0x1F #define Q932_PROTOCOL_ROSE 0x11 /* X.219 & X.229 */ #define Q932_PROTOCOL_CMIP 0x12 /* Q.941 */ #define Q932_PROTOCOL_ACSE 0x13 /* X.217 & X.227 */ #define Q932_PROTOCOL_GAT 0x16 #define Q932_PROTOCOL_EXTENSIONS 0x1F -/* Argument values */ -#define ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE 0x80 -#define ROSE_NAME_PRESENTATION_RESTRICTED_NULL 0x87 -#define ROSE_NAME_PRESENTATION_ALLOWED_EXTENDED 0xA1 -#define ROSE_NAME_PRESENTATION_RESTRICTED_SIMPLE 0xA2 -#define ROSE_NAME_PRESENTATION_RESTRICTED_EXTENDED 0xA3 -#define ROSE_NAME_NOT_AVAIL 0x84 - -/* Component types */ -#define COMP_TYPE_INTERPRETATION 0x8B -#define COMP_TYPE_NETWORK_PROTOCOL_PROFILE 0x92 -#define COMP_TYPE_INVOKE 0xA1 -#define COMP_TYPE_RETURN_RESULT 0xA2 -#define COMP_TYPE_RETURN_ERROR 0xA3 -#define COMP_TYPE_REJECT 0xA4 -#define COMP_TYPE_NFE 0xAA - -/* Operation ID values */ -/* Q.952.7 (ECMA-178) ROSE operations (Transfer) */ -#define ROSE_CALL_TRANSFER_IDENTIFY 7 -#define ROSE_CALL_TRANSFER_ABANDON 8 -#define ROSE_CALL_TRANSFER_INITIATE 9 -#define ROSE_CALL_TRANSFER_SETUP 10 -#define ROSE_CALL_TRANSFER_ACTIVE 11 -#define ROSE_CALL_TRANSFER_COMPLETE 12 -#define ROSE_CALL_TRANSFER_UPDATE 13 -#define ROSE_SUBADDRESS_TRANSFER 14 -/* Q.952 ROSE operations (Diverting) */ -#define ROSE_DIVERTING_LEG_INFORMATION1 18 -#define ROSE_DIVERTING_LEG_INFORMATION2 0x15 -#define ROSE_DIVERTING_LEG_INFORMATION3 19 -/* Q.956 ROSE operations (Advice Of Charge) */ -#define ROSE_AOC_NO_CHARGING_INFO_AVAILABLE 26 -#define ROSE_AOC_CHARGING_REQUEST 30 -#define ROSE_AOC_AOCS_CURRENCY 31 -#define ROSE_AOC_AOCS_SPECIAL_ARR 32 -#define ROSE_AOC_AOCD_CURRENCY 33 -#define ROSE_AOC_AOCD_CHARGING_UNIT 34 -#define ROSE_AOC_AOCE_CURRENCY 35 -#define ROSE_AOC_AOCE_CHARGING_UNIT 36 -#define ROSE_AOC_IDENTIFICATION_OF_CHARGE 37 -/* Q.SIG operations */ -#define SS_CNID_CALLINGNAME 0 -#define SS_ANFPR_PATHREPLACEMENT 4 -#define SS_DIVERTING_LEG_INFORMATION2 21 -#define SS_MWI_ACTIVATE 80 -#define SS_MWI_DEACTIVATE 81 -#define SS_MWI_INTERROGATE 82 - -/* ROSE definitions and data structures */ -#define INVOKE_IDENTIFIER 0x02 -#define INVOKE_LINKED_IDENTIFIER 0x80 -#define INVOKE_NULL_IDENTIFIER __USE_ASN1_NULL - -/* ASN.1 Identifier Octet - Data types */ -#define ASN1_TYPE_MASK 0x1f -#define ASN1_BOOLEAN 0x01 -#define ASN1_INTEGER 0x02 -#define ASN1_BITSTRING 0x03 -#define ASN1_OCTETSTRING 0x04 -#define ASN1_NULL 0x05 -#define ASN1_OBJECTIDENTIFIER 0x06 -#define ASN1_OBJECTDESCRIPTOR 0x07 -#define ASN1_EXTERN 0x08 -#define ASN1_REAL 0x09 -#define ASN1_ENUMERATED 0x0a -#define ASN1_EMBEDDEDPDV 0x0b -#define ASN1_UTF8STRING 0x0c -#define ASN1_RELATIVEOBJECTID 0x0d -/* 0x0e & 0x0f are reserved for future ASN.1 editions */ -#define ASN1_SEQUENCE 0x10 -#define ASN1_SET 0x11 -#define ASN1_NUMERICSTRING 0x12 -#define ASN1_PRINTABLESTRING 0x13 -#define ASN1_TELETEXSTRING 0x14 -#define ASN1_IA5STRING 0x16 -#define ASN1_UTCTIME 0x17 -#define ASN1_GENERALIZEDTIME 0x18 - -/* ASN.1 Identifier Octet - Tags */ -#define ASN1_TAG_0 0x00 -#define ASN1_TAG_1 0x01 -#define ASN1_TAG_2 0x02 -#define ASN1_TAG_3 0x03 -#define ASN1_TAG_4 0x04 -#define ASN1_TAG_5 0x05 -#define ASN1_TAG_6 0x06 -#define ASN1_TAG_7 0x07 -#define ASN1_TAG_8 0x08 -#define ASN1_TAG_9 0x09 - -/* ASN.1 Identifier Octet - Primitive/Constructor Bit */ -#define ASN1_PC_MASK 0x20 -#define ASN1_PRIMITIVE 0x00 -#define ASN1_CONSTRUCTOR 0x20 - -/* ASN.1 Identifier Octet - Clan Bits */ -#define ASN1_CLAN_MASK 0xc0 -#define ASN1_UNIVERSAL 0x00 -#define ASN1_APPLICATION 0x40 -#define ASN1_CONTEXT_SPECIFIC 0x80 -#define ASN1_PRIVATE 0xc0 - -/* ASN.1 Length masks */ -#define ASN1_LEN_INDEF 0x80 - - -#define INVOKE_OPERATION_INT __USE_ASN1_INTEGER -#define INVOKE_OBJECT_ID __USE_ASN1_OBJECTIDENTIFIER - /* Q.952 Divert cause */ #define Q952_DIVERT_REASON_UNKNOWN 0x00 #define Q952_DIVERT_REASON_CFU 0x01 @@ -169,138 +67,112 @@ #define Q932_TON_SUBSCRIBER 0x04 #define Q932_TON_ABBREVIATED 0x06 -/* RLT related Operations */ -#define RLT_SERVICE_ID 0x3e -#define RLT_OPERATION_IND 0x01 -#define RLT_THIRD_PARTY 0x02 +/* Q.SIG Subscription Option. Listed in ECMA-174 */ +#define QSIG_NO_NOTIFICATION 0x00 +#define QSIG_NOTIFICATION_WITHOUT_DIVERTED_TO_NR 0x01 +#define QSIG_NOTIFICATION_WITH_DIVERTED_TO_NR 0x02 -struct rose_component { - u_int8_t type; - u_int8_t len; - u_int8_t data[0]; +/*! Reasons an APDU callback is called. */ +enum APDU_CALLBACK_REASON { + /*! + * \brief Send setup error. Abort and cleanup. + * \note The message may or may not actually get sent. + * \note The callback cannot generate an event subcmd. + * \note The callback should not send messages. Out of order messages will result. + */ + APDU_CALLBACK_REASON_ERROR, + /*! + * \brief Abort and cleanup. + * \note The APDU queue is being destroyed. + * \note The callback cannot generate an event subcmd. + * \note The callback cannot send messages as the call is likely being destroyed. + */ + APDU_CALLBACK_REASON_CLEANUP, + /*! + * \brief Timeout waiting for responses to the message. + * \note The callback can generate an event subcmd. + * \note The callback can send messages. + */ + APDU_CALLBACK_REASON_TIMEOUT, + /*! + * \brief Received a facility response message. + * \note The callback can generate an event subcmd. + * \note The callback can send messages. + */ + APDU_CALLBACK_REASON_MSG_RESULT, + /*! + * \brief Received a facility error message. + * \note The callback can generate an event subcmd. + * \note The callback can send messages. + */ + APDU_CALLBACK_REASON_MSG_ERROR, + /*! + * \brief Received a facility reject message. + * \note The callback can generate an event subcmd. + * \note The callback can send messages. + */ + APDU_CALLBACK_REASON_MSG_REJECT, }; -#if 1 - #define GET_COMPONENT(component, idx, ptr, length) \ - if ((idx)+2 > (length)) \ - break; \ - (component) = (struct rose_component*)&((ptr)[idx]); \ - if ((idx)+(component)->len+2 > (length)) { \ - if ((component)->len != ASN1_LEN_INDEF) \ - pri_message(pri, "Length (%d) of 0x%X component is too long\n", (component)->len, (component)->type); \ - } -#else /* Debugging */ - #define GET_COMPONENT(component, idx, ptr, length) \ - if ((idx)+2 > (length)) \ - break; \ - (component) = (struct rose_component*)&((ptr)[idx]); \ - if ((idx)+(component)->len+2 > (length)) { \ - if ((component)->len != 128) \ - pri_message(pri, "Length (%d) of 0x%X component is too long\n", (component)->len, (component)->type); \ - } \ - pri_message(pri, "XX %s:%d Got component %d (0x%02X), length %d\n", __FUNCTION__, __LINE__, (component)->type, (component)->type, (component)->len); \ - if ((component)->len > 0) { \ - int zzz; \ - pri_message(pri, "XX Data:"); \ - for (zzz = 0; zzz < (component)->len; ++zzz) \ - pri_message(pri, " %02X", (component)->data[zzz]); \ - pri_message(pri, "\n"); \ - } -#endif +union apdu_msg_data { + const struct rose_msg_result *result; + const struct rose_msg_error *error; + const struct rose_msg_reject *reject; +}; -#define NEXT_COMPONENT(component, idx) \ - (idx) += (component)->len + 2 +union apdu_callback_param { + void *ptr; + long value; + char pad[8]; +}; -#define SUB_COMPONENT(component, idx) \ - (idx) += 2 +struct apdu_callback_data { + /*! APDU invoke id to match with any response messages. (Result/Error/Reject) */ + int invoke_id; + /*! + * \brief Time to wait for responses to APDU in ms. + * \note Set to 0 if send the message only. + * \note Set to less than 0 for PRI_TIMER_T_RESPONSE time. + */ + int timeout_time; + /*! + * \brief APDU callback function. + * + * \param reason Reason callback is called. + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param apdu APDU queued entry. Do not change! + * \param msg APDU response message data. (NULL if was not the reason called.) + * + * \note + * A callback must be supplied if the sender cares about any APDU_CALLBACK_REASON. + * + * \return TRUE if no more responses are expected. + */ + int (*callback)(enum APDU_CALLBACK_REASON reason, struct pri *ctrl, struct q931_call *call, struct apdu_event *apdu, const union apdu_msg_data *msg); + /*! \brief Sender data for the callback function to identify the particular APDU. */ + union apdu_callback_param user; +}; -#define CHECK_COMPONENT(component, comptype, message) \ - if ((component)->type && ((component)->type & ASN1_TYPE_MASK) != (comptype)) { \ - pri_message(pri, (message), (component)->type); \ - asn1_dump(pri, (component), (component)->len+2); \ - break; \ - } - -#define ASN1_GET_INTEGER(component, variable) \ - do { \ - int comp_idx; \ - (variable) = 0; \ - for (comp_idx = 0; comp_idx < (component)->len; ++comp_idx) \ - (variable) = ((variable) << 8) | (component)->data[comp_idx]; \ - } while (0) +struct apdu_event { + /*! Linked list pointer */ + struct apdu_event *next; + /*! TRUE if this APDU has been sent. */ + int sent; + /*! What message to send the ADPU in */ + int message; + /*! Sender supplied information to handle APDU response messages. */ + struct apdu_callback_data response; + /*! Q.931 call leg. (Needed for the APDU timeout.) */ + struct q931_call *call; + /*! Response timeout timer. */ + int timer; + /*! Length of ADPU */ + int apdu_len; + /*! ADPU to send */ + unsigned char apdu[255]; +}; -#define ASN1_FIXUP_LEN(component, size) \ - do { \ - if ((component)->len == ASN1_LEN_INDEF) \ - size += 2; \ - } while (0) - -#define ASN1_ADD_SIMPLE(component, comptype, ptr, idx) \ - do { \ - (component) = (struct rose_component *)&((ptr)[(idx)]); \ - (component)->type = (comptype); \ - (component)->len = 0; \ - (idx) += 2; \ - } while (0) - -#define ASN1_ADD_BYTECOMP(component, comptype, ptr, idx, value) \ - do { \ - (component) = (struct rose_component *)&((ptr)[(idx)]); \ - (component)->type = (comptype); \ - (component)->len = 1; \ - (component)->data[0] = (value); \ - (idx) += 3; \ - } while (0) - -#define ASN1_ADD_WORDCOMP(component, comptype, ptr, idx, value) \ - do { \ - int __val = (value); \ - int __i = 0; \ - (component) = (struct rose_component *)&((ptr)[(idx)]); \ - (component)->type = (comptype); \ - if ((__val >> 24)) \ - (component)->data[__i++] = (__val >> 24) & 0xff; \ - if ((__val >> 16)) \ - (component)->data[__i++] = (__val >> 16) & 0xff; \ - if ((__val >> 8)) \ - (component)->data[__i++] = (__val >> 8) & 0xff; \ - (component)->data[__i++] = __val & 0xff; \ - (component)->len = __i; \ - (idx) += 2 + __i; \ - } while (0) - -#define ASN1_PUSH(stack, stackpointer, component) \ - (stack)[(stackpointer)++] = (component) - -#define ASN1_FIXUP(stack, stackpointer, data, idx) \ - do { \ - --(stackpointer); \ - (stack)[(stackpointer)]->len = (unsigned char *)&((data)[(idx)]) - (unsigned char *)(stack)[(stackpointer)] - 2; \ - } while (0) - -/* Decoder for the invoke ROSE component */ -int rose_invoke_decode(struct pri *pri, struct q931_call *call, q931_ie *ie, unsigned char *data, int len); - -/* Decoder for the return result ROSE component */ -int rose_return_result_decode(struct pri *pri, struct q931_call *call, q931_ie *ie, unsigned char *data, int len); - -/* Decoder for the return error ROSE component */ -int rose_return_error_decode(struct pri *pri, struct q931_call *call, q931_ie *ie, unsigned char *data, int len); - -/* Decoder for the reject ROSE component */ -int rose_reject_decode(struct pri *pri, struct q931_call *call, q931_ie *ie, unsigned char *data, int len); - -int asn1_copy_string(char * buf, int buflen, struct rose_component *comp); - -int asn1_string_encode(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_len); - -/* Get Name types from ASN.1 */ -int asn1_name_decode(void * data, int len, char *namebuf, int buflen); - -int typeofnumber_from_q931(struct pri *pri, int ton); - -int redirectingreason_from_q931(struct pri *pri, int redirectingreason); - /* Queues an MWI apdu on a the given call */ int mwi_message_send(struct pri *pri, q931_call *call, struct pri_sr *req, int activate); @@ -310,21 +182,31 @@ int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const char* original, const char* reason); +int send_reroute_request(struct pri *ctrl, q931_call *call, const struct q931_party_id *caller, const struct q931_party_redirecting *deflection, int subscription_option); /* starts a QSIG Path Replacement */ int anfpr_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2); -/* Use this function to queue a facility-IE born APDU onto a call - * call is the call to use, messagetype is any one of the Q931 messages, - * apdu is the apdu data, apdu_len is the length of the apdu data */ -int pri_call_apdu_queue(q931_call *call, int messagetype, void *apdu, int apdu_len, void (*function)(void *data), void *data); +int send_call_transfer_complete(struct pri *pri, q931_call *call, int call_status); -/* Used by q931.c to cleanup the apdu queue upon destruction of a call */ -int pri_call_apdu_queue_cleanup(q931_call *call); +int rose_diverting_leg_information1_encode(struct pri *pri, q931_call *call); +int rose_diverting_leg_information3_encode(struct pri *pri, q931_call *call, int messagetype); +int rose_connected_name_encode(struct pri *pri, q931_call *call, int messagetype); +int rose_called_name_encode(struct pri *pri, q931_call *call, int messagetype); + +int pri_call_apdu_queue(q931_call *call, int messagetype, const unsigned char *apdu, int apdu_len, struct apdu_callback_data *response); +void pri_call_apdu_queue_cleanup(q931_call *call); +void pri_call_apdu_delete(struct q931_call *call, struct apdu_event *doomed); + /* Adds the "standard" APDUs to a call */ int pri_call_add_standard_apdus(struct pri *pri, q931_call *call); -int asn1_dump(struct pri *pri, void *comp, int len); +void asn1_dump(struct pri *ctrl, const unsigned char *start_asn1, const unsigned char *end); +void rose_handle_invoke(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_invoke *invoke); +void rose_handle_result(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_result *result); +void rose_handle_error(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_error *error); +void rose_handle_reject(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_reject *reject); + #endif /* _PRI_FACILITY_H */ Index: libpri.h =================================================================== --- a/libpri.h (.../tags/1.4.10.2) (revision 1357) +++ b/libpri.h (.../branches/1.4) (revision 1357) @@ -26,7 +26,14 @@ * provided with that copy of Asterisk, instead of the license * terms granted here. */ - + +/* + * NOTE: + * All new global identifiers that are added to this file MUST be + * prefixed with PRI_ or pri_ to indicate that they are part of this + * library and to reduce potential naming conflicts. + */ + #ifndef _LIBPRI_H #define _LIBPRI_H @@ -73,7 +80,8 @@ #define PRI_EVENT_ANSWER 8 /* Call has been answered (CONNECT) */ #define PRI_EVENT_HANGUP_ACK 9 /* Call hangup has been acknowledged */ #define PRI_EVENT_RESTART_ACK 10 /* Restart complete on a given channel (RESTART_ACKNOWLEDGE) */ -#define PRI_EVENT_FACNAME 11 /* Caller*ID Name received on Facility */ +#define PRI_EVENT_FACNAME 11 /* Caller*ID Name received on Facility (DEPRECATED) */ +#define PRI_EVENT_FACILITY 11 /* Facility received (FACILITY) */ #define PRI_EVENT_INFO_RECEIVED 12 /* Additional info (digits) received (INFORMATION) */ #define PRI_EVENT_PROCEEDING 13 /* When we get CALL_PROCEEDING */ #define PRI_EVENT_SETUP_ACK 14 /* When we get SETUP_ACKNOWLEDGE */ @@ -81,6 +89,14 @@ #define PRI_EVENT_NOTIFY 16 /* Notification received (NOTIFY) */ #define PRI_EVENT_PROGRESS 17 /* When we get PROGRESS */ #define PRI_EVENT_KEYPAD_DIGIT 18 /* When we receive during ACTIVE state (INFORMATION) */ +#define PRI_EVENT_SERVICE 19 /* SERVICE maintenance message */ +#define PRI_EVENT_SERVICE_ACK 20 /* SERVICE maintenance acknowledgement message */ +#define PRI_EVENT_HOLD 21 /* HOLD request received */ +#define PRI_EVENT_HOLD_ACK 22 /* HOLD_ACKNOWLEDGE received */ +#define PRI_EVENT_HOLD_REJ 23 /* HOLD_REJECT received */ +#define PRI_EVENT_RETRIEVE 24 /* RETRIEVE request received */ +#define PRI_EVENT_RETRIEVE_ACK 25 /* RETRIEVE_ACKNOWLEDGE received */ +#define PRI_EVENT_RETRIEVE_REJ 26 /* RETRIEVE_REJECT received */ /* Simple states */ #define PRI_STATE_DOWN 0 @@ -101,13 +117,13 @@ #define PRI_PROG_CALLER_RETURNED_TO_ISDN (1 << 9) /* Numbering plan identifier */ -#define PRI_NPI_UNKNOWN 0x0 -#define PRI_NPI_E163_E164 0x1 -#define PRI_NPI_X121 0x3 -#define PRI_NPI_F69 0x4 -#define PRI_NPI_NATIONAL 0x8 -#define PRI_NPI_PRIVATE 0x9 -#define PRI_NPI_RESERVED 0xF +#define PRI_NPI_UNKNOWN 0x0 /*!< Unknown numbering plan */ +#define PRI_NPI_E163_E164 0x1 /*!< ISDN/telephony numbering plan (public) */ +#define PRI_NPI_X121 0x3 /*!< Data numbering plan */ +#define PRI_NPI_F69 0x4 /*!< Telex numbering plan */ +#define PRI_NPI_NATIONAL 0x8 /*!< National standard numbering plan */ +#define PRI_NPI_PRIVATE 0x9 /*!< Private numbering plan */ +#define PRI_NPI_RESERVED 0xF /*!< Reserved for extension */ /* Type of number */ #define PRI_TON_UNKNOWN 0x0 @@ -135,16 +151,49 @@ #define PRI_UNKNOWN 0x0 /* Presentation */ -#define PRES_ALLOWED_USER_NUMBER_NOT_SCREENED 0x00 -#define PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN 0x01 -#define PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN 0x02 -#define PRES_ALLOWED_NETWORK_NUMBER 0x03 -#define PRES_PROHIB_USER_NUMBER_NOT_SCREENED 0x20 -#define PRES_PROHIB_USER_NUMBER_PASSED_SCREEN 0x21 -#define PRES_PROHIB_USER_NUMBER_FAILED_SCREEN 0x22 -#define PRES_PROHIB_NETWORK_NUMBER 0x23 -#define PRES_NUMBER_NOT_AVAILABLE 0x43 +#define PRI_PRES_NUMBER_TYPE 0x03 +#define PRI_PRES_USER_NUMBER_UNSCREENED 0x00 +#define PRI_PRES_USER_NUMBER_PASSED_SCREEN 0x01 +#define PRI_PRES_USER_NUMBER_FAILED_SCREEN 0x02 +#define PRI_PRES_NETWORK_NUMBER 0x03 +#define PRI_PRES_RESTRICTION 0x60 +#define PRI_PRES_ALLOWED 0x00 +#define PRI_PRES_RESTRICTED 0x20 +#define PRI_PRES_UNAVAILABLE 0x40 +#define PRI_PRES_RESERVED 0x60 + +#define PRES_ALLOWED_USER_NUMBER_NOT_SCREENED \ + (PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED) + +#define PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN \ + (PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_PASSED_SCREEN) + +#define PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN \ + (PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_FAILED_SCREEN) + +#define PRES_ALLOWED_NETWORK_NUMBER \ + (PRI_PRES_ALLOWED | PRI_PRES_NETWORK_NUMBER) + +#define PRES_PROHIB_USER_NUMBER_NOT_SCREENED \ + (PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED) + +#define PRES_PROHIB_USER_NUMBER_PASSED_SCREEN \ + (PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_PASSED_SCREEN) + +#define PRES_PROHIB_USER_NUMBER_FAILED_SCREEN \ + (PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_FAILED_SCREEN) + +#define PRES_PROHIB_NETWORK_NUMBER \ + (PRI_PRES_RESTRICTED | PRI_PRES_NETWORK_NUMBER) + +#define PRES_NUMBER_NOT_AVAILABLE \ + (PRI_PRES_UNAVAILABLE | PRI_PRES_NETWORK_NUMBER) + +/* Reverse Charging Indication */ +#define PRI_REVERSECHARGE_NONE -1 +#define PRI_REVERSECHARGE_REQUESTED 1 + /* Causes for disconnection */ #define PRI_CAUSE_UNALLOCATED 1 #define PRI_CAUSE_NO_ROUTE_TRANSIT_NET 2 /* !Q.SIG */ @@ -157,6 +206,7 @@ #define PRI_CAUSE_NO_ANSWER 19 #define PRI_CAUSE_CALL_REJECTED 21 #define PRI_CAUSE_NUMBER_CHANGED 22 +#define PRI_CAUSE_NONSELECTED_USER_CLEARING 26 #define PRI_CAUSE_DESTINATION_OUT_OF_ORDER 27 #define PRI_CAUSE_INVALID_NUMBER_FORMAT 28 #define PRI_CAUSE_FACILITY_REJECTED 29 /* !Q.SIG */ @@ -169,6 +219,7 @@ #define PRI_CAUSE_ACCESS_INFO_DISCARDED 43 /* !Q.SIG */ #define PRI_CAUSE_REQUESTED_CHAN_UNAVAIL 44 #define PRI_CAUSE_PRE_EMPTED 45 /* !Q.SIG */ +#define PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED 47 #define PRI_CAUSE_FACILITY_NOT_SUBSCRIBED 50 /* !Q.SIG */ #define PRI_CAUSE_OUTGOING_CALL_BARRED 52 /* !Q.SIG */ #define PRI_CAUSE_INCOMING_CALL_BARRED 54 /* !Q.SIG */ @@ -259,8 +310,8 @@ #define PRI_RATE_ADAPT_ASYNC 0x40 /* Notifications */ -#define PRI_NOTIFY_USER_SUSPENDED 0x00 /* User suspended */ -#define PRI_NOTIFY_USER_RESUMED 0x01 /* User resumed */ +#define PRI_NOTIFY_USER_SUSPENDED 0x00 /* User suspended (Q.931) (Call is placed on hold) */ +#define PRI_NOTIFY_USER_RESUMED 0x01 /* User resumed (Q.931) (Call is taken off hold) */ #define PRI_NOTIFY_BEARER_CHANGE 0x02 /* Bearer service change (DSS1) */ #define PRI_NOTIFY_ASN1_COMPONENT 0x03 /* ASN.1 encoded component (DSS1) */ #define PRI_NOTIFY_COMPLETION_DELAY 0x04 /* Call completion delay */ @@ -275,12 +326,12 @@ #define PRI_NOTIFY_CONF_OTHER_DISCONNECTED 0x4a /* Other party disconnected */ #define PRI_NOTIFY_CONF_FLOATING 0x4b /* Conference floating */ #define PRI_NOTIFY_WAITING_CALL 0x60 /* Call is waiting call */ -#define PRI_NOTIFY_DIVERSION_ACTIVATED 0x68 /* Diversion activated (DSS1) */ -#define PRI_NOTIFY_TRANSFER_ALERTING 0x69 /* Call transfer, alerting */ -#define PRI_NOTIFY_TRANSFER_ACTIVE 0x6a /* Call transfer, active */ +#define PRI_NOTIFY_DIVERSION_ACTIVATED 0x68 /* Diversion activated (DSS1) (cfu, cfb, cfnr) (EN 300 207-1 Section 7.2.1) */ +#define PRI_NOTIFY_TRANSFER_ALERTING 0x69 /* Call transfer, alerting (EN 300 369-1 Section 7.2) */ +#define PRI_NOTIFY_TRANSFER_ACTIVE 0x6a /* Call transfer, active(answered) (EN 300 369-1 Section 7.2) */ #define PRI_NOTIFY_REMOTE_HOLD 0x79 /* Remote hold */ #define PRI_NOTIFY_REMOTE_RETRIEVAL 0x7a /* Remote retrieval */ -#define PRI_NOTIFY_CALL_DIVERTING 0x7b /* Call is diverting */ +#define PRI_NOTIFY_CALL_DIVERTING 0x7b /* Call is diverting (EN 300 207-1 Section 7.2.1) */ #define PRI_COPY_DIGITS_CALLED_NUMBER @@ -305,6 +356,200 @@ typedef struct q931_call q931_call; +/* Name character set enumeration values */ +#define PRI_CHAR_SET_UNKNOWN 0 +#define PRI_CHAR_SET_ISO8859_1 1 +#define PRI_CHAR_SET_WITHDRAWN 2 +#define PRI_CHAR_SET_ISO8859_2 3 +#define PRI_CHAR_SET_ISO8859_3 4 +#define PRI_CHAR_SET_ISO8859_4 5 +#define PRI_CHAR_SET_ISO8859_5 6 +#define PRI_CHAR_SET_ISO8859_7 7 +#define PRI_CHAR_SET_ISO10646_BMPSTRING 8 +#define PRI_CHAR_SET_ISO10646_UTF_8STRING 9 + +/*! \brief Q.SIG name information. */ +struct pri_party_name { + /*! \brief TRUE if the name information is valid/present */ + int valid; + /*! + * \brief Q.931 presentation-indicator encoded field + * \note Must tollerate the Q.931 screening-indicator field values being present. + */ + int presentation; + /*! + * \brief Character set the name is using. + * \details + * unknown(0), + * iso8859-1(1), + * enum-value-withdrawn-by-ITU-T(2) + * iso8859-2(3), + * iso8859-3(4), + * iso8859-4(5), + * iso8859-5(6), + * iso8859-7(7), + * iso10646-BmpString(8), + * iso10646-utf-8String(9) + * \details + * Set to iso8859-1(1) if unsure what to use. + */ + int char_set; + /*! \brief Name data with null terminator. */ + char str[64]; +}; + +struct pri_party_number { + /*! \brief TRUE if the number information is valid/present */ + int valid; + /*! \brief Q.931 presentation-indicator and screening-indicator encoded fields */ + int presentation; + /*! \brief Q.931 Type-Of-Number and numbering-plan encoded fields */ + int plan; + /*! \brief Number data with null terminator. */ + char str[64]; +}; + +/*! + * \note This structure is a place holder for possible future subaddress support + * to maintain ABI compatibility. + */ +struct pri_party_subaddress { + /*! \brief TRUE if the subaddress information is valid/present */ + int valid; + /*! + * \brief Subaddress type. + * \details + * nsap(0), + * user_specified(2) + */ + int type; + /*! + * \brief TRUE if odd number of address signals + * \note The odd/even indicator is used when the type of subaddress is + * user_specified and the coding is BCD. + */ + int odd_even_indicator; + /*! \brief Length of the subaddress data */ + int length; + /*! + * \brief Subaddress data with null terminator. + * \note The null terminator is a convenience only since the data could be + * BCD/binary and thus have a null byte as part of the contents. + */ + unsigned char data[32]; +}; + +/*! \brief Information needed to identify an endpoint in a call. */ +struct pri_party_id { + /*! \brief Subscriber name */ + struct pri_party_name name; + /*! \brief Subscriber phone number */ + struct pri_party_number number; + /*! \brief Subscriber subaddress */ + struct pri_party_subaddress subaddress; +}; + +/*! \brief Connected Line/Party information */ +struct pri_party_connected_line { + /*! Connected party ID */ + struct pri_party_id id; +}; + +/*! + * \brief Redirecting Line information. + * \details + * RDNIS (Redirecting Directory Number Information Service) + * Where a call diversion or transfer was invoked. + */ +struct pri_party_redirecting { + /*! Who is redirecting the call (Sent to the party the call is redirected toward) */ + struct pri_party_id from; + /*! Call is redirecting to a new party (Sent to the caller) */ + struct pri_party_id to; + /*! Originally called party (in cases of multiple redirects) */ + struct pri_party_id orig_called; + /*! Number of times the call was redirected */ + int count; + /*! Original reason for redirect (in cases of multiple redirects) */ + int orig_reason; + /*! Redirection reason */ + int reason; +}; + +/*! + * \brief Information for rerouting/deflecting the call. + */ +struct pri_rerouting_data { + /*! + * \brief Updated caller-id information. + * \note The information may have been altered by procedure in the private network. + */ + struct pri_party_id caller; + /*! + * \note + * deflection.to is the new called number and must always be present. + */ + struct pri_party_redirecting deflection; + /*! + * \brief Diverting user subscription option to specify if caller is notified. + * \details + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2), + * notApplicable(3) (Status only.) + */ + int subscription_option; + /*! Invocation ID to use when sending a reply to the call rerouting/deflection request. */ + int invoke_id; +}; + +/* Subcommands derived from supplementary services. */ +#define PRI_SUBCMD_REDIRECTING 1 +#define PRI_SUBCMD_CONNECTED_LINE 2 +#define PRI_SUBCMD_REROUTING 3 + + +struct pri_subcommand { + /*! PRI_SUBCMD_xxx defined values */ + int cmd; + union { + /*! Reserve room for possible expansion to maintain ABI compatibility. */ + char reserve_space[512]; + struct pri_party_connected_line connected_line; + struct pri_party_redirecting redirecting; + struct pri_rerouting_data rerouting; + } u; +}; + +/* Max number of subcommands per event message */ +#define PRI_MAX_SUBCOMMANDS 8 + +struct pri_subcommands { + int counter_subcmd; + struct pri_subcommand subcmd[PRI_MAX_SUBCOMMANDS]; +}; + + +/* + * Event channel parameter encoding: + * 3322 2222 2222 1111 1111 1100 0000 0000 + * 1098 7654 3210 9876 5432 1098 7654 3210 + * xxxx xxxx xxxx xEDC BBBBBBBBB AAAAAAAAA + * + * Bit field + * A - B channel + * B - Span (DS1) (0 - 127) + * C - DS1 Explicit bit + * D - D channel (cis_call) bit (status only) + * E - Call is held bit (status only) + * + * B channel values: + * 0 - No channel (ISDN uses for call waiting feature) + * 1-127 - B channel # + * 0xFF - Any channel (Also if whole channel value is -1 in event) + */ + + typedef struct pri_event_generic { /* Events with no additional information fall in this category */ int e; @@ -328,6 +573,7 @@ int progressmask; q931_call *call; char useruserinfo[260]; /* User->User info */ + struct pri_subcommands *subcmds; } pri_event_ringing; typedef struct pri_event_answer { @@ -338,8 +584,10 @@ int progressmask; q931_call *call; char useruserinfo[260]; /* User->User info */ + struct pri_subcommands *subcmds; } pri_event_answer; +/*! Deprecated replaced by struct pri_event_facility. */ typedef struct pri_event_facname { int e; char callingname[256]; @@ -351,6 +599,24 @@ int callingplan; /* Dialing plan of Calling entity */ } pri_event_facname; +struct pri_event_facility { + int e; + char callingname[256]; /*!< Deprecated, preserved for struct pri_event_facname compatibility */ + char callingnum[256]; /*!< Deprecated, preserved for struct pri_event_facname compatibility */ + int channel; + int cref; + /*! + * \brief Master call or normal call. + * \note Call pointer known about by upper layer. + * \note NULL if dummy call reference. + */ + q931_call *call; + int callingpres; /*!< Presentation of Calling CallerID (Deprecated, preserved for struct pri_event_facname compatibility) */ + int callingplan; /*!< Dialing plan of Calling entity (Deprecated, preserved for struct pri_event_facname compatibility) */ + struct pri_subcommands *subcmds; + q931_call *subcall; /*!< Subcall to send any reply toward. */ +}; + #define PRI_CALLINGPLANANI #define PRI_CALLINGPLANRDNIS typedef struct pri_event_ring { @@ -376,13 +642,18 @@ int layer1; /* User layer 1 */ int complete; /* Have we seen "Complete" i.e. no more number? */ q931_call *call; /* Opaque call pointer */ - char callingsubaddr[256]; /* Calling parties subaddress */ + char callingsubaddr[256]; /* Calling parties subaddress, backwards compatibility */ int progress; int progressmask; char origcalledname[256]; char origcallednum[256]; int callingplanorigcalled; /* Dialing plan of Originally Called Number */ int origredirectingreason; + int reversecharge; + struct pri_subcommands *subcmds; + struct pri_party_id calling; /* Calling Party's info, initially subaddress' */ + struct pri_party_subaddress called_subaddress; /* Called party's subaddress */ + char keypad_digits[64]; /* Keypad digits in the SETUP message. */ } pri_event_ring; typedef struct pri_event_hangup { @@ -390,10 +661,23 @@ int channel; /* Channel requested */ int cause; int cref; - q931_call *call; /* Opaque call pointer */ + q931_call *call; /* Opaque call pointer of call hanging up. */ long aoc_units; /* Advise of Charge number of charged units */ char useruserinfo[260]; /* User->User info */ -} pri_event_hangup; + struct pri_subcommands *subcmds; + /*! + * \brief Opaque held call pointer for possible transfer to active call. + * \note The call_held and call_active pointers must not be NULL if + * transfer held call on disconnect is available. + */ + q931_call *call_held; + /*! + * \brief Opaque active call pointer for possible transfer with held call. + * \note The call_held and call_active pointers must not be NULL if + * transfer held call on disconnect is available. + */ + q931_call *call_active; +} pri_event_hangup; typedef struct pri_event_restart_ack { int e; @@ -409,18 +693,22 @@ int progressmask; int cause; q931_call *call; + struct pri_subcommands *subcmds; } pri_event_proceeding; - + typedef struct pri_event_setup_ack { int e; int channel; q931_call *call; + struct pri_subcommands *subcmds; } pri_event_setup_ack; typedef struct pri_event_notify { int e; int channel; int info; + struct pri_subcommands *subcmds; + q931_call *call; } pri_event_notify; typedef struct pri_event_keypad_digit { @@ -428,14 +716,72 @@ int channel; q931_call *call; char digits[64]; + struct pri_subcommands *subcmds; } pri_event_keypad_digit; +typedef struct pri_event_service { + int e; + int channel; + int changestatus; +} pri_event_service; + +typedef struct pri_event_service_ack { + int e; + int channel; + int changestatus; +} pri_event_service_ack; + +struct pri_event_hold { + int e; + int channel; + q931_call *call; + struct pri_subcommands *subcmds; +}; + +struct pri_event_hold_ack { + int e; + int channel; + q931_call *call; + struct pri_subcommands *subcmds; +}; + +struct pri_event_hold_rej { + int e; + int channel; + q931_call *call; + int cause; + struct pri_subcommands *subcmds; +}; + +struct pri_event_retrieve { + int e; + int channel; + q931_call *call; + int flexible; /* Are we flexible with our channel selection? */ + struct pri_subcommands *subcmds; +}; + +struct pri_event_retrieve_ack { + int e; + int channel; + q931_call *call; + struct pri_subcommands *subcmds; +}; + +struct pri_event_retrieve_rej { + int e; + int channel; + q931_call *call; + int cause; + struct pri_subcommands *subcmds; +}; + typedef union { int e; pri_event_generic gen; /* Generic view */ pri_event_restart restart; /* Restart view */ pri_event_error err; /* Error view */ - pri_event_facname facname; /* Caller*ID Name on Facility */ + pri_event_facname facname; /* Caller*ID Name on Facility (Deprecated, use pri_event.facility) */ pri_event_ring ring; /* Ring */ pri_event_hangup hangup; /* Hang up */ pri_event_ringing ringing; /* Ringing */ @@ -445,6 +791,15 @@ pri_event_setup_ack setup_ack; /* SETUP_ACKNOWLEDGE structure */ pri_event_notify notify; /* Notification */ pri_event_keypad_digit digit; /* Digits that come during a call */ + pri_event_service service; /* service message */ + pri_event_service_ack service_ack; /* service acknowledgement message */ + struct pri_event_facility facility; + struct pri_event_hold hold; + struct pri_event_hold_ack hold_ack; + struct pri_event_hold_rej hold_rej; + struct pri_event_retrieve retrieve; + struct pri_event_retrieve_ack retrieve_ack; + struct pri_event_retrieve_rej retrieve_rej; } pri_event; struct pri; @@ -456,7 +811,7 @@ /* Create a D-channel on a given file descriptor. The file descriptor must be a channel operating in HDLC mode with FCS computed by the fd's driver. Also it - must be NON-BLOCKING! Frames received on the fd should include FCS. Nodetype + must be NON-BLOCKING! Frames received on the fd should include FCS. Nodetype must be one of PRI_NETWORK or PRI_CPE. switchtype should be PRI_SWITCH_* */ struct pri *pri_new(int fd, int nodetype, int switchtype); struct pri *pri_new_bri(int fd, int ptpmode, int nodetype, int switchtype); @@ -521,7 +876,7 @@ #define PRI_KEYPAD_FACILITY_TX /* Send a keypad facility string of digits */ -int pri_keypad_facility(struct pri *pri, q931_call *call, char *digits); +int pri_keypad_facility(struct pri *pri, q931_call *call, const char *digits); /* Answer the incomplete(call without called number) call on the given channel. Set non-isdn to non-zero if you are not connecting to ISDN equipment */ @@ -531,6 +886,18 @@ Set non-isdn to non-zero if you are not connecting to ISDN equipment */ int pri_answer(struct pri *pri, q931_call *call, int channel, int nonisdn); +/*! + * \brief Give connected line information to a call + * \note Could be used instead of pri_sr_set_caller_party() before calling pri_setup(). + */ +int pri_connected_line_update(struct pri *pri, q931_call *call, const struct pri_party_connected_line *connected); + +/*! + * \brief Give redirection information to a call + * \note Could be used instead of pri_sr_set_redirecting_parties() before calling pri_setup(). + */ +int pri_redirecting_update(struct pri *pri, q931_call *call, const struct pri_party_redirecting *redirecting); + /* Set CRV reference for GR-303 calls */ @@ -556,9 +923,20 @@ int pri_reset(struct pri *pri, int channel); +/* handle b-channel maintenance messages */ +extern int pri_maintenance_service(struct pri *pri, int span, int channel, int changestatus); + /* Create a new call */ q931_call *pri_new_call(struct pri *pri); +/*! + * \brief Deterimine if the given call control pointer is a dummy call. + * + * \retval TRUE if given call is a dummy call. + * \retval FALSE otherwise. + */ +int pri_is_dummy_call(q931_call *call); + /* Retrieve CRV reference for GR-303 calls. Returns >0 on success. */ int pri_get_crv(struct pri *pri, q931_call *call, int *callmode); @@ -573,8 +951,8 @@ extern pri_event *pri_schedule_run_tv(struct pri *pri, const struct timeval *now); int pri_call(struct pri *pri, q931_call *c, int transmode, int channel, - int exclusive, int nonisdn, char *caller, int callerplan, char *callername, int callerpres, - char *called,int calledplan, int ulayer1); + int exclusive, int nonisdn, char *caller, int callerplan, char *callername, int callerpres, + char *called, int calledplan, int ulayer1); struct pri_sr *pri_sr_new(void); void pri_sr_free(struct pri_sr *sr); @@ -582,25 +960,91 @@ int pri_sr_set_channel(struct pri_sr *sr, int channel, int exclusive, int nonisdn); int pri_sr_set_bearer(struct pri_sr *sr, int transmode, int userl1); int pri_sr_set_called(struct pri_sr *sr, char *called, int calledplan, int complete); + +/*! + * \brief Set the caller party ID information in the call SETUP record. + * + * \param sr New call SETUP record. + * \param caller Caller party ID information to set. + * + * \return Nothing + */ +void pri_sr_set_caller_party(struct pri_sr *sr, const struct pri_party_id *caller); +/*! \note Use pri_sr_set_caller_party() instead to pass more precise caller information. */ int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, int callerplan, int callerpres); + +/*! + * \brief Set the calling subaddress information in the call SETUP record. + * + * \param sr New call SETUP record. + * \param subaddress information to set. + * + * \return Nothing + */ +void pri_sr_set_caller_subaddress(struct pri_sr *sr, const struct pri_party_subaddress *subaddress); + +/*! + * \brief Set the called subaddress information in the call SETUP record. + * + * \param sr New call SETUP record. + * \param subaddress information to set. + * + * \return Nothing + */ +void pri_sr_set_called_subaddress(struct pri_sr *sr, const struct pri_party_subaddress *subaddress); + +/*! + * \brief Set the redirecting information in the call SETUP record. + * + * \param sr New call SETUP record. + * \param caller Redirecting information to set. + * + * \return Nothing + */ +void pri_sr_set_redirecting_parties(struct pri_sr *sr, const struct pri_party_redirecting *redirecting); +/*! \note Use pri_sr_set_redirecting_parties() instead to pass more precise redirecting information. */ int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason); + +/*! + * \brief Set the keypad digits in the call SETUP record. + * + * \param sr New call SETUP record. + * \param keypad_digits Keypad digits to send. + * + * \return Nothing + */ +void pri_sr_set_keypad_digits(struct pri_sr *sr, const char *keypad_digits); + #define PRI_USER_USER_TX /* Set the user user field. Warning! don't send binary data accross this field */ void pri_sr_set_useruser(struct pri_sr *sr, const char *userchars); +void pri_sr_set_reversecharge(struct pri_sr *sr, int requested); void pri_call_set_useruser(q931_call *sr, const char *userchars); int pri_setup(struct pri *pri, q931_call *call, struct pri_sr *req); -/* Set a call has a call indpendent signalling connection (i.e. no bchan) */ +/*! + * \brief Set a call as a call indpendent signalling connection (i.e. no bchan) + * \note Call will automaticlly disconnect after signalling sent. + */ int pri_sr_set_connection_call_independent(struct pri_sr *req); +/*! + * \brief Set a call as a call indpendent signalling connection (i.e. no bchan) + * \note Call will stay connected until explicitly disconnected. + */ +int pri_sr_set_no_channel_call(struct pri_sr *req); + /* Send an MWI indication to a remote location. If activate is non zero, activates, if zero, deactivates */ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); /* Send an MWI deactivate request to a remote location */ int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); +/* Set service message support flag */ +int pri_set_service_message_support(struct pri *pri, int supportflag); + #define PRI_2BCT /* Attempt to pass the channels back to the NET side if compatable and * suscribed. Sometimes called 2 bchannel transfer (2BCT) */ @@ -656,48 +1100,208 @@ int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason); +/*! + * \brief Set the call deflection/rerouting feature enable flag. + * + * \param ctrl D channel controller. + * \param enable TRUE to enable call deflection/rerouting feature. + * + * \return Nothing + */ +void pri_reroute_enable(struct pri *ctrl, int enable); + +/*! + * \brief Send the CallRerouting/CallDeflection message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param caller Call rerouting/deflecting updated caller data. (NULL if data not updated.) + * \param deflection Call rerouting/deflecting redirection data. + * \param subscription_option Diverting user subscription option to specify if caller is notified. + * + * \note + * deflection->to is the new called number and must always be present. + * \note + * subscription option: + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option); + +enum PRI_REROUTING_RSP_CODE { + /*! + * Rerouting invocation accepted and the network provider option + * "served user call retention on invocation of diversion" + * is "clear call on invocation". + */ + PRI_REROUTING_RSP_OK_CLEAR, + /*! + * Rerouting invocation accepted and the network provider option + * "served user call retention on invocation of diversion" + * is "retain call until alerting begins at the deflected-to user". + */ + PRI_REROUTING_RSP_OK_RETAIN, + PRI_REROUTING_RSP_NOT_SUBSCRIBED, + PRI_REROUTING_RSP_NOT_AVAILABLE, + /*! Supplementary service interaction not allowed. */ + PRI_REROUTING_RSP_NOT_ALLOWED, + PRI_REROUTING_RSP_INVALID_NUMBER, + /*! Deflection to prohibited number (e.g., operator, police, emergency). */ + PRI_REROUTING_RSP_SPECIAL_SERVICE_NUMBER, + /*! Deflection to served user number. */ + PRI_REROUTING_RSP_DIVERSION_TO_SELF, + PRI_REROUTING_RSP_MAX_DIVERSIONS_EXCEEDED, + PRI_REROUTING_RSP_RESOURCE_UNAVAILABLE, +}; + +/*! + * \brief Send the CallRerouteing/CallDeflection response message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * \param invoke_id Value given by the initiating request. + * \param code The result to send. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_rerouting_rsp(struct pri *ctrl, q931_call *call, int invoke_id, enum PRI_REROUTING_RSP_CODE code); + +/*! + * \brief Set the call hold feature enable flag. + * + * \param ctrl D channel controller. + * \param enable TRUE to enable call hold feature. + * + * \return Nothing + */ +void pri_hold_enable(struct pri *ctrl, int enable); + +/*! + * \brief Send the HOLD message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_hold(struct pri *ctrl, q931_call *call); + +/*! + * \brief Send the HOLD ACKNOWLEDGE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_hold_ack(struct pri *ctrl, q931_call *call); + +/*! + * \brief Send the HOLD REJECT message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param cause Q.931 cause code for rejecting the hold request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause); + +/*! + * \brief Send the RETRIEVE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param channel Encoded channel id to use. If zero do not send channel id. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_retrieve(struct pri *ctrl, q931_call *call, int channel); + +/*! + * \brief Send the RETRIEVE ACKNOWLEDGE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param channel Encoded channel id to use. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel); + +/*! + * \brief Send the RETRIEVE REJECT message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param cause Q.931 cause code for rejecting the retrieve request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause); + /* Get/Set PRI Timers */ #define PRI_GETSET_TIMERS int pri_set_timer(struct pri *pri, int timer, int value); int pri_get_timer(struct pri *pri, int timer); -int pri_timer2idx(char *timer); +int pri_timer2idx(const char *timer_name); -#define PRI_MAX_TIMERS 32 +/*! New configurable timers and counters must be added to the end of the list */ +enum PRI_TIMERS_AND_COUNTERS { + PRI_TIMER_N200, /*!< Maximum numer of Q.921 retransmissions */ + PRI_TIMER_N201, /*!< Maximum numer of octets in an information field */ + PRI_TIMER_N202, /*!< Maximum numer of transmissions of the TEI identity request message */ + PRI_TIMER_K, /*!< Maximum number of outstanding I-frames */ -#define PRI_TIMER_N200 0 /* Maximum numer of q921 retransmissions */ -#define PRI_TIMER_N201 1 /* Maximum numer of octets in an information field */ -#define PRI_TIMER_N202 2 /* Maximum numer of transmissions of the TEI identity request message */ -#define PRI_TIMER_K 3 /* Maximum number of outstanding I-frames */ + PRI_TIMER_T200, /*!< Time between SABME's */ + PRI_TIMER_T201, /*!< Minimum time between retransmissions of the TEI Identity check messages */ + PRI_TIMER_T202, /*!< Minimum time between transmission of TEI Identity request messages */ + PRI_TIMER_T203, /*!< Maximum time without exchanging packets */ -#define PRI_TIMER_T200 4 /* time between SABME's */ -#define PRI_TIMER_T201 5 /* minimum time between retransmissions of the TEI Identity check messages */ -#define PRI_TIMER_T202 6 /* minimum time between transmission of TEI Identity request messages */ -#define PRI_TIMER_T203 7 /* maxiumum time without exchanging packets */ + PRI_TIMER_T300, + PRI_TIMER_T301, /*!< Maximum time to respond to an ALERT */ + PRI_TIMER_T302, + PRI_TIMER_T303, /*!< Maximum time to wait after sending a SETUP without a response */ + PRI_TIMER_T304, + PRI_TIMER_T305, /*!< Wait for DISCONNECT acknowledge */ + PRI_TIMER_T306, + PRI_TIMER_T307, + PRI_TIMER_T308, /*!< Wait for RELEASE acknowledge */ + PRI_TIMER_T309, /*!< Time active calls can tollerate data link layer being down before clearing. */ + PRI_TIMER_T310, /*!< Maximum time between receiving a CALL_PROCEEDING and receiving a ALERT/CONNECT/DISCONNECT/PROGRESS */ + PRI_TIMER_T313, /*!< Wait for CONNECT acknowledge, CPE side only */ + PRI_TIMER_T314, + PRI_TIMER_T316, /*!< Maximum time between transmitting a RESTART and receiving a RESTART ACK */ + PRI_TIMER_T317, + PRI_TIMER_T318, + PRI_TIMER_T319, + PRI_TIMER_T320, + PRI_TIMER_T321, + PRI_TIMER_T322, -#define PRI_TIMER_T300 8 -#define PRI_TIMER_T301 9 /* maximum time to respond to an ALERT */ -#define PRI_TIMER_T302 10 -#define PRI_TIMER_T303 11 /* maximum time to wait after sending a SETUP without a response */ -#define PRI_TIMER_T304 12 -#define PRI_TIMER_T305 13 -#define PRI_TIMER_T306 14 -#define PRI_TIMER_T307 15 -#define PRI_TIMER_T308 16 -#define PRI_TIMER_T309 17 -#define PRI_TIMER_T310 18 /* maximum time between receiving a CALLPROCEEDING and receiving a ALERT/CONNECT/DISCONNECT/PROGRESS */ -#define PRI_TIMER_T313 19 -#define PRI_TIMER_T314 20 -#define PRI_TIMER_T316 21 /* maximum time between transmitting a RESTART and receiving a RESTART ACK */ -#define PRI_TIMER_T317 22 -#define PRI_TIMER_T318 23 -#define PRI_TIMER_T319 24 -#define PRI_TIMER_T320 25 -#define PRI_TIMER_T321 26 -#define PRI_TIMER_T322 27 + PRI_TIMER_TM20, /*!< Maximum time awaiting XID response */ + PRI_TIMER_NM20, /*!< Number of XID retransmits */ -#define PRI_TIMER_TM20 28 /* maximum time avaiting XID response */ -#define PRI_TIMER_NM20 29 /* number of XID retransmits */ + PRI_TIMER_T_HOLD, /*!< Maximum time to wait for HOLD request response. */ + PRI_TIMER_T_RETRIEVE, /*!< Maximum time to wait for RETRIEVE request response. */ + PRI_TIMER_T_RESPONSE, /*!< Maximum time to wait for a typical APDU response. */ + + /* Must be last in the enum list */ + PRI_MAX_TIMERS +}; + /* Get PRI version */ const char *pri_get_version(void); Index: asn1_primitive.c =================================================================== --- a/asn1_primitive.c (.../tags/1.4.10.2) (revision 0) +++ b/asn1_primitive.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,1306 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 ASN.1 BER encode/decode primitives + * + * \author Richard Mudgett + */ + + +#include +#include + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "asn1.h" + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Dump the memory contents indicated in printable characters. (Helper function.) + * + * \param ctrl D channel controller for any diagnostic messages. + * \param start Dump memory starting position. + * \param end Dump memory ending position. (Not included in dump.) + * + * \return Nothing + */ +static void asn1_dump_mem_helper(struct pri *ctrl, const unsigned char *start, + const unsigned char *end) +{ + pri_message(ctrl, " - \""); + for (; start < end; ++start) { + pri_message(ctrl, "%c", (isprint(*start)) ? *start : '~'); + } + pri_message(ctrl, "\"\n"); +} + +/*! + * \internal + * \brief Dump the memory contents indicated. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param indent Number of spaces to indent for each new memory dump line. + * \param pos Dump memory starting position. + * \param length Number of bytes to dump. + * + * \return Nothing + */ +static void asn1_dump_mem(struct pri *ctrl, unsigned indent, const unsigned char *pos, + unsigned length) +{ + const unsigned char *seg_start; + const unsigned char *end; + unsigned delimiter; + unsigned count; + + seg_start = pos; + end = pos + length; + if (pos < end) { + delimiter = '<'; + for (;;) { + pri_message(ctrl, "%*s", indent, ""); + for (count = 0; count++ < 16 && pos < end;) { + pri_message(ctrl, "%c%02X", delimiter, *pos++); + delimiter = (count == 8) ? '-' : ' '; + } + if (end <= pos) { + break; + } + asn1_dump_mem_helper(ctrl, seg_start, pos); + seg_start = pos; + } + } else { + pri_message(ctrl, "%*s<", indent, ""); + } + pri_message(ctrl, ">"); + asn1_dump_mem_helper(ctrl, seg_start, end); +} + +/*! + * \brief Convert the given tag value to a descriptive string. + * + * \param tag Component tag value to convert to a string. + * + * \return Converted tag string. + */ +const char *asn1_tag2str(unsigned tag) +{ + static const char *primitives[32] = { + [ASN1_TYPE_INDEF_TERM] = "Indefinite length terminator", + [ASN1_TYPE_BOOLEAN] = "Boolean", + [ASN1_TYPE_INTEGER] = "Integer", + [ASN1_TYPE_BIT_STRING] = "Bit String", + [ASN1_TYPE_OCTET_STRING] = "Octet String", + [ASN1_TYPE_NULL] = "NULL", + [ASN1_TYPE_OBJECT_IDENTIFIER] = "OID", + [ASN1_TYPE_OBJECT_DESCRIPTOR] = "Object Descriptor", + [ASN1_TYPE_EXTERN] = "External", + [ASN1_TYPE_REAL] = "Real", + [ASN1_TYPE_ENUMERATED] = "Enumerated", + [ASN1_TYPE_EMBEDDED_PDV] = "Embedded PDV", + [ASN1_TYPE_UTF8_STRING] = "UTF8 String", + [ASN1_TYPE_RELATIVE_OID] = "Relative OID", + [ASN1_TYPE_SEQUENCE] = "Sequence", + [ASN1_TYPE_SET] = "Set", + [ASN1_TYPE_NUMERIC_STRING] = "Numeric String", + [ASN1_TYPE_PRINTABLE_STRING] = "Printable String", + [ASN1_TYPE_TELETEX_STRING] = "Teletex String", + [ASN1_TYPE_VIDEOTEX_STRING] = "Videotex String", + [ASN1_TYPE_IA5_STRING] = "IA5 String", + [ASN1_TYPE_UTC_TIME] = "UTC Time", + [ASN1_TYPE_GENERALIZED_TIME] = "Generalized Time", + [ASN1_TYPE_GRAPHIC_STRING] = "Graphic String", + [ASN1_TYPE_VISIBLE_STRING] = "Visible/ISO646 String", + [ASN1_TYPE_GENERAL_STRING] = "General String", + [ASN1_TYPE_UNIVERSAL_STRING] = "Universal String", + [ASN1_TYPE_CHAR_STRING] = "Character String", + [ASN1_TYPE_BMP_STRING] = "BMP String", + [ASN1_TYPE_EXTENSION] = "Type Extension", + }; + static char buf[64]; + const char *description; + unsigned asn1_constructed; /*! TRUE if the tag is constructed. */ + unsigned asn1_type; + + asn1_constructed = ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED); + asn1_type = tag & ASN1_TYPE_MASK; + + switch (tag & ASN1_CLASS_MASK) { + case ASN1_CLASS_UNIVERSAL: + if (tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_CONSTRUCTED | ASN1_TYPE_INDEF_TERM)) { + description = NULL; + } else { + description = primitives[asn1_type]; + } + if (!description) { + description = "Reserved"; + } + snprintf(buf, sizeof(buf), "%s%s(%u 0x%02X)", description, + asn1_constructed ? "/C" : "", tag, tag); + return buf; + case ASN1_CLASS_APPLICATION: + description = "Application"; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC: + description = "Context Specific"; + break; + case ASN1_CLASS_PRIVATE: + description = "Private"; + break; + default: + snprintf(buf, sizeof(buf), "Unknown tag (%u 0x%02X)", tag, tag); + return buf; + } + snprintf(buf, sizeof(buf), "%s%s [%u 0x%02X]", description, + asn1_constructed ? "/C" : "", asn1_type, asn1_type); + return buf; +} + +/*! + * \brief Decode the ASN.1 tag value. + * + * \param tag_pos ASN.1 tag starting position. + * \param end End of ASN.1 encoded data buffer. + * \param tag Decoded tag value returned on success. + * + * \retval Next octet after the tag on success. + * \retval NULL on error. + */ +const unsigned char *asn1_dec_tag(const unsigned char *tag_pos, const unsigned char *end, + unsigned *tag) +{ + unsigned extended_tag; + + if (end <= tag_pos) { + return NULL; + } + *tag = *tag_pos++; + if ((*tag & ASN1_TYPE_MASK) == ASN1_TYPE_EXTENSION) { + /* Extract the extended tag value */ + extended_tag = 0; + do { + if (end <= tag_pos) { + return NULL; + } + extended_tag <<= 7; + extended_tag |= *tag_pos & ~0x80; + } while (*tag_pos++ & 0x80); + if (extended_tag && extended_tag < ASN1_TYPE_EXTENSION) { + /* + * The sender did not need to use the extended format. + * This is an encoding error on their part, but we will + * accept it anyway. + * + * Note we cannot return a null tag value from this path. + * We would misinterpret the indefinite length + * terminator. + */ + *tag &= ~ASN1_TYPE_MASK; + *tag |= extended_tag; + } + } + + return tag_pos; +} + +/*! + * \brief Decode the length of an ASN.1 component length. + * + * \param len_pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param length Decoded length value returned on success. (-1 if indefinite) + * + * \retval Next octet after the length on success. + * \retval NULL on error. + * + * \note The decoded length is checked to see if there is enough buffer + * left for the component body. + */ +const unsigned char *asn1_dec_length(const unsigned char *len_pos, + const unsigned char *end, int *length) +{ + unsigned length_size; + + if (end <= len_pos) { + /* Not enough buffer to determine how the length is encoded */ + return NULL; + } + + if (*len_pos < 0x80) { + /* Short length encoding */ + *length = *len_pos++; + } else if (*len_pos == 0x80) { + /* Indefinite length encoding */ + *length = -1; + ++len_pos; + if (end < len_pos + ASN1_INDEF_TERM_LEN) { + /* Not enough buffer for the indefinite length terminator */ + return NULL; + } + return len_pos; + } else { + /* Long length encoding */ + length_size = *len_pos++ & 0x7f; + if (length_size == 0x7f) { + /* Reserved extension encoding that has not been defined. */ + return NULL; + } + if (end < len_pos + length_size) { + /* Not enough buffer for the length value */ + return NULL; + } + *length = 0; + while (length_size--) { + *length = (*length << 8) | *len_pos++; + } + } + + if (end < len_pos + *length) { + /* Not enough buffer for the component body. */ + return NULL; + } + return len_pos; +} + +/*! + * \internal + * \brief Skip to the end of an indefinite length constructed component helper. + * + * \param pos ASN.1 tag starting position. + * \param end End of ASN.1 decoding data buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *asn1_dec_indef_end_fixup_helper(const unsigned char *pos, + const unsigned char *end) +{ + unsigned tag; + int length; + + while (pos < end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + if (length < 0) { + /* Skip over indefinite length sub-component */ + if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED + || tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SET) + || tag == + (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SEQUENCE)) { + /* This is an ITU encoded indefinite length component. */ + ASN1_CALL(pos, asn1_dec_indef_end_fixup_helper(pos, end)); + } else { + /* This is a non-ITU encoded indefinite length component. */ + while (pos < end && *pos != ASN1_INDEF_TERM) { + ++pos; + } + pos += ASN1_INDEF_TERM_LEN; + } + } else { + /* Skip over defininte length sub-component */ + pos += length; + } + } + if (end < pos + ASN1_INDEF_TERM_LEN) { + return NULL; + } + + return pos + ASN1_INDEF_TERM_LEN; +} + +/*! + * \brief Skip to the end of an indefinite length constructed component. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param pos ASN.1 tag starting position. + * \param end End of ASN.1 decoding data buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *asn1_dec_indef_end_fixup(struct pri *ctrl, const unsigned char *pos, + const unsigned char *end) +{ + if (pos < end && *pos != ASN1_INDEF_TERM && (ctrl->debug & PRI_DEBUG_APDU)) { + pri_message(ctrl, + " Skipping unused indefinite length constructed component octets!\n"); + } + return asn1_dec_indef_end_fixup_helper(pos, end); +} + +/*! + * \brief Decode the boolean primitive. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this primitive. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param value Decoded boolean value. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *asn1_dec_boolean(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, int32_t *value) +{ + int length; + + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + if (length != 1) { + /* + * The encoding rules say the length can only be one. + * It is rediculus to get anything else anyway. + */ + return NULL; + } + + *value = *pos++ ? 1 : 0; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %s = %d\n", name, asn1_tag2str(tag), *value); + } + + return pos; +} + +/*! + * \brief Decode the integer type primitive. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this primitive. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param value Decoded integer type value. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *asn1_dec_int(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, int32_t *value) +{ + int length; + + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + if (length <= 0) { + /* + * The encoding rules say the length can not be indefinite. + * It cannot be empty for that matter either. + */ + return NULL; + } + +#if 1 + /* Read value as signed */ + if (*pos & 0x80) { + /* The value is negative */ + *value = -1; + } else { + *value = 0; + } +#else + /* Read value as unsigned */ + *value = 0; +#endif + while (length--) { + *value = (*value << 8) | *pos; + pos++; + } + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %s = %d 0x%04X\n", name, asn1_tag2str(tag), *value, + *value); + } + + return pos; +} + +/*! + * \brief Decode the null primitive. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this primitive. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *asn1_dec_null(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end) +{ + int length; + + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + if (length != 0) { + /* + * The encoding rules say the length can only be zero. + * It is rediculus to get anything else anyway. + */ + return NULL; + } + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %s\n", name, asn1_tag2str(tag)); + } + + return pos; +} + +/*! + * \brief Decode the object identifier primitive. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this primitive. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param oid Decoded OID type value. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *asn1_dec_oid(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct asn1_oid *oid) +{ + int length; + unsigned num_values; + unsigned value; + unsigned delimiter; + + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + if (length < 0) { + /* + * The encoding rules say the length can not be indefinite. + */ + return NULL; + } + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %s =", name, asn1_tag2str(tag)); + } + delimiter = ' '; + num_values = 0; + while (length) { + value = 0; + for (;;) { + --length; + value = (value << 7) | (*pos & 0x7F); + if (!(*pos++ & 0x80)) { + /* Last octet in the OID subidentifier value */ + if (num_values < ARRAY_LEN(oid->value)) { + oid->value[num_values] = value; + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "%c%u", delimiter, value); + } + delimiter = '.'; + } else { + /* Too many OID subidentifier values */ + delimiter = '~'; + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "%c%u", delimiter, value); + } + } + ++num_values; + break; + } + if (!length) { + oid->num_values = 0; + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "\n" + " Last OID subidentifier value not terminated!\n"); + } + return NULL; + } + } + } + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "\n"); + } + + if (num_values <= ARRAY_LEN(oid->value)) { + oid->num_values = num_values; + return pos; + } else { + /* Need to increase the size of the OID subidentifier list. */ + oid->num_values = 0; + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Too many OID values!\n"); + } + return NULL; + } +} + +/*! + * \brief Decode a binary string primitive. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this primitive. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param buf_size Size of the supplied string buffer. (Must be nonzero) + * \param str Where to put the decoded string. + * \param str_len Length of the decoded string. + * + * \note The string will be null terminated just in case. + * The buffer needs to have enough room for a null terminator. + * \note The parse will fail if the parsed string is too large for + * the supplied buffer. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *asn1_dec_string_bin(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, size_t buf_size, + unsigned char *str, size_t *str_len) +{ + int length; + size_t sub_buf_size; + size_t sub_str_len; + unsigned char *sub_str; + + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + if (length < 0) { + /* This is an indefinite length string */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %s = Indefinite length string\n", name, + asn1_tag2str(tag)); + } + if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED) { + /* + * This is an ITU encoded indefinite length string + * and could contain null. + */ + + /* Ensure that an empty string is null terminated. */ + *str = 0; + + /* Collect all substrings into the original string buffer. */ + *str_len = 0; + sub_str = str; + sub_buf_size = buf_size; + for (;;) { + ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); + if (tag == ASN1_INDEF_TERM) { + /* End-of-contents octets */ + break; + } + + /* Append the substring to the accumulated indefinite string. */ + ASN1_CALL(pos, asn1_dec_string_bin(ctrl, name, tag, pos, end, + sub_buf_size, sub_str, &sub_str_len)); + + sub_buf_size -= sub_str_len; + sub_str += sub_str_len; + *str_len += sub_str_len; + } + } else { + /* + * This is a non-ITU encoded indefinite length string + * and must not contain null's. + */ + for (length = 0;; ++length) { + if (end <= pos + length) { + /* Not enough buffer left */ + return NULL; + } + if (pos[length] == 0) { + /* Found End-of-contents octets */ + break; + } + } + + if (buf_size - 1 < length) { + /* The destination buffer is not large enough for the data */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " String buffer not large enough!\n"); + } + return NULL; + } + + /* Extract the string and null terminate it. */ + memcpy(str, pos, length); + str[length] = 0; + *str_len = length; + + pos += length + 1; + } + if (end <= pos) { + /* Not enough buffer left for End-of-contents octets */ + return NULL; + } + if (*pos++ != 0) { + /* We actually did not find the End-of-contents octets. */ + return NULL; + } + if (ctrl->debug & PRI_DEBUG_APDU) { + /* Dump the collected string buffer contents. */ + pri_message(ctrl, " Completed string =\n"); + asn1_dump_mem(ctrl, 6, str, *str_len); + } + } else { + /* This is a definite length string */ + if (buf_size - 1 < length) { + /* The destination buffer is not large enough for the data */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %s = Buffer not large enough!\n", name, + asn1_tag2str(tag)); + } + return NULL; + } + + /* Extract the string and null terminate it. */ + memcpy(str, pos, length); + str[length] = 0; + *str_len = length; + + pos += length; + + if (ctrl->debug & PRI_DEBUG_APDU) { + /* Dump the collected string buffer contents. */ + pri_message(ctrl, " %s %s =\n", name, asn1_tag2str(tag)); + asn1_dump_mem(ctrl, 4, str, *str_len); + } + } + + return pos; +} + +/*! + * \brief Decode a string that can be truncated to a maximum length primitive. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this primitive. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param buf_size Size of the supplied string buffer. (Must be nonzero) + * \param str Where to put the decoded null terminated string. + * \param str_len Length of the decoded string. + * (computed for convenience since you could just do a strlen()) + * + * \note The parsed string will be truncated if the string buffer + * cannot contain it. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *asn1_dec_string_max(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, size_t buf_size, + unsigned char *str, size_t *str_len) +{ + int length; + size_t str_length; + size_t sub_buf_size; + size_t sub_str_len; + unsigned char *sub_str; + + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + if (length < 0) { + /* This is an indefinite length string */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %s = Indefinite length string\n", name, + asn1_tag2str(tag)); + } + if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED) { + /* This is an ITU encoded indefinite length string. */ + + /* Ensure that an empty string is null terminated. */ + *str = 0; + + /* Collect all substrings into the original string buffer. */ + *str_len = 0; + sub_str = str; + sub_buf_size = buf_size; + for (;;) { + ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); + if (tag == ASN1_INDEF_TERM) { + /* End-of-contents octets */ + break; + } + + /* Append the substring to the accumulated indefinite string. */ + ASN1_CALL(pos, asn1_dec_string_max(ctrl, name, tag, pos, end, + sub_buf_size, sub_str, &sub_str_len)); + + sub_buf_size -= sub_str_len; + sub_str += sub_str_len; + *str_len += sub_str_len; + } + } else { + /* This is a non-ITU encoded indefinite length string. */ + for (length = 0;; ++length) { + if (end <= pos + length) { + /* Not enough buffer left */ + return NULL; + } + if (pos[length] == 0) { + /* Found End-of-contents octets */ + break; + } + } + + /* Extract the string, truncate if necessary, and terminate it. */ + str_length = (buf_size - 1 < length) ? buf_size - 1 : length; + memcpy(str, pos, str_length); + str[str_length] = 0; + *str_len = str_length; + + pos += length + 1; + } + if (end <= pos) { + /* Not enough buffer left for End-of-contents octets */ + return NULL; + } + if (*pos++ != 0) { + /* We actually did not find the End-of-contents octets. */ + return NULL; + } + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Completed string = \"%s\"\n", str); + } + } else { + /* + * This is a definite length string + * + * Extract the string, truncate if necessary, and terminate it. + */ + str_length = (buf_size - 1 < length) ? buf_size - 1 : length; + memcpy(str, pos, str_length); + str[str_length] = 0; + *str_len = str_length; + + pos += length; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %s = \"%s\"\n", name, asn1_tag2str(tag), str); + } + } + + return pos; +} + +/*! + * \internal + * \brief Recursive ASN.1 buffer decoding dump helper. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param pos ASN.1 tag starting position. + * \param end End of ASN.1 decoding data buffer. + * \param level Indentation level to use. + * \param indefinite_term TRUE if to stop on an indefinite length terminator. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *asn1_dump_helper(struct pri *ctrl, const unsigned char *pos, + const unsigned char *end, unsigned level, unsigned indefinite_term) +{ + unsigned delimiter; + unsigned tag; + int length; + const unsigned char *len_pos; + + while (pos < end && (!indefinite_term || *pos != ASN1_INDEF_TERM)) { + /* Decode the tag */ + pri_message(ctrl, "%*s", 2 * level, ""); + len_pos = asn1_dec_tag(pos, end, &tag); + if (!len_pos) { + pri_message(ctrl, "Invalid tag encoding!\n"); + return NULL; + } + + /* Dump the tag contents. */ + pri_message(ctrl, "%s ", asn1_tag2str(tag)); + delimiter = '<'; + while (pos < len_pos) { + pri_message(ctrl, "%c%02X", delimiter, *pos); + delimiter = ' '; + ++pos; + } + pri_message(ctrl, "> "); + + /* Decode the length */ + pos = asn1_dec_length(len_pos, end, &length); + if (!pos) { + pri_message(ctrl, "Invalid length encoding!\n"); + return NULL; + } + + /* Dump the length contents. */ + if (length < 0) { + pri_message(ctrl, "Indefinite length "); + } else { + pri_message(ctrl, "Len:%d ", length); + } + delimiter = '<'; + while (len_pos < pos) { + pri_message(ctrl, "%c%02X", delimiter, *len_pos); + delimiter = ' '; + ++len_pos; + } + pri_message(ctrl, ">\n"); + + /* Dump the body contents */ + ++level; + if (length < 0) { + /* Indefinite length */ + if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED) { + /* This is an ITU encoded indefinite length component. */ + ASN1_CALL(pos, asn1_dump_helper(ctrl, pos, end, level, 1)); + } else if (tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SET) + || tag == + (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SEQUENCE)) { + pri_message(ctrl, "%*sThis tag must always be constructed!\n", 2 * level, + ""); + /* Assume tag was constructed to keep going */ + ASN1_CALL(pos, asn1_dump_helper(ctrl, pos, end, level, 1)); + } else { + /* This is a non-ITU encoded indefinite length component. */ + pri_message(ctrl, "%*sNon-ITU indefininte length component.\n", + 2 * level, ""); + length = 0; + while (pos + length < end && pos[length] != ASN1_INDEF_TERM) { + ++length; + } + if (length) { + asn1_dump_mem(ctrl, 2 * level, pos, length); + pos += length; + } + } + --level; + if (end < pos + ASN1_INDEF_TERM_LEN) { + pri_message(ctrl, "%*sNot enough room for the End-of-contents octets!\n", + 2 * level, ""); + pos = end; + } else { + pri_message(ctrl, "%*sEnd-of-contents <%02X %02X>%s\n", 2 * level, "", + pos[0], pos[1], (pos[1] != 0) ? " Invalid!" : ""); + pos += ASN1_INDEF_TERM_LEN; + } + } else { + /* Defininte length */ + if ((tag & ASN1_PC_MASK) == ASN1_PC_CONSTRUCTED) { + /* Dump constructed contents */ + ASN1_CALL(pos, asn1_dump_helper(ctrl, pos, pos + length, level, 0)); + } else if (tag == (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SET) + || tag == + (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_SEQUENCE)) { + /* Assume tag was constructed to keep going */ + pri_message(ctrl, "%*sThis tag must always be constructed!\n", 2 * level, + ""); + ASN1_CALL(pos, asn1_dump_helper(ctrl, pos, pos + length, level, 0)); + } else if (0 < length) { + /* Dump primitive contents. */ + asn1_dump_mem(ctrl, 2 * level, pos, length); + pos += length; + } + --level; + } + +#if 0 + pri_message(ctrl, "%*sEnd\n", 2 * level, ""); +#endif + } + + return pos; +} + +/*! + * \brief Dump the given ASN.1 buffer contents. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param start_asn1 First octet in the ASN.1 buffer. (ASN.1 tag starting position) + * \param end One beyond the last octet in the ASN.1 buffer. + * + * \return Nothing + */ +void asn1_dump(struct pri *ctrl, const unsigned char *start_asn1, + const unsigned char *end) +{ + pri_message(ctrl, "ASN.1 dump\n"); + if (start_asn1) { + asn1_dump_helper(ctrl, start_asn1, end, 1, 0); + } + pri_message(ctrl, "ASN.1 end\n"); +} + +/*! + * \brief Encode the length of an ASN.1 component body of predetermined size. + * + * \param len_pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 encoding data buffer. + * \param length Predetermined component body length. + * + * \note The encoding buffer does not need to be checked after calling. + * It is already checked to have the requested room. + * + * \retval Next octet after the length on success. + * \retval NULL on error. + */ +unsigned char *asn1_enc_length(unsigned char *len_pos, unsigned char *end, size_t length) +{ + u_int32_t body_length; /* Length of component contents */ + u_int32_t value; + u_int32_t test_mask; + unsigned length_size; /* Length of the length encoding */ + + body_length = length; + + /* Determine length encoding length */ + if (body_length < 128) { + length_size = 1; + } else { + /* Find most significant octet of 32 bit integer that carries meaning. */ + test_mask = 0xFF000000; + for (length_size = 4; --length_size;) { + if (body_length & test_mask) { + /* + * Found the first 8 bits of a multiple octet length that + * is not all zeroes. + */ + break; + } + test_mask >>= 8; + } + length_size += 1 + 1; + } + + if (end < len_pos + length_size + body_length) { + /* No room for the length and component body in the buffer */ + return NULL; + } + + /* Encode the component body length */ + if (length_size == 1) { + *len_pos++ = body_length; + } else { + *len_pos++ = 0x80 | --length_size; + while (length_size--) { + value = body_length; + value >>= (8 * length_size); + *len_pos++ = value & 0xFF; + } + } + + return len_pos; +} + +/*! + * \brief Encode the length of an already encoded ASN.1 component. + * + * \param len_pos Starting position of the ASN.1 component length. + * \param component_end Next octet after the component body. + * \param end End of ASN.1 encoding data buffer. + * + * \note The total component size could increase or decrease. + * \note The component length field must have been initialized with + * ASN1_LEN_INIT() or ASN1_CONSTRUCTED_BEGIN(). + * + * \retval Next octet after the component body on success. + * \retval NULL on error. + */ +unsigned char *asn1_enc_length_fixup(unsigned char *len_pos, + unsigned char *component_end, unsigned char *end) +{ + u_int32_t body_length; /* Length of component contents */ + u_int32_t value; + u_int32_t test_mask; + unsigned length_size; /* Length of the length encoding */ + + if (component_end < len_pos + *len_pos) { + /* Sanity check */ + return NULL; + } + + body_length = component_end - len_pos - *len_pos; + + /* Determine length encoding length */ + if (body_length < 128) { + length_size = 1; + } else { + /* Find most significant octet of 32 bit integer that carries meaning. */ + test_mask = 0xFF000000; + for (length_size = 4; --length_size;) { + if (body_length & test_mask) { + /* + * Found the first 8 bits of a multiple octet length that + * is not all zeroes. + */ + break; + } + test_mask >>= 8; + } + length_size += 1 + 1; + } + + component_end = len_pos + length_size + body_length; + if (end < component_end) { + /* No room for the component in the buffer */ + return NULL; + } + if (length_size != *len_pos) { + /* Must shift the component body */ + memmove(len_pos + length_size, len_pos + *len_pos, body_length); + } + + /* Encode the component body length */ + if (length_size == 1) { + *len_pos = body_length; + } else { + *len_pos++ = 0x80 | --length_size; + while (length_size--) { + value = body_length; + value >>= (8 * length_size); + *len_pos++ = value & 0xFF; + } + } + + return component_end; +} + +/*! + * \brief Encode the boolean primitive. + * + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * \param value Component value to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *asn1_enc_boolean(unsigned char *pos, unsigned char *end, unsigned tag, + int32_t value) +{ + if (end < pos + 3) { + /* No room for the component in the buffer */ + return NULL; + } + + /* Encode component */ + *pos++ = tag; + *pos++ = 1; + *pos++ = value ? 1 : 0; + + return pos; +} + +/*! + * \brief Encode the integer type primitive. + * + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * \param value Component value to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *asn1_enc_int(unsigned char *pos, unsigned char *end, unsigned tag, + int32_t value) +{ + unsigned count; + u_int32_t test_mask; + u_int32_t val; + + /* Find most significant octet of 32 bit integer that carries meaning. */ + test_mask = 0xFF800000; + val = (u_int32_t) value; + for (count = 4; --count;) { + if ((val & test_mask) != test_mask && (val & test_mask) != 0) { + /* + * The first 9 bits of a multiple octet integer is not + * all ones or zeroes. + */ + break; + } + test_mask >>= 8; + } + + if (end < pos + 3 + count) { + /* No room for the component in the buffer */ + return NULL; + } + + /* Encode component */ + *pos++ = tag; + *pos++ = count + 1; + do { + val = (u_int32_t) value; + val >>= (8 * count); + *pos++ = val & 0xFF; + } while (count--); + + return pos; +} + +/*! + * \brief Encode the null type primitive. + * + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *asn1_enc_null(unsigned char *pos, unsigned char *end, unsigned tag) +{ + if (end < pos + 2) { + /* No room for the component in the buffer */ + return NULL; + } + + /* Encode component */ + *pos++ = tag; + *pos++ = 0; + + return pos; +} + +/*! + * \brief Encode the object identifier (OID) primitive. + * + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * \param oid Component value to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *asn1_enc_oid(unsigned char *pos, unsigned char *end, unsigned tag, + const struct asn1_oid *oid) +{ + unsigned char *len_pos; + unsigned num_values; + unsigned count; + u_int32_t value; + + if (end < pos + 2) { + /* No room for the component tag and length in the buffer */ + return NULL; + } + + *pos++ = tag; + len_pos = pos++; + + /* For all OID subidentifer values */ + for (num_values = 0; num_values < oid->num_values; ++num_values) { + /* + * Count the number of 7 bit chunks that are needed + * to encode the integer. + */ + value = oid->value[num_values] >> 7; + for (count = 0; value; ++count) { + /* There are bits still set */ + value >>= 7; + } + + if (end < pos + count + 1) { + /* No room for the component body in the buffer */ + return NULL; + } + + /* Store OID subidentifier value */ + do { + value = oid->value[num_values]; + value >>= (7 * count); + *pos++ = (value & 0x7F) | (count ? 0x80 : 0); + } while (count--); + } + + /* length */ + *len_pos = pos - len_pos - 1; + + return pos; +} + +/*! + * \brief Encode the binary string type primitive. + * + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * \param str Binary string to encode. + * \param str_len Length of binary string to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *asn1_enc_string_bin(unsigned char *pos, unsigned char *end, unsigned tag, + const unsigned char *str, size_t str_len) +{ + if (end < pos + 1) { + /* No room for the component tag in the buffer */ + return NULL; + } + + /* Encode component */ + *pos++ = tag; + ASN1_CALL(pos, asn1_enc_length(pos, end, str_len)); + memcpy(pos, str, str_len); + + return pos + str_len; +} + +/*! + * \brief Encode a string that can be truncated to a maximum length primitive. + * + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param tag Component tag to identify the encoded component. + * \param str Null terminated string to encode. + * \param max_len Maximum length of string to encode. + * + * \note The string will be truncated if it is too long. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *asn1_enc_string_max(unsigned char *pos, unsigned char *end, unsigned tag, + const unsigned char *str, size_t max_len) +{ + size_t str_len; + + str_len = strlen((char *) str); + if (max_len < str_len) { + str_len = max_len; + } + return asn1_enc_string_bin(pos, end, tag, str, str_len); +} + +/* ------------------------------------------------------------------- */ +/* end asn1_primitive.c */ Property changes on: asn1_primitive.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: pri.c =================================================================== --- a/pri.c (.../tags/1.4.10.2) (revision 1357) +++ b/pri.c (.../branches/1.4) (revision 1357) @@ -42,8 +42,56 @@ #include "pri_facility.h" #include "pri_q921.h" #include "pri_q931.h" -#include "pri_timers.h" +#define PRI_BIT(a_bit) (1UL << (a_bit)) +#define PRI_ALL_SWITCHES 0xFFFFFFFF + +struct pri_timer_table { + const char *name; + enum PRI_TIMERS_AND_COUNTERS number; + unsigned long used_by; +}; + +/*! + * \note Sort the timer table entries in the order of the timer name so + * pri_dump_info_str() can display them in a consitent order. + */ +static const struct pri_timer_table pri_timer[] = { +/* *INDENT-OFF* */ + /* timer name timer number used by switches */ + { "N200", PRI_TIMER_N200, PRI_ALL_SWITCHES }, + { "N201", PRI_TIMER_N201, PRI_ALL_SWITCHES }, + { "N202", PRI_TIMER_N202, PRI_ALL_SWITCHES }, + { "K", PRI_TIMER_K, PRI_ALL_SWITCHES }, + { "T200", PRI_TIMER_T200, PRI_ALL_SWITCHES }, + { "T202", PRI_TIMER_T202, PRI_ALL_SWITCHES }, + { "T203", PRI_TIMER_T203, PRI_ALL_SWITCHES }, + { "T300", PRI_TIMER_T300, PRI_ALL_SWITCHES }, + { "T301", PRI_TIMER_T301, PRI_ALL_SWITCHES }, + { "T302", PRI_TIMER_T302, PRI_ALL_SWITCHES }, + { "T303", PRI_TIMER_T303, PRI_ALL_SWITCHES }, + { "T304", PRI_TIMER_T304, PRI_ALL_SWITCHES }, + { "T305", PRI_TIMER_T305, PRI_ALL_SWITCHES }, + { "T306", PRI_TIMER_T306, PRI_ALL_SWITCHES }, + { "T307", PRI_TIMER_T307, PRI_ALL_SWITCHES }, + { "T308", PRI_TIMER_T308, PRI_ALL_SWITCHES }, + { "T309", PRI_TIMER_T309, PRI_ALL_SWITCHES }, + { "T310", PRI_TIMER_T310, PRI_ALL_SWITCHES }, + { "T313", PRI_TIMER_T313, PRI_ALL_SWITCHES }, + { "T314", PRI_TIMER_T314, PRI_ALL_SWITCHES }, + { "T316", PRI_TIMER_T316, PRI_ALL_SWITCHES }, + { "T317", PRI_TIMER_T317, PRI_ALL_SWITCHES }, + { "T318", PRI_TIMER_T318, PRI_ALL_SWITCHES }, + { "T319", PRI_TIMER_T319, PRI_ALL_SWITCHES }, + { "T320", PRI_TIMER_T320, PRI_ALL_SWITCHES }, + { "T321", PRI_TIMER_T321, PRI_ALL_SWITCHES }, + { "T322", PRI_TIMER_T322, PRI_ALL_SWITCHES }, + { "T-HOLD", PRI_TIMER_T_HOLD, PRI_ALL_SWITCHES }, + { "T-RETRIEVE", PRI_TIMER_T_RETRIEVE, PRI_ALL_SWITCHES }, + { "T-RESPONSE", PRI_TIMER_T_RESPONSE, PRI_ALL_SWITCHES }, +/* *INDENT-ON* */ +}; + char *pri_node2str(int node) { switch(node) { @@ -84,14 +132,39 @@ } } -static void pri_default_timers(struct pri *pri, int switchtype) +static void pri_default_timers(struct pri *ctrl, int switchtype) { - static const int defaulttimers[20][PRI_MAX_TIMERS] = PRI_TIMERS_ALL; - int x; + unsigned idx; - for (x = 0; xtimers[x] = defaulttimers[switchtype][x]; + /* Initialize all timers/counters to unsupported/disabled. */ + for (idx = 0; idx < PRI_MAX_TIMERS; ++idx) { + ctrl->timers[idx] = -1; } + + /* Set timer values to standard defaults. Time is in ms. */ + ctrl->timers[PRI_TIMER_N200] = 3; /* Max numer of Q.921 retransmissions */ + ctrl->timers[PRI_TIMER_N202] = 3; /* Max numer of transmissions of the TEI identity request message */ + ctrl->timers[PRI_TIMER_K] = 7; /* Max number of outstanding I-frames */ + ctrl->timers[PRI_TIMER_T200] = 1000; /* Time between SABME's */ + ctrl->timers[PRI_TIMER_T202] = 10 * 1000; /* Min time between transmission of TEI Identity request messages */ + ctrl->timers[PRI_TIMER_T203] = 10 * 1000; /* Max time without exchanging packets */ + ctrl->timers[PRI_TIMER_T305] = 30 * 1000; /* Wait for DISCONNECT acknowledge */ + ctrl->timers[PRI_TIMER_T308] = 4 * 1000; /* Wait for RELEASE acknowledge */ + ctrl->timers[PRI_TIMER_T313] = 4 * 1000; /* Wait for CONNECT acknowledge, CPE side only */ + ctrl->timers[PRI_TIMER_TM20] = 2500; /* Max time awaiting XID response - Q.921 Appendix IV */ + ctrl->timers[PRI_TIMER_NM20] = 3; /* Number of XID retransmits - Q.921 Appendix IV */ + ctrl->timers[PRI_TIMER_T303] = 4 * 1000; /* Length between SETUP retransmissions and timeout */ + + ctrl->timers[PRI_TIMER_T_HOLD] = 4 * 1000; /* Wait for HOLD request response. */ + ctrl->timers[PRI_TIMER_T_RETRIEVE] = 4 * 1000;/* Wait for RETRIEVE request response. */ + + ctrl->timers[PRI_TIMER_T_RESPONSE] = 4 * 1000; /* Maximum time to wait for a typical APDU response. */ + + /* Set any switch specific override default values */ + switch (switchtype) { + default: + break; + } } int pri_set_timer(struct pri *pri, int timer, int value) @@ -110,66 +183,30 @@ return pri->timers[timer]; } -int pri_timer2idx(char *timer) +int pri_set_service_message_support(struct pri *pri, int supportflag) { - if (!strcasecmp(timer, "N200")) - return PRI_TIMER_N200; - else if (!strcasecmp(timer, "N201")) - return PRI_TIMER_N201; - else if (!strcasecmp(timer, "N202")) - return PRI_TIMER_N202; - else if (!strcasecmp(timer, "K")) - return PRI_TIMER_K; - else if (!strcasecmp(timer, "T200")) - return PRI_TIMER_T200; - else if (!strcasecmp(timer, "T202")) - return PRI_TIMER_T202; - else if (!strcasecmp(timer, "T203")) - return PRI_TIMER_T203; - else if (!strcasecmp(timer, "T300")) - return PRI_TIMER_T300; - else if (!strcasecmp(timer, "T301")) - return PRI_TIMER_T301; - else if (!strcasecmp(timer, "T302")) - return PRI_TIMER_T302; - else if (!strcasecmp(timer, "T303")) - return PRI_TIMER_T303; - else if (!strcasecmp(timer, "T304")) - return PRI_TIMER_T304; - else if (!strcasecmp(timer, "T305")) - return PRI_TIMER_T305; - else if (!strcasecmp(timer, "T306")) - return PRI_TIMER_T306; - else if (!strcasecmp(timer, "T307")) - return PRI_TIMER_T307; - else if (!strcasecmp(timer, "T308")) - return PRI_TIMER_T308; - else if (!strcasecmp(timer, "T309")) - return PRI_TIMER_T309; - else if (!strcasecmp(timer, "T310")) - return PRI_TIMER_T310; - else if (!strcasecmp(timer, "T313")) - return PRI_TIMER_T313; - else if (!strcasecmp(timer, "T314")) - return PRI_TIMER_T314; - else if (!strcasecmp(timer, "T316")) - return PRI_TIMER_T316; - else if (!strcasecmp(timer, "T317")) - return PRI_TIMER_T317; - else if (!strcasecmp(timer, "T318")) - return PRI_TIMER_T318; - else if (!strcasecmp(timer, "T319")) - return PRI_TIMER_T319; - else if (!strcasecmp(timer, "T320")) - return PRI_TIMER_T320; - else if (!strcasecmp(timer, "T321")) - return PRI_TIMER_T321; - else if (!strcasecmp(timer, "T322")) - return PRI_TIMER_T322; - else + if (!pri) { return -1; + } + pri->service_message_support = supportflag; + return 0; } +int pri_timer2idx(const char *timer_name) +{ + unsigned idx; + enum PRI_TIMERS_AND_COUNTERS timer_number; + + timer_number = -1; + for (idx = 0; idx < ARRAY_LEN(pri_timer); ++idx) { + if (!strcasecmp(timer_name, pri_timer[idx].name)) { + timer_number = pri_timer[idx].number; + break; + } + } + return timer_number; +} + static int __pri_read(struct pri *pri, void *buf, int buflen) { int res = read(pri->fd, buf, buflen); @@ -192,18 +229,53 @@ return res; } -/* Pass in the master for this function */ void __pri_free_tei(struct pri * p) { - free (p); + if (p) { + struct q931_call *call; + + call = p->dummy_call; + if (call) { + pri_schedule_del(call->pri, call->retranstimer); + pri_call_apdu_queue_cleanup(call); + } + free(p->msg_line); + free(p); + } } struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, pri_io_cb rd, pri_io_cb wr, void *userdata, int tei, int bri) { + struct d_ctrl_dummy *dummy_ctrl; struct pri *p; - if (!(p = calloc(1, sizeof(*p)))) - return NULL; + switch (switchtype) { + case PRI_SWITCH_GR303_EOC: + case PRI_SWITCH_GR303_TMC: + case PRI_SWITCH_GR303_TMC_SWITCHING: + case PRI_SWITCH_GR303_EOC_PATH: + p = calloc(1, sizeof(*p)); + if (!p) { + return NULL; + } + dummy_ctrl = NULL; + break; + default: + dummy_ctrl = calloc(1, sizeof(*dummy_ctrl)); + if (!dummy_ctrl) { + return NULL; + } + p = &dummy_ctrl->ctrl; + break; + } + if (!master) { + /* This is the master record. */ + p->msg_line = calloc(1, sizeof(*p->msg_line)); + if (!p->msg_line) { + free(p); + return NULL; + } + } p->bri = bri; p->fd = fd; @@ -231,7 +303,14 @@ p->q931_rxcount = 0; p->q931_txcount = 0; #endif - if (switchtype == PRI_SWITCH_GR303_EOC) { + if (dummy_ctrl) { + /* Initialize the dummy call reference call record. */ + dummy_ctrl->ctrl.dummy_call = &dummy_ctrl->dummy_call; + q931_init_call_record(&dummy_ctrl->ctrl, dummy_ctrl->ctrl.dummy_call, + Q931_DUMMY_CALL_REFERENCE); + } + switch (switchtype) { + case PRI_SWITCH_GR303_EOC: p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_EOC; p->tei = Q921_TEI_GR303_EOC_OPS; @@ -240,7 +319,8 @@ free(p); p = NULL; } - } else if (switchtype == PRI_SWITCH_GR303_TMC) { + break; + case PRI_SWITCH_GR303_TMC: p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_TMC_CALLPROC; p->tei = Q921_TEI_GR303_TMC_CALLPROC; @@ -249,14 +329,19 @@ free(p); p = NULL; } - } else if (switchtype == PRI_SWITCH_GR303_TMC_SWITCHING) { + break; + case PRI_SWITCH_GR303_TMC_SWITCHING: p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_TMC_SWITCHING; p->tei = Q921_TEI_GR303_TMC_SWITCHING; - } else if (switchtype == PRI_SWITCH_GR303_EOC_PATH) { + break; + case PRI_SWITCH_GR303_EOC_PATH: p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_EOC; p->tei = Q921_TEI_GR303_EOC_PATH; + break; + default: + break; } /* Start Q.921 layer, Wait if we're the network */ if (p) @@ -327,44 +412,47 @@ char *pri_event2str(int id) { - switch(id) { - case PRI_EVENT_DCHAN_UP: - return "D-Channel Up"; - case PRI_EVENT_DCHAN_DOWN: - return "D-channel Down"; - case PRI_EVENT_RESTART: - return "Restart channel"; - case PRI_EVENT_RING: - return "Ring"; - case PRI_EVENT_HANGUP: - return "Hangup"; - case PRI_EVENT_RINGING: - return "Ringing"; - case PRI_EVENT_ANSWER: - return "Answer"; - case PRI_EVENT_HANGUP_ACK: - return "Hangup ACK"; - case PRI_EVENT_RESTART_ACK: - return "Restart ACK"; - case PRI_EVENT_FACNAME: - return "FacName"; - case PRI_EVENT_INFO_RECEIVED: - return "Info Received"; - case PRI_EVENT_PROCEEDING: - return "Proceeding"; - case PRI_EVENT_SETUP_ACK: - return "Setup ACK"; - case PRI_EVENT_HANGUP_REQ: - return "Hangup Req"; - case PRI_EVENT_NOTIFY: - return "Notify"; - case PRI_EVENT_PROGRESS: - return "Progress"; - case PRI_EVENT_CONFIG_ERR: - return "Configuration Error"; - default: - return "Unknown Event"; + unsigned idx; + struct { + int id; + char *name; + } events[] = { +/* *INDENT-OFF* */ + { PRI_EVENT_DCHAN_UP, "D-Channel Up" }, + { PRI_EVENT_DCHAN_DOWN, "D-channel Down" }, + { PRI_EVENT_RESTART, "Restart channel" }, + { PRI_EVENT_CONFIG_ERR, "Configuration Error" }, + { PRI_EVENT_RING, "Ring" }, + { PRI_EVENT_HANGUP, "Hangup" }, + { PRI_EVENT_RINGING, "Ringing" }, + { PRI_EVENT_ANSWER, "Answer" }, + { PRI_EVENT_HANGUP_ACK, "Hangup ACK" }, + { PRI_EVENT_RESTART_ACK, "Restart ACK" }, + { PRI_EVENT_FACILITY, "Facility" }, + { PRI_EVENT_INFO_RECEIVED, "Info Received" }, + { PRI_EVENT_PROCEEDING, "Proceeding" }, + { PRI_EVENT_SETUP_ACK, "Setup ACK" }, + { PRI_EVENT_HANGUP_REQ, "Hangup Req" }, + { PRI_EVENT_NOTIFY, "Notify" }, + { PRI_EVENT_PROGRESS, "Progress" }, + { PRI_EVENT_KEYPAD_DIGIT, "Keypad Digit" }, + { PRI_EVENT_SERVICE, "Service" }, + { PRI_EVENT_SERVICE_ACK, "Service ACK" }, + { PRI_EVENT_HOLD, "Hold" }, + { PRI_EVENT_HOLD_ACK, "Hold Ack" }, + { PRI_EVENT_HOLD_REJ, "Hold Rej" }, + { PRI_EVENT_RETRIEVE, "Retrieve" }, + { PRI_EVENT_RETRIEVE_ACK, "Retrieve ACK" }, + { PRI_EVENT_RETRIEVE_REJ, "Retrieve Rej" }, +/* *INDENT-ON* */ + }; + + for (idx = 0; idx < ARRAY_LEN(events); ++idx) { + if (events[idx].id == id) { + return events[idx].name; + } } + return "Unknown Event"; } pri_event *pri_check_event(struct pri *pri) @@ -506,7 +594,7 @@ return q931_information(pri, call, digit); } -int pri_keypad_facility(struct pri *pri, q931_call *call, char *digits) +int pri_keypad_facility(struct pri *pri, q931_call *call, const char *digits) { if (!pri || !call || !digits || !digits[0]) return -1; @@ -514,15 +602,6 @@ return q931_keypad_facility(pri, call, digits); } - -int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason) -{ - if (!pri || !call) - return -1; - - return qsig_cf_callrerouting(pri, call, dest, original, reason); -} - int pri_notify(struct pri *pri, q931_call *call, int channel, int info) { if (!pri || !call) @@ -533,7 +612,7 @@ void pri_destroycall(struct pri *pri, q931_call *call) { if (pri && call) - __q931_destroycall(pri, call); + q931_destroycall(pri, call); return; } @@ -551,6 +630,268 @@ return q931_connect(pri, call, channel, nonisdn); } +/*! + * \internal + * \brief Copy the PRI party name to the Q.931 party name structure. + * + * \param q931_name Q.931 party name structure + * \param pri_name PRI party name structure + * + * \return Nothing + */ +static void pri_copy_party_name_to_q931(struct q931_party_name *q931_name, const struct pri_party_name *pri_name) +{ + q931_party_name_init(q931_name); + if (pri_name->valid) { + q931_name->valid = 1; + q931_name->presentation = pri_name->presentation; + q931_name->char_set = pri_name->char_set; + libpri_copy_string(q931_name->str, pri_name->str, sizeof(q931_name->str)); + } +} + +/*! + * \internal + * \brief Copy the PRI party number to the Q.931 party number structure. + * + * \param q931_number Q.931 party number structure + * \param pri_number PRI party number structure + * + * \return Nothing + */ +static void pri_copy_party_number_to_q931(struct q931_party_number *q931_number, const struct pri_party_number *pri_number) +{ + q931_party_number_init(q931_number); + if (pri_number->valid) { + q931_number->valid = 1; + q931_number->presentation = pri_number->presentation; + q931_number->plan = pri_number->plan; + libpri_copy_string(q931_number->str, pri_number->str, sizeof(q931_number->str)); + } +} + +/*! + * \internal + * \brief Copy the PRI party subaddress to the Q.931 party subaddress structure. + * + * \param q931_subaddress Q.931 party subaddress structure + * \param pri_subaddress PRI party subaddress structure + * + * \return Nothing + */ +static void pri_copy_party_subaddress_to_q931(struct q931_party_subaddress *q931_subaddress, const struct pri_party_subaddress *pri_subaddress) +{ + int length; + int maxlen = sizeof(q931_subaddress->data) - 1; + + q931_party_subaddress_init(q931_subaddress); + + if (!pri_subaddress->valid) { + return; + } + + q931_subaddress->valid = 1; + q931_subaddress->type = pri_subaddress->type; + + length = pri_subaddress->length; + if (length > maxlen){ + length = maxlen; + } else { + q931_subaddress->odd_even_indicator = pri_subaddress->odd_even_indicator; + } + q931_subaddress->length = length; + memcpy(q931_subaddress->data, pri_subaddress->data, length); + q931_subaddress->data[length] = '\0'; +} + +/*! + * \internal + * \brief Copy the PRI party id to the Q.931 party id structure. + * + * \param q931_id Q.931 party id structure + * \param pri_id PRI party id structure + * + * \return Nothing + */ +static void pri_copy_party_id_to_q931(struct q931_party_id *q931_id, const struct pri_party_id *pri_id) +{ + pri_copy_party_name_to_q931(&q931_id->name, &pri_id->name); + pri_copy_party_number_to_q931(&q931_id->number, &pri_id->number); + pri_copy_party_subaddress_to_q931(&q931_id->subaddress, &pri_id->subaddress); +} + +int pri_connected_line_update(struct pri *ctrl, q931_call *call, const struct pri_party_connected_line *connected) +{ + struct q931_party_id party_id; + unsigned idx; + struct q931_call *subcall; + + if (!ctrl || !call) { + return -1; + } + + pri_copy_party_id_to_q931(&party_id, &connected->id); + q931_party_id_fixup(ctrl, &party_id); + if (!q931_party_id_cmp(&party_id, &call->local_id)) { + /* The local party information did not change so do nothing. */ + return 0; + } + call->local_id = party_id; + + /* Update all subcalls with new local_id. */ + if (call->outboundbroadcast && call->master_call == call) { + for (idx = 0; idx < Q931_MAX_TEI; ++idx) { + subcall = call->subcalls[idx]; + if (subcall) { + subcall->local_id = party_id; + } + } + } + + switch (call->ourcallstate) { + case Q931_CALL_STATE_CALL_INITIATED: + case Q931_CALL_STATE_OVERLAP_SENDING: + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + /* + * The local party transferred to someone else before + * the remote end answered. + */ + case Q931_CALL_STATE_ACTIVE: + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (q931_is_ptmp(ctrl)) { + /* PTMP mode */ + q931_notify_redirection(ctrl, call, PRI_NOTIFY_TRANSFER_ACTIVE, + &call->local_id.number); + } else { + /* PTP mode */ + /* Immediately send EctInform APDU, callStatus=answered(0) */ + send_call_transfer_complete(ctrl, call, 0); + } + break; + case PRI_SWITCH_QSIG: + /* Immediately send CallTransferComplete APDU, callStatus=answered(0) */ + send_call_transfer_complete(ctrl, call, 0); + break; + default: + break; + } + break; + default: + /* Just save the data for further developments. */ + break; + } + + return 0; +} + +int pri_redirecting_update(struct pri *ctrl, q931_call *call, const struct pri_party_redirecting *redirecting) +{ + unsigned idx; + struct q931_call *subcall; + + if (!ctrl || !call) { + return -1; + } + + /* Save redirecting.to information and reason. */ + pri_copy_party_id_to_q931(&call->redirecting.to, &redirecting->to); + q931_party_id_fixup(ctrl, &call->redirecting.to); + call->redirecting.reason = redirecting->reason; + + /* + * Update all subcalls with new redirecting.to information and reason. + * I do not think we will ever have any subcalls when this data is relevant, + * but update it just in case. + */ + if (call->outboundbroadcast && call->master_call == call) { + for (idx = 0; idx < Q931_MAX_TEI; ++idx) { + subcall = call->subcalls[idx]; + if (subcall) { + subcall->redirecting.to = call->redirecting.to; + subcall->redirecting.reason = redirecting->reason; + } + } + } + + switch (call->ourcallstate) { + case Q931_CALL_STATE_NULL: + /* Save the remaining redirecting information before we place a call. */ + pri_copy_party_id_to_q931(&call->redirecting.from, &redirecting->from); + q931_party_id_fixup(ctrl, &call->redirecting.from); + pri_copy_party_id_to_q931(&call->redirecting.orig_called, &redirecting->orig_called); + q931_party_id_fixup(ctrl, &call->redirecting.orig_called); + call->redirecting.orig_reason = redirecting->orig_reason; + if (redirecting->count <= 0) { + if (call->redirecting.from.number.valid) { + /* + * We are redirecting with an unknown count + * so assume the count is one. + */ + call->redirecting.count = 1; + } else { + call->redirecting.count = 0; + } + } else if (redirecting->count < PRI_MAX_REDIRECTS) { + call->redirecting.count = redirecting->count; + } else { + call->redirecting.count = PRI_MAX_REDIRECTS; + } + break; + case Q931_CALL_STATE_OVERLAP_RECEIVING: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_RECEIVED: + /* This is an incoming call that has not connected yet. */ + if (!call->redirecting.to.number.valid) { + /* Not being redirected toward valid number data. Ignore. */ + break; + } + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (q931_is_ptmp(ctrl)) { + /* PTMP mode */ + q931_notify_redirection(ctrl, call, PRI_NOTIFY_CALL_DIVERTING, + &call->redirecting.to.number); + break; + } + /* PTP mode - same behaviour as Q.SIG */ + /* fall through */ + case PRI_SWITCH_QSIG: + if (call->redirecting.state != Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3 + || strcmp(call->redirecting.to.number.str, call->called.number.str) != 0) { + /* immediately send divertingLegInformation1 APDU */ + if (rose_diverting_leg_information1_encode(ctrl, call) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for divertingLegInfo1\n"); + } + } + call->redirecting.state = Q931_REDIRECTING_STATE_IDLE; + + /* immediately send divertingLegInformation3 APDU */ + if (rose_diverting_leg_information3_encode(ctrl, call, Q931_FACILITY) + || q931_facility(ctrl, call)) { + pri_message(ctrl, + "Could not schedule facility message for divertingLegInfo3\n"); + } + break; + default: + break; + } + break; + default: + pri_message(ctrl, "Ignored redirecting update because call in state %s(%d).\n", + q931_call_state_str(call->ourcallstate), call->ourcallstate); + break; + } + + return 0; +} + #if 0 /* deprecated routines, use pri_hangup */ int pri_release(struct pri *pri, q931_call *call, int cause) @@ -619,7 +960,7 @@ return -1; if (cause == -1) /* normal clear cause */ - cause = 16; + cause = PRI_CAUSE_NORMAL_CLEARING; return q931_hangup(pri, call, cause); } @@ -630,6 +971,14 @@ return q931_restart(pri, channel); } +int pri_maintenance_service(struct pri *pri, int span, int channel, int changestatus) +{ + if (!pri) { + return -1; + } + return maintenance_service(pri, span, channel, changestatus); +} + q931_call *pri_new_call(struct pri *pri) { if (!pri) @@ -637,6 +986,14 @@ return q931_new_call(pri); } +int pri_is_dummy_call(q931_call *call) +{ + if (!call) { + return 0; + } + return q931_is_dummy_call(call); +} + void pri_dump_event(struct pri *pri, pri_event *e) { if (!pri || !e) @@ -667,7 +1024,10 @@ static void pri_sr_init(struct pri_sr *req) { memset(req, 0, sizeof(struct pri_sr)); - + q931_party_redirecting_init(&req->redirecting); + q931_party_id_init(&req->caller); + q931_party_address_init(&req->called); + req->reversecharge = PRI_REVERSECHARGE_NONE; } int pri_sr_set_connection_call_independent(struct pri_sr *req) @@ -675,10 +1035,21 @@ if (!req) return -1; - req->justsignalling = 1; /* have to set justsignalling for all those pesky IEs we need to setup */ + req->cis_call = 1; /* have to set cis_call for all those pesky IEs we need to setup */ + req->cis_auto_disconnect = 1; return 0; } +int pri_sr_set_no_channel_call(struct pri_sr *req) +{ + if (!req) { + return -1; + } + + req->cis_call = 1; + return 0; +} + /* Don't call any other pri functions on this */ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan) @@ -689,14 +1060,9 @@ pri_sr_init(&req); pri_sr_set_connection_call_independent(&req); + pri_sr_set_caller(&req, caller, callername, callerplan, callerpres); + pri_sr_set_called(&req, called, calledplan, 0); - req.caller = caller; - req.callerplan = callerplan; - req.callername = callername; - req.callerpres = callerpres; - req.called = called; - req.calledplan = calledplan; - if (mwi_message_send(pri, c, &req, 1) < 0) { pri_message(pri, "Unable to send MWI activate message\n"); return -1; @@ -714,14 +1080,9 @@ pri_sr_init(&req); pri_sr_set_connection_call_independent(&req); + pri_sr_set_caller(&req, caller, callername, callerplan, callerpres); + pri_sr_set_called(&req, called, calledplan, 0); - req.caller = caller; - req.callerplan = callerplan; - req.callername = callername; - req.callerpres = callerpres; - req.called = called; - req.calledplan = calledplan; - if(mwi_message_send(pri, c, &req, 0) < 0) { pri_message(pri, "Unable to send MWI deactivate message\n"); return -1; @@ -740,22 +1101,18 @@ int pri_call(struct pri *pri, q931_call *c, int transmode, int channel, int exclusive, int nonisdn, char *caller, int callerplan, char *callername, int callerpres, char *called, - int calledplan,int ulayer1) + int calledplan, int ulayer1) { struct pri_sr req; if (!pri || !c) return -1; pri_sr_init(&req); + pri_sr_set_caller(&req, caller, callername, callerplan, callerpres); + pri_sr_set_called(&req, called, calledplan, 0); req.transmode = transmode; req.channel = channel; req.exclusive = exclusive; req.nonisdn = nonisdn; - req.caller = caller; - req.callerplan = callerplan; - req.callername = callername; - req.callerpres = callerpres; - req.called = called; - req.calledplan = calledplan; req.userl1 = ulayer1; return q931_setup(pri, c, &req); } @@ -773,28 +1130,85 @@ __pri_error = func; } -void pri_message(struct pri *pri, char *fmt, ...) +static void pri_old_message(struct pri *ctrl, const char *fmt, va_list *ap) { char tmp[1024]; - va_list ap; - va_start(ap, fmt); - vsnprintf(tmp, sizeof(tmp), fmt, ap); - va_end(ap); + + vsnprintf(tmp, sizeof(tmp), fmt, *ap); if (__pri_message) - __pri_message(pri, tmp); + __pri_message(ctrl, tmp); else fputs(tmp, stdout); } -void pri_error(struct pri *pri, char *fmt, ...) +void pri_message(struct pri *ctrl, const char *fmt, ...) { + int added_length; + va_list ap; + + ctrl = PRI_MASTER(ctrl); + if (!ctrl || !ctrl->msg_line) { + /* Just have to do it the old way. */ + va_start(ap, fmt); + pri_old_message(ctrl, fmt, &ap); + va_end(ap); + return; + } + + va_start(ap, fmt); + added_length = vsnprintf(ctrl->msg_line->str + ctrl->msg_line->length, + sizeof(ctrl->msg_line->str) - ctrl->msg_line->length, fmt, ap); + va_end(ap); + if (added_length < 0 + || sizeof(ctrl->msg_line->str) <= ctrl->msg_line->length + added_length) { + static char truncated_output[] = + "v-- Error building output or output was truncated. (Next line) --v\n"; + + /* + * This clause should never need to run because the + * output line accumulation buffer is quite large. + */ + + /* vsnprintf() error or output string was truncated. */ + if (__pri_message) { + __pri_message(ctrl, truncated_output); + } else { + fputs(truncated_output, stdout); + } + + /* Add a terminating '\n' to force a flush of the line. */ + ctrl->msg_line->length = strlen(ctrl->msg_line->str); + if (ctrl->msg_line->length) { + ctrl->msg_line->str[ctrl->msg_line->length - 1] = '\n'; + } else { + ctrl->msg_line->str[0] = '\n'; + ctrl->msg_line->str[1] = '\0'; + } + } else { + ctrl->msg_line->length += added_length; + } + + if (ctrl->msg_line->length + && ctrl->msg_line->str[ctrl->msg_line->length - 1] == '\n') { + /* The accumulated output line was terminated so send it out. */ + ctrl->msg_line->length = 0; + if (__pri_message) { + __pri_message(ctrl, ctrl->msg_line->str); + } else { + fputs(ctrl->msg_line->str, stdout); + } + } +} + +void pri_error(struct pri *pri, const char *fmt, ...) +{ char tmp[1024]; va_list ap; va_start(ap, fmt); vsnprintf(tmp, sizeof(tmp), fmt, ap); va_end(ap); if (__pri_error) - __pri_error(pri, tmp); + __pri_error(PRI_MASTER(pri), tmp); else fputs(tmp, stderr); } @@ -821,49 +1235,102 @@ return pri->fd; } -char *pri_dump_info_str(struct pri *pri) +/*! + * \internal + * \brief Append snprintf output to the given buffer. + * + * \param buf Buffer currently filling. + * \param buf_used Offset into buffer where to put new stuff. + * \param buf_size Actual buffer size of buf. + * \param format printf format string. + * + * \return Total buffer space used. + */ +static size_t pri_snprintf(char *buf, size_t buf_used, size_t buf_size, const char *format, ...) __attribute__((format(printf, 4, 5))); +static size_t pri_snprintf(char *buf, size_t buf_used, size_t buf_size, const char *format, ...) { - char buf[4096]; - int len = 0; + va_list args; + + if (buf_used < buf_size) { + va_start(args, format); + buf_used += vsnprintf(buf + buf_used, buf_size - buf_used, format, args); + va_end(args); + } + if (buf_size < buf_used) { + buf_used = buf_size + 1; + } + return buf_used; +} + +char *pri_dump_info_str(struct pri *ctrl) +{ + char *buf; + size_t buf_size; + size_t used; #ifdef LIBPRI_COUNTERS struct q921_frame *f; - int q921outstanding = 0; + unsigned q921outstanding; #endif - if (!pri) + unsigned idx; + unsigned long switch_bit; + + if (!ctrl) { return NULL; + } + buf_size = 4096; /* This should be bigger than we will ever need. */ + buf = malloc(buf_size); + if (!buf) { + return NULL; + } + /* Might be nice to format these a little better */ - len += sprintf(buf + len, "Switchtype: %s\n", pri_switch2str(pri->switchtype)); - len += sprintf(buf + len, "Type: %s\n", pri_node2str(pri->localtype)); + used = 0; + used = pri_snprintf(buf, used, buf_size, "Switchtype: %s\n", + pri_switch2str(ctrl->switchtype)); + used = pri_snprintf(buf, used, buf_size, "Type: %s\n", pri_node2str(ctrl->localtype)); #ifdef LIBPRI_COUNTERS /* Remember that Q921 Counters include Q931 packets (and any retransmissions) */ - len += sprintf(buf + len, "Q931 RX: %d\n", pri->q931_rxcount); - len += sprintf(buf + len, "Q931 TX: %d\n", pri->q931_txcount); - len += sprintf(buf + len, "Q921 RX: %d\n", pri->q921_rxcount); - len += sprintf(buf + len, "Q921 TX: %d\n", pri->q921_txcount); - f = pri->txqueue; + used = pri_snprintf(buf, used, buf_size, "Q931 RX: %d\n", ctrl->q931_rxcount); + used = pri_snprintf(buf, used, buf_size, "Q931 TX: %d\n", ctrl->q931_txcount); + used = pri_snprintf(buf, used, buf_size, "Q921 RX: %d\n", ctrl->q921_rxcount); + used = pri_snprintf(buf, used, buf_size, "Q921 TX: %d\n", ctrl->q921_txcount); + q921outstanding = 0; + f = ctrl->txqueue; while (f) { q921outstanding++; f = f->next; } - len += sprintf(buf + len, "Q921 Outstanding: %d\n", q921outstanding); + used = pri_snprintf(buf, used, buf_size, "Q921 Outstanding: %u\n", q921outstanding); #endif - len += sprintf(buf + len, "Window Length: %d/%d\n", pri->windowlen, pri->window); - len += sprintf(buf + len, "Sentrej: %d\n", pri->sentrej); - len += sprintf(buf + len, "SolicitFbit: %d\n", pri->solicitfbit); - len += sprintf(buf + len, "Retrans: %d\n", pri->retrans); - len += sprintf(buf + len, "Busy: %d\n", pri->busy); - len += sprintf(buf + len, "Overlap Dial: %d\n", pri->overlapdial); - len += sprintf(buf + len, "Logical Channel Mapping: %d\n", pri->chan_mapping_logical); - len += sprintf(buf + len, "T200 Timer: %d\n", pri->timers[PRI_TIMER_T200]); - len += sprintf(buf + len, "T203 Timer: %d\n", pri->timers[PRI_TIMER_T203]); - len += sprintf(buf + len, "T305 Timer: %d\n", pri->timers[PRI_TIMER_T305]); - len += sprintf(buf + len, "T308 Timer: %d\n", pri->timers[PRI_TIMER_T308]); - len += sprintf(buf + len, "T309 Timer: %d\n", pri->timers[PRI_TIMER_T309]); - len += sprintf(buf + len, "T313 Timer: %d\n", pri->timers[PRI_TIMER_T313]); - len += sprintf(buf + len, "N200 Counter: %d\n", pri->timers[PRI_TIMER_N200]); + used = pri_snprintf(buf, used, buf_size, "Window Length: %d/%d\n", ctrl->windowlen, + ctrl->window); + used = pri_snprintf(buf, used, buf_size, "Sentrej: %d\n", ctrl->sentrej); + used = pri_snprintf(buf, used, buf_size, "SolicitFbit: %d\n", ctrl->solicitfbit); + used = pri_snprintf(buf, used, buf_size, "Retrans: %d\n", ctrl->retrans); + used = pri_snprintf(buf, used, buf_size, "Busy: %d\n", ctrl->busy); + used = pri_snprintf(buf, used, buf_size, "Overlap Dial: %d\n", ctrl->overlapdial); + used = pri_snprintf(buf, used, buf_size, "Logical Channel Mapping: %d\n", + ctrl->chan_mapping_logical); + used = pri_snprintf(buf, used, buf_size, "Timer and counter settings:\n"); + switch_bit = PRI_BIT(ctrl->switchtype); + for (idx = 0; idx < ARRAY_LEN(pri_timer); ++idx) { + if (pri_timer[idx].used_by & switch_bit) { + enum PRI_TIMERS_AND_COUNTERS tmr; - return strdup(buf); + tmr = pri_timer[idx].number; + if (0 <= ctrl->timers[tmr] || tmr == PRI_TIMER_T309) { + used = pri_snprintf(buf, used, buf_size, " %s: %d\n", + pri_timer[idx].name, ctrl->timers[tmr]); + } + } + } + + if (buf_size < used) { + pri_message(ctrl, + "pri_dump_info_str(): Produced output exceeded buffer capacity. (Truncated)\n"); + } + return buf; } int pri_get_crv(struct pri *pri, q931_call *call, int *callmode) @@ -913,26 +1380,213 @@ int pri_sr_set_called(struct pri_sr *sr, char *called, int calledplan, int numcomplete) { - sr->called = called; - sr->calledplan = calledplan; + q931_party_address_init(&sr->called); + if (called) { + sr->called.number.valid = 1; + sr->called.number.plan = calledplan; + libpri_copy_string(sr->called.number.str, called, sizeof(sr->called.number.str)); + } sr->numcomplete = numcomplete; return 0; } +void pri_sr_set_called_subaddress(struct pri_sr *sr, const struct pri_party_subaddress *subaddress) +{ + pri_copy_party_subaddress_to_q931(&sr->called.subaddress, subaddress); +} + int pri_sr_set_caller(struct pri_sr *sr, char *caller, char *callername, int callerplan, int callerpres) { - sr->caller = caller; - sr->callername = callername; - sr->callerplan = callerplan; - sr->callerpres = callerpres; + q931_party_id_init(&sr->caller); + if (caller) { + sr->caller.number.valid = 1; + sr->caller.number.presentation = callerpres; + sr->caller.number.plan = callerplan; + libpri_copy_string(sr->caller.number.str, caller, sizeof(sr->caller.number.str)); + + if (callername) { + sr->caller.name.valid = 1; + sr->caller.name.presentation = callerpres; + sr->caller.name.char_set = PRI_CHAR_SET_ISO8859_1; + libpri_copy_string(sr->caller.name.str, callername, + sizeof(sr->caller.name.str)); + } + } return 0; } +void pri_sr_set_caller_subaddress(struct pri_sr *sr, const struct pri_party_subaddress *subaddress) +{ + pri_copy_party_subaddress_to_q931(&sr->caller.subaddress, subaddress); +} + +void pri_sr_set_caller_party(struct pri_sr *sr, const struct pri_party_id *caller) +{ + pri_copy_party_id_to_q931(&sr->caller, caller); +} + int pri_sr_set_redirecting(struct pri_sr *sr, char *num, int plan, int pres, int reason) { - sr->redirectingnum = num; - sr->redirectingplan = plan; - sr->redirectingpres = pres; - sr->redirectingreason = reason; + q931_party_redirecting_init(&sr->redirecting); + if (num && num[0]) { + sr->redirecting.from.number.valid = 1; + sr->redirecting.from.number.presentation = pres; + sr->redirecting.from.number.plan = plan; + libpri_copy_string(sr->redirecting.from.number.str, num, + sizeof(sr->redirecting.from.number.str)); + + sr->redirecting.count = 1; + sr->redirecting.reason = reason; + } return 0; } + +void pri_sr_set_redirecting_parties(struct pri_sr *sr, const struct pri_party_redirecting *redirecting) +{ + pri_copy_party_id_to_q931(&sr->redirecting.from, &redirecting->from); + pri_copy_party_id_to_q931(&sr->redirecting.to, &redirecting->to); + pri_copy_party_id_to_q931(&sr->redirecting.orig_called, &redirecting->orig_called); + sr->redirecting.orig_reason = redirecting->orig_reason; + sr->redirecting.reason = redirecting->reason; + if (redirecting->count <= 0) { + if (sr->redirecting.from.number.valid) { + /* + * We are redirecting with an unknown count + * so assume the count is one. + */ + sr->redirecting.count = 1; + } else { + sr->redirecting.count = 0; + } + } else if (redirecting->count < PRI_MAX_REDIRECTS) { + sr->redirecting.count = redirecting->count; + } else { + sr->redirecting.count = PRI_MAX_REDIRECTS; + } +} + +void pri_sr_set_reversecharge(struct pri_sr *sr, int requested) +{ + sr->reversecharge = requested; +} + +void pri_sr_set_keypad_digits(struct pri_sr *sr, const char *keypad_digits) +{ + sr->keypad_digits = keypad_digits; +} + +void pri_hold_enable(struct pri *ctrl, int enable) +{ + ctrl = PRI_MASTER(ctrl); + if (ctrl) { + ctrl->hold_support = enable ? 1 : 0; + } +} + +int pri_hold(struct pri *ctrl, q931_call *call) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_hold(ctrl, call); +} + +int pri_hold_ack(struct pri *ctrl, q931_call *call) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_hold_ack(ctrl, call); +} + +int pri_hold_rej(struct pri *ctrl, q931_call *call, int cause) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_hold_rej(ctrl, call, cause); +} + +int pri_retrieve(struct pri *ctrl, q931_call *call, int channel) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_retrieve(ctrl, call, channel); +} + +int pri_retrieve_ack(struct pri *ctrl, q931_call *call, int channel) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_retrieve_ack(ctrl, call, channel); +} + +int pri_retrieve_rej(struct pri *ctrl, q931_call *call, int cause) +{ + if (!ctrl || !call) { + return -1; + } + return q931_send_retrieve_rej(ctrl, call, cause); +} + +int pri_callrerouting_facility(struct pri *pri, q931_call *call, const char *dest, const char* original, const char* reason) +{ + if (!pri || !call || !dest) + return -1; + + return qsig_cf_callrerouting(pri, call, dest, original, reason); +} + +void pri_reroute_enable(struct pri *ctrl, int enable) +{ + ctrl = PRI_MASTER(ctrl); + if (ctrl) { + ctrl->deflection_support = enable ? 1 : 0; + } +} + +int pri_reroute_call(struct pri *ctrl, q931_call *call, const struct pri_party_id *caller, const struct pri_party_redirecting *deflection, int subscription_option) +{ + const struct q931_party_id *caller_id; + struct q931_party_id local_caller; + struct q931_party_redirecting reroute; + + if (!ctrl || !call || !deflection) { + return -1; + } + + if (caller) { + /* Convert the caller update information. */ + pri_copy_party_id_to_q931(&local_caller, caller); + q931_party_id_fixup(ctrl, &local_caller); + caller_id = &local_caller; + } else { + caller_id = NULL; + } + + /* Convert the deflection information. */ + q931_party_redirecting_init(&reroute); + pri_copy_party_id_to_q931(&reroute.from, &deflection->from); + q931_party_id_fixup(ctrl, &reroute.from); + pri_copy_party_id_to_q931(&reroute.to, &deflection->to); + q931_party_id_fixup(ctrl, &reroute.to); + pri_copy_party_id_to_q931(&reroute.orig_called, &deflection->orig_called); + q931_party_id_fixup(ctrl, &reroute.orig_called); + reroute.reason = deflection->reason; + reroute.orig_reason = deflection->orig_reason; + if (deflection->count <= 0) { + /* + * We are deflecting with an unknown count + * so assume the count is one. + */ + reroute.count = 1; + } else if (deflection->count < PRI_MAX_REDIRECTS) { + reroute.count = deflection->count; + } else { + reroute.count = PRI_MAX_REDIRECTS; + } + + return send_reroute_request(ctrl, call, caller_id, &reroute, subscription_option); +} Index: Makefile =================================================================== --- a/Makefile (.../tags/1.4.10.2) (revision 1357) +++ b/Makefile (.../branches/1.4) (revision 1357) @@ -41,15 +41,55 @@ STATIC_LIBRARY=libpri.a DYNAMIC_LIBRARY:=libpri.so.$(SONAME) -STATIC_OBJS=copy_string.o pri.o q921.o prisched.o q931.o pri_facility.o version.o -DYNAMIC_OBJS=copy_string.lo pri.lo q921.lo prisched.lo q931.lo pri_facility.lo version.lo -CFLAGS=-Wall -Werror -Wstrict-prototypes -Wmissing-prototypes -g -fPIC $(ALERTING) $(LIBPRI_COUNTERS) +STATIC_OBJS= \ + copy_string.o \ + pri.o \ + q921.o \ + prisched.o \ + q931.o \ + pri_facility.o \ + asn1_primitive.o \ + rose.o \ + rose_address.o \ + rose_etsi_aoc.o \ + rose_etsi_diversion.o \ + rose_etsi_ect.o \ + rose_other.o \ + rose_q931.o \ + rose_qsig_aoc.o \ + rose_qsig_ct.o \ + rose_qsig_diversion.o \ + rose_qsig_mwi.o \ + rose_qsig_name.o \ + version.o +DYNAMIC_OBJS= \ + copy_string.lo \ + pri.lo \ + q921.lo \ + prisched.lo \ + q931.lo \ + pri_facility.lo \ + asn1_primitive.lo \ + rose.lo \ + rose_address.lo \ + rose_etsi_aoc.lo \ + rose_etsi_diversion.lo \ + rose_etsi_ect.lo \ + rose_other.lo \ + rose_q931.lo \ + rose_qsig_aoc.lo \ + rose_qsig_ct.lo \ + rose_qsig_diversion.lo \ + rose_qsig_mwi.lo \ + rose_qsig_name.lo \ + version.lo +CFLAGS+=-Wall -Werror -Wstrict-prototypes -Wmissing-prototypes -g -fPIC $(ALERTING) $(LIBPRI_COUNTERS) $(LIBPRI_OPT) INSTALL_PREFIX=$(DESTDIR) INSTALL_BASE=/usr libdir?=$(INSTALL_BASE)/lib SOFLAGS:=-Wl,-h$(DYNAMIC_LIBRARY) LDCONFIG = /sbin/ldconfig -ifneq (,$(findstring X$(OSARCH)X, XLinuxX XGNU/kFreeBSDX)) +ifneq (,$(findstring X$(OSARCH)X, XLinuxX XGNU/kFreeBSDX XGNUX)) LDCONFIG_FLAGS=-n else ifeq (${OSARCH},FreeBSD) @@ -74,7 +114,9 @@ #A ultrasparc cpu is really v9 but the stock debian stable 3.0 gcc doesnt support it. ifeq ($(PROC),sparc64) PROC=ultrasparc -CFLAGS += -mtune=$(PROC) -O3 -pipe -fomit-frame-pointer -mcpu=v8 +LIBPRI_OPT = -mtune=$(PROC) -O3 -pipe -fomit-frame-pointer -mcpu=v8 +else +LIBPRI_OPT = -O2 endif all: $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY) @@ -132,6 +174,9 @@ pridump: pridump.o $(CC) -o pridump pridump.o -L. -lpri $(CFLAGS) +rosetest: rosetest.o + $(CC) -o rosetest rosetest.o -L. -lpri $(CFLAGS) + MAKE_DEPS= -MD -MT $@ -MF .$(subst /,_,$@).d -MP %.o: %.c Index: q931.c =================================================================== --- a/q931.c (.../tags/1.4.10.2) (revision 1357) +++ b/q931.c (.../branches/1.4) (revision 1357) @@ -33,6 +33,7 @@ #include "pri_q921.h" #include "pri_q931.h" #include "pri_facility.h" +#include "rose.h" #include #include @@ -67,7 +68,7 @@ { Q931_RESTART_ACKNOWLEDGE, "RESTART ACKNOWLEDGE", { Q931_RESTART_INDICATOR } }, /* Miscellaneous */ - { Q931_STATUS, "STATUS", { Q931_CAUSE, Q931_CALL_STATE } }, + { Q931_STATUS, "STATUS", { Q931_CAUSE, Q931_IE_CALL_STATE } }, { Q931_STATUS_ENQUIRY, "STATUS ENQUIRY" }, { Q931_USER_INFORMATION, "USER_INFORMATION" }, { Q931_SEGMENT, "SEGMENT" }, @@ -79,22 +80,32 @@ /* Call Management */ { Q931_HOLD, "HOLD" }, { Q931_HOLD_ACKNOWLEDGE, "HOLD ACKNOWLEDGE" }, - { Q931_HOLD_REJECT, "HOLD REJECT" }, + { Q931_HOLD_REJECT, "HOLD REJECT", { Q931_CAUSE } }, { Q931_RETRIEVE, "RETRIEVE" }, { Q931_RETRIEVE_ACKNOWLEDGE, "RETRIEVE ACKNOWLEDGE" }, - { Q931_RETRIEVE_REJECT, "RETRIEVE REJECT" }, + { Q931_RETRIEVE_REJECT, "RETRIEVE REJECT", { Q931_CAUSE } }, { Q931_RESUME, "RESUME" }, { Q931_RESUME_ACKNOWLEDGE, "RESUME ACKNOWLEDGE", { Q931_CHANNEL_IDENT } }, { Q931_RESUME_REJECT, "RESUME REJECT", { Q931_CAUSE } }, { Q931_SUSPEND, "SUSPEND" }, { Q931_SUSPEND_ACKNOWLEDGE, "SUSPEND ACKNOWLEDGE" }, { Q931_SUSPEND_REJECT, "SUSPEND REJECT" }, +}; - /* Maintenance */ - { NATIONAL_SERVICE, "SERVICE" }, - { NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE" }, +static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand); +static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int *allow_event, int *allow_posthandle); + +struct msgtype att_maintenance_msgs[] = { + { ATT_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } }, + { ATT_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE", { Q931_CHANNEL_IDENT } }, }; +struct msgtype national_maintenance_msgs[] = { + { NATIONAL_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } }, + { NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE", { Q931_CHANNEL_IDENT } }, +}; +static int post_handle_maintenance_message(struct pri *ctrl, int protodisc, struct q931_mh *mh, struct q931_call *c); + static struct msgtype causes[] = { { PRI_CAUSE_UNALLOCATED, "Unallocated (unassigned) number" }, { PRI_CAUSE_NO_ROUTE_TRANSIT_NET, "No route to specified transmit network" }, @@ -107,6 +118,7 @@ { PRI_CAUSE_NO_ANSWER, "User alerting, no answer" }, { PRI_CAUSE_CALL_REJECTED, "Call Rejected" }, { PRI_CAUSE_NUMBER_CHANGED, "Number changed" }, + { PRI_CAUSE_NONSELECTED_USER_CLEARING, "Non-selected user clearing" }, { PRI_CAUSE_DESTINATION_OUT_OF_ORDER, "Destination out of order" }, { PRI_CAUSE_INVALID_NUMBER_FORMAT, "Invalid number format" }, { PRI_CAUSE_FACILITY_REJECTED, "Facility rejected" }, @@ -119,13 +131,14 @@ { PRI_CAUSE_ACCESS_INFO_DISCARDED, "Access information discarded" }, { PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, "Requested channel not available" }, { PRI_CAUSE_PRE_EMPTED, "Pre-empted" }, + { PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED, "Resource unavailable, unspecified" }, { PRI_CAUSE_FACILITY_NOT_SUBSCRIBED, "Facility not subscribed" }, { PRI_CAUSE_OUTGOING_CALL_BARRED, "Outgoing call barred" }, { PRI_CAUSE_INCOMING_CALL_BARRED, "Incoming call barred" }, { PRI_CAUSE_BEARERCAPABILITY_NOTAUTH, "Bearer capability not authorized" }, { PRI_CAUSE_BEARERCAPABILITY_NOTAVAIL, "Bearer capability not available" }, + { PRI_CAUSE_SERVICEOROPTION_NOTAVAIL, "Service or option not available, unspecified" }, { PRI_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented" }, - { PRI_CAUSE_SERVICEOROPTION_NOTAVAIL, "Service or option not available, unspecified" }, { PRI_CAUSE_CHAN_NOT_IMPLEMENTED, "Channel not implemented" }, { PRI_CAUSE_FACILITY_NOT_IMPLEMENTED, "Facility not implemented" }, { PRI_CAUSE_INVALID_CALL_REFERENCE, "Invalid call reference value" }, @@ -163,8 +176,9 @@ { PRI_NSF_CALL_REDIRECTION_SERVICE, "Call Redirection Service" } }; -#define FLAG_PREFERRED 2 -#define FLAG_EXCLUSIVE 4 +#define FLAG_WHOLE_INTERFACE 0x01 +#define FLAG_PREFERRED 0x02 +#define FLAG_EXCLUSIVE 0x04 #define RESET_INDICATOR_CHANNEL 0 #define RESET_INDICATOR_DS1 6 @@ -214,26 +228,49 @@ #define LOC_NETWORK_BEYOND_INTERWORKING 0xa static char *ie2str(int ie); -static char *msg2str(int msg); -#define FUNC_DUMP(name) void ((name))(int full_ie, struct pri *pri, q931_ie *ie, int len, char prefix) -#define FUNC_RECV(name) int ((name))(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len) -#define FUNC_SEND(name) int ((name))(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len, int order) +#define FUNC_DUMP(name) void (name)(int full_ie, struct pri *pri, q931_ie *ie, int len, char prefix) +#define FUNC_RECV(name) int (name)(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len) +#define FUNC_SEND(name) int (name)(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len, int order) #if 1 /* Update call state with transition trace. */ -#define UPDATE_OURCALLSTATE(pri,c,newstate) do {\ - if (pri->debug & (PRI_DEBUG_Q931_STATE) && c->ourcallstate != newstate) \ - pri_message(pri, DBGHEAD "call %d on channel %d enters state %d (%s)\n", DBGINFO, \ - c->cr, c->channelno, newstate, callstate2str(newstate)); \ - c->ourcallstate = newstate; \ +#define UPDATE_OURCALLSTATE(ctrl, call, newstate) \ + do { \ + if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) && (call)->ourcallstate != (newstate)) { \ + pri_message((ctrl), \ + DBGHEAD "%s %d enters state %d (%s). Hold state: %s\n", \ + DBGINFO, ((call) == (call)->master_call) ? "Call" : "Subcall", \ + (call)->cr, (newstate), q931_call_state_str(newstate), \ + q931_hold_state_str((call)->master_call->hold_state)); \ + } \ + (call)->ourcallstate = (newstate); \ } while (0) #else /* Update call state with no trace. */ -#define UPDATE_OURCALLSTATE(pri,c,newstate) c->ourcallstate = newstate +#define UPDATE_OURCALLSTATE(ctrl, call, newstate) (call)->ourcallstate = (newstate) #endif +#if 1 +/* Update hold state with transition trace. */ +#define UPDATE_HOLD_STATE(ctrl, master_call, newstate) \ + do { \ + if (((ctrl)->debug & PRI_DEBUG_Q931_STATE) \ + && (master_call)->hold_state != (newstate)) { \ + pri_message((ctrl), \ + DBGHEAD "Call %d in state %d (%s) enters Hold state: %s\n", \ + DBGINFO, (master_call)->cr, (master_call)->ourcallstate, \ + q931_call_state_str((master_call)->ourcallstate), \ + q931_hold_state_str(newstate)); \ + } \ + (master_call)->hold_state = (newstate); \ + } while (0) +#else +/* Update hold state with no trace. */ +#define UPDATE_HOLD_STATE(ctrl, master_call, newstate) (master_call)->hold_state = (newstate) +#endif + struct ie { /* Maximal count of same IEs at the message (0 - any, 1..n - limited) */ int max_count; @@ -249,6 +286,506 @@ FUNC_SEND(*transmit); }; +/*! + * \internal + * \brief Encode the channel id information to pass to upper level. + * + * \param call Q.931 call leg + * + * \return Encoded channel value. + */ +static int q931_encode_channel(const q931_call *call) +{ + int held_call; + int channelno; + int ds1no; + + switch (call->master_call->hold_state) { + case Q931_HOLD_STATE_CALL_HELD: + case Q931_HOLD_STATE_RETRIEVE_REQ: + case Q931_HOLD_STATE_RETRIEVE_IND: + held_call = 1 << 18; + + /* So a -1 does not wipe out the held_call flag. */ + channelno = call->channelno & 0xFF; + ds1no = call->ds1no & 0xFF; + break; + default: + held_call = 0; + channelno = call->channelno; + ds1no = call->ds1no; + break; + } + return channelno | (ds1no << 8) | (call->ds1explicit << 16) | (call->cis_call << 17) + | held_call; +} + +/*! + * \brief Determine if layer 2 is in PTMP mode. + * + * \param ctrl D channel controller. + * + * \retval TRUE if in PTMP mode. + * \retval FALSE otherwise. + */ +int q931_is_ptmp(const struct pri *ctrl) +{ + /* Check master control structure */ + for (; ctrl->master; ctrl = ctrl->master) { + } + return ctrl->tei == Q921_TEI_GROUP; +} + +/*! + * \brief Initialize the given struct q931_party_name + * + * \param name Structure to initialize + * + * \return Nothing + */ +void q931_party_name_init(struct q931_party_name *name) +{ + name->valid = 0; + name->presentation = PRI_PRES_UNAVAILABLE; + name->char_set = PRI_CHAR_SET_ISO8859_1; + name->str[0] = '\0'; +} + +/*! + * \brief Initialize the given struct q931_party_number + * + * \param number Structure to initialize + * + * \return Nothing + */ +void q931_party_number_init(struct q931_party_number *number) +{ + number->valid = 0; + number->presentation = PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED; + number->plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + number->str[0] = '\0'; +} + +/*! + * \brief Initialize the given struct q931_party_subaddress + * + * \param subaddress Structure to initialize + * + * \return Nothing + */ +void q931_party_subaddress_init(struct q931_party_subaddress *subaddress) +{ + subaddress->valid = 0; + subaddress->type = 0; + subaddress->odd_even_indicator = 0; + subaddress->length = 0; + subaddress->data[0] = '\0'; +} + +/*! + * \brief Initialize the given struct q931_party_address + * + * \param address Structure to initialize + * + * \return Nothing + */ +void q931_party_address_init(struct q931_party_address *address) +{ + q931_party_number_init(&address->number); + q931_party_subaddress_init(&address->subaddress); +} + +/*! + * \brief Initialize the given struct q931_party_id + * + * \param id Structure to initialize + * + * \return Nothing + */ +void q931_party_id_init(struct q931_party_id *id) +{ + q931_party_name_init(&id->name); + q931_party_number_init(&id->number); + q931_party_subaddress_init(&id->subaddress); +} + +/*! + * \brief Initialize the given struct q931_party_redirecting + * + * \param redirecting Structure to initialize + * + * \return Nothing + */ +void q931_party_redirecting_init(struct q931_party_redirecting *redirecting) +{ + q931_party_id_init(&redirecting->from); + q931_party_id_init(&redirecting->to); + q931_party_id_init(&redirecting->orig_called); + redirecting->state = Q931_REDIRECTING_STATE_IDLE; + redirecting->count = 0; + redirecting->orig_reason = PRI_REDIR_UNKNOWN; + redirecting->reason = PRI_REDIR_UNKNOWN; +} + +/*! + * \brief Compare the left and right party name. + * + * \param left Left parameter party name. + * \param right Right parameter party name. + * + * \retval < 0 when left < right. + * \retval == 0 when left == right. + * \retval > 0 when left > right. + */ +int q931_party_name_cmp(const struct q931_party_name *left, const struct q931_party_name *right) +{ + int cmp; + + if (!left->valid) { + if (!right->valid) { + return 0; + } + return -1; + } else if (!right->valid) { + return 1; + } + cmp = left->char_set - right->char_set; + if (cmp) { + return cmp; + } + cmp = strcmp(left->str, right->str); + if (cmp) { + return cmp; + } + cmp = left->presentation - right->presentation; + return cmp; +} + +/*! + * \brief Compare the left and right party number. + * + * \param left Left parameter party number. + * \param right Right parameter party number. + * + * \retval < 0 when left < right. + * \retval == 0 when left == right. + * \retval > 0 when left > right. + */ +int q931_party_number_cmp(const struct q931_party_number *left, const struct q931_party_number *right) +{ + int cmp; + + if (!left->valid) { + if (!right->valid) { + return 0; + } + return -1; + } else if (!right->valid) { + return 1; + } + cmp = left->plan - right->plan; + if (cmp) { + return cmp; + } + cmp = strcmp(left->str, right->str); + if (cmp) { + return cmp; + } + cmp = left->presentation - right->presentation; + return cmp; +} + +/*! + * \brief Compare the left and right party subaddress. + * + * \param left Left parameter party subaddress. + * \param right Right parameter party subaddress. + * + * \retval < 0 when left < right. + * \retval == 0 when left == right. + * \retval > 0 when left > right. + */ +int q931_party_subaddress_cmp(const struct q931_party_subaddress *left, const struct q931_party_subaddress *right) +{ + int cmp; + + if (!left->valid) { + if (!right->valid) { + return 0; + } + return -1; + } else if (!right->valid) { + return 1; + } + cmp = left->type - right->type; + if (cmp) { + return cmp; + } + cmp = memcmp(left->data, right->data, + (left->length < right->length) ? left->length : right->length); + if (cmp) { + return cmp; + } + cmp = left->length - right->length; + if (cmp) { + return cmp; + } + cmp = left->odd_even_indicator - right->odd_even_indicator; + return cmp; +} + +/*! + * \brief Compare the left and right party id. + * + * \param left Left parameter party id. + * \param right Right parameter party id. + * + * \retval < 0 when left < right. + * \retval == 0 when left == right. + * \retval > 0 when left > right. + */ +int q931_party_id_cmp(const struct q931_party_id *left, const struct q931_party_id *right) +{ + int cmp; + + cmp = q931_party_number_cmp(&left->number, &right->number); + if (cmp) { + return cmp; + } + cmp = q931_party_subaddress_cmp(&left->subaddress, &right->subaddress); + if (cmp) { + return cmp; + } + cmp = q931_party_name_cmp(&left->name, &right->name); + return cmp; +} + +/*! + * \brief Copy the Q.931 party name to the PRI party name structure. + * + * \param pri_name PRI party name structure + * \param q931_name Q.931 party name structure + * + * \return Nothing + */ +void q931_party_name_copy_to_pri(struct pri_party_name *pri_name, const struct q931_party_name *q931_name) +{ + if (q931_name->valid) { + pri_name->valid = 1; + pri_name->presentation = q931_name->presentation; + pri_name->char_set = q931_name->char_set; + libpri_copy_string(pri_name->str, q931_name->str, sizeof(pri_name->str)); + } else { + pri_name->valid = 0; + pri_name->presentation = PRI_PRES_UNAVAILABLE; + pri_name->char_set = PRI_CHAR_SET_ISO8859_1; + pri_name->str[0] = 0; + } +} + +/*! + * \brief Copy the Q.931 party number to the PRI party number structure. + * + * \param pri_number PRI party number structure + * \param q931_number Q.931 party number structure + * + * \return Nothing + */ +void q931_party_number_copy_to_pri(struct pri_party_number *pri_number, const struct q931_party_number *q931_number) +{ + if (q931_number->valid) { + pri_number->valid = 1; + pri_number->presentation = q931_number->presentation; + pri_number->plan = q931_number->plan; + libpri_copy_string(pri_number->str, q931_number->str, sizeof(pri_number->str)); + } else { + pri_number->valid = 0; + pri_number->presentation = PRI_PRES_UNAVAILABLE | PRI_PRES_USER_NUMBER_UNSCREENED; + pri_number->plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + pri_number->str[0] = 0; + } +} + +/*! + * \brief Copy the Q.931 party subaddress to the PRI party subaddress structure. + * + * \param pri_subaddress PRI party subaddress structure + * \param q931_subaddress Q.931 party subaddress structure + * + * \return Nothing + */ +void q931_party_subaddress_copy_to_pri(struct pri_party_subaddress *pri_subaddress, const struct q931_party_subaddress *q931_subaddress) +{ + int length; + + /* + * The size of pri_subaddress->data[] is not the same as the size of + * q931_subaddress->data[]. + */ + + if (!q931_subaddress->valid) { + pri_subaddress->valid = 0; + pri_subaddress->type = 0; + pri_subaddress->odd_even_indicator = 0; + pri_subaddress->length = 0; + pri_subaddress->data[0] = '\0'; + return; + } + + pri_subaddress->valid = 1; + pri_subaddress->type = q931_subaddress->type; + pri_subaddress->odd_even_indicator = q931_subaddress->odd_even_indicator; + + length = q931_subaddress->length; + pri_subaddress->length = length; + memcpy(pri_subaddress->data, q931_subaddress->data, length); + pri_subaddress->data[length] = '\0'; +} + +/*! + * \brief Copy the Q.931 party id to the PRI party id structure. + * + * \param pri_id PRI party id structure + * \param q931_id Q.931 party id structure + * + * \return Nothing + */ +void q931_party_id_copy_to_pri(struct pri_party_id *pri_id, const struct q931_party_id *q931_id) +{ + q931_party_name_copy_to_pri(&pri_id->name, &q931_id->name); + q931_party_number_copy_to_pri(&pri_id->number, &q931_id->number); + q931_party_subaddress_copy_to_pri(&pri_id->subaddress, &q931_id->subaddress); +} + +/*! + * \brief Copy the Q.931 redirecting data to the PRI redirecting structure. + * + * \param pri_redirecting PRI redirecting structure + * \param q931_redirecting Q.931 redirecting structure + * + * \return Nothing + */ +void q931_party_redirecting_copy_to_pri(struct pri_party_redirecting *pri_redirecting, const struct q931_party_redirecting *q931_redirecting) +{ + q931_party_id_copy_to_pri(&pri_redirecting->from, &q931_redirecting->from); + q931_party_id_copy_to_pri(&pri_redirecting->to, &q931_redirecting->to); + q931_party_id_copy_to_pri(&pri_redirecting->orig_called, + &q931_redirecting->orig_called); + pri_redirecting->count = q931_redirecting->count; + pri_redirecting->orig_reason = q931_redirecting->orig_reason; + pri_redirecting->reason = q931_redirecting->reason; +} + +/*! + * \brief Fixup some values in the q931_party_id that may be objectionable by switches. + * + * \param ctrl D channel controller. + * \param id Party ID to tweak. + * + * \return Nothing + */ +void q931_party_id_fixup(const struct pri *ctrl, struct q931_party_id *id) +{ + switch (ctrl->switchtype) { + case PRI_SWITCH_DMS100: + case PRI_SWITCH_ATT4ESS: + /* Doesn't like certain presentation types */ + if (id->number.valid && !(id->number.presentation & 0x7c)) { + /* i.e., If presentation is allowed it must be a network number */ + id->number.presentation = PRES_ALLOWED_NETWORK_NUMBER; + } + break; + default: + break; + } +} + +/*! + * \brief Determine the overall presentation value for the given party. + * + * \param id Party to determine the overall presentation value. + * + * \return Overall presentation value for the given party. + */ +int q931_party_id_presentation(const struct q931_party_id *id) +{ + int number_priority; + int number_value; + int number_screening; + int name_priority; + int name_value; + + /* Determine name presentation priority. */ + if (!id->name.valid) { + name_value = PRI_PRES_UNAVAILABLE; + name_priority = 3; + } else { + name_value = id->name.presentation & PRI_PRES_RESTRICTION; + switch (name_value) { + case PRI_PRES_RESTRICTED: + name_priority = 0; + break; + case PRI_PRES_ALLOWED: + name_priority = 1; + break; + case PRI_PRES_UNAVAILABLE: + name_priority = 2; + break; + default: + name_value = PRI_PRES_UNAVAILABLE; + name_priority = 3; + break; + } + } + + /* Determine number presentation priority. */ + if (!id->number.valid) { + number_screening = PRI_PRES_USER_NUMBER_UNSCREENED; + number_value = PRI_PRES_UNAVAILABLE; + number_priority = 3; + } else { + number_screening = id->number.presentation & PRI_PRES_NUMBER_TYPE; + number_value = id->number.presentation & PRI_PRES_RESTRICTION; + switch (number_value) { + case PRI_PRES_RESTRICTED: + number_priority = 0; + break; + case PRI_PRES_ALLOWED: + number_priority = 1; + break; + case PRI_PRES_UNAVAILABLE: + number_priority = 2; + break; + default: + number_screening = PRI_PRES_USER_NUMBER_UNSCREENED; + number_value = PRI_PRES_UNAVAILABLE; + number_priority = 3; + break; + } + } + + /* Select the wining presentation value. */ + if (name_priority < number_priority) { + number_value = name_value; + } + + return number_value | number_screening; +} + +static void q931_clr_subcommands(struct pri *ctrl) +{ + ctrl->subcmds.counter_subcmd = 0; +} + +struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl) +{ + if (ctrl->subcmds.counter_subcmd < PRI_MAX_SUBCOMMANDS) { + return &ctrl->subcmds.subcmd[ctrl->subcmds.counter_subcmd++]; + } + + return NULL; +} + static char *code2str(int code, struct msgtype *codes, int max) { int x; @@ -258,15 +795,18 @@ return "Unknown"; } -static void call_init(struct q931_call *c) +static char *pritype(int type) { - c->forceinvert = -1; - c->cr = -1; - c->slotmap = -1; - c->channelno = -1; - c->newcall = 1; - c->ourcallstate = Q931_CALL_STATE_NULL; - c->peercallstate = Q931_CALL_STATE_NULL; + switch (type) { + case PRI_CPE: + return "CPE"; + break; + case PRI_NETWORK: + return "NET"; + break; + default: + return "UNKNOWN"; + } } static char *binary(int b, int len) { @@ -280,56 +820,85 @@ return res; } -static FUNC_RECV(receive_channel_id) +static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int x; - int pos=0; -#ifdef NO_BRI_SUPPORT - if (!ie->data[0] & 0x20) { - pri_error(pri, "!! Not PRI type!?\n"); - return -1; - } -#endif -#ifndef NOAUTO_CHANNEL_SELECTION_SUPPORT - if (pri->bri) { - if (!(ie->data[0] & 3)) - call->justsignalling = 1; - else - call->channelno = ie->data[0] & 3; + int pos = 0; + int need_extended_channel_octets;/*!< TRUE if octets 3.2 and 3.3 need to be present. */ + + if (ie->data[0] & 0x08) { + call->chanflags = FLAG_EXCLUSIVE; } else { - switch (ie->data[0] & 3) { - case 0: - call->justsignalling = 1; - break; - case 1: - break; - default: - pri_error(pri, "!! Unexpected Channel selection %d\n", ie->data[0] & 3); - return -1; + call->chanflags = FLAG_PREFERRED; + } + + need_extended_channel_octets = 0; + if (ie->data[0] & 0x20) { + /* PRI encoded interface type */ + switch (ie->data[0] & 0x03) { + case 0x00: + /* No channel */ + call->channelno = 0; + call->chanflags = FLAG_PREFERRED; + break; + case 0x01: + /* As indicated in following octets */ + need_extended_channel_octets = 1; + break; + case 0x03: + /* Any channel */ + call->chanflags = FLAG_PREFERRED; + break; + default: + pri_error(ctrl, "!! Unexpected Channel selection %d\n", ie->data[0] & 0x03); + return -1; } + } else { + /* BRI encoded interface type */ + switch (ie->data[0] & 0x03) { + case 0x00: + /* No channel */ + call->channelno = 0; + call->chanflags = FLAG_PREFERRED; + break; + case 0x03: + /* Any channel */ + call->chanflags = FLAG_PREFERRED; + break; + default: + /* Specified B channel (B1 or B2) */ + call->channelno = ie->data[0] & 0x03; + break; + } } -#endif - if (ie->data[0] & 0x08) - call->chanflags = FLAG_EXCLUSIVE; - else - call->chanflags = FLAG_PREFERRED; + pos++; if (ie->data[0] & 0x40) { /* DS1 specified -- stop here */ call->ds1no = ie->data[1] & 0x7f; call->ds1explicit = 1; pos++; - } else + } else { call->ds1explicit = 0; + } - if (pos+2 < len) { + if (ie->data[0] & 0x04) { + /* D channel call. Signaling only. */ + call->cis_call = 1; + call->chanflags = FLAG_EXCLUSIVE;/* For safety mark this channel as exclusive. */ + call->channelno = 0; + return 0; + } + + if (need_extended_channel_octets && pos + 2 < len) { /* More coming */ if ((ie->data[pos] & 0x0f) != 3) { - pri_error(pri, "!! Unexpected Channel Type %d\n", ie->data[1] & 0x0f); + /* Channel type/mapping is not for B channel units. */ + pri_error(ctrl, "!! Unexpected Channel Type %d\n", ie->data[1] & 0x0f); return -1; } if ((ie->data[pos] & 0x60) != 0) { - pri_error(pri, "!! Invalid CCITT coding %d\n", (ie->data[1] & 0x60) >> 5); + pri_error(ctrl, "!! Invalid CCITT coding %d\n", (ie->data[1] & 0x60) >> 5); return -1; } if (ie->data[pos] & 0x10) { @@ -340,136 +909,167 @@ call->slotmap <<= 8; call->slotmap |= ie->data[x + pos]; } - return 0; } else { pos++; /* Only expect a particular channel */ call->channelno = ie->data[pos] & 0x7f; - if (pri->chan_mapping_logical && call->channelno > 15) + if (ctrl->chan_mapping_logical && call->channelno > 15) call->channelno++; - return 0; } - } else - return 0; - return -1; + } + return 0; } -static FUNC_SEND(transmit_channel_id) +static int transmit_channel_id(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { - int pos=0; + int pos = 0; - /* We are ready to transmit single IE only */ if (order > 1) return 0; - - if (call->justsignalling) { - ie->data[pos++] = 0xac; /* Read the standards docs to figure this out - ECMA-165 section 7.3 */ + + if (call->cis_call) { + /* + * Read the standards docs to figure this out. + * Q.SIG ECMA-165 section 7.3 + * ITU Q.931 section 4.5.13 + */ + ie->data[pos++] = ctrl->bri ? 0x8c : 0xac; return pos + 2; } - + /* Start with standard stuff */ - if (pri->switchtype == PRI_SWITCH_GR303_TMC) + if (ctrl->switchtype == PRI_SWITCH_GR303_TMC) ie->data[pos] = 0x69; - else if (pri->bri) { + else if (ctrl->bri) { ie->data[pos] = 0x80; - if (call->channelno > -1) - ie->data[pos] |= (call->channelno & 0x3); - } else - ie->data[pos] = 0xa1; - /* Add exclusive flag if necessary */ - if (call->chanflags & FLAG_EXCLUSIVE) + ie->data[pos] |= (call->channelno & 0x3); + } else { + /* PRI */ + if (call->slotmap != -1 || (call->chanflags & FLAG_WHOLE_INTERFACE)) { + /* Specified channel */ + ie->data[pos] = 0xa1; + } else if (call->channelno < 0 || call->channelno == 0xff) { + /* Any channel */ + ie->data[pos] = 0xa3; + } else if (!call->channelno) { + /* No channel */ + ie->data[pos] = 0xa0; + } else { + /* Specified channel */ + ie->data[pos] = 0xa1; + } + } + if (call->chanflags & FLAG_EXCLUSIVE) { + /* Channel is exclusive */ ie->data[pos] |= 0x08; - else if (!(call->chanflags & FLAG_PREFERRED)) { + } else if (!call->chanflags) { /* Don't need this IE */ return 0; } - if (((pri->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit) { - /* Note that we are specifying the identifier */ + if (!ctrl->bri && (((ctrl->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit)) { + /* We are specifying the interface. Octet 3.1 */ ie->data[pos++] |= 0x40; - /* We need to use the Channel Identifier Present thingy. Just specify it and we're done */ ie->data[pos++] = 0x80 | call->ds1no; - } else - pos++; + } else { + ++pos; + } - if (pri->bri) - return pos + 2; + if (!ctrl->bri && (ie->data[0] & 0x03) == 0x01 /* Specified channel */ + && !(call->chanflags & FLAG_WHOLE_INTERFACE)) { + /* The 3.2 and 3.3 octets need to be present */ + ie->data[pos] = 0x83; + if (call->slotmap != -1) { + int octet; - if ((call->channelno > -1) || (call->slotmap != -1)) { - /* We'll have the octet 8.2 and 8.3's present */ - ie->data[pos++] = 0x83; - if (call->channelno > -1) { + /* We have to send a channel map */ + ie->data[pos++] |= 0x10; + for (octet = 3; octet--;) { + ie->data[pos++] = (call->slotmap >> (8 * octet)) & 0xff; + } + } else { /* Channel number specified */ - if (pri->chan_mapping_logical && call->channelno > 16) + ++pos; + if (ctrl->chan_mapping_logical && call->channelno > 16) { ie->data[pos++] = 0x80 | (call->channelno - 1); - else + } else { ie->data[pos++] = 0x80 | call->channelno; - return pos + 2; + } } - /* We have to send a channel map */ - if (call->slotmap != -1) { - ie->data[pos-1] |= 0x10; - ie->data[pos++] = (call->slotmap & 0xff0000) >> 16; - ie->data[pos++] = (call->slotmap & 0xff00) >> 8; - ie->data[pos++] = (call->slotmap & 0xff); - return pos + 2; - } } - if (call->ds1no > 0) { - /* We're done */ - return pos + 2; - } - pri_error(pri, "!! No channel map, no channel, and no ds1? What am I supposed to identify?\n"); - return -1; + + return pos + 2; } -static FUNC_DUMP(dump_channel_id) +static void dump_channel_id(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - int pos=0; + int pos; int x; - int res = 0; - static const char* msg_chan_sel[] = { - "No channel selected", "B1 channel", "B2 channel","Any channel selected", - "No channel selected", "As indicated in following octets", "Reserved","Any channel selected" + int res; + + static const char *msg_chan_sel[] = { + "No channel selected", "B1 channel", "B2 channel", "Any channel selected", + "No channel selected", "As indicated in following octets", "Reserved", "Any channel selected" }; - pri_message(pri, "%c Channel ID (len=%2d) [ Ext: %d IntID: %s %s Spare: %d %s Dchan: %d\n", - prefix, len, (ie->data[0] & 0x80) ? 1 : 0, (ie->data[0] & 0x40) ? "Explicit" : "Implicit", - (ie->data[0] & 0x20) ? "PRI" : "Other", (ie->data[0] & 0x10) ? 1 : 0, - (ie->data[0] & 0x08) ? "Exclusive" : "Preferred", (ie->data[0] & 0x04) ? 1 : 0); - pri_message(pri, "%c ChanSel: %s\n", - prefix, msg_chan_sel[(ie->data[0] & 0x3) + ((ie->data[0]>>3) & 0x4)]); - pos++; - len--; - if (ie->data[0] & 0x40) { + pri_message(ctrl, + "%c Channel ID (len=%2d) [ Ext: %d IntID: %s %s Spare: %d %s Dchan: %d\n", + prefix, len, + (ie->data[0] & 0x80) ? 1 : 0, + (ie->data[0] & 0x40) ? "Explicit" : "Implicit", + (ie->data[0] & 0x20) ? "Other(PRI)" : "BRI", + (ie->data[0] & 0x10) ? 1 : 0, + (ie->data[0] & 0x08) ? "Exclusive" : "Preferred", + (ie->data[0] & 0x04) ? 1 : 0); + pri_message(ctrl, "%c ChanSel: %s\n", + prefix, msg_chan_sel[(ie->data[0] & 0x03) | ((ie->data[0] >> 3) & 0x04)]); + pos = 1; + len -= 2; + if (ie->data[0] & 0x40) { /* Explicitly defined DS1 */ - pri_message(pri, "%c Ext: %d DS1 Identifier: %d \n", prefix, (ie->data[pos] & 0x80) >> 7, ie->data[pos] & 0x7f); - pos++; + do { + pri_message(ctrl, "%c Ext: %d DS1 Identifier: %d \n", + prefix, (ie->data[pos] & 0x80) >> 7, ie->data[pos] & 0x7f); + ++pos; + } while (!(ie->data[pos - 1] & 0x80) && pos < len); } else { /* Implicitly defined DS1 */ } - if (pos+2 < len) { + if (pos < len) { /* Still more information here */ - pri_message(pri, "%c Ext: %d Coding: %d %s Specified Channel Type: %d\n", - prefix, (ie->data[pos] & 0x80) >> 7, (ie->data[pos] & 60) >> 5, - (ie->data[pos] & 0x10) ? "Slot Map" : "Number", ie->data[pos] & 0x0f); - if (!(ie->data[pos] & 0x10)) { + pri_message(ctrl, + "%c Ext: %d Coding: %d %s Specified Channel Type: %d\n", + prefix, (ie->data[pos] & 0x80) >> 7, (ie->data[pos] & 60) >> 5, + (ie->data[pos] & 0x10) ? "Slot Map" : "Number", ie->data[pos] & 0x0f); + ++pos; + } + if (pos < len) { + if (!(ie->data[pos - 1] & 0x10)) { /* Number specified */ - pos++; - pri_message(pri, "%c Ext: %d Channel: %d ]\n", prefix, (ie->data[pos] & 0x80) >> 7, - (ie->data[pos]) & 0x7f); + do { + pri_message(ctrl, + "%c Ext: %d Channel: %d Type: %s%c\n", + prefix, (ie->data[pos] & 0x80) >> 7, + (ie->data[pos]) & 0x7f, pritype(ctrl->localtype), + (pos + 1 < len) ? ' ' : ']'); + ++pos; + } while (pos < len); } else { - pos++; /* Map specified */ - for (x=0;x<3;x++) { + res = 0; + x = 0; + do { res <<= 8; res |= ie->data[pos++]; - } - pri_message(pri, "%c Map: %s ]\n", prefix, binary(res, 24)); + ++x; + } while (pos < len); + pri_message(ctrl, "%c Map len: %d Map: %s ]\n", prefix, + x, binary(res, x << 3)); } - } else pri_message(pri, " ]\n"); + } else { + pri_message(ctrl, " ]\n"); + } } static char *ri2str(int ri) @@ -482,20 +1082,20 @@ return code2str(ri, ris, sizeof(ris) / sizeof(ris[0])); } -static FUNC_DUMP(dump_restart_indicator) +static void dump_restart_indicator(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - pri_message(pri, "%c Restart Indentifier (len=%2d) [ Ext: %d Spare: %d Resetting %s (%d) ]\n", + pri_message(ctrl, "%c Restart Indentifier (len=%2d) [ Ext: %d Spare: %d Resetting %s (%d) ]\n", prefix, len, (ie->data[0] & 0x80) >> 7, (ie->data[0] & 0x78) >> 3, ri2str(ie->data[0] & 0x7), ie->data[0] & 0x7); } -static FUNC_RECV(receive_restart_indicator) +static int receive_restart_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { /* Pretty simple */ call->ri = ie->data[0] & 0x7; return 0; } -static FUNC_SEND(transmit_restart_indicator) +static int transmit_restart_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { /* Pretty simple */ switch(call->ri) { @@ -509,7 +1109,7 @@ ie->data[0] = 0xA0 | (call->ri & 0x7); break; default: - pri_error(pri, "!! Invalid restart indicator value %d\n", call->ri); + pri_error(ctrl, "!! Invalid restart indicator value %d\n", call->ri); return-1; } return 3; @@ -607,16 +1207,16 @@ return code2str(proto, protos, sizeof(protos) / sizeof(protos[0])); } -static FUNC_DUMP(dump_bearer_capability) +static void dump_bearer_capability(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int pos=2; - pri_message(pri, "%c Bearer Capability (len=%2d) [ Ext: %d Q.931 Std: %d Info transfer capability: %s (%d)\n", + pri_message(ctrl, "%c Bearer Capability (len=%2d) [ Ext: %d Q.931 Std: %d Info transfer capability: %s (%d)\n", prefix, len, (ie->data[0] & 0x80 ) >> 7, (ie->data[0] & 0x60) >> 5, cap2str(ie->data[0] & 0x1f), (ie->data[0] & 0x1f)); - pri_message(pri, "%c Ext: %d Trans mode/rate: %s (%d)\n", prefix, (ie->data[1] & 0x80) >> 7, mode2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); + pri_message(ctrl, "%c Ext: %d Trans mode/rate: %s (%d)\n", prefix, (ie->data[1] & 0x80) >> 7, mode2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); /* octet 4.1 exists iff mode/rate is multirate */ if ((ie->data[1] & 0x7f) == 0x18) { - pri_message(pri, "%c Ext: %d Transfer rate multiplier: %d x 64\n", prefix, (ie->data[2] & 0x80) >> 7, ie->data[2] & 0x7f); + pri_message(ctrl, "%c Ext: %d Transfer rate multiplier: %d x 64\n", prefix, (ie->data[2] & 0x80) >> 7, ie->data[2] & 0x7f); pos++; } @@ -632,7 +1232,7 @@ too, so we have to do the same for binary compatability */ u_int8_t layer1 = ie->data[pos] & 0x7f; - pri_message(pri, "%c User information layer 1: %s (%d)\n", + pri_message(ctrl, "%c User information layer 1: %s (%d)\n", prefix, l12str(layer1), layer1); pos++; @@ -640,7 +1240,7 @@ if (pos < len && !(ie->data[pos-1] & 0x80)) { int ra = ie->data[pos] & 0x7f; - pri_message(pri, "%c Async: %d, Negotiation: %d, " + pri_message(ctrl, "%c Async: %d, Negotiation: %d, " "User rate: %s (%#x)\n", prefix, ra & PRI_RATE_ADAPT_ASYNC ? 1 : 0, @@ -654,7 +1254,7 @@ if (pos < len && !(ie->data[pos-1] & 0x80)) { u_int8_t data = ie->data[pos]; if (layer1 == PRI_LAYER_1_ITU_RATE_ADAPT) { - pri_message(pri, "%c Intermediate rate: %s (%d), " + pri_message(ctrl, "%c Intermediate rate: %s (%d), " "NIC on Tx: %d, NIC on Rx: %d, " "Flow control on Tx: %d, " "Flow control on Rx: %d\n", @@ -665,7 +1265,7 @@ (data & 0x04)?1:0, (data & 0x02)?1:0); } else if (layer1 == PRI_LAYER_1_V120_RATE_ADAPT) { - pri_message(pri, "%c Hdr: %d, Multiframe: %d, Mode: %d, " + pri_message(ctrl, "%c Hdr: %d, Multiframe: %d, Mode: %d, " "LLI negot: %d, Assignor: %d, " "In-band neg: %d\n", prefix, (data & 0x40)?1:0, @@ -675,7 +1275,8 @@ (data & 0x04)?1:0, (data & 0x02)?1:0); } else { - pri_message(pri, "%c Unknown octet 5b: 0x%x\n", data ); + pri_message(ctrl, "%c Unknown octet 5b: 0x%x\n", + prefix, data); } pos++; } @@ -688,7 +1289,7 @@ const char *parity[] = {"Odd","?","Even","None", "zero","one","?","?"}; - pri_message(pri, "%c Stop bits: %s, data bits: %s, " + pri_message(ctrl, "%c Stop bits: %s, data bits: %s, " "parity: %s\n", prefix, stop_bits[(data & 0x60) >> 5], data_bits[(data & 0x18) >> 3], @@ -700,7 +1301,7 @@ /* octet 5d? */ if (pos < len && !(ie->data[pos-1] & 0x80)) { u_int8_t data = ie->data[pos]; - pri_message(pri, "%c Duplex mode: %d, modem type: %d\n", + pri_message(ctrl, "%c Duplex mode: %d, modem type: %d\n", prefix, (data & 0x40) ? 1 : 0,data & 0x3F); pos++; } @@ -710,7 +1311,7 @@ /* Look for octet 6; this is identified by bits 5,6 == 10 */ if (pos < len && (ie->data[pos] & 0x60) == 0x40) { - pri_message(pri, "%c User information layer 2: %s (%d)\n", + pri_message(ctrl, "%c User information layer 2: %s (%d)\n", prefix, l22str(ie->data[pos] & 0x1f), ie->data[pos] & 0x1f); pos++; @@ -718,7 +1319,7 @@ /* Look for octet 7; this is identified by bits 5,6 == 11 */ if (pos < len && (ie->data[pos] & 0x60) == 0x60) { - pri_message(pri, "%c User information layer 3: %s (%d)\n", + pri_message(ctrl, "%c User information layer 3: %s (%d)\n", prefix, l32str(ie->data[pos] & 0x1f), ie->data[pos] & 0x1f); pos++; @@ -730,18 +1331,18 @@ proto = ((ie->data[pos] & 0xF) << 4 ) | (ie->data[pos+1] & 0xF); - pri_message(pri, "%c Network layer: 0x%x\n", prefix, + pri_message(ctrl, "%c Network layer: 0x%x\n", prefix, proto ); pos += 2; } } } -static FUNC_RECV(receive_bearer_capability) +static int receive_bearer_capability(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int pos=2; if (ie->data[0] & 0x60) { - pri_error(pri, "!! non-standard Q.931 standard field\n"); + pri_error(ctrl, "!! non-standard Q.931 standard field\n"); return -1; } call->transcapability = ie->data[0] & 0x1f; @@ -788,7 +1389,7 @@ return 0; } -static FUNC_SEND(transmit_bearer_capability) +static int transmit_bearer_capability(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { int tc; int pos; @@ -797,20 +1398,20 @@ if(order > 1) return 0; - tc = call->transcapability; - if (pri->subchannel && !pri->bri) { + if (ctrl->subchannel && !ctrl->bri) { /* Bearer capability is *hard coded* in GR-303 */ ie->data[0] = 0x88; ie->data[1] = 0x90; return 4; } - - if (call->justsignalling) { + + if (call->cis_call) { ie->data[0] = 0xa8; ie->data[1] = 0x80; return 4; } - + + tc = call->transcapability; ie->data[0] = 0x80 | tc; ie->data[1] = call->transmoderate | 0x80; @@ -820,7 +1421,7 @@ ie->data[pos++] = call->transmultiple | 0x80; } - if ((tc & PRI_TRANS_CAP_DIGITAL) && (pri->switchtype == PRI_SWITCH_EUROISDN_E1) && + if ((tc & PRI_TRANS_CAP_DIGITAL) && (ctrl->switchtype == PRI_SWITCH_EUROISDN_E1) && (call->transmoderate == TRANS_MODE_PACKET)) { /* Apparently EuroISDN switches don't seem to like user layer 2/3 */ return 4; @@ -833,7 +1434,7 @@ if (call->transmoderate != TRANS_MODE_PACKET) { /* If you have an AT&T 4ESS, you don't send any more info */ - if ((pri->switchtype != PRI_SWITCH_ATT4ESS) && (call->userl1 > -1)) { + if ((ctrl->switchtype != PRI_SWITCH_ATT4ESS) && (call->userl1 > -1)) { ie->data[pos++] = call->userl1 | 0x80; /* XXX Ext bit? XXX */ if (call->userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) { ie->data[pos++] = call->rateadaption | 0x80; @@ -908,6 +1509,30 @@ return code2str(plan, plans, sizeof(plans) / sizeof(plans[0])); } +/* Calling Party Category (Definitions from Q.763) */ +static char *cpc2str(int plan) +{ + static struct msgtype plans[] = { + { 0, "Unknown Source" }, + { 1, "Operator French" }, + { 2, "Operator English" }, + { 3, "Operator German" }, + { 4, "Operator Russian" }, + { 5, "Operator Spanish" }, + { 6, "Mut Agree Chinese" }, + { 7, "Mut Agreement" }, + { 8, "Mut Agree Japanese" }, + { 9, "National Operator" }, + { 10, "Ordinary Toll Caller" }, + { 11, "Priority Toll Caller" }, + { 12, "Data Call" }, + { 13, "Test Call" }, + { 14, "Spare" }, + { 15, "Pay Phone" }, + }; + return code2str(plan, plans, ARRAY_LEN(plans)); +} + char *pri_pres2str(int pres) { static struct msgtype press[] = { @@ -934,51 +1559,134 @@ num[len] = 0; } -static FUNC_DUMP(dump_called_party_number) +static void q931_get_subaddr_specific(unsigned char *num, int maxlen, unsigned char *src, int len, char oddflag) { - unsigned char cnum[256]; + /* User Specified */ + int x; + char *ptr = (char *) num; - q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3); - pri_message(pri, "%c Called Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d) '%s' ]\n", - prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f, cnum); + if (len <= 0) { + num[0] = '\0'; + return; + } + + if (((len * 2) + 1) > maxlen) { + len = (maxlen / 2) - 1; + } + + for (x = 0; x < (len - 1); ++x) { + ptr += sprintf(ptr, "%02x", src[x]); + } + + if (oddflag) { + /* ODD */ + sprintf(ptr, "%01x", (src[len - 1]) >> 4); + } else { + /* EVEN */ + sprintf(ptr, "%02x", src[len - 1]); + } } -static FUNC_DUMP(dump_called_party_subaddr) +static int transmit_subaddr_helper(int full_ie, struct pri *ctrl, struct q931_party_subaddress *q931_subaddress, int msgtype, q931_ie *ie, int offset, int len, int order) { + size_t datalen; + + if (!q931_subaddress->valid) { + return 0; + } + + datalen = q931_subaddress->length; + if (!q931_subaddress->type) { + /* 0 = NSAP */ + /* 0 = Odd/Even indicator */ + ie->data[0] = 0x80; + } else { + /* 2 = User Specified */ + ie->data[0] = q931_subaddress->odd_even_indicator ? 0xA8 : 0xA0; + } + memcpy(ie->data + offset, q931_subaddress->data, datalen); + + return datalen + (offset + 2); +} + +static int receive_subaddr_helper(int full_ie, struct pri *ctrl, struct q931_party_subaddress *q931_subaddress, int msgtype, q931_ie *ie, int offset, int len) +{ + if (len <= 0) { + return -1; + } + + q931_subaddress->valid = 1; + q931_subaddress->length = len; + /* type: 0 = NSAP, 2 = User Specified */ + q931_subaddress->type = ((ie->data[0] & 0x70) >> 4); + q931_subaddress->odd_even_indicator = (ie->data[0] & 0x08) ? 1 : 0; + q931_get_number(q931_subaddress->data, sizeof(q931_subaddress->data), + ie->data + offset, len); + + return 0; +} + +static void dump_subaddr_helper(int full_ie, struct pri *ctrl, q931_ie *ie, int offset, int len, int datalen, char prefix, const char *named) +{ unsigned char cnum[256]; - q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3); - pri_message(pri, "%c Called Sub-Address (len=%2d) [ Ext: %d Type: %s (%d) O: %d '%s' ]\n", - prefix, len, ie->data[0] >> 7, + + if (!(ie->data[0] & 0x70)) { + /* NSAP */ + q931_get_number(cnum, sizeof(cnum), ie->data + offset, datalen); + } else { + /* User Specified */ + q931_get_subaddr_specific(cnum, sizeof(cnum), ie->data + offset, datalen, + ie->data[0] & 0x08); + } + + pri_message(ctrl, + "%c %s Sub-Address (len=%2d) [ Ext: %d Type: %s (%d) O: %d '%s' ]\n", + prefix, named, len, ie->data[0] >> 7, subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4, (ie->data[0] & 0x08) >> 3, cnum); } -static FUNC_DUMP(dump_calling_party_number) +static void dump_called_party_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { unsigned char cnum[256]; + + q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3); + pri_message(ctrl, "%c Called Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d) '%s' ]\n", + prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f, cnum); +} + +static void dump_called_party_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ + dump_subaddr_helper(full_ie, ctrl, ie, 1 , len, len - 3, prefix, "Called"); +} + +static void dump_calling_party_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ + unsigned char cnum[256]; if (ie->data[0] & 0x80) q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3); else q931_get_number(cnum, sizeof(cnum), ie->data + 2, len - 4); - pri_message(pri, "%c Calling Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)\n", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); + pri_message(ctrl, "%c Calling Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)\n", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); if (ie->data[0] & 0x80) - pri_message(pri, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(0), 0, cnum); + pri_message(ctrl, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(0), 0, cnum); else - pri_message(pri, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, cnum); + pri_message(ctrl, "%c Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, cnum); } -static FUNC_DUMP(dump_calling_party_subaddr) +static void dump_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - unsigned char cnum[256]; - q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3); - pri_message(pri, "%c Calling Sub-Address (len=%2d) [ Ext: %d Type: %s (%d) O: %d '%s' ]\n", - prefix, len, ie->data[0] >> 7, - subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4, - (ie->data[0] & 0x08) >> 3, cnum); + dump_subaddr_helper(full_ie, ctrl, ie, 1 , len, len - 3, prefix, "Calling"); } -static FUNC_DUMP(dump_redirecting_number) +static void dump_calling_party_category(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { + pri_message(ctrl, "%c Calling Party Category (len=%2d) [ Ext: %d Cat: %s (%d) ]\n", + prefix, len, ie->data[0] >> 7, cpc2str(ie->data[0] & 0x0F), ie->data[0] & 0x0F); +} + +static void dump_redirecting_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ unsigned char cnum[256]; int i = 0; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by @@ -986,172 +1694,369 @@ do { switch(i) { case 0: /* Octet 3 */ - pri_message(pri, "%c Redirecting Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", + pri_message(ctrl, "%c Redirecting Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); break; case 1: /* Octet 3a */ - pri_message(pri, "\n%c Ext: %d Presentation: %s (%d)", + pri_message(ctrl, "\n%c Ext: %d Presentation: %s (%d)", prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); break; case 2: /* Octet 3b */ - pri_message(pri, "\n%c Ext: %d Reason: %s (%d)", + pri_message(ctrl, "\n%c Ext: %d Reason: %s (%d)", prefix, ie->data[2] >> 7, redirection_reason2str(ie->data[2] & 0x7f), ie->data[2] & 0x7f); break; } - } - while(!(ie->data[i++]& 0x80)); + } while(!(ie->data[i++]& 0x80)); q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i); - pri_message(pri, " '%s' ]\n", cnum); + pri_message(ctrl, " '%s' ]\n", cnum); } -static FUNC_DUMP(dump_connected_number) +static void dump_redirection_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { unsigned char cnum[256]; int i = 0; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { + switch (i) { + case 0: /* Octet 3 */ + pri_message(ctrl, + "%c Redirection Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", + prefix, len, ie->data[0] >> 7, + ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, + npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); + break; + case 1: /* Octet 3a */ + pri_message(ctrl, "\n%c Ext: %d Presentation: %s (%d)", + prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); + break; + } + } while (!(ie->data[i++] & 0x80)); + q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i); + pri_message(ctrl, " '%s' ]\n", cnum); +} + +static int receive_connected_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) +{ + int i = 0; + + call->connected_number_in_message = 1; + call->remote_id.number.valid = 1; + call->remote_id.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + /* To follow Q.931 (4.5.1), we must search for start of octet 4 by + walking through all bytes until one with ext bit (8) set to 1 */ + do { + switch (i) { + case 0: + call->remote_id.number.plan = ie->data[i] & 0x7f; + break; + case 1: + /* Keep only the presentation and screening fields */ + call->remote_id.number.presentation = + ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); + break; + } + } while (!(ie->data[i++] & 0x80)); + q931_get_number((unsigned char *) call->remote_id.number.str, sizeof(call->remote_id.number.str), ie->data + i, ie->len - i); + + return 0; +} + +static int transmit_connected_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) +{ + size_t datalen; + + if (!call->local_id.number.valid) { + return 0; + } + + datalen = strlen(call->local_id.number.str); + ie->data[0] = call->local_id.number.plan; + ie->data[1] = 0x80 | call->local_id.number.presentation; + memcpy(ie->data + 2, call->local_id.number.str, datalen); + return datalen + (2 + 2); +} + +static void dump_connected_number(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ + unsigned char cnum[256]; + int i = 0; + /* To follow Q.931 (4.5.1), we must search for start of octet 4 by + walking through all bytes until one with ext bit (8) set to 1 */ + do { switch(i) { case 0: /* Octet 3 */ - pri_message(pri, "%c Connected Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", + pri_message(ctrl, "%c Connected Number (len=%2d) [ Ext: %d TON: %s (%d) NPI: %s (%d)", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f); break; case 1: /* Octet 3a */ - pri_message(pri, "\n%c Ext: %d Presentation: %s (%d)", + pri_message(ctrl, "\n%c Ext: %d Presentation: %s (%d)", prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); break; } - } - while(!(ie->data[i++]& 0x80)); + } while(!(ie->data[i++]& 0x80)); q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i); - pri_message(pri, " '%s' ]\n", cnum); + pri_message(ctrl, " '%s' ]\n", cnum); } +static int receive_connected_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) +{ + if (len < 3) { + return -1; + } -static FUNC_RECV(receive_redirecting_number) + return receive_subaddr_helper(full_ie, ctrl, &call->remote_id.subaddress, msgtype, ie, + 1, len - 3); +} + +static int transmit_connected_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { + return transmit_subaddr_helper(full_ie, ctrl, &call->local_id.subaddress, msgtype, ie, + 1, len, order); +} + +static void dump_connected_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ + dump_subaddr_helper(full_ie, ctrl, ie, 1 , len, len - 3, prefix, "Connected"); +} + +static int receive_redirecting_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) +{ int i = 0; + call->redirecting_number_in_message = 1; + call->redirecting.from.number.valid = 1; + call->redirecting.from.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + call->redirecting.reason = PRI_REDIR_UNKNOWN; /* To follow Q.931 (4.5.1), we must search for start of octet 4 by walking through all bytes until one with ext bit (8) set to 1 */ do { - switch(i) { + switch (i) { case 0: - call->redirectingplan = ie->data[i] & 0x7f; + call->redirecting.from.number.plan = ie->data[i] & 0x7f; break; case 1: - call->redirectingpres = ie->data[i] & 0x7f; + /* Keep only the presentation and screening fields */ + call->redirecting.from.number.presentation = + ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); break; case 2: - call->redirectingreason = ie->data[i] & 0x0f; + call->redirecting.reason = ie->data[i] & 0x0f; break; } - } - while(!(ie->data[i++] & 0x80)); - q931_get_number((unsigned char *) call->redirectingnum, sizeof(call->redirectingnum), ie->data + i, ie->len - i); + } while (!(ie->data[i++] & 0x80)); + q931_get_number((unsigned char *) call->redirecting.from.number.str, sizeof(call->redirecting.from.number.str), ie->data + i, ie->len - i); return 0; } -static FUNC_SEND(transmit_redirecting_number) +static int transmit_redirecting_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { + size_t datalen; + if (order > 1) return 0; - if (call->redirectingnum && *call->redirectingnum) { - ie->data[0] = call->redirectingplan; - ie->data[1] = call->redirectingpres; - ie->data[2] = (call->redirectingreason & 0x0f) | 0x80; - memcpy(ie->data + 3, call->redirectingnum, strlen(call->redirectingnum)); - return strlen(call->redirectingnum) + 3 + 2; + if (!call->redirecting.from.number.valid) { + return 0; } - return 0; + + datalen = strlen(call->redirecting.from.number.str); + ie->data[0] = call->redirecting.from.number.plan; +#if 1 + /* ETSI and Q.952 do not define the screening field */ + ie->data[1] = call->redirecting.from.number.presentation & PRI_PRES_RESTRICTION; +#else + /* Q.931 defines the screening field */ + ie->data[1] = call->redirecting.from.number.presentation; +#endif + ie->data[2] = (call->redirecting.reason & 0x0f) | 0x80; + memcpy(ie->data + 3, call->redirecting.from.number.str, datalen); + return datalen + (3 + 2); } -static FUNC_DUMP(dump_redirecting_subaddr) +static void dump_redirecting_subaddr(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - unsigned char cnum[256]; - q931_get_number(cnum, sizeof(cnum), ie->data + 2, len - 4); - pri_message(pri, "%c Redirecting Sub-Address (len=%2d) [ Ext: %d Type: %s (%d) O: %d '%s' ]\n", - prefix, len, ie->data[0] >> 7, - subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4, - (ie->data[0] & 0x08) >> 3, cnum); + dump_subaddr_helper(full_ie, ctrl, ie, 2, len, len - 4, prefix, "Redirecting"); } -static FUNC_RECV(receive_calling_party_subaddr) +static int receive_redirection_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { - /* copy digits to call->callingsubaddr */ - q931_get_number((unsigned char *) call->callingsubaddr, sizeof(call->callingsubaddr), ie->data + 1, len - 3); + int i = 0; + + call->redirection_number.valid = 1; + call->redirection_number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + /* To follow Q.931 (4.5.1), we must search for start of octet 4 by + walking through all bytes until one with ext bit (8) set to 1 */ + do { + switch (i) { + case 0: + call->redirection_number.plan = ie->data[i] & 0x7f; + break; + case 1: + /* Keep only the presentation and screening fields */ + call->redirection_number.presentation = + ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); + break; + } + } while (!(ie->data[i++] & 0x80)); + q931_get_number((unsigned char *) call->redirection_number.str, sizeof(call->redirection_number.str), ie->data + i, ie->len - i); return 0; } -static FUNC_RECV(receive_called_party_number) +static int transmit_redirection_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { - /* copy digits to call->callednum */ - q931_get_number((unsigned char *) call->callednum, sizeof(call->callednum), ie->data + 1, len - 3); - call->calledplan = ie->data[0] & 0x7f; - return 0; + size_t datalen; + + if (order > 1) { + return 0; + } + if (!call->redirection_number.valid) { + return 0; + } + + datalen = strlen(call->redirection_number.str); + ie->data[0] = call->redirection_number.plan; + ie->data[1] = (call->redirection_number.presentation & PRI_PRES_RESTRICTION) | 0x80; + memcpy(ie->data + 2, call->redirection_number.str, datalen); + return datalen + (2 + 2); } -static FUNC_SEND(transmit_called_party_number) +static int receive_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { - ie->data[0] = 0x80 | call->calledplan; - if (*call->callednum) - memcpy(ie->data + 1, call->callednum, strlen(call->callednum)); - return strlen(call->callednum) + 3; + if (len < 3) { + return -1; + } + + return receive_subaddr_helper(full_ie, ctrl, &call->remote_id.subaddress, msgtype, ie, + 1, len - 3); } -static FUNC_RECV(receive_calling_party_number) +static int transmit_calling_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { - u_int8_t *data; - size_t length; - - if (ie->data[0] & 0x80) { - data = ie->data + 1; - length = len - 3; - call->callerpres = 0; /* PI presentation allowed SI user-provided, not screened */ - } else { - data = ie->data + 2; - length = len - 4; - call->callerpres = ie->data[1] & 0x7f; + return transmit_subaddr_helper(full_ie, ctrl, &call->local_id.subaddress, msgtype, ie, + 1, len, order); +} + +static int receive_called_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) +{ + if (len < 3) { + return -1; } + return receive_subaddr_helper(full_ie, ctrl, &call->called.subaddress, msgtype, ie, 1, + len - 3); +} - if (call->callerpres == PRES_ALLOWED_NETWORK_NUMBER || - call->callerpres == PRES_PROHIB_NETWORK_NUMBER) { - q931_get_number((u_int8_t *)call->callerani, sizeof(call->callerani), data, length); - call->callerplanani = ie->data[0] & 0x7f; +static int transmit_called_party_subaddr(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) +{ + return transmit_subaddr_helper(full_ie, ctrl, &call->called.subaddress, msgtype, ie, + 1, len, order); +} - if (!*call->callernum) { /*Copy ANI to CallerID if CallerID is not already set */ - libpri_copy_string(call->callernum, call->callerani, sizeof(call->callernum)); - call->callerplan = call->callerplanani; +static int receive_called_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) +{ + size_t called_len; + size_t max_len; + char *called_end; + + if (len < 3) { + return -1; + } + + call->called.number.valid = 1; + call->called.number.plan = ie->data[0] & 0x7f; + if (msgtype == Q931_SETUP) { + q931_get_number((unsigned char *) call->called.number.str, + sizeof(call->called.number.str), ie->data + 1, len - 3); + } else if (call->ourcallstate == Q931_CALL_STATE_OVERLAP_RECEIVING) { + /* + * Since we are receiving overlap digits now, we need to append + * them to any previously received digits in call->called.number.str. + */ + called_len = strlen(call->called.number.str); + called_end = call->called.number.str + called_len; + max_len = (sizeof(call->called.number.str) - 1) - called_len; + if (max_len < len - 3) { + called_len = max_len; + } else { + called_len = len - 3; } - - } else { - q931_get_number((u_int8_t *)call->callernum, sizeof(call->callernum), data, length); - call->callerplan = ie->data[0] & 0x7f; + strncat(called_end, (char *) ie->data + 1, called_len); } + q931_get_number((unsigned char *) call->overlap_digits, sizeof(call->overlap_digits), + ie->data + 1, len - 3); return 0; } -static FUNC_SEND(transmit_calling_party_number) +static int transmit_called_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { - ie->data[0] = call->callerplan; - ie->data[1] = 0x80 | call->callerpres; - if (*call->callernum) - memcpy(ie->data + 2, call->callernum, strlen(call->callernum)); - return strlen(call->callernum) + 4; + size_t datalen; + + if (!call->called.number.valid) { + return 0; + } + + datalen = strlen(call->overlap_digits); + ie->data[0] = 0x80 | call->called.number.plan; + memcpy(ie->data + 1, call->overlap_digits, datalen); + return datalen + (1 + 2); } -static FUNC_DUMP(dump_user_user) +static int receive_calling_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { + int i = 0; + + call->remote_id.number.valid = 1; + call->remote_id.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + /* To follow Q.931 (4.5.1), we must search for start of octet 4 by + walking through all bytes until one with ext bit (8) set to 1 */ + do { + switch (i) { + case 0: + call->remote_id.number.plan = ie->data[i] & 0x7f; + break; + case 1: + /* Keep only the presentation and screening fields */ + call->remote_id.number.presentation = + ie->data[i] & (PRI_PRES_RESTRICTION | PRI_PRES_NUMBER_TYPE); + break; + } + } while (!(ie->data[i++] & 0x80)); + q931_get_number((unsigned char *) call->remote_id.number.str, + sizeof(call->remote_id.number.str), ie->data + i, ie->len - i); + + return 0; +} + +static int transmit_calling_party_number(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) +{ + size_t datalen; + + if (!call->local_id.number.valid) { + return 0; + } + + datalen = strlen(call->local_id.number.str); + ie->data[0] = call->local_id.number.plan; + ie->data[1] = 0x80 | call->local_id.number.presentation; + memcpy(ie->data + 2, call->local_id.number.str, datalen); + return datalen + (2 + 2); +} + +static void dump_user_user(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ int x; - pri_message(pri, "%c User-User Information (len=%2d) [", prefix, len); + pri_message(ctrl, "%c User-User Information (len=%2d) [", prefix, len); for (x=0;xlen;x++) - pri_message(pri, " %02x", ie->data[x] & 0x7f); - pri_message(pri, " ]\n"); + pri_message(ctrl, " %02x", ie->data[x] & 0x7f); + pri_message(ctrl, " ]\n"); } -static FUNC_RECV(receive_user_user) +static int receive_user_user(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->useruserprotocoldisc = ie->data[0] & 0xff; if (call->useruserprotocoldisc == 4) /* IA5 */ @@ -1159,7 +2064,7 @@ return 0; } -static FUNC_SEND(transmit_user_user) +static int transmit_user_user(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { int datalen = strlen(call->useruserinfo); if (datalen > 0) { @@ -1180,6 +2085,29 @@ return 0; } +static void dump_change_status(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ + int x; + + pri_message(ctrl, "%c Change Status Information (len=%2d) [", prefix, len); + for (x=0; xlen; x++) { + pri_message(ctrl, " %02x", ie->data[x] & 0x7f); + } + pri_message(ctrl, " ]\n"); +} + +static int receive_change_status(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) +{ + call->changestatus = ie->data[0] & 0x0f; + return 0; +} + +static int transmit_change_status(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) +{ + ie->data[0] = 0xc0 | call->changestatus; + return 3; +} + static char *prog2str(int prog) { static struct msgtype progs[] = { @@ -1222,47 +2150,83 @@ return code2str(loc, locs, sizeof(locs) / sizeof(locs[0])); } -static FUNC_DUMP(dump_progress_indicator) +static void dump_progress_indicator(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - pri_message(pri, "%c Progress Indicator (len=%2d) [ Ext: %d Coding: %s (%d) 0: %d Location: %s (%d)\n", + pri_message(ctrl, "%c Progress Indicator (len=%2d) [ Ext: %d Coding: %s (%d) 0: %d Location: %s (%d)\n", prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5, (ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf); - pri_message(pri, "%c Ext: %d Progress Description: %s (%d) ]\n", + pri_message(ctrl, "%c Ext: %d Progress Description: %s (%d) ]\n", prefix, ie->data[1] >> 7, prog2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f); } -static FUNC_RECV(receive_display) +static int receive_display(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { unsigned char *data; + + switch (msgtype) { + case Q931_SETUP: + case Q931_CONNECT: + /* + * Only keep the display message on SETUP and CONNECT messages + * as the remote name. + */ + break; + default: + return 0; + } + + call->remote_id.name.valid = 1; + data = ie->data; if (data[0] & 0x80) { /* Skip over character set */ data++; len--; } - q931_get_number((unsigned char *) call->callername, sizeof(call->callername), data, len - 2); + call->remote_id.name.char_set = PRI_CHAR_SET_ISO8859_1; + + q931_get_number((unsigned char *) call->remote_id.name.str, sizeof(call->remote_id.name.str), data, len - 2); + if (call->remote_id.name.str[0]) { + call->remote_id.name.presentation = PRI_PRES_ALLOWED; + } else { + call->remote_id.name.presentation = PRI_PRES_RESTRICTED; + } return 0; } -static FUNC_SEND(transmit_display) +static int transmit_display(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { + size_t datalen; int i; - - if ((pri->switchtype == PRI_SWITCH_QSIG) || - ((pri->switchtype == PRI_SWITCH_EUROISDN_E1) && (pri->localtype == PRI_CPE)) || - !call->callername[0]) - return 0; i = 0; - if(pri->switchtype != PRI_SWITCH_EUROISDN_E1) { + + if (!call->local_id.name.valid || !call->local_id.name.str[0]) { + return 0; + } + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + /* Q.SIG supports names */ + return 0; + case PRI_SWITCH_EUROISDN_E1: + case PRI_SWITCH_EUROISDN_T1: + if (ctrl->localtype == PRI_CPE) { + return 0; + } + break; + default: + /* Prefix name with character set indicator. */ ie->data[0] = 0xb1; ++i; + break; } - memcpy(ie->data + i, call->callername, strlen(call->callername)); - return 2 + i + strlen(call->callername); + + datalen = strlen(call->local_id.name.str); + memcpy(ie->data + i, call->local_id.name.str, datalen); + return 2 + i + datalen; } -static FUNC_RECV(receive_progress_indicator) +static int receive_progress_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->progloc = ie->data[0] & 0xf; call->progcode = (ie->data[0] & 0x60) >> 5; @@ -1298,153 +2262,204 @@ call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER; break; default: - pri_error(pri, "XXX Invalid Progress indicator value received: %02x\n",(ie->data[1] & 0x7f)); + pri_error(ctrl, "XXX Invalid Progress indicator value received: %02x\n",(ie->data[1] & 0x7f)); break; } return 0; } -static FUNC_SEND(transmit_facility) +static void q931_apdu_timeout(void *data); + +static int transmit_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { - struct apdu_event *tmp; - int i = 0; + struct apdu_event **prev; + struct apdu_event *cur; + int apdu_len; - for (tmp = call->apdus; tmp; tmp = tmp->next) { - if ((tmp->message == msgtype) && !tmp->sent) + for (prev = &call->apdus, cur = call->apdus; + cur; + prev = &cur->next, cur = cur->next) { + if (!cur->sent && cur->message == msgtype) { break; + } } - - if (!tmp) /* No APDU found */ + if (!cur) { + /* No APDU found */ return 0; + } - if (tmp->apdu_len > 235) { /* TODO: find out how much space we can use */ - pri_message(pri, "Requested APDU (%d bytes) is too long\n", tmp->apdu_len); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "Adding facility ie contents to send in %s message:\n", + msg2str(msgtype)); + facility_decode_dump(ctrl, cur->apdu, cur->apdu_len); + } + + if (len < cur->apdu_len) { + pri_error(ctrl, + "Could not fit facility ie in message. Size needed:%d Available space:%d\n", + cur->apdu_len + 2, len); + + /* Remove APDU from list. */ + *prev = cur->next; + + if (cur->response.callback) { + /* Indicate to callback that the APDU had a problem getting sent. */ + cur->response.callback(APDU_CALLBACK_REASON_ERROR, ctrl, call, cur, NULL); + } + + free(cur); return 0; } - - memcpy(&ie->data[i], tmp->apdu, tmp->apdu_len); - i += tmp->apdu_len; - tmp->sent = 1; - return i + 2; + memcpy(ie->data, cur->apdu, cur->apdu_len); + apdu_len = cur->apdu_len; + cur->sent = 1; + + if (cur->response.callback && cur->response.timeout_time) { + int duration; + + if (0 < cur->response.timeout_time) { + /* Sender specified timeout duration. */ + duration = cur->response.timeout_time; + } else { + /* Sender wants to use the typical timeout duration. */ + duration = ctrl->timers[PRI_TIMER_T_RESPONSE]; + } + cur->timer = pri_schedule_event(ctrl, duration, q931_apdu_timeout, cur); + if (!cur->timer) { + /* Remove APDU from list. */ + *prev = cur->next; + + /* Indicate to callback that the APDU had a problem getting sent. */ + cur->response.callback(APDU_CALLBACK_REASON_ERROR, ctrl, call, cur, NULL); + + free(cur); + } + } else if (!cur->timer) { + /* Remove APDU from list. */ + *prev = cur->next; + free(cur); + } + + return apdu_len + 2; } -static FUNC_RECV(receive_facility) +static int receive_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { - int i = 0; - int protocol, next_protocol; - struct rose_component *comp = NULL; - enum { - Q932_STATE_NFE, /* Network facility extension */ - Q932_STATE_NPP, /* Network protocol profile */ - Q932_STATE_INTERPRETATION, /* Interpretation component */ - Q932_STATE_SERVICE /* Service component(s) */ - } state = Q932_STATE_SERVICE; -#define Q932_HANDLE_PROC(component, my_state, name, handler) \ - case component: \ - if(state > my_state) { \ - pri_error(pri, "!! %s component received in wrong place\n"); \ - break; \ - } \ - state = my_state; \ - if (pri->debug) \ - pri_message(pri, "Handle Q.932 %s component\n", name); \ - (handler)(pri, call, ie, comp->data, comp->len); \ - break; -#define Q932_HANDLE_NULL(component, my_state, name, handle) \ - case component: \ - if(state > my_state) { \ - pri_error(pri, "!! %s component received in wrong place\n"); \ - break; \ - } \ - state = my_state; \ - if (pri->debug & PRI_DEBUG_APDU) \ - pri_message(pri, "Q.932 %s component is not handled\n", name); \ - break; - - if (ie->len < 1) + /* Delay processing facility ie's till after all other ie's are processed. */ + if (MAX_FACILITY_IES <= ctrl->facility.count) { + pri_message(ctrl, "!! Too many facility ie's to delay.\n"); return -1; + } + /* Make sure we have enough room for the protocol profile ie octet(s) */ + if (ie->data + ie->len < ie->data + 2) { + return -1; + } - switch(next_protocol = protocol = (ie->data[i] & 0x1f)) { - case Q932_PROTOCOL_CMIP: - case Q932_PROTOCOL_ACSE: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "!! Don't know how to handle Q.932 Protocol Profile of type 0x%X\n", protocol); + /* Save the facility ie location for delayed decode. */ + ctrl->facility.ie[ctrl->facility.count] = ie; + ctrl->facility.codeset[ctrl->facility.count] = Q931_IE_CODESET((unsigned) full_ie); + ++ctrl->facility.count; + return 0; +} + +static int process_facility(struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie) +{ + struct fac_extension_header header; + struct rose_message rose; + const unsigned char *pos; + const unsigned char *end; + + pos = ie->data; + end = ie->data + ie->len; + + /* Make sure we have enough room for the protocol profile ie octet(s) */ + if (end < pos + 2) { return -1; + } + switch (*pos & Q932_PROTOCOL_MASK) { + case Q932_PROTOCOL_ROSE: case Q932_PROTOCOL_EXTENSIONS: - state = Q932_STATE_NFE; - next_protocol = Q932_PROTOCOL_ROSE; break; - case Q932_PROTOCOL_ROSE: - break; default: - pri_error(pri, "!! Invalid Q.932 Protocol Profile of type 0x%X received\n", protocol); + case Q932_PROTOCOL_CMIP: + case Q932_PROTOCOL_ACSE: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, + "!! Don't know how to handle Q.932 Protocol Profile type 0x%X\n", + *pos & Q932_PROTOCOL_MASK); + } return -1; } - /* Service indicator octet - Just ignore for now */ - if (!(ie->data[i] & 0x80)) - i++; - i++; + if (!(*pos & 0x80)) { + /* DMS-100 Service indicator octet - Just ignore for now */ + ++pos; + } + ++pos; - if (ie->len < 3) + if (ctrl->debug & PRI_DEBUG_APDU) { + asn1_dump(ctrl, pos, end); + } + + pos = fac_dec_extension_header(ctrl, pos, end, &header); + if (!pos) { return -1; - - while ((i+1 < ie->len) && (&ie->data[i])) { - comp = (struct rose_component*)&ie->data[i]; - if (comp->type) { - if (protocol == Q932_PROTOCOL_EXTENSIONS) { - switch (comp->type) { - Q932_HANDLE_NULL(COMP_TYPE_INTERPRETATION, Q932_STATE_INTERPRETATION, "Interpretation", NULL); - Q932_HANDLE_NULL(COMP_TYPE_NFE, Q932_STATE_NFE, "Network facility extensions", NULL); - Q932_HANDLE_NULL(COMP_TYPE_NETWORK_PROTOCOL_PROFILE, Q932_STATE_NPP, "Network protocol profile", NULL); - default: - protocol = next_protocol; - break; - } - } - switch (protocol) { - case Q932_PROTOCOL_ROSE: - switch (comp->type) { - Q932_HANDLE_PROC(COMP_TYPE_INVOKE, Q932_STATE_SERVICE, "ROSE Invoke", rose_invoke_decode); - Q932_HANDLE_PROC(COMP_TYPE_RETURN_RESULT, Q932_STATE_SERVICE, "ROSE return result", rose_return_result_decode); - Q932_HANDLE_PROC(COMP_TYPE_RETURN_ERROR, Q932_STATE_SERVICE, "ROSE return error", rose_return_error_decode); - Q932_HANDLE_PROC(COMP_TYPE_REJECT, Q932_STATE_SERVICE, "ROSE reject", rose_reject_decode); - default: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "Don't know how to handle ROSE component of type 0x%X\n", comp->type); - break; - } - break; - case Q932_PROTOCOL_CMIP: - switch (comp->type) { - default: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "Don't know how to handle CMIP component of type 0x%X\n", comp->type); - break; - } - break; - case Q932_PROTOCOL_ACSE: - switch (comp->type) { - default: - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, "Don't know how to handle ACSE component of type 0x%X\n", comp->type); - break; - } - break; - } + } + if (header.npp_present) { + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, + "!! Don't know how to handle Network Protocol Profile type 0x%X\n", + header.npp); } - i += (comp->len + 2); + return -1; } -#undef Q932_HANDLE + pos = rose_decode(ctrl, pos, end, &rose); + if (!pos) { + return -1; + } + switch (rose.type) { + case ROSE_COMP_TYPE_INVOKE: + rose_handle_invoke(ctrl, call, msgtype, ie, &header, &rose.component.invoke); + break; + case ROSE_COMP_TYPE_RESULT: + rose_handle_result(ctrl, call, msgtype, ie, &header, &rose.component.result); + break; + case ROSE_COMP_TYPE_ERROR: + rose_handle_error(ctrl, call, msgtype, ie, &header, &rose.component.error); + break; + case ROSE_COMP_TYPE_REJECT: + rose_handle_reject(ctrl, call, msgtype, ie, &header, &rose.component.reject); + break; + default: + return -1; + } return 0; } -static FUNC_SEND(transmit_progress_indicator) +static void q931_handle_facilities(struct pri *ctrl, q931_call *call, int msgtype) { + unsigned idx; + unsigned codeset; + unsigned full_ie; + q931_ie *ie; + + for (idx = 0; idx < ctrl->facility.count; ++idx) { + ie = ctrl->facility.ie[idx]; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + codeset = ctrl->facility.codeset[idx]; + full_ie = Q931_FULL_IE(codeset, ie->ie); + pri_message(ctrl, "-- Delayed processing IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); + } + process_facility(ctrl, call, msgtype, ie); + } +} + +static int transmit_progress_indicator(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) +{ int code, mask; /* Can't send progress indicator on GR-303 -- EVER! */ - if (pri->subchannel && !pri->bri) + if (ctrl->subchannel && !ctrl->bri) return 0; if (call->progressmask > 0) { if (call->progressmask & (mask = PRI_PROG_CALL_NOT_E2E_ISDN)) @@ -1467,7 +2482,7 @@ code = Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER; else { code = 0; - pri_error(pri, "XXX Undefined progress bit: %x\n", call->progressmask); + pri_error(ctrl, "XXX Undefined progress bit: %x\n", call->progressmask); } if (code) { ie->data[0] = 0x80 | (call->progcode << 5) | (call->progloc); @@ -1479,82 +2494,139 @@ /* Leave off */ return 0; } -static FUNC_SEND(transmit_call_state) +static int transmit_call_state(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { - if (call->ourcallstate > -1 ) { + ie->data[0] = Q931_CALL_STATE_NULL; + switch (call->ourcallstate) { + case Q931_CALL_STATE_NULL: + case Q931_CALL_STATE_CALL_INITIATED: + case Q931_CALL_STATE_OVERLAP_SENDING: + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_CALL_PRESENT: + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + case Q931_CALL_STATE_ACTIVE: + case Q931_CALL_STATE_DISCONNECT_REQUEST: + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_SUSPEND_REQUEST: + case Q931_CALL_STATE_RESUME_REQUEST: + case Q931_CALL_STATE_RELEASE_REQUEST: + case Q931_CALL_STATE_CALL_ABORT: + case Q931_CALL_STATE_OVERLAP_RECEIVING: + case Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE: + case Q931_CALL_STATE_RESTART_REQUEST: + case Q931_CALL_STATE_RESTART: ie->data[0] = call->ourcallstate; - return 3; + break; + case Q931_CALL_STATE_NOT_SET: + break; } - return 0; + return 3; } -static FUNC_RECV(receive_call_state) +static int receive_call_state(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->sugcallstate = ie->data[0] & 0x3f; return 0; } -static char *callstate2str(int callstate) +/*! + * \brief Convert the internal Q.931 call state to a string. + * + * \param callstate Internal Q.931 call state. + * + * \return String equivalent of the given Q.931 call state. + */ +const char *q931_call_state_str(enum Q931_CALL_STATE callstate) { static struct msgtype callstates[] = { - { 0, "Null" }, - { 1, "Call Initiated" }, - { 2, "Overlap sending" }, - { 3, "Outgoing call Proceeding" }, - { 4, "Call Delivered" }, - { 6, "Call Present" }, - { 7, "Call Received" }, - { 8, "Connect Request" }, - { 9, "Incoming Call Proceeding" }, - { 10, "Active" }, - { 11, "Disconnect Request" }, - { 12, "Disconnect Indication" }, - { 15, "Suspend Request" }, - { 17, "Resume Request" }, - { 19, "Release Request" }, - { 22, "Call Abort" }, - { 25, "Overlap Receiving" }, - { 61, "Restart Request" }, - { 62, "Restart" }, +/* *INDENT-OFF* */ + { Q931_CALL_STATE_NULL, "Null" }, + { Q931_CALL_STATE_CALL_INITIATED, "Call Initiated" }, + { Q931_CALL_STATE_OVERLAP_SENDING, "Overlap Sending" }, + { Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING, "Outgoing Call Proceeding" }, + { Q931_CALL_STATE_CALL_DELIVERED, "Call Delivered" }, + { Q931_CALL_STATE_CALL_PRESENT, "Call Present" }, + { Q931_CALL_STATE_CALL_RECEIVED, "Call Received" }, + { Q931_CALL_STATE_CONNECT_REQUEST, "Connect Request" }, + { Q931_CALL_STATE_INCOMING_CALL_PROCEEDING, "Incoming Call Proceeding" }, + { Q931_CALL_STATE_ACTIVE, "Active" }, + { Q931_CALL_STATE_DISCONNECT_REQUEST, "Disconnect Request" }, + { Q931_CALL_STATE_DISCONNECT_INDICATION, "Disconnect Indication" }, + { Q931_CALL_STATE_SUSPEND_REQUEST, "Suspend Request" }, + { Q931_CALL_STATE_RESUME_REQUEST, "Resume Request" }, + { Q931_CALL_STATE_RELEASE_REQUEST, "Release Request" }, + { Q931_CALL_STATE_CALL_ABORT, "Call Abort" }, + { Q931_CALL_STATE_OVERLAP_RECEIVING, "Overlap Receiving" }, + { Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE, "Call Independent Service" }, + { Q931_CALL_STATE_RESTART_REQUEST, "Restart Request" }, + { Q931_CALL_STATE_RESTART, "Restart" }, + { Q931_CALL_STATE_NOT_SET, "Not set. Internal use only." }, +/* *INDENT-ON* */ }; - return code2str(callstate, callstates, sizeof(callstates) / sizeof(callstates[0])); + return code2str(callstate, callstates, ARRAY_LEN(callstates)); } -static FUNC_DUMP(dump_call_state) +/*! + * \internal + * \brief Convert the Q.932 supplementary hold state to a string. + * + * \param state Q.932 supplementary hold state. + * + * \return String equivalent of the given hold state. + */ +static const char *q931_hold_state_str(enum Q931_HOLD_STATE state) { - pri_message(pri, "%c Call State (len=%2d) [ Ext: %d Coding: %s (%d) Call state: %s (%d)\n", + static struct msgtype hold_states[] = { +/* *INDENT-OFF* */ + { Q931_HOLD_STATE_IDLE, "Idle" }, + { Q931_HOLD_STATE_HOLD_REQ, "Hold Request" }, + { Q931_HOLD_STATE_HOLD_IND, "Hold Indication" }, + { Q931_HOLD_STATE_CALL_HELD, "Call Held" }, + { Q931_HOLD_STATE_RETRIEVE_REQ, "Retrieve Request" }, + { Q931_HOLD_STATE_RETRIEVE_IND, "Retrieve Indication" }, +/* *INDENT-ON* */ + }; + return code2str(state, hold_states, ARRAY_LEN(hold_states)); +} + +static void dump_call_state(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ + pri_message(ctrl, "%c Call State (len=%2d) [ Ext: %d Coding: %s (%d) Call state: %s (%d)\n", prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0xC0) >> 6), (ie->data[0] & 0xC0) >> 6, - callstate2str(ie->data[0] & 0x3f), ie->data[0] & 0x3f); + q931_call_state_str(ie->data[0] & 0x3f), ie->data[0] & 0x3f); } -static FUNC_DUMP(dump_call_identity) +static void dump_call_identity(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int x; - pri_message(pri, "%c Call Identity (len=%2d) [ ", prefix, len); + pri_message(ctrl, "%c Call Identity (len=%2d) [ ", prefix, len); for (x=0;xlen;x++) - pri_message(pri, "0x%02X ", ie->data[x]); - pri_message(pri, " ]\n"); + pri_message(ctrl, "0x%02X ", ie->data[x]); + pri_message(ctrl, " ]\n"); } -static FUNC_DUMP(dump_time_date) +static void dump_time_date(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - pri_message(pri, "%c Time Date (len=%2d) [ ", prefix, len); + pri_message(ctrl, "%c Time Date (len=%2d) [ ", prefix, len); if (ie->len > 0) - pri_message(pri, "%02d", ie->data[0]); + pri_message(ctrl, "%02d", ie->data[0]); if (ie->len > 1) - pri_message(pri, "-%02d", ie->data[1]); + pri_message(ctrl, "-%02d", ie->data[1]); if (ie->len > 2) - pri_message(pri, "-%02d", ie->data[2]); + pri_message(ctrl, "-%02d", ie->data[2]); if (ie->len > 3) - pri_message(pri, " %02d", ie->data[3]); + pri_message(ctrl, " %02d", ie->data[3]); if (ie->len > 4) - pri_message(pri, ":%02d", ie->data[4]); + pri_message(ctrl, ":%02d", ie->data[4]); if (ie->len > 5) - pri_message(pri, ":%02d", ie->data[5]); - pri_message(pri, " ]\n"); + pri_message(ctrl, ":%02d", ie->data[5]); + pri_message(ctrl, " ]\n"); } -static FUNC_DUMP(dump_keypad_facility) +static void dump_keypad_facility(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { char tmp[64]; @@ -1563,10 +2635,10 @@ memcpy(tmp, ie->data, ie->len); tmp[ie->len] = '\0'; - pri_message(pri, "%c Keypad Facility (len=%2d) [ %s ]\n", prefix, ie->len, tmp ); + pri_message(ctrl, "%c Keypad Facility (len=%2d) [ %s ]\n", prefix, ie->len, tmp ); } -static FUNC_RECV(receive_keypad_facility) +static int receive_keypad_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int mylen; @@ -1584,27 +2656,19 @@ return 0; } -static FUNC_SEND(transmit_keypad_facility) +static int transmit_keypad_facility(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { int sublen; sublen = strlen(call->keypad_digits); - - if (sublen > 32) { - sublen = 32; - call->keypad_digits[32] = '\0'; - } - if (sublen) { - libpri_copy_string((char *)ie->data, (char *)call->keypad_digits, sizeof(call->keypad_digits)); - /* Make sure we clear the field */ - call->keypad_digits[0] = '\0'; + libpri_copy_string((char *) ie->data, call->keypad_digits, sizeof(call->keypad_digits)); return sublen + 2; } else return 0; } -static FUNC_DUMP(dump_display) +static void dump_display(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int x, y; char *buf = malloc(len + 1); @@ -1618,7 +2682,7 @@ for (y=x; xlen; x++) buf[x] = ie->data[x] & 0x7f; buf[x] = '\0'; - pri_message(pri, "%c Display (len=%2d) %s[ %s ]\n", prefix, ie->len, tmp, &buf[y]); + pri_message(ctrl, "%c Display (len=%2d) %s[ %s ]\n", prefix, ie->len, tmp, &buf[y]); free(buf); } } @@ -1626,10 +2690,10 @@ #define CHECK_OVERFLOW(limit) \ if (tmpptr - tmp + limit >= sizeof(tmp)) { \ *tmpptr = '\0'; \ - pri_message(pri, "%s", tmpptr = tmp); \ + pri_message(ctrl, "%s", tmpptr = tmp); \ } -static void dump_ie_data(struct pri *pri, unsigned char *c, int len) +static void dump_ie_data(struct pri *ctrl, unsigned char *c, int len) { static char hexs[16] = "0123456789ABCDEF"; char tmp[1024], *tmpptr; @@ -1665,47 +2729,54 @@ if (lastascii) *tmpptr++ = '\''; *tmpptr = '\0'; - pri_message(pri, "%s", tmp); + pri_message(ctrl, "%s", tmp); } -static FUNC_DUMP(dump_facility) +static void dump_facility(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - int dataat = (ie->data[0] & 0x80) ? 1 : 2; - pri_message(pri, "%c Facility (len=%2d, codeset=%d) [ ", prefix, len, Q931_IE_CODESET(full_ie)); - dump_ie_data(pri, ie->data, ie->len); - pri_message(NULL, " ]\n"); + pri_message(ctrl, "%c Facility (len=%2d, codeset=%d) [ ", prefix, len, Q931_IE_CODESET(full_ie)); + dump_ie_data(ctrl, ie->data, ie->len); + pri_message(ctrl, " ]\n"); +#if 0 /* Lets not dump parse of facility contents here anymore. */ + /* + * The ASN.1 decode dump has already been done when the facility ie was added to the outgoing + * message or the ASN.1 decode dump will be done when the facility ie is processed on incoming + * messages. This dump is redundant and very noisy. + */ if (ie->len > 1) { - pri_message(pri, "PROTOCOL %02X\n", ie->data[0] & ASN1_TYPE_MASK); - asn1_dump(pri, &ie->data[dataat], ie->len - dataat); + int dataat = (ie->data[0] & 0x80) ? 1 : 2; + + pri_message(ctrl, "PROTOCOL %02X\n", ie->data[0] & Q932_PROTOCOL_MASK); + asn1_dump(ctrl, ie->data + dataat, ie->data + ie->len); } - +#endif /* Lets not dump parse of facility contents here anymore. */ } -static FUNC_DUMP(dump_network_spec_fac) +static void dump_network_spec_fac(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - pri_message(pri, "%c Network-Specific Facilities (len=%2d) [ ", prefix, ie->len); + pri_message(ctrl, "%c Network-Specific Facilities (len=%2d) [ ", prefix, ie->len); if (ie->data[0] == 0x00) { - pri_message(pri, "%s", code2str(ie->data[1], facilities, sizeof(facilities) / sizeof(facilities[0]))); + pri_message(ctrl, "%s", code2str(ie->data[1], facilities, ARRAY_LEN(facilities))); } else - dump_ie_data(pri, ie->data, ie->len); - pri_message(pri, " ]\n"); + dump_ie_data(ctrl, ie->data, ie->len); + pri_message(ctrl, " ]\n"); } -static FUNC_RECV(receive_network_spec_fac) +static int receive_network_spec_fac(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { return 0; } -static FUNC_SEND(transmit_network_spec_fac) +static int transmit_network_spec_fac(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { /* We are ready to transmit single IE only */ if (order > 1) return 0; - if (pri->nsf != PRI_NSF_NONE) { + if (ctrl->nsf != PRI_NSF_NONE) { ie->data[0] = 0x00; - ie->data[1] = pri->nsf; + ie->data[1] = ctrl->nsf; return 4; } /* Leave off */ @@ -1732,13 +2803,13 @@ return code2str(cause, causeclasses, sizeof(causeclasses) / sizeof(causeclasses[0])); } -static FUNC_DUMP(dump_cause) +static void dump_cause(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int x; - pri_message(pri, "%c Cause (len=%2d) [ Ext: %d Coding: %s (%d) Spare: %d Location: %s (%d)\n", + pri_message(ctrl, "%c Cause (len=%2d) [ Ext: %d Coding: %s (%d) Spare: %d Location: %s (%d)\n", prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5, (ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf); - pri_message(pri, "%c Ext: %d Cause: %s (%d), class = %s (%d) ]\n", + pri_message(ctrl, "%c Ext: %d Cause: %s (%d), class = %s (%d) ]\n", prefix, (ie->data[1] >> 7), pri_cause2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, pri_causeclass2str((ie->data[1] & 0x7f) >> 4), (ie->data[1] & 0x7f) >> 4); if (ie->len < 3) @@ -1747,29 +2818,29 @@ switch(ie->data[1] & 0x7f) { case PRI_CAUSE_IE_NONEXIST: for (x=2;xlen;x++) - pri_message(pri, "%c Cause data %d: %02x (%d, %s IE)\n", prefix, x-1, ie->data[x], ie->data[x], ie2str(ie->data[x])); + pri_message(ctrl, "%c Cause data %d: %02x (%d, %s IE)\n", prefix, x-1, ie->data[x], ie->data[x], ie2str(ie->data[x])); break; case PRI_CAUSE_WRONG_CALL_STATE: for (x=2;xlen;x++) - pri_message(pri, "%c Cause data %d: %02x (%d, %s message)\n", prefix, x-1, ie->data[x], ie->data[x], msg2str(ie->data[x])); + pri_message(ctrl, "%c Cause data %d: %02x (%d, %s message)\n", prefix, x-1, ie->data[x], ie->data[x], msg2str(ie->data[x])); break; case PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE: - pri_message(pri, "%c Cause data:", prefix); + pri_message(ctrl, "%c Cause data:", prefix); for (x=2;xlen;x++) - pri_message(pri, " %02x", ie->data[x]); - pri_message(pri, " (Timer T"); + pri_message(ctrl, " %02x", ie->data[x]); + pri_message(ctrl, " (Timer T"); for (x=2;xlen;x++) - pri_message(pri, "%c", ((ie->data[x] >= ' ') && (ie->data[x] < 0x7f)) ? ie->data[x] : '.'); - pri_message(pri, ")\n"); + pri_message(ctrl, "%c", ((ie->data[x] >= ' ') && (ie->data[x] < 0x7f)) ? ie->data[x] : '.'); + pri_message(ctrl, ")\n"); break; default: for (x=2;xlen;x++) - pri_message(pri, "%c Cause data %d: %02x (%d)\n", prefix, x-1, ie->data[x], ie->data[x]); + pri_message(ctrl, "%c Cause data %d: %02x (%d)\n", prefix, x-1, ie->data[x], ie->data[x]); break; } } -static FUNC_RECV(receive_cause) +static int receive_cause(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->causeloc = ie->data[0] & 0xf; call->causecode = (ie->data[0] & 0x60) >> 5; @@ -1777,7 +2848,7 @@ return 0; } -static FUNC_SEND(transmit_cause) +static int transmit_cause(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { /* We are ready to transmit single IE only */ if (order > 1) @@ -1793,23 +2864,23 @@ } } -static FUNC_DUMP(dump_sending_complete) +static void dump_sending_complete(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - pri_message(pri, "%c Sending Complete (len=%2d)\n", prefix, len); + pri_message(ctrl, "%c Sending Complete (len=%2d)\n", prefix, len); } -static FUNC_RECV(receive_sending_complete) +static int receive_sending_complete(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { /* We've got a "Complete" message: Exect no further digits. */ call->complete = 1; return 0; } -static FUNC_SEND(transmit_sending_complete) +static int transmit_sending_complete(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { - if ((pri->overlapdial && call->complete) || /* Explicit */ - (!pri->overlapdial && ((pri->switchtype == PRI_SWITCH_EUROISDN_E1) || - /* Implicit */ (pri->switchtype == PRI_SWITCH_EUROISDN_T1)))) { + if ((ctrl->overlapdial && call->complete) || /* Explicit */ + (!ctrl->overlapdial && ((ctrl->switchtype == PRI_SWITCH_EUROISDN_E1) || + /* Implicit */ (ctrl->switchtype == PRI_SWITCH_EUROISDN_T1)))) { /* Include this single-byte IE */ return 1; } @@ -1846,18 +2917,18 @@ return code2str(info, notifies, sizeof(notifies) / sizeof(notifies[0])); } -static FUNC_DUMP(dump_notify) +static void dump_notify(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - pri_message(pri, "%c Notification indicator (len=%2d): Ext: %d %s (%d)\n", prefix, len, ie->data[0] >> 7, notify2str(ie->data[0] & 0x7f), ie->data[0] & 0x7f); + pri_message(ctrl, "%c Notification indicator (len=%2d): Ext: %d %s (%d)\n", prefix, len, ie->data[0] >> 7, notify2str(ie->data[0] & 0x7f), ie->data[0] & 0x7f); } -static FUNC_RECV(receive_notify) +static int receive_notify(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->notify = ie->data[0] & 0x7F; return 0; } -static FUNC_SEND(transmit_notify) +static int transmit_notify(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { if (call->notify >= 0) { ie->data[0] = 0x80 | call->notify; @@ -1866,9 +2937,9 @@ return 0; } -static FUNC_DUMP(dump_shift) +static void dump_shift(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - pri_message(pri, "%c %sLocking Shift (len=%02d): Requested codeset %d\n", prefix, (full_ie & 8) ? "Non-" : "", len, full_ie & 7); + pri_message(ctrl, "%c %sLocking Shift (len=%02d): Requested codeset %d\n", prefix, (full_ie & 8) ? "Non-" : "", len, full_ie & 7); } static char *lineinfo2str(int info) @@ -1903,21 +2974,21 @@ return code2str(info, lineinfo, sizeof(lineinfo) / sizeof(lineinfo[0])); } -static FUNC_DUMP(dump_line_information) +static void dump_line_information(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - pri_message(pri, "%c Originating Line Information (len=%02d): %s (%d)\n", prefix, len, lineinfo2str(ie->data[0]), ie->data[0]); + pri_message(ctrl, "%c Originating Line Information (len=%02d): %s (%d)\n", prefix, len, lineinfo2str(ie->data[0]), ie->data[0]); } -static FUNC_RECV(receive_line_information) +static int receive_line_information(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { call->ani2 = ie->data[0]; return 0; } -static FUNC_SEND(transmit_line_information) +static int transmit_line_information(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { #if 0 /* XXX Is this IE possible for 4ESS only? XXX */ - if(pri->switchtype == PRI_SWITCH_ATT4ESS) { + if(ctrl->switchtype == PRI_SWITCH_ATT4ESS) { ie->data[0] = 0; return 3; } @@ -1953,53 +3024,54 @@ return code2str(type, gdtype, sizeof(gdtype) / sizeof(gdtype[0])); } -static FUNC_DUMP(dump_generic_digits) +static void dump_generic_digits(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { int encoding; int type; int idx; int value; if (len < 3) { - pri_message(pri, "%c Generic Digits (len=%02d): Invalid length\n", prefix, len); + pri_message(ctrl, "%c Generic Digits (len=%02d): Invalid length\n", prefix, len); return; } encoding = (ie->data[0] >> 5) & 7; type = ie->data[0] & 0x1F; - pri_message(pri, "%c Generic Digits (len=%02d): Encoding %s Type %s\n", prefix, len, gdencoding2str(encoding), gdtype2str(type)); + pri_message(ctrl, "%c Generic Digits (len=%02d): Encoding %s Type %s\n", prefix, len, gdencoding2str(encoding), gdtype2str(type)); if (encoding == 3) { /* Binary */ - pri_message(pri, "%c Don't know how to handle binary encoding\n"); + pri_message(ctrl, "%c Don't know how to handle binary encoding\n", + prefix); return; } if (len == 3) /* No number information */ return; - pri_message(pri, "%c Digits: "); + pri_message(ctrl, "%c Digits: ", prefix); value = 0; for(idx = 3; idx < len; ++idx) { switch(encoding) { case 0: /* BCD even */ case 1: /* BCD odd */ - pri_message(pri, "%d", ie->data[idx-2] & 0x0f); + pri_message(ctrl, "%d", ie->data[idx-2] & 0x0f); value = value * 10 + (ie->data[idx-2] & 0x0f); if(!encoding || (idx+1 < len)) { /* Special handling for BCD odd */ - pri_message(pri, "%d", (ie->data[idx-2] >> 4) & 0x0f); + pri_message(ctrl, "%d", (ie->data[idx-2] >> 4) & 0x0f); value = value * 10 + ((ie->data[idx-2] >> 4) & 0x0f); } break; case 2: /* IA5 */ - pri_message(pri, "%c", ie->data[idx-2]); + pri_message(ctrl, "%c", ie->data[idx-2]); value = value * 10 + ie->data[idx-2] - '0'; break; } } switch(type) { case 4: /* Info Digits */ - pri_message(pri, " - %s", lineinfo2str(value)); + pri_message(ctrl, " - %s", lineinfo2str(value)); break; } - pri_message(pri, "\n"); + pri_message(ctrl, "\n"); } -static FUNC_RECV(receive_generic_digits) +static int receive_generic_digits(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) { int encoding; int type; @@ -2009,13 +3081,13 @@ char number[260]; if (len < 3) { - pri_error(pri, "Invalid length of Generic Digits IE\n"); + pri_error(ctrl, "Invalid length of Generic Digits IE\n"); return -1; } encoding = (ie->data[0] >> 5) & 7; type = ie->data[0] & 0x1F; if (encoding == 3) { /* Binary */ - pri_message(pri, "!! Unable to handle binary encoded Generic Digits IE\n"); + pri_message(ctrl, "!! Unable to handle binary encoded Generic Digits IE\n"); return 0; } if (len == 3) /* No number information */ @@ -2063,10 +3135,13 @@ break; #if 0 case 5: /* Callid */ - if (!call->callernum[0]) { - memcpy(call->callernum, number, sizeof(call->callernum)-1); - call->callerpres = 0; - call->callerplan = 0; + if (!call->remote_id.number.valid) { + call->remote_id.number.valid = 1; + call->remote_id.number.presentation = + PRI_PRES_ALLOWED | PRI_PRES_USER_NUMBER_UNSCREENED; + call->remote_id.number.plan = PRI_UNKNOWN; + libpri_copy_string(call->remote_id.number.str, number, + sizeof(call->remote_id.number.str)); } break; #endif @@ -2074,13 +3149,13 @@ return 0; } -static FUNC_SEND(transmit_generic_digits) +static int transmit_generic_digits(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) { #if 0 /* XXX Is this IE possible for other switches? XXX */ if (order > 1) return 0; - if(pri->switchtype == PRI_SWITCH_NI1) { + if(ctrl->switchtype == PRI_SWITCH_NI1) { ie->data[0] = 0x04; /* BCD even, Info Digits */ ie->data[1] = 0x00; /* POTS */ return 4; @@ -2119,35 +3194,54 @@ } -static FUNC_DUMP(dump_signal) +static void dump_signal(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { - pri_message(pri, "%c Signal (len=%02d): ", prefix, len); + pri_message(ctrl, "%c Signal (len=%02d): ", prefix, len); if (len < 3) { - pri_message(pri, "Invalid length\n"); + pri_message(ctrl, "Invalid length\n"); return; } - pri_message(pri, "Signal %s (%d)\n", signal2str(ie->data[0]), ie->data[0]); + pri_message(ctrl, "Signal %s (%d)\n", signal2str(ie->data[0]), ie->data[0]); } -static FUNC_DUMP(dump_transit_count) +static void dump_transit_count(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) { /* Defined in ECMA-225 */ - pri_message(pri, "%c Transit Count (len=%02d): ", prefix, len); + pri_message(ctrl, "%c Transit Count (len=%02d): ", prefix, len); if (len < 3) { - pri_message(pri, "Invalid length\n"); + pri_message(ctrl, "Invalid length\n"); return; } - pri_message(pri, "Count=%d (0x%02x)\n", ie->data[0] & 0x1f, ie->data[0] & 0x1f); + pri_message(ctrl, "Count=%d (0x%02x)\n", ie->data[0] & 0x1f, ie->data[0] & 0x1f); } +static void dump_reverse_charging_indication(int full_ie, struct pri *ctrl, q931_ie *ie, int len, char prefix) +{ + pri_message(ctrl, "%c Reverse Charging Indication (len=%02d): %d\n", prefix, len, ie->data[0] & 0x7); +} +static int receive_reverse_charging_indication(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len) +{ + call->reversecharge = ie->data[0] & 0x7; + return 0; +} + +static int transmit_reverse_charging_indication(int full_ie, struct pri *ctrl, q931_call *call, int msgtype, q931_ie *ie, int len, int order) +{ + if (call->reversecharge != PRI_REVERSECHARGE_NONE) { + ie->data[0] = 0x80 | (call->reversecharge & 0x7); + return 3; + } + return 0; +} + static struct ie ies[] = { /* Codeset 0 - Common */ - { 1, NATIONAL_CHANGE_STATUS, "Change Status" }, + { 1, NATIONAL_CHANGE_STATUS, "Change Status", dump_change_status, receive_change_status, transmit_change_status }, { 0, Q931_LOCKING_SHIFT, "Locking Shift", dump_shift }, { 0, Q931_BEARER_CAPABILITY, "Bearer Capability", dump_bearer_capability, receive_bearer_capability, transmit_bearer_capability }, { 0, Q931_CAUSE, "Cause", dump_cause, receive_cause, transmit_cause }, - { 1, Q931_CALL_STATE, "Call State", dump_call_state, receive_call_state, transmit_call_state }, + { 1, Q931_IE_CALL_STATE, "Call State", dump_call_state, receive_call_state, transmit_call_state }, { 0, Q931_CHANNEL_IDENT, "Channel Identification", dump_channel_id, receive_channel_id, transmit_channel_id }, { 0, Q931_PROGRESS_INDICATOR, "Progress Indicator", dump_progress_indicator, receive_progress_indicator, transmit_progress_indicator }, { 0, Q931_NETWORK_SPEC_FAC, "Network-Specific Facilities", dump_network_spec_fac, receive_network_spec_fac, transmit_network_spec_fac }, @@ -2157,11 +3251,11 @@ { 1, Q931_BINARY_PARAMETERS, "Packet-layer Binary Parameters" }, { 1, Q931_WINDOW_SIZE, "Packet-layer Window Size" }, { 1, Q931_CLOSED_USER_GROUP, "Closed User Group" }, - { 1, Q931_REVERSE_CHARGE_INDIC, "Reverse Charging Indication" }, + { 1, Q931_REVERSE_CHARGE_INDIC, "Reverse Charging Indication", dump_reverse_charging_indication, receive_reverse_charging_indication, transmit_reverse_charging_indication }, { 1, Q931_CALLING_PARTY_NUMBER, "Calling Party Number", dump_calling_party_number, receive_calling_party_number, transmit_calling_party_number }, - { 1, Q931_CALLING_PARTY_SUBADDR, "Calling Party Subaddress", dump_calling_party_subaddr, receive_calling_party_subaddr }, + { 1, Q931_CALLING_PARTY_SUBADDR, "Calling Party Subaddress", dump_calling_party_subaddr, receive_calling_party_subaddr, transmit_calling_party_subaddr }, { 1, Q931_CALLED_PARTY_NUMBER, "Called Party Number", dump_called_party_number, receive_called_party_number, transmit_called_party_number }, - { 1, Q931_CALLED_PARTY_SUBADDR, "Called Party Subaddress", dump_called_party_subaddr }, + { 1, Q931_CALLED_PARTY_SUBADDR, "Called Party Subaddress", dump_called_party_subaddr, receive_called_party_subaddr, transmit_called_party_subaddr }, { 0, Q931_REDIRECTING_NUMBER, "Redirecting Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number }, { 1, Q931_REDIRECTING_SUBADDR, "Redirecting Subaddress", dump_redirecting_subaddr }, { 0, Q931_TRANSIT_NET_SELECT, "Transit Network Selection" }, @@ -2170,7 +3264,7 @@ { 0, Q931_HIGH_LAYER_COMPAT, "High-layer Compatibility" }, { 1, Q931_PACKET_SIZE, "Packet Size" }, { 0, Q931_IE_FACILITY, "Facility" , dump_facility, receive_facility, transmit_facility }, - { 1, Q931_IE_REDIRECTION_NUMBER, "Redirection Number" }, + { 1, Q931_IE_REDIRECTION_NUMBER, "Redirection Number", dump_redirection_number, receive_redirection_number, transmit_redirection_number }, { 1, Q931_IE_REDIRECTION_SUBADDR, "Redirection Subaddress" }, { 1, Q931_IE_FEATURE_ACTIVATE, "Feature Activation" }, { 1, Q931_IE_INFO_REQUEST, "Feature Request" }, @@ -2187,15 +3281,18 @@ { 1, Q931_IE_USER_USER, "User-User", dump_user_user, receive_user_user, transmit_user_user }, { 1, Q931_IE_ESCAPE_FOR_EXT, "Escape for Extension" }, { 1, Q931_IE_CALL_STATUS, "Call Status" }, - { 1, Q931_IE_CHANGE_STATUS, "Change Status" }, + { 1, Q931_IE_CHANGE_STATUS, "Change Status", dump_change_status, receive_change_status, transmit_change_status }, { 1, Q931_IE_CONNECTED_ADDR, "Connected Number", dump_connected_number }, - { 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number }, + { 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number, receive_connected_number, transmit_connected_number }, + { 1, Q931_IE_CONNECTED_SUBADDR, "Connected Subaddress", dump_connected_subaddr, receive_connected_subaddr, transmit_connected_subaddr }, { 1, Q931_IE_ORIGINAL_CALLED_NUMBER, "Original Called Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number }, { 1, Q931_IE_USER_USER_FACILITY, "User-User Facility" }, { 1, Q931_IE_UPDATE, "Update" }, { 1, Q931_SENDING_COMPLETE, "Sending Complete", dump_sending_complete, receive_sending_complete, transmit_sending_complete }, /* Codeset 4 - Q.SIG specific */ { 1, QSIG_IE_TRANSIT_COUNT | Q931_CODESET(4), "Transit Count", dump_transit_count }, + /* Codeset 5 - National specific (ETSI PISN specific) */ + { 1, Q931_CALLING_PARTY_CATEGORY, "Calling Party Category", dump_calling_party_category }, /* Codeset 6 - Network specific */ { 1, Q931_IE_ORIGINATING_LINE_INFO, "Originating Line Information", dump_line_information, receive_line_information, transmit_line_information }, { 1, Q931_IE_FACILITY | Q931_CODESET(6), "Facility", dump_facility, receive_facility, transmit_facility }, @@ -2264,7 +3361,7 @@ return 2 + ie->len; } -static char *msg2str(int msg) +const char *msg2str(int msg) { unsigned int x; for (x=0;xcrlen > 3) { pri_error(NULL, "Call Reference Length Too long: %d\n", h->crlen); - return -1; + return Q931_DUMMY_CALL_REFERENCE; } switch (h->crlen) { - case 2: - for (x=0;xcrlen;x++) { - cr <<= 8; - cr |= h->crv[x]; - } - break; - case 1: - cr = h->crv[0]; - if (cr & 0x80) { - cr &= ~0x80; - cr |= 0x8000; - } - break; - default: - pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen); + case 2: + cr = 0; + for (x = 0; x < h->crlen; ++x) { + cr <<= 8; + cr |= h->crv[x]; + } + break; + case 1: + cr = h->crv[0]; + if (cr & 0x80) { + cr &= ~0x80; + cr |= 0x8000; + } + break; + case 0: + cr = Q931_DUMMY_CALL_REFERENCE; + break; + default: + pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen); + cr = Q931_DUMMY_CALL_REFERENCE; + break; } return cr; } -static inline void q931_dumpie(struct pri *pri, int codeset, q931_ie *ie, char prefix) +static inline void q931_dumpie(struct pri *ctrl, int codeset, q931_ie *ie, char prefix) { unsigned int x; int full_ie = Q931_FULL_IE(codeset, ie->ie); @@ -2315,7 +3441,7 @@ for (x = 0; x + 2 < ielen(ie); ++x) buflen += sprintf(buf + buflen, " %02x", ie->data[x]); } - pri_message(pri, "%c [%02x%s]\n", prefix, ie->ie, buf); + pri_message(ctrl, "%c [%02x%s]\n", prefix, ie->ie, buf); free(buf); /* Special treatment for shifts */ @@ -2324,130 +3450,306 @@ base_ie = (((full_ie & ~0x7f) == Q931_FULL_IE(0, 0x80)) && ((full_ie & 0x70) != 0x20)) ? full_ie & ~0x0f : full_ie; - for (x=0;xcr = cr; + call->slotmap = -1; + call->channelno = -1; + if (cr != Q931_DUMMY_CALL_REFERENCE) { + call->newcall = 1; + } + call->ourcallstate = Q931_CALL_STATE_NULL; + call->peercallstate = Q931_CALL_STATE_NULL; + call->sugcallstate = Q931_CALL_STATE_NOT_SET; + call->ri = -1; + call->transcapability = -1; + call->transmoderate = -1; + call->transmultiple = -1; + call->userl1 = -1; + call->userl2 = -1; + call->userl3 = -1; + call->rateadaption = -1; + call->progress = -1; + call->causecode = -1; + call->causeloc = -1; + call->cause = -1; + call->useruserprotocoldisc = -1; + call->aoc_units = -1; + call->changestatus = -1; + call->reversecharge = -1; + call->pri_winner = -1; + call->master_call = call; + q931_party_number_init(&call->redirection_number); + q931_party_address_init(&call->called); + q931_party_id_init(&call->local_id); + q931_party_id_init(&call->remote_id); + q931_party_redirecting_init(&call->redirecting); + + /* PRI is set to whoever called us */ + if (BRI_TE_PTMP(ctrl)) { + /* + * Point to the master to avoid stale pointer problems if + * the TEI is removed later. + */ + call->pri = PRI_MASTER(ctrl); + } else { + call->pri = ctrl; + } +} + +static q931_call *q931_getcall(struct pri *ctrl, int cr) +{ + q931_call *cur; + q931_call *prev; struct pri *master; + if (cr == Q931_DUMMY_CALL_REFERENCE) { + return ctrl->dummy_call; + } + /* Find the master - He has the call pool */ - if (pri->master) - master = pri->master; - else - master = pri; - + master = PRI_MASTER(ctrl); + cur = *master->callpool; prev = NULL; - while(cur) { - if (cur->cr == cr) + while (cur) { + if (cur->cr == cr) { + /* Found existing call. */ + switch (ctrl->switchtype) { + case PRI_SWITCH_GR303_EOC: + case PRI_SWITCH_GR303_EOC_PATH: + case PRI_SWITCH_GR303_TMC: + case PRI_SWITCH_GR303_TMC_SWITCHING: + break; + default: + if (!ctrl->bri) { + /* PRI is set to whoever called us */ + cur->pri = ctrl; + } + break; + } return cur; + } prev = cur; cur = cur->next; } + /* No call exists, make a new one */ - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "-- Making new call for cr %d\n", cr); - - if (!(cur = calloc(1, sizeof(*cur)))) + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "-- Making new call for cr %d\n", cr); + } + + cur = calloc(1, sizeof(*cur)); + if (!cur) { return NULL; + } - call_init(cur); - /* Call reference */ - cur->cr = cr; - /* PRI is set to whoever called us */ - if (pri->bri && (pri->localtype == PRI_CPE) && pri->subchannel && outboundnew) - cur->pri = pri->subchannel; - else - cur->pri = pri; + /* Initialize call structure. */ + q931_init_call_record(ctrl, cur, cr); /* Append to end of list */ - if (prev) + if (prev) { prev->next = cur; - else + } else { *master->callpool = cur; - + } + return cur; } -q931_call *q931_new_call(struct pri *pri) +q931_call *q931_new_call(struct pri *ctrl) { q931_call *cur; do { - cur = *pri->callpool; - pri->cref++; - if (!pri->bri) { - if (pri->cref > 32767) - pri->cref = 1; + cur = *ctrl->callpool; + ctrl->cref++; + if (!ctrl->bri) { + if (ctrl->cref > 32767) + ctrl->cref = 1; } else { - if (pri->cref > 127) - pri->cref = 1; + if (ctrl->cref > 127) + ctrl->cref = 1; } while(cur) { - if (cur->cr == (0x8000 | pri->cref)) + if (cur->cr == (0x8000 | ctrl->cref)) break; cur = cur->next; } } while(cur); - return q931_getcall(pri, pri->cref | 0x8000, 1); + return q931_getcall(ctrl, ctrl->cref | 0x8000); } -static void q931_destroy(struct pri *pri, int cr, q931_call *c) +static void stop_t303(struct q931_call *call); + +static void cleanup_and_free_call(struct q931_call *cur) { - q931_call *cur, *prev; + stop_t303(cur); + pri_schedule_del(cur->pri, cur->retranstimer); + pri_call_apdu_queue_cleanup(cur); + free(cur); +} +static void pri_create_fake_clearing(struct q931_call *c, struct pri *master); + +void q931_destroycall(struct pri *ctrl, q931_call *c) +{ + q931_call *cur; + q931_call *prev; + q931_call *slave; + int i; + int slavesleft; + int slaveidx; + + if (q931_is_dummy_call(c)) { + /* Cannot destroy the dummy call. */ + return; + } + if (c->master_call != c) { + slave = c; + c = slave->master_call; + } else { + slave = NULL; + } + /* For destroying, make sure we are using the master span, since it maintains the call pool */ - for (;pri->master; pri = pri->master); + ctrl = PRI_MASTER(ctrl); prev = NULL; - cur = *pri->callpool; - while(cur) { - if ((c && (cur == c)) || (!c && (cur->cr == cr))) { + cur = *ctrl->callpool; + while (cur) { + if (cur == c) { + slaveidx = -1; + if (slave) { + for (i = 0; i < Q931_MAX_TEI; i++) { + if (cur->subcalls[i] == slave) { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Destroying subcall %p of call %p at %d\n", + slave, cur, i); + } + cleanup_and_free_call(slave); + cur->subcalls[i] = NULL; + slaveidx = i; + break; + } + } + } + + slavesleft = 0; + for (i = 0; i < Q931_MAX_TEI; i++) { + if (cur->subcalls[i]) { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Subcall still present at %d\n", i); + } + slavesleft++; + } + } + + /* We have 3 different phases to deal with: + * 1.) Sent outbound call, but no response, indicated by t203 present + * 2.) Sent outbound call, with responses, indicated by lack of t203 and subcalls present + * 3.) Outbound call connected, indicated by pri_winner > -1 + * + * If chan_dahdi hangs up in phase: + * 1.) T303 will be present, and we will fake clear in this case + * 2.) pri_winner will be < 0 and subcalls will be present. + * 3.) pri_winner will be > -1 and we will free the master when the winner dies. + * + * If remote ends hang up in phase: + * 1.) Impossible, defined by phase. + * 2.) When last end hangs up, we should cause a fake clearing. + * 3.) Pass events to winner up and be freed when winner is freed + * + * Exceptional conditions in phase: + * 1.) None. + * 2.) None. + * 3.) We hang up a call so quickly that it hangs up before other competing lines finish hangup sequence + * Subcalls present still even though we have hung up the winner. + * + * So, we could say: + * If, when the library user hangs up the master call, and there are more than one subcall up, we fake clear + * regardless of whether or not we drop down to one subcall left in the clearing process. + * + * If there are only one call up, we mirror what it does. + * + * OR + * + * Phase 2. them clearing: + * For handling of Phase 2 (indicated by not running and pri_winner not present): + * We create a fake hangup sequence after all the subcalls have been destroyed and after + * + * "" us clearing: + * For we need to start the fake clearing, but it needs to be half of a fake clearing, not a full one (since we already had a hangup). + * + * For handling of Phase 3 plus exceptions: + * + * If pri_winner exists, we mirror him in terms of events (which provides our hangup sequence), and when we have the complete + * hangup sequence completed (destroy called on master call), if there still exist non winner subcalls at this time, we declare the master + * call as dead and free it when the last subcall clears. + */ + + if ((slave && !slavesleft) && + ((cur->pri_winner < 0) || (slave && slaveidx != cur->pri_winner))) { + pri_create_fake_clearing(cur, ctrl); + return; + } + + if (slavesleft) { + return; + } + + /* Master call or normal call destruction. */ + if ((cur->pri_winner > -1) && cur->outboundbroadcast) { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, + "Since we already had a winner, we should just be able to kill the call anyways\n"); + } + } if (prev) prev->next = cur->next; else - *pri->callpool = cur->next; - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s\n",callstate2str(cur->ourcallstate),callstate2str(cur->peercallstate)); - if (cur->retranstimer) - pri_schedule_del(pri, cur->retranstimer); - pri_call_apdu_queue_cleanup(cur); - free(cur); + *ctrl->callpool = cur->next; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, + "NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s, hold-state %s\n", + q931_call_state_str(cur->ourcallstate), + q931_call_state_str(cur->peercallstate), + q931_hold_state_str(cur->hold_state)); + pri_schedule_del(ctrl, cur->hold_timer); + cleanup_and_free_call(cur); return; } prev = cur; cur = cur->next; } - pri_error(pri, "Can't destroy call %d!\n", cr); + pri_error(ctrl, "Can't destroy call %p cref:%d!\n", c, c->cr); } -static void q931_destroycall(struct pri *pri, int cr) +static int add_ie(struct pri *ctrl, q931_call *call, int msgtype, int ie, q931_ie *iet, int maxlen, int *codeset) { - return q931_destroy(pri, cr, NULL); -} - - -void __q931_destroycall(struct pri *pri, q931_call *c) -{ - if (pri && c) - q931_destroy(pri,0, c); - return; -} - -static int add_ie(struct pri *pri, q931_call *call, int msgtype, int ie, q931_ie *iet, int maxlen, int *codeset) -{ unsigned int x; int res, total_res; int have_shift; @@ -2473,7 +3775,7 @@ total_res = 0; do { iet->ie = ie; - res = ies[x].transmit(ie, pri, call, msgtype, iet, maxlen, ++order); + res = ies[x].transmit(ie, ctrl, call, msgtype, iet, maxlen, ++order); /* Error if res < 0 or ignored if res == 0 */ if (res < 0) return res; @@ -2484,8 +3786,7 @@ maxlen -= res; iet = (q931_ie *)((char *)iet + res); } - } - while (res > 0 && order < ies_count); + } while (res > 0 && order < ies_count); if (have_shift && total_res) { if (Q931_IE_CODESET(ies[x].ie)) *codeset = Q931_IE_CODESET(ies[x].ie); @@ -2493,12 +3794,12 @@ } return total_res; } else { - pri_error(pri, "!! Don't know how to add an IE %s (%d)\n", ie2str(ie), ie); + pri_error(ctrl, "!! Don't know how to add an IE %s (%d)\n", ie2str(ie), ie); return -1; } } } - pri_error(pri, "!! Unknown IE %d (%s)\n", ie, ie2str(ie)); + pri_error(ctrl, "!! Unknown IE %d (%s)\n", ie, ie2str(ie)); return -1; } @@ -2513,25 +3814,37 @@ return code2str(disc, discs, sizeof(discs) / sizeof(discs[0])); } -void q931_dump(struct pri *pri, q931_h *h, int len, int txrx) +void q931_dump(struct pri *ctrl, q931_h *h, int len, int txrx) { q931_mh *mh; char c; int x=0, r; int cur_codeset; int codeset; + int cref; + c = txrx ? '>' : '<'; - pri_message(pri, "%c Protocol Discriminator: %s (%d) len=%d\n", c, disc2str(h->pd), h->pd, len); - pri_message(pri, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n", c, h->crlen, q931_cr(h) & 0x7FFF, q931_cr(h) & 0x7FFF, (h->crv[0] & 0x80) ? "Terminator" : "Originator"); + pri_message(ctrl, "%c Protocol Discriminator: %s (%d) len=%d\n", c, disc2str(h->pd), h->pd, len); + cref = q931_cr(h); + pri_message(ctrl, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n", + c, h->crlen, cref & 0x7FFF, cref & 0x7FFF, + (cref == Q931_DUMMY_CALL_REFERENCE) + ? "Dummy" + : (cref & 0x8000) ? "Terminator" : "Originator"); + /* Message header begins at the end of the call reference number */ mh = (q931_mh *)(h->contents + h->crlen); - pri_message(pri, "%c Message type: %s (%d)\n", c, msg2str(mh->msg), mh->msg); + if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { + pri_message(ctrl, "%c Message Type: %s (%d)\n", c, maintenance_msg2str(mh->msg, h->pd), mh->msg); + } else { + pri_message(ctrl, "%c Message Type: %s (%d)\n", c, msg2str(mh->msg), mh->msg); + } /* Drop length of header, including call reference */ len -= (h->crlen + 3); codeset = cur_codeset = 0; while(x < len) { r = ielen((q931_ie *)(mh->data + x)); - q931_dumpie(pri, cur_codeset, (q931_ie *)(mh->data + x), c); + q931_dumpie(ctrl, cur_codeset, (q931_ie *)(mh->data + x), c); switch (mh->data[x] & 0xf8) { case Q931_LOCKING_SHIFT: if ((mh->data[x] & 7) > 0) @@ -2547,86 +3860,106 @@ x += r; } if (x > len) - pri_error(pri, "XXX Message longer than it should be?? XXX\n"); + pri_error(ctrl, "XXX Message longer than it should be?? XXX\n"); } -static int q931_handle_ie(int codeset, struct pri *pri, q931_call *c, int msg, q931_ie *ie) +static int q931_handle_ie(int codeset, struct pri *ctrl, q931_call *c, int msg, q931_ie *ie) { unsigned int x; int full_ie = Q931_FULL_IE(codeset, ie->ie); - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "-- Processing IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, "-- Processing IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); for (x=0;xdebug & PRI_DEBUG_Q931_ANOMALY) - pri_error(pri, "!! No handler for IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); + if (ctrl->debug & PRI_DEBUG_Q931_ANOMALY) + pri_error(ctrl, "!! No handler for IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); return -1; } } } - pri_message(pri, "!! Unknown IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); + pri_message(ctrl, "!! Unknown IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie)); return -1; } -static void init_header(struct pri *pri, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len) +/* Returns header and message header and modifies length in place */ +static void init_header(struct pri *ctrl, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len, int protodisc) { - /* Returns header and message header and modifies length in place */ - q931_h *h = (q931_h *)buf; - q931_mh * mh; - h->pd = pri->protodisc; + q931_h *h = (q931_h *) buf; + q931_mh *mh; + unsigned crv; + + if (protodisc) { + h->pd = protodisc; + } else { + h->pd = ctrl->protodisc; + } h->x0 = 0; /* Reserved 0 */ - if (!pri->bri) { - h->crlen = 2; /* Two bytes of Call Reference. Invert the top bit to make it from our sense */ - if (call->cr || call->forceinvert) { - h->crv[0] = ((call->cr ^ 0x8000) & 0xff00) >> 8; - h->crv[1] = (call->cr & 0xff); - } else { - /* Unless of course this has no call reference */ - h->crv[0] = 0; - h->crv[1] = 0; - } - if (pri->subchannel && !pri->bri) { + if (q931_is_dummy_call(call)) { + h->crlen = 0; + } else if (!ctrl->bri) { + /* Two bytes of Call Reference. */ + h->crlen = 2; + /* Invert the top bit to make it from our sense */ + crv = (unsigned) call->cr; + h->crv[0] = ((crv >> 8) ^ 0x80) & 0xff; + h->crv[1] = crv & 0xff; + if (ctrl->subchannel && !ctrl->bri) { /* On GR-303, top bit is always 0 */ h->crv[0] &= 0x7f; } } else { h->crlen = 1; - if (call->cr || call->forceinvert) { - h->crv[0] = (((call->cr ^ 0x8000) & 0x8000) >> 8) | (call->cr & 0x7f); - } else { - /* Unless of course this has no call reference */ - h->crv[0] = 0; - } + /* Invert the top bit to make it from our sense */ + crv = (unsigned) call->cr; + h->crv[0] = (((crv >> 8) ^ 0x80) & 0x80) | (crv & 0x7f); } - mh = (q931_mh *)(h->contents + h->crlen); + *hb = h; + + *len -= 3;/* Protocol discriminator, call reference length, message type id */ + *len -= h->crlen; + + mh = (q931_mh *) (h->contents + h->crlen); mh->f = 0; - *hb = h; *mhb = mh; - if (h->crlen == 2) - *len -= 5; - else - *len -= 4; - } -static int q931_xmit(struct pri *pri, q931_h *h, int len, int cr) +static int q931_xmit(struct pri *ctrl, q931_h *h, int len, int cr, int uiframe) { - q921_transmit_iframe(pri, h, len, cr); + if (uiframe) { + q921_transmit_uiframe(ctrl, h, len); + } else { + q921_transmit_iframe(ctrl, h, len, cr); + } /* The transmit operation might dump the q921 header, so logging the q931 message body after the transmit puts the sections of the message in the right order in the log */ - if (pri->debug & PRI_DEBUG_Q931_DUMP) - q931_dump(pri, h, len, 1); + if (ctrl->debug & PRI_DEBUG_Q931_DUMP) + q931_dump(ctrl, h, len, 1); #ifdef LIBPRI_COUNTERS - pri->q931_txcount++; + ctrl->q931_txcount++; #endif return 0; } -static int send_message(struct pri *pri, q931_call *c, int msgtype, int ies[]) +/*! + * \internal + * \brief Build and send the requested message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param msgtype Q.931 message type to build. + * \param ies List of ie's to put in the message. + * + * \note The ie's in the ie list must be in numerical order. + * See Q.931 section 4.5.1 coding rules. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[]) { unsigned char buf[1024]; q931_h *h; @@ -2636,18 +3969,24 @@ int offset=0; int x; int codeset; - + + if (call->outboundbroadcast && call->master_call == call && msgtype != Q931_SETUP) { + pri_error(ctrl, + "Attempting to use master call record to send %s on BRI PTMP NT %p\n", + msg2str(msgtype), ctrl); + return -1; + } + memset(buf, 0, sizeof(buf)); len = sizeof(buf); - init_header(pri, c, buf, &h, &mh, &len); - mh->msg = msgtype; + init_header(ctrl, call, buf, &h, &mh, &len, (msgtype >> 8)); + mh->msg = msgtype & 0x00ff; x=0; codeset = 0; while(ies[x] > -1) { - res = add_ie(pri, c, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset); - + res = add_ie(ctrl, call, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset); if (res < 0) { - pri_error(pri, "!! Unable to add IE '%s'\n", ie2str(ies[x])); + pri_error(ctrl, "!! Unable to add IE '%s'\n", ie2str(ies[x])); return -1; } @@ -2657,20 +3996,121 @@ } /* Invert the logic */ len = sizeof(buf) - len; - q931_xmit(c->pri, h, len, 1); - c->acked = 1; + + ctrl = call->pri; + if (BRI_TE_PTMP(ctrl)) { + /* + * Must use the BRI subchannel structure to send with the correct TEI. + * Note: If the subchannel is NULL then there is no TEI assigned and + * we should not be sending anything out at this time. + */ + ctrl = ctrl->subchannel; + } + if (ctrl) { + int uiframe; + + switch (msgtype) { + case Q931_SETUP: + /* + * For NT-PTMP mode, we need to check the following: + * MODE = NT-PTMP + * MESSAGE = SETUP + * + * If those are true, we need to send the SETUP in a UI frame + * instead of an I-frame. + */ + if (BRI_NT_PTMP(ctrl)) + uiframe = 1; + else + uiframe = 0; + break; + case Q931_FACILITY: + if (ctrl->tei == Q921_TEI_GROUP) { + /* Broadcast TEI. */ + if (q931_is_dummy_call(call)) { + /* + * This is a FACILITY message on the dummy call reference + * for the broadcast TEI. + */ + uiframe = 1; + } else { + pri_error(ctrl, + "Attempting to broadcast %s on cref %d\n", + msg2str(msgtype), call->cr); + return -1; + } + } else { + uiframe = 0; + } + break; + default: + uiframe = 0; + break; + } + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, + "Sending message for call %p on %p TEI/SAPI %d/%d, call->pri is %p, TEI/SAPI %d/%d\n", + call, + ctrl, ctrl->tei, ctrl->sapi, + call->pri, call->pri->tei, call->pri->sapi); + } + q931_xmit(ctrl, h, len, 1, uiframe); + } + call->acked = 1; return 0; } -static int status_ies[] = { Q931_CAUSE, Q931_CALL_STATE, -1 }; +static int maintenance_service_ies[] = { Q931_IE_CHANGE_STATUS, Q931_CHANNEL_IDENT, -1 }; -static int q931_status(struct pri *pri, q931_call *c, int cause) +int maintenance_service_ack(struct pri *ctrl, q931_call *c) { + int pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_1; + int mt = ATT_SERVICE_ACKNOWLEDGE; + + if (ctrl->switchtype == PRI_SWITCH_NI2) { + pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_2; + mt = NATIONAL_SERVICE_ACKNOWLEDGE; + } + return send_message(ctrl, c, (pd << 8) | mt, maintenance_service_ies); +} + +int maintenance_service(struct pri *ctrl, int span, int channel, int changestatus) +{ + struct q931_call *c; + int pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_1; + int mt = ATT_SERVICE; + + c = q931_getcall(ctrl, 0 | 0x8000); + if (!c) { + return -1; + } + if (channel > -1) { + c->channelno = channel & 0xff; + c->chanflags = FLAG_EXCLUSIVE; + } else { + c->channelno = channel; + c->chanflags = FLAG_EXCLUSIVE | FLAG_WHOLE_INTERFACE; + } + c->ds1no = span; + c->ds1explicit = 0; + c->changestatus = changestatus; + + if (ctrl->switchtype == PRI_SWITCH_NI2) { + pd = MAINTENANCE_PROTOCOL_DISCRIMINATOR_2; + mt = NATIONAL_SERVICE; + } + return send_message(ctrl, c, (pd << 8) | mt, maintenance_service_ies); +} + +static int status_ies[] = { Q931_CAUSE, Q931_IE_CALL_STATE, -1 }; + +static int q931_status(struct pri *ctrl, q931_call *c, int cause) +{ q931_call *cur = NULL; if (!cause) cause = PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY; if (c->cr > -1) - cur = *pri->callpool; + cur = *ctrl->callpool; while(cur) { if (cur->cr == c->cr) { cur->cause=cause; @@ -2681,79 +4121,156 @@ cur = cur->next; } if (!cur) { - pri_message(pri, "YYY Here we get reset YYY\n"); + pri_message(ctrl, "YYY Here we get reset YYY\n"); /* something went wrong, respond with "no such call" */ c->ourcallstate = Q931_CALL_STATE_NULL; c->peercallstate = Q931_CALL_STATE_NULL; cur=c; } - return send_message(pri, cur, Q931_STATUS, status_ies); + return send_message(ctrl, cur, Q931_STATUS, status_ies); } -static int information_ies[] = { Q931_IE_KEYPAD_FACILITY, Q931_CALLED_PARTY_NUMBER, -1 }; +static int information_ies[] = { Q931_CALLED_PARTY_NUMBER, -1 }; -int q931_information(struct pri *pri, q931_call *c, char digit) +int q931_information(struct pri *ctrl, q931_call *c, char digit) { - c->callednum[0] = digit; - c->callednum[1] = '\0'; - return send_message(pri, c, Q931_INFORMATION, information_ies); + c->overlap_digits[0] = digit; + c->overlap_digits[1] = '\0'; + + /* + * Since we are doing overlap dialing now, we need to accumulate + * the digits into call->called.number.str. + */ + c->called.number.valid = 1; + if (strlen(c->called.number.str) < sizeof(c->called.number.str) - 1) { + /* There is enough room for the new digit. */ + strcat(c->called.number.str, c->overlap_digits); + } + + return send_message(ctrl, c, Q931_INFORMATION, information_ies); } static int keypad_facility_ies[] = { Q931_IE_KEYPAD_FACILITY, -1 }; -int q931_keypad_facility(struct pri *pri, q931_call *call, char *digits) +int q931_keypad_facility(struct pri *ctrl, q931_call *call, const char *digits) { libpri_copy_string(call->keypad_digits, digits, sizeof(call->keypad_digits)); - return send_message(pri, call, Q931_INFORMATION, keypad_facility_ies); + return send_message(ctrl, call, Q931_INFORMATION, keypad_facility_ies); } static int restart_ack_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 }; -static int restart_ack(struct pri *pri, q931_call *c) +static int restart_ack(struct pri *ctrl, q931_call *c) { - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; - return send_message(pri, c, Q931_RESTART_ACKNOWLEDGE, restart_ack_ies); + return send_message(ctrl, c, Q931_RESTART_ACKNOWLEDGE, restart_ack_ies); } static int facility_ies[] = { Q931_IE_FACILITY, -1 }; -int q931_facility(struct pri*pri, q931_call *c) +int q931_facility(struct pri*ctrl, q931_call *c) { - return send_message(pri, c, Q931_FACILITY, facility_ies); + return send_message(ctrl, c, Q931_FACILITY, facility_ies); } -static int notify_ies[] = { Q931_IE_NOTIFY_IND, -1 }; +static int notify_ies[] = { Q931_IE_NOTIFY_IND, Q931_IE_REDIRECTION_NUMBER, -1 }; -int q931_notify(struct pri *pri, q931_call *c, int channel, int info) +/*! + * \internal + * \brief Actually send a NOTIFY message with optional redirection number. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param notify Notification indicator + * \param number Redirection number to send if not NULL. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int q931_notify_redirection_helper(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number) { - if ((pri->switchtype == PRI_SWITCH_EUROISDN_T1) || (pri->switchtype != PRI_SWITCH_EUROISDN_E1)) { - if ((info > 0x2) || (info < 0x00)) + if (number) { + call->redirection_number = *number; + } else { + q931_party_number_init(&call->redirection_number); + } + call->notify = notify; + return send_message(ctrl, call, Q931_NOTIFY, notify_ies); +} + +/*! + * \brief Send a NOTIFY message with optional redirection number. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg + * \param notify Notification indicator + * \param number Redirection number to send if not NULL. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number) +{ + int status; + unsigned idx; + struct q931_call *subcall; + + if (call->outboundbroadcast && call->master_call == call) { + status = 0; + for (idx = 0; idx < Q931_MAX_TEI; ++idx) { + subcall = call->subcalls[idx]; + if (subcall) { + /* Send to all subcalls that have given a positive response. */ + switch (subcall->ourcallstate) { + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + if (q931_notify_redirection_helper(ctrl, subcall, notify, number)) { + status = -1; + } + break; + default: + break; + } + } + } + } else { + status = q931_notify_redirection_helper(ctrl, call, notify, number); + } + return status; +} + +int q931_notify(struct pri *ctrl, q931_call *c, int channel, int info) +{ + if ((ctrl->switchtype == PRI_SWITCH_EUROISDN_T1) || (ctrl->switchtype != PRI_SWITCH_EUROISDN_E1)) { + if ((info > 0x2) || (info < 0x00)) { return 0; + } } - if (info >= 0) - c->notify = info & 0x7F; - else - c->notify = -1; - return send_message(pri, c, Q931_NOTIFY, notify_ies); + if (info >= 0) { + info = info & 0x7F; + } else { + info = -1; + } + return q931_notify_redirection(ctrl, c, info, NULL); } #ifdef ALERTING_NO_PROGRESS static int call_progress_ies[] = { -1 }; #else -static int call_progress_with_cause_ies[] = { Q931_PROGRESS_INDICATOR, Q931_CAUSE, -1 }; +static int call_progress_with_cause_ies[] = { Q931_CAUSE, Q931_PROGRESS_INDICATOR, -1 }; static int call_progress_ies[] = { Q931_PROGRESS_INDICATOR, -1 }; #endif -int q931_call_progress(struct pri *pri, q931_call *c, int channel, int info) +int q931_call_progress(struct pri *ctrl, q931_call *c, int channel, int info) { if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; - channel &= 0xff; - c->channelno = channel; + c->channelno = channel & 0xff; } if (info) { @@ -2762,21 +4279,20 @@ c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else { /* PI is mandatory IE for PROGRESS message - Q.931 3.1.8 */ - pri_error(pri, "XXX Progress message requested but no information is provided\n"); + pri_error(ctrl, "XXX Progress message requested but no information is provided\n"); c->progressmask = 0; } c->alive = 1; - return send_message(pri, c, Q931_PROGRESS, call_progress_ies); + return send_message(ctrl, c, Q931_PROGRESS, call_progress_ies); } -int q931_call_progress_with_cause(struct pri *pri, q931_call *c, int channel, int info, int cause) +int q931_call_progress_with_cause(struct pri *ctrl, q931_call *c, int channel, int info, int cause) { if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; - channel &= 0xff; - c->channelno = channel; + c->channelno = channel & 0xff; } if (info) { @@ -2785,7 +4301,7 @@ c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else { /* PI is mandatory IE for PROGRESS message - Q.931 3.1.8 */ - pri_error(pri, "XXX Progress message requested but no information is provided\n"); + pri_error(ctrl, "XXX Progress message requested but no information is provided\n"); c->progressmask = 0; } @@ -2794,7 +4310,7 @@ c->causeloc = LOC_PRIV_NET_LOCAL_USER; c->alive = 1; - return send_message(pri, c, Q931_PROGRESS, call_progress_with_cause_ies); + return send_message(ctrl, c, Q931_PROGRESS, call_progress_with_cause_ies); } #ifdef ALERTING_NO_PROGRESS @@ -2803,17 +4319,16 @@ static int call_proceeding_ies[] = { Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, -1 }; #endif -int q931_call_proceeding(struct pri *pri, q931_call *c, int channel, int info) +int q931_call_proceeding(struct pri *ctrl, q931_call *c, int channel, int info) { if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; - channel &= 0xff; - c->channelno = channel; + c->channelno = channel & 0xff; } c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_INCOMING_CALL_PROCEEDING); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_INCOMING_CALL_PROCEEDING); c->peercallstate = Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING; if (info) { c->progloc = LOC_PRIV_NET_LOCAL_USER; @@ -2823,62 +4338,73 @@ c->progressmask = 0; c->proc = 1; c->alive = 1; - return send_message(pri, c, Q931_CALL_PROCEEDING, call_proceeding_ies); + return send_message(ctrl, c, Q931_CALL_PROCEEDING, call_proceeding_ies); } #ifndef ALERTING_NO_PROGRESS -static int alerting_ies[] = { Q931_PROGRESS_INDICATOR, Q931_IE_USER_USER, -1 }; +static int alerting_ies[] = { Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, Q931_IE_USER_USER, -1 }; #else -static int alerting_ies[] = { -1 }; +static int alerting_ies[] = { Q931_IE_FACILITY, -1 }; #endif -int q931_alerting(struct pri *pri, q931_call *c, int channel, int info) +int q931_alerting(struct pri *ctrl, q931_call *c, int channel, int info) { if (!c->proc) - q931_call_proceeding(pri, c, channel, 0); + q931_call_proceeding(ctrl, c, channel, 0); if (info) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_INBAND_AVAILABLE; } else c->progressmask = 0; - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CALL_RECEIVED); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_RECEIVED); c->peercallstate = Q931_CALL_STATE_CALL_DELIVERED; c->alive = 1; - return send_message(pri, c, Q931_ALERTING, alerting_ies); + + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + if (c->local_id.name.valid) { + /* Send calledName with ALERTING */ + rose_called_name_encode(ctrl, c, Q931_ALERTING); + } + break; + default: + break; + } + + return send_message(ctrl, c, Q931_ALERTING, alerting_ies); } -static int connect_ies[] = { Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, -1 }; +static int setup_ack_ies[] = { Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, -1 }; -int q931_setup_ack(struct pri *pri, q931_call *c, int channel, int nonisdn) +int q931_setup_ack(struct pri *ctrl, q931_call *c, int channel, int nonisdn) { if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; - channel &= 0xff; - c->channelno = channel; + c->channelno = channel & 0xff; } c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; - if (nonisdn && (pri->switchtype != PRI_SWITCH_DMS100)) { + if (nonisdn && (ctrl->switchtype != PRI_SWITCH_DMS100)) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_CALLED_NOT_ISDN; } else c->progressmask = 0; - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_OVERLAP_RECEIVING); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OVERLAP_RECEIVING); c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING; c->alive = 1; - return send_message(pri, c, Q931_SETUP_ACKNOWLEDGE, connect_ies); + return send_message(ctrl, c, Q931_SETUP_ACKNOWLEDGE, setup_ack_ies); } /* T313 expiry, first time */ static void pri_connect_timeout(void *data) { struct q931_call *c = data; - struct pri *pri = c->pri; - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "Timed out looking for connect acknowledge\n"); - q931_disconnect(pri, c, PRI_CAUSE_NORMAL_CLEARING); + struct pri *ctrl = c->pri; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, "Timed out looking for connect acknowledge\n"); + q931_disconnect(ctrl, c, PRI_CAUSE_NORMAL_CLEARING); } @@ -2886,85 +4412,120 @@ static void pri_release_timeout(void *data) { struct q931_call *c = data; - struct pri *pri = c->pri; - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "Timed out looking for release complete\n"); + struct pri *ctrl = c->pri; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, "Timed out looking for release complete\n"); c->t308_timedout++; c->alive = 1; /* The call to q931_release will re-schedule T308 */ - q931_release(pri, c, c->cause); + q931_release(ctrl, c, c->cause); } /* T308 expiry, second time */ static void pri_release_finaltimeout(void *data) { struct q931_call *c = data; - struct pri *pri = c->pri; + struct pri *ctrl = c->pri; c->alive = 1; - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "Final time-out looking for release complete\n"); + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, "Final time-out looking for release complete\n"); c->t308_timedout++; c->ourcallstate = Q931_CALL_STATE_NULL; c->peercallstate = Q931_CALL_STATE_NULL; - pri->schedev = 1; - pri->ev.e = PRI_EVENT_HANGUP_ACK; - pri->ev.hangup.channel = c->channelno; - pri->ev.hangup.cause = c->cause; - pri->ev.hangup.cref = c->cr; - pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; - libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); - q931_hangup(pri, c, c->cause); + q931_clr_subcommands(ctrl); + ctrl->schedev = 1; + ctrl->ev.e = PRI_EVENT_HANGUP_ACK; + ctrl->ev.hangup.subcmds = &ctrl->subcmds; + ctrl->ev.hangup.channel = q931_encode_channel(c); + ctrl->ev.hangup.cause = c->cause; + ctrl->ev.hangup.cref = c->cr; + ctrl->ev.hangup.call = c->master_call; + ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; + libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); + pri_hangup(ctrl, c, c->cause); } /* T305 expiry, first time */ static void pri_disconnect_timeout(void *data) { struct q931_call *c = data; - struct pri *pri = c->pri; - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "Timed out looking for release\n"); + struct pri *ctrl = c->pri; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, "Timed out looking for release\n"); c->alive = 1; - q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING); + q931_release(ctrl, c, PRI_CAUSE_NORMAL_CLEARING); } -int q931_connect(struct pri *pri, q931_call *c, int channel, int nonisdn) +static int connect_ies[] = { + Q931_CHANNEL_IDENT, + Q931_IE_FACILITY, + Q931_PROGRESS_INDICATOR, + Q931_DISPLAY, + Q931_IE_CONNECTED_NUM, + Q931_IE_CONNECTED_SUBADDR, + -1 +}; + +int q931_connect(struct pri *ctrl, q931_call *c, int channel, int nonisdn) { if (channel) { c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; - channel &= 0xff; - c->channelno = channel; + c->channelno = channel & 0xff; } c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; - if (nonisdn && (pri->switchtype != PRI_SWITCH_DMS100)) { + if (nonisdn && (ctrl->switchtype != PRI_SWITCH_DMS100)) { c->progloc = LOC_PRIV_NET_LOCAL_USER; c->progcode = CODE_CCITT; c->progressmask = PRI_PROG_CALLED_NOT_ISDN; } else c->progressmask = 0; - if(pri->localtype == PRI_NETWORK || pri->switchtype == PRI_SWITCH_QSIG) - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_ACTIVE); + if(ctrl->localtype == PRI_NETWORK || ctrl->switchtype == PRI_SWITCH_QSIG) + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE); else - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CONNECT_REQUEST); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CONNECT_REQUEST); c->peercallstate = Q931_CALL_STATE_ACTIVE; c->alive = 1; /* Connect request timer */ - if (c->retranstimer) - pri_schedule_del(pri, c->retranstimer); + pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; - if ((c->ourcallstate == Q931_CALL_STATE_CONNECT_REQUEST) && (pri->bri || (!pri->subchannel))) - c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T313], pri_connect_timeout, c); - return send_message(pri, c, Q931_CONNECT, connect_ies); + if ((c->ourcallstate == Q931_CALL_STATE_CONNECT_REQUEST) && (ctrl->bri || (!ctrl->subchannel))) + c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T313], pri_connect_timeout, c); + + if (c->redirecting.state == Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3) { + c->redirecting.state = Q931_REDIRECTING_STATE_IDLE; + /* Send DivertingLegInformation3 with CONNECT. */ + c->redirecting.to = c->local_id; + if (!c->redirecting.to.number.valid) { + q931_party_number_init(&c->redirecting.to.number); + c->redirecting.to.number.valid = 1; + c->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + rose_diverting_leg_information3_encode(ctrl, c, Q931_CONNECT); + } + switch (ctrl->switchtype) { + case PRI_SWITCH_QSIG: + if (c->local_id.name.valid) { + /* Send connectedName with CONNECT */ + rose_connected_name_encode(ctrl, c, Q931_CONNECT); + } + break; + default: + break; + } + return send_message(ctrl, c, Q931_CONNECT, connect_ies); } static int release_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; -int q931_release(struct pri *pri, q931_call *c, int cause) +int q931_release(struct pri *ctrl, q931_call *c, int cause) { - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_RELEASE_REQUEST); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RELEASE_REQUEST); /* c->peercallstate stays the same */ if (c->alive) { c->alive = 0; @@ -2972,26 +4533,26 @@ c->causecode = CODE_CCITT; c->causeloc = LOC_PRIV_NET_LOCAL_USER; if (c->acked) { - if (c->retranstimer) - pri_schedule_del(pri, c->retranstimer); + pri_schedule_del(ctrl, c->retranstimer); if (!c->t308_timedout) { - c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T308], pri_release_timeout, c); + c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T308], pri_release_timeout, c); } else { - c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T308], pri_release_finaltimeout, c); + c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T308], pri_release_finaltimeout, c); } - return send_message(pri, c, Q931_RELEASE, release_ies); + return send_message(ctrl, c, Q931_RELEASE, release_ies); } else - return send_message(pri, c, Q931_RELEASE_COMPLETE, release_ies); /* Yes, release_ies, not release_complete_ies */ + return send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_ies); /* Yes, release_ies, not release_complete_ies */ } else return 0; } static int restart_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 }; -int q931_restart(struct pri *pri, int channel) +int q931_restart(struct pri *ctrl, int channel) { struct q931_call *c; - c = q931_getcall(pri, 0 | 0x8000, 1); + + c = q931_getcall(ctrl, 0 | 0x8000); if (!c) return -1; if (!channel) @@ -2999,20 +4560,19 @@ c->ri = 0; c->ds1no = (channel & 0xff00) >> 8; c->ds1explicit = (channel & 0x10000) >> 16; - channel &= 0xff; - c->channelno = channel; + c->channelno = channel & 0xff; c->chanflags &= ~FLAG_PREFERRED; c->chanflags |= FLAG_EXCLUSIVE; - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_RESTART); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RESTART); c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST; - return send_message(pri, c, Q931_RESTART, restart_ies); + return send_message(ctrl, c, Q931_RESTART, restart_ies); } -static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 }; +static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_FACILITY, Q931_IE_USER_USER, -1 }; -int q931_disconnect(struct pri *pri, q931_call *c, int cause) +int q931_disconnect(struct pri *ctrl, q931_call *c, int cause) { - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_DISCONNECT_REQUEST); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_DISCONNECT_REQUEST); c->peercallstate = Q931_CALL_STATE_DISCONNECT_INDICATION; if (c->alive) { c->alive = 0; @@ -3020,27 +4580,125 @@ c->causecode = CODE_CCITT; c->causeloc = LOC_PRIV_NET_LOCAL_USER; c->sendhangupack = 1; - if (c->retranstimer) - pri_schedule_del(pri, c->retranstimer); - c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T305], pri_disconnect_timeout, c); - return send_message(pri, c, Q931_DISCONNECT, disconnect_ies); + pri_schedule_del(ctrl, c->retranstimer); + c->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T305], pri_disconnect_timeout, c); + return send_message(ctrl, c, Q931_DISCONNECT, disconnect_ies); } else return 0; } -static int setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, Q931_NETWORK_SPEC_FAC, Q931_DISPLAY, - Q931_CALLING_PARTY_NUMBER, Q931_CALLED_PARTY_NUMBER, Q931_REDIRECTING_NUMBER, Q931_IE_USER_USER, Q931_SENDING_COMPLETE, - Q931_IE_ORIGINATING_LINE_INFO, Q931_IE_GENERIC_DIGITS, -1 }; +static int setup_ies[] = { + Q931_BEARER_CAPABILITY, + Q931_CHANNEL_IDENT, + Q931_IE_FACILITY, + Q931_PROGRESS_INDICATOR, + Q931_NETWORK_SPEC_FAC, + Q931_DISPLAY, + Q931_IE_KEYPAD_FACILITY, + Q931_REVERSE_CHARGE_INDIC, + Q931_CALLING_PARTY_NUMBER, + Q931_CALLING_PARTY_SUBADDR, + Q931_CALLED_PARTY_NUMBER, + Q931_CALLED_PARTY_SUBADDR, + Q931_REDIRECTING_NUMBER, + Q931_IE_USER_USER, + Q931_SENDING_COMPLETE, + Q931_IE_ORIGINATING_LINE_INFO, + Q931_IE_GENERIC_DIGITS, + -1 +}; -static int gr303_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, -1 }; +static int gr303_setup_ies[] = { + Q931_BEARER_CAPABILITY, + Q931_CHANNEL_IDENT, + -1 +}; -static int cis_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_CALLED_PARTY_NUMBER, -1 }; +/*! Call Independent Signalling SETUP ie's */ +static int cis_setup_ies[] = { + Q931_BEARER_CAPABILITY, + Q931_CHANNEL_IDENT, + Q931_IE_FACILITY, + Q931_IE_KEYPAD_FACILITY, + Q931_CALLING_PARTY_NUMBER, + Q931_CALLING_PARTY_SUBADDR, + Q931_CALLED_PARTY_NUMBER, + Q931_CALLED_PARTY_SUBADDR, + Q931_SENDING_COMPLETE, + -1 +}; -int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req) +static void stop_t303(struct q931_call *call) { + /* T303 should only be running on the master call */ + pri_schedule_del(call->master_call->pri, call->master_call->t303_timer); + call->master_call->t303_timer = 0; +} + +static void t303_expiry(void *data); + +static void start_t303(struct q931_call *call) +{ + if (call->t303_timer) { + pri_error(call->pri, "Should not have T303 set when starting again. Stopping first\n"); + stop_t303(call); + } + + //pri_error(call->pri, "T303 should be %d\n", call->pri->timers[PRI_TIMER_T303]); + call->t303_timer = pri_schedule_event(call->pri, call->pri->timers[PRI_TIMER_T303], t303_expiry, call); +} + +static void pri_fake_clearing(void *data); + +static void t303_expiry(void *data) +{ + struct q931_call *c = data; + struct pri *ctrl = c->pri; int res; - - + + c->t303_expirycnt++; + c->t303_timer = 0; + + if (c->cause != -1) { + /* We got a DISCONNECT, RELEASE, or RELEASE_COMPLETE and no other responses. */ + pri_fake_clearing(c); + } else if (c->t303_expirycnt < 2) { + if (ctrl->subchannel && !ctrl->bri) + res = send_message(ctrl, c, Q931_SETUP, gr303_setup_ies); + else if (c->cis_call) + res = send_message(ctrl, c, Q931_SETUP, cis_setup_ies); + else + res = send_message(ctrl, c, Q931_SETUP, setup_ies); + + if (res) { + pri_error(c->pri, "Error resending setup message!\n"); + } + start_t303(c); + } else { + c->cause = PRI_CAUSE_NO_USER_RESPONSE; + pri_fake_clearing(c); + } +} + +int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) +{ + int res; + + if (!req->called.number.valid && (!req->keypad_digits || !req->keypad_digits[0])) { + /* No called number or keypad digits to send. */ + return -1; + } + + c->called = req->called; + libpri_copy_string(c->overlap_digits, req->called.number.str, sizeof(c->overlap_digits)); + + if (req->keypad_digits) { + libpri_copy_string(c->keypad_digits, req->keypad_digits, + sizeof(c->keypad_digits)); + } else { + c->keypad_digits[0] = '\0'; + } + c->transcapability = req->transmode; c->transmoderate = TRANS_MODE_64_CIRCUIT; if (!req->userl1) @@ -3050,89 +4708,68 @@ c->userl3 = -1; c->ds1no = (req->channel & 0xff00) >> 8; c->ds1explicit = (req->channel & 0x10000) >> 16; - req->channel &= 0xff; - if ((pri->localtype == PRI_CPE) && pri->subchannel && !pri->bri) { - req->channel = 0; - req->exclusive = 0; + if ((ctrl->localtype == PRI_CPE) && ctrl->subchannel && !ctrl->bri) { + c->channelno = 0; + c->chanflags = 0; + } else { + c->channelno = req->channel & 0xff; + if (req->exclusive) { + c->chanflags = FLAG_EXCLUSIVE; + } else { + c->chanflags = FLAG_PREFERRED; + } } - - c->channelno = req->channel; + c->slotmap = -1; c->nonisdn = req->nonisdn; c->newcall = 0; - c->justsignalling = req->justsignalling; + c->cis_call = req->cis_call; + c->cis_auto_disconnect = req->cis_auto_disconnect; c->complete = req->numcomplete; - if (req->exclusive) - c->chanflags = FLAG_EXCLUSIVE; - else if (c->channelno) - c->chanflags = FLAG_PREFERRED; - if (req->caller) { - libpri_copy_string(c->callernum, req->caller, sizeof(c->callernum)); - c->callerplan = req->callerplan; - if (req->callername) - libpri_copy_string(c->callername, req->callername, sizeof(c->callername)); - else - c->callername[0] = '\0'; - if ((pri->switchtype == PRI_SWITCH_DMS100) || - (pri->switchtype == PRI_SWITCH_ATT4ESS)) { - /* Doesn't like certain presentation types */ - if (!(req->callerpres & 0x7c)) - req->callerpres = PRES_ALLOWED_NETWORK_NUMBER; - } - c->callerpres = req->callerpres; - } else { - c->callernum[0] = '\0'; - c->callername[0] = '\0'; - c->callerplan = PRI_UNKNOWN; - c->callerpres = PRES_NUMBER_NOT_AVAILABLE; + + if (req->caller.number.valid) { + c->local_id = req->caller; + q931_party_id_fixup(ctrl, &c->local_id); } - if (req->redirectingnum) { - libpri_copy_string(c->redirectingnum, req->redirectingnum, sizeof(c->redirectingnum)); - c->redirectingplan = req->redirectingplan; - if ((pri->switchtype == PRI_SWITCH_DMS100) || - (pri->switchtype == PRI_SWITCH_ATT4ESS)) { - /* Doesn't like certain presentation types */ - if (!(req->redirectingpres & 0x7c)) - req->redirectingpres = PRES_ALLOWED_NETWORK_NUMBER; - } - c->redirectingpres = req->redirectingpres; - c->redirectingreason = req->redirectingreason; - } else { - c->redirectingnum[0] = '\0'; - c->redirectingplan = PRI_UNKNOWN; - c->redirectingpres = PRES_NUMBER_NOT_AVAILABLE; - c->redirectingreason = PRI_REDIR_UNKNOWN; + + if (req->redirecting.from.number.valid) { + c->redirecting = req->redirecting; + q931_party_id_fixup(ctrl, &c->redirecting.from); + q931_party_id_fixup(ctrl, &c->redirecting.to); + q931_party_id_fixup(ctrl, &c->redirecting.orig_called); } - if (req->called) { - libpri_copy_string(c->callednum, req->called, sizeof(c->callednum)); - c->calledplan = req->calledplan; - } else - return -1; if (req->useruserinfo) libpri_copy_string(c->useruserinfo, req->useruserinfo, sizeof(c->useruserinfo)); else c->useruserinfo[0] = '\0'; - if (req->nonisdn && (pri->switchtype == PRI_SWITCH_NI2)) + if (req->nonisdn && (ctrl->switchtype == PRI_SWITCH_NI2)) c->progressmask = PRI_PROG_CALLER_NOT_ISDN; else c->progressmask = 0; - pri_call_add_standard_apdus(pri, c); + c->reversecharge = req->reversecharge; - if (pri->subchannel && !pri->bri) - res = send_message(pri, c, Q931_SETUP, gr303_setup_ies); - else if (c->justsignalling) - res = send_message(pri, c, Q931_SETUP, cis_setup_ies); + pri_call_add_standard_apdus(ctrl, c); + + if (ctrl->subchannel && !ctrl->bri) + res = send_message(ctrl, c, Q931_SETUP, gr303_setup_ies); + else if (c->cis_call) + res = send_message(ctrl, c, Q931_SETUP, cis_setup_ies); else - res = send_message(pri, c, Q931_SETUP, setup_ies); + res = send_message(ctrl, c, Q931_SETUP, setup_ies); if (!res) { c->alive = 1; /* make sure we call PRI_EVENT_HANGUP_ACK once we send/receive RELEASE_COMPLETE */ c->sendhangupack = 1; - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CALL_INITIATED); - c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING; + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_INITIATED); + c->peercallstate = Q931_CALL_STATE_CALL_PRESENT; + c->t303_expirycnt = 0; + if (BRI_NT_PTMP(ctrl)) { + c->outboundbroadcast = 1; + } + start_t303(c); } return res; @@ -3140,22 +4777,22 @@ static int release_complete_ies[] = { Q931_IE_USER_USER, -1 }; -static int q931_release_complete(struct pri *pri, q931_call *c, int cause) +static int q931_release_complete(struct pri *ctrl, q931_call *c, int cause) { int res = 0; - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; if (cause > -1) { c->cause = cause; c->causecode = CODE_CCITT; c->causeloc = LOC_PRIV_NET_LOCAL_USER; /* release_ies has CAUSE in it */ - res = send_message(pri, c, Q931_RELEASE_COMPLETE, release_ies); + res = send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_ies); } else - res = send_message(pri, c, Q931_RELEASE_COMPLETE, release_complete_ies); + res = send_message(ctrl, c, Q931_RELEASE_COMPLETE, release_complete_ies); c->alive = 0; /* release the structure */ - res += q931_hangup(pri,c,cause); + res += pri_hangup(ctrl, c, cause); return res; } @@ -3163,46 +4800,553 @@ static int gr303_connect_acknowledge_ies[] = { Q931_CHANNEL_IDENT, -1 }; -static int q931_connect_acknowledge(struct pri *pri, q931_call *c) +static int q931_connect_acknowledge(struct pri *ctrl, q931_call *c) { - if (pri->subchannel && !pri->bri) { - if (pri->localtype == PRI_CPE) - return send_message(pri, c, Q931_CONNECT_ACKNOWLEDGE, gr303_connect_acknowledge_ies); + if (ctrl->subchannel && !ctrl->bri) { + if (ctrl->localtype == PRI_CPE) + return send_message(ctrl, c, Q931_CONNECT_ACKNOWLEDGE, gr303_connect_acknowledge_ies); } else - return send_message(pri, c, Q931_CONNECT_ACKNOWLEDGE, connect_acknowledge_ies); + return send_message(ctrl, c, Q931_CONNECT_ACKNOWLEDGE, connect_acknowledge_ies); return 0; } -int q931_hangup(struct pri *pri, q931_call *c, int cause) +/*! + * \internal + * \brief Find the winning subcall if it exists or current call if not outboundbroadcast. + * + * \param call Starting Q.931 call record of search. + * + * \retval winning-call or given call if not outboundbroadcast. + * \retval NULL if no winning call yet. + */ +static struct q931_call *q931_find_winning_call(struct q931_call *call) { + struct q931_call *master; + + master = call->master_call; + if (master->outboundbroadcast) { + /* We have potential subcalls. Now get the winning call if declared yet. */ + if (master->pri_winner < 0) { + /* Winner not declared yet.*/ + call = NULL; + } else { + call = master->subcalls[master->pri_winner]; + } + } + return call; +} + +/*! + * \internal + * \brief Send HOLD message response wait timeout. + * + * \param data Q.931 call leg. (Master Q.931 subcall structure) + * + * \return Nothing + */ +static void q931_hold_timeout(void *data) +{ + struct q931_call *call = data; + struct pri *ctrl = call->pri; + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Time-out waiting for HOLD response\n"); + } + + /* Ensure that the timer is deleted. */ + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = 0; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE); + + q931_clr_subcommands(ctrl); + ctrl->schedev = 1; + ctrl->ev.e = PRI_EVENT_HOLD_REJ; + ctrl->ev.hold_rej.channel = q931_encode_channel(call); + ctrl->ev.hold_rej.call = call; + ctrl->ev.hold_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST; + ctrl->ev.hold_rej.subcmds = &ctrl->subcmds; +} + +/*! + * \internal + * \brief Determine if a hold request is allowed now. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * + * \retval TRUE if we can send a HOLD request. + * \retval FALSE if not allowed. + */ +static int q931_is_hold_allowed(const struct pri *ctrl, const struct q931_call *call) +{ + int allowed; + + allowed = 0; + switch (call->ourcallstate) { + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + if (q931_is_ptmp(ctrl)) { + /* HOLD request only allowed in these states if point-to-point mode. */ + break; + } + /* Fall through */ + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + switch (call->hold_state) { + case Q931_HOLD_STATE_IDLE: + allowed = 1; + break; + default: + break; + } + break; + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_RELEASE_REQUEST: + /* Ignore HOLD request in these states. */ + break; + default: + break; + } + + return allowed; +} + +static int hold_ies[] = { + -1 +}; + +/*! + * \brief Send the HOLD message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_hold(struct pri *ctrl, struct q931_call *call) +{ + struct q931_call *winner; + + winner = q931_find_winning_call(call); + if (!winner || !q931_is_hold_allowed(ctrl, call)) { + return -1; + } + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_HOLD], + q931_hold_timeout, call); + if (send_message(ctrl, winner, Q931_HOLD, hold_ies)) { + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = 0; + return -1; + } + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_HOLD_REQ); + return 0; +} + +static int hold_ack_ies[] = { + -1 +}; + +/*! + * \brief Send the HOLD ACKNOWLEDGE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call) +{ + struct q931_call *winner; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD); + + winner = q931_find_winning_call(call); + if (!winner) { + return -1; + } + + /* Call is now on hold so forget the channel. */ + winner->channelno = 0;/* No channel */ + winner->ds1no = 0; + winner->ds1explicit = 0; + winner->chanflags = 0; + + return send_message(ctrl, winner, Q931_HOLD_ACKNOWLEDGE, hold_ack_ies); +} + +static int hold_reject_ies[] = { + Q931_CAUSE, + -1 +}; + +/*! + * \internal + * \brief Send the HOLD REJECT message only. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (subcall) + * \param cause Q.931 cause code for rejecting the hold request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int q931_send_hold_rej_msg(struct pri *ctrl, struct q931_call *call, int cause) +{ + call->cause = cause; + call->causecode = CODE_CCITT; + call->causeloc = LOC_PRIV_NET_LOCAL_USER; + return send_message(ctrl, call, Q931_HOLD_REJECT, hold_reject_ies); +} + +/*! + * \brief Send the HOLD REJECT message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * \param cause Q.931 cause code for rejecting the hold request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause) +{ + struct q931_call *winner; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE); + + winner = q931_find_winning_call(call); + if (!winner) { + return -1; + } + + return q931_send_hold_rej_msg(ctrl, winner, cause); +} + +/*! + * \internal + * \brief Send RETRIEVE message response wait timeout. + * + * \param data Q.931 call leg. (Master Q.931 subcall structure) + * + * \return Nothing + */ +static void q931_retrieve_timeout(void *data) +{ + struct q931_call *call = data; + struct pri *ctrl = call->pri; + struct q931_call *winner; + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Time-out waiting for RETRIEVE response\n"); + } + + /* Ensure that the timer is deleted. */ + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = 0; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD); + + winner = q931_find_winning_call(call); + if (winner) { + /* Call is still on hold so forget the channel. */ + winner->channelno = 0;/* No channel */ + winner->ds1no = 0; + winner->ds1explicit = 0; + winner->chanflags = 0; + } + + q931_clr_subcommands(ctrl); + ctrl->schedev = 1; + ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ; + ctrl->ev.retrieve_rej.channel = q931_encode_channel(call); + ctrl->ev.retrieve_rej.call = call; + ctrl->ev.retrieve_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST; + ctrl->ev.retrieve_rej.subcmds = &ctrl->subcmds; +} + +/*! + * \internal + * \brief Determine if a retrieve request is allowed now. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * + * \retval TRUE if we can send a RETRIEVE request. + * \retval FALSE if not allowed. + */ +static int q931_is_retrieve_allowed(const struct pri *ctrl, const struct q931_call *call) +{ + int allowed; + + allowed = 0; + switch (call->ourcallstate) { + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + if (q931_is_ptmp(ctrl)) { + /* RETRIEVE request only allowed in these states if point-to-point mode. */ + break; + } + /* Fall through */ + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + switch (call->hold_state) { + case Q931_HOLD_STATE_CALL_HELD: + allowed = 1; + break; + default: + break; + } + break; + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_RELEASE_REQUEST: + /* Ignore RETRIEVE request in these states. */ + break; + default: + break; + } + + return allowed; +} + +static int retrieve_ies[] = { + Q931_CHANNEL_IDENT, + -1 +}; + +/*! + * \brief Send the RETRIEVE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * \param channel Encoded channel id to use. If zero do not send channel id. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel) +{ + struct q931_call *winner; + + winner = q931_find_winning_call(call); + if (!winner || !q931_is_retrieve_allowed(ctrl, call)) { + return -1; + } + + if (channel) { + winner->ds1no = (channel & 0xff00) >> 8; + winner->ds1explicit = (channel & 0x10000) >> 16; + winner->channelno = channel & 0xff; + if (ctrl->localtype == PRI_NETWORK) { + winner->chanflags = FLAG_EXCLUSIVE; + } else { + winner->chanflags = FLAG_PREFERRED; + } + } else { + /* Do not send Q931_CHANNEL_IDENT */ + winner->chanflags = 0; + } + + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_RETRIEVE], + q931_retrieve_timeout, call); + if (send_message(ctrl, winner, Q931_RETRIEVE, retrieve_ies)) { + pri_schedule_del(ctrl, call->hold_timer); + call->hold_timer = 0; + + /* Call is still on hold so forget the channel. */ + winner->channelno = 0;/* No channel */ + winner->ds1no = 0; + winner->ds1explicit = 0; + winner->chanflags = 0; + return -1; + } + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_RETRIEVE_REQ); + return 0; +} + +static int retrieve_ack_ies[] = { + Q931_CHANNEL_IDENT, + -1 +}; + +/*! + * \brief Send the RETRIEVE ACKNOWLEDGE message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * \param channel Encoded channel id to use. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_retrieve_ack(struct pri *ctrl, struct q931_call *call, int channel) +{ + struct q931_call *winner; + + winner = q931_find_winning_call(call); + if (!winner) { + return -1; + } + + winner->ds1no = (channel & 0xff00) >> 8; + winner->ds1explicit = (channel & 0x10000) >> 16; + winner->channelno = channel & 0xff; + winner->chanflags = FLAG_EXCLUSIVE; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE); + + return send_message(ctrl, winner, Q931_RETRIEVE_ACKNOWLEDGE, retrieve_ack_ies); +} + +static int retrieve_reject_ies[] = { + Q931_CAUSE, + -1 +}; + +/*! + * \internal + * \brief Send the RETRIEVE REJECT message only. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (subcall) + * \param cause Q.931 cause code for rejecting the retrieve request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int q931_send_retrieve_rej_msg(struct pri *ctrl, struct q931_call *call, int cause) +{ + call->cause = cause; + call->causecode = CODE_CCITT; + call->causeloc = LOC_PRIV_NET_LOCAL_USER; + return send_message(ctrl, call, Q931_RETRIEVE_REJECT, retrieve_reject_ies); +} + +/*! + * \brief Send the RETRIEVE REJECT message. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. (Master Q.931 subcall structure) + * \param cause Q.931 cause code for rejecting the retrieve request. + * + * \retval 0 on success. + * \retval -1 on error. + */ +int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause) +{ + struct q931_call *winner; + + UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD); + + winner = q931_find_winning_call(call); + if (!winner) { + return -1; + } + + /* Call is still on hold so forget the channel. */ + winner->channelno = 0;/* No channel */ + winner->ds1no = 0; + winner->ds1explicit = 0; + winner->chanflags = 0; + + return q931_send_retrieve_rej_msg(ctrl, winner, cause); +} + +static int pri_internal_clear(void *data); + +/* Fake RELEASE for NT-PTMP initiated SETUPs w/o response */ +static void pri_fake_clearing(void *data) +{ + struct q931_call *c = data; + struct pri *ctrl = c->pri; + + c->performing_fake_clearing = 1; + if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) + ctrl->schedev = 1; +} + +static void pri_create_fake_clearing(struct q931_call *c, struct pri *master) +{ + c->pri = master; + + pri_schedule_del(master, c->retranstimer); + c->retranstimer = pri_schedule_event(master, 0, pri_fake_clearing, c); +} + +//static int q931_get_subcall_count(struct q931_call *call); + +static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause) +{ int disconnect = 1; int release_compl = 0; - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "NEW_HANGUP DEBUG: Calling q931_hangup, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate)); - if (!pri || !c) + int t303_was_running = c->master_call->t303_timer; + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, + "NEW_HANGUP DEBUG: Calling q931_hangup, ourstate %s, peerstate %s, hold-state %s\n", + q931_call_state_str(c->ourcallstate), + q931_call_state_str(c->peercallstate), + q931_hold_state_str(c->master_call->hold_state)); + if (!ctrl || !c) return -1; /* If mandatory IE was missing, insist upon that cause code */ if (c->cause == PRI_CAUSE_MANDATORY_IE_MISSING) cause = c->cause; - if (cause == 34 || cause == 44 || cause == 82 || cause == 1 || cause == 81) { + switch (cause) { + case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: + case PRI_CAUSE_REQUESTED_CHAN_UNAVAIL: + case PRI_CAUSE_IDENTIFIED_CHANNEL_NOTEXIST: + case PRI_CAUSE_UNALLOCATED: + case PRI_CAUSE_INVALID_CALL_REFERENCE: /* We'll send RELEASE_COMPLETE with these causes */ disconnect = 0; release_compl = 1; - } - if (cause == 6 || cause == 7 || cause == 26) { + break; + case PRI_CAUSE_CHANNEL_UNACCEPTABLE: + case PRI_CAUSE_CALL_AWARDED_DELIVERED: + case PRI_CAUSE_NONSELECTED_USER_CLEARING: /* We'll send RELEASE with these causes */ disconnect = 0; + break; + default: + break; } + if (c->cis_call) { + disconnect = 0; + } + + c->hangupinitiated = 1; + stop_t303(c); + /* All other causes we send with DISCONNECT */ switch(c->ourcallstate) { case Q931_CALL_STATE_NULL: if (c->peercallstate == Q931_CALL_STATE_NULL) /* free the resources if we receive or send REL_COMPL */ - q931_destroycall(pri, c->cr); + pri_destroycall(ctrl, c); else if (c->peercallstate == Q931_CALL_STATE_RELEASE_REQUEST) - q931_release_complete(pri,c,cause); + q931_release_complete(ctrl,c,cause); break; case Q931_CALL_STATE_CALL_INITIATED: + if (c->outboundbroadcast && c->master_call == c && t303_was_running) { + //c->fakeclearing = 1; + //c->alive = 0; + /* We need to fake a received clearing sequence in this case... */ + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Faking clearing\n"); + } + pri_create_fake_clearing(c, PRI_MASTER(ctrl)); + /* This means that we never got a response from a TEI */ + return 0; + } /* we sent SETUP */ case Q931_CALL_STATE_OVERLAP_SENDING: /* received SETUP_ACKNOWLEDGE */ @@ -3221,29 +5365,45 @@ case Q931_CALL_STATE_OVERLAP_RECEIVING: /* received SETUP_ACKNOWLEDGE */ /* send DISCONNECT in general */ - if (c->peercallstate != Q931_CALL_STATE_NULL && c->peercallstate != Q931_CALL_STATE_DISCONNECT_REQUEST && c->peercallstate != Q931_CALL_STATE_DISCONNECT_INDICATION && c->peercallstate != Q931_CALL_STATE_RELEASE_REQUEST && c->peercallstate != Q931_CALL_STATE_RESTART_REQUEST && c->peercallstate != Q931_CALL_STATE_RESTART) { + switch (c->peercallstate) { + default: if (disconnect) - q931_disconnect(pri,c,cause); + q931_disconnect(ctrl,c,cause); else if (release_compl) - q931_release_complete(pri,c,cause); + q931_release_complete(ctrl,c,cause); else - q931_release(pri,c,cause); - } else - pri_error(pri, "Wierd, doing nothing but this shouldn't happen, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate)); + q931_release(ctrl,c,cause); + break; + case Q931_CALL_STATE_NULL: + case Q931_CALL_STATE_DISCONNECT_REQUEST: + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_RELEASE_REQUEST: + case Q931_CALL_STATE_RESTART_REQUEST: + case Q931_CALL_STATE_RESTART: + pri_error(ctrl, + "Wierd, doing nothing but this shouldn't happen, ourstate %s, peerstate %s\n", + q931_call_state_str(c->ourcallstate), + q931_call_state_str(c->peercallstate)); + break; + } break; case Q931_CALL_STATE_ACTIVE: /* received CONNECT */ - q931_disconnect(pri,c,cause); + if (c->cis_call) { + q931_release(ctrl, c, cause); + break; + } + q931_disconnect(ctrl,c,cause); break; case Q931_CALL_STATE_DISCONNECT_REQUEST: /* sent DISCONNECT */ - q931_release(pri,c,cause); + q931_release(ctrl,c,cause); break; case Q931_CALL_STATE_DISCONNECT_INDICATION: /* received DISCONNECT */ if (c->peercallstate == Q931_CALL_STATE_DISCONNECT_REQUEST) { c->alive = 1; - q931_release(pri,c,cause); + q931_release(ctrl,c,cause); } break; case Q931_CALL_STATE_RELEASE_REQUEST: @@ -3253,75 +5413,129 @@ case Q931_CALL_STATE_RESTART: case Q931_CALL_STATE_RESTART_REQUEST: /* sent RESTART */ - pri_error(pri, "q931_hangup shouldn't be called in this state, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate)); + pri_error(ctrl, + "q931_hangup shouldn't be called in this state, ourstate %s, peerstate %s\n", + q931_call_state_str(c->ourcallstate), + q931_call_state_str(c->peercallstate)); break; default: - pri_error(pri, "We're not yet handling hanging up when our state is %d, contact support@digium.com, ourstate %s, peerstate %s\n", - c->ourcallstate, - callstate2str(c->ourcallstate), - callstate2str(c->peercallstate)); + pri_error(ctrl, + "We're not yet handling hanging up when our state is %d, contact support@digium.com, ourstate %s, peerstate %s\n", + c->ourcallstate, + q931_call_state_str(c->ourcallstate), + q931_call_state_str(c->peercallstate)); return -1; } /* we did handle hangup properly at this point */ return 0; } -int q931_receive(struct pri *pri, q931_h *h, int len) +static void initiate_hangup_if_needed(struct pri *pri, q931_call *call, int cause); + +int q931_hangup(struct pri *ctrl, q931_call *call, int cause) { - q931_mh *mh; - q931_call *c; - q931_ie *ie; - unsigned int x; - int y; - int res; - int r; - int mandies[MAX_MAND_IES]; - int missingmand; - int codeset, cur_codeset; - int last_ie[8]; - struct apdu_event *cur = NULL; + int i; - memset(last_ie, 0, sizeof(last_ie)); - if (pri->debug & PRI_DEBUG_Q931_DUMP) - q931_dump(pri, h, len, 0); -#ifdef LIBPRI_COUNTERS - pri->q931_rxcount++; -#endif - mh = (q931_mh *)(h->contents + h->crlen); - if ((h->pd == 0x3) || (h->pd == 0x43)) { - /* This is the weird maintenance stuff. We majorly - KLUDGE this by changing byte 4 from a 0xf (SERVICE) - to a 0x7 (SERVICE ACKNOWLEDGE) */ - h->raw[h->crlen + 2] -= 0x8; - q931_xmit(pri, h, len, 1); - return 0; - } else if (h->pd != pri->protodisc) { - pri_error(pri, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd); - return 0; + if (call->master_call->outboundbroadcast) { + if (call->master_call == call) { + int slaves = 0; + + /* Master is called with hangup - initiate hangup with slaves */ + for (i = 0; i < Q931_MAX_TEI; i++) { + if (call->subcalls[i]) { + slaves++; + if (i == call->master_call->pri_winner) { + __q931_hangup(call->subcalls[i]->pri, call->subcalls[i], cause); + } else { + initiate_hangup_if_needed(call->subcalls[i]->pri, call->subcalls[i], cause); + } + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "%s: Hanging up %d, winner %d\n", __FUNCTION__, + i, call->master_call->pri_winner); + } + } + } + + call->hangupinitiated = 1; + + if ((!slaves && (call->master_call->pri_winner < 0)) || (call->performing_fake_clearing)) { + __q931_hangup(ctrl, call, cause); + } + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "%s: Slaves %d\n", __FUNCTION__, slaves); + } + return 0; + } else { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "%s: Slave hangup\n", __FUNCTION__); + } + return __q931_hangup(ctrl, call, cause); + } + } else { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "%s: other hangup\n", __FUNCTION__); + } + return __q931_hangup(ctrl, call, cause); } - c = q931_getcall(pri, q931_cr(h), 0); - if (!c) { - pri_error(pri, "Unable to locate call %d\n", q931_cr(h)); + return 0; +} + +static int prepare_to_handle_maintenance_message(struct pri *ctrl, q931_mh *mh, q931_call *c) +{ + if ((!ctrl) || (!mh) || (!c)) { return -1; } - /* Preliminary handling */ + /* SERVICE messages are a superset of messages that can take b-channels + * or entire d-channels in and out of service */ switch(mh->msg) { + /* the ATT_SERVICE/ATT_SERVICE_ACKNOWLEDGE and NATIONAL_SERVICE/NATIONAL_SERVICE_ACKNOWLEDGE + * are mirrors of each other. We only have to check for one type because they are pre-handled + * the same way as each other */ + case ATT_SERVICE: + case ATT_SERVICE_ACKNOWLEDGE: + c->channelno = -1; + c->slotmap = -1; + c->chanflags = 0; + c->ds1explicit = 0; + c->ds1no = 0; + c->cis_call = 0; + c->ri = -1; + c->changestatus = -1; + break; + default: + pri_error(ctrl, "!! Don't know how to pre-handle maintenance message type '%d'\n", mh->msg); + return -1; + } + return 0; +} + +static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_call *c) +{ + if ((!ctrl) || (!mh) || (!c)) { + return -1; + } + + switch(mh->msg) { case Q931_RESTART: - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "-- Processing Q.931 Restart\n"); + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, "-- Processing Q.931 Restart\n"); /* Reset information */ c->channelno = -1; c->slotmap = -1; c->chanflags = 0; c->ds1no = 0; + c->ds1explicit = 0; + c->cis_call = 0; c->ri = -1; break; case Q931_FACILITY: - c->callername[0] = '\0'; + if (q931_is_dummy_call(c)) { + q931_party_address_init(&c->called); + } break; case Q931_SETUP: - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, "-- Processing Q.931 Call Setup\n"); + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, "-- Processing Q.931 Call Setup\n"); c->channelno = -1; c->slotmap = -1; c->chanflags = 0; @@ -3334,29 +5548,25 @@ c->userl2 = -1; c->userl3 = -1; c->rateadaption = -1; - c->calledplan = -1; - c->callerplan = -1; - c->callerpres = -1; - c->callernum[0] = '\0'; - c->callednum[0] = '\0'; - c->callername[0] = '\0'; - c->callerani[0] = '\0'; - c->callerplanani = -1; - c->redirectingplan = -1; - c->redirectingpres = -1; - c->redirectingreason = -1; - c->origcalledplan = -1; - c->origcalledpres = -1; - c->origredirectingreason = -1; - c->redirectingnum[0] = '\0'; - c->origcallednum[0] = '\0'; - c->redirectingname[0] = '\0'; - c->origcalledname[0] = '\0'; + + q931_party_address_init(&c->called); + q931_party_id_init(&c->local_id); + q931_party_id_init(&c->remote_id); + q931_party_redirecting_init(&c->redirecting); + + /* + * Make sure that keypad and overlap digit buffers are empty in + * case they are not in the message. + */ + c->keypad_digits[0] = '\0'; + c->overlap_digits[0] = '\0'; + c->useruserprotocoldisc = -1; c->useruserinfo[0] = '\0'; c->complete = 0; c->nonisdn = 0; c->aoc_units = -1; + c->reversecharge = -1; /* Fall through */ case Q931_CONNECT: case Q931_ALERTING: @@ -3369,8 +5579,7 @@ c->progressmask = 0; break; case Q931_CONNECT_ACKNOWLEDGE: - if (c->retranstimer) - pri_schedule_del(pri, c->retranstimer); + pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; break; case Q931_RELEASE: @@ -3379,14 +5588,12 @@ c->causecode = -1; c->causeloc = -1; c->aoc_units = -1; - if (c->retranstimer) - pri_schedule_del(pri, c->retranstimer); + pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; break; case Q931_RELEASE_COMPLETE: - if (c->retranstimer) - pri_schedule_del(pri, c->retranstimer); + pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; /* Fall through */ @@ -3394,45 +5601,252 @@ c->cause = -1; c->causecode = -1; c->causeloc = -1; - c->sugcallstate = -1; + c->sugcallstate = Q931_CALL_STATE_NOT_SET; c->aoc_units = -1; break; case Q931_RESTART_ACKNOWLEDGE: c->channelno = -1; + c->ds1no = 0; + c->ds1explicit = 0; + c->cis_call = 0; break; case Q931_INFORMATION: - c->callednum[0] = '\0'; + /* + * Make sure that keypad and overlap digit buffers are empty in + * case they are not in the message. + */ + c->keypad_digits[0] = '\0'; + c->overlap_digits[0] = '\0'; break; case Q931_STATUS_ENQUIRY: break; case Q931_SETUP_ACKNOWLEDGE: break; case Q931_NOTIFY: + q931_party_number_init(&c->redirection_number); break; - case Q931_USER_INFORMATION: - case Q931_SEGMENT: - case Q931_CONGESTION_CONTROL: case Q931_HOLD: + break; case Q931_HOLD_ACKNOWLEDGE: + break; case Q931_HOLD_REJECT: + c->cause = -1; + break; case Q931_RETRIEVE: + c->channelno = 0xFF; + c->ds1no = 0; + c->ds1explicit = 0; + break; case Q931_RETRIEVE_ACKNOWLEDGE: + break; case Q931_RETRIEVE_REJECT: + c->cause = -1; + break; + case Q931_USER_INFORMATION: + case Q931_SEGMENT: + case Q931_CONGESTION_CONTROL: case Q931_RESUME: case Q931_RESUME_ACKNOWLEDGE: case Q931_RESUME_REJECT: case Q931_SUSPEND: case Q931_SUSPEND_ACKNOWLEDGE: case Q931_SUSPEND_REJECT: - pri_error(pri, "!! Not yet handling pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); + pri_error(ctrl, "!! Not yet handling pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); /* Fall through */ default: - pri_error(pri, "!! Don't know how to pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); - q931_status(pri,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); + pri_error(ctrl, "!! Don't know how to pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); + q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); if (c->newcall) - q931_destroycall(pri,c->cr); + pri_destroycall(ctrl, c); return -1; } + return 0; +} + +static struct q931_call *q931_get_subcall_winner(struct q931_call *master) +{ + if (master->pri_winner < 0) { + return NULL; + } else { + return master->subcalls[master->pri_winner]; + } +} + +static void initiate_hangup_if_needed(struct pri *pri, q931_call *call, int cause) +{ + if (!call->hangupinitiated) { + q931_hangup(pri, call, cause); + call->alive = 0; + } +} + +#if 0 +static int q931_get_subcall_count(struct q931_call *call) +{ + int count = 0; + int i; + + call = call->master_call; + for (i = 0; i < Q931_MAX_TEI; i++) { + if (call->subcalls[i]) + count++; + } + + return count; +} +#endif + +static void q931_set_subcall_winner(struct q931_call *subcall) +{ + struct q931_call *realcall = subcall->master_call; + int i; + + /* Set the winner first */ + for (i = 0; i < Q931_MAX_TEI; i++) { + if (realcall->subcalls[i] && realcall->subcalls[i] == subcall) { + realcall->pri_winner = i; + } + } + if (realcall->pri_winner < 0) { + pri_error(subcall->pri, "We should always find the winner in the list!\n"); + return; + } + + /* Start tear down of calls that were not chosen */ + for (i = 0; i < Q931_MAX_TEI; i++) { + if (realcall->subcalls[i] && realcall->subcalls[i] != subcall) { + initiate_hangup_if_needed(realcall->subcalls[i]->pri, realcall->subcalls[i], + PRI_CAUSE_NONSELECTED_USER_CLEARING); + } + } +} + +static struct q931_call *q931_get_subcall(struct pri *ctrl, struct q931_call *master_call) +{ + int i; + struct q931_call *cur; + int firstfree = -1; + + /* First try to locate our subcall */ + for (i = 0; i < Q931_MAX_TEI; i++) { + if (master_call->subcalls[i]) { + if (master_call->subcalls[i]->pri == ctrl) { + return master_call->subcalls[i]; + } + } else if (firstfree == -1) { + firstfree = i; + } + } + if (firstfree < 0) { + pri_error(ctrl, "Tried to add more than %d TEIs to call and failed\n", + Q931_MAX_TEI); + return NULL; + } + + /* Create new subcall. */ + cur = malloc(sizeof(*cur)); + if (!cur) { + pri_error(ctrl, "Unable to allocate call\n"); + return NULL; + } + *cur = *master_call; + cur->pri = ctrl; + cur->next = NULL; + cur->apdus = NULL; + cur->bridged_call = NULL; + //cur->master_call = master_call; /* We get this assignment for free. */ + for (i = 0; i < Q931_MAX_TEI; ++i) { + cur->subcalls[i] = NULL; + } + cur->t303_timer = 0;/* T303 should only be on on the master call */ + cur->hold_timer = 0; + cur->retranstimer = 0; + + /* Assume we sent a SETUP and this is the first response to it from this peer. */ + cur->ourcallstate = Q931_CALL_STATE_CALL_INITIATED; + cur->peercallstate = Q931_CALL_STATE_CALL_PRESENT; + + master_call->subcalls[firstfree] = cur; + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Adding subcall %p for TEI %d to call %p at position %d\n", + cur, ctrl->tei, master_call, firstfree); + } + /* Should only get here if the TEI is not found */ + return cur; +} + +int q931_receive(struct pri *ctrl, q931_h *h, int len) +{ + q931_mh *mh; + q931_call *c; + q931_ie *ie; + unsigned int x; + int y; + int res; + int r; + int mandies[MAX_MAND_IES]; + int missingmand; + int codeset, cur_codeset; + int last_ie[8]; + int cref; + + memset(last_ie, 0, sizeof(last_ie)); + if (ctrl->debug & PRI_DEBUG_Q931_DUMP) + q931_dump(ctrl, h, len, 0); +#ifdef LIBPRI_COUNTERS + ctrl->q931_rxcount++; +#endif + mh = (q931_mh *)(h->contents + h->crlen); + if ((h->pd != ctrl->protodisc) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { + pri_error(ctrl, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd); + return 0; + } + if (((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) && (!ctrl->service_message_support)) { + /* Real service message support has not been enabled (and is OFF in libpri by default), + * so we have to revert to the 'traditional' KLUDGE of changing byte 4 from a 0xf (SERVICE) + * to a 0x7 (SERVICE ACKNOWLEDGE) */ + /* This is the weird maintenance stuff. We majorly + KLUDGE this by changing byte 4 from a 0xf (SERVICE) + to a 0x7 (SERVICE ACKNOWLEDGE) */ + h->raw[h->crlen + 2] -= 0x8; + q931_xmit(ctrl, h, len, 1, 0); + return 0; + } + + cref = q931_cr(h); + c = q931_getcall(ctrl, cref); + if (!c) { + pri_error(ctrl, "Unable to locate call %d\n", cref); + return -1; + } + if (c->master_call->outboundbroadcast && ctrl != PRI_MASTER(ctrl)) { + c = q931_get_subcall(ctrl, c->master_call); + if (!c) { + pri_error(ctrl, "Unable to locate subcall for %d\n", cref); + return -1; + } + } + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, + "Received message for call %p on %p TEI/SAPI %d/%d, call->pri is %p TEI/SAPI %d/%d\n", + c, + ctrl, ctrl->tei, ctrl->sapi, + c->pri, c->pri->tei, c->pri->sapi); + } + + /* Preliminary handling */ + ctrl->facility.count = 0; + c->connected_number_in_message = 0; + c->redirecting_number_in_message = 0; + if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { + prepare_to_handle_maintenance_message(ctrl, mh, c); + } else { + prepare_to_handle_q931_message(ctrl, mh, c); + } + q931_clr_subcommands(ctrl); + /* Handle IEs */ memset(mandies, 0, sizeof(mandies)); missingmand = 0; @@ -3453,7 +5867,7 @@ } r = ielen(ie); if (r > len) { - pri_error(pri, "XXX Message longer than it should be?? XXX\n"); + pri_error(ctrl, "XXX Message longer than it should be?? XXX\n"); return -1; } /* Special processing for codeset shifts */ @@ -3461,16 +5875,16 @@ case Q931_LOCKING_SHIFT: y = ie->ie & 7; /* Requested codeset */ /* Locking shifts couldn't go to lower codeset, and couldn't follows non-locking shifts - verify this */ - if ((cur_codeset != codeset) && (pri->debug & PRI_DEBUG_Q931_ANOMALY)) - pri_message(pri, "XXX Locking shift immediately follows non-locking shift (from %d through %d to %d) XXX\n", codeset, cur_codeset, y); + if ((cur_codeset != codeset) && (ctrl->debug & PRI_DEBUG_Q931_ANOMALY)) + pri_message(ctrl, "XXX Locking shift immediately follows non-locking shift (from %d through %d to %d) XXX\n", codeset, cur_codeset, y); if (y > 0) { - if ((y < codeset) && (pri->debug & PRI_DEBUG_Q931_ANOMALY)) - pri_error(pri, "!! Trying to locked downshift codeset from %d to %d !!\n", codeset, y); + if ((y < codeset) && (ctrl->debug & PRI_DEBUG_Q931_ANOMALY)) + pri_error(ctrl, "!! Trying to locked downshift codeset from %d to %d !!\n", codeset, y); codeset = cur_codeset = y; } else { /* Locking shift to codeset 0 is forbidden by all specifications */ - pri_error(pri, "!! Invalid locking shift to codeset 0 !!\n"); + pri_error(ctrl, "!! Invalid locking shift to codeset 0 !!\n"); } break; case Q931_NON_LOCKING_SHIFT: @@ -3480,24 +5894,24 @@ /* Sanity check for IE code order */ if (!(ie->ie & 0x80)) { if (last_ie[cur_codeset] > ie->ie) { - if ((pri->debug & PRI_DEBUG_Q931_ANOMALY)) - pri_message(pri, "XXX Out-of-order IE %d at codeset %d (last was %d)\n", ie->ie, cur_codeset, last_ie[cur_codeset]); + if ((ctrl->debug & PRI_DEBUG_Q931_ANOMALY)) + pri_message(ctrl, "XXX Out-of-order IE %d at codeset %d (last was %d)\n", ie->ie, cur_codeset, last_ie[cur_codeset]); } else last_ie[cur_codeset] = ie->ie; } /* Ignore non-locking shifts for TR41459-based signalling */ - switch (pri->switchtype) { + switch (ctrl->switchtype) { case PRI_SWITCH_LUCENT5E: case PRI_SWITCH_ATT4ESS: if (cur_codeset != codeset) { - if ((pri->debug & PRI_DEBUG_Q931_DUMP)) - pri_message(pri, "XXX Ignoring IE %d for temporary codeset %d XXX\n", ie->ie, cur_codeset); + if ((ctrl->debug & PRI_DEBUG_Q931_DUMP)) + pri_message(ctrl, "XXX Ignoring IE %d for temporary codeset %d XXX\n", ie->ie, cur_codeset); break; } /* Fall through */ default: - y = q931_handle_ie(cur_codeset, pri, c, mh->msg, ie); + y = q931_handle_ie(cur_codeset, ctrl, c, mh->msg, ie); /* XXX Applicable to codeset 0 only? XXX */ if (!cur_codeset && !(ie->ie & 0xf0) && (y < 0)) mandies[MAX_MAND_IES - 1] = Q931_FULL_IE(cur_codeset, ie->ie); @@ -3512,33 +5926,562 @@ for (x=0;x that's not an error */ - if (((pri->localtype != PRI_NETWORK) || (mh->msg != Q931_SETUP) || (mandies[x] != Q931_CHANNEL_IDENT)) && + if (((ctrl->localtype != PRI_NETWORK) || (mh->msg != Q931_SETUP) || (mandies[x] != Q931_CHANNEL_IDENT)) && ((mh->msg != Q931_PROGRESS) || (mandies[x] != Q931_PROGRESS_INDICATOR))) { - pri_error(pri, "XXX Missing handling for mandatory IE %d (cs%d, %s) XXX\n", Q931_IE_IE(mandies[x]), Q931_IE_CODESET(mandies[x]), ie2str(mandies[x])); + pri_error(ctrl, "XXX Missing handling for mandatory IE %d (cs%d, %s) XXX\n", Q931_IE_IE(mandies[x]), Q931_IE_CODESET(mandies[x]), ie2str(mandies[x])); missingmand++; } } } - + + /* Now handle the facility ie's after all the other ie's were processed. */ + q931_handle_facilities(ctrl, c, mh->msg); + /* Post handling */ + if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { + res = post_handle_maintenance_message(ctrl, h->pd, mh, c); + } else { + int allow_event = 1, allow_posthandle = 1; + + if (c->master_call->outboundbroadcast) { + nt_ptmp_handle_q931_message(ctrl, mh, c, &allow_event, &allow_posthandle); + } + + if (allow_posthandle) { + res = post_handle_q931_message(ctrl, mh, c, missingmand); + + if (res == Q931_RES_HAVEEVENT && !allow_event) { + res = 0; + } + } else { + res = 0; + } + } + return res; +} + +static int post_handle_maintenance_message(struct pri *ctrl, int protodisc, struct q931_mh *mh, struct q931_call *c) +{ + /* Do some maintenance stuff */ + if (((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (mh->msg == ATT_SERVICE)) + || ((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2) && (mh->msg == NATIONAL_SERVICE))) { + if (c->channelno > 0) { + ctrl->ev.e = PRI_EVENT_SERVICE; + ctrl->ev.service.channel = q931_encode_channel(c); + ctrl->ev.service.changestatus = 0x0f & c->changestatus; + } else { + switch (0x0f & c->changestatus) { + case SERVICE_CHANGE_STATUS_INSERVICE: + ctrl->ev.e = PRI_EVENT_DCHAN_UP; + q921_dchannel_up(ctrl); + break; + case SERVICE_CHANGE_STATUS_OUTOFSERVICE: + ctrl->ev.e = PRI_EVENT_DCHAN_DOWN; + q921_dchannel_down(ctrl); + break; + default: + pri_error(ctrl, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus)); + return -1; + } + } + maintenance_service_ack(ctrl, c); + return Q931_RES_HAVEEVENT; + } + if (((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (mh->msg == ATT_SERVICE_ACKNOWLEDGE)) + || ((protodisc == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2) && (mh->msg == NATIONAL_SERVICE_ACKNOWLEDGE))) { + if (c->channelno > 0) { + ctrl->ev.e = PRI_EVENT_SERVICE_ACK; + ctrl->ev.service_ack.channel = q931_encode_channel(c); + ctrl->ev.service_ack.changestatus = 0x0f & c->changestatus; + } else { + switch (0x0f & c->changestatus) { + case SERVICE_CHANGE_STATUS_INSERVICE: + ctrl->ev.e = PRI_EVENT_DCHAN_UP; + q921_dchannel_up(ctrl); + break; + case SERVICE_CHANGE_STATUS_OUTOFSERVICE: + ctrl->ev.e = PRI_EVENT_DCHAN_DOWN; + q921_dchannel_down(ctrl); + break; + default: + pri_error(ctrl, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus)); + return -1; + } + } + return Q931_RES_HAVEEVENT; + } + + pri_error(ctrl, "!! Don't know how to post-handle maintenance message type %d\n", mh->msg); + return -1; +} + +/*! + * \internal + * \brief Rank the given Q.931 call state for call etablishment. + * + * \param state Q.931 call state to rank for competing PTMP NT calls. + * + * \return Call establishment state ranking. + */ +static enum Q931_RANKED_CALL_STATE q931_rank_state(enum Q931_CALL_STATE state) +{ + enum Q931_RANKED_CALL_STATE rank; + + switch (state) { + case Q931_CALL_STATE_CALL_INITIATED: + case Q931_CALL_STATE_CALL_PRESENT: + rank = Q931_RANKED_CALL_STATE_PRESENT; + break; + case Q931_CALL_STATE_OVERLAP_SENDING: + case Q931_CALL_STATE_OVERLAP_RECEIVING: + rank = Q931_RANKED_CALL_STATE_OVERLAP; + break; + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + rank = Q931_RANKED_CALL_STATE_PROCEEDING; + break; + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + rank = Q931_RANKED_CALL_STATE_ALERTING; + break; + case Q931_CALL_STATE_ACTIVE: + case Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE: + rank = Q931_RANKED_CALL_STATE_CONNECT; + break; + default: + rank = Q931_RANKED_CALL_STATE_OTHER; + break; + } + + return rank; +} + +/*! + * \brief Determine if the master will pass an event to the upper layer. + * + * \param ctrl D channel controller. + * \param subcall Q.931 call leg. + * \param msg_type Current message type being processed. + * + * \note This function must parallel nt_ptmp_handle_q931_message(). + * + * \retval TRUE if the master will pass an event to the upper layer. + * \retval FALSE if the event will be blocked. + */ +int q931_master_pass_event(struct pri *ctrl, struct q931_call *subcall, int msg_type) +{ + struct q931_call *winner; + struct q931_call *master; + enum Q931_RANKED_CALL_STATE master_rank; + enum Q931_RANKED_CALL_STATE subcall_rank; + int will_pass; /*!< TRUE if the master will pass an event to the upper layer. */ + + master = subcall->master_call; + if (subcall == master) { + /* We are the master call so of course the master will pass an event. */ + return 1; + } + + winner = q931_get_subcall_winner(master); + if (winner && subcall == winner) { + /* We are the winner so of course the master will pass an event. */ + return 1; + } + + master_rank = q931_rank_state(master->ourcallstate); + will_pass = 0; + switch (msg_type) { + case Q931_SETUP_ACKNOWLEDGE: +#if 0 /* Overlap dialing in PTMP NT mode not supported at the present time. */ + if (master_rank < Q931_RANKED_CALL_STATE_OVERLAP) { + will_pass = 1; + } +#endif /* Overlap dialing in PTMP NT mode not supported at the present time. */ + break; + case Q931_CALL_PROCEEDING: + if (master_rank < Q931_RANKED_CALL_STATE_PROCEEDING) { + will_pass = 1; + } + break; + case Q931_PROGRESS: + /* + * We will just ignore this message since there could be multiple devices + * competing for this call. Who has access to the B channel at this time + * to give in-band signals anyway? + */ + break; + case Q931_ALERTING: + if (master_rank < Q931_RANKED_CALL_STATE_ALERTING) { + will_pass = 1; + } + break; + case Q931_CONNECT: + if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) { + /* We are expected to be the winner for the next message. */ + will_pass = 1; + } + break; + case Q931_DISCONNECT: + case Q931_RELEASE: + case Q931_RELEASE_COMPLETE: + /* Only deal with the winner. */ + break; + case Q931_FACILITY: + case Q931_NOTIFY: + if (!winner) { + /* The overlap rank does not count here. */ + if (master_rank == Q931_RANKED_CALL_STATE_OVERLAP) { + master_rank = Q931_RANKED_CALL_STATE_PRESENT; + } + subcall_rank = q931_rank_state(subcall->ourcallstate); + if (subcall_rank == Q931_RANKED_CALL_STATE_OVERLAP) { + subcall_rank = Q931_RANKED_CALL_STATE_PRESENT; + } + if (master_rank == subcall_rank) { + /* + * No winner yet but the subcall is as advanced as the master. + * Allow the supplementary service event to pass. + */ + will_pass = 1; + } + } + break; + default: + /* Only deal with the winner. */ + break; + } + + return will_pass; +} + +/*! + * \internal + * \brief Handle outboundbroadcast incoming messages for the master_call's state. + * + * \param ctrl D channel controller. + * \param mh Q.931 message type header. + * \param subcall Q.931 call leg. + * \param allow_event Where to set the allow event to upper layer flag. + * \param allow_posthandle Where to set the allow post handle event flag. + * + * \details + * This is where we interact the subcalls state with the master_call's state. + * + * \note This function must parallel q931_master_pass_event(). + * + * \return Nothing + */ +static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *subcall, int *allow_event, int *allow_posthandle) +{ + struct q931_call *master = subcall->master_call; + struct q931_call *winner = q931_get_subcall_winner(master); + enum Q931_RANKED_CALL_STATE master_rank; + enum Q931_RANKED_CALL_STATE subcall_rank; + enum Q931_CALL_STATE newstate; + + /* For broadcast calls, we default to not allowing events to keep events received to a minimum + * and to allow post processing, since that is where hangup and subcall state handling and other processing is done */ + *allow_event = 0; + *allow_posthandle = 1; + + master_rank = q931_rank_state(master->ourcallstate); + + switch (mh->msg) { + case Q931_SETUP_ACKNOWLEDGE: +#if 0 /* Overlap dialing in PTMP NT mode not supported at the present time. */ + if (master_rank < Q931_RANKED_CALL_STATE_OVERLAP) { + *allow_event = 1; + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_OVERLAP_SENDING); + } +#endif /* Overlap dialing in PTMP NT mode not supported at the present time. */ + break; + case Q931_CALL_PROCEEDING: + if (master_rank < Q931_RANKED_CALL_STATE_PROCEEDING) { + *allow_event = 1; + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING); + } + break; + case Q931_PROGRESS: + /* + * We will just ignore this message since there could be multiple devices + * competing for this call. Who has access to the B channel at this time + * to give in-band signals anyway? + */ + break; + case Q931_ALERTING: + if (master_rank < Q931_RANKED_CALL_STATE_ALERTING) { + *allow_event = 1; + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_CALL_DELIVERED); + } + break; + case Q931_CONNECT: + if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) { + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_ACTIVE); + q931_set_subcall_winner(subcall); + *allow_event = 1; + } else { + /* Call clearing of non selected calls occurs in + * q931_set_subcall_winner() - All we need to do is make sure + * that this connect is not acknowledged */ + *allow_posthandle = 0; + } + break; + case Q931_DISCONNECT: + newstate = Q931_CALL_STATE_DISCONNECT_INDICATION; + goto process_hangup; + case Q931_RELEASE: + case Q931_RELEASE_COMPLETE: + newstate = Q931_CALL_STATE_NULL; +process_hangup: + if (!winner) { + /* If there's not a winner, we just take the cause and pass it up to the + * master_call */ + master->cause = subcall->cause; + } else { + /* There *is* a winner */ + if (subcall == winner) { + /* .. and we're it: */ + *allow_event = 1; + UPDATE_OURCALLSTATE(ctrl, master, newstate); + } + } + break; + case Q931_FACILITY: + case Q931_NOTIFY: + if (winner) { + if (subcall == winner) { + /* Only deal with the winner. */ + *allow_event = 1; + } + } else { + /* The overlap rank does not count here. */ + if (master_rank == Q931_RANKED_CALL_STATE_OVERLAP) { + master_rank = Q931_RANKED_CALL_STATE_PRESENT; + } + subcall_rank = q931_rank_state(subcall->ourcallstate); + if (subcall_rank == Q931_RANKED_CALL_STATE_OVERLAP) { + subcall_rank = Q931_RANKED_CALL_STATE_PRESENT; + } + if (master_rank == subcall_rank) { + /* + * No winner yet but the subcall is as advanced as the master. + * Allow the supplementary service event to pass. + */ + *allow_event = 1; + } + } + break; + default: + if (winner && subcall == winner) { + /* Only deal with the winner. */ + *allow_event = 1; + } + break; + } +} + +/*! + * \internal + * \brief Fill in the FACILITY event fields. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * + * \return Nothing + */ +static void q931_fill_facility_event(struct pri *ctrl, struct q931_call *call) +{ + ctrl->ev.e = PRI_EVENT_FACILITY; + ctrl->ev.facility.subcmds = &ctrl->subcmds; + ctrl->ev.facility.channel = q931_encode_channel(call); + ctrl->ev.facility.cref = call->cr; + ctrl->ev.facility.call = call->master_call; + ctrl->ev.facility.subcall = call; + + /* Need to do this for backward compatibility with struct pri_event_facname */ + libpri_copy_string(ctrl->ev.facility.callingname, call->remote_id.name.str, + sizeof(ctrl->ev.facility.callingname)); + libpri_copy_string(ctrl->ev.facility.callingnum, call->remote_id.number.str, + sizeof(ctrl->ev.facility.callingnum)); + ctrl->ev.facility.callingpres = q931_party_id_presentation(&call->remote_id); + ctrl->ev.facility.callingplan = call->remote_id.number.plan; +} + +/*! + * \internal + * \brief APDU wait for response message timeout. + * + * \param data Callback data pointer. + * + * \return Nothing + */ +static void q931_apdu_timeout(void *data) +{ + struct apdu_event *apdu; + struct pri *ctrl; + struct q931_call *call; + + apdu = data; + call = apdu->call; + ctrl = call->pri; + + q931_clr_subcommands(ctrl); + apdu->response.callback(APDU_CALLBACK_REASON_TIMEOUT, ctrl, call, apdu, NULL); + if (ctrl->subcmds.counter_subcmd) { + q931_fill_facility_event(ctrl, call); + ctrl->schedev = 1; + } + + pri_call_apdu_delete(call, apdu); +} + +/*! + * \internal + * \brief Find the active call given the held call. + * + * \param ctrl D channel controller. + * \param held_call Held call to help locate a compatible active call. + * + * \retval master-active-call on success. + * \retval NULL on error. + */ +static struct q931_call *q931_find_held_active_call(struct pri *ctrl, struct q931_call *held_call) +{ + struct pri *master; + struct q931_call *cur; + struct q931_call *winner; + struct q931_call *match; + + match = NULL; + master = PRI_MASTER(ctrl); + for (cur = *master->callpool; cur; cur = cur->next) { + if (cur->hold_state == Q931_HOLD_STATE_IDLE) { + /* Found an active call. */ + winner = q931_find_winning_call(cur); + if (!winner || (BRI_NT_PTMP(ctrl) && winner->pri != held_call->pri)) { + /* There is no winner or the active call does not go to the same TEI. */ + continue; + } + switch (winner->ourcallstate) { + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + case Q931_CALL_STATE_ACTIVE: + break; + default: + /* Active call not in a good state to transfer. */ + continue; + } + if (q931_party_number_cmp(&winner->remote_id.number, + &held_call->remote_id.number)) { + /* The remote party number does not match. This is a weak match. */ + match = cur; + continue; + } + /* Found an exact match. */ + match = cur; + break; + } + } + + return match; +} + +/*! + * \internal + * \brief Find the held call given the active call. + * + * \param ctrl D channel controller. + * \param active_call Active call to help locate a compatible held call. + * + * \retval master-held-call on success. + * \retval NULL on error. + */ +static struct q931_call *q931_find_held_call(struct pri *ctrl, struct q931_call *active_call) +{ + struct pri *master; + struct q931_call *cur; + struct q931_call *winner; + struct q931_call *match; + + match = NULL; + master = PRI_MASTER(ctrl); + for (cur = *master->callpool; cur; cur = cur->next) { + if (cur->hold_state == Q931_HOLD_STATE_CALL_HELD) { + /* Found a held call. */ + winner = q931_find_winning_call(cur); + if (!winner || (BRI_NT_PTMP(ctrl) && winner->pri != active_call->pri)) { + /* There is no winner or the held call does not go to the same TEI. */ + continue; + } + switch (winner->ourcallstate) { + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + case Q931_CALL_STATE_ACTIVE: + break; + default: + /* Held call not in a good state to transfer. */ + continue; + } + if (q931_party_number_cmp(&winner->remote_id.number, + &active_call->remote_id.number)) { + /* The remote party number does not match. This is a weak match. */ + match = cur; + continue; + } + /* Found an exact match. */ + match = cur; + break; + } + } + + return match; +} + +/*! + * \internal + * \brief Process the decoded information in the Q.931 message. + * + * \param ctrl D channel controller. + * \param mh Q.931 message header. + * \param c Q.931 call leg. + * \param missingmand Number of missing mandatory ie's. + * + * \retval 0 if no error or event. + * \retval Q931_RES_HAVEEVENT if have an event. + * \retval -1 on error. + */ +static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand) +{ + int res; + struct apdu_event *cur = NULL; + struct pri_subcommand *subcmd; + struct q931_call *master_call; + switch(mh->msg) { case Q931_RESTART: if (missingmand) { - q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING); - q931_destroycall(pri, c->cr); + q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); + pri_destroycall(ctrl, c); break; } - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_RESTART); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RESTART); c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST; /* Send back the Restart Acknowledge */ - restart_ack(pri, c); + restart_ack(ctrl, c); /* Notify user of restart event */ - pri->ev.e = PRI_EVENT_RESTART; - pri->ev.restart.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); + ctrl->ev.e = PRI_EVENT_RESTART; + ctrl->ev.restart.channel = q931_encode_channel(c); return Q931_RES_HAVEEVENT; case Q931_SETUP: if (missingmand) { - q931_release_complete(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING); + q931_release_complete(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); break; } /* Must be new call */ @@ -3548,185 +6491,279 @@ if (c->progressmask & PRI_PROG_CALLER_NOT_ISDN) c->nonisdn = 1; c->newcall = 0; - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CALL_PRESENT); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_PRESENT); c->peercallstate = Q931_CALL_STATE_CALL_INITIATED; /* it's not yet a call since higher level can respond with RELEASE or RELEASE_COMPLETE */ c->alive = 0; if (c->transmoderate != TRANS_MODE_64_CIRCUIT) { - q931_release_complete(pri, c, PRI_CAUSE_BEARERCAPABILITY_NOTIMPL); + q931_release_complete(ctrl, c, PRI_CAUSE_BEARERCAPABILITY_NOTIMPL); break; } - pri->ev.e = PRI_EVENT_RING; - pri->ev.ring.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.ring.callingpres = c->callerpres; - pri->ev.ring.callingplan = c->callerplan; - pri->ev.ring.callingplanani = c->callerplanani; - pri->ev.ring.callingplanrdnis = c->redirectingplan; - pri->ev.ring.callingplanorigcalled = c->origcalledplan; - pri->ev.ring.ani2 = c->ani2; - libpri_copy_string(pri->ev.ring.callingani, c->callerani, sizeof(pri->ev.ring.callingani)); - libpri_copy_string(pri->ev.ring.callingnum, c->callernum, sizeof(pri->ev.ring.callingnum)); - libpri_copy_string(pri->ev.ring.callingname, c->callername, sizeof(pri->ev.ring.callingname)); - pri->ev.ring.calledplan = c->calledplan; - libpri_copy_string(pri->ev.ring.callingsubaddr, c->callingsubaddr, sizeof(pri->ev.ring.callingsubaddr)); - libpri_copy_string(pri->ev.ring.callednum, c->callednum, sizeof(pri->ev.ring.callednum)); - libpri_copy_string(pri->ev.ring.origcalledname, c->origcalledname, sizeof(pri->ev.ring.origcalledname)); - libpri_copy_string(pri->ev.ring.origcallednum, c->origcallednum, sizeof(pri->ev.ring.origcallednum)); - libpri_copy_string(pri->ev.ring.redirectingnum, c->redirectingnum, sizeof(pri->ev.ring.redirectingnum)); - libpri_copy_string(pri->ev.ring.redirectingname, c->redirectingname, sizeof(pri->ev.ring.redirectingname)); - libpri_copy_string(pri->ev.ring.useruserinfo, c->useruserinfo, sizeof(pri->ev.ring.useruserinfo)); + + if (c->redirecting.from.number.valid && !c->redirecting.count) { + /* + * This is most likely because the redirecting number came in + * with the redirecting ie only and not a DivertingLegInformation2. + */ + c->redirecting.count = 1; + } + if (c->redirecting.state == Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3) { + /* + * Valid for Q.SIG and ETSI PRI/BRI-PTP modes: + * Setup the redirecting.to informtion so we can identify + * if the user wants to manually supply the COLR for this + * redirected to number if further redirects could happen. + * + * All the user needs to do is set the REDIRECTING(to-pres) + * to the COLR and REDIRECTING(to-num) = complete-dialed-number + * (i.e. CALLERID(dnid)) to be safe after determining that the + * incoming call was redirected by checking if the + * REDIRECTING(count) is nonzero. + */ + c->redirecting.to.number = c->called.number; + c->redirecting.to.number.presentation = + PRI_PRES_RESTRICTED | PRI_PRES_USER_NUMBER_UNSCREENED; + } + + ctrl->ev.e = PRI_EVENT_RING; + ctrl->ev.ring.subcmds = &ctrl->subcmds; + ctrl->ev.ring.channel = q931_encode_channel(c); + + /* Calling party information */ + ctrl->ev.ring.callingpres = q931_party_id_presentation(&c->remote_id); + ctrl->ev.ring.callingplan = c->remote_id.number.plan; + if (c->remote_id.number.valid + && (c->remote_id.number.presentation == PRES_ALLOWED_NETWORK_NUMBER + || c->remote_id.number.presentation == PRES_PROHIB_NETWORK_NUMBER)) { + ctrl->ev.ring.callingplanani = c->remote_id.number.plan; + libpri_copy_string(ctrl->ev.ring.callingani, c->remote_id.number.str, sizeof(ctrl->ev.ring.callingani)); + } else { + ctrl->ev.ring.callingplanani = -1; + ctrl->ev.ring.callingani[0] = '\0'; + } + libpri_copy_string(ctrl->ev.ring.callingnum, c->remote_id.number.str, sizeof(ctrl->ev.ring.callingnum)); + libpri_copy_string(ctrl->ev.ring.callingname, c->remote_id.name.str, sizeof(ctrl->ev.ring.callingname)); + q931_party_id_copy_to_pri(&ctrl->ev.ring.calling, &c->remote_id); + /* for backwards compatibility, still need ctrl->ev.ring.callingsubaddr */ + if (!c->remote_id.subaddress.type) { /* NSAP: Type = 0 */ + libpri_copy_string(ctrl->ev.ring.callingsubaddr, (char *) c->remote_id.subaddress.data, sizeof(ctrl->ev.ring.callingsubaddr)); + } else { + ctrl->ev.ring.callingsubaddr[0] = '\0'; + } + + ctrl->ev.ring.ani2 = c->ani2; + + /* Called party information */ + ctrl->ev.ring.calledplan = c->called.number.plan; + libpri_copy_string(ctrl->ev.ring.callednum, c->called.number.str, sizeof(ctrl->ev.ring.callednum)); + q931_party_subaddress_copy_to_pri(&ctrl->ev.ring.called_subaddress, &c->called.subaddress); + + /* Original called party information (For backward compatibility) */ + libpri_copy_string(ctrl->ev.ring.origcalledname, c->redirecting.orig_called.name.str, sizeof(ctrl->ev.ring.origcalledname)); + libpri_copy_string(ctrl->ev.ring.origcallednum, c->redirecting.orig_called.number.str, sizeof(ctrl->ev.ring.origcallednum)); + ctrl->ev.ring.callingplanorigcalled = c->redirecting.orig_called.number.plan; + if (c->redirecting.orig_called.number.valid + || c->redirecting.orig_called.name.valid) { + ctrl->ev.ring.origredirectingreason = c->redirecting.orig_reason; + } else { + ctrl->ev.ring.origredirectingreason = -1; + } + + /* Redirecting from party information (For backward compatibility) */ + ctrl->ev.ring.callingplanrdnis = c->redirecting.from.number.plan; + libpri_copy_string(ctrl->ev.ring.redirectingnum, c->redirecting.from.number.str, sizeof(ctrl->ev.ring.redirectingnum)); + libpri_copy_string(ctrl->ev.ring.redirectingname, c->redirecting.from.name.str, sizeof(ctrl->ev.ring.redirectingname)); + + ctrl->ev.ring.redirectingreason = c->redirecting.reason; + + libpri_copy_string(ctrl->ev.ring.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.ring.useruserinfo)); c->useruserinfo[0] = '\0'; - pri->ev.ring.redirectingreason = c->redirectingreason; - pri->ev.ring.origredirectingreason = c->origredirectingreason; - pri->ev.ring.flexible = ! (c->chanflags & FLAG_EXCLUSIVE); - pri->ev.ring.cref = c->cr; - pri->ev.ring.call = c; - pri->ev.ring.layer1 = c->userl1; - pri->ev.ring.complete = c->complete; - pri->ev.ring.ctype = c->transcapability; - pri->ev.ring.redirectingreason = c->redirectingreason; - pri->ev.ring.progress = c->progress; - pri->ev.ring.progressmask = c->progressmask; + + libpri_copy_string(ctrl->ev.ring.keypad_digits, c->keypad_digits, + sizeof(ctrl->ev.ring.keypad_digits)); + + ctrl->ev.ring.flexible = ! (c->chanflags & FLAG_EXCLUSIVE); + ctrl->ev.ring.cref = c->cr; + ctrl->ev.ring.call = c->master_call; + ctrl->ev.ring.layer1 = c->userl1; + ctrl->ev.ring.complete = c->complete; + ctrl->ev.ring.ctype = c->transcapability; + ctrl->ev.ring.progress = c->progress; + ctrl->ev.ring.progressmask = c->progressmask; + ctrl->ev.ring.reversecharge = c->reversecharge; + + if (c->redirecting.count) { + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + /* Setup redirecting subcommand */ + subcmd->cmd = PRI_SUBCMD_REDIRECTING; + q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, + &c->redirecting); + } + } + return Q931_RES_HAVEEVENT; case Q931_ALERTING: + stop_t303(c); if (c->newcall) { - q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_CALL_DELIVERED); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_DELIVERED); c->peercallstate = Q931_CALL_STATE_CALL_RECEIVED; - pri->ev.e = PRI_EVENT_RINGING; - pri->ev.ringing.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.ringing.cref = c->cr; - pri->ev.ringing.call = c; - pri->ev.ringing.progress = c->progress; - pri->ev.ringing.progressmask = c->progressmask; - libpri_copy_string(pri->ev.ringing.useruserinfo, c->useruserinfo, sizeof(pri->ev.ringing.useruserinfo)); + ctrl->ev.e = PRI_EVENT_RINGING; + ctrl->ev.ringing.subcmds = &ctrl->subcmds; + ctrl->ev.ringing.channel = q931_encode_channel(c); + ctrl->ev.ringing.cref = c->cr; + ctrl->ev.ringing.call = c->master_call; + ctrl->ev.ringing.progress = c->progress; + ctrl->ev.ringing.progressmask = c->progressmask; + + libpri_copy_string(ctrl->ev.ringing.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.ringing.useruserinfo)); c->useruserinfo[0] = '\0'; - cur = c->apdus; - while (cur) { + for (cur = c->apdus; cur; cur = cur->next) { if (!cur->sent && cur->message == Q931_FACILITY) { - q931_facility(pri, c); + q931_facility(ctrl, c); break; } - cur = cur->next; } return Q931_RES_HAVEEVENT; case Q931_CONNECT: + stop_t303(c); if (c->newcall) { - q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } if (c->ourcallstate == Q931_CALL_STATE_ACTIVE) { - q931_status(pri, c, PRI_CAUSE_WRONG_MESSAGE); + q931_status(ctrl, c, PRI_CAUSE_WRONG_MESSAGE); break; } - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_ACTIVE); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE); c->peercallstate = Q931_CALL_STATE_CONNECT_REQUEST; - pri->ev.e = PRI_EVENT_ANSWER; - pri->ev.answer.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.answer.cref = c->cr; - pri->ev.answer.call = c; - pri->ev.answer.progress = c->progress; - pri->ev.answer.progressmask = c->progressmask; - libpri_copy_string(pri->ev.answer.useruserinfo, c->useruserinfo, sizeof(pri->ev.answer.useruserinfo)); + + ctrl->ev.e = PRI_EVENT_ANSWER; + ctrl->ev.answer.subcmds = &ctrl->subcmds; + ctrl->ev.answer.channel = q931_encode_channel(c); + ctrl->ev.answer.cref = c->cr; + ctrl->ev.answer.call = c->master_call; + ctrl->ev.answer.progress = c->progress; + ctrl->ev.answer.progressmask = c->progressmask; + libpri_copy_string(ctrl->ev.answer.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.answer.useruserinfo)); c->useruserinfo[0] = '\0'; - q931_connect_acknowledge(pri, c); - if (c->justsignalling) { /* Make sure WE release when we initiatie a signalling only connection */ - q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING); + + q931_connect_acknowledge(ctrl, c); + + if (c->cis_auto_disconnect && c->cis_call) { + /* Make sure WE release when we initiate a signalling only connection */ + q931_hangup(ctrl, c, PRI_CAUSE_NORMAL_CLEARING); break; - } else + } else { + c->incoming_ct_state = INCOMING_CT_STATE_IDLE; + + /* Setup connected line subcommand */ + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; + q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &c->remote_id); + } + return Q931_RES_HAVEEVENT; + } case Q931_FACILITY: if (c->newcall) { - q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } - pri->ev.e = PRI_EVENT_FACNAME; - libpri_copy_string(pri->ev.facname.callingname, c->callername, sizeof(pri->ev.facname.callingname)); - libpri_copy_string(pri->ev.facname.callingnum, c->callernum, sizeof(pri->ev.facname.callingnum)); - pri->ev.facname.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.facname.callingpres = c->callerpres; - pri->ev.facname.callingplan = c->callerplan; - pri->ev.facname.cref = c->cr; - pri->ev.facname.call = c; -#if 0 - pri_message(pri, "Sending facility event (%s/%s)\n", pri->ev.facname.callingname, pri->ev.facname.callingnum); -#endif - return Q931_RES_HAVEEVENT; + switch (c->incoming_ct_state) { + case INCOMING_CT_STATE_POST_CONNECTED_LINE: + c->incoming_ct_state = INCOMING_CT_STATE_IDLE; + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; + q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, &c->remote_id); + } + break; + default: + break; + } + if (ctrl->subcmds.counter_subcmd) { + q931_fill_facility_event(ctrl, c); + return Q931_RES_HAVEEVENT; + } + break; case Q931_PROGRESS: if (missingmand) { - q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING); - q931_destroycall(pri, c->cr); + q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); + pri_destroycall(ctrl, c); break; } - pri->ev.e = PRI_EVENT_PROGRESS; - pri->ev.proceeding.cause = c->cause; + ctrl->ev.e = PRI_EVENT_PROGRESS; + ctrl->ev.proceeding.cause = c->cause; /* Fall through */ case Q931_CALL_PROCEEDING: + stop_t303(c); + ctrl->ev.proceeding.subcmds = &ctrl->subcmds; if (c->newcall) { - q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } if ((c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED) && (c->ourcallstate != Q931_CALL_STATE_OVERLAP_SENDING) && (c->ourcallstate != Q931_CALL_STATE_CALL_DELIVERED) && (c->ourcallstate != Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING)) { - q931_status(pri,c,PRI_CAUSE_WRONG_MESSAGE); + q931_status(ctrl,c,PRI_CAUSE_WRONG_MESSAGE); break; } - pri->ev.proceeding.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); + ctrl->ev.proceeding.channel = q931_encode_channel(c); if (mh->msg == Q931_CALL_PROCEEDING) { - pri->ev.e = PRI_EVENT_PROCEEDING; - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING); + ctrl->ev.e = PRI_EVENT_PROCEEDING; + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING); c->peercallstate = Q931_CALL_STATE_INCOMING_CALL_PROCEEDING; } - pri->ev.proceeding.progress = c->progress; - pri->ev.proceeding.progressmask = c->progressmask; - pri->ev.proceeding.cref = c->cr; - pri->ev.proceeding.call = c; + ctrl->ev.proceeding.progress = c->progress; + ctrl->ev.proceeding.progressmask = c->progressmask; + ctrl->ev.proceeding.cref = c->cr; + ctrl->ev.proceeding.call = c->master_call; - cur = c->apdus; - while (cur) { + for (cur = c->apdus; cur; cur = cur->next) { if (!cur->sent && cur->message == Q931_FACILITY) { - q931_facility(pri, c); + q931_facility(ctrl, c); break; } - cur = cur->next; } return Q931_RES_HAVEEVENT; case Q931_CONNECT_ACKNOWLEDGE: if (c->newcall) { - q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } if (!(c->ourcallstate == Q931_CALL_STATE_CONNECT_REQUEST) && !(c->ourcallstate == Q931_CALL_STATE_ACTIVE && - (pri->localtype == PRI_NETWORK || pri->switchtype == PRI_SWITCH_QSIG))) { - q931_status(pri,c,PRI_CAUSE_WRONG_MESSAGE); + (ctrl->localtype == PRI_NETWORK || ctrl->switchtype == PRI_SWITCH_QSIG))) { + q931_status(ctrl,c,PRI_CAUSE_WRONG_MESSAGE); break; } - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_ACTIVE); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_ACTIVE); c->peercallstate = Q931_CALL_STATE_ACTIVE; break; case Q931_STATUS: if (missingmand) { - q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING); - q931_destroycall(pri, c->cr); + q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING); + pri_destroycall(ctrl, c); break; } if (c->newcall) { if (c->cr & 0x7fff) - q931_release_complete(pri,c,PRI_CAUSE_WRONG_CALL_STATE); + q931_release_complete(ctrl,c,PRI_CAUSE_WRONG_CALL_STATE); break; } /* Do nothing */ /* Also when the STATUS asks for the call of an unexisting reference send RELEASE_COMPL */ - if ((pri->debug & PRI_DEBUG_Q931_ANOMALY) && + if ((ctrl->debug & PRI_DEBUG_Q931_ANOMALY) && (c->cause != PRI_CAUSE_INTERWORKING)) - pri_error(pri, "Received unsolicited status: %s\n", pri_cause2str(c->cause)); + pri_error(ctrl, "Received unsolicited status: %s\n", pri_cause2str(c->cause)); /* Workaround for S-12 ver 7.3 - it responds for invalid/non-implemented IEs at SETUP with null call state */ #if 0 if (!c->sugcallstate && (c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED)) { @@ -3737,25 +6774,28 @@ if (!c->sugcallstate) { #endif - pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.hangup.cause = c->cause; - pri->ev.hangup.cref = c->cr; - pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; - libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); + ctrl->ev.hangup.subcmds = &ctrl->subcmds; + ctrl->ev.hangup.channel = q931_encode_channel(c); + ctrl->ev.hangup.cause = c->cause; + ctrl->ev.hangup.cref = c->cr; + ctrl->ev.hangup.call = c->master_call; + ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; + libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); /* Free resources */ - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; if (c->alive) { - pri->ev.e = PRI_EVENT_HANGUP; + ctrl->ev.e = PRI_EVENT_HANGUP; res = Q931_RES_HAVEEVENT; c->alive = 0; } else if (c->sendhangupack) { res = Q931_RES_HAVEEVENT; - pri->ev.e = PRI_EVENT_HANGUP_ACK; - q931_hangup(pri, c, c->cause); + ctrl->ev.e = PRI_EVENT_HANGUP_ACK; + pri_hangup(ctrl, c, c->cause); } else { - q931_hangup(pri, c, c->cause); + pri_hangup(ctrl, c, c->cause); res = 0; } if (res) @@ -3763,32 +6803,38 @@ } break; case Q931_RELEASE_COMPLETE: - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); + c->hangupinitiated = 1; + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; - pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.hangup.cause = c->cause; - pri->ev.hangup.cref = c->cr; - pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; - libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); + ctrl->ev.hangup.subcmds = &ctrl->subcmds; + ctrl->ev.hangup.channel = q931_encode_channel(c); + ctrl->ev.hangup.cause = c->cause; + ctrl->ev.hangup.cref = c->cr; + ctrl->ev.hangup.call = c->master_call; + ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; + libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; /* Free resources */ if (c->alive) { - pri->ev.e = PRI_EVENT_HANGUP; + ctrl->ev.e = PRI_EVENT_HANGUP; res = Q931_RES_HAVEEVENT; c->alive = 0; } else if (c->sendhangupack) { res = Q931_RES_HAVEEVENT; - pri->ev.e = PRI_EVENT_HANGUP_ACK; - pri_hangup(pri, c, c->cause); + ctrl->ev.e = PRI_EVENT_HANGUP_ACK; + pri_hangup(ctrl, c, c->cause); } else res = 0; + if (res) return res; else - q931_hangup(pri,c,c->cause); + pri_hangup(ctrl,c,c->cause); break; case Q931_RELEASE: + c->hangupinitiated = 1; if (missingmand) { /* Force cause to be mandatory IE missing */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; @@ -3798,59 +6844,113 @@ else { c->peercallstate = Q931_CALL_STATE_RELEASE_REQUEST; } - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); - pri->ev.e = PRI_EVENT_HANGUP; - pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.hangup.cause = c->cause; - pri->ev.hangup.cref = c->cr; - pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; - libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); + ctrl->ev.e = PRI_EVENT_HANGUP; + ctrl->ev.hangup.subcmds = &ctrl->subcmds; + ctrl->ev.hangup.channel = q931_encode_channel(c); + ctrl->ev.hangup.cause = c->cause; + ctrl->ev.hangup.cref = c->cr; + ctrl->ev.hangup.call = c->master_call; + ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; + libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; /* Don't send release complete if they send us release while we sent it, assume a NULL state */ if (c->newcall) - q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); - else + q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + else if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) + return pri_hangup(ctrl, c, -1); + else return Q931_RES_HAVEEVENT; break; case Q931_DISCONNECT: + c->hangupinitiated = 1; if (missingmand) { /* Still let user call release */ c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; } if (c->newcall) { - q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_DISCONNECT_INDICATION); + + /* + * Determine if there are any calls that can be proposed for + * a transfer of held call on disconnect. + */ + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; + switch (c->ourcallstate) { + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + case Q931_CALL_STATE_ACTIVE: + if (c->master_call->hold_state == Q931_HOLD_STATE_CALL_HELD) { + /* Held call is being disconnected first. */ + ctrl->ev.hangup.call_held = c->master_call; + ctrl->ev.hangup.call_active = q931_find_held_active_call(ctrl, c); + } else { + /* Active call is being disconnected first. */ + if (q931_find_winning_call(c) == c) { + /* + * Only a normal call or the winning call of a broadcast SETUP + * can participate in a transfer of held call on disconnet. + */ + ctrl->ev.hangup.call_active = c->master_call; + ctrl->ev.hangup.call_held = q931_find_held_call(ctrl, c); + } + } + break; + default: + break; + } + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + if (ctrl->ev.hangup.call_held) { + pri_message(ctrl, "-- Found held call: %p cref:%d\n", + ctrl->ev.hangup.call_held, ctrl->ev.hangup.call_held->cr); + } + if (ctrl->ev.hangup.call_active) { + pri_message(ctrl, "-- Found active call: %p cref:%d\n", + ctrl->ev.hangup.call_active, ctrl->ev.hangup.call_active->cr); + } + if (ctrl->ev.hangup.call_held && ctrl->ev.hangup.call_active) { + pri_message(ctrl, "-- Transfer held call on disconnect possible.\n"); + } + } + + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_DISCONNECT_INDICATION); c->peercallstate = Q931_CALL_STATE_DISCONNECT_REQUEST; c->sendhangupack = 1; /* wait for a RELEASE so that sufficient time has passed for the inband audio to be heard */ - if (pri->acceptinbanddisconnect && (c->progressmask & PRI_PROG_INBAND_AVAILABLE)) + if (ctrl->acceptinbanddisconnect && (c->progressmask & PRI_PROG_INBAND_AVAILABLE)) break; /* Return such an event */ - pri->ev.e = PRI_EVENT_HANGUP_REQ; - pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.hangup.cause = c->cause; - pri->ev.hangup.cref = c->cr; - pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; - libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); + ctrl->ev.e = PRI_EVENT_HANGUP_REQ; + ctrl->ev.hangup.subcmds = &ctrl->subcmds; + ctrl->ev.hangup.channel = q931_encode_channel(c); + ctrl->ev.hangup.cause = c->cause; + ctrl->ev.hangup.cref = c->cr; + ctrl->ev.hangup.call = c->master_call; + ctrl->ev.hangup.aoc_units = c->aoc_units; + libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; if (c->alive) return Q931_RES_HAVEEVENT; else - q931_hangup(pri,c,c->cause); + pri_hangup(ctrl,c,c->cause); break; case Q931_RESTART_ACKNOWLEDGE: - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; - pri->ev.e = PRI_EVENT_RESTART_ACK; - pri->ev.restartack.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); + ctrl->ev.e = PRI_EVENT_RESTART_ACK; + ctrl->ev.restartack.channel = q931_encode_channel(c); return Q931_RES_HAVEEVENT; case Q931_INFORMATION: /* XXX We're handling only INFORMATION messages that contain @@ -3858,80 +6958,376 @@ + the "Complete" msg which is basically an EOF on further digits XXX */ if (c->newcall) { - q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } if (c->ourcallstate != Q931_CALL_STATE_OVERLAP_RECEIVING) { - pri->ev.e = PRI_EVENT_KEYPAD_DIGIT; - pri->ev.digit.call = c; - pri->ev.digit.channel = c->channelno | (c->ds1no << 8); - libpri_copy_string(pri->ev.digit.digits, c->keypad_digits, sizeof(pri->ev.digit.digits)); - /* Make sure we clear it out before we return */ - c->keypad_digits[0] = '\0'; + ctrl->ev.e = PRI_EVENT_KEYPAD_DIGIT; + ctrl->ev.digit.subcmds = &ctrl->subcmds; + ctrl->ev.digit.call = c->master_call; + ctrl->ev.digit.channel = q931_encode_channel(c); + libpri_copy_string(ctrl->ev.digit.digits, c->keypad_digits, sizeof(ctrl->ev.digit.digits)); return Q931_RES_HAVEEVENT; } - pri->ev.e = PRI_EVENT_INFO_RECEIVED; - pri->ev.ring.call = c; - pri->ev.ring.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - libpri_copy_string(pri->ev.ring.callednum, c->callednum, sizeof(pri->ev.ring.callednum)); - libpri_copy_string(pri->ev.ring.callingsubaddr, c->callingsubaddr, sizeof(pri->ev.ring.callingsubaddr)); - pri->ev.ring.complete = c->complete; /* this covers IE 33 (Sending Complete) */ + ctrl->ev.e = PRI_EVENT_INFO_RECEIVED; + ctrl->ev.ring.subcmds = &ctrl->subcmds; + ctrl->ev.ring.call = c->master_call; + ctrl->ev.ring.channel = q931_encode_channel(c); + libpri_copy_string(ctrl->ev.ring.callednum, c->overlap_digits, sizeof(ctrl->ev.ring.callednum)); + + q931_party_id_copy_to_pri(&ctrl->ev.ring.calling, &c->remote_id); + /* for backwards compatibility, still need ctrl->ev.ring.callingsubaddr */ + if (!c->remote_id.subaddress.type) { /* NSAP: Type = 0 */ + libpri_copy_string(ctrl->ev.ring.callingsubaddr, (char *) c->remote_id.subaddress.data, sizeof(ctrl->ev.ring.callingsubaddr)); + } else { + ctrl->ev.ring.callingsubaddr[0] = '\0'; + } + + ctrl->ev.ring.complete = c->complete; /* this covers IE 33 (Sending Complete) */ return Q931_RES_HAVEEVENT; case Q931_STATUS_ENQUIRY: if (c->newcall) { - q931_release_complete(pri, c, PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, PRI_CAUSE_INVALID_CALL_REFERENCE); } else - q931_status(pri,c, 0); + q931_status(ctrl,c, 0); break; case Q931_SETUP_ACKNOWLEDGE: + stop_t303(c); if (c->newcall) { - q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); break; } - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_OVERLAP_SENDING); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OVERLAP_SENDING); c->peercallstate = Q931_CALL_STATE_OVERLAP_RECEIVING; - pri->ev.e = PRI_EVENT_SETUP_ACK; - pri->ev.setup_ack.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.setup_ack.call = c; + ctrl->ev.e = PRI_EVENT_SETUP_ACK; + ctrl->ev.setup_ack.subcmds = &ctrl->subcmds; + ctrl->ev.setup_ack.channel = q931_encode_channel(c); + ctrl->ev.setup_ack.call = c->master_call; - cur = c->apdus; - while (cur) { + for (cur = c->apdus; cur; cur = cur->next) { if (!cur->sent && cur->message == Q931_FACILITY) { - q931_facility(pri, c); + q931_facility(ctrl, c); break; } - cur = cur->next; } return Q931_RES_HAVEEVENT; case Q931_NOTIFY: - pri->ev.e = PRI_EVENT_NOTIFY; - pri->ev.notify.channel = c->channelno; - pri->ev.notify.info = c->notify; - return Q931_RES_HAVEEVENT; - case Q931_USER_INFORMATION: - case Q931_SEGMENT: - case Q931_CONGESTION_CONTROL: + res = 0; + switch (c->notify) { + case PRI_NOTIFY_CALL_DIVERTING: + if (c->redirection_number.valid) { + c->redirecting.to.number = c->redirection_number; + if (c->redirecting.count < PRI_MAX_REDIRECTS) { + ++c->redirecting.count; + } + switch (c->ourcallstate) { + case Q931_CALL_STATE_CALL_DELIVERED: + /* Call is deflecting after we have seen an ALERTING message */ + c->redirecting.reason = PRI_REDIR_FORWARD_ON_NO_REPLY; + break; + default: + /* Call is deflecting for call forwarding unconditional or busy reason. */ + c->redirecting.reason = PRI_REDIR_UNKNOWN; + break; + } + + /* Setup redirecting subcommand */ + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + subcmd->cmd = PRI_SUBCMD_REDIRECTING; + q931_party_redirecting_copy_to_pri(&subcmd->u.redirecting, + &c->redirecting); + } + } + + if (ctrl->subcmds.counter_subcmd) { + q931_fill_facility_event(ctrl, c); + res = Q931_RES_HAVEEVENT; + } + break; + case PRI_NOTIFY_TRANSFER_ALERTING: + case PRI_NOTIFY_TRANSFER_ACTIVE: + if (c->redirection_number.valid + && q931_party_number_cmp(&c->remote_id.number, &c->redirection_number)) { + /* The remote party information changed. */ + c->remote_id.number = c->redirection_number; + + /* Setup connected line subcommand */ + subcmd = q931_alloc_subcommand(ctrl); + if (subcmd) { + subcmd->cmd = PRI_SUBCMD_CONNECTED_LINE; + q931_party_id_copy_to_pri(&subcmd->u.connected_line.id, + &c->remote_id); + } + } + + if (ctrl->subcmds.counter_subcmd) { + q931_fill_facility_event(ctrl, c); + res = Q931_RES_HAVEEVENT; + } + break; + default: + ctrl->ev.e = PRI_EVENT_NOTIFY; + ctrl->ev.notify.subcmds = &ctrl->subcmds; + ctrl->ev.notify.channel = q931_encode_channel(c); + ctrl->ev.notify.info = c->notify; + ctrl->ev.notify.call = c->master_call; + res = Q931_RES_HAVEEVENT; + break; + } + return res; case Q931_HOLD: + res = 0; + if (!PRI_MASTER(ctrl)->hold_support) { + /* + * Blocking any calls from getting on HOLD effectively + * disables HOLD/RETRIEVE. + */ + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_FACILITY_NOT_IMPLEMENTED); + break; + } + switch (c->ourcallstate) { + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + if (q931_is_ptmp(ctrl)) { + /* HOLD request only allowed in these states if point-to-point mode. */ + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + /* Fall through */ + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + if (!q931_find_winning_call(c)) { + /* + * Only the winning call of a broadcast SETUP can do hold since the + * call must be answered first. + */ + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_HOLD_REQ: + if (ctrl->localtype == PRI_NETWORK) { + /* The network ignores HOLD request on a hold collision. */ + break; + } + /* Fall through */ + case Q931_HOLD_STATE_IDLE: + ctrl->ev.e = PRI_EVENT_HOLD; + ctrl->ev.hold.channel = q931_encode_channel(c); + ctrl->ev.hold.call = master_call; + ctrl->ev.hold.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_HOLD_IND); + + /* Stop any T-HOLD timer from possible hold collision. */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + break; + default: + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + break; + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_RELEASE_REQUEST: + /* Ignore HOLD request in these states. */ + break; + default: + q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + return res; case Q931_HOLD_ACKNOWLEDGE: + res = 0; + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_HOLD_REQ: + ctrl->ev.e = PRI_EVENT_HOLD_ACK; + ctrl->ev.hold_ack.channel = q931_encode_channel(c); + ctrl->ev.hold_ack.call = master_call; + ctrl->ev.hold_ack.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_CALL_HELD); + + /* Call is now on hold so forget the channel. */ + c->channelno = 0;/* No channel */ + c->ds1no = 0; + c->ds1explicit = 0; + c->chanflags = 0; + + /* Stop T-HOLD timer */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + break; + default: + /* Ignore response. Response is late or spurrious. */ + break; + } + return res; case Q931_HOLD_REJECT: + res = 0; + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_HOLD_REQ: + if (missingmand) { + /* Still, let hold rejection continue. */ + c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; + } + ctrl->ev.e = PRI_EVENT_HOLD_REJ; + ctrl->ev.hold_rej.channel = q931_encode_channel(c); + ctrl->ev.hold_rej.call = master_call; + ctrl->ev.hold_rej.cause = c->cause; + ctrl->ev.hold_rej.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_IDLE); + + /* Stop T-HOLD timer */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + break; + default: + /* Ignore response. Response is late or spurrious. */ + break; + } + return res; case Q931_RETRIEVE: + res = 0; + switch (c->ourcallstate) { + case Q931_CALL_STATE_CALL_RECEIVED: + case Q931_CALL_STATE_CONNECT_REQUEST: + case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING: + if (q931_is_ptmp(ctrl)) { + /* RETRIEVE request only allowed in these states if point-to-point mode. */ + q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + /* Fall through */ + case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING: + case Q931_CALL_STATE_CALL_DELIVERED: + case Q931_CALL_STATE_ACTIVE: + if (!q931_find_winning_call(c)) { + /* + * Only the winning call of a broadcast SETUP can do hold since the + * call must be answered first. + */ + q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_RETRIEVE_REQ: + if (ctrl->localtype == PRI_NETWORK) { + /* The network ignores RETRIEVE request on a retrieve collision. */ + break; + } + /* Fall through */ + case Q931_HOLD_STATE_CALL_HELD: + ctrl->ev.e = PRI_EVENT_RETRIEVE; + ctrl->ev.retrieve.channel = q931_encode_channel(c); + ctrl->ev.retrieve.call = master_call; + ctrl->ev.retrieve.flexible = !(c->chanflags & FLAG_EXCLUSIVE); + ctrl->ev.retrieve.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_RETRIEVE_IND); + + /* Stop any T-RETRIEVE timer from possible retrieve collision. */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + break; + default: + q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + break; + case Q931_CALL_STATE_DISCONNECT_INDICATION: + case Q931_CALL_STATE_RELEASE_REQUEST: + /* Ignore RETRIEVE request in these states. */ + break; + default: + q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); + break; + } + return res; case Q931_RETRIEVE_ACKNOWLEDGE: + res = 0; + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_RETRIEVE_REQ: + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_IDLE); + + /* Stop T-RETRIEVE timer */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + + ctrl->ev.e = PRI_EVENT_RETRIEVE_ACK; + ctrl->ev.retrieve_ack.channel = q931_encode_channel(c); + ctrl->ev.retrieve_ack.call = master_call; + ctrl->ev.retrieve_ack.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + break; + default: + /* Ignore response. Response is late or spurrious. */ + break; + } + return res; case Q931_RETRIEVE_REJECT: + res = 0; + master_call = c->master_call; + switch (master_call->hold_state) { + case Q931_HOLD_STATE_RETRIEVE_REQ: + UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_CALL_HELD); + + /* Call is still on hold so forget the channel. */ + c->channelno = 0;/* No channel */ + c->ds1no = 0; + c->ds1explicit = 0; + c->chanflags = 0; + + /* Stop T-RETRIEVE timer */ + pri_schedule_del(ctrl, master_call->hold_timer); + master_call->hold_timer = 0; + + if (missingmand) { + /* Still, let retrive rejection continue. */ + c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; + } + ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ; + ctrl->ev.retrieve_rej.channel = q931_encode_channel(c); + ctrl->ev.retrieve_rej.call = master_call; + ctrl->ev.retrieve_rej.cause = c->cause; + ctrl->ev.retrieve_rej.subcmds = &ctrl->subcmds; + res = Q931_RES_HAVEEVENT; + break; + default: + /* Ignore response. Response is late or spurrious. */ + break; + } + return res; + case Q931_USER_INFORMATION: + case Q931_SEGMENT: + case Q931_CONGESTION_CONTROL: case Q931_RESUME: case Q931_RESUME_ACKNOWLEDGE: case Q931_RESUME_REJECT: case Q931_SUSPEND: case Q931_SUSPEND_ACKNOWLEDGE: case Q931_SUSPEND_REJECT: - pri_error(pri, "!! Not yet handling post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); + pri_error(ctrl, "!! Not yet handling post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); /* Fall through */ default: - - pri_error(pri, "!! Don't know how to post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); - q931_status(pri,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); + pri_error(ctrl, "!! Don't know how to post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg); + q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); if (c->newcall) - q931_destroycall(pri,c->cr); + pri_destroycall(ctrl, c); return -1; } return 0; @@ -3941,40 +7337,46 @@ static int pri_internal_clear(void *data) { struct q931_call *c = data; - struct pri *pri = c->pri; + struct pri *ctrl = c->pri; int res; - if (c->retranstimer) - pri_schedule_del(pri, c->retranstimer); + pri_schedule_del(ctrl, c->retranstimer); c->retranstimer = 0; c->useruserinfo[0] = '\0'; - c->cause = -1; + //c->cause = -1; c->causecode = -1; c->causeloc = -1; - c->sugcallstate = -1; + c->sugcallstate = Q931_CALL_STATE_NOT_SET; c->aoc_units = -1; - UPDATE_OURCALLSTATE(pri, c, Q931_CALL_STATE_NULL); + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; - pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16); - pri->ev.hangup.cause = c->cause; - pri->ev.hangup.cref = c->cr; - pri->ev.hangup.call = c; - pri->ev.hangup.aoc_units = c->aoc_units; - libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.hangup.useruserinfo)); + q931_clr_subcommands(ctrl); + ctrl->ev.hangup.subcmds = &ctrl->subcmds; + ctrl->ev.hangup.channel = q931_encode_channel(c); + ctrl->ev.hangup.cause = c->cause; + ctrl->ev.hangup.cref = c->cr; + ctrl->ev.hangup.call = c->master_call; + ctrl->ev.hangup.aoc_units = c->aoc_units; + ctrl->ev.hangup.call_held = NULL; + ctrl->ev.hangup.call_active = NULL; + libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "clearing, alive %d, hangupack %d\n", c->alive, c->sendhangupack); + } /* Free resources */ if (c->alive) { - pri->ev.e = PRI_EVENT_HANGUP; + ctrl->ev.e = PRI_EVENT_HANGUP; res = Q931_RES_HAVEEVENT; c->alive = 0; } else if (c->sendhangupack) { res = Q931_RES_HAVEEVENT; - pri->ev.e = PRI_EVENT_HANGUP_ACK; - q931_hangup(pri, c, c->cause); + ctrl->ev.e = PRI_EVENT_HANGUP_ACK; + pri_hangup(ctrl, c, c->cause); } else { res = 0; - q931_hangup(pri, c, c->cause); + pri_hangup(ctrl, c, c->cause); } return res; @@ -3984,94 +7386,97 @@ static void pri_dl_down_timeout(void *data) { struct q931_call *c = data; - struct pri *pri = c->pri; - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, DBGHEAD "Timed out waiting for data link re-establishment\n", DBGINFO); + struct pri *ctrl = c->pri; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, DBGHEAD "Timed out waiting for data link re-establishment\n", DBGINFO); c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER; if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) - pri->schedev = 1; + ctrl->schedev = 1; } /* Handle Layer 2 down event for a non active call. */ static void pri_dl_down_cancelcall(void *data) { struct q931_call *c = data; - struct pri *pri = c->pri; - if (pri->debug & PRI_DEBUG_Q931_STATE) - pri_message(pri, DBGHEAD "Cancel non active call after data link failure\n", DBGINFO); + struct pri *ctrl = c->pri; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) + pri_message(ctrl, DBGHEAD "Cancel non active call after data link failure\n", DBGINFO); c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER; if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) - pri->schedev = 1; + ctrl->schedev = 1; } /* Receive an indication from Layer 2 */ -void q931_dl_indication(struct pri *pri, int event) +void q931_dl_indication(struct pri *ctrl, int event) { q931_call *cur = NULL; /* Just return if T309 is not enabled. */ - if (!pri || pri->timers[PRI_TIMER_T309] < 0) + if (!ctrl || ctrl->timers[PRI_TIMER_T309] < 0) return; switch (event) { case PRI_EVENT_DCHAN_DOWN: - pri_message(pri, DBGHEAD "link is DOWN\n", DBGINFO); - cur = *pri->callpool; + pri_message(ctrl, DBGHEAD "link is DOWN\n", DBGINFO); + cur = *ctrl->callpool; while(cur) { if (cur->ourcallstate == Q931_CALL_STATE_ACTIVE) { /* For a call in Active state, activate T309 only if there is no timer already running. */ if (!cur->retranstimer) { - pri_message(pri, DBGHEAD "activate T309 for call %d on channel %d\n", DBGINFO, cur->cr, cur->channelno); - cur->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T309], pri_dl_down_timeout, cur); + pri_message(ctrl, DBGHEAD "activate T309 for call %d on channel %d\n", DBGINFO, cur->cr, cur->channelno); + cur->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T309], pri_dl_down_timeout, cur); } } else if (cur->ourcallstate != Q931_CALL_STATE_NULL) { /* For a call that is not in Active state, schedule internal clearing of the call 'ASAP' (delay 0). */ - pri_message(pri, DBGHEAD "cancel call %d on channel %d in state %d (%s)\n", DBGINFO, - cur->cr, cur->channelno, cur->ourcallstate, callstate2str(cur->ourcallstate)); - if (cur->retranstimer) - pri_schedule_del(pri, cur->retranstimer); - cur->retranstimer = pri_schedule_event(pri, 0, pri_dl_down_cancelcall, cur); + pri_message(ctrl, DBGHEAD "cancel call %d on channel %d in state %d (%s)\n", DBGINFO, + cur->cr, cur->channelno, cur->ourcallstate, + q931_call_state_str(cur->ourcallstate)); + pri_schedule_del(ctrl, cur->retranstimer); + cur->retranstimer = pri_schedule_event(ctrl, 0, pri_dl_down_cancelcall, cur); } cur = cur->next; } break; case PRI_EVENT_DCHAN_UP: - pri_message(pri, DBGHEAD "link is UP\n", DBGINFO); - cur = *pri->callpool; + pri_message(ctrl, DBGHEAD "link is UP\n", DBGINFO); + cur = *ctrl->callpool; while(cur) { if (cur->ourcallstate == Q931_CALL_STATE_ACTIVE && cur->retranstimer) { - pri_message(pri, DBGHEAD "cancel T309 for call %d on channel %d\n", DBGINFO, cur->cr, cur->channelno); - pri_schedule_del(pri, cur->retranstimer); + pri_message(ctrl, DBGHEAD "cancel T309 for call %d on channel %d\n", DBGINFO, cur->cr, cur->channelno); + pri_schedule_del(ctrl, cur->retranstimer); cur->retranstimer = 0; - q931_status(pri, cur, PRI_CAUSE_NORMAL_UNSPECIFIED); + q931_status(ctrl, cur, PRI_CAUSE_NORMAL_UNSPECIFIED); } else if (cur->ourcallstate != Q931_CALL_STATE_NULL && cur->ourcallstate != Q931_CALL_STATE_DISCONNECT_REQUEST && cur->ourcallstate != Q931_CALL_STATE_DISCONNECT_INDICATION && cur->ourcallstate != Q931_CALL_STATE_RELEASE_REQUEST) { /* The STATUS message sent here is not required by Q.931, but it may help anyway. */ - q931_status(pri, cur, PRI_CAUSE_NORMAL_UNSPECIFIED); + q931_status(ctrl, cur, PRI_CAUSE_NORMAL_UNSPECIFIED); } cur = cur->next; } break; default: - pri_message(pri, DBGHEAD "unexpected event %d.\n", DBGINFO, event); + pri_message(ctrl, DBGHEAD "unexpected event %d.\n", DBGINFO, event); } } -int q931_call_getcrv(struct pri *pri, q931_call *call, int *callmode) +int q931_call_getcrv(struct pri *ctrl, q931_call *call, int *callmode) { if (callmode) *callmode = call->cr & 0x7; return ((call->cr & 0x7fff) >> 3); } -int q931_call_setcrv(struct pri *pri, q931_call *call, int crv, int callmode) +int q931_call_setcrv(struct pri *ctrl, q931_call *call, int crv, int callmode) { - call->cr = (crv << 3) & 0x7fff; - call->cr |= (callmode & 0x7); + /* Do not allow changing the dummy call reference */ + if (!q931_is_dummy_call(call)) { + call->cr = (crv << 3) & 0x7fff; + call->cr |= (callmode & 0x7); + } return 0; } Index: asn1.h =================================================================== --- a/asn1.h (.../tags/1.4.10.2) (revision 0) +++ b/asn1.h (.../branches/1.4) (revision 1357) @@ -0,0 +1,257 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 ASN.1 definitions and prototypes + * + * \details + * This file contains all ASN.1 primitive data structures and + * definitions needed for ROSE component encoding and decoding. + * + * ROSE - Remote Operations Service Element + * ASN.1 - Abstract Syntax Notation 1 + * APDU - Application Protocol Data Unit + * + * \author Richard Mudgett + */ + +#ifndef _LIBPRI_ASN1_H +#define _LIBPRI_ASN1_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------------------------------------------------------- */ + +/*! ASN.1 Identifier Octet - Tag class bits */ +#define ASN1_CLASS_MASK 0xc0 +#define ASN1_CLASS_UNIVERSAL 0x00 /*!< Universal primitive data types */ +#define ASN1_CLASS_APPLICATION 0x40 /*!< Application wide data tag */ +#define ASN1_CLASS_CONTEXT_SPECIFIC 0x80 /*!< Context specifc data tag */ +#define ASN1_CLASS_PRIVATE 0xc0 /*!< Private organization data tag */ + +/*! ASN.1 Identifier Octet - Primitive/Constructor bit */ +#define ASN1_PC_MASK 0x20 +#define ASN1_PC_PRIMITIVE 0x00 +#define ASN1_PC_CONSTRUCTED 0x20 + +/*! ASN.1 Identifier Octet - Universal data types */ +#define ASN1_TYPE_MASK 0x1f +#define ASN1_TYPE_INDEF_TERM 0x00 /* 0 */ +#define ASN1_TYPE_BOOLEAN 0x01 /* 1 */ +#define ASN1_TYPE_INTEGER 0x02 /* 2 */ +#define ASN1_TYPE_BIT_STRING 0x03 /* 3 */ +#define ASN1_TYPE_OCTET_STRING 0x04 /* 4 */ +#define ASN1_TYPE_NULL 0x05 /* 5 */ +#define ASN1_TYPE_OBJECT_IDENTIFIER 0x06 /* 6 */ +#define ASN1_TYPE_OBJECT_DESCRIPTOR 0x07 /* 7 */ +#define ASN1_TYPE_EXTERN 0x08 /* 8 */ +#define ASN1_TYPE_REAL 0x09 /* 9 */ +#define ASN1_TYPE_ENUMERATED 0x0a /* 10 */ +#define ASN1_TYPE_EMBEDDED_PDV 0x0b /* 11 */ +#define ASN1_TYPE_UTF8_STRING 0x0c /* 12 */ +#define ASN1_TYPE_RELATIVE_OID 0x0d /* 13 */ +/* 0x0e & 0x0f are reserved for future ASN.1 editions */ +#define ASN1_TYPE_SEQUENCE 0x10 /* 16 */ +#define ASN1_TYPE_SET 0x11 /* 17 */ +#define ASN1_TYPE_NUMERIC_STRING 0x12 /* 18 */ +#define ASN1_TYPE_PRINTABLE_STRING 0x13 /* 19 */ +#define ASN1_TYPE_TELETEX_STRING 0x14 /* 20 */ +#define ASN1_TYPE_VIDEOTEX_STRING 0x15 /* 21 */ +#define ASN1_TYPE_IA5_STRING 0x16 /* 22 */ +#define ASN1_TYPE_UTC_TIME 0x17 /* 23 */ +#define ASN1_TYPE_GENERALIZED_TIME 0x18 /* 24 */ +#define ASN1_TYPE_GRAPHIC_STRING 0x19 /* 25 */ +#define ASN1_TYPE_VISIBLE_STRING 0x1a /* 26 */ +#define ASN1_TYPE_ISO646_STRING 0x1a /* 26 */ +#define ASN1_TYPE_GENERAL_STRING 0x1b /* 27 */ +#define ASN1_TYPE_UNIVERSAL_STRING 0x1c /* 28 */ +#define ASN1_TYPE_CHAR_STRING 0x1d /* 29 */ +#define ASN1_TYPE_BMP_STRING 0x1e /* 30 */ +#define ASN1_TYPE_EXTENSION 0x1f /* 31 */ + +#define ASN1_TAG_SEQUENCE (ASN1_CLASS_UNIVERSAL | ASN1_PC_CONSTRUCTED | ASN1_TYPE_SEQUENCE) +#define ASN1_TAG_SET (ASN1_CLASS_UNIVERSAL | ASN1_PC_CONSTRUCTED | ASN1_TYPE_SET) + +#define ASN1_INDEF_TERM (ASN1_CLASS_UNIVERSAL | ASN1_PC_PRIMITIVE | ASN1_TYPE_INDEF_TERM) +#define ASN1_INDEF_TERM_LEN 2 + +struct asn1_oid { + /*! \brief Number of subidentifier values in OID list */ + u_int16_t num_values; + + /*! + * \brief OID subidentifier value list + * \note The first value is really the first two OID subidentifiers. + * They are compressed using this formula: + * First_Value = (First_Subidentifier * 40) + Second_Subidentifier + */ + u_int16_t value[10]; +}; + +#define ASN1_CALL(new_pos, do_it) \ + do \ + { \ + (new_pos) = (do_it); \ + if (!(new_pos)) { \ + return NULL; \ + } \ + } while (0) + +/*! \brief Determine the ending position of the set or sequence to verify the length. */ +#define ASN1_END_SETUP(component_end, offset, length, pos, end) \ + do { \ + if ((length) < 0) { \ + (offset) = ASN1_INDEF_TERM_LEN; \ + (component_end) = (end); \ + } else { \ + (offset) = 0; \ + (component_end) = (pos) + (length); \ + } \ + } while (0) + +/*! \brief Account for the indefinite length terminator of the set or sequence. */ +#define ASN1_END_FIXUP(ctrl, pos, offset, component_end, end) \ + do { \ + if (offset) { \ + ASN1_CALL((pos), asn1_dec_indef_end_fixup((ctrl), (pos), (end))); \ + } else if ((pos) != (component_end)) { \ + if ((ctrl)->debug & PRI_DEBUG_APDU) { \ + pri_message((ctrl), \ + " Skipping unused constructed component octets!\n"); \ + } \ + (pos) = (component_end); \ + } \ + } while (0) + +#define ASN1_DID_NOT_EXPECT_TAG(ctrl, tag) \ + do { \ + if ((ctrl)->debug & PRI_DEBUG_APDU) { \ + pri_message((ctrl), " Did not expect: %s\n", asn1_tag2str(tag)); \ + } \ + } while (0) + +#define ASN1_CHECK_TAG(ctrl, actual_tag, match_tag, expected_tag) \ + do { \ + if ((match_tag) != (expected_tag)) { \ + ASN1_DID_NOT_EXPECT_TAG((ctrl), (actual_tag)); \ + return NULL; \ + } \ + } while (0) + + +const unsigned char *asn1_dec_tag(const unsigned char *tag_pos, const unsigned char *end, + unsigned *tag); +const unsigned char *asn1_dec_length(const unsigned char *len_pos, + const unsigned char *end, int *length); +const unsigned char *asn1_dec_indef_end_fixup(struct pri *ctrl, const unsigned char *pos, + const unsigned char *end); + +const unsigned char *asn1_dec_boolean(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, int32_t *value); +const unsigned char *asn1_dec_int(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, int32_t *value); +const unsigned char *asn1_dec_null(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end); +const unsigned char *asn1_dec_oid(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct asn1_oid *oid); +const unsigned char *asn1_dec_string_bin(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, size_t buf_size, + unsigned char *str, size_t *str_len); +const unsigned char *asn1_dec_string_max(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, size_t buf_size, + unsigned char *str, size_t *str_len); + +const char *asn1_tag2str(unsigned tag); +void asn1_dump(struct pri *ctrl, const unsigned char *start_asn1, + const unsigned char *end); + + +#define ASN1_LEN_FORM_SHORT 1 /*!< Hint that the final length will be less than 128 octets */ +#define ASN1_LEN_FORM_LONG_U8 2 /*!< Hint that the final length will be less than 256 octets */ +#define ASN1_LEN_FORM_LONG_U16 3 /*!< Hint that the final length will be less than 65536 octets */ +#define ASN1_LEN_INIT(len_pos, end, form_hint) \ + do { \ + if ((end) < (len_pos) + (form_hint)) { \ + return NULL; \ + } \ + *(len_pos) = (form_hint); \ + (len_pos) += (form_hint); \ + } while (0) + +#define ASN1_LEN_FIXUP(len_pos, component_end, end) \ + ASN1_CALL((component_end), asn1_enc_length_fixup((len_pos), (component_end), (end))) + +/*! \brief Use to begin encoding explicit tags, SET, and SEQUENCE constructed groupings. */ +#define ASN1_CONSTRUCTED_BEGIN(len_pos_save, pos, end, tag) \ + do { \ + if ((end) < (pos) + (1 + ASN1_LEN_FORM_SHORT)) { \ + return NULL; \ + } \ + *(pos)++ = (tag) | ASN1_PC_CONSTRUCTED; \ + (len_pos_save) = (pos); \ + *(pos) = ASN1_LEN_FORM_SHORT; \ + (pos) += ASN1_LEN_FORM_SHORT; \ + } while (0) + +/*! \brief Use to end encoding explicit tags, SET, and SEQUENCE constructed groupings. */ +#define ASN1_CONSTRUCTED_END(len_pos, component_end, end) \ + ASN1_CALL((component_end), asn1_enc_length_fixup((len_pos), (component_end), (end))) + +#define ASN1_ENC_ERROR(ctrl, msg) \ + pri_error((ctrl), "%s error: %s\n", __FUNCTION__, (msg)) + +unsigned char *asn1_enc_length(unsigned char *len_pos, unsigned char *end, + size_t str_len); +unsigned char *asn1_enc_length_fixup(unsigned char *len_pos, + unsigned char *component_end, unsigned char *end); + +unsigned char *asn1_enc_boolean(unsigned char *pos, unsigned char *end, unsigned tag, + int32_t value); +unsigned char *asn1_enc_int(unsigned char *pos, unsigned char *end, unsigned tag, + int32_t value); +unsigned char *asn1_enc_null(unsigned char *pos, unsigned char *end, unsigned tag); +unsigned char *asn1_enc_oid(unsigned char *pos, unsigned char *end, unsigned tag, + const struct asn1_oid *oid); +unsigned char *asn1_enc_string_bin(unsigned char *pos, unsigned char *end, unsigned tag, + const unsigned char *str, size_t str_len); +unsigned char *asn1_enc_string_max(unsigned char *pos, unsigned char *end, unsigned tag, + const unsigned char *str, size_t max_len); + +/* ------------------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBPRI_ASN1_H */ +/* ------------------------------------------------------------------- */ +/* end asn1.h */ Property changes on: asn1.h ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose.c =================================================================== --- a/rose.c (.../tags/1.4.10.2) (revision 0) +++ b/rose.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,2464 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 + */ + + +#include + +#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 Explicit Call Transfer OID prefix. */ +static const struct asn1_oid rose_etsi_ect = { +/* *INDENT-OFF* */ + /* {ccitt(0) identified-organization(4) etsi(0) 369 operations-and-errors(1)} */ + 4, { 4, 0, 369, 1 } +/* *INDENT-ON* */ +}; + +/*! \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 Diversion-Operations + * {ccitt identified-organization etsi(0) 207 operations-and-errors(1)} + */ + { + ROSE_ETSI_ActivationDiversion, NULL, 7, + rose_enc_etsi_ActivationDiversion_ARG, NULL, + rose_dec_etsi_ActivationDiversion_ARG, NULL + }, + { + ROSE_ETSI_DeactivationDiversion, NULL, 8, + rose_enc_etsi_DeactivationDiversion_ARG,NULL, + rose_dec_etsi_DeactivationDiversion_ARG,NULL + }, + { + ROSE_ETSI_ActivationStatusNotificationDiv, NULL, 9, + rose_enc_etsi_ActivationStatusNotificationDiv_ARG,NULL, + rose_dec_etsi_ActivationStatusNotificationDiv_ARG,NULL + }, + { + ROSE_ETSI_DeactivationStatusNotificationDiv,NULL, 10, + rose_enc_etsi_DeactivationStatusNotificationDiv_ARG,NULL, + rose_dec_etsi_DeactivationStatusNotificationDiv_ARG,NULL + }, + { + ROSE_ETSI_InterrogationDiversion, NULL, 11, + rose_enc_etsi_InterrogationDiversion_ARG,rose_enc_etsi_InterrogationDiversion_RES, + rose_dec_etsi_InterrogationDiversion_ARG,rose_dec_etsi_InterrogationDiversion_RES + }, + { + ROSE_ETSI_DiversionInformation, NULL, 12, + rose_enc_etsi_DiversionInformation_ARG, NULL, + rose_dec_etsi_DiversionInformation_ARG, NULL + }, + { + ROSE_ETSI_CallDeflection, NULL, 13, + rose_enc_etsi_CallDeflection_ARG, NULL, + rose_dec_etsi_CallDeflection_ARG, NULL + }, + { + ROSE_ETSI_CallRerouting, NULL, 14, + rose_enc_etsi_CallRerouting_ARG, NULL, + rose_dec_etsi_CallRerouting_ARG, NULL + }, + { + ROSE_ETSI_DivertingLegInformation2, NULL, 15, + rose_enc_etsi_DivertingLegInformation2_ARG,NULL, + rose_dec_etsi_DivertingLegInformation2_ARG,NULL + }, + { + ROSE_ETSI_InterrogateServedUserNumbers, NULL, 17, + NULL, rose_enc_etsi_InterrogateServedUserNumbers_RES, + NULL, rose_dec_etsi_InterrogateServedUserNumbers_RES + }, + { + ROSE_ETSI_DivertingLegInformation1, NULL, 18, + rose_enc_etsi_DivertingLegInformation1_ARG,NULL, + rose_dec_etsi_DivertingLegInformation1_ARG,NULL + }, + { + ROSE_ETSI_DivertingLegInformation3, NULL, 19, + rose_enc_etsi_DivertingLegInformation3_ARG,NULL, + rose_dec_etsi_DivertingLegInformation3_ARG,NULL + }, + + /* + * 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 + }, + + /* + * localValue's from Explicit-Call-Transfer-Operations-and-Errors + * {ccitt identified-organization etsi(0) 369 operations-and-errors(1)} + */ + { + ROSE_ETSI_EctExecute, NULL, 6, + NULL, NULL, + NULL, NULL + }, + + /* + * globalValue's (OIDs) from Explicit-Call-Transfer-Operations-and-Errors + * {ccitt identified-organization etsi(0) 369 operations-and-errors(1)} + */ + { + ROSE_ETSI_ExplicitEctExecute, &rose_etsi_ect, 1, + rose_enc_etsi_ExplicitEctExecute_ARG, NULL, + rose_dec_etsi_ExplicitEctExecute_ARG, NULL + }, + { + ROSE_ETSI_RequestSubaddress, &rose_etsi_ect, 2, + NULL, NULL, + NULL, NULL + }, + { + ROSE_ETSI_SubaddressTransfer, &rose_etsi_ect, 3, + rose_enc_etsi_SubaddressTransfer_ARG, NULL, + rose_dec_etsi_SubaddressTransfer_ARG, NULL + }, + { + ROSE_ETSI_EctLinkIdRequest, &rose_etsi_ect, 4, + NULL, rose_enc_etsi_EctLinkIdRequest_RES, + NULL, rose_dec_etsi_EctLinkIdRequest_RES + }, + { + ROSE_ETSI_EctInform, &rose_etsi_ect, 5, + rose_enc_etsi_EctInform_ARG, NULL, + rose_dec_etsi_EctInform_ARG, NULL + }, + { + ROSE_ETSI_EctLoopTest, &rose_etsi_ect, 6, + rose_enc_etsi_EctLoopTest_ARG, rose_enc_etsi_EctLoopTest_RES, + rose_dec_etsi_EctLoopTest_ARG, rose_dec_etsi_EctLoopTest_RES + }, +/* *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 Diversion-Operations + * {ccitt identified-organization etsi(0) 207 operations-and-errors(1)} + */ + { + 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_IncomingCallAccepted, NULL, 23, + NULL, NULL + }, + { + ROSE_ERROR_Div_NumberOfDiversionsExceeded, NULL, 24, + NULL, NULL + }, + { + ROSE_ERROR_Div_NotActivated, NULL, 46, + NULL, NULL + }, + { + ROSE_ERROR_Div_RequestAlreadyAccepted, NULL, 48, + 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 + }, + + /* + * globalValue Errors (OIDs) from Explicit-Call-Transfer-Operations-and-Errors + * {ccitt identified-organization etsi(0) 369 operations-and-errors(1)} + */ + { + ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, &rose_etsi_ect, 21, + 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 SS-AOC-Operations + * { iso(1) standard(0) pss1-advice-of-charge(15050) advice-of-charge-operations(0) } + */ + { + ROSE_QSIG_ChargeRequest, NULL, 59, + rose_enc_qsig_ChargeRequest_ARG, rose_enc_qsig_ChargeRequest_RES, + rose_dec_qsig_ChargeRequest_ARG, rose_dec_qsig_ChargeRequest_RES + }, + { + ROSE_QSIG_GetFinalCharge, NULL, 60, + rose_enc_qsig_DummyArg_ARG, NULL, + rose_dec_qsig_DummyArg_ARG, NULL + }, + { + ROSE_QSIG_AocFinal, NULL, 61, + rose_enc_qsig_AocFinal_ARG, NULL, + rose_dec_qsig_AocFinal_ARG, NULL + }, + { + ROSE_QSIG_AocInterim, NULL, 62, + rose_enc_qsig_AocInterim_ARG, NULL, + rose_dec_qsig_AocInterim_ARG, NULL + }, + { + ROSE_QSIG_AocRate, NULL, 63, + rose_enc_qsig_AocRate_ARG, NULL, + rose_dec_qsig_AocRate_ARG, NULL + }, + { + ROSE_QSIG_AocComplete, NULL, 64, + rose_enc_qsig_AocComplete_ARG, rose_enc_qsig_AocComplete_RES, + rose_dec_qsig_AocComplete_ARG, rose_dec_qsig_AocComplete_RES + }, + { + ROSE_QSIG_AocDivChargeReq, NULL, 65, + rose_enc_qsig_AocDivChargeReq_ARG, NULL, + rose_dec_qsig_AocDivChargeReq_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 Errors from Q.SIG SS-AOC-Operations + * { iso(1) standard(0) pss1-advice-of-charge(15050) advice-of-charge-operations(0) } + */ + { + ROSE_ERROR_QSIG_AOC_FreeOfCharge, NULL, 1016, + 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_ActivationDiversion, "ROSE_ETSI_ActivationDiversion" }, + { ROSE_ETSI_DeactivationDiversion, "ROSE_ETSI_DeactivationDiversion" }, + { ROSE_ETSI_ActivationStatusNotificationDiv,"ROSE_ETSI_ActivationStatusNotificationDiv" }, + { ROSE_ETSI_DeactivationStatusNotificationDiv,"ROSE_ETSI_DeactivationStatusNotificationDiv" }, + { ROSE_ETSI_InterrogationDiversion, "ROSE_ETSI_InterrogationDiversion" }, + { ROSE_ETSI_DiversionInformation, "ROSE_ETSI_DiversionInformation" }, + { ROSE_ETSI_CallDeflection, "ROSE_ETSI_CallDeflection" }, + { ROSE_ETSI_CallRerouting, "ROSE_ETSI_CallRerouting" }, + { ROSE_ETSI_DivertingLegInformation2, "ROSE_ETSI_DivertingLegInformation2" }, + { ROSE_ETSI_InterrogateServedUserNumbers, "ROSE_ETSI_InterrogateServedUserNumbers" }, + { ROSE_ETSI_DivertingLegInformation1, "ROSE_ETSI_DivertingLegInformation1" }, + { ROSE_ETSI_DivertingLegInformation3, "ROSE_ETSI_DivertingLegInformation3" }, + + { ROSE_ETSI_EctExecute, "ROSE_ETSI_EctExecute" }, + { ROSE_ETSI_ExplicitEctExecute, "ROSE_ETSI_ExplicitEctExecute" }, + { ROSE_ETSI_RequestSubaddress, "ROSE_ETSI_RequestSubaddress" }, + { ROSE_ETSI_SubaddressTransfer, "ROSE_ETSI_SubaddressTransfer" }, + { ROSE_ETSI_EctLinkIdRequest, "ROSE_ETSI_EctLinkIdRequest" }, + { ROSE_ETSI_EctInform, "ROSE_ETSI_EctInform" }, + { ROSE_ETSI_EctLoopTest, "ROSE_ETSI_EctLoopTest" }, + + { 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_ChargeRequest, "ROSE_QSIG_ChargeRequest" }, + { ROSE_QSIG_GetFinalCharge, "ROSE_QSIG_GetFinalCharge" }, + { ROSE_QSIG_AocFinal, "ROSE_QSIG_AocFinal" }, + { ROSE_QSIG_AocInterim, "ROSE_QSIG_AocInterim" }, + { ROSE_QSIG_AocRate, "ROSE_QSIG_AocRate" }, + { ROSE_QSIG_AocComplete, "ROSE_QSIG_AocComplete" }, + { ROSE_QSIG_AocDivChargeReq, "ROSE_QSIG_AocDivChargeReq" }, + + { 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_IncomingCallAccepted, "Diversion: Incoming Call Accepted" }, + { ROSE_ERROR_Div_NumberOfDiversionsExceeded, "Diversion: Number Of Diversions Exceeded" }, + { ROSE_ERROR_Div_NotActivated, "Diversion: Not Activated" }, + { ROSE_ERROR_Div_RequestAlreadyAccepted, "Diversion: Request Already Accepted" }, + + { ROSE_ERROR_AOC_NoChargingInfoAvailable, "AOC: No Charging Info Available" }, + + { ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, "ECT: Link ID Not Assigned By Network" }, + + /* Q.SIG specific errors */ + { ROSE_ERROR_QSIG_Unspecified, "Unspecified" }, + + { ROSE_ERROR_QSIG_AOC_FreeOfCharge, "AOC: FreeOfCharge" }, + + { 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 */ Property changes on: rose.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose_other.c =================================================================== --- a/rose_other.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_other.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,277 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 Switch type operations for: NI2, 4ESS, 5ESS, DMS-100 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \brief Encode the DMS-100 RLT_OperationInd result facility ie 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. + */ +unsigned char *rose_enc_dms100_RLT_OperationInd_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + args->dms100.RLT_OperationInd.call_id); +} + +/*! + * \brief Encode the DMS-100 RLT_ThirdParty invoke facility ie 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. + */ +unsigned char *rose_enc_dms100_RLT_ThirdParty_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseDms100RLTThirdParty_ARG *rlt_thirdparty; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + rlt_thirdparty = &args->dms100.RLT_ThirdParty; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + rlt_thirdparty->call_id)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + rlt_thirdparty->reason)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Decode the DMS-100 RLT_OperationInd result argument 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. + */ +const unsigned char *rose_dec_dms100_RLT_OperationInd_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 0); + ASN1_CALL(pos, asn1_dec_int(ctrl, "callId", tag, pos, end, &value)); + args->dms100.RLT_OperationInd.call_id = value; + + return pos; +} + +/*! + * \brief Decode the DMS-100 RLT_ThirdParty invoke argument 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. + */ +const unsigned char *rose_dec_dms100_RLT_ThirdParty_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseDms100RLTThirdParty_ARG *rlt_third_party; + + rlt_third_party = &args->dms100.RLT_ThirdParty; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " RLT_ThirdParty %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_CLASS_CONTEXT_SPECIFIC | 0); + ASN1_CALL(pos, asn1_dec_int(ctrl, "callId", tag, pos, seq_end, &value)); + rlt_third_party->call_id = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, asn1_dec_int(ctrl, "reason", tag, pos, seq_end, &value)); + rlt_third_party->reason = value; + + /* Fixup will skip over any OPTIONAL information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Encode the NI2 InformationFollowing invoke facility ie 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. + */ +unsigned char *rose_enc_ni2_InformationFollowing_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + /* Encode the unknown enumeration value. */ + return asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + args->ni2.InformationFollowing.value); +} + +/*! + * \brief Encode the NI2 InitiateTransfer invoke facility ie 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. + */ +unsigned char *rose_enc_ni2_InitiateTransfer_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseNi2InitiateTransfer_ARG *initiate_transfer; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + initiate_transfer = &args->ni2.InitiateTransfer; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + initiate_transfer->call_reference)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Decode the NI2 InformationFollowing invoke argument 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. + */ +const unsigned char *rose_dec_ni2_InformationFollowing_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "unknown", tag, pos, end, &value)); + args->ni2.InformationFollowing.value = value; + + return pos; +} + +/*! + * \brief Decode the NI2 InitiateTransfer invoke argument 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. + */ +const unsigned char *rose_dec_ni2_InitiateTransfer_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseNi2InitiateTransfer_ARG *initiate_transfer; + + initiate_transfer = &args->ni2.InitiateTransfer; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " InitiateTransfer %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, "callReference", tag, pos, seq_end, &value)); + initiate_transfer->call_reference = value; + + /* Fixup will skip over any OPTIONAL information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_other.c */ Property changes on: rose_other.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: pri_q931.h =================================================================== --- a/pri_q931.h (.../tags/1.4.10.2) (revision 1357) +++ b/pri_q931.h (.../branches/1.4) (revision 1357) @@ -30,44 +30,6 @@ #ifndef _PRI_Q931_H #define _PRI_Q931_H -typedef enum q931_state { - /* User states */ - U0_NULL_STATE, - U1_CALL_INITIATED, - U2_OVERLAP_SENDING, - U3_OUTGOING_CALL_PROCEEDING, - U4_CALL_DELIVERED, - U6_CALL_PRESENT, - U7_CALL_RECEIVED, - U8_CONNECT_REQUEST, - U9_INCOMING_CALL_PROCEEDING, - U10_ACTIVE, - U11_DISCONNECT_REQUEST, - U12_DISCONNECT_INDICATION, - U15_SUSPEND_REQUEST, - U17_RESUME_REQUEST, - U19_RELEASE_REQUEST, - U25_OVERLAP_RECEIVING, - /* Network states */ - N0_NULL_STATE, - N1_CALL_INITIATED, - N2_OVERLAP_SENDING, - N3_OUTGOING_CALL_PROCEEDING, - N4_CALL_DELIVERED, - N6_CALL_PRESENT, - N7_CALL_RECEIVED, - N8_CONNECT_REQUEST, - N9_INCOMING_CALL_PROCEEDING, - N10_ACTIVE, - N11_DISCONNECT_REQUEST, - N12_DISCONNECT_INDICATION, - N15_SUSPEND_REQUEST, - N17_RESUME_REQUEST, - N19_RELEASE_REQUEST, - N22_CALL_ABORT, - N25_OVERLAP_RECEIVING -} q931_state; - typedef enum q931_mode { UNKNOWN_MODE, CIRCUIT_MODE, @@ -79,13 +41,13 @@ u_int8_t pd; /* Protocol Discriminator */ #if __BYTE_ORDER == __BIG_ENDIAN u_int8_t x0:4; - u_int8_t crlen:4; + u_int8_t crlen:4;/*!< Call reference length */ #else - u_int8_t crlen:4; + u_int8_t crlen:4;/*!< Call reference length */ u_int8_t x0:4; #endif u_int8_t contents[0]; - u_int8_t crv[3]; + u_int8_t crv[3];/*!< Call reference value */ } __attribute__ ((packed)) q931_h; @@ -113,6 +75,10 @@ #define Q931_PROTOCOL_DISCRIMINATOR 0x08 #define GR303_PROTOCOL_DISCRIMINATOR 0x4f +/* AT&T Maintenance Protocol Discriminator */ +#define MAINTENANCE_PROTOCOL_DISCRIMINATOR_1 0x03 +/* National Maintenance Protocol Discriminator */ +#define MAINTENANCE_PROTOCOL_DISCRIMINATOR_2 0x43 /* Q.931 / National ISDN Message Types */ @@ -157,9 +123,17 @@ #define Q931_SUSPEND_REJECT 0x21 /* Maintenance messages (codeset 0 only) */ -#define NATIONAL_SERVICE 0x0f -#define NATIONAL_SERVICE_ACKNOWLEDGE 0x07 +#define ATT_SERVICE 0x0f +#define ATT_SERVICE_ACKNOWLEDGE 0x07 +#define NATIONAL_SERVICE 0x07 +#define NATIONAL_SERVICE_ACKNOWLEDGE 0x0f +#define SERVICE_CHANGE_STATUS_INSERVICE 0 +#define SERVICE_CHANGE_STATUS_LOOPBACK 1 /* not supported */ +#define SERVICE_CHANGE_STATUS_OUTOFSERVICE 2 +#define SERVICE_CHANGE_STATUS_REQCONTINUITYCHECK 3 /* not supported */ +#define SERVICE_CHANGE_STATUS_SHUTDOWN 4 /* not supported */ + /* Special codeset 0 IE */ #define NATIONAL_CHANGE_STATUS 0x1 @@ -168,10 +142,11 @@ #define Q931_NON_LOCKING_SHIFT 0x98 #define Q931_BEARER_CAPABILITY 0x04 #define Q931_CAUSE 0x08 -#define Q931_CALL_STATE 0x14 +#define Q931_IE_CALL_STATE 0x14 #define Q931_CHANNEL_IDENT 0x18 #define Q931_PROGRESS_INDICATOR 0x1e #define Q931_NETWORK_SPEC_FAC 0x20 +#define Q931_CALLING_PARTY_CATEGORY (0x32 | Q931_CODESET(5)) #define Q931_INFORMATION_RATE 0x40 #define Q931_TRANSIT_DELAY 0x42 #define Q931_TRANS_DELAY_SELECT 0x43 @@ -200,8 +175,9 @@ #define Q931_IE_SEGMENTED_MSG 0x00 #define Q931_IE_CHANGE_STATUS 0x01 #define Q931_IE_ORIGINATING_LINE_INFO (0x01 | Q931_CODESET(6)) -#define Q931_IE_CONNECTED_ADDR 0x0C -#define Q931_IE_CONNECTED_NUM 0x4C +#define Q931_IE_CONNECTED_ADDR 0x0c +#define Q931_IE_CONNECTED_NUM 0x4c +#define Q931_IE_CONNECTED_SUBADDR 0x4d #define Q931_IE_CALL_IDENTITY 0x10 #define Q931_IE_FACILITY 0x1c #define Q931_IE_ENDPOINT_ID 0x26 @@ -224,31 +200,255 @@ #define Q931_IE_ESCAPE_FOR_EXT 0x7F -/* Call state stuff */ -#define Q931_CALL_STATE_NULL 0 -#define Q931_CALL_STATE_CALL_INITIATED 1 -#define Q931_CALL_STATE_OVERLAP_SENDING 2 -#define Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING 3 -#define Q931_CALL_STATE_CALL_DELIVERED 4 -#define Q931_CALL_STATE_CALL_PRESENT 6 -#define Q931_CALL_STATE_CALL_RECEIVED 7 -#define Q931_CALL_STATE_CONNECT_REQUEST 8 -#define Q931_CALL_STATE_INCOMING_CALL_PROCEEDING 9 -#define Q931_CALL_STATE_ACTIVE 10 -#define Q931_CALL_STATE_DISCONNECT_REQUEST 11 -#define Q931_CALL_STATE_DISCONNECT_INDICATION 12 -#define Q931_CALL_STATE_SUSPEND_REQUEST 15 -#define Q931_CALL_STATE_RESUME_REQUEST 17 -#define Q931_CALL_STATE_RELEASE_REQUEST 19 -#define Q931_CALL_STATE_OVERLAP_RECEIVING 25 -#define Q931_CALL_STATE_RESTART_REQUEST 61 -#define Q931_CALL_STATE_RESTART 62 +/*! Q.931 call states */ +enum Q931_CALL_STATE { + /*! + * \details + * null state (U0): + * No call exists. + * \details + * null state (N0): + * No call exists. + */ + Q931_CALL_STATE_NULL = 0, + /*! + * \details + * call initiated (U1): + * This state exists for an outgoing call, when the user requests + * call establishment from the network. + * \details + * call initiated (N1): + * This state exists for an outgoing call when the network has received + * a call establishment request but has not yet responded. + */ + Q931_CALL_STATE_CALL_INITIATED = 1, + /*! + * \details + * overlap sending (U2): + * This state exists for an outgoing call when the user has + * received acknowledgement of the call establishment request which + * permits the user to send additional call information to the network + * in overlap mode. + * \details + * overlap sending (N2): + * This state exists for an outgoing call when the network has acknowledged + * the call establishment request and is prepared to receive additional + * call information (if any) in overlap mode. + */ + Q931_CALL_STATE_OVERLAP_SENDING = 2, + /*! + * \details + * outgoing call proceeding (U3): + * This state exists for an outgoing call when the user has + * received acknowledgement that the network has received all + * call information necessary to effect call establishment. + * \details + * outgoing call proceeding (N3): + * This state exists for an outgoing call when the network has sent + * acknowledgement that the network has received all call information + * necessary to effect call establishment. + */ + Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING = 3, + /*! + * \details + * call delivered (U4): + * This state exists for an outgoing call when the calling user has + * received an indication that remote user alerting has been initiated. + * \details + * call delivered (N4): + * This state exists for an outgoing call when the network has indicated + * that remote user alerting has been initiated. + */ + Q931_CALL_STATE_CALL_DELIVERED = 4, + /*! + * \details + * call present (U6): + * This state exists for an incoming call when the user has received a + * call establishment request but has not yet responded. + * \details + * call present (N6): + * This state exists for an incoming call when the network has sent a + * call establishment request but has not yet received a satisfactory + * response. + */ + Q931_CALL_STATE_CALL_PRESENT = 6, + /*! + * \details + * call received (U7): + * This state exists for an incoming call when the user has indicated + * alerting but has not yet answered. + * \details + * call received (N7): + * This state exists for an incoming call when the network has received + * an indication that the user is alerting but has not yet received an + * answer. + */ + Q931_CALL_STATE_CALL_RECEIVED = 7, + /*! + * \details + * connect request (U8): + * This state exists for an incoming call when the user has answered + * the call and is waiting to be awarded the call. + * \details + * connect request (N8): + * This state exists for an incoming call when the network has received + * an answer but the network has not yet awarded the call. + */ + Q931_CALL_STATE_CONNECT_REQUEST = 8, + /*! + * \details + * incoming call proceeding (U9): + * This state exists for an incoming call when the user has sent + * acknowledgement that the user has received all call information + * necessary to effect call establishment. + * \details + * incoming call proceeding (N9): + * This state exists for an incoming call when the network has received + * acknowledgement that the user has received all call information + * necessary to effect call establishment. + */ + Q931_CALL_STATE_INCOMING_CALL_PROCEEDING = 9, + /*! + * \details + * active (U10): + * This state exists for an incoming call when the user has received + * an acknowledgement from the network that the user has been awarded + * the call. This state exists for an outgoing call when the user has + * received an indication that the remote user has answered the call. + * \details + * active (N10): + * This state exists for an incoming call when the network has awarded + * the call to the called user. This state exists for an outgoing call + * when the network has indicated that the remote user has answered + * the call. + */ + Q931_CALL_STATE_ACTIVE = 10, + /*! + * \details + * disconnect request (U11): + * This state exists when the user has requested the network to clear + * the end-to-end connection (if any) and is waiting for a response. + * \details + * disconnect request (N11): + * This state exists when the network has received a request from the + * user to clear the end-to-end connection (if any). + */ + Q931_CALL_STATE_DISCONNECT_REQUEST = 11, + /*! + * \details + * disconnect indication (U12): + * This state exists when the user has received an invitation to + * disconnect because the network has disconnected the end-to-end + * connection (if any). + * \details + * disconnect indication (N12): + * This state exists when the network has disconnected the end-to-end + * connection (if any) and has sent an invitation to disconnect the + * user-network connection. + */ + Q931_CALL_STATE_DISCONNECT_INDICATION = 12, + /*! + * \details + * suspend request (U15): + * This state exists when the user has requested the network to suspend + * the call and is waiting for a response. + * \details + * suspend request (N15): + * This state exists when the network has received a request to suspend + * the call but has not yet responded. + */ + Q931_CALL_STATE_SUSPEND_REQUEST = 15, + /*! + * \details + * resume request (U17): + * This state exists when the user has requested the network to resume + * a previously suspended call and is waiting for a response. + * \details + * resume request (N17): + * This state exists when the network has received a request to resume + * a previously suspended call but has not yet responded. + */ + Q931_CALL_STATE_RESUME_REQUEST = 17, + /*! + * \details + * release request (U19): + * This state exists when the user has requested the network to release + * and is waiting for a response. + * \details + * release request (N19): + * This state exists when the network has requested the user to release + * and is waiting for a response. + */ + Q931_CALL_STATE_RELEASE_REQUEST = 19, + /*! + * \details + * call abort (N22): + * This state exists for an incoming call for the point-to-multipoint + * configuration when the call is being cleared before any user has been + * awarded the call. + */ + Q931_CALL_STATE_CALL_ABORT = 22, + /*! + * \details + * overlap receiving (U25): + * This state exists for an incoming call when the user has acknowledged + * the call establishment request from the network and is prepared to + * receive additional call information (if any) in overlap mode. + * \details + * overlap receiving (N25): + * This state exists for an incoming call when the network has received + * acknowledgement of the call establishment request which permits the + * network to send additional call information (if any) in the overlap + * mode. + */ + Q931_CALL_STATE_OVERLAP_RECEIVING = 25, + /*! + * \details + * call independent service (U31): (From Q.932) + * This state exists when a call independent supplementary service + * signalling connection is established. + * \details + * call independent service (N31): (From Q.932) + * This state exists when a call independent supplementary service + * signalling connection is established. + */ + Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE = 31, + Q931_CALL_STATE_RESTART_REQUEST = 61, + Q931_CALL_STATE_RESTART = 62, + /*! + * \details + * Call state has not been set. + * Call state does not exist. + * Call state not initialized. + * Call state internal use only. + */ + Q931_CALL_STATE_NOT_SET = 0xFF, +}; +/*! Q.931 call establishment state ranking for competing calls in PTMP NT mode. */ +enum Q931_RANKED_CALL_STATE { + /*! Call is present but has no response yet. */ + Q931_RANKED_CALL_STATE_PRESENT, + /*! Call is collecting digits. */ + Q931_RANKED_CALL_STATE_OVERLAP, + /*! Call routing is happening. */ + Q931_RANKED_CALL_STATE_PROCEEDING, + /*! Called party is being alerted of the call. */ + Q931_RANKED_CALL_STATE_ALERTING, + /*! Call is connected. A winner has been declared. */ + Q931_RANKED_CALL_STATE_CONNECT, + /*! Call is in some non-call establishment state (likely disconnecting). */ + Q931_RANKED_CALL_STATE_OTHER, +}; /* EuroISDN */ #define Q931_SENDING_COMPLETE 0xa1 +extern int maintenance_service(struct pri *pri, int span, int channel, int changestatus); +extern int maintenance_service_ack(struct pri *pri, q931_call *call); + + /* Q.SIG specific */ #define QSIG_IE_TRANSIT_COUNT 0x31 @@ -268,7 +468,7 @@ extern int q931_information(struct pri *pri, q931_call *call, char digit); -extern int q931_keypad_facility(struct pri *pri, q931_call *call, char *digits); +extern int q931_keypad_facility(struct pri *pri, q931_call *call, const char *digits); extern int q931_connect(struct pri *pri, q931_call *call, int channel, int nonisdn); @@ -291,8 +491,16 @@ extern int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req); extern void q931_dump(struct pri *pri, q931_h *h, int len, int txrx); -extern void __q931_destroycall(struct pri *pri, q931_call *c); +void q931_destroycall(struct pri *pri, q931_call *c); extern void q931_dl_indication(struct pri *pri, int event); +int q931_send_hold(struct pri *ctrl, struct q931_call *call); +int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call); +int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause); + +int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel); +int q931_send_retrieve_ack(struct pri *ctrl, struct q931_call *call, int channel); +int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause); + #endif Index: rose_qsig_diversion.c =================================================================== --- a/rose_qsig_diversion.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_qsig_diversion.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,1390 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 Q.SIG ROSE Call-Diversion-Operations + * + * Call-Diversion-Operations ECMA-174 Annex F Table F.1 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the IntResult 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param int_result Forwarding record information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_IntResult(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseQsigForwardingRecord *int_result) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &int_result->served_user_number)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + int_result->basic_service)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, int_result->procedure)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &int_result->diverted_to)); + if (int_result->remote_enabled) { + /* Not the DEFAULT value */ + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, + int_result->remote_enabled)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the IntResultList 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SET unless the caller implicitly + * tags it otherwise. + * \param int_result_list Forwarding record list information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_IntResultList(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, + const struct roseQsigForwardingList *int_result_list) +{ + unsigned index; + unsigned char *set_len; + + ASN1_CONSTRUCTED_BEGIN(set_len, pos, end, tag); + + for (index = 0; index < int_result_list->num_records; ++index) { + ASN1_CALL(pos, rose_enc_qsig_IntResult(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &int_result_list->list[index])); + } + + ASN1_CONSTRUCTED_END(set_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the ActivateDiversionQ invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_ActivateDiversionQ_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigActivateDiversionQ_ARG *activate_diversion_q; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + activate_diversion_q = &args->qsig.ActivateDiversionQ; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activate_diversion_q->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activate_diversion_q->basic_service)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &activate_diversion_q->diverted_to)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &activate_diversion_q->served_user_number)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &activate_diversion_q->activating_user_number)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DeactivateDiversionQ invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_DeactivateDiversionQ_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigDeactivateDiversionQ_ARG *deactivate_diversion_q; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + deactivate_diversion_q = &args->qsig.DeactivateDiversionQ; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivate_diversion_q->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivate_diversion_q->basic_service)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &deactivate_diversion_q->served_user_number)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &deactivate_diversion_q->deactivating_user_number)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the InterrogateDiversionQ invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_InterrogateDiversionQ_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigInterrogateDiversionQ_ARG *interrogate_diversion_q; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + interrogate_diversion_q = &args->qsig.InterrogateDiversionQ; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + interrogate_diversion_q->procedure)); + if (interrogate_diversion_q->basic_service) { + /* Not the DEFAULT value */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + interrogate_diversion_q->basic_service)); + } + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &interrogate_diversion_q->served_user_number)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &interrogate_diversion_q->interrogating_user_number)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the InterrogateDiversionQ result facility ie 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. + */ +unsigned char *rose_enc_qsig_InterrogateDiversionQ_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_qsig_IntResultList(ctrl, pos, end, ASN1_TAG_SET, + &args->qsig.InterrogateDiversionQ); +} + +/*! + * \brief Encode the CheckRestriction invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_CheckRestriction_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigCheckRestriction_ARG *check_restriction; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + check_restriction = &args->qsig.CheckRestriction; + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &check_restriction->served_user_number)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + check_restriction->basic_service)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &check_restriction->diverted_to_number)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CallRerouting invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigCallRerouting_ARG *call_rerouting; + unsigned char *seq_len; + unsigned char *exp_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + call_rerouting = &args->qsig.CallRerouting; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + call_rerouting->rerouting_reason)); + if (call_rerouting->original_rerouting_reason_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + call_rerouting->original_rerouting_reason)); + } + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &call_rerouting->called)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + call_rerouting->diversion_counter)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &call_rerouting->q931ie)); + + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &call_rerouting->last_rerouting)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + call_rerouting->subscription_option)); + + if (call_rerouting->calling_subaddress.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &call_rerouting->calling_subaddress)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); + ASN1_CALL(pos, rose_enc_PresentedNumberScreened(ctrl, pos, end, + &call_rerouting->calling)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + + if (call_rerouting->calling_name_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5); + ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, + &call_rerouting->calling_name)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + if (call_rerouting->original_called_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 6); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &call_rerouting->original_called)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + if (call_rerouting->redirecting_name_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 7); + ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, + &call_rerouting->redirecting_name)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + if (call_rerouting->original_called_name_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 8); + ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, + &call_rerouting->original_called_name)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DivertingLegInformation1 invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigDivertingLegInformation1_ARG *diverting_leg_information_1; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + diverting_leg_information_1 = &args->qsig.DivertingLegInformation1; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diverting_leg_information_1->diversion_reason)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diverting_leg_information_1->subscription_option)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &diverting_leg_information_1->nominated_number)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DivertingLegInformation2 invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigDivertingLegInformation2_ARG *diverting_leg_information_2; + unsigned char *seq_len; + unsigned char *exp_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + diverting_leg_information_2 = &args->qsig.DivertingLegInformation2; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + diverting_leg_information_2->diversion_counter)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diverting_leg_information_2->diversion_reason)); + if (diverting_leg_information_2->original_diversion_reason_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + diverting_leg_information_2->original_diversion_reason)); + } + + if (diverting_leg_information_2->diverting_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diverting_leg_information_2->diverting)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + if (diverting_leg_information_2->original_called_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diverting_leg_information_2->original_called)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + if (diverting_leg_information_2->redirecting_name_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, + &diverting_leg_information_2->redirecting_name)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + if (diverting_leg_information_2->original_called_name_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4); + ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, + &diverting_leg_information_2->original_called_name)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DivertingLegInformation3 invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseQsigDivertingLegInformation3_ARG *diverting_leg_information_3; + unsigned char *seq_len; + unsigned char *exp_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + diverting_leg_information_3 = &args->qsig.DivertingLegInformation3; + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, + diverting_leg_information_3->presentation_allowed_indicator)); + + if (diverting_leg_information_3->redirection_name_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(exp_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); + ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, + &diverting_leg_information_3->redirection_name)); + ASN1_CONSTRUCTED_END(exp_len, pos, end); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the IntResult 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 int_result Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_IntResult(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigForwardingRecord *int_result) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + const unsigned char *save_pos; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s IntResult %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_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, + &int_result->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + int_result->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + int_result->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "divertedToAddress", tag, pos, seq_end, + &int_result->diverted_to)); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + int_result->remote_enabled = 0; /* DEFAULT FALSE */ + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag & ~ASN1_PC_MASK) { + case ASN1_TYPE_BOOLEAN: + /* Must not be constructed but we will not check for it for simplicity. */ + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "remoteEnabled", tag, pos, seq_end, + &value)); + int_result->remote_enabled = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " extension %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the IntResultList 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 int_result_list Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_IntResultList(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigForwardingList *int_result_list) +{ + int length; + int set_offset; + const unsigned char *set_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s IntResultList %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(set_end, set_offset, length, pos, end); + + int_result_list->num_records = 0; + while (pos < set_end && *pos != ASN1_INDEF_TERM) { + if (int_result_list->num_records < ARRAY_LEN(int_result_list->list)) { + ASN1_CALL(pos, asn1_dec_tag(pos, set_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_qsig_IntResult(ctrl, "listEntry", tag, pos, set_end, + &int_result_list->list[int_result_list->num_records])); + ++int_result_list->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, set_offset, set_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG ActivateDiversionQ invoke argument 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. + */ +const unsigned char *rose_dec_qsig_ActivateDiversionQ_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigActivateDiversionQ_ARG *activate_diversion_q; + + activate_diversion_q = &args->qsig.ActivateDiversionQ; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " ActivateDiversionQ %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_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + activate_diversion_q->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + activate_diversion_q->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "divertedToAddress", tag, pos, seq_end, + &activate_diversion_q->diverted_to)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, + &activate_diversion_q->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "activatingUserNr", tag, pos, seq_end, + &activate_diversion_q->activating_user_number)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG DeactivateDiversionQ invoke argument 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. + */ +const unsigned char *rose_dec_qsig_DeactivateDiversionQ_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigDeactivateDiversionQ_ARG *deactivate_diversion_q; + + deactivate_diversion_q = &args->qsig.DeactivateDiversionQ; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DeactivateDiversionQ %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_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + deactivate_diversion_q->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + deactivate_diversion_q->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, + &deactivate_diversion_q->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "deactivatingUserNr", tag, pos, seq_end, + &deactivate_diversion_q->deactivating_user_number)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG InterrogateDiversionQ invoke argument 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. + */ +const unsigned char *rose_dec_qsig_InterrogateDiversionQ_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigInterrogateDiversionQ_ARG *interrogate_diversion_q; + + interrogate_diversion_q = &args->qsig.InterrogateDiversionQ; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " InterrogateDiversionQ %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_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + interrogate_diversion_q->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + if (tag == ASN1_TYPE_ENUMERATED) { + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + interrogate_diversion_q->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + } else { + interrogate_diversion_q->basic_service = 0; /* allServices */ + } + + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, + &interrogate_diversion_q->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "interrogatingUserNr", tag, pos, seq_end, + &interrogate_diversion_q->interrogating_user_number)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG InterrogateDiversionQ result argument 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. + */ +const unsigned char *rose_dec_qsig_InterrogateDiversionQ_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args) +{ + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SET); + return rose_dec_qsig_IntResultList(ctrl, "InterrogateDiversionQ", tag, pos, end, + &args->qsig.InterrogateDiversionQ); +} + +/*! + * \brief Decode the Q.SIG CheckRestriction invoke argument 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. + */ +const unsigned char *rose_dec_qsig_CheckRestriction_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigCheckRestriction_ARG *check_restriction; + + check_restriction = &args->qsig.CheckRestriction; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CheckRestriction %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_CALL(pos, rose_dec_PartyNumber(ctrl, "servedUserNr", tag, pos, seq_end, + &check_restriction->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + check_restriction->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "divertedToNr", tag, pos, seq_end, + &check_restriction->diverted_to_number)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG CallRerouting invoke argument 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. + */ +const unsigned char *rose_dec_qsig_CallRerouting_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + int explicit_offset; + const unsigned char *explicit_end; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigCallRerouting_ARG *call_rerouting; + + call_rerouting = &args->qsig.CallRerouting; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallRerouting %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_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "reroutingReason", tag, pos, seq_end, &value)); + call_rerouting->rerouting_reason = 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, "originalReroutingReason", tag, pos, seq_end, + &value)); + call_rerouting->original_rerouting_reason = value; + call_rerouting->original_rerouting_reason_present = 1; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + } else { + call_rerouting->original_rerouting_reason_present = 0; + } + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "calledAddress", tag, pos, seq_end, + &call_rerouting->called)); + + 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, "diversionCounter", tag, pos, seq_end, &value)); + call_rerouting->diversion_counter = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "pSS1InfoElement", tag, pos, seq_end, + &call_rerouting->q931ie, sizeof(call_rerouting->q931ie_contents))); + + /* Remove EXPLICIT tag */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); + 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_PresentedNumberUnscreened(ctrl, "lastReroutingNr", tag, pos, + explicit_end, &call_rerouting->last_rerouting)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "subscriptionOption", tag, pos, seq_end, &value)); + call_rerouting->subscription_option = value; + + 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_PartySubaddress(ctrl, "callingPartySubaddress", tag, pos, + explicit_end, &call_rerouting->calling_subaddress)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + } else { + call_rerouting->calling_subaddress.length = 0; + } + + /* Remove EXPLICIT tag */ + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4); + 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_PresentedNumberScreened(ctrl, "callingNumber", tag, pos, + explicit_end, &call_rerouting->calling)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + call_rerouting->calling_name_present = 0; + call_rerouting->redirecting_name_present = 0; + call_rerouting->original_called_name_present = 0; + call_rerouting->original_called_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 5: + /* 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_qsig_Name(ctrl, "callingName", tag, pos, + explicit_end, &call_rerouting->calling_name)); + call_rerouting->calling_name_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 6: + /* 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_PresentedNumberUnscreened(ctrl, "originalCalledNr", + tag, pos, explicit_end, &call_rerouting->original_called)); + call_rerouting->original_called_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 7: + /* 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_qsig_Name(ctrl, "redirectingName", tag, pos, + explicit_end, &call_rerouting->redirecting_name)); + call_rerouting->redirecting_name_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 8: + /* 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_qsig_Name(ctrl, "originalCalledName", tag, pos, + explicit_end, &call_rerouting->original_called_name)); + call_rerouting->original_called_name_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 9: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 9: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 10: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " extension %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG DivertingLegInformation1 invoke argument 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. + */ +const unsigned char *rose_dec_qsig_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigDivertingLegInformation1_ARG *diverting_leg_information_1; + + diverting_leg_information_1 = &args->qsig.DivertingLegInformation1; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DivertingLegInformation1 %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_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionReason", tag, pos, seq_end, &value)); + diverting_leg_information_1->diversion_reason = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "subscriptionOption", tag, pos, seq_end, &value)); + diverting_leg_information_1->subscription_option = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "nominatedNr", tag, pos, seq_end, + &diverting_leg_information_1->nominated_number)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG DivertingLegInformation2 invoke argument 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. + */ +const unsigned char *rose_dec_qsig_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + int explicit_offset; + const unsigned char *explicit_end; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigDivertingLegInformation2_ARG *diverting_leg_information_2; + + diverting_leg_information_2 = &args->qsig.DivertingLegInformation2; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DivertingLegInformation2 %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, "diversionCounter", tag, pos, seq_end, &value)); + diverting_leg_information_2->diversion_counter = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionReason", tag, pos, seq_end, &value)); + diverting_leg_information_2->diversion_reason = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + diverting_leg_information_2->original_diversion_reason_present = 0; + diverting_leg_information_2->diverting_present = 0; + diverting_leg_information_2->original_called_present = 0; + diverting_leg_information_2->redirecting_name_present = 0; + diverting_leg_information_2->original_called_name_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + 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, "originalDiversionReason", tag, pos, + seq_end, &value)); + diverting_leg_information_2->original_diversion_reason = value; + diverting_leg_information_2->original_diversion_reason_present = 1; + break; + case 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_PresentedNumberUnscreened(ctrl, "divertingNr", tag, + pos, explicit_end, &diverting_leg_information_2->diverting)); + diverting_leg_information_2->diverting_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + /* 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_PresentedNumberUnscreened(ctrl, "originalCalledNr", + tag, pos, explicit_end, &diverting_leg_information_2->original_called)); + diverting_leg_information_2->original_called_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case 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_qsig_Name(ctrl, "redirectingName", tag, pos, + explicit_end, &diverting_leg_information_2->redirecting_name)); + diverting_leg_information_2->redirecting_name_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4: + /* 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_qsig_Name(ctrl, "originalCalledName", tag, pos, + explicit_end, &diverting_leg_information_2->original_called_name)); + diverting_leg_information_2->original_called_name_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 5: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 5: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 6: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " extension %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG DivertingLegInformation3 invoke argument 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. + */ +const unsigned char *rose_dec_qsig_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + int explicit_offset; + const unsigned char *explicit_end; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigDivertingLegInformation3_ARG *diverting_leg_information_3; + + diverting_leg_information_3 = &args->qsig.DivertingLegInformation3; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DivertingLegInformation3 %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_BOOLEAN); + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "presentationAllowedIndicator", tag, pos, + seq_end, &value)); + diverting_leg_information_3->presentation_allowed_indicator = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + diverting_leg_information_3->redirection_name_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + /* 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_qsig_Name(ctrl, "redirectionName", tag, pos, + explicit_end, &diverting_leg_information_3->redirection_name)); + diverting_leg_information_3->redirection_name_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " extension %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_qsig_diversion.c */ Property changes on: rose_qsig_diversion.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose.h =================================================================== --- a/rose.h (.../tags/1.4.10.2) (revision 0) +++ b/rose.h (.../branches/1.4) (revision 1357) @@ -0,0 +1,3580 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 ROSE definitions and prototypes + * + * \details + * This file contains all of the data structures and definitions needed + * for ROSE component encoding and decoding. + * + * ROSE - Remote Operations Service Element + * ASN.1 - Abstract Syntax Notation 1 + * APDU - Application Protocol Data Unit + * + * \author Richard Mudgett + */ + +#ifndef _LIBPRI_ROSE_H +#define _LIBPRI_ROSE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------------------------------------------------------- */ + + +/* Northern Telecom DMS-100 RLT related operations */ +#define ROSE_DMS100_RLT_SERVICE_ID 0x3e +#define ROSE_DMS100_RLT_OPERATION_IND 0x01 +#define ROSE_DMS100_RLT_THIRD_PARTY 0x02 + +/*! \brief ROSE operation-value function code */ +enum rose_operation { + /*! \brief No ROSE operation */ + ROSE_None, + /*! \brief Unknown OID/localValue operation-value code */ + ROSE_Unknown, + +/* *INDENT-OFF* */ + /* ETSI Diversion-Operations */ + ROSE_ETSI_ActivationDiversion, /*!< Invoke/Result */ + ROSE_ETSI_DeactivationDiversion, /*!< Invoke/Result */ + ROSE_ETSI_ActivationStatusNotificationDiv,/*!< Invoke only */ + ROSE_ETSI_DeactivationStatusNotificationDiv,/*!< Invoke only */ + ROSE_ETSI_InterrogationDiversion, /*!< Invoke/Result */ + ROSE_ETSI_DiversionInformation, /*!< Invoke only */ + ROSE_ETSI_CallDeflection, /*!< Invoke/Result */ + ROSE_ETSI_CallRerouting, /*!< Invoke/Result */ + ROSE_ETSI_InterrogateServedUserNumbers, /*!< Invoke/Result */ + ROSE_ETSI_DivertingLegInformation1, /*!< Invoke only */ + ROSE_ETSI_DivertingLegInformation2, /*!< Invoke only */ + ROSE_ETSI_DivertingLegInformation3, /*!< Invoke only */ + + /* + * ETSI Advice-of-Charge-Operations + * + * 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, /*!< Invoke/Result */ + ROSE_ETSI_AOCSCurrency, /*!< Invoke only */ + ROSE_ETSI_AOCSSpecialArr, /*!< Invoke only */ + ROSE_ETSI_AOCDCurrency, /*!< Invoke only */ + ROSE_ETSI_AOCDChargingUnit, /*!< Invoke only */ + ROSE_ETSI_AOCECurrency, /*!< Invoke only */ + ROSE_ETSI_AOCEChargingUnit, /*!< Invoke only */ + + /* ETSI Explicit-Call-Transfer-Operations-and-Errors */ + ROSE_ETSI_EctExecute, /*!< Invoke/Result */ + ROSE_ETSI_ExplicitEctExecute, /*!< Invoke/Result */ + ROSE_ETSI_RequestSubaddress, /*!< Invoke only */ + ROSE_ETSI_SubaddressTransfer, /*!< Invoke only */ + ROSE_ETSI_EctLinkIdRequest, /*!< Invoke/Result */ + ROSE_ETSI_EctInform, /*!< Invoke only */ + ROSE_ETSI_EctLoopTest, /*!< Invoke/Result */ + + /* Q.SIG Name-Operations */ + ROSE_QSIG_CallingName, /*!< Invoke only */ + ROSE_QSIG_CalledName, /*!< Invoke only */ + ROSE_QSIG_ConnectedName, /*!< Invoke only */ + ROSE_QSIG_BusyName, /*!< Invoke only */ + + /* Q.SIG SS-AOC-Operations */ + ROSE_QSIG_ChargeRequest, /*!< Invoke/Result */ + ROSE_QSIG_GetFinalCharge, /*!< Invoke only */ + ROSE_QSIG_AocFinal, /*!< Invoke only */ + ROSE_QSIG_AocInterim, /*!< Invoke only */ + ROSE_QSIG_AocRate, /*!< Invoke only */ + ROSE_QSIG_AocComplete, /*!< Invoke/Result */ + ROSE_QSIG_AocDivChargeReq, /*!< Invoke only */ + + /* Q.SIG Call-Transfer-Operations (CT) */ + ROSE_QSIG_CallTransferIdentify, /*!< Invoke/Result */ + ROSE_QSIG_CallTransferAbandon, /*!< Invoke only */ + ROSE_QSIG_CallTransferInitiate, /*!< Invoke/Result */ + ROSE_QSIG_CallTransferSetup, /*!< Invoke/Result */ + ROSE_QSIG_CallTransferActive, /*!< Invoke only */ + ROSE_QSIG_CallTransferComplete, /*!< Invoke only */ + ROSE_QSIG_CallTransferUpdate, /*!< Invoke only */ + ROSE_QSIG_SubaddressTransfer, /*!< Invoke only */ + + ROSE_QSIG_PathReplacement, /*!< Invoke only */ + + /* Q.SIG Call-Diversion-Operations */ + ROSE_QSIG_ActivateDiversionQ, /*!< Invoke/Result */ + ROSE_QSIG_DeactivateDiversionQ, /*!< Invoke/Result */ + ROSE_QSIG_InterrogateDiversionQ, /*!< Invoke/Result */ + ROSE_QSIG_CheckRestriction, /*!< Invoke/Result */ + ROSE_QSIG_CallRerouting, /*!< Invoke/Result */ + ROSE_QSIG_DivertingLegInformation1, /*!< Invoke only */ + ROSE_QSIG_DivertingLegInformation2, /*!< Invoke only */ + ROSE_QSIG_DivertingLegInformation3, /*!< Invoke only */ + ROSE_QSIG_CfnrDivertedLegFailed, /*!< Invoke only */ + + /* Q.SIG SS-MWI-Operations */ + ROSE_QSIG_MWIActivate, /*!< Invoke/Result */ + ROSE_QSIG_MWIDeactivate, /*!< Invoke/Result */ + ROSE_QSIG_MWIInterrogate, /*!< Invoke/Result */ + + /* Northern Telecom DMS-100 RLT related operations */ + /*! Invoke/Result: Must set invokeId to ROSE_DMS100_RLT_OPERATION_IND */ + ROSE_DMS100_RLT_OperationInd, + /*! Invoke/Result: Must set invokeId to ROSE_DMS100_RLT_THIRD_PARTY */ + ROSE_DMS100_RLT_ThirdParty, + + ROSE_NI2_InformationFollowing, /*!< Invoke only? */ + ROSE_NI2_InitiateTransfer, /*!< Invoke only? Is this correct operation name? */ + + ROSE_Num_Operation_Codes /*!< Must be last in the enumeration */ +/* *INDENT-ON* */ +}; + +enum rose_error_code { + /*! \brief No error occurred */ + ROSE_ERROR_None, + /*! \brief Unknown OID/localValue error-value code */ + ROSE_ERROR_Unknown, + + /* General-Errors (ETS 300 196) and General-Error-List(Q.950) */ + ROSE_ERROR_Gen_NotSubscribed, /*!< also: UserNotSubscribed */ + ROSE_ERROR_Gen_NotAvailable, + ROSE_ERROR_Gen_NotImplemented, /*!< Not in Q.950 */ + ROSE_ERROR_Gen_InvalidServedUserNr, + ROSE_ERROR_Gen_InvalidCallState, + ROSE_ERROR_Gen_BasicServiceNotProvided, + ROSE_ERROR_Gen_NotIncomingCall, + ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed, + ROSE_ERROR_Gen_ResourceUnavailable, + + /* Additional General-Error-List(Q.950) */ + ROSE_ERROR_Gen_RejectedByNetwork, + ROSE_ERROR_Gen_RejectedByUser, + ROSE_ERROR_Gen_InsufficientInformation, + ROSE_ERROR_Gen_CallFailure, + ROSE_ERROR_Gen_ProceduralError, + + /* ETSI Diversion-Operations */ + ROSE_ERROR_Div_InvalidDivertedToNr, + ROSE_ERROR_Div_SpecialServiceNr, + ROSE_ERROR_Div_DiversionToServedUserNr, + ROSE_ERROR_Div_IncomingCallAccepted, + ROSE_ERROR_Div_NumberOfDiversionsExceeded, + ROSE_ERROR_Div_NotActivated, + ROSE_ERROR_Div_RequestAlreadyAccepted, + + /* ETSI Advice-of-Charge-Operations */ + ROSE_ERROR_AOC_NoChargingInfoAvailable, + + /* ETSI Explicit-Call-Transfer-Operations-and-Errors */ + ROSE_ERROR_ECT_LinkIdNotAssignedByNetwork, + + /* Q.SIG from various specifications */ + ROSE_ERROR_QSIG_Unspecified, + + /* Q.SIG SS-AOC-Operations */ + ROSE_ERROR_QSIG_AOC_FreeOfCharge, + + /* Q.SIG Call-Transfer-Operations (CT) */ + ROSE_ERROR_QSIG_CT_InvalidReroutingNumber, + ROSE_ERROR_QSIG_CT_UnrecognizedCallIdentity, + ROSE_ERROR_QSIG_CT_EstablishmentFailure, + + /* Q.SIG Call-Diversion-Operations (Additional Q.SIG specific errors) */ + ROSE_ERROR_QSIG_Div_TemporarilyUnavailable, + ROSE_ERROR_QSIG_Div_NotAuthorized, + + /* Q.SIG SS-MWI-Operations */ + ROSE_ERROR_QSIG_InvalidMsgCentreId, + + /* Northern Telecom DMS-100 RLT related operations */ + ROSE_ERROR_DMS100_RLT_BridgeFail, + ROSE_ERROR_DMS100_RLT_CallIDNotFound, + ROSE_ERROR_DMS100_RLT_NotAllowed, + ROSE_ERROR_DMS100_RLT_SwitchEquipCongs, + + ROSE_ERROR_Num_Codes /*!< Must be last in the enumeration */ +}; + +#define ROSE_REJECT_BASE(base) ((base) * 0x100) +enum rose_reject_base { + ROSE_REJECT_BASE_General, + ROSE_REJECT_BASE_Invoke, + ROSE_REJECT_BASE_Result, + ROSE_REJECT_BASE_Error, + + /*! \brief Must be last in the list */ + ROSE_REJECT_BASE_Last +}; + +/*! + * \brief From Facility-Information-Element-Components + * {itu-t identified-organization etsi(0) 196 facility-information-element-component(3)} + */ +enum rose_reject_code { + /*! \brief Not rejected */ + ROSE_REJECT_None = -1, + /*! \brief Unknown reject code */ + ROSE_REJECT_Unknown = -2, + +/* *INDENT-OFF* */ + ROSE_REJECT_Gen_UnrecognizedComponent = ROSE_REJECT_BASE(ROSE_REJECT_BASE_General) + 0, + ROSE_REJECT_Gen_MistypedComponent = ROSE_REJECT_BASE(ROSE_REJECT_BASE_General) + 1, + ROSE_REJECT_Gen_BadlyStructuredComponent = ROSE_REJECT_BASE(ROSE_REJECT_BASE_General) + 2, + + ROSE_REJECT_Inv_DuplicateInvocation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 0, + ROSE_REJECT_Inv_UnrecognizedOperation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 1, + ROSE_REJECT_Inv_MistypedArgument = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 2, + ROSE_REJECT_Inv_ResourceLimitation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 3, + ROSE_REJECT_Inv_InitiatorReleasing = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 4, + ROSE_REJECT_Inv_UnrecognizedLinkedID = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 5, + ROSE_REJECT_Inv_LinkedResponseUnexpected = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 6, + ROSE_REJECT_Inv_UnexpectedChildOperation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) + 7, + + ROSE_REJECT_Res_UnrecognizedInvocation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result) + 0, + ROSE_REJECT_Res_ResultResponseUnexpected = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result) + 1, + ROSE_REJECT_Res_MistypedResult = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result) + 2, + + ROSE_REJECT_Err_UnrecognizedInvocation = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 0, + ROSE_REJECT_Err_ErrorResponseUnexpected = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 1, + ROSE_REJECT_Err_UnrecognizedError = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 2, + ROSE_REJECT_Err_UnexpectedError = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 3, + ROSE_REJECT_Err_MistypedParameter = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) + 4, +/* *INDENT-ON* */ +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * Q931InformationElement ::= [APPLICATION 0] IMPLICIT OCTET STRING + */ +struct roseQ931ie { + /*! + * \brief The Q.931 ie is present if length is nonzero. + * (If this field is optional in the message.) + */ + u_int8_t length; + + /*! + * \brief We mostly just need to store the contents so we will defer + * decoding/encoding. + * + * \note To reduce the size of the structure, the memory for the + * ie contents is "allocated" after the structure. + * \note Remember the "allocated" memory needs to have room for a + * null terminator. + */ + unsigned char contents[0]; +}; + +enum { + /*! Bearer Capability has a max length of 12. */ + ROSE_Q931_MAX_BC = 12, + /*! High Layer Compatibility has a max length of 5. */ + ROSE_Q931_MAX_HLC = 5, + /*! Low Layer Compatibility has a max length of 18. */ + ROSE_Q931_MAX_LLC = 18, + /*! + * User-User Information has a network dependent maximum. + * The network dependent maximum is either 35 or 131 octets + * in non-USER-INFORMATION messages. + */ + ROSE_Q931_MAX_USER = 131, + /*! + * Progress Indicator has a max length of 4. + * There can be multiple progress indicator ies. + * Q.SIG allows up to 3. + * ITU-T allows up to 2. + */ + ROSE_Q931_MAX_PROGRESS = 3 * 4, +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * PartyNumber ::= CHOICE { + * -- the numbering plan is the default numbering plan of + * -- the network. It is recommended that this value is + * -- used. + * unknownPartyNumber [0] IMPLICIT NumberDigits, + * + * -- the numbering plan is according to + * -- ITU-T Recommendation E.164. + * publicPartyNumber [1] IMPLICIT PublicPartyNumber, + * + * -- ATM endsystem address encoded as an NSAP address. + * nsapEncodedNumber [2] IMPLICIT NsapEncodedNumber, + * + * -- not used, value reserved. + * dataPartyNumber [3] IMPLICIT NumberDigits, + * + * -- not used, value reserved. + * telexPartyNumber [4] IMPLICIT NumberDigits, + * privatePartyNumber [5] IMPLICIT PrivatePartyNumber, + * + * -- not used, value reserved. + * nationalStandardPartyNumber [8] IMPLICIT NumberDigits + * } + */ +struct rosePartyNumber { + /*! + * \brief Party numbering plan + * \details + * unknown(0), + * public(1) - The numbering plan is according to ITU-T E.164, + * nsapEncoded(2), + * data(3) - Reserved, + * telex(4) - Reserved, + * private(5), + * nationalStandard(8) - Reserved + */ + u_int8_t plan; + + /*! + * \brief Type-Of-Number valid for public and private party number plans + * \details + * public: + * unknown(0), + * internationalNumber(1), + * nationalNumber(2), + * networkSpecificNumber(3) - Reserved, + * subscriberNumber(4) - Reserved, + * abbreviatedNumber(6) + * \details + * private: + * unknown(0), + * level2RegionalNumber(1), + * level1RegionalNumber(2), + * pTNSpecificNumber/pISNSpecificNumber(3), + * localNumber(4), + * abbreviatedNumber(6) + */ + u_int8_t ton; + + /*! \brief Number present if length is nonzero. */ + u_int8_t length; + + /*! \brief Number string data. */ + unsigned char str[20 + 1]; +}; + +/* + * NumberScreened ::= SEQUENCE { + * partyNumber PartyNumber, + * screeningIndicator ScreeningIndicator + * } + */ +struct roseNumberScreened { + struct rosePartyNumber number; + + /*! + * \details + * userProvidedNotScreened(0), + * userProvidedVerifiedAndPassed(1), + * userProvidedVerifiedAndFailed(2) (Not used, value reserved), + * networkProvided(3) + */ + u_int8_t screening_indicator; +}; + +/* + * PartySubaddress ::= CHOICE { + * -- not recommended + * UserSpecifiedSubaddress, + * + * -- according to ITU-T Recommendation X.213 + * NSAPSubaddress + * } + * + * UserSpecifiedSubaddress ::= SEQUENCE { + * SubaddressInformation, + * + * -- used when the coding of subaddress is BCD + * oddCountIndicator BOOLEAN OPTIONAL + * } + * + * -- specified according to ITU-T Recommendation X.213. Some + * -- networks may limit the subaddress value to some other + * -- length, e.g. 4 octets + * NSAPSubaddress ::= OCTET STRING (SIZE(1..20)) + * + * -- coded according to user requirements. Some networks may + * -- limit the subaddress value to some other length, + * -- e.g. 4 octets + * SubaddressInformation ::= OCTET STRING (SIZE(1..20)) + */ +struct rosePartySubaddress { + /*! \brief Subaddress type UserSpecified(0), NSAP(1) */ + u_int8_t type; + + /*! \brief Subaddress present if length is nonzero */ + u_int8_t length; + + union { + /*! \brief Specified according to ITU-T Recommendation X.213 */ + unsigned char nsap[20 + 1]; + + /*! \brief Use of this formatting is not recommended */ + struct { + /*! \brief TRUE if OddCount present */ + u_int8_t odd_count_present; + + /*! + * \brief TRUE if odd number of BCD digits (optional) + * \note Used when the coding of subaddress is BCD. + */ + u_int8_t odd_count; + unsigned char information[20 + 1]; + } user_specified; + } u; +}; + +/* + * Address ::= SEQUENCE { + * PartyNumber, + * PartySubaddress OPTIONAL + * } + */ +struct roseAddress { + struct rosePartyNumber number; + + /*! \brief Subaddress (Optional) */ + struct rosePartySubaddress subaddress; +}; + +/* + * AddressScreened ::= SEQUENCE { + * PartyNumber, + * ScreeningIndicator, + * PartySubaddress OPTIONAL + * } + */ +struct roseAddressScreened { + struct rosePartyNumber number; + + /*! \brief Subaddress (Optional) */ + struct rosePartySubaddress subaddress; + + /*! + * \details + * userProvidedNotScreened(0), + * userProvidedVerifiedAndPassed(1), + * userProvidedVerifiedAndFailed(2) (Not used, value reserved), + * networkProvided(3) + */ + u_int8_t screening_indicator; +}; + +/* + * PresentedNumberUnscreened ::= CHOICE { + * presentationAllowedNumber [0] EXPLICIT PartyNumber, + * presentationRestricted [1] IMPLICIT NULL, + * numberNotAvailableDueToInterworking [2] IMPLICIT NULL, + * presentationRestrictedNumber [3] EXPLICIT PartyNumber + * } + */ +struct rosePresentedNumberUnscreened { + struct rosePartyNumber number; + /*! + * \brief Number presentation type + * \details + * presentationAllowedNumber(0), + * presentationRestricted(1), + * numberNotAvailableDueToInterworking(2), + * presentationRestrictedNumber(3) + */ + u_int8_t presentation; +}; + +/* + * PresentedNumberScreened ::= CHOICE { + * presentationAllowedNumber [0] IMPLICIT NumberScreened, + * presentationRestricted [1] IMPLICIT NULL, + * numberNotAvailableDueToInterworking [2] IMPLICIT NULL, + * presentationRestrictedNumber [3] IMPLICIT NumberScreened + * } + */ +struct rosePresentedNumberScreened { + /*! \brief Screened number */ + struct roseNumberScreened screened; + /*! + * \brief Number presentation type + * \details + * presentationAllowedNumber(0), + * presentationRestricted(1), + * numberNotAvailableDueToInterworking(2), + * presentationRestrictedNumber(3) + */ + u_int8_t presentation; +}; + +/* + * PresentedAddressScreened ::= CHOICE { + * presentationAllowedAddress [0] IMPLICIT AddressScreened, + * presentationRestricted [1] IMPLICIT NULL, + * numberNotAvailableDueToInterworking [2] IMPLICIT NULL, + * presentationRestrictedAddress [3] IMPLICIT AddressScreened + * } + */ +struct rosePresentedAddressScreened { + /*! \breif Screened address */ + struct roseAddressScreened screened; + /*! + * \brief Address presentation type + * \details + * presentationAllowedAddress(0), + * presentationRestricted(1), + * numberNotAvailableDueToInterworking(2), + * presentationRestrictedAddress(3) + */ + u_int8_t presentation; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * Time ::= SEQUENCE { + * lengthOfTimeUnit [1] IMPLICIT LengthOfTimeUnit, + * scale [2] IMPLICIT Scale + * } + */ +struct roseEtsiAOCTime { + /*! LengthOfTimeUnit ::= INTEGER (0..16777215) -- 24 bit number */ + u_int32_t length; + /*! + * \details + * oneHundredthSecond(0), + * oneTenthSecond(1), + * oneSecond(2), + * tenSeconds(3), + * oneMinute(4), + * oneHour(5), + * twentyFourHours(6) + */ + u_int8_t scale; +}; + +/* + * Amount ::= SEQUENCE { + * currencyAmount [1] IMPLICIT CurrencyAmount, + * multiplier [2] IMPLICIT Multiplier + * } + */ +struct roseEtsiAOCAmount { + /*! CurrencyAmount ::= INTEGER (0..16777215) -- 24 bit number */ + u_int32_t currency; + /*! + * \details + * oneThousandth(0), + * oneHundredth(1), + * oneTenth(2), + * one(3), + * ten(4), + * hundred(5), + * thousand(6) + */ + u_int8_t multiplier; +}; + +/* + * DurationCurrency ::= SEQUENCE { + * dCurrency [1] IMPLICIT Currency, + * dAmount [2] IMPLICIT Amount, + * dChargingType [3] IMPLICIT ChargingType, + * dTime [4] IMPLICIT Time, + * dGranularity [5] IMPLICIT Time OPTIONAL + * } + */ +struct roseEtsiAOCDurationCurrency { + struct roseEtsiAOCAmount amount; + struct roseEtsiAOCTime time; + /*! \breif dGranularity (optional) */ + struct roseEtsiAOCTime granularity; + /*! Currency ::= IA5String (SIZE (1..10)) -- Name of currency. */ + unsigned char currency[10 + 1]; + /*! + * \details + * continuousCharging(0), + * stepFunction(1) + */ + u_int8_t charging_type; + /*! TRUE if granularity time is present */ + u_int8_t granularity_present; +}; + +/* + * FlatRateCurrency ::= SEQUENCE { + * fRCurrency [1] IMPLICIT Currency, + * fRAmount [2] IMPLICIT Amount + * } + */ +struct roseEtsiAOCFlatRateCurrency { + struct roseEtsiAOCAmount amount; + /*! Currency ::= IA5String (SIZE (1..10)) -- Name of currency. */ + unsigned char currency[10 + 1]; +}; + +/* + * VolumeRateCurrency ::= SEQUENCE { + * vRCurrency [1] IMPLICIT Currency, + * vRAmount [2] IMPLICIT Amount, + * vRVolumeUnit [3] IMPLICIT VolumeUnit + * } + */ +struct roseEtsiAOCVolumeRateCurrency { + struct roseEtsiAOCAmount amount; + /*! Currency ::= IA5String (SIZE (1..10)) -- Name of currency. */ + unsigned char currency[10 + 1]; + /*! + * \brief Volume rate volume unit + * \details + * octet(0), + * segment(1), + * message(2) + */ + u_int8_t unit; +}; + +/* + * AOCSCurrencyInfo ::= SEQUENCE { + * chargedItem ChargedItem, + * CHOICE { + * specialChargingCode SpecialChargingCode, + * durationCurrency [1] IMPLICIT DurationCurrency, + * flatRateCurrency [2] IMPLICIT FlatRateCurrency, + * volumeRateCurrency [3] IMPLICIT VolumeRateCurrency + * freeOfCharge [4] IMPLICIT NULL, + * currencyInfoNotAvailable [5] IMPLICIT NULL + * } + * } + */ +struct roseEtsiAOCSCurrencyInfo { + union { + struct roseEtsiAOCDurationCurrency duration; + struct roseEtsiAOCFlatRateCurrency flat_rate; + struct roseEtsiAOCVolumeRateCurrency volume_rate; + /*! SpecialChargingCode ::= INTEGER (1..10) */ + u_int8_t special_charging_code; + } u; + /*! + * \brief Determine what is stored in the union. + * \details + * specialChargingCode(0), + * durationCurrency(1), + * flatRateCurrency(2), + * volumeRateCurrency(3), + * freeOfCharge(4), + * currencyInfoNotAvailable(5), + */ + u_int8_t currency_type; + /*! + * \brief What service is being billed. + * \details + * basicCommunication(0), + * callAttempt(1), + * callSetup(2), + * userToUserInfo(3), + * operationOfSupplementaryServ(4) + */ + u_int8_t charged_item; +}; + +/* + * AOCSCurrencyInfoList ::= SEQUENCE SIZE (1..10) OF AOCSCurrencyInfo + */ +struct roseEtsiAOCSCurrencyInfoList { + /*! \brief SEQUENCE SIZE (1..10) OF AOCSCurrencyInfo */ + struct roseEtsiAOCSCurrencyInfo list[10]; + + /*! \brief Number of AOCSCurrencyInfo records present */ + u_int8_t num_records; +}; + +/* + * RecordedCurrency ::= SEQUENCE { + * rCurrency [1] IMPLICIT Currency, + * rAmount [2] IMPLICIT Amount + * } + */ +struct roseEtsiAOCRecordedCurrency { + /*! Amount of currency involved. */ + struct roseEtsiAOCAmount amount; + /*! Currency ::= IA5String (SIZE (1..10)) -- Name of currency. */ + unsigned char currency[10 + 1]; +}; + +/* + * RecordedUnits ::= SEQUENCE { + * CHOICE { + * recordedNumberOfUnits NumberOfUnits, + * notAvailable NULL + * }, + * recordedTypeOfUnits TypeOfUnit OPTIONAL + * } + */ +struct roseEtsiAOCRecordedUnits { + /*! \brief recordedNumberOfUnits INTEGER (0..16777215) -- 24 bit number */ + u_int32_t number_of_units; + /*! \brief TRUE if number_of_units is not available (not present) */ + u_int8_t not_available; + /*! \brief recordedTypeOfUnits INTEGER (1..16) (optional) */ + u_int8_t type_of_unit; + /*! \brief TRUE if type_of_unit is present */ + u_int8_t type_of_unit_present; +}; + +/* + * RecordedUnitsList ::= SEQUENCE SIZE (1..32) OF RecordedUnits + */ +struct roseEtsiAOCRecordedUnitsList { + /*! \brief SEQUENCE SIZE (1..32) OF RecordedUnits */ + struct roseEtsiAOCRecordedUnits list[32]; + + /*! \brief Number of RecordedUnits records present */ + u_int8_t num_records; +}; + +/* + * ChargingAssociation ::= CHOICE { + * chargedNumber [0] EXPLICIT PartyNumber, + * chargeIdentifier ChargeIdentifier + * } + */ +struct roseEtsiAOCChargingAssociation { + /*! chargeIdentifier: INTEGER (-32768..32767) -- 16 bit number */ + int16_t id; + /*! chargedNumber */ + struct rosePartyNumber number; + /*! + * \details + * charge_identifier(0), + * charged_number(1) + */ + u_int8_t type; +}; + +/* + * AOCECurrencyInfo ::= SEQUENCE { + * CHOICE { + * freeOfCharge [1] IMPLICIT NULL, + * specificCurrency SEQUENCE { + * recordedCurrency [1] IMPLICIT RecordedCurrency, + * aOCEBillingId [2] IMPLICIT AOCEBillingId OPTIONAL + * } + * }, + * chargingAssociation ChargingAssociation OPTIONAL + * } + */ +struct roseEtsiAOCECurrencyInfo { + struct { + /*! \brief recorded currency */ + struct roseEtsiAOCRecordedCurrency recorded; + /*! + * \brief AOCEBillingId (optional) + * \details + * normalCharging(0), + * reverseCharging(1), + * creditCardCharging(2), + * callForwardingUnconditional(3), + * callForwardingBusy(4), + * callForwardingNoReply(5), + * callDeflection(6), + * callTransfer(7) + */ + u_int8_t billing_id; + /*! \brief TRUE if billing id is present */ + u_int8_t billing_id_present; + } specific; + + /*! \brief chargingAssociation (optional) */ + struct roseEtsiAOCChargingAssociation charging_association; + + /*! \brief TRUE if charging_association is present */ + u_int8_t charging_association_present; + + /*! + * \brief TRUE if this is free of charge. + * \note When TRUE, the contents of specific are not valid. + */ + u_int8_t free_of_charge; +}; + +/* + * AOCEChargingUnitInfo ::= SEQUENCE { + * CHOICE { + * freeOfCharge [1] IMPLICIT NULL, + * specificChargingUnits SEQUENCE + * { + * recordedUnitsList [1] IMPLICIT RecordedUnitsList, + * aOCEBillingId [2] IMPLICIT AOCEBillingId OPTIONAL + * } + * }, + * chargingAssociation ChargingAssociation OPTIONAL + * } + */ +struct roseEtsiAOCEChargingUnitInfo { + /*! \brief Not valid if free_of_charge is TRUE */ + struct { + /*! \brief RecordedUnitsList */ + struct roseEtsiAOCRecordedUnitsList recorded; + /*! + * \brief AOCEBillingId (optional) + * \details + * normalCharging(0), + * reverseCharging(1), + * creditCardCharging(2), + * callForwardingUnconditional(3), + * callForwardingBusy(4), + * callForwardingNoReply(5), + * callDeflection(6), + * callTransfer(7) + */ + u_int8_t billing_id; + /*! \brief TRUE if billing id is present */ + u_int8_t billing_id_present; + } specific; + + /*! \brief chargingAssociation (optional) */ + struct roseEtsiAOCChargingAssociation charging_association; + + /*! \brief TRUE if charging_association is present */ + u_int8_t charging_association_present; + + /*! + * \brief TRUE if this is free of charge. + * \note When TRUE, the contents of specific are not valid. + */ + u_int8_t free_of_charge; +}; + +/* + * ARGUMENT ChargingCase + */ +struct roseEtsiChargingRequest_ARG { + /*! + * \details + * chargingInformationAtCallSetup(0), + * chargingDuringACall(1), + * chargingAtTheEndOfACall(2) + */ + u_int8_t charging_case; +}; + +/* + * RESULT CHOICE { + * AOCSCurrencyInfoList, + * AOCSSpecialArrInfo, + * chargingInfoFollows NULL + * } + */ +struct roseEtsiChargingRequest_RES { + union { + struct roseEtsiAOCSCurrencyInfoList currency_info; + + /*! AOCSSpecialArrInfo ::= INTEGER (1..10) */ + u_int8_t special_arrangement; + } u; + /*! + * \details + * currency_info_list(0), + * special_arrangement_info(1), + * charging_info_follows(2) + */ + u_int8_t type; +}; + +/* + * ARGUMENT CHOICE { + * chargeNotAvailable NULL, + * AOCSCurrencyInfoList + * } + */ +struct roseEtsiAOCSCurrency_ARG { + struct roseEtsiAOCSCurrencyInfoList currency_info; + /*! + * \details + * charge_not_available(0), + * currency_info_list(1) + */ + u_int8_t type; +}; + +/* + * ARGUMENT CHOICE { + * chargeNotAvailable NULL, + * AOCSSpecialArrInfo + * } + */ +struct roseEtsiAOCSSpecialArr_ARG { + /*! + * \details + * charge_not_available(0), + * special_arrangement_info(1) + */ + u_int8_t type; + /*! AOCSSpecialArrInfo ::= INTEGER (1..10) */ + u_int8_t special_arrangement; +}; + +/* + * ARGUMENT CHOICE { + * chargeNotAvailable NULL, + * aOCDCurrencyInfo CHOICE { + * freeOfCharge [1] IMPLICIT NULL, + * specificCurrency SEQUENCE { + * recordedCurrency [1] IMPLICIT RecordedCurrency, + * typeOfChargingInfo [2] IMPLICIT TypeOfChargingInfo, + * aOCDBillingId [3] IMPLICIT AOCDBillingId OPTIONAL + * } + * } + * } + */ +struct roseEtsiAOCDCurrency_ARG { + struct { + /*! \brief recorded currency */ + struct roseEtsiAOCRecordedCurrency recorded; + /*! + * \brief Type of recorded charging information. + * \details + * subTotal(0), + * total(1) + */ + u_int8_t type_of_charging_info; + /*! + * \brief AOCDBillingId (optional) + * \details + * normalCharging(0), + * reverseCharging(1), + * creditCardCharging(2) + */ + u_int8_t billing_id; + /*! \brief TRUE if billing id is present */ + u_int8_t billing_id_present; + } specific; + /*! + * \details + * charge_not_available(0), + * free_of_charge(1), + * specific_currency(2) + */ + u_int8_t type; +}; + +/* + * ARGUMENT CHOICE { + * chargeNotAvailable NULL, + * aOCDChargingUnitInfo CHOICE { + * freeOfCharge [1] IMPLICIT NULL, + * specificChargingUnits SEQUENCE { + * recordedUnitsList [1] IMPLICIT RecordedUnitsList, + * typeOfChargingInfo [2] IMPLICIT TypeOfChargingInfo, + * aOCDBillingId [3] IMPLICIT AOCDBillingId OPTIONAL + * } + * } + * } + */ +struct roseEtsiAOCDChargingUnit_ARG { + struct { + /*! \brief RecordedUnitsList */ + struct roseEtsiAOCRecordedUnitsList recorded; + /*! + * \brief Type of recorded charging information. + * \details + * subTotal(0), + * total(1) + */ + u_int8_t type_of_charging_info; + /*! + * \brief AOCDBillingId (optional) + * \details + * normalCharging(0), + * reverseCharging(1), + * creditCardCharging(2) + */ + u_int8_t billing_id; + /*! \brief TRUE if billing id is present */ + u_int8_t billing_id_present; + } specific; + /*! + * \details + * charge_not_available(0), + * free_of_charge(1), + * specific_charging_units(2) + */ + u_int8_t type; +}; + +/* + * ARGUMENT CHOICE { + * chargeNotAvailable NULL, + * AOCECurrencyInfo + * } + */ +struct roseEtsiAOCECurrency_ARG { + struct roseEtsiAOCECurrencyInfo currency_info; + /*! + * \details + * charge_not_available(0), + * currency_info(1) + */ + u_int8_t type; +}; + +/* + * ARGUMENT CHOICE { + * chargeNotAvailable NULL, + * AOCEChargingUnitInfo + * } + */ +struct roseEtsiAOCEChargingUnit_ARG { + struct roseEtsiAOCEChargingUnitInfo charging_unit; + /*! + * \details + * charge_not_available(0), + * charging_unit(1) + */ + u_int8_t type; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * forwardedToAddress Address, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiActivationDiversion_ARG { + /*! \brief Forwarded to address */ + struct roseAddress forwarded_to; + + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiDeactivationDiversion_ARG { + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * forwardedToAddresss Address, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiActivationStatusNotificationDiv_ARG { + /*! \brief Forwarded to address */ + struct roseAddress forwarded_to; + + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiDeactivationStatusNotificationDiv_ARG { + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService DEFAULT allServices, + * servedUserNr ServedUserNr + * } + */ +struct roseEtsiInterrogationDiversion_ARG { + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * DEFAULT allServices + * + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + +/* + * IntResult ::= SEQUENCE { + * servedUserNr ServedUserNr, + * basicService BasicService, + * procedure Procedure, + * forwardedToAddress Address + * } + */ +struct roseEtsiForwardingRecord { + /*! \brief Forwarded to address */ + struct roseAddress forwarded_to; + + /*! \brief Forward all numbers if not present (Number length is zero). */ + struct rosePartyNumber served_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + +/* + * roseInterrogationDiversion_RES + * IntResultList ::= SET SIZE (0..29) OF IntResult + */ +struct roseEtsiForwardingList { + /*! + * \brief SET SIZE (0..29) OF Forwarding Records + * \note Reduced the size of the array to conserve + * potential stack usage. + */ + struct roseEtsiForwardingRecord list[10]; + + /*! \brief Number of Forwarding records present */ + u_int8_t num_records; +}; + +/* + * ARGUMENT SEQUENCE { + * diversionReason DiversionReason, + * basicService BasicService, + * servedUserSubaddress PartySubaddress OPTIONAL, + * callingAddress [0] EXPLICIT PresentedAddressScreened OPTIONAL, + * originalCalledNr [1] EXPLICIT PresentedNumberUnscreened OPTIONAL, + * lastDivertingNr [2] EXPLICIT PresentedNumberUnscreened OPTIONAL, + * lastDivertingReason [3] EXPLICIT DiversionReason OPTIONAL, + * + * -- The User-user information element, as specified + * -- in ETS 300 102-1 [11], subclause 4.5.29, shall + * -- be embedded in the userInfo parameter. + * userInfo Q931InformationElement OPTIONAL + * } + */ +struct roseEtsiDiversionInformation_ARG { + /*! \brief Served user subaddress (Optional) */ + struct rosePartySubaddress served_user_subaddress; + + /*! \brief Calling address (Optional) */ + struct rosePresentedAddressScreened calling; + + /*! \brief Original called number (Optional) */ + struct rosePresentedNumberUnscreened original_called; + + /*! \brief Last diverting number (Optional) */ + struct rosePresentedNumberUnscreened last_diverting; + + /*! \brief User-User information embedded in Q.931 IE (Optional) */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_USER + 1]; + + /*! + * \brief Last diverting reason (Optional) + * + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t last_diverting_reason; + + /*! \brief TRUE if CallingAddress is present */ + u_int8_t calling_present; + + /*! \brief TRUE if OriginalCalled is present */ + u_int8_t original_called_present; + + /*! \brief TRUE if LastDiverting is present */ + u_int8_t last_diverting_present; + + /*! \brief TRUE if LastDivertingReason is present */ + u_int8_t last_diverting_reason_present; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t diversion_reason; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3k1Hz(3), + * unrestrictedDigitalInformationWithTonesAndAnnouncements(4), + * multirate(5), + * telephony3k1Hz(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * telephony7kHz(38), + * euroFileTransfer(39), + * fileTransferAndAccessManagement(40), + * videoconference(41), + * audioGraphicConference(42) + */ + u_int8_t basic_service; +}; + + +/* + * ARGUMENT SEQUENCE { + * deflectionAddress Address, + * presentationAllowedDivertedToUser PresentationAllowedIndicator OPTIONAL + * } + */ +struct roseEtsiCallDeflection_ARG { + /*! \brief Deflection address (Deflected-To address) */ + struct roseAddress deflection; + + /*! \brief TRUE if PresentationAllowedToDivertedToUser is present */ + u_int8_t presentation_allowed_to_diverted_to_user_present; + + /*! \brief TRUE if presentation is allowed (Optional) */ + u_int8_t presentation_allowed_to_diverted_to_user; +}; + + +/* + * ARGUMENT SEQUENCE { + * reroutingReason DiversionReason, + * calledAddress Address, + * reroutingCounter DiversionCounter, + * + * -- The User-user information element (optional), + * -- High layer compatibility information element (optional), + * -- Bearer capability information element + * -- and Low layer compatibility information element (optional) + * -- as specified in ETS 300 102-1 [11] subclause 4.5 shall be + * -- embedded in the q931InfoElement. + * q931InfoElement Q931InformationElement, + * lastReroutingNr [1] EXPLICIT PresentedNumberUnscreened, + * subscriptionOption [2] EXPLICIT SubscriptionOption DEFAULT noNotification, + * callingPartySubaddress [3] EXPLICIT PartySubaddress OPTIONAL + * } + */ +struct roseEtsiCallRerouting_ARG { + struct roseAddress called_address; + + /*! \brief The BC, HLC (optional), LLC (optional), and User-user (optional) information */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + ROSE_Q931_MAX_USER + 1]; + + /*! \brief Last rerouting number */ + struct rosePresentedNumberUnscreened last_rerouting; + + /*! \brief Calling party subaddress (Optional) */ + struct rosePartySubaddress calling_subaddress; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t rerouting_reason; + + /*! \brief Range 1-5 */ + u_int8_t rerouting_counter; + + /*! + * \details + * DEFAULT noNotification + * + * \details + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + */ + u_int8_t subscription_option; +}; + + +/* + * roseInterrogateServedUserNumbers_RES + * ServedUserNumberList ::= SET SIZE (0..99) OF PartyNumber + */ +struct roseEtsiServedUserNumberList { + /*! + * \brief SET SIZE (0..99) OF Served user numbers + * \note Reduced the size of the array to conserve + * potential stack usage. + */ + struct rosePartyNumber number[20]; + + /*! \brief Number of Served user numbers present */ + u_int8_t num_records; +}; + + +/* + * ARGUMENT SEQUENCE { + * diversionReason DiversionReason, + * subscriptionOption SubscriptionOption, + * divertedToNumber PresentedNumberUnscreened OPTIONAL + * } + */ +struct roseEtsiDivertingLegInformation1_ARG { + /*! \brief Diverted to number (Optional) */ + struct rosePresentedNumberUnscreened diverted_to; + + /*! \brief TRUE if DivertedTo is present */ + u_int8_t diverted_to_present; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t diversion_reason; + + /*! + * \details + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + */ + u_int8_t subscription_option; +}; + +/* + * ARGUMENT SEQUENCE { + * diversionCounter DiversionCounter, + * diversionReason DiversionReason, + * divertingNr [1] EXPLICIT PresentedNumberUnscreened OPTIONAL, + * originalCalledNr [2] EXPLICIT PresentedNumberUnscreened OPTIONAL + * } + */ +struct roseEtsiDivertingLegInformation2_ARG { + /*! \brief Diverting number (Optional) */ + struct rosePresentedNumberUnscreened diverting; + + /*! \brief Original called number (Optional) */ + struct rosePresentedNumberUnscreened original_called; + + /*! \brief TRUE if Diverting number is present */ + u_int8_t diverting_present; + + /*! \brief TRUE if OriginalCalled is present */ + u_int8_t original_called_present; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3), + * cdAlerting(4), + * cdImmediate(5) + */ + u_int8_t diversion_reason; + + /*! \brief Range 1-5 */ + u_int8_t diversion_counter; +}; + +/* + * ARGUMENT presentationAllowedIndicator PresentationAllowedIndicator + */ +struct roseEtsiDivertingLegInformation3_ARG { + /*! \brief TRUE if presentation is allowed */ + u_int8_t presentation_allowed_indicator; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * ARGUMENT LinkId + */ +struct roseEtsiExplicitEctExecute_ARG { + int16_t link_id; +}; + +/* + * ARGUMENT transferredToSubaddress PartySubaddress + */ +struct roseEtsiSubaddressTransfer_ARG { + /*! \brief Transferred to subaddress */ + struct rosePartySubaddress subaddress; +}; + + +/* + * RESULT LinkId + */ +struct roseEtsiEctLinkIdRequest_RES { + int16_t link_id; +}; + + +/* + * ARGUMENT SEQUENCE { + * ENUMERATED { + * alerting (0), + * active (1) + * }, + * redirectionNumber PresentedNumberUnscreened OPTIONAL + * } + */ +struct roseEtsiEctInform_ARG { + /*! \brief Redirection Number (Optional) */ + struct rosePresentedNumberUnscreened redirection; + + /*! \brief TRUE if the Redirection Number is present */ + u_int8_t redirection_present; + + /*! \details alerting(0), active(1) */ + u_int8_t status; +}; + + +/* + * ARGUMENT CallTransferIdentity + */ +struct roseEtsiEctLoopTest_ARG { + int8_t call_transfer_id; +}; + +/* + * RESULT LoopResult + */ +struct roseEtsiEctLoopTest_RES { + /*! + * \details + * insufficientInformation(0), + * noLoopExists(1), + * simultaneousTransfer(2) + */ + u_int8_t loop_result; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * Name ::= CHOICE { + * -- iso8859-1 is implied in namePresentationAllowedSimple. + * namePresentationAllowedSimple [0] IMPLICIT NameData, + * namePresentationAllowedExtended [1] IMPLICIT NameSet, + * + * -- iso8859-1 is implied in namePresentationRestrictedSimple. + * namePresentationRestrictedSimple [2] IMPLICIT NameData, + * namePresentationRestrictedExtended [3] IMPLICIT NameSet, + * + * -- namePresentationRestrictedNull shall only be used in the + * -- case of interworking where the other network provides an + * -- indication that the name is restricted without the name itself. + * namePresentationRestrictedNull [7] IMPLICIT NULL, + * + * nameNotAvailable [4] IMPLICIT NULL + * } + * + * NameSet ::= SEQUENCE { + * nameData NameData, + * + * -- If characterSet is not included, iso8859-1 is implied. + * characterSet CharacterSet OPTIONAL -- DEFAULT iso8859-1 + * } + * + * -- The maximum allowed size of the name field is 50 octets. + * -- The minimum required size of the name field is 1 octet. + * NameData ::= OCTET STRING (SIZE (1..50)) + */ +struct roseQsigName { + /*! + * \details + * optional_name_not_present(0), + * presentation_allowed(1), + * presentation_restricted(2), + * presentation_restricted_null(3), + * name_not_available(4) + */ + u_int8_t presentation; + + /*! + * \details + * Set to iso8859-1 if not present in the encoding. + * + * \details + * unknown(0), + * iso8859-1(1), + * enum-value-withdrawn-by-ITU-T(2) + * iso8859-2(3), + * iso8859-3(4), + * iso8859-4(5), + * iso8859-5(6), + * iso8859-7(7), + * iso10646-BmpString(8), + * iso10646-utf-8String(9) + */ + u_int8_t char_set; + + /*! \brief Length of name data */ + u_int8_t length; + + /*! \brief Name string data */ + unsigned char data[50 + 1]; +}; + +/* + * NOTE: We are not going to record the Extension information + * since it is manufacturer specific. + * + * ARGUMENT CHOICE { + * Name, + * SEQUENCE { + * Name, + * CHOICE { + * [5] IMPLICIT Extension, + * [6] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + * } + */ +struct roseQsigPartyName_ARG { + struct roseQsigName name; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * Time ::= SEQUENCE { + * lengthOfTimeUnit [1] IMPLICIT LengthOfTimeUnit, + * scale [2] IMPLICIT Scale + * } + */ +struct roseQsigAOCTime { + /*! LengthOfTimeUnit ::= INTEGER (0..16777215) -- 24 bit number */ + u_int32_t length; + /*! + * \details + * oneHundredthSecond(0), + * oneTenthSecond(1), + * oneSecond(2), + * tenSeconds(3), + * oneMinute(4), + * oneHour(5), + * twentyFourHours(6) + */ + u_int8_t scale; +}; + +/* + * Amount ::= SEQUENCE { + * currencyAmount [1] IMPLICIT CurrencyAmount, + * multiplier [2] IMPLICIT Multiplier + * } + */ +struct roseQsigAOCAmount { + /*! CurrencyAmount ::= INTEGER (0..16777215) -- 24 bit number */ + u_int32_t currency; + /*! + * \details + * oneThousandth(0), + * oneHundredth(1), + * oneTenth(2), + * one(3), + * ten(4), + * hundred(5), + * thousand(6) + */ + u_int8_t multiplier; +}; + +/* + * DurationCurrency ::= SEQUENCE { + * dCurrency [1] IMPLICIT Currency, + * dAmount [2] IMPLICIT Amount, + * dChargingType [3] IMPLICIT ChargingType, + * dTime [4] IMPLICIT Time, + * dGranularity [5] IMPLICIT Time OPTIONAL + * } + */ +struct roseQsigAOCDurationCurrency { + struct roseQsigAOCAmount amount; + struct roseQsigAOCTime time; + /*! \brief dGranularity (optional) */ + struct roseQsigAOCTime granularity; + /*! + * \brief Name of currency + * \details + * Currency ::= IA5String (SIZE (0..10)) + * \note The empty string is the default currency for the network. + */ + unsigned char currency[10 + 1]; + /*! + * \details + * continuousCharging(0), + * stepFunction(1) + */ + u_int8_t charging_type; + /*! TRUE if granularity time is present */ + u_int8_t granularity_present; +}; + +/* + * FlatRateCurrency ::= SEQUENCE { + * fRCurrency [1] IMPLICIT Currency, + * fRAmount [2] IMPLICIT Amount + * } + */ +struct roseQsigAOCFlatRateCurrency { + struct roseQsigAOCAmount amount; + /*! + * \brief Name of currency + * \details + * Currency ::= IA5String (SIZE (0..10)) + * \note The empty string is the default currency for the network. + */ + unsigned char currency[10 + 1]; +}; + +/* + * VolumeRateCurrency ::= SEQUENCE { + * vRCurrency [1] IMPLICIT Currency, + * vRAmount [2] IMPLICIT Amount, + * vRVolumeUnit [3] IMPLICIT VolumeUnit + * } + */ +struct roseQsigAOCVolumeRateCurrency { + struct roseQsigAOCAmount amount; + /*! + * \brief Name of currency + * \details + * Currency ::= IA5String (SIZE (0..10)) + * \note The empty string is the default currency for the network. + */ + unsigned char currency[10 + 1]; + /*! + * \brief Volume rate volume unit + * \details + * octet(0), + * segment(1), + * message(2) + */ + u_int8_t unit; +}; + +/* + * AOCSCurrencyInfo ::= SEQUENCE { + * chargedItem ChargedItem, + * rateType CHOICE { + * durationCurrency [1] IMPLICIT DurationCurrency, + * flatRateCurrency [2] IMPLICIT FlatRateCurrency, + * volumeRateCurrency [3] IMPLICIT VolumeRateCurrency, + * specialChargingCode SpecialChargingCode, + * freeOfCharge [4] IMPLICIT NULL, + * currencyInfoNotAvailable [5] IMPLICIT NULL, + * freeOfChargefromBeginning [6] IMPLICIT NULL + * } + * } + */ +struct roseQsigAOCSCurrencyInfo { + union { + struct roseQsigAOCDurationCurrency duration; + struct roseQsigAOCFlatRateCurrency flat_rate; + struct roseQsigAOCVolumeRateCurrency volume_rate; + /*! SpecialChargingCode ::= INTEGER (1..10) */ + u_int8_t special_charging_code; + } u; + /*! + * \brief Determine what is stored in the union. + * \details + * specialChargingCode(0), + * durationCurrency(1), + * flatRateCurrency(2), + * volumeRateCurrency(3), + * freeOfCharge(4), + * currencyInfoNotAvailable(5), + * freeOfChargeFromBeginning(6) + */ + u_int8_t currency_type; + /*! + * \brief What service is being billed. + * \details + * basicCommunication(0), + * callAttempt(1), + * callSetup(2), + * userToUserInfo(3), + * operationOfSupplementaryServ(4) + */ + u_int8_t charged_item; +}; + +/* + * AOCSCurrencyInfoList ::= SEQUENCE SIZE (1..10) OF AOCSCurrencyInfo + */ +struct roseQsigAOCSCurrencyInfoList { + /*! \brief SEQUENCE SIZE (1..10) OF AOCSCurrencyInfo */ + struct roseQsigAOCSCurrencyInfo list[10]; + + /*! \brief Number of AOCSCurrencyInfo records present */ + u_int8_t num_records; +}; + +/* + * RecordedCurrency ::= SEQUENCE { + * rCurrency [1] IMPLICIT Currency, + * rAmount [2] IMPLICIT Amount + * } + */ +struct roseQsigAOCRecordedCurrency { + /*! Amount of currency involved. */ + struct roseQsigAOCAmount amount; + /*! + * \brief Name of currency + * \details + * Currency ::= IA5String (SIZE (0..10)) + * \note The empty string is the default currency for the network. + */ + unsigned char currency[10 + 1]; +}; + +/* + * ChargingAssociation ::= CHOICE { + * chargedNumber [0] EXPLICIT PartyNumber, + * chargeIdentifier ChargeIdentifier + * } + */ +struct roseQsigAOCChargingAssociation { + /*! chargeIdentifier: INTEGER (-32768..32767) -- 16 bit number */ + int16_t id; + /*! chargedNumber */ + struct rosePartyNumber number; + /*! + * \details + * charge_identifier(0) + * charged_number(1), + */ + u_int8_t type; +}; + +/* + * AocRateArg ::= SEQUENCE { + * aocRate CHOICE + * { + * chargeNotAvailable NULL, + * aocSCurrencyInfoList AOCSCurrencyInfoList + * }, + * rateArgExtension CHOICE { + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigAocRateArg_ARG { + struct roseQsigAOCSCurrencyInfoList currency_info; + /*! + * \details + * charge_not_available(0), + * currency_info_list(1) + */ + u_int8_t type; +}; + +/* + * AocInterimArg ::= SEQUENCE { + * interimCharge CHOICE { + * chargeNotAvailable [0] IMPLICIT NULL, + * freeOfCharge [1] IMPLICIT NULL, + * specificCurrency SEQUENCE { + * recordedCurrency [1] IMPLICIT RecordedCurrency, + * interimBillingId [2] IMPLICIT InterimBillingId OPTIONAL + * } + * }, + * interimArgExtension CHOICE { + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigAocInterimArg_ARG { + struct { + /*! \brief recorded currency */ + struct roseQsigAOCRecordedCurrency recorded; + /*! + * \brief InterimBillingId (optional) + * \details + * normalCharging(0), + * creditCardCharging(2) + */ + u_int8_t billing_id; + /*! \brief TRUE if billing id is present */ + u_int8_t billing_id_present; + } specific; + /*! + * \details + * charge_not_available(0), + * free_of_charge(1), + * specific_currency(2) + */ + u_int8_t type; +}; + +/* + * AocFinalArg ::= SEQUENCE { + * finalCharge CHOICE { + * chargeNotAvailable [0] IMPLICIT NULL, + * freeOfCharge [1] IMPLICIT NULL, + * specificCurrency SEQUENCE { + * recordedCurrency [1] IMPLICIT RecordedCurrency, + * finalBillingId [2] IMPLICIT FinalBillingId OPTIONAL + * } + * }, + * chargingAssociation ChargingAssociation OPTIONAL, + * finalArgExtension CHOICE { + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigAocFinalArg_ARG { + struct { + /*! \brief recorded currency */ + struct roseQsigAOCRecordedCurrency recorded; + /*! + * \brief FinalBillingId (optional) + * \details + * normalCharging(0), + * creditCardCharging(2), + * callForwardingUnconditional(3), + * callForwardingBusy(4), + * callForwardingNoReply(5), + * callDeflection(6), + * callTransfer(7) + */ + u_int8_t billing_id; + /*! \brief TRUE if billing id is present */ + u_int8_t billing_id_present; + } specific; + + /*! \brief chargingAssociation (optional) */ + struct roseQsigAOCChargingAssociation charging_association; + + /*! \brief TRUE if charging_association is present */ + u_int8_t charging_association_present; + + /*! + * \details + * charge_not_available(0), + * free_of_charge(1), + * specific_currency(2) + */ + u_int8_t type; +}; + +/* + * ChargeRequestArg ::= SEQUENCE { + * adviceModeCombinations SEQUENCE SIZE(0..7) OF AdviceModeCombination, + * chargeReqArgExtension CHOICE { + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigChargeRequestArg_ARG { + /*! + * \brief SEQUENCE SIZE(0..7) OF AdviceModeCombination + * \details + * rate(0) , + * rateInterim(1) , + * rateFinal(2) , + * interim(3) , + * final(4) , + * interimFinal(5) , + * rateInterimFinal(6) + */ + u_int8_t advice_mode_combinations[7]; + + /*! \brief Number of AdviceModeCombination values present */ + u_int8_t num_records; +}; + +/* + * ChargeRequestRes ::= SEQUENCE { + * adviceModeCombination AdviceModeCombination, + * chargeReqResExtension CHOICE { + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigChargeRequestRes_RES { + /*! + * \details + * rate(0) , + * rateInterim(1) , + * rateFinal(2) , + * interim(3) , + * final(4) , + * interimFinal(5) , + * rateInterimFinal(6) + */ + u_int8_t advice_mode_combination; +}; + +/* + * AocCompleteArg ::= SEQUENCE { + * chargedUser PartyNumber, + * chargingAssociation ChargingAssociation OPTIONAL, + * completeArgExtension CHOICE { + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigAocCompleteArg_ARG { + /*! \brief chargingAssociation (optional) */ + struct roseQsigAOCChargingAssociation charging_association; + + struct rosePartyNumber charged_user_number; + + /*! \brief TRUE if charging_association is present */ + u_int8_t charging_association_present; +}; + +/* + * AocCompleteRes ::= SEQUENCE { + * chargingOption ChargingOption, + * completeResExtension CHOICE { + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigAocCompleteRes_RES { + /*! + * \details + * aocFreeOfCharge(0), + * aocContinueCharging(1), + * aocStopCharging(2) + */ + u_int8_t charging_option; +}; + +/* + * AocDivChargeReqArg ::= SEQUENCE { + * divertingUser PartyNumber, + * chargingAssociation ChargingAssociation OPTIONAL, + * diversionType DiversionType, + * aocDivChargeReqArgExt CHOICE { + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigAocDivChargeReqArg_ARG { + /*! \brief chargingAssociation (optional) */ + struct roseQsigAOCChargingAssociation charging_association; + + struct rosePartyNumber diverting_user_number; + + /*! \brief TRUE if charging_association is present */ + u_int8_t charging_association_present; + + /*! + * \details + * callForwardingUnconditional(0), + * callForwardingBusy(1), + * callForwardingNoReply(2), + * callDeflection(3) + */ + u_int8_t diversion_type; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * CTIdentifyRes ::= SEQUENCE { + * callIdentity CallIdentity, + * reroutingNumber PartyNumber, + * resultExtension CHOICE { + * [6] IMPLICIT Extension, + * [7] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigCTIdentifyRes_RES { + struct rosePartyNumber rerouting_number; + + /*! \brief CallIdentity ::= NumericString (SIZE (1..4)) */ + unsigned char call_id[4 + 1]; +}; + +/* + * CTInitiateArg ::= SEQUENCE { + * callIdentity CallIdentity, + * reroutingNumber PartyNumber, + * argumentExtension CHOICE { + * [6] IMPLICIT Extension, + * [7] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigCTInitiateArg_ARG { + struct rosePartyNumber rerouting_number; + + /*! \brief CallIdentity ::= NumericString (SIZE (1..4)) */ + unsigned char call_id[4 + 1]; +}; + +/* + * CTSetupArg ::= SEQUENCE { + * callIdentity CallIdentity, + * argumentExtension CHOICE { + * [0] IMPLICIT Extension, + * [1] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigCTSetupArg_ARG { + /*! \brief CallIdentity ::= NumericString (SIZE (1..4)) */ + unsigned char call_id[4 + 1]; +}; + +/* + * CTActiveArg ::= SEQUENCE { + * connectedAddress PresentedAddressScreened, + * + * -- ISO/IEC 11572 information elements Party + * -- category and Progress indicator are conveyed + * basicCallInfoElements PSS1InformationElement OPTIONAL, + * connectedName Name OPTIONAL, + * argumentExtension CHOICE { + * [9] IMPLICIT Extension, + * [10] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigCTActiveArg_ARG { + /*! \brief connectedAddress */ + struct rosePresentedAddressScreened connected; + + /*! \brief basicCallInfoElements (optional) */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_PROGRESS + 1]; + + /*! \brief connectedName (optional) */ + struct roseQsigName connected_name; + + /*! \brief TRUE if connected_name is present */ + u_int8_t connected_name_present; +}; + +/* + * CTCompleteArg ::= SEQUENCE { + * endDesignation EndDesignation, + * redirectionNumber PresentedNumberScreened, + * + * -- ISO/IEC 11572 information elements Party + * -- category and Progress indicator are conveyed + * basicCallInfoElements PSS1InformationElement OPTIONAL, + * redirectionName Name OPTIONAL, + * callStatus CallStatus DEFAULT answered, + * argumentExtension CHOICE { + * [9] IMPLICIT Extension, + * [10] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigCTCompleteArg_ARG { + /*! \brief redirectionNumber */ + struct rosePresentedNumberScreened redirection; + + /*! \brief basicCallInfoElements (optional) */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_PROGRESS + 1]; + + /*! \brief redirectionName (optional) */ + struct roseQsigName redirection_name; + + /*! \brief TRUE if redirection_name is present */ + u_int8_t redirection_name_present; + + /*! + * \brief endDesignation + * \details + * primaryEnd(0), + * secondaryEnd(1) + */ + u_int8_t end_designation; + + /*! + * \brief callStatus + * \details + * DEFAULT answered + * + * \details + * answered(0), + * alerting(1) + */ + u_int8_t call_status; +}; + +/* + * CTUpdateArg ::= SEQUENCE { + * redirectionNumber PresentedNumberScreened, + * redirectionName Name OPTIONAL, + * + * -- ISO/IEC 11572 information elements Party + * -- category and Progress indicator are conveyed + * basicCallInfoElements PSS1InformationElement OPTIONAL, + * argumentExtension CHOICE { + * [9] IMPLICIT Extension, + * [10] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigCTUpdateArg_ARG { + /*! \brief redirectionNumber */ + struct rosePresentedNumberScreened redirection; + + /*! \brief basicCallInfoElements (optional) */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_PROGRESS + 1]; + + /*! \brief redirectionName (optional) */ + struct roseQsigName redirection_name; + + /*! \brief TRUE if redirection_name is present */ + u_int8_t redirection_name_present; +}; + +/* + * SubaddressTransferArg ::= SEQUENCE { + * redirectionSubaddress PartySubaddress, + * argumentExtension CHOICE { + * [0] IMPLICIT Extension, + * [1] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigSubaddressTransferArg_ARG { + struct rosePartySubaddress redirection_subaddress; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * IntResult ::= SEQUENCE { + * servedUserNr PartyNumber, + * basicService BasicService, + * procedure Procedure, + * divertedToAddress Address, + * remoteEnabled BOOLEAN DEFAULT FALSE, + * extension CHOICE { + * [1] IMPLICIT Extension, + * [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigForwardingRecord { + /*! \brief Diverted to address */ + struct roseAddress diverted_to; + + struct rosePartyNumber served_user_number; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3100Hz(3), + * telephony(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36) + */ + u_int8_t basic_service; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! \brief remoteEnabled BOOLEAN DEFAULT FALSE */ + u_int8_t remote_enabled; +}; + +/* + * roseQsigInterrogateDiversionQ_REQ + * IntResultList ::= SET SIZE (0..29) OF IntResult + */ +struct roseQsigForwardingList { + /*! + * \brief SET SIZE (0..29) OF Forwarding Records + * \note Reduced the size of the array to conserve + * potential stack usage. + */ + struct roseQsigForwardingRecord list[10]; + + /*! \brief Number of Forwarding records present */ + u_int8_t num_records; +}; + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * divertedToAddress Address, + * servedUserNr PartyNumber, + * activatingUserNr PartyNumber, + * extension CHOICE { + * [1] IMPLICIT Extension, + * [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigActivateDiversionQ_ARG { + /*! \brief divertedToAddress */ + struct roseAddress diverted_to; + + struct rosePartyNumber served_user_number; + struct rosePartyNumber activating_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3100Hz(3), + * telephony(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36) + */ + u_int8_t basic_service; +}; + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService, + * servedUserNr PartyNumber, + * deactivatingUserNr PartyNumber, + * extension CHOICE { + * [1] IMPLICIT Extension, + * [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigDeactivateDiversionQ_ARG { + struct rosePartyNumber served_user_number; + struct rosePartyNumber deactivating_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3100Hz(3), + * telephony(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36) + */ + u_int8_t basic_service; +}; + +/* + * ARGUMENT SEQUENCE { + * procedure Procedure, + * basicService BasicService DEFAULT allServices, + * servedUserNr PartyNumber, + * interrogatingUserNr PartyNumber, + * extension CHOICE { + * [1] IMPLICIT Extension, + * [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigInterrogateDiversionQ_ARG { + struct rosePartyNumber served_user_number; + struct rosePartyNumber interrogating_user_number; + + /*! \details cfu(0), cfb(1), cfnr(2) */ + u_int8_t procedure; + + /*! + * \details + * DEFAULT allServices + * + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3100Hz(3), + * telephony(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36) + */ + u_int8_t basic_service; +}; + +/* + * ARGUMENT SEQUENCE { + * servedUserNr PartyNumber, + * basicService BasicService, + * divertedToNr PartyNumber, + * extension CHOICE { + * [1] IMPLICIT Extension, + * [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigCheckRestriction_ARG { + struct rosePartyNumber served_user_number; + struct rosePartyNumber diverted_to_number; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3100Hz(3), + * telephony(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotexSyntaxBased(35), + * videotelephony(36) + */ + u_int8_t basic_service; +}; + +/* + * ARGUMENT SEQUENCE { + * reroutingReason DiversionReason, + * originalReroutingReason [0] IMPLICIT DiversionReason OPTIONAL, + * calledAddress Address, + * diversionCounter INTEGER (1..15), + * + * -- The basic call information elements Bearer capability, + * -- High layer compatibility, Low layer compatibity + * -- and Progress indicator can be embedded in the + * -- pSS1InfoElement in accordance with 6.5.3.1.5. + * pSS1InfoElement PSS1InformationElement, + * lastReroutingNr [1] EXPLICIT PresentedNumberUnscreened, + * subscriptionOption [2] IMPLICIT SubscriptionOption, + * callingPartySubaddress [3] EXPLICIT PartySubaddress OPTIONAL, + * callingNumber [4] EXPLICIT PresentedNumberScreened, + * callingName [5] EXPLICIT Name OPTIONAL, + * originalCalledNr [6] EXPLICIT PresentedNumberUnscreened OPTIONAL, + * redirectingName [7] EXPLICIT Name OPTIONAL, + * originalCalledName [8] EXPLICIT Name OPTIONAL, + * extension CHOICE { + * [9] IMPLICIT Extension, + * [10] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigCallRerouting_ARG { + /*! \brief calledAddress */ + struct roseAddress called; + + /*! \brief lastReroutingNr */ + struct rosePresentedNumberUnscreened last_rerouting; + + /*! \brief originalCalledNr (optional) */ + struct rosePresentedNumberUnscreened original_called; + + /*! + * \brief callingPartySubaddress (optional) + * The subaddress is present if the length is nonzero. + */ + struct rosePartySubaddress calling_subaddress; + + /*! \brief callingNumber */ + struct rosePresentedNumberScreened calling; + + /*! \brief callingName (optional) */ + struct roseQsigName calling_name; + + /*! \brief redirectingName (optional) */ + struct roseQsigName redirecting_name; + + /*! \brief originalCalledName (optional) */ + struct roseQsigName original_called_name; + + /*! + * \brief The BC, HLC (optional), LLC (optional), + * and progress indicator(s) information. + */ + struct roseQ931ie q931ie; + /*! \brief q931ie.contents "allocated" after the stucture. */ + unsigned char q931ie_contents[ROSE_Q931_MAX_BC + ROSE_Q931_MAX_HLC + + ROSE_Q931_MAX_LLC + ROSE_Q931_MAX_PROGRESS + 1]; + + /*! \brief TRUE if calling_name is present */ + u_int8_t calling_name_present; + + /*! \brief TRUE if redirecting_name is present */ + u_int8_t redirecting_name_present; + + /*! \brief TRUE if original_called_name is present */ + u_int8_t original_called_name_present; + + /*! \brief TRUE if original_called number is present */ + u_int8_t original_called_present; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3) + * + * \note The value unknown is only used if received from + * another network when interworking. + */ + u_int8_t rerouting_reason; + + /*! + * \brief originalReroutingReason (optional) + * + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3) + * + * \note The value unknown is only used if received from + * another network when interworking. + */ + u_int8_t original_rerouting_reason; + + /*! \brief TRUE if original_rerouting_reason is present */ + u_int8_t original_rerouting_reason_present; + + /*! \brief diversionCounter INTEGER (1..15) */ + u_int8_t diversion_counter; + + /*! + * \brief subscriptionOption + * + * \details + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + */ + u_int8_t subscription_option; +}; + +/* + * ARGUMENT SEQUENCE { + * diversionReason DiversionReason, + * subscriptionOption SubscriptionOption, + * nominatedNr PartyNumber, + * extension CHOICE { + * [9] IMPLICIT Extension, + * [10] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigDivertingLegInformation1_ARG { + struct rosePartyNumber nominated_number; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3) + * + * \note The value unknown is only used if received from + * another network when interworking. + */ + u_int8_t diversion_reason; + + /*! + * \brief subscriptionOption + * + * \details + * noNotification(0), + * notificationWithoutDivertedToNr(1), + * notificationWithDivertedToNr(2) + */ + u_int8_t subscription_option; +}; + +/* + * ARGUMENT SEQUENCE { + * diversionCounter INTEGER (1..15), + * diversionReason DiversionReason, + * originalDiversionReason [0] IMPLICIT DiversionReason OPTIONAL, + * + * -- The divertingNr element is mandatory except in the case + * -- of interworking. + * divertingNr [1] EXPLICIT PresentedNumberUnscreened OPTIONAL, + * originalCalledNr [2] EXPLICIT PresentedNumberUnscreened OPTIONAL, + * redirectingName [3] EXPLICIT Name OPTIONAL, + * originalCalledName [4] EXPLICIT Name OPTIONAL, + * extension CHOICE { + * [5] IMPLICIT Extension, + * [6] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigDivertingLegInformation2_ARG { + /*! \brief divertingNr (optional) */ + struct rosePresentedNumberUnscreened diverting; + + /*! \brief originalCalledNr (optional) */ + struct rosePresentedNumberUnscreened original_called; + + /*! \brief redirectingName (optional) */ + struct roseQsigName redirecting_name; + + /*! \brief originalCalledName (optional) */ + struct roseQsigName original_called_name; + + /*! \brief diversionCounter INTEGER (1..15) */ + u_int8_t diversion_counter; + + /*! + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3) + * + * \note The value unknown is only used if received from + * another network when interworking. + */ + u_int8_t diversion_reason; + + /*! + * \brief originalDiversionReason (optional) + * + * \details + * unknown(0), + * cfu(1), + * cfb(2), + * cfnr(3) + * + * \note The value unknown is only used if received from + * another network when interworking. + */ + u_int8_t original_diversion_reason; + + /*! \brief TRUE if original_diversion_reason is present */ + u_int8_t original_diversion_reason_present; + + /*! \brief TRUE if diverting number is present */ + u_int8_t diverting_present; + + /*! \brief TRUE if original_called number is present */ + u_int8_t original_called_present; + + /*! \brief TRUE if redirecting_name is present */ + u_int8_t redirecting_name_present; + + /*! \brief TRUE if original_called_name is present */ + u_int8_t original_called_name_present; +}; + +/* + * ARGUMENT SEQUENCE { + * presentationAllowedIndicator PresentationAllowedIndicator, -- BOOLEAN + * redirectionName [0] EXPLICIT Name OPTIONAL, + * extension CHOICE { + * [1] IMPLICIT Extension, + * [2] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigDivertingLegInformation3_ARG { + /*! \brief redirectionName (optional) */ + struct roseQsigName redirection_name; + + /*! \brief TRUE if redirection_name is present */ + u_int8_t redirection_name_present; + + /*! \brief TRUE if presentation is allowed */ + u_int8_t presentation_allowed_indicator; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * MsgCentreId ::= CHOICE { + * integer [0] IMPLICIT INTEGER (0..65535), + * + * -- The party number must be a complete number as required + * -- for routing purposes. + * partyNumber [1] EXPLICIT PartyNumber, + * numericString [2] IMPLICIT NumericString (SIZE (1..10)) + * } + */ +struct roseQsigMsgCentreId { + union { + /*! \brief INTEGER (0..65535) */ + u_int16_t integer; + + /*! + * \note The party number must be a complete number as required + * for routing purposes. + */ + struct rosePartyNumber number; + + /*! \brief NumericString (SIZE (1..10)) */ + unsigned char str[10 + 1]; + } u; + + /*! + * \details + * integer(0), + * partyNumber(1), + * numericString(2) + */ + u_int8_t type; +}; + +/* + * MWIActivateArg ::= SEQUENCE { + * servedUserNr PartyNumber, + * basicService BasicService, + * msgCentreId MsgCentreId OPTIONAL, + * nbOfMessages [3] IMPLICIT NbOfMessages OPTIONAL, + * originatingNr [4] EXPLICIT PartyNumber OPTIONAL, + * timestamp TimeStamp OPTIONAL, + * + * -- The value 0 means the highest priority and 9 the lowest + * priority [5] IMPLICIT INTEGER (0..9) OPTIONAL, + * argumentExt CHOICE { + * extension [6] IMPLICIT Extension, + * multipleExtension [7] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigMWIActivateArg { + /*! \brief NbOfMessages ::= INTEGER (0..65535) (optional) */ + u_int16_t number_of_messages; + + /*! \brief msgCentreId (optional) */ + struct roseQsigMsgCentreId msg_centre_id; + + struct rosePartyNumber served_user_number; + + /*! \brief originatingNr (optional) (Number present if length is nonzero) */ + struct rosePartyNumber originating_number; + + /*! \brief GeneralizedTime (SIZE (12..19)) (optional) */ + unsigned char timestamp[19 + 1]; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3100Hz(3), + * telephony(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotextSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * reservedNotUsed1(38), + * reservedNotUsed2(39), + * reservedNotUsed3(40), + * reservedNotUsed4(41), + * reservedNotUsed5(42), + * email(51), + * video(52), + * fileTransfer(53), + * shortMessageService(54), + * speechAndVideo(55), + * speechAndFax(56), + * speechAndEmail(57), + * videoAndFax(58), + * videoAndEmail(59), + * faxAndEmail(60), + * speechVideoAndFax(61), + * speechVideoAndEmail(62), + * speechFaxAndEmail(63), + * videoFaxAndEmail(64), + * speechVideoFaxAndEmail(65), + * multimediaUnknown(66), + * serviceUnknown(67), + * futureReserve1(68), + * futureReserve2(69), + * futureReserve3(70), + * futureReserve4(71), + * futureReserve5(72), + * futureReserve6(73), + * futureReserve7(74), + * futureReserve8(75) + */ + u_int8_t basic_service; + + /*! + * \brief INTEGER (0..9) (optional) + * \note The value 0 means the highest priority and 9 the lowest. + */ + u_int8_t priority; + + /*! \brief TRUE if msg_centre_id is present */ + u_int8_t msg_centre_id_present; + + /*! \brief TRUE if number_of_messages is present */ + u_int8_t number_of_messages_present; + + /*! \brief TRUE if timestamp is present */ + u_int8_t timestamp_present; + + /*! \brief TRUE if priority is present */ + u_int8_t priority_present; +}; + +/* + * MWIDeactivateArg ::= SEQUENCE { + * servedUserNr PartyNumber, + * basicService BasicService, + * msgCentreId MsgCentreId OPTIONAL, + * argumentExt CHOICE { + * extension [3] IMPLICIT Extension, + * multipleExtension [4] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigMWIDeactivateArg { + /*! \brief msgCentreId (optional) */ + struct roseQsigMsgCentreId msg_centre_id; + + struct rosePartyNumber served_user_number; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3100Hz(3), + * telephony(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotextSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * reservedNotUsed1(38), + * reservedNotUsed2(39), + * reservedNotUsed3(40), + * reservedNotUsed4(41), + * reservedNotUsed5(42), + * email(51), + * video(52), + * fileTransfer(53), + * shortMessageService(54), + * speechAndVideo(55), + * speechAndFax(56), + * speechAndEmail(57), + * videoAndFax(58), + * videoAndEmail(59), + * faxAndEmail(60), + * speechVideoAndFax(61), + * speechVideoAndEmail(62), + * speechFaxAndEmail(63), + * videoFaxAndEmail(64), + * speechVideoFaxAndEmail(65), + * multimediaUnknown(66), + * serviceUnknown(67), + * futureReserve1(68), + * futureReserve2(69), + * futureReserve3(70), + * futureReserve4(71), + * futureReserve5(72), + * futureReserve6(73), + * futureReserve7(74), + * futureReserve8(75) + */ + u_int8_t basic_service; + + /*! \brief TRUE if msg_centre_id is present */ + u_int8_t msg_centre_id_present; +}; + +/* + * MWIInterrogateArg ::= SEQUENCE { + * servedUserNr PartyNumber, + * basicService BasicService, + * msgCentreId MsgCentreId OPTIONAL, + * argumentExt CHOICE { + * extension [3] IMPLICIT Extension, + * multipleExtension [4] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigMWIInterrogateArg { + /*! \brief msgCentreId (optional) */ + struct roseQsigMsgCentreId msg_centre_id; + + struct rosePartyNumber served_user_number; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3100Hz(3), + * telephony(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotextSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * reservedNotUsed1(38), + * reservedNotUsed2(39), + * reservedNotUsed3(40), + * reservedNotUsed4(41), + * reservedNotUsed5(42), + * email(51), + * video(52), + * fileTransfer(53), + * shortMessageService(54), + * speechAndVideo(55), + * speechAndFax(56), + * speechAndEmail(57), + * videoAndFax(58), + * videoAndEmail(59), + * faxAndEmail(60), + * speechVideoAndFax(61), + * speechVideoAndEmail(62), + * speechFaxAndEmail(63), + * videoFaxAndEmail(64), + * speechVideoFaxAndEmail(65), + * multimediaUnknown(66), + * serviceUnknown(67), + * futureReserve1(68), + * futureReserve2(69), + * futureReserve3(70), + * futureReserve4(71), + * futureReserve5(72), + * futureReserve6(73), + * futureReserve7(74), + * futureReserve8(75) + */ + u_int8_t basic_service; + + /*! \brief TRUE if msg_centre_id is present */ + u_int8_t msg_centre_id_present; +}; + +/* + * MWIInterrogateResElt ::= SEQUENCE { + * basicService BasicService, + * msgCentreId MsgCentreId OPTIONAL, + * nbOfMessages [3] IMPLICIT NbOfMessages OPTIONAL, + * originatingNr [4] EXPLICIT PartyNumber OPTIONAL, + * timestamp TimeStamp OPTIONAL, + * + * -- The value 0 means the highest priority and 9 the lowest + * priority [5] IMPLICIT INTEGER (0..9) OPTIONAL, + * argumentExt CHOICE { + * extension [6] IMPLICIT Extension, + * multipleExtension [7] IMPLICIT SEQUENCE OF Extension + * } OPTIONAL + * } + */ +struct roseQsigMWIInterrogateResElt { + /*! \brief NbOfMessages ::= INTEGER (0..65535) (optional) */ + u_int16_t number_of_messages; + + /*! \brief msgCentreId (optional) */ + struct roseQsigMsgCentreId msg_centre_id; + + /*! \brief originatingNr (optional) (Number present if length is nonzero) */ + struct rosePartyNumber originating_number; + + /*! \brief GeneralizedTime (SIZE (12..19)) (optional) */ + unsigned char timestamp[19 + 1]; + + /*! + * \details + * allServices(0), + * speech(1), + * unrestrictedDigitalInformation(2), + * audio3100Hz(3), + * telephony(32), + * teletex(33), + * telefaxGroup4Class1(34), + * videotextSyntaxBased(35), + * videotelephony(36), + * telefaxGroup2-3(37), + * reservedNotUsed1(38), + * reservedNotUsed2(39), + * reservedNotUsed3(40), + * reservedNotUsed4(41), + * reservedNotUsed5(42), + * email(51), + * video(52), + * fileTransfer(53), + * shortMessageService(54), + * speechAndVideo(55), + * speechAndFax(56), + * speechAndEmail(57), + * videoAndFax(58), + * videoAndEmail(59), + * faxAndEmail(60), + * speechVideoAndFax(61), + * speechVideoAndEmail(62), + * speechFaxAndEmail(63), + * videoFaxAndEmail(64), + * speechVideoFaxAndEmail(65), + * multimediaUnknown(66), + * serviceUnknown(67), + * futureReserve1(68), + * futureReserve2(69), + * futureReserve3(70), + * futureReserve4(71), + * futureReserve5(72), + * futureReserve6(73), + * futureReserve7(74), + * futureReserve8(75) + */ + u_int8_t basic_service; + + /*! + * \brief INTEGER (0..9) (optional) + * \note The value 0 means the highest priority and 9 the lowest. + */ + u_int8_t priority; + + /*! \brief TRUE if msg_centre_id is present */ + u_int8_t msg_centre_id_present; + + /*! \brief TRUE if number_of_messages is present */ + u_int8_t number_of_messages_present; + + /*! \brief TRUE if timestamp is present */ + u_int8_t timestamp_present; + + /*! \brief TRUE if priority is present */ + u_int8_t priority_present; +}; + +/* + * MWIInterrogateRes ::= SEQUENCE SIZE(1..10) OF MWIInterrogateResElt + */ +struct roseQsigMWIInterrogateRes { + /*! \brief SEQUENCE SIZE(1..10) OF MWIInterrogateResElt */ + struct roseQsigMWIInterrogateResElt list[10]; + + /*! \brief Number of MWIInterrogateResElt records present */ + u_int8_t num_records; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * Northern Telecom DMS-100 transfer ability result + * + * callId [0] IMPLICIT INTEGER (0..16777215) -- 24 bit number + */ +struct roseDms100RLTOperationInd_RES { + /*! INTEGER (0..16777215) -- 24 bit number */ + u_int32_t call_id; +}; + +/* + * Northern Telecom DMS-100 transfer invoke + * + * ARGUMENT SEQUENCE { + * callId [0] IMPLICIT INTEGER (0..16777215), -- 24 bit number + * reason [1] IMPLICIT INTEGER + * } + */ +struct roseDms100RLTThirdParty_ARG { + /*! INTEGER (0..16777215) -- 24 bit number */ + u_int32_t call_id; + + /*! Reason for redirect */ + u_int8_t reason; +}; + + +/* ------------------------------------------------------------------- */ + + +/* ARGUMENT ENUMERATED */ +struct roseNi2InformationFollowing_ARG { + u_int8_t value; /*!< Unknown enumerated value */ +}; + +/* + * ARGUMENT SEQUENCE { + * callReference INTEGER -- 16 bit number + * } + */ +struct roseNi2InitiateTransfer_ARG { + u_int16_t call_reference; +}; + + +/* ------------------------------------------------------------------- */ + + +/*! \brief Facility ie invoke etsi messages with arguments. */ +union rose_msg_invoke_etsi_args { + /* ETSI Advice Of Charge (AOC) */ + struct roseEtsiChargingRequest_ARG ChargingRequest; + struct roseEtsiAOCSCurrency_ARG AOCSCurrency; + struct roseEtsiAOCSSpecialArr_ARG AOCSSpecialArr; + struct roseEtsiAOCDCurrency_ARG AOCDCurrency; + struct roseEtsiAOCDChargingUnit_ARG AOCDChargingUnit; + struct roseEtsiAOCECurrency_ARG AOCECurrency; + struct roseEtsiAOCEChargingUnit_ARG AOCEChargingUnit; + + /* ETSI Call Diversion */ + struct roseEtsiActivationDiversion_ARG ActivationDiversion; + struct roseEtsiDeactivationDiversion_ARG DeactivationDiversion; + struct roseEtsiActivationStatusNotificationDiv_ARG ActivationStatusNotificationDiv; + struct roseEtsiDeactivationStatusNotificationDiv_ARG + DeactivationStatusNotificationDiv; + struct roseEtsiInterrogationDiversion_ARG InterrogationDiversion; + struct roseEtsiDiversionInformation_ARG DiversionInformation; + struct roseEtsiCallDeflection_ARG CallDeflection; + struct roseEtsiCallRerouting_ARG CallRerouting; + struct roseEtsiDivertingLegInformation1_ARG DivertingLegInformation1; + struct roseEtsiDivertingLegInformation2_ARG DivertingLegInformation2; + struct roseEtsiDivertingLegInformation3_ARG DivertingLegInformation3; + + /* ETSI Explicit Call Transfer (ECT) */ + struct roseEtsiExplicitEctExecute_ARG ExplicitEctExecute; + struct roseEtsiSubaddressTransfer_ARG SubaddressTransfer; + struct roseEtsiEctInform_ARG EctInform; + struct roseEtsiEctLoopTest_ARG EctLoopTest; +}; + +/*! \brief Facility ie result etsi messages with arguments. */ +union rose_msg_result_etsi_args { + /* ETSI Advice Of Charge (AOC) */ + struct roseEtsiChargingRequest_RES ChargingRequest; + + /* ETSI Call Diversion */ + struct roseEtsiForwardingList InterrogationDiversion; + struct roseEtsiServedUserNumberList InterrogateServedUserNumbers; + + /* ETSI Explicit Call Transfer (ECT) */ + struct roseEtsiEctLinkIdRequest_RES EctLinkIdRequest; + struct roseEtsiEctLoopTest_RES EctLoopTest; +}; + +/*! \brief Facility ie invoke qsig messages with arguments. */ +union rose_msg_invoke_qsig_args { + /* Q.SIG Name-Operations */ + struct roseQsigPartyName_ARG CallingName; + struct roseQsigPartyName_ARG CalledName; + struct roseQsigPartyName_ARG ConnectedName; + struct roseQsigPartyName_ARG BusyName; + + /* Q.SIG SS-AOC-Operations */ + struct roseQsigChargeRequestArg_ARG ChargeRequest; + struct roseQsigAocFinalArg_ARG AocFinal; + struct roseQsigAocInterimArg_ARG AocInterim; + struct roseQsigAocRateArg_ARG AocRate; + struct roseQsigAocCompleteArg_ARG AocComplete; + struct roseQsigAocDivChargeReqArg_ARG AocDivChargeReq; + + /* Q.SIG Call-Transfer-Operations */ + struct roseQsigCTInitiateArg_ARG CallTransferInitiate; + struct roseQsigCTSetupArg_ARG CallTransferSetup; + struct roseQsigCTActiveArg_ARG CallTransferActive; + struct roseQsigCTCompleteArg_ARG CallTransferComplete; + struct roseQsigCTUpdateArg_ARG CallTransferUpdate; + struct roseQsigSubaddressTransferArg_ARG SubaddressTransfer; + + /* Q.SIG Call-Diversion-Operations */ + struct roseQsigActivateDiversionQ_ARG ActivateDiversionQ; + struct roseQsigDeactivateDiversionQ_ARG DeactivateDiversionQ; + struct roseQsigInterrogateDiversionQ_ARG InterrogateDiversionQ; + struct roseQsigCheckRestriction_ARG CheckRestriction; + struct roseQsigCallRerouting_ARG CallRerouting; + struct roseQsigDivertingLegInformation1_ARG DivertingLegInformation1; + struct roseQsigDivertingLegInformation2_ARG DivertingLegInformation2; + struct roseQsigDivertingLegInformation3_ARG DivertingLegInformation3; + + /* Q.SIG SS-MWI-Operations */ + struct roseQsigMWIActivateArg MWIActivate; + struct roseQsigMWIDeactivateArg MWIDeactivate; + struct roseQsigMWIInterrogateArg MWIInterrogate; +}; + +/*! \brief Facility ie result qsig messages with arguments. */ +union rose_msg_result_qsig_args { + /* Q.SIG SS-AOC-Operations */ + struct roseQsigChargeRequestRes_RES ChargeRequest; + struct roseQsigAocCompleteRes_RES AocComplete; + + /* Q.SIG Call-Transfer-Operations */ + struct roseQsigCTIdentifyRes_RES CallTransferIdentify; + + /* Q.SIG Call-Diversion-Operations */ + struct roseQsigForwardingList InterrogateDiversionQ; + + /* Q.SIG SS-MWI-Operations */ + struct roseQsigMWIInterrogateRes MWIInterrogate; +}; + +/*! \brief Facility ie invoke DMS-100 messages with arguments. */ +union rose_msg_invoke_dms100_args { + struct roseDms100RLTThirdParty_ARG RLT_ThirdParty; +}; + +/*! \brief Facility ie result DMS-100 messages with arguments. */ +union rose_msg_result_dms100_args { + struct roseDms100RLTOperationInd_RES RLT_OperationInd; +}; + +/*! \brief Facility ie invoke NI2 messages with arguments. */ +union rose_msg_invoke_ni2_args { + struct roseNi2InformationFollowing_ARG InformationFollowing; + struct roseNi2InitiateTransfer_ARG InitiateTransfer; +}; + +/*! \brief Facility ie result NI2 messages with arguments. */ +union rose_msg_result_ni2_args { + int dummy; /*!< place holder until there are results with parameters */ +}; + +/*! \brief Facility ie invoke messages with arguments. */ +union rose_msg_invoke_args { + union rose_msg_invoke_etsi_args etsi; + union rose_msg_invoke_qsig_args qsig; + union rose_msg_invoke_dms100_args dms100; + union rose_msg_invoke_ni2_args ni2; +}; + +/*! \brief Facility ie result messages with arguments. */ +union rose_msg_result_args { + union rose_msg_result_etsi_args etsi; + union rose_msg_result_qsig_args qsig; + union rose_msg_result_dms100_args dms100; + union rose_msg_result_ni2_args ni2; +}; + +/*! \brief Facility ie error messages with parameters. */ +union rose_msg_error_args { + int dummy; /*!< place holder until there are errors with parameters */ +}; + +struct rose_msg_invoke { + /*! \brief Invoke ID (-32768..32767) */ + int16_t invoke_id; + /*! \brief Linked ID (-32768..32767) (optional) */ + int16_t linked_id; + /*! \brief library encoded operation-value */ + enum rose_operation operation; + /*! \brief TRUE if the Linked ID is present */ + u_int8_t linked_id_present; + union rose_msg_invoke_args args; +}; + +struct rose_msg_result { + /*! \brief Invoke ID (-32768..32767) */ + int16_t invoke_id; + /*! + * \brief library encoded operation-value + * \note Set to ROSE_None if the operation sequence is not present. + * \note ETSI and Q.SIG imply that if a return result does not have + * any arguments then you must rely upon the invokeId value to + * distinguish between return results because the operation-value is + * not present. + */ + enum rose_operation operation; + union rose_msg_result_args args; +}; + +struct rose_msg_error { + /*! \brief Invoke ID (-32768..32767) */ + int16_t invoke_id; + /*! \brief library encoded error-value */ + enum rose_error_code code; + union rose_msg_error_args args; +}; + +struct rose_msg_reject { + /*! \brief Invoke ID (-32768..32767) (optional) */ + int16_t invoke_id; + /*! \brief TRUE if the Invoke ID is present */ + u_int8_t invoke_id_present; + /*! \brief library encoded problem-value */ + enum rose_reject_code code; +}; + +enum rose_component_type { + ROSE_COMP_TYPE_INVALID, + ROSE_COMP_TYPE_INVOKE, + ROSE_COMP_TYPE_RESULT, + ROSE_COMP_TYPE_ERROR, + ROSE_COMP_TYPE_REJECT +}; + +struct rose_message { + /*! \brief invoke, result, error, reject */ + enum rose_component_type type; + union { + struct rose_msg_invoke invoke; + struct rose_msg_result result; + struct rose_msg_error error; + struct rose_msg_reject reject; + } component; +}; + +/* + * NetworkFacilityExtension ::= [10] IMPLICIT SEQUENCE { + * sourceEntity [0] IMPLICIT EntityType, + * sourceEntityAddress [1] EXPLICIT AddressInformation OPTIONAL, + * destinationEntity [2] IMPLICIT EntityType, + * destinationEntityAddress [3] EXPLICIT AddressInformation OPTIONAL + * } + * + * AddressInformation ::= PartyNumber + */ +struct facNetworkFacilityExtension { + /*! \brief sourceEntityAddress (optional) (Number present if length is nonzero) */ + struct rosePartyNumber source_number; + /*! \brief destinationEntityAddress (optional) (Number present if length is nonzero) */ + struct rosePartyNumber destination_number; + + /*! + * \details + * endPINX(0), + * anyTypeOfPINX(1) + */ + u_int8_t source_entity; + + /*! + * \details + * endPINX(0), + * anyTypeOfPINX(1) + */ + u_int8_t destination_entity; +}; + +/* + * The network extensions header is a sequence of the following components: + * + * nfe NetworkFacilityExtension OPTIONAL, + * npp NetworkProtocolProfile OPTIONAL, + * interpretation InterpretationApdu OPTIONAL + * + * NetworkProtocolProfile ::= [18] IMPLICIT INTEGER (0..254) + * + * InterpretationApdu ::= [11] IMPLICIT ENUMERATED { + * discardAnyUnrecognisedInvokePdu(0), + * + * -- this value also applies to Call independent signalling connections + * -- see clause 8.1.2 (ECMA-165) + * clearCallIfAnyInvokePduNotRecognised(1), + * + * -- this coding is implied by the absence of an + * -- interpretation APDU. + * rejectAnyUnrecognisedInvokePdu(2) + * } + */ +struct fac_extension_header { + /*! \brief Network Facility Extension component */ + struct facNetworkFacilityExtension nfe; + + /*! \brief Network Protocol Profile component */ + u_int8_t npp; + + /*! + * \brief interpretation component + * + * \details + * discardAnyUnrecognisedInvokePdu(0), + * clearCallIfAnyInvokePduNotRecognised(1), + * rejectAnyUnrecognisedInvokePdu(2) + */ + u_int8_t interpretation; + + /*! \brief TRUE if nfe is present */ + u_int8_t nfe_present; + + /*! \brief TRUE if npp is present */ + u_int8_t npp_present; + + /*! \brief TRUE if interpretation is present */ + u_int8_t interpretation_present; +}; + +const char *rose_operation2str(enum rose_operation operation); +const char *rose_error2str(enum rose_error_code code); +const char *rose_reject2str(enum rose_reject_code code); + +unsigned char *rose_encode_invoke(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rose_msg_invoke *msg); +unsigned char *rose_encode_result(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rose_msg_result *msg); +unsigned char *rose_encode_error(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rose_msg_error *msg); +unsigned char *rose_encode_reject(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rose_msg_reject *msg); + +unsigned char *rose_encode(struct pri *ctrl, unsigned char *pos, unsigned char *end, + const struct rose_message *msg); +const unsigned char *rose_decode(struct pri *ctrl, const unsigned char *pos, + const unsigned char *end, struct rose_message *msg); + +unsigned char *fac_enc_extension_header(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct fac_extension_header *header); +unsigned char *facility_encode_header(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct fac_extension_header *header); + +const unsigned char *fac_dec_extension_header(struct pri *ctrl, const unsigned char *pos, + const unsigned char *end, struct fac_extension_header *header); +const unsigned char *facility_decode_header(struct pri *ctrl, const unsigned char *pos, + const unsigned char *end, struct fac_extension_header *header); + +void facility_decode_dump(struct pri *ctrl, const unsigned char *buf, size_t length); + +/* ------------------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBPRI_ROSE_H */ +/* ------------------------------------------------------------------- */ +/* end rose.h */ Property changes on: rose.h ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose_address.c =================================================================== --- a/rose_address.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_address.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,983 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 ROSE Addressing-Data-Elements + * + * Addressing-Data-Elements ETS 300 196-1 D.3 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the public or private network PartyNumber 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param number + * \param length_of_number + * \param type_of_number + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_NetworkPartyNumber(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const unsigned char *number, + size_t length_of_number, u_int8_t type_of_number) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, type_of_number)); + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_TYPE_NUMERIC_STRING, number, + length_of_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the PartyNumber 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 party_number + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_PartyNumber(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePartyNumber *party_number) +{ + switch (party_number->plan) { + case 0: /* Unknown PartyNumber */ + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + party_number->str, party_number->length)); + break; + case 1: /* Public PartyNumber */ + ASN1_CALL(pos, rose_enc_NetworkPartyNumber(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, party_number->str, party_number->length, + party_number->ton)); + break; + case 2: /* NSAP encoded PartyNumber */ + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + party_number->str, party_number->length)); + break; + case 3: /* Data PartyNumber (Not used) */ + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, + party_number->str, party_number->length)); + break; + case 4: /* Telex PartyNumber (Not used) */ + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4, + party_number->str, party_number->length)); + break; + case 5: /* Private PartyNumber */ + ASN1_CALL(pos, rose_enc_NetworkPartyNumber(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 5, party_number->str, party_number->length, + party_number->ton)); + break; + case 8: /* National Standard PartyNumber (Not used) */ + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 8, + party_number->str, party_number->length)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown numbering plan"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the PartySubaddress 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 party_subaddress + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_PartySubaddress(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePartySubaddress *party_subaddress) +{ + unsigned char *seq_len; + + switch (party_subaddress->type) { + case 0: /* UserSpecified */ + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_TYPE_OCTET_STRING, + party_subaddress->u.user_specified.information, party_subaddress->length)); + if (party_subaddress->u.user_specified.odd_count_present) { + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, + party_subaddress->u.user_specified.odd_count)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + break; + case 1: /* NSAP */ + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_TYPE_OCTET_STRING, + party_subaddress->u.nsap, party_subaddress->length)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown subaddress type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the Address 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param address + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_Address(struct pri *ctrl, unsigned char *pos, unsigned char *end, + unsigned tag, const struct roseAddress *address) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &address->number)); + if (address->subaddress.length) { + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, &address->subaddress)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the PresentedNumberUnscreened 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 party + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_PresentedNumberUnscreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePresentedNumberUnscreened *party) +{ + unsigned char *seq_len; + + switch (party->presentation) { + case 0: /* presentationAllowedNumber */ + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &party->number)); + ASN1_CONSTRUCTED_END(seq_len, pos, end); + break; + case 1: /* presentationRestricted */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); + break; + case 2: /* numberNotAvailableDueToInterworking */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2)); + break; + case 3: /* presentationRestrictedNumber */ + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &party->number)); + ASN1_CONSTRUCTED_END(seq_len, pos, end); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown presentation type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the NumberScreened 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param screened + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_NumberScreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseNumberScreened *screened) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &screened->number)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + screened->screening_indicator)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the PresentedNumberScreened 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 party + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_PresentedNumberScreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePresentedNumberScreened *party) +{ + switch (party->presentation) { + case 0: /* presentationAllowedNumber */ + ASN1_CALL(pos, rose_enc_NumberScreened(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 0, &party->screened)); + break; + case 1: /* presentationRestricted */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); + break; + case 2: /* numberNotAvailableDueToInterworking */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2)); + break; + case 3: /* presentationRestrictedNumber */ + ASN1_CALL(pos, rose_enc_NumberScreened(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 3, &party->screened)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown presentation type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the AddressScreened 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param screened + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_AddressScreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseAddressScreened *screened) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &screened->number)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + screened->screening_indicator)); + if (screened->subaddress.length) { + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, &screened->subaddress)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the PresentedAddressScreened 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 party + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_PresentedAddressScreened(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rosePresentedAddressScreened *party) +{ + switch (party->presentation) { + case 0: /* presentationAllowedAddress */ + ASN1_CALL(pos, rose_enc_AddressScreened(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 0, &party->screened)); + break; + case 1: /* presentationRestricted */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); + break; + case 2: /* numberNotAvailableDueToInterworking */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2)); + break; + case 3: /* presentationRestrictedAddress */ + ASN1_CALL(pos, rose_enc_AddressScreened(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 3, &party->screened)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown presentation type"); + return NULL; + } + + return pos; +} + +/*! + * \internal + * \brief Decode the NumberDigits PartyNumber 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 party_number Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_NumberDigits(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartyNumber *party_number) +{ + size_t str_len; + + ASN1_CALL(pos, asn1_dec_string_max(ctrl, name, tag, pos, end, + sizeof(party_number->str), party_number->str, &str_len)); + party_number->length = str_len; + + return pos; +} + +/*! + * \internal + * \brief Decode the NSAP PartyNumber 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 party_number Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_NSAPPartyNumber(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartyNumber *party_number) +{ + size_t str_len; + + ASN1_CALL(pos, asn1_dec_string_bin(ctrl, name, tag, pos, end, + sizeof(party_number->str), party_number->str, &str_len)); + party_number->length = str_len; + + return pos; +} + +/*! + * \internal + * \brief Decode the public or private network PartyNumber 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 party_number Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_NetworkPartyNumber(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartyNumber *party_number) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %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_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "typeOfNumber", tag, pos, seq_end, &value)); + party_number->ton = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_TYPE_NUMERIC_STRING); + ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "numberDigits", tag, pos, seq_end, + party_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the PartyNumber 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 party_number Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_PartyNumber(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartyNumber *party_number) +{ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s PartyNumber\n", name); + } + party_number->ton = 0; /* unknown */ + switch (tag & ~ASN1_PC_MASK) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + party_number->plan = 0; /* Unknown PartyNumber */ + ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "unknownPartyNumber", tag, pos, end, + party_number)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + /* Must be constructed but we will not check for it for simplicity. */ + party_number->plan = 1; /* Public PartyNumber */ + ASN1_CALL(pos, rose_dec_NetworkPartyNumber(ctrl, "publicPartyNumber", tag, pos, + end, party_number)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + party_number->plan = 2; /* NSAP encoded PartyNumber */ + ASN1_CALL(pos, rose_dec_NSAPPartyNumber(ctrl, "nsapEncodedPartyNumber", tag, pos, + end, party_number)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + party_number->plan = 3; /* Data PartyNumber (Not used) */ + ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "dataPartyNumber", tag, pos, end, + party_number)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + party_number->plan = 4; /* Telex PartyNumber (Not used) */ + ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "telexPartyNumber", tag, pos, end, + party_number)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 5: + /* Must be constructed but we will not check for it for simplicity. */ + party_number->plan = 5; /* Private PartyNumber */ + ASN1_CALL(pos, rose_dec_NetworkPartyNumber(ctrl, "privatePartyNumber", tag, pos, + end, party_number)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 8: + party_number->plan = 8; /* National Standard PartyNumber (Not used) */ + ASN1_CALL(pos, rose_dec_NumberDigits(ctrl, "nationalStandardPartyNumber", tag, + pos, end, party_number)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \internal + * \brief Decode the User PartySubaddress 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 party_subaddress Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_UserSubaddress(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartySubaddress *party_subaddress) +{ + size_t str_len; + int32_t odd_count; + int length; + int seq_offset; + const unsigned char *seq_end; + + party_subaddress->type = 0; /* UserSpecified */ + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s UserSpecified %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); + + /* SubaddressInformation */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_TYPE_OCTET_STRING); + ASN1_CALL(pos, asn1_dec_string_bin(ctrl, "subaddressInformation", tag, pos, seq_end, + sizeof(party_subaddress->u.user_specified.information), + party_subaddress->u.user_specified.information, &str_len)); + party_subaddress->length = str_len; + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + /* + * The optional odd count indicator must be present since there + * is something left. + */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_BOOLEAN); + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "oddCount", tag, pos, seq_end, + &odd_count)); + party_subaddress->u.user_specified.odd_count = odd_count; + party_subaddress->u.user_specified.odd_count_present = 1; + } else { + party_subaddress->u.user_specified.odd_count = 0; + party_subaddress->u.user_specified.odd_count_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the NSAP PartySubaddress 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 party_subaddress Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_NSAPSubaddress(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartySubaddress *party_subaddress) +{ + size_t str_len; + + party_subaddress->type = 1; /* NSAP */ + + ASN1_CALL(pos, asn1_dec_string_bin(ctrl, name, tag, pos, end, + sizeof(party_subaddress->u.nsap), party_subaddress->u.nsap, &str_len)); + party_subaddress->length = str_len; + + return pos; +} + +/*! + * \brief Decode the PartySubaddress 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 party_subaddress Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_PartySubaddress(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartySubaddress *party_subaddress) +{ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s PartySubaddress\n", name); + } + switch (tag) { + case ASN1_TAG_SEQUENCE: + ASN1_CALL(pos, rose_dec_UserSubaddress(ctrl, "user", tag, pos, end, + party_subaddress)); + break; + case ASN1_TYPE_OCTET_STRING: + case ASN1_TYPE_OCTET_STRING | ASN1_PC_CONSTRUCTED: + ASN1_CALL(pos, rose_dec_NSAPSubaddress(ctrl, "nsap", tag, pos, end, + party_subaddress)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the Address 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 address Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_Address(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct roseAddress *address) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s Address %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_CALL(pos, rose_dec_PartyNumber(ctrl, "partyNumber", tag, pos, seq_end, + &address->number)); + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + /* The optional subaddress must be present since there is something left. */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "partySubaddress", tag, pos, + seq_end, &address->subaddress)); + } else { + address->subaddress.length = 0; /* Subaddress not present */ + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the PresentedNumberUnscreened 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 party Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_PresentedNumberUnscreened(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePresentedNumberUnscreened *party) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s PresentedNumberUnscreened\n", name); + } + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %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)); + party->presentation = 0; /* presentationAllowedNumber */ + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "presentationAllowedNumber", tag, pos, + seq_end, &party->number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + party->presentation = 1; /* presentationRestricted */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "presentationRestricted", tag, pos, end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + party->presentation = 2; /* numberNotAvailableDueToInterworking */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "numberNotAvailableDueToInterworking", tag, + pos, end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %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)); + party->presentation = 3; /* presentationRestrictedNumber */ + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "presentationRestrictedNumber", tag, + pos, seq_end, &party->number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the NumberScreened 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 screened Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_NumberScreened(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseNumberScreened *screened) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s NumberScreened %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_CALL(pos, rose_dec_PartyNumber(ctrl, "partyNumber", tag, pos, seq_end, + &screened->number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "screeningIndicator", tag, pos, seq_end, &value)); + screened->screening_indicator = value; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the PresentedNumberScreened 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 party Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_PresentedNumberScreened(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePresentedNumberScreened *party) +{ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s PresentedNumberScreened\n", name); + } + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + party->presentation = 0; /* presentationAllowedNumber */ + ASN1_CALL(pos, rose_dec_NumberScreened(ctrl, "presentationAllowedNumber", tag, + pos, end, &party->screened)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + party->presentation = 1; /* presentationRestricted */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "presentationRestricted", tag, pos, end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + party->presentation = 2; /* numberNotAvailableDueToInterworking */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "numberNotAvailableDueToInterworking", tag, + pos, end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: + party->presentation = 3; /* presentationRestrictedNumber */ + ASN1_CALL(pos, rose_dec_NumberScreened(ctrl, "presentationRestrictedNumber", tag, + pos, end, &party->screened)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the AddressScreened 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 screened Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_AddressScreened(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseAddressScreened *screened) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s AddressScreened %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_CALL(pos, rose_dec_PartyNumber(ctrl, "partyNumber", tag, pos, seq_end, + &screened->number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "screeningIndicator", tag, pos, seq_end, &value)); + screened->screening_indicator = value; + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + /* The optional subaddress must be present since there is something left. */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "partySubaddress", tag, pos, + seq_end, &screened->subaddress)); + } else { + screened->subaddress.length = 0; /* Subaddress not present */ + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the PresentedAddressScreened 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 party Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_PresentedAddressScreened(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePresentedAddressScreened *party) +{ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s PresentedAddressScreened\n", name); + } + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + party->presentation = 0; /* presentationAllowedAddress */ + ASN1_CALL(pos, rose_dec_AddressScreened(ctrl, "presentationAllowedAddress", tag, + pos, end, &party->screened)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + party->presentation = 1; /* presentationRestricted */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "presentationRestricted", tag, pos, end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + party->presentation = 2; /* numberNotAvailableDueToInterworking */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "numberNotAvailableDueToInterworking", tag, + pos, end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: + party->presentation = 3; /* presentationRestrictedAddress */ + ASN1_CALL(pos, rose_dec_AddressScreened(ctrl, "presentationRestrictedAddress", + tag, pos, end, &party->screened)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_address.c */ Property changes on: rose_address.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose_qsig_aoc.c =================================================================== --- a/rose_qsig_aoc.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_qsig_aoc.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,1714 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 Q.SIG ROSE Advice-Of-Charge (AOC) operations + * + * SS-AOC-Operations ECMA-212 Annex E Table E.1 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the Time 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param time Time information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_AOC_Time(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseQsigAOCTime *time) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + time->length)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, time->scale)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the Amount 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param amount Amount information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_AOC_Amount(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseQsigAOCAmount *amount) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + amount->currency)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + amount->multiplier)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the RecordedCurrency 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param recorded Recorded currency information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_AOC_RecordedCurrency(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseQsigAOCRecordedCurrency *recorded) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + recorded->currency, sizeof(recorded->currency) - 1)); + ASN1_CALL(pos, rose_enc_qsig_AOC_Amount(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, &recorded->amount)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the DurationCurrency 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param duration Duration currency information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_AOC_DurationCurrency(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseQsigAOCDurationCurrency *duration) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + duration->currency, sizeof(duration->currency) - 1)); + ASN1_CALL(pos, rose_enc_qsig_AOC_Amount(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, &duration->amount)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, + duration->charging_type)); + ASN1_CALL(pos, rose_enc_qsig_AOC_Time(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 4, &duration->time)); + if (duration->granularity_present) { + ASN1_CALL(pos, rose_enc_qsig_AOC_Time(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 5, &duration->granularity)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the FlatRateCurrency 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param flat_rate Flat rate currency information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_AOC_FlatRateCurrency(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseQsigAOCFlatRateCurrency *flat_rate) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + flat_rate->currency, sizeof(flat_rate->currency) - 1)); + ASN1_CALL(pos, rose_enc_qsig_AOC_Amount(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, &flat_rate->amount)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the VolumeRateCurrency 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param volume_rate Volume rate currency information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_AOC_VolumeRateCurrency(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseQsigAOCVolumeRateCurrency *volume_rate) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + volume_rate->currency, sizeof(volume_rate->currency) - 1)); + ASN1_CALL(pos, rose_enc_qsig_AOC_Amount(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, &volume_rate->amount)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, + volume_rate->unit)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the AOCSCurrencyInfo 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param currency_info Currency information record to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_AOCSCurrencyInfo(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseQsigAOCSCurrencyInfo *currency_info) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + currency_info->charged_item)); + + switch (currency_info->currency_type) { + case 0: /* specialChargingCode */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + currency_info->u.special_charging_code)); + break; + case 1: /* durationCurrency */ + ASN1_CALL(pos, rose_enc_qsig_AOC_DurationCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, ¤cy_info->u.duration)); + break; + case 2: /* flatRateCurrency */ + ASN1_CALL(pos, rose_enc_qsig_AOC_FlatRateCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, ¤cy_info->u.flat_rate)); + break; + case 3: /* volumeRateCurrency */ + ASN1_CALL(pos, rose_enc_qsig_AOC_VolumeRateCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 3, ¤cy_info->u.volume_rate)); + break; + case 4: /* freeOfCharge */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4)); + break; + case 5: /* currencyInfoNotAvailable */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5)); + break; + case 6: /* freeOfChargeFromBeginning */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 6)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown currency type"); + return NULL; + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the AOCSCurrencyInfoList 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param currency_info Currency information list to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_AOCSCurrencyInfoList(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseQsigAOCSCurrencyInfoList *currency_info) +{ + unsigned index; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + for (index = 0; index < currency_info->num_records; ++index) { + ASN1_CALL(pos, rose_enc_qsig_AOCSCurrencyInfo(ctrl, pos, end, ASN1_TAG_SEQUENCE, + ¤cy_info->list[index])); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the ChargingAssociation 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 charging Charging association information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_AOC_ChargingAssociation(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct roseQsigAOCChargingAssociation *charging) +{ + unsigned char *explicit_len; + + switch (charging->type) { + case 0: /* charge_identifier */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, charging->id)); + break; + case 1: /* charged_number */ + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &charging->number)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown ChargingAssociation type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the Q.SIG ChargeRequest invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_ChargeRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned index; + unsigned char *seq_len; + unsigned char *advice_len; + const struct roseQsigChargeRequestArg_ARG *charge_request; + + charge_request = &args->qsig.ChargeRequest; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + /* SEQUENCE SIZE(0..7) OF AdviceModeCombination */ + ASN1_CONSTRUCTED_BEGIN(advice_len, pos, end, ASN1_TAG_SEQUENCE); + for (index = 0; index < charge_request->num_records; ++index) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + charge_request->advice_mode_combinations[index])); + } + ASN1_CONSTRUCTED_END(advice_len, pos, end); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG ChargeRequest result facility ie 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. + */ +unsigned char *rose_enc_qsig_ChargeRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + unsigned char *seq_len; + const struct roseQsigChargeRequestRes_RES *charge_request; + + charge_request = &args->qsig.ChargeRequest; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + charge_request->advice_mode_combination)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG AocFinal invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_AocFinal_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + unsigned char *specific_len; + const struct roseQsigAocFinalArg_ARG *aoc_final; + + aoc_final = &args->qsig.AocFinal; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + switch (aoc_final->type) { + case 0: /* charge_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0)); + break; + case 1: /* free_of_charge */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); + break; + case 2: /* specific_currency */ + ASN1_CONSTRUCTED_BEGIN(specific_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_qsig_AOC_RecordedCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, &aoc_final->specific.recorded)); + + if (aoc_final->specific.billing_id_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + aoc_final->specific.billing_id)); + } + + ASN1_CONSTRUCTED_END(specific_len, pos, end); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown AocFinal type"); + return NULL; + } + + if (aoc_final->charging_association_present) { + ASN1_CALL(pos, rose_enc_qsig_AOC_ChargingAssociation(ctrl, pos, end, + &aoc_final->charging_association)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG AocInterim invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_AocInterim_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + unsigned char *specific_len; + const struct roseQsigAocInterimArg_ARG *aoc_interim; + + aoc_interim = &args->qsig.AocInterim; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + switch (aoc_interim->type) { + case 0: /* charge_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0)); + break; + case 1: /* free_of_charge */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); + break; + case 2: /* specific_currency */ + ASN1_CONSTRUCTED_BEGIN(specific_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_qsig_AOC_RecordedCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, &aoc_interim->specific.recorded)); + + if (aoc_interim->specific.billing_id_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + aoc_interim->specific.billing_id)); + } + + ASN1_CONSTRUCTED_END(specific_len, pos, end); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown AocInterim type"); + return NULL; + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG AocRate invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_AocRate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + const struct roseQsigAocRateArg_ARG *aoc_rate; + + aoc_rate = &args->qsig.AocRate; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + switch (aoc_rate->type) { + case 0: /* charge_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + break; + case 1: /* currency_info_list */ + ASN1_CALL(pos, rose_enc_qsig_AOCSCurrencyInfoList(ctrl, pos, end, + ASN1_TAG_SEQUENCE, &aoc_rate->currency_info)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown AocRate type"); + return NULL; + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG AocComplete invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_AocComplete_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + const struct roseQsigAocCompleteArg_ARG *aoc_complete; + + aoc_complete = &args->qsig.AocComplete; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &aoc_complete->charged_user_number)); + + if (aoc_complete->charging_association_present) { + ASN1_CALL(pos, rose_enc_qsig_AOC_ChargingAssociation(ctrl, pos, end, + &aoc_complete->charging_association)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG AocComplete result facility ie 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. + */ +unsigned char *rose_enc_qsig_AocComplete_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + unsigned char *seq_len; + const struct roseQsigAocCompleteRes_RES *aoc_complete; + + aoc_complete = &args->qsig.AocComplete; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + aoc_complete->charging_option)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG AocDivChargeReq invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_AocDivChargeReq_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + const struct roseQsigAocDivChargeReqArg_ARG *aoc_div_charge_req; + + aoc_div_charge_req = &args->qsig.AocDivChargeReq; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &aoc_div_charge_req->diverting_user_number)); + + if (aoc_div_charge_req->charging_association_present) { + ASN1_CALL(pos, rose_enc_qsig_AOC_ChargingAssociation(ctrl, pos, end, + &aoc_div_charge_req->charging_association)); + } + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + aoc_div_charge_req->diversion_type)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the Time type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 time Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_AOC_Time(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigAOCTime *time) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s Time %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 | 1); + ASN1_CALL(pos, asn1_dec_int(ctrl, "lengthOfTimeUnit", tag, pos, seq_end, &value)); + time->length = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "scale", tag, pos, seq_end, &value)); + time->scale = value; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the Amount type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 amount Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_AOC_Amount(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigAOCAmount *amount) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s Amount %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 | 1); + ASN1_CALL(pos, asn1_dec_int(ctrl, "currencyAmount", tag, pos, seq_end, &value)); + amount->currency = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "multiplier", tag, pos, seq_end, &value)); + amount->multiplier = value; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the RecordedCurrency type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 recorded Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_AOC_RecordedCurrency(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigAOCRecordedCurrency *recorded) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + size_t str_len; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s RecordedCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "rCurrency", tag, pos, seq_end, + sizeof(recorded->currency), recorded->currency, &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); + ASN1_CALL(pos, rose_dec_qsig_AOC_Amount(ctrl, "rAmount", tag, pos, seq_end, + &recorded->amount)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the DurationCurrency type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 duration Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_AOC_DurationCurrency(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigAOCDurationCurrency *duration) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + size_t str_len; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s DurationCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "dCurrency", tag, pos, seq_end, + sizeof(duration->currency), duration->currency, &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); + ASN1_CALL(pos, rose_dec_qsig_AOC_Amount(ctrl, "dAmount", tag, pos, seq_end, + &duration->amount)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, asn1_dec_int(ctrl, "dChargingType", tag, pos, seq_end, &value)); + duration->charging_type = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4); + ASN1_CALL(pos, rose_dec_qsig_AOC_Time(ctrl, "dTime", tag, pos, seq_end, + &duration->time)); + + 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_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 5); + ASN1_CALL(pos, rose_dec_qsig_AOC_Time(ctrl, "dGranularity", tag, pos, seq_end, + &duration->granularity)); + duration->granularity_present = 1; + } else { + duration->granularity_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the FlatRateCurrency type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 flat_rate Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_AOC_FlatRateCurrency(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigAOCFlatRateCurrency *flat_rate) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + size_t str_len; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s FlatRateCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "fRCurrency", tag, pos, seq_end, + sizeof(flat_rate->currency), flat_rate->currency, &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); + ASN1_CALL(pos, rose_dec_qsig_AOC_Amount(ctrl, "fRAmount", tag, pos, seq_end, + &flat_rate->amount)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the VolumeRateCurrency type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 volume_rate Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_AOC_VolumeRateCurrency(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigAOCVolumeRateCurrency *volume_rate) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + size_t str_len; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s VolumeRateCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "vRCurrency", tag, pos, seq_end, + sizeof(volume_rate->currency), volume_rate->currency, &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); + ASN1_CALL(pos, rose_dec_qsig_AOC_Amount(ctrl, "vRAmount", tag, pos, seq_end, + &volume_rate->amount)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, asn1_dec_int(ctrl, "vRVolumeUnit", tag, pos, seq_end, &value)); + volume_rate->unit = value; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the AOCSCurrencyInfo type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 currency_info Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_AOCSCurrencyInfo(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigAOCSCurrencyInfo *currency_info) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s AOCSCurrencyInfo %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_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "chargedItem", tag, pos, seq_end, &value)); + currency_info->charged_item = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_TYPE_INTEGER: + currency_info->currency_type = 0; /* specialChargingCode */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "specialChargingCode", tag, pos, seq_end, + &value)); + currency_info->u.special_charging_code = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + currency_info->currency_type = 1; /* durationCurrency */ + ASN1_CALL(pos, rose_dec_qsig_AOC_DurationCurrency(ctrl, "durationCurrency", tag, + pos, seq_end, ¤cy_info->u.duration)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + currency_info->currency_type = 2; /* flatRateCurrency */ + ASN1_CALL(pos, rose_dec_qsig_AOC_FlatRateCurrency(ctrl, "flatRateCurrency", tag, + pos, seq_end, ¤cy_info->u.flat_rate)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: + currency_info->currency_type = 3; /* volumeRateCurrency */ + ASN1_CALL(pos, rose_dec_qsig_AOC_VolumeRateCurrency(ctrl, "volumeRateCurrency", + tag, pos, seq_end, ¤cy_info->u.volume_rate)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + currency_info->currency_type = 4; /* freeOfCharge */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 5: + currency_info->currency_type = 5; /* currencyInfoNotAvailable */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "currencyInfoNotAvailable", tag, pos, + seq_end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 6: + currency_info->currency_type = 6; /* freeOfChargeFromBeginning */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfChargeFromBeginning", tag, pos, + seq_end)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the AOCSCurrencyInfoList type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 currency_info Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_AOCSCurrencyInfoList(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigAOCSCurrencyInfoList *currency_info) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s AOCSCurrencyInfoList %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); + + currency_info->num_records = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + if (currency_info->num_records < ARRAY_LEN(currency_info->list)) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_qsig_AOCSCurrencyInfo(ctrl, "listEntry", tag, pos, + seq_end, ¤cy_info->list[currency_info->num_records])); + ++currency_info->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the ChargingAssociation type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 charging Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_AOC_ChargingAssociation(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigAOCChargingAssociation *charging) +{ + int32_t value; + int length; + int explicit_offset; + const unsigned char *explicit_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s ChargingAssociation\n", name); + } + switch (tag) { + case ASN1_TYPE_INTEGER: + charging->type = 0; /* charge_identifier */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "chargeIdentifier", tag, pos, end, &value)); + charging->id = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + charging->type = 1; /* charged_number */ + + /* 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, end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "chargedNumber", tag, pos, + explicit_end, &charging->number)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, end); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the Q.SIG ChargeRequest invoke argument 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. + */ +const unsigned char *rose_dec_qsig_ChargeRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + int advice_offset; + const unsigned char *seq_end; + const unsigned char *advice_end; + struct roseQsigChargeRequestArg_ARG *charge_request; + + charge_request = &args->qsig.ChargeRequest; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " ChargeRequest %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + /* SEQUENCE SIZE(0..7) OF AdviceModeCombination */ + 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, " adviceModeCombinations %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(advice_end, advice_offset, length, pos, seq_end); + + /* Decode SIZE(0..7) OF AdviceModeCombination */ + charge_request->num_records = 0; + while (pos < advice_end && *pos != ASN1_INDEF_TERM) { + if (charge_request->num_records < + ARRAY_LEN(charge_request->advice_mode_combinations)) { + ASN1_CALL(pos, asn1_dec_tag(pos, advice_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "adviceModeCombination", tag, pos, + advice_end, &value)); + charge_request->advice_mode_combinations[charge_request->num_records] = + value; + ++charge_request->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, advice_offset, advice_end, seq_end); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG ChargeRequest result argument 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. + */ +const unsigned char *rose_dec_qsig_ChargeRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigChargeRequestRes_RES *charge_request; + + charge_request = &args->qsig.ChargeRequest; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " ChargeRequest %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_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "adviceModeCombination", tag, pos, seq_end, + &value)); + charge_request->advice_mode_combination = value; + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG AocFinal invoke argument 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. + */ +const unsigned char *rose_dec_qsig_AocFinal_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + int specific_offset; + const unsigned char *seq_end; + const unsigned char *specific_end; + const unsigned char *save_pos; + struct roseQsigAocFinalArg_ARG *aoc_final; + + aoc_final = &args->qsig.AocFinal; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " AocFinal %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)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + aoc_final->type = 0; /* charge_not_available */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, seq_end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + aoc_final->type = 1; /* free_of_charge */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); + break; + case ASN1_TAG_SEQUENCE: + aoc_final->type = 2; /* specific_currency */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " specificCurrency %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(specific_end, specific_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); + ASN1_CALL(pos, rose_dec_qsig_AOC_RecordedCurrency(ctrl, "recordedCurrency", tag, + pos, specific_end, &aoc_final->specific.recorded)); + + if (pos < specific_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "finalBillingId", tag, pos, specific_end, + &value)); + aoc_final->specific.billing_id = value; + aoc_final->specific.billing_id_present = 1; + } else { + aoc_final->specific.billing_id_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, specific_offset, specific_end, seq_end); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + aoc_final->charging_association_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + case ASN1_TYPE_INTEGER: + ASN1_CALL(pos, rose_dec_qsig_AOC_ChargingAssociation(ctrl, + "chargingAssociation", tag, pos, seq_end, + &aoc_final->charging_association)); + aoc_final->charging_association_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " finalArgExtension %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG AocInterim invoke argument 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. + */ +const unsigned char *rose_dec_qsig_AocInterim_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + int specific_offset; + const unsigned char *seq_end; + const unsigned char *specific_end; + struct roseQsigAocInterimArg_ARG *aoc_interim; + + aoc_interim = &args->qsig.AocInterim; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " AocInterim %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)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + aoc_interim->type = 0; /* charge_not_available */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, seq_end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + aoc_interim->type = 1; /* free_of_charge */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); + break; + case ASN1_TAG_SEQUENCE: + aoc_interim->type = 2; /* specific_currency */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " specificCurrency %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(specific_end, specific_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); + ASN1_CALL(pos, rose_dec_qsig_AOC_RecordedCurrency(ctrl, "recordedCurrency", tag, + pos, specific_end, &aoc_interim->specific.recorded)); + + if (pos < specific_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "interimBillingId", tag, pos, specific_end, + &value)); + aoc_interim->specific.billing_id = value; + aoc_interim->specific.billing_id_present = 1; + } else { + aoc_interim->specific.billing_id_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, specific_offset, specific_end, seq_end); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG AocRate invoke argument 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. + */ +const unsigned char *rose_dec_qsig_AocRate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigAocRateArg_ARG *aoc_rate; + + aoc_rate = &args->qsig.AocRate; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " AocRate %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)); + switch (tag) { + case ASN1_TYPE_NULL: + aoc_rate->type = 0; /* charge_not_available */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, seq_end)); + break; + case ASN1_TAG_SEQUENCE: + aoc_rate->type = 1; /* currency_info_list */ + ASN1_CALL(pos, rose_dec_qsig_AOCSCurrencyInfoList(ctrl, "aocSCurrencyInfoList", + tag, pos, seq_end, &aoc_rate->currency_info)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG AocComplete invoke argument 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. + */ +const unsigned char *rose_dec_qsig_AocComplete_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigAocCompleteArg_ARG *aoc_complete; + + aoc_complete = &args->qsig.AocComplete; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " AocComplete %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_CALL(pos, rose_dec_PartyNumber(ctrl, "chargedUser", tag, pos, seq_end, + &aoc_complete->charged_user_number)); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + aoc_complete->charging_association_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + case ASN1_TYPE_INTEGER: + ASN1_CALL(pos, rose_dec_qsig_AOC_ChargingAssociation(ctrl, + "chargingAssociation", tag, pos, seq_end, + &aoc_complete->charging_association)); + aoc_complete->charging_association_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " completeArgExtension %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG AocComplete result argument 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. + */ +const unsigned char *rose_dec_qsig_AocComplete_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigAocCompleteRes_RES *aoc_complete; + + aoc_complete = &args->qsig.AocComplete; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " AocComplete %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_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "chargingOption", tag, pos, seq_end, &value)); + aoc_complete->charging_option = value; + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG AocDivChargeReq invoke argument 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. + */ +const unsigned char *rose_dec_qsig_AocDivChargeReq_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigAocDivChargeReqArg_ARG *aoc_div_charge_req; + + aoc_div_charge_req = &args->qsig.AocDivChargeReq; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " AocDivChargeReq %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_CALL(pos, rose_dec_PartyNumber(ctrl, "divertingUser", tag, pos, seq_end, + &aoc_div_charge_req->diverting_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + case ASN1_TYPE_INTEGER: + ASN1_CALL(pos, rose_dec_qsig_AOC_ChargingAssociation(ctrl, "chargingAssociation", + tag, pos, seq_end, &aoc_div_charge_req->charging_association)); + aoc_div_charge_req->charging_association_present = 1; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + break; + default: + aoc_div_charge_req->charging_association_present = 0; + break; + } + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionType", tag, pos, seq_end, &value)); + aoc_div_charge_req->diversion_type = value; + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_qsig_aoc.c */ Property changes on: rose_qsig_aoc.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose_etsi_aoc.c =================================================================== --- a/rose_etsi_aoc.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_etsi_aoc.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,1929 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 ROSE Advice Of Charge (AOC) operations + * + * Advice of Charge (AOC) supplementary service EN 300 182-1 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the Time 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param time Time information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOC_Time(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseEtsiAOCTime *time) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + time->length)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, time->scale)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the Amount 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param amount Amount information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOC_Amount(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseEtsiAOCAmount *amount) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + amount->currency)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + amount->multiplier)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the RecordedCurrency 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param recorded Recorded currency information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOC_RecordedCurrency(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCRecordedCurrency *recorded) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + recorded->currency, sizeof(recorded->currency) - 1)); + ASN1_CALL(pos, rose_enc_etsi_AOC_Amount(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, &recorded->amount)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the DurationCurrency 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param duration Duration currency information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOC_DurationCurrency(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCDurationCurrency *duration) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + duration->currency, sizeof(duration->currency) - 1)); + ASN1_CALL(pos, rose_enc_etsi_AOC_Amount(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, &duration->amount)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, + duration->charging_type)); + ASN1_CALL(pos, rose_enc_etsi_AOC_Time(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 4, &duration->time)); + if (duration->granularity_present) { + ASN1_CALL(pos, rose_enc_etsi_AOC_Time(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 5, &duration->granularity)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the FlatRateCurrency 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param flat_rate Flat rate currency information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOC_FlatRateCurrency(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCFlatRateCurrency *flat_rate) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + flat_rate->currency, sizeof(flat_rate->currency) - 1)); + ASN1_CALL(pos, rose_enc_etsi_AOC_Amount(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, &flat_rate->amount)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the VolumeRateCurrency 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param volume_rate Volume rate currency information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOC_VolumeRateCurrency(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCVolumeRateCurrency *volume_rate) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1, + volume_rate->currency, sizeof(volume_rate->currency) - 1)); + ASN1_CALL(pos, rose_enc_etsi_AOC_Amount(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, &volume_rate->amount)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, + volume_rate->unit)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the AOCSCurrencyInfo 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param currency_info Currency information record to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOCSCurrencyInfo(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCSCurrencyInfo *currency_info) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + currency_info->charged_item)); + + switch (currency_info->currency_type) { + case 0: /* specialChargingCode */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + currency_info->u.special_charging_code)); + break; + case 1: /* durationCurrency */ + ASN1_CALL(pos, rose_enc_etsi_AOC_DurationCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, ¤cy_info->u.duration)); + break; + case 2: /* flatRateCurrency */ + ASN1_CALL(pos, rose_enc_etsi_AOC_FlatRateCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 2, ¤cy_info->u.flat_rate)); + break; + case 3: /* volumeRateCurrency */ + ASN1_CALL(pos, rose_enc_etsi_AOC_VolumeRateCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 3, ¤cy_info->u.volume_rate)); + break; + case 4: /* freeOfCharge */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4)); + break; + case 5: /* currencyInfoNotAvailable */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 5)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown currency type"); + return NULL; + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the AOCSCurrencyInfoList 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param currency_info Currency information list to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOCSCurrencyInfoList(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCSCurrencyInfoList *currency_info) +{ + unsigned index; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + for (index = 0; index < currency_info->num_records; ++index) { + ASN1_CALL(pos, rose_enc_etsi_AOCSCurrencyInfo(ctrl, pos, end, ASN1_TAG_SEQUENCE, + ¤cy_info->list[index])); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the RecordedUnits 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param recorded Recorded units information record to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOC_RecordedUnits(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCRecordedUnits *recorded) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + if (recorded->not_available) { + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + } else { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + recorded->number_of_units)); + } + + if (recorded->type_of_unit_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + recorded->type_of_unit)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the RecordedUnitsList 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param recorded_info Recorded units information list to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOC_RecordedUnitsList(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCRecordedUnitsList *recorded_info) +{ + unsigned index; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + for (index = 0; index < recorded_info->num_records; ++index) { + ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedUnits(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &recorded_info->list[index])); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the ChargingAssociation 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 charging Charging association information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOC_ChargingAssociation(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct roseEtsiAOCChargingAssociation *charging) +{ + unsigned char *explicit_len; + + switch (charging->type) { + case 0: /* charge_identifier */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, charging->id)); + break; + case 1: /* charged_number */ + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &charging->number)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown ChargingAssociation type"); + return NULL; + } + + return pos; +} + +/*! + * \internal + * \brief Encode the AOCECurrencyInfo 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param currency_info Currency information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOCECurrencyInfo(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCECurrencyInfo *currency_info) +{ + unsigned char *seq_len; + unsigned char *specific_seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + if (currency_info->free_of_charge) { + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); + } else { + ASN1_CONSTRUCTED_BEGIN(specific_seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, ¤cy_info->specific.recorded)); + + if (currency_info->specific.billing_id_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + currency_info->specific.billing_id)); + } + + ASN1_CONSTRUCTED_END(specific_seq_len, pos, end); + } + + if (currency_info->charging_association_present) { + ASN1_CALL(pos, rose_enc_etsi_AOC_ChargingAssociation(ctrl, pos, end, + ¤cy_info->charging_association)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the AOCEChargingUnitInfo 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param charging_unit Charging unit information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_AOCEChargingUnitInfo(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiAOCEChargingUnitInfo *charging_unit) +{ + unsigned char *seq_len; + unsigned char *specific_seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + if (charging_unit->free_of_charge) { + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); + } else { + ASN1_CONSTRUCTED_BEGIN(specific_seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedUnitsList(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, &charging_unit->specific.recorded)); + + if (charging_unit->specific.billing_id_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + charging_unit->specific.billing_id)); + } + + ASN1_CONSTRUCTED_END(specific_seq_len, pos, end); + } + + if (charging_unit->charging_association_present) { + ASN1_CALL(pos, rose_enc_etsi_AOC_ChargingAssociation(ctrl, pos, end, + &charging_unit->charging_association)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the ChargingRequest invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_ChargingRequest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + args->etsi.ChargingRequest.charging_case); +} + +/*! + * \brief Encode the ChargingRequest result facility ie 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. + */ +unsigned char *rose_enc_etsi_ChargingRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + switch (args->etsi.ChargingRequest.type) { + case 0: /* currency_info_list */ + ASN1_CALL(pos, rose_enc_etsi_AOCSCurrencyInfoList(ctrl, pos, end, + ASN1_TAG_SEQUENCE, &args->etsi.ChargingRequest.u.currency_info)); + break; + case 1: /* special_arrangement_info */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.ChargingRequest.u.special_arrangement)); + break; + case 2: /* charging_info_follows */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown ChargingRequst type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the AOCSCurrency invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_AOCSCurrency_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + switch (args->etsi.AOCSCurrency.type) { + case 0: /* charge_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + break; + case 1: /* currency_info_list */ + ASN1_CALL(pos, rose_enc_etsi_AOCSCurrencyInfoList(ctrl, pos, end, + ASN1_TAG_SEQUENCE, &args->etsi.AOCSCurrency.currency_info)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown AOCSCurrency type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the AOCSSpecialArr invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_AOCSSpecialArr_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + switch (args->etsi.AOCSSpecialArr.type) { + case 0: /* charge_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + break; + case 1: /* special_arrangement_info */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.AOCSSpecialArr.special_arrangement)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown AOCSSpecialArr type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the AOCDCurrency invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_AOCDCurrency_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiAOCDCurrency_ARG *aoc_d; + unsigned char *seq_len; + + aoc_d = &args->etsi.AOCDCurrency; + switch (aoc_d->type) { + case 0: /* charge_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + break; + case 1: /* free_of_charge */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); + break; + case 2: /* specific_currency */ + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedCurrency(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, &aoc_d->specific.recorded)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + aoc_d->specific.type_of_charging_info)); + if (aoc_d->specific.billing_id_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, + aoc_d->specific.billing_id)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown AOCDCurrency type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the AOCDChargingUnit invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_AOCDChargingUnit_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiAOCDChargingUnit_ARG *aoc_d; + unsigned char *seq_len; + + aoc_d = &args->etsi.AOCDChargingUnit; + switch (aoc_d->type) { + case 0: /* charge_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + break; + case 1: /* free_of_charge */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1)); + break; + case 2: /* specific_charging_units */ + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_etsi_AOC_RecordedUnitsList(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, &aoc_d->specific.recorded)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + aoc_d->specific.type_of_charging_info)); + if (aoc_d->specific.billing_id_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3, + aoc_d->specific.billing_id)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown AOCDChargingUnit type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the AOCECurrency invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_AOCECurrency_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + switch (args->etsi.AOCECurrency.type) { + case 0: /* charge_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + break; + case 1: /* currency_info */ + ASN1_CALL(pos, rose_enc_etsi_AOCECurrencyInfo(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &args->etsi.AOCECurrency.currency_info)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown AOCECurrency type"); + return NULL; + } + + return pos; +} + +/*! + * \brief Encode the AOCEChargingUnit invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_AOCEChargingUnit_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + switch (args->etsi.AOCEChargingUnit.type) { + case 0: /* charge_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + break; + case 1: /* charging_unit */ + ASN1_CALL(pos, rose_enc_etsi_AOCEChargingUnitInfo(ctrl, pos, end, + ASN1_TAG_SEQUENCE, &args->etsi.AOCEChargingUnit.charging_unit)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown AOCEChargingUnit type"); + return NULL; + } + + return pos; +} + +/*! + * \internal + * \brief Decode the Time type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 time Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOC_Time(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCTime *time) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s Time %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 | 1); + ASN1_CALL(pos, asn1_dec_int(ctrl, "lengthOfTimeUnit", tag, pos, seq_end, &value)); + time->length = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "scale", tag, pos, seq_end, &value)); + time->scale = value; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the Amount type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 amount Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOC_Amount(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCAmount *amount) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s Amount %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 | 1); + ASN1_CALL(pos, asn1_dec_int(ctrl, "currencyAmount", tag, pos, seq_end, &value)); + amount->currency = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "multiplier", tag, pos, seq_end, &value)); + amount->multiplier = value; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the RecordedCurrency type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 recorded Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOC_RecordedCurrency(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCRecordedCurrency *recorded) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + size_t str_len; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s RecordedCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "rCurrency", tag, pos, seq_end, + sizeof(recorded->currency), recorded->currency, &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); + ASN1_CALL(pos, rose_dec_etsi_AOC_Amount(ctrl, "rAmount", tag, pos, seq_end, + &recorded->amount)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the DurationCurrency type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 duration Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOC_DurationCurrency(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCDurationCurrency *duration) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + size_t str_len; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s DurationCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "dCurrency", tag, pos, seq_end, + sizeof(duration->currency), duration->currency, &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); + ASN1_CALL(pos, rose_dec_etsi_AOC_Amount(ctrl, "dAmount", tag, pos, seq_end, + &duration->amount)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, asn1_dec_int(ctrl, "dChargingType", tag, pos, seq_end, &value)); + duration->charging_type = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 4); + ASN1_CALL(pos, rose_dec_etsi_AOC_Time(ctrl, "dTime", tag, pos, seq_end, + &duration->time)); + + 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_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 5); + ASN1_CALL(pos, rose_dec_etsi_AOC_Time(ctrl, "dGranularity", tag, pos, seq_end, + &duration->granularity)); + duration->granularity_present = 1; + } else { + duration->granularity_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the FlatRateCurrency type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 flat_rate Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOC_FlatRateCurrency(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCFlatRateCurrency *flat_rate) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + size_t str_len; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s FlatRateCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "fRCurrency", tag, pos, seq_end, + sizeof(flat_rate->currency), flat_rate->currency, &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); + ASN1_CALL(pos, rose_dec_etsi_AOC_Amount(ctrl, "fRAmount", tag, pos, seq_end, + &flat_rate->amount)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the VolumeRateCurrency type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 volume_rate Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOC_VolumeRateCurrency(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCVolumeRateCurrency *volume_rate) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + size_t str_len; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s VolumeRateCurrency %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 & ~ASN1_PC_MASK, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "vRCurrency", tag, pos, seq_end, + sizeof(volume_rate->currency), volume_rate->currency, &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2); + ASN1_CALL(pos, rose_dec_etsi_AOC_Amount(ctrl, "vRAmount", tag, pos, seq_end, + &volume_rate->amount)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, asn1_dec_int(ctrl, "vRVolumeUnit", tag, pos, seq_end, &value)); + volume_rate->unit = value; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the AOCSCurrencyInfo type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 currency_info Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOCSCurrencyInfo(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCSCurrencyInfo *currency_info) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s AOCSCurrencyInfo %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_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "chargedItem", tag, pos, seq_end, &value)); + currency_info->charged_item = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_TYPE_INTEGER: + currency_info->currency_type = 0; /* specialChargingCode */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "specialChargingCode", tag, pos, seq_end, + &value)); + currency_info->u.special_charging_code = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + currency_info->currency_type = 1; /* durationCurrency */ + ASN1_CALL(pos, rose_dec_etsi_AOC_DurationCurrency(ctrl, "durationCurrency", tag, + pos, seq_end, ¤cy_info->u.duration)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + currency_info->currency_type = 2; /* flatRateCurrency */ + ASN1_CALL(pos, rose_dec_etsi_AOC_FlatRateCurrency(ctrl, "flatRateCurrency", tag, + pos, seq_end, ¤cy_info->u.flat_rate)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3: + currency_info->currency_type = 3; /* volumeRateCurrency */ + ASN1_CALL(pos, rose_dec_etsi_AOC_VolumeRateCurrency(ctrl, "volumeRateCurrency", + tag, pos, seq_end, ¤cy_info->u.volume_rate)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + currency_info->currency_type = 4; /* freeOfCharge */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 5: + currency_info->currency_type = 5; /* currencyInfoNotAvailable */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "currencyInfoNotAvailable", tag, pos, + seq_end)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the AOCSCurrencyInfoList type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 currency_info Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOCSCurrencyInfoList(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCSCurrencyInfoList *currency_info) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s AOCSCurrencyInfoList %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); + + currency_info->num_records = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + if (currency_info->num_records < ARRAY_LEN(currency_info->list)) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_etsi_AOCSCurrencyInfo(ctrl, "listEntry", tag, pos, + seq_end, ¤cy_info->list[currency_info->num_records])); + ++currency_info->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the RecordedUnits type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 recorded Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOC_RecordedUnits(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCRecordedUnits *recorded) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s RecordedUnits %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)); + switch (tag) { + case ASN1_TYPE_INTEGER: + recorded->not_available = 0; + ASN1_CALL(pos, asn1_dec_int(ctrl, "recordedNumberOfUnits", tag, pos, seq_end, + &value)); + recorded->number_of_units = value; + break; + case ASN1_TYPE_NULL: + recorded->not_available = 1; + recorded->number_of_units = 0; + ASN1_CALL(pos, asn1_dec_null(ctrl, "notAvailable", tag, pos, seq_end)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + 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_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "recordedTypeOfUnits", tag, pos, seq_end, + &value)); + recorded->type_of_unit = value; + recorded->type_of_unit_present = 1; + } else { + recorded->type_of_unit_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the RecordedUnitsList type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 recorded_info Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOC_RecordedUnitsList(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCRecordedUnitsList *recorded_info) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s RecordedUnitsList %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); + + recorded_info->num_records = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + if (recorded_info->num_records < ARRAY_LEN(recorded_info->list)) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_etsi_AOC_RecordedUnits(ctrl, "listEntry", tag, pos, + seq_end, &recorded_info->list[recorded_info->num_records])); + ++recorded_info->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the ChargingAssociation type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 charging Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOC_ChargingAssociation(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCChargingAssociation *charging) +{ + int32_t value; + int length; + int explicit_offset; + const unsigned char *explicit_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s ChargingAssociation\n", name); + } + switch (tag) { + case ASN1_TYPE_INTEGER: + charging->type = 0; /* charge_identifier */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "chargeIdentifier", tag, pos, end, &value)); + charging->id = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + charging->type = 1; /* charged_number */ + + /* 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, end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "chargedNumber", tag, pos, + explicit_end, &charging->number)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, end); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \internal + * \brief Decode the AOCECurrencyInfo type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 currency_info Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOCECurrencyInfo(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCECurrencyInfo *currency_info) +{ + int32_t value; + int length; + int seq_offset; + int specific_offset; + const unsigned char *seq_end; + const unsigned char *specific_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s AOCECurrencyInfo %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)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + currency_info->free_of_charge = 1; + ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); + break; + case ASN1_TAG_SEQUENCE: + currency_info->free_of_charge = 0; + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " specificCurrency %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(specific_end, specific_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); + ASN1_CALL(pos, rose_dec_etsi_AOC_RecordedCurrency(ctrl, "recordedCurrency", tag, + pos, specific_end, ¤cy_info->specific.recorded)); + + if (pos < specific_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "billingId", tag, pos, specific_end, + &value)); + currency_info->specific.billing_id = value; + currency_info->specific.billing_id_present = 1; + } else { + currency_info->specific.billing_id_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, specific_offset, specific_end, seq_end); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_AOC_ChargingAssociation(ctrl, "chargingAssociation", + tag, pos, seq_end, ¤cy_info->charging_association)); + currency_info->charging_association_present = 1; + } else { + currency_info->charging_association_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the AOCEChargingUnitInfo type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 charging_unit Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_AOCEChargingUnitInfo(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiAOCEChargingUnitInfo *charging_unit) +{ + int32_t value; + int length; + int seq_offset; + int specific_offset; + const unsigned char *seq_end; + const unsigned char *specific_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s AOCEChargingUnitInfo %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)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + charging_unit->free_of_charge = 1; + ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, seq_end)); + break; + case ASN1_TAG_SEQUENCE: + charging_unit->free_of_charge = 0; + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " specificChargingUnits %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(specific_end, specific_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); + ASN1_CALL(pos, rose_dec_etsi_AOC_RecordedUnitsList(ctrl, "recordedUnitsList", + tag, pos, specific_end, &charging_unit->specific.recorded)); + + if (pos < specific_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, specific_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "billingId", tag, pos, specific_end, + &value)); + charging_unit->specific.billing_id = value; + charging_unit->specific.billing_id_present = 1; + } else { + charging_unit->specific.billing_id_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, specific_offset, specific_end, seq_end); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_AOC_ChargingAssociation(ctrl, "chargingAssociation", + tag, pos, seq_end, &charging_unit->charging_association)); + charging_unit->charging_association_present = 1; + } else { + charging_unit->charging_association_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the ChargingRequest invoke argument 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. + */ +const unsigned char *rose_dec_etsi_ChargingRequest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "chargingCase", tag, pos, end, &value)); + args->etsi.ChargingRequest.charging_case = value; + + return pos; +} + +/*! + * \brief Decode the ChargingRequest result 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. + */ +const unsigned char *rose_dec_etsi_ChargingRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + struct roseEtsiChargingRequest_RES *charging_request; + int32_t value; + + charging_request = &args->etsi.ChargingRequest; + switch (tag) { + case ASN1_TAG_SEQUENCE: + charging_request->type = 0; /* currency_info_list */ + ASN1_CALL(pos, rose_dec_etsi_AOCSCurrencyInfoList(ctrl, "currencyList", tag, pos, + end, &charging_request->u.currency_info)); + break; + case ASN1_TYPE_INTEGER: + charging_request->type = 1; /* special_arrangement_info */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "specialArrangement", tag, pos, end, &value)); + charging_request->u.special_arrangement = value; + break; + case ASN1_TYPE_NULL: + charging_request->type = 2; /* charging_info_follows */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargingInfoFollows", tag, pos, end)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the AOCSCurrency invoke argument 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. + */ +const unsigned char *rose_dec_etsi_AOCSCurrency_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiAOCSCurrency_ARG *aoc_s; + + aoc_s = &args->etsi.AOCSCurrency; + switch (tag) { + case ASN1_TYPE_NULL: + aoc_s->type = 0; /* charge_not_available */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); + break; + case ASN1_TAG_SEQUENCE: + aoc_s->type = 1; /* currency_info_list */ + ASN1_CALL(pos, rose_dec_etsi_AOCSCurrencyInfoList(ctrl, "currencyInfo", tag, pos, + end, &aoc_s->currency_info)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the AOCSSpecialArr invoke argument 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. + */ +const unsigned char *rose_dec_etsi_AOCSSpecialArr_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiAOCSSpecialArr_ARG *aoc_s; + int32_t value; + + aoc_s = &args->etsi.AOCSSpecialArr; + switch (tag) { + case ASN1_TYPE_NULL: + aoc_s->type = 0; /* charge_not_available */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); + break; + case ASN1_TYPE_INTEGER: + aoc_s->type = 1; /* special_arrangement_info */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "specialArrangement", tag, pos, end, &value)); + aoc_s->special_arrangement = value; + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the AOCDCurrency invoke argument 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. + */ +const unsigned char *rose_dec_etsi_AOCDCurrency_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiAOCDCurrency_ARG *aoc_d; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + aoc_d = &args->etsi.AOCDCurrency; + switch (tag) { + case ASN1_TYPE_NULL: + aoc_d->type = 0; /* charge_not_available */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + aoc_d->type = 1; /* free_of_charge */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, end)); + break; + case ASN1_TAG_SEQUENCE: + aoc_d->type = 2; /* specific_currency */ + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " specificCurrency %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_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); + ASN1_CALL(pos, rose_dec_etsi_AOC_RecordedCurrency(ctrl, "recordedCurrency", tag, + pos, seq_end, &aoc_d->specific.recorded)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "typeOfChargingInfo", tag, pos, seq_end, + &value)); + aoc_d->specific.type_of_charging_info = value; + + 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_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, asn1_dec_int(ctrl, "billingId", tag, pos, seq_end, &value)); + aoc_d->specific.billing_id = value; + aoc_d->specific.billing_id_present = 1; + } else { + aoc_d->specific.billing_id_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the AOCDChargingUnit invoke argument 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. + */ +const unsigned char *rose_dec_etsi_AOCDChargingUnit_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiAOCDChargingUnit_ARG *aoc_d; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + aoc_d = &args->etsi.AOCDChargingUnit; + switch (tag) { + case ASN1_TYPE_NULL: + aoc_d->type = 0; /* charge_not_available */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + aoc_d->type = 1; /* free_of_charge */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "freeOfCharge", tag, pos, end)); + break; + case ASN1_TAG_SEQUENCE: + aoc_d->type = 2; /* specific_charging_units */ + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " specificChargingUnits %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_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); + ASN1_CALL(pos, rose_dec_etsi_AOC_RecordedUnitsList(ctrl, "recordedUnitsList", + tag, pos, seq_end, &aoc_d->specific.recorded)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "typeOfChargingInfo", tag, pos, seq_end, + &value)); + aoc_d->specific.type_of_charging_info = value; + + 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_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, asn1_dec_int(ctrl, "billingId", tag, pos, seq_end, &value)); + aoc_d->specific.billing_id = value; + aoc_d->specific.billing_id_present = 1; + } else { + aoc_d->specific.billing_id_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the AOCECurrency invoke argument 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. + */ +const unsigned char *rose_dec_etsi_AOCECurrency_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiAOCECurrency_ARG *aoc_e; + + aoc_e = &args->etsi.AOCECurrency; + switch (tag) { + case ASN1_TYPE_NULL: + aoc_e->type = 0; /* charge_not_available */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); + break; + case ASN1_TAG_SEQUENCE: + aoc_e->type = 1; /* currency_info */ + ASN1_CALL(pos, rose_dec_etsi_AOCECurrencyInfo(ctrl, "currencyInfo", tag, pos, + end, &aoc_e->currency_info)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \brief Decode the AOCEChargingUnit invoke argument 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. + */ +const unsigned char *rose_dec_etsi_AOCEChargingUnit_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiAOCEChargingUnit_ARG *aoc_e; + + aoc_e = &args->etsi.AOCEChargingUnit; + switch (tag) { + case ASN1_TYPE_NULL: + aoc_e->type = 0; /* charge_not_available */ + ASN1_CALL(pos, asn1_dec_null(ctrl, "chargeNotAvailable", tag, pos, end)); + break; + case ASN1_TAG_SEQUENCE: + aoc_e->type = 1; /* charging_unit */ + ASN1_CALL(pos, rose_dec_etsi_AOCEChargingUnitInfo(ctrl, "chargingUnitInfo", tag, + pos, end, &aoc_e->charging_unit)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_etsi_aoc.c */ Property changes on: rose_etsi_aoc.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose_etsi_diversion.c =================================================================== --- a/rose_etsi_diversion.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_etsi_diversion.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,1623 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 ROSE Call diversion operations + * + * Diversion Supplementary Services ETS 300 207-1 Table 3 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the ServedUserNr 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 served_user_number Served user number information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_ServedUserNumber(struct pri *ctrl, + unsigned char *pos, unsigned char *end, + const struct rosePartyNumber *served_user_number) +{ + if (served_user_number->length) { + /* Forward this number */ + pos = rose_enc_PartyNumber(ctrl, pos, end, served_user_number); + } else { + /* Forward all numbers */ + pos = asn1_enc_null(pos, end, ASN1_TYPE_NULL); + } + + return pos; +} + +/*! + * \internal + * \brief Encode the IntResult 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller implicitly + * tags it otherwise. + * \param int_result Forwarding record information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_IntResult(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseEtsiForwardingRecord *int_result) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &int_result->served_user_number)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + int_result->basic_service)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, int_result->procedure)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &int_result->forwarded_to)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the IntResultList 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SET unless the caller implicitly + * tags it otherwise. + * \param int_result_list Forwarding record list information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_IntResultList(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, + const struct roseEtsiForwardingList *int_result_list) +{ + unsigned index; + unsigned char *set_len; + + ASN1_CONSTRUCTED_BEGIN(set_len, pos, end, tag); + + for (index = 0; index < int_result_list->num_records; ++index) { + ASN1_CALL(pos, rose_enc_etsi_IntResult(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &int_result_list->list[index])); + } + + ASN1_CONSTRUCTED_END(set_len, pos, end); + + return pos; +} + +/*! + * \internal + * \brief Encode the ServedUserNumberList 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SET unless the caller implicitly + * tags it otherwise. + * \param served_user_number_list Served user record list information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_etsi_ServedUserNumberList(struct pri *ctrl, + unsigned char *pos, unsigned char *end, unsigned tag, + const struct roseEtsiServedUserNumberList *served_user_number_list) +{ + unsigned index; + unsigned char *set_len; + + ASN1_CONSTRUCTED_BEGIN(set_len, pos, end, tag); + + for (index = 0; index < served_user_number_list->num_records; ++index) { + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &served_user_number_list->number[index])); + } + + ASN1_CONSTRUCTED_END(set_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the ActivationDiversion invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_ActivationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiActivationDiversion_ARG *activation_diversion; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + activation_diversion = &args->etsi.ActivationDiversion; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activation_diversion->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activation_diversion->basic_service)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &activation_diversion->forwarded_to)); + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &activation_diversion->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DeactivationDiversion invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_DeactivationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDeactivationDiversion_ARG *deactivation_diversion; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + deactivation_diversion = &args->etsi.DeactivationDiversion; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivation_diversion->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivation_diversion->basic_service)); + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &deactivation_diversion->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the ActivationStatusNotificationDiv invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiActivationStatusNotificationDiv_ARG + *activation_status_notification_div; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + activation_status_notification_div = &args->etsi.ActivationStatusNotificationDiv; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activation_status_notification_div->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + activation_status_notification_div->basic_service)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &activation_status_notification_div->forwarded_to)); + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &activation_status_notification_div->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DeactivationStatusNotificationDiv invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_DeactivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDeactivationStatusNotificationDiv_ARG + *deactivation_status_notification_div; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + deactivation_status_notification_div = &args->etsi.DeactivationStatusNotificationDiv; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivation_status_notification_div->procedure)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + deactivation_status_notification_div->basic_service)); + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &deactivation_status_notification_div->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the InterrogationDiversion invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_InterrogationDiversion_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiInterrogationDiversion_ARG *interrogation_diversion; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + interrogation_diversion = &args->etsi.InterrogationDiversion; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + interrogation_diversion->procedure)); + if (interrogation_diversion->basic_service) { + /* Not the DEFAULT value */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + interrogation_diversion->basic_service)); + } + ASN1_CALL(pos, rose_enc_etsi_ServedUserNumber(ctrl, pos, end, + &interrogation_diversion->served_user_number)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the InterrogationDiversion result facility ie 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. + */ +unsigned char *rose_enc_etsi_InterrogationDiversion_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_IntResultList(ctrl, pos, end, ASN1_TAG_SET, + &args->etsi.InterrogationDiversion); +} + +/*! + * \brief Encode the DiversionInformation invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_DiversionInformation_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDiversionInformation_ARG *diversion_information; + unsigned char *seq_len; + unsigned char *explicit_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + diversion_information = &args->etsi.DiversionInformation; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diversion_information->diversion_reason)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diversion_information->basic_service)); + if (diversion_information->served_user_subaddress.length) { + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &diversion_information->served_user_subaddress)); + } + if (diversion_information->calling_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0); + ASN1_CALL(pos, rose_enc_PresentedAddressScreened(ctrl, pos, end, + &diversion_information->calling)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (diversion_information->original_called_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diversion_information->original_called)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (diversion_information->last_diverting_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diversion_information->last_diverting)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (diversion_information->last_diverting_reason_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diversion_information->last_diverting_reason)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + if (diversion_information->q931ie.length) { + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &diversion_information->q931ie)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CallDeflection invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_CallDeflection_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiCallDeflection_ARG *call_deflection; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + call_deflection = &args->etsi.CallDeflection; + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &call_deflection->deflection)); + if (call_deflection->presentation_allowed_to_diverted_to_user_present) { + ASN1_CALL(pos, asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, + call_deflection->presentation_allowed_to_diverted_to_user)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the CallRerouting invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_CallRerouting_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiCallRerouting_ARG *call_rerouting; + unsigned char *seq_len; + unsigned char *explicit_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + call_rerouting = &args->etsi.CallRerouting; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + call_rerouting->rerouting_reason)); + ASN1_CALL(pos, rose_enc_Address(ctrl, pos, end, ASN1_TAG_SEQUENCE, + &call_rerouting->called_address)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + call_rerouting->rerouting_counter)); + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &call_rerouting->q931ie)); + + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &call_rerouting->last_rerouting)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + + if (call_rerouting->subscription_option) { + /* Not the DEFAULT value */ + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + call_rerouting->subscription_option)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + + if (call_rerouting->calling_subaddress.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &call_rerouting->calling_subaddress)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the InterrogateServedUserNumbers result facility ie 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. + */ +unsigned char *rose_enc_etsi_InterrogateServedUserNumbers_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) +{ + return rose_enc_etsi_ServedUserNumberList(ctrl, pos, end, ASN1_TAG_SET, + &args->etsi.InterrogateServedUserNumbers); +} + +/*! + * \brief Encode the DivertingLegInformation1 invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDivertingLegInformation1_ARG *diverting_leg_information_1; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + diverting_leg_information_1 = &args->etsi.DivertingLegInformation1; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diverting_leg_information_1->diversion_reason)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diverting_leg_information_1->subscription_option)); + if (diverting_leg_information_1->diverted_to_present) { + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diverting_leg_information_1->diverted_to)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DivertingLegInformation2 invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiDivertingLegInformation2_ARG *diverting_leg_information_2; + unsigned char *seq_len; + unsigned char *explicit_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + diverting_leg_information_2 = &args->etsi.DivertingLegInformation2; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + diverting_leg_information_2->diversion_counter)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + diverting_leg_information_2->diversion_reason)); + + if (diverting_leg_information_2->diverting_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diverting_leg_information_2->diverting)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + + if (diverting_leg_information_2->original_called_present) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &diverting_leg_information_2->original_called)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the DivertingLegInformation3 invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_boolean(pos, end, ASN1_TYPE_BOOLEAN, + args->etsi.DivertingLegInformation3.presentation_allowed_indicator); +} + +/*! + * \internal + * \brief Decode the ServedUserNr 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 served_user_number Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_ServedUserNumber(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct rosePartyNumber *served_user_number) +{ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s ServedUserNumber\n", name); + } + if (tag == ASN1_TYPE_NULL) { + served_user_number->length = 0; + pos = asn1_dec_null(ctrl, "allNumbers", tag, pos, end); + } else { + /* Must be a PartyNumber (Which is itself a CHOICE) */ + pos = + rose_dec_PartyNumber(ctrl, "individualNumber", tag, pos, end, + served_user_number); + } + return pos; +} + +/*! + * \internal + * \brief Decode the IntResult 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 int_result Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_IntResult(struct pri *ctrl, const char *name, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiForwardingRecord *int_result) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s IntResult %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_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &int_result->served_user_number)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + int_result->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + int_result->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "forwardedToAddress", tag, pos, seq_end, + &int_result->forwarded_to)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the IntResultList 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 int_result_list Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_IntResultList(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiForwardingList *int_result_list) +{ + int length; + int set_offset; + const unsigned char *set_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s IntResultList %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(set_end, set_offset, length, pos, end); + + int_result_list->num_records = 0; + while (pos < set_end && *pos != ASN1_INDEF_TERM) { + if (int_result_list->num_records < ARRAY_LEN(int_result_list->list)) { + ASN1_CALL(pos, asn1_dec_tag(pos, set_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_etsi_IntResult(ctrl, "listEntry", tag, pos, set_end, + &int_result_list->list[int_result_list->num_records])); + ++int_result_list->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, set_offset, set_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the ServedUserNumberList 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 served_user_number_list Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_etsi_ServedUserNumberList(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseEtsiServedUserNumberList *served_user_number_list) +{ + int length; + int set_offset; + const unsigned char *set_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s ServedUserNumberList %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(set_end, set_offset, length, pos, end); + + served_user_number_list->num_records = 0; + while (pos < set_end && *pos != ASN1_INDEF_TERM) { + if (served_user_number_list->num_records < + ARRAY_LEN(served_user_number_list->number)) { + ASN1_CALL(pos, asn1_dec_tag(pos, set_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "listEntry", tag, pos, set_end, + &served_user_number_list->number[served_user_number_list->num_records])); + ++served_user_number_list->num_records; + } else { + /* Too many records */ + return NULL; + } + } + + ASN1_END_FIXUP(ctrl, pos, set_offset, set_end, end); + + return pos; +} + +/*! + * \brief Decode the ActivationDiversion invoke argument 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. + */ +const unsigned char *rose_dec_etsi_ActivationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiActivationDiversion_ARG *activation_diversion; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " ActivationDiversion %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + activation_diversion = &args->etsi.ActivationDiversion; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + activation_diversion->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + activation_diversion->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "forwardedToAddress", tag, pos, seq_end, + &activation_diversion->forwarded_to)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &activation_diversion->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the DeactivationDiversion invoke argument 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. + */ +const unsigned char *rose_dec_etsi_DeactivationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDeactivationDiversion_ARG *deactivation_diversion; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DeactivationDiversion %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + deactivation_diversion = &args->etsi.DeactivationDiversion; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + deactivation_diversion->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + deactivation_diversion->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &deactivation_diversion->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the ActivationStatusNotificationDiv invoke argument 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. + */ +const unsigned char *rose_dec_etsi_ActivationStatusNotificationDiv_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiActivationStatusNotificationDiv_ARG + *activation_status_notification_div; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " ActivationStatusNotificationDiv %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + activation_status_notification_div = &args->etsi.ActivationStatusNotificationDiv; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + activation_status_notification_div->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + activation_status_notification_div->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "forwardedToAddress", tag, pos, seq_end, + &activation_status_notification_div->forwarded_to)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &activation_status_notification_div->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the DeactivationStatusNotificationDiv invoke argument 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. + */ +const unsigned char *rose_dec_etsi_DeactivationStatusNotificationDiv_ARG(struct pri + *ctrl, unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDeactivationStatusNotificationDiv_ARG + *deactivation_status_notification_div; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DeactivationStatusNotificationDiv %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + deactivation_status_notification_div = &args->etsi.DeactivationStatusNotificationDiv; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + deactivation_status_notification_div->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + deactivation_status_notification_div->basic_service = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "forwardedToAddress", tag, pos, + seq_end, &deactivation_status_notification_div->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the InterrogationDiversion invoke argument 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. + */ +const unsigned char *rose_dec_etsi_InterrogationDiversion_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiInterrogationDiversion_ARG *interrogation_diversion; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " InterrogationDiversion %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + interrogation_diversion = &args->etsi.InterrogationDiversion; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "procedure", tag, pos, seq_end, &value)); + interrogation_diversion->procedure = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + if (tag == ASN1_TYPE_ENUMERATED) { + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + } else { + value = 0; /* DEFAULT BasicService value (allServices) */ + } + interrogation_diversion->basic_service = value; + + ASN1_CALL(pos, rose_dec_etsi_ServedUserNumber(ctrl, "servedUserNr", tag, pos, + seq_end, &interrogation_diversion->served_user_number)); + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the InterrogationDiversion result 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. + */ +const unsigned char *rose_dec_etsi_InterrogationDiversion_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args) +{ + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SET); + return rose_dec_etsi_IntResultList(ctrl, "diversionList", tag, pos, end, + &args->etsi.InterrogationDiversion); +} + +/*! + * \brief Decode the DiversionInformation invoke argument 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. + */ +const unsigned char *rose_dec_etsi_DiversionInformation_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDiversionInformation_ARG *diversion_information; + 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; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DiversionInformation %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + diversion_information = &args->etsi.DiversionInformation; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionReason", tag, pos, seq_end, &value)); + diversion_information->diversion_reason = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "basicService", tag, pos, seq_end, &value)); + diversion_information->basic_service = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + diversion_information->served_user_subaddress.length = 0; + diversion_information->calling_present = 0; + diversion_information->original_called_present = 0; + diversion_information->last_diverting_present = 0; + diversion_information->last_diverting_reason_present = 0; + diversion_information->q931ie.length = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_TAG_SEQUENCE: + case ASN1_TYPE_OCTET_STRING: + case ASN1_TYPE_OCTET_STRING | ASN1_PC_CONSTRUCTED: + ASN1_CALL(pos, rose_dec_PartySubaddress(ctrl, "servedUserSubaddress", tag, + pos, seq_end, &diversion_information->served_user_subaddress)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 0: + /* 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_PresentedAddressScreened(ctrl, "callingAddress", tag, + pos, explicit_end, &diversion_information->calling)); + diversion_information->calling_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case 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_PresentedNumberUnscreened(ctrl, "originalCalledNr", + tag, pos, explicit_end, &diversion_information->original_called)); + diversion_information->original_called_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + /* 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_PresentedNumberUnscreened(ctrl, "lastDivertingNr", + tag, pos, explicit_end, &diversion_information->last_diverting)); + diversion_information->last_diverting_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case 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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "lastDivertingReason", tag, pos, + explicit_end, &value)); + diversion_information->last_diverting_reason = value; + diversion_information->last_diverting_reason_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_APPLICATION | 0: + case ASN1_CLASS_APPLICATION | ASN1_PC_CONSTRUCTED | 0: + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "userInfo", tag, pos, seq_end, + &diversion_information->q931ie, + sizeof(diversion_information->q931ie_contents))); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CallDeflection invoke argument 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. + */ +const unsigned char *rose_dec_etsi_CallDeflection_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiCallDeflection_ARG *call_deflection; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallDeflection %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + call_deflection = &args->etsi.CallDeflection; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "deflectionAddress", tag, pos, seq_end, + &call_deflection->deflection)); + + 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_TYPE_BOOLEAN); + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "presentationAllowedDivertedToUser", tag, + pos, seq_end, &value)); + call_deflection->presentation_allowed_to_diverted_to_user = value; + call_deflection->presentation_allowed_to_diverted_to_user_present = 1; + } else { + call_deflection->presentation_allowed_to_diverted_to_user_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the CallRerouting invoke argument 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. + */ +const unsigned char *rose_dec_etsi_CallRerouting_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiCallRerouting_ARG *call_rerouting; + 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; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallRerouting %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + call_rerouting = &args->etsi.CallRerouting; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "reroutingReason", tag, pos, seq_end, &value)); + call_rerouting->rerouting_reason = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + ASN1_CALL(pos, rose_dec_Address(ctrl, "calledAddress", tag, pos, seq_end, + &call_rerouting->called_address)); + + 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, "reroutingCounter", tag, pos, seq_end, &value)); + call_rerouting->rerouting_counter = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag & ~ASN1_PC_MASK, ASN1_CLASS_APPLICATION | 0); + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "q931ie", tag, pos, seq_end, + &call_rerouting->q931ie, sizeof(call_rerouting->q931ie_contents))); + + /* Remove EXPLICIT tag */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, + ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1); + 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_PresentedNumberUnscreened(ctrl, "lastReroutingNr", tag, pos, + explicit_end, &call_rerouting->last_rerouting)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + call_rerouting->subscription_option = 0; /* DEFAULT value noNotification */ + call_rerouting->calling_subaddress.length = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + /* 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_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "subscriptionOption", tag, pos, + explicit_end, &value)); + call_rerouting->subscription_option = value; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case 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_PartySubaddress(ctrl, "callingPartySubaddress", tag, + pos, explicit_end, &call_rerouting->calling_subaddress)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the InterrogateServedUserNumbers result 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. + */ +const unsigned char *rose_dec_etsi_InterrogateServedUserNumbers_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args) +{ + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SET); + return rose_dec_etsi_ServedUserNumberList(ctrl, "interrogateServedUserNumbers", tag, + pos, end, &args->etsi.InterrogateServedUserNumbers); +} + +/*! + * \brief Decode the DivertingLegInformation1 invoke argument 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. + */ +const unsigned char *rose_dec_etsi_DivertingLegInformation1_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDivertingLegInformation1_ARG *diverting_leg_informtion_1; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DivertingLegInformation1 %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + diverting_leg_informtion_1 = &args->etsi.DivertingLegInformation1; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionReason", tag, pos, seq_end, &value)); + diverting_leg_informtion_1->diversion_reason = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "subscriptionOption", tag, pos, seq_end, &value)); + diverting_leg_informtion_1->subscription_option = value; + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "divertedToNumber", tag, + pos, seq_end, &diverting_leg_informtion_1->diverted_to)); + diverting_leg_informtion_1->diverted_to_present = 1; + } else { + diverting_leg_informtion_1->diverted_to_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the DivertingLegInformation2 invoke argument 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. + */ +const unsigned char *rose_dec_etsi_DivertingLegInformation2_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + struct roseEtsiDivertingLegInformation2_ARG *diverting_leg_information_2; + 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; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " DivertingLegInformation2 %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + diverting_leg_information_2 = &args->etsi.DivertingLegInformation2; + + 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, "diversionCounter", tag, pos, seq_end, &value)); + diverting_leg_information_2->diversion_counter = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "diversionReason", tag, pos, seq_end, &value)); + diverting_leg_information_2->diversion_reason = value; + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + diverting_leg_information_2->diverting_present = 0; + diverting_leg_information_2->original_called_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case 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_PresentedNumberUnscreened(ctrl, "divertingNr", tag, + pos, explicit_end, &diverting_leg_information_2->diverting)); + diverting_leg_information_2->diverting_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + /* 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_PresentedNumberUnscreened(ctrl, "originalCalledNr", + tag, pos, explicit_end, &diverting_leg_information_2->original_called)); + diverting_leg_information_2->original_called_present = 1; + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the DivertingLegInformation3 invoke argument 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. + */ +const unsigned char *rose_dec_etsi_DivertingLegInformation3_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_BOOLEAN); + ASN1_CALL(pos, asn1_dec_boolean(ctrl, "presentationAllowedIndicator", tag, pos, end, + &value)); + args->etsi.DivertingLegInformation3.presentation_allowed_indicator = value; + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_etsi_diversion.c */ Property changes on: rose_etsi_diversion.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose_qsig_ct.c =================================================================== --- a/rose_qsig_ct.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_qsig_ct.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,883 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 Q.SIG ROSE Call-Transfer-Operations (CT) + * + * Call-Transfer-Operations ECMA-178 Annex F Table F.1 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \brief Encode the Q.SIG CallTransferIdentify result facility ie 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. + */ +unsigned char *rose_enc_qsig_CallTransferIdentify_RES(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_result_args *args) +{ + unsigned char *seq_len; + const struct roseQsigCTIdentifyRes_RES *call_transfer_identify; + + call_transfer_identify = &args->qsig.CallTransferIdentify; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_NUMERIC_STRING, + call_transfer_identify->call_id, sizeof(call_transfer_identify->call_id) - 1)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &call_transfer_identify->rerouting_number)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG CallTransferInitiate invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_CallTransferInitiate_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + const struct roseQsigCTInitiateArg_ARG *call_transfer_initiate; + + call_transfer_initiate = &args->qsig.CallTransferInitiate; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_NUMERIC_STRING, + call_transfer_initiate->call_id, sizeof(call_transfer_initiate->call_id) - 1)); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, + &call_transfer_initiate->rerouting_number)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG CallTransferSetup invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_CallTransferSetup_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + const struct roseQsigCTSetupArg_ARG *call_transfer_setup; + + call_transfer_setup = &args->qsig.CallTransferSetup; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, asn1_enc_string_max(pos, end, ASN1_TYPE_NUMERIC_STRING, + call_transfer_setup->call_id, sizeof(call_transfer_setup->call_id) - 1)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG CallTransferActive invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_CallTransferActive_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + const struct roseQsigCTActiveArg_ARG *call_transfer_active; + + call_transfer_active = &args->qsig.CallTransferActive; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_PresentedAddressScreened(ctrl, pos, end, + &call_transfer_active->connected)); + + if (call_transfer_active->q931ie.length) { + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &call_transfer_active->q931ie)); + } + + if (call_transfer_active->connected_name_present) { + ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, + &call_transfer_active->connected_name)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG CallTransferComplete invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_CallTransferComplete_ARG(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + const struct roseQsigCTCompleteArg_ARG *call_transfer_complete; + + call_transfer_complete = &args->qsig.CallTransferComplete; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + call_transfer_complete->end_designation)); + + ASN1_CALL(pos, rose_enc_PresentedNumberScreened(ctrl, pos, end, + &call_transfer_complete->redirection)); + + if (call_transfer_complete->q931ie.length) { + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &call_transfer_complete->q931ie)); + } + + if (call_transfer_complete->redirection_name_present) { + ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, + &call_transfer_complete->redirection_name)); + } + + if (call_transfer_complete->call_status) { + /* Not the DEFAULT value */ + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + call_transfer_complete->call_status)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG CallTransferUpdate invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_CallTransferUpdate_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + const struct roseQsigCTUpdateArg_ARG *call_transfer_update; + + call_transfer_update = &args->qsig.CallTransferUpdate; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_PresentedNumberScreened(ctrl, pos, end, + &call_transfer_update->redirection)); + + if (call_transfer_update->redirection_name_present) { + ASN1_CALL(pos, rose_enc_qsig_Name(ctrl, pos, end, + &call_transfer_update->redirection_name)); + } + + if (call_transfer_update->q931ie.length) { + ASN1_CALL(pos, rose_enc_Q931ie(ctrl, pos, end, ASN1_CLASS_APPLICATION | 0, + &call_transfer_update->q931ie)); + } + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG SubaddressTransfer invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + unsigned char *seq_len; + const struct roseQsigSubaddressTransferArg_ARG *subaddress_transfer; + + subaddress_transfer = &args->qsig.SubaddressTransfer; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ASN1_CALL(pos, rose_enc_PartySubaddress(ctrl, pos, end, + &subaddress_transfer->redirection_subaddress)); + + /* No extension to encode */ + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG DummyArg invoke facility ie 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. + * + * \details + * DummyArg ::= CHOICE { + * none NULL, + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } + */ +unsigned char *rose_enc_qsig_DummyArg_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_null(pos, end, ASN1_TYPE_NULL); +} + +/*! + * \brief Encode the Q.SIG DummyRes result facility ie 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. + * + * \details + * DummyRes ::= CHOICE { + * none NULL, + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } + */ +unsigned char *rose_enc_qsig_DummyRes_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return asn1_enc_null(pos, end, ASN1_TYPE_NULL); +} + +/*! + * \brief Decode the Q.SIG CallTransferIdentify result argument 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. + */ +const unsigned char *rose_dec_qsig_CallTransferIdentify_RES(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_result_args *args) +{ + size_t str_len; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigCTIdentifyRes_RES *call_transfer_identify; + + call_transfer_identify = &args->qsig.CallTransferIdentify; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallTransferIdentify %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 & ~ASN1_PC_MASK, tag, ASN1_TYPE_NUMERIC_STRING); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "callIdentity", tag, pos, seq_end, + sizeof(call_transfer_identify->call_id), call_transfer_identify->call_id, + &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "reroutingNumber", tag, pos, seq_end, + &call_transfer_identify->rerouting_number)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG CallTransferInitiate invoke argument 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. + */ +const unsigned char *rose_dec_qsig_CallTransferInitiate_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + size_t str_len; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigCTInitiateArg_ARG *call_transfer_initiate; + + call_transfer_initiate = &args->qsig.CallTransferInitiate; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallTransferInitiate %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 & ~ASN1_PC_MASK, tag, ASN1_TYPE_NUMERIC_STRING); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "callIdentity", tag, pos, seq_end, + sizeof(call_transfer_initiate->call_id), call_transfer_initiate->call_id, + &str_len)); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "reroutingNumber", tag, pos, seq_end, + &call_transfer_initiate->rerouting_number)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG CallTransferSetup invoke argument 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. + */ +const unsigned char *rose_dec_qsig_CallTransferSetup_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + size_t str_len; + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigCTSetupArg_ARG *call_transfer_setup; + + call_transfer_setup = &args->qsig.CallTransferSetup; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallTransferSetup %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 & ~ASN1_PC_MASK, tag, ASN1_TYPE_NUMERIC_STRING); + ASN1_CALL(pos, asn1_dec_string_max(ctrl, "callIdentity", tag, pos, seq_end, + sizeof(call_transfer_setup->call_id), call_transfer_setup->call_id, &str_len)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG CallTransferActive invoke argument 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. + */ +const unsigned char *rose_dec_qsig_CallTransferActive_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigCTActiveArg_ARG *call_transfer_active; + + call_transfer_active = &args->qsig.CallTransferActive; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallTransferActive %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_CALL(pos, rose_dec_PresentedAddressScreened(ctrl, "connectedAddress", tag, pos, + seq_end, &call_transfer_active->connected)); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + call_transfer_active->q931ie.length = 0; + call_transfer_active->connected_name_present = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag & ~ASN1_PC_MASK) { + case ASN1_CLASS_APPLICATION | 0: + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "basicCallInfoElements", tag, pos, + seq_end, &call_transfer_active->q931ie, + sizeof(call_transfer_active->q931ie_contents))); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + case ASN1_CLASS_CONTEXT_SPECIFIC | 7: + ASN1_CALL(pos, rose_dec_qsig_Name(ctrl, "connectedName", tag, pos, seq_end, + &call_transfer_active->connected_name)); + call_transfer_active->connected_name_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 9: + case ASN1_CLASS_CONTEXT_SPECIFIC | 10: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " argumentExtension %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG CallTransferComplete invoke argument 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. + */ +const unsigned char *rose_dec_qsig_CallTransferComplete_ARG(struct pri *ctrl, + unsigned tag, const unsigned char *pos, const unsigned char *end, + union rose_msg_invoke_args *args) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigCTCompleteArg_ARG *call_transfer_complete; + + call_transfer_complete = &args->qsig.CallTransferComplete; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallTransferComplete %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_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "endDesignation", tag, pos, seq_end, &value)); + call_transfer_complete->end_designation = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberScreened(ctrl, "redirectionNumber", tag, pos, + seq_end, &call_transfer_complete->redirection)); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + call_transfer_complete->q931ie.length = 0; + call_transfer_complete->redirection_name_present = 0; + call_transfer_complete->call_status = 0; /* DEFAULT answered */ + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag & ~ASN1_PC_MASK) { + case ASN1_CLASS_APPLICATION | 0: + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "basicCallInfoElements", tag, pos, + seq_end, &call_transfer_complete->q931ie, + sizeof(call_transfer_complete->q931ie_contents))); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + case ASN1_CLASS_CONTEXT_SPECIFIC | 7: + ASN1_CALL(pos, rose_dec_qsig_Name(ctrl, "redirectionName", tag, pos, seq_end, + &call_transfer_complete->redirection_name)); + call_transfer_complete->redirection_name_present = 1; + break; + case ASN1_TYPE_ENUMERATED: + /* Must not be constructed but we will not check for it for simplicity. */ + ASN1_CALL(pos, asn1_dec_int(ctrl, "callStatus", tag, pos, seq_end, &value)); + call_transfer_complete->call_status = value; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 9: + case ASN1_CLASS_CONTEXT_SPECIFIC | 10: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " argumentExtension %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG CallTransferUpdate invoke argument 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. + */ +const unsigned char *rose_dec_qsig_CallTransferUpdate_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + const unsigned char *save_pos; + struct roseQsigCTUpdateArg_ARG *call_transfer_update; + + call_transfer_update = &args->qsig.CallTransferUpdate; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " CallTransferUpdate %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_CALL(pos, rose_dec_PresentedNumberScreened(ctrl, "redirectionNumber", tag, pos, + seq_end, &call_transfer_update->redirection)); + + /* + * A sequence specifies an ordered list of component types. + * However, for simplicity we are not checking the order of + * the remaining optional components. + */ + call_transfer_update->redirection_name_present = 0; + call_transfer_update->q931ie.length = 0; + while (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag & ~ASN1_PC_MASK) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + case ASN1_CLASS_CONTEXT_SPECIFIC | 7: + ASN1_CALL(pos, rose_dec_qsig_Name(ctrl, "redirectionName", tag, pos, seq_end, + &call_transfer_update->redirection_name)); + call_transfer_update->redirection_name_present = 1; + break; + case ASN1_CLASS_APPLICATION | 0: + ASN1_CALL(pos, rose_dec_Q931ie(ctrl, "basicCallInfoElements", tag, pos, + seq_end, &call_transfer_update->q931ie, + sizeof(call_transfer_update->q931ie_contents))); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 9: + case ASN1_CLASS_CONTEXT_SPECIFIC | 10: + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " argumentExtension %s\n", asn1_tag2str(tag)); + } + /* Fixup will skip over the manufacturer extension information */ + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG SubaddressTransfer invoke argument 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. + */ +const unsigned char *rose_dec_qsig_SubaddressTransfer_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + struct roseQsigSubaddressTransferArg_ARG *subaddress_transfer; + + subaddress_transfer = &args->qsig.SubaddressTransfer; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " SubaddressTransfer %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_CALL(pos, rose_dec_PartySubaddress(ctrl, "redirectionSubaddress", tag, pos, + seq_end, &subaddress_transfer->redirection_subaddress)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG DummyArg invoke argument 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. + * + * \details + * DummyArg ::= CHOICE { + * none NULL, + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } + */ +const unsigned char *rose_dec_qsig_DummyArg_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + const char *name; + int length; + int seq_offset; + const unsigned char *seq_end; + + switch (tag) { + case ASN1_TYPE_NULL: + return asn1_dec_null(ctrl, "none", tag, pos, end); + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + name = "extension Extension"; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + name = "multipleExtension SEQUENCE OF Extension"; + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %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); + + /* Fixup will skip over the manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG DummyRes result argument 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. + * + * \details + * DummyRes ::= CHOICE { + * none NULL, + * extension [1] IMPLICIT Extension, + * multipleExtension [2] IMPLICIT SEQUENCE OF Extension + * } + */ +const unsigned char *rose_dec_qsig_DummyRes_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + const char *name; + int length; + int seq_offset; + const unsigned char *seq_end; + + switch (tag) { + case ASN1_TYPE_NULL: + return asn1_dec_null(ctrl, "none", tag, pos, end); + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1: + name = "extension Extension"; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 2: + name = "multipleExtension SEQUENCE OF Extension"; + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %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); + + /* Fixup will skip over the manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_qsig_ct.c */ Property changes on: rose_qsig_ct.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose_q931.c =================================================================== --- a/rose_q931.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_q931.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,100 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 ROSE Q.931 ie encode/decode functions + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \brief Encode the Q.931 ie value. + * + * \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 tag Component tag to identify the encoded component. + * The tag should be ASN1_CLASS_APPLICATION | 0 unless the caller + * implicitly tags it otherwise. + * \param q931ie Q931 ie information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_Q931ie(struct pri *ctrl, unsigned char *pos, unsigned char *end, + unsigned tag, const struct roseQ931ie *q931ie) +{ + return asn1_enc_string_bin(pos, end, tag, q931ie->contents, q931ie->length); +} + +/*! + * \brief Decode the Q.931 ie value. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \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 q931ie Parameter storage to fill. + * \param contents_size Amount of space "allocated" for the q931ie->contents + * element. Must have enough room for a null terminator. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_Q931ie(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct roseQ931ie *q931ie, + size_t contents_size) +{ + size_t str_len; + + /* NOTE: The q931ie->contents memory is "allocated" after the struct. */ + ASN1_CALL(pos, asn1_dec_string_bin(ctrl, name, tag, pos, end, contents_size, + q931ie->contents, &str_len)); + q931ie->length = str_len; + + /* + * NOTE: We may want to do some basic decoding of the Q.931 ie list + * for debug purposes. + */ + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_q931.c */ Property changes on: rose_q931.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: rose_etsi_ect.c =================================================================== --- a/rose_etsi_ect.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_etsi_ect.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,332 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 ROSE Explicit Call Transfer operations. + * + * Explicit Call Transfer (ECT) Supplementary Services ETS 300 369-1 + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \brief Encode the ExplicitEctExecute invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_ExplicitEctExecute_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.ExplicitEctExecute.link_id); +} + +/*! + * \brief Encode the SubaddressTransfer invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_SubaddressTransfer_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_PartySubaddress(ctrl, pos, end, + &args->etsi.SubaddressTransfer.subaddress); +} + +/*! + * \brief Encode the EctLinkIdRequest result facility ie 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. + */ +unsigned char *rose_enc_etsi_EctLinkIdRequest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.EctLinkIdRequest.link_id); +} + +/*! + * \brief Encode the EctInform invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_EctInform_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + const struct roseEtsiEctInform_ARG *ect_inform; + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_TAG_SEQUENCE); + + ect_inform = &args->etsi.EctInform; + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, ect_inform->status)); + if (ect_inform->redirection_present) { + ASN1_CALL(pos, rose_enc_PresentedNumberUnscreened(ctrl, pos, end, + &ect_inform->redirection)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the EctLoopTest invoke facility ie 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. + */ +unsigned char *rose_enc_etsi_EctLoopTest_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, + args->etsi.EctLoopTest.call_transfer_id); +} + +/*! + * \brief Encode the EctLoopTest result facility ie 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. + */ +unsigned char *rose_enc_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_result_args *args) +{ + return asn1_enc_int(pos, end, ASN1_TYPE_ENUMERATED, + args->etsi.EctLoopTest.loop_result); +} + +/*! + * \brief Decode the ExplicitEctExecute invoke argument 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. + */ +const unsigned char *rose_dec_etsi_ExplicitEctExecute_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "linkId", tag, pos, end, &value)); + args->etsi.ExplicitEctExecute.link_id = value; + + return pos; +} + +/*! + * \brief Decode the SubaddressTransfer invoke argument 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. + */ +const unsigned char *rose_dec_etsi_SubaddressTransfer_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_PartySubaddress(ctrl, "transferredToSubaddress", tag, pos, end, + &args->etsi.SubaddressTransfer.subaddress); +} + +/*! + * \brief Decode the EctLinkIdRequest result argument 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. + */ +const unsigned char *rose_dec_etsi_EctLinkIdRequest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "linkId", tag, pos, end, &value)); + args->etsi.EctLinkIdRequest.link_id = value; + + return pos; +} + +/*! + * \brief Decode the EctInform invoke argument 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. + */ +const unsigned char *rose_dec_etsi_EctInform_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + struct roseEtsiEctInform_ARG *ect_inform; + int length; + int seq_offset; + const unsigned char *seq_end; + int32_t value; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " EctInform %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ect_inform = &args->etsi.EctInform; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "callStatus", tag, pos, seq_end, &value)); + ect_inform->status = value; + + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, rose_dec_PresentedNumberUnscreened(ctrl, "redirectionNumber", tag, + pos, seq_end, &ect_inform->redirection)); + ect_inform->redirection_present = 1; + } else { + ect_inform->redirection_present = 0; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the EctLoopTest invoke argument 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. + */ +const unsigned char *rose_dec_etsi_EctLoopTest_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "callTransferId", tag, pos, end, &value)); + args->etsi.EctLoopTest.call_transfer_id = value; + + return pos; +} + +/*! + * \brief Decode the EctLoopTest result argument 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. + */ +const unsigned char *rose_dec_etsi_EctLoopTest_RES(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_result_args *args) +{ + int32_t value; + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_ENUMERATED); + ASN1_CALL(pos, asn1_dec_int(ctrl, "loopResult", tag, pos, end, &value)); + args->etsi.EctLoopTest.loop_result = value; + + return pos; +} + +/* ------------------------------------------------------------------- */ +/* end rose_etsi_ect.c */ Property changes on: rose_etsi_ect.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: pri_internal.h =================================================================== --- a/pri_internal.h (.../tags/1.4.10.2) (revision 1357) +++ b/pri_internal.h (.../branches/1.4) (revision 1357) @@ -32,30 +32,45 @@ #include #include +#include "pri_q921.h" +#include "pri_q931.h" +#define ARRAY_LEN(arr) (sizeof(arr) / sizeof((arr)[0])) + #define DBGHEAD __FILE__ ":%d %s: " #define DBGINFO __LINE__,__PRETTY_FUNCTION__ +/* Forward declare some structs */ +struct apdu_event; + struct pri_sched { struct timeval when; void (*callback)(void *data); void *data; }; -struct q921_frame; -enum q931_state; -enum q931_mode; - -/* No more than 128 scheduled events */ +/*! Maximum number of scheduled events active at the same time. */ #define MAX_SCHED 128 -#define MAX_TIMERS 32 +/*! Maximum number of facility ie's to handle per incoming message. */ +#define MAX_FACILITY_IES 8 +/*! Accumulated pri_message() line until a '\n' is seen on the end. */ +struct pri_msg_line { + /*! Accumulated buffer used. */ + unsigned length; + /*! Accumulated pri_message() contents. */ + char str[2048]; +}; + +/*! \brief D channel controller structure */ struct pri { int fd; /* File descriptor for D-Channel */ pri_io_cb read_func; /* Read data callback */ pri_io_cb write_func; /* Write data callback */ void *userdata; + /*! Accumulated pri_message() line. (Valid in master record only) */ + struct pri_msg_line *msg_line; struct pri *subchannel; /* Sub-channel if appropriate */ struct pri *master; /* Master channel if appropriate */ struct pri_sched pri_sched[MAX_SCHED]; /* Scheduled events */ @@ -71,7 +86,9 @@ int protodisc; unsigned int bri:1; unsigned int acceptinbanddisconnect:1; /* Should we allow inband progress after DISCONNECT? */ - + unsigned int hold_support:1;/* TRUE if upper layer supports call hold. */ + unsigned int deflection_support:1;/* TRUE if upper layer supports call deflection/rerouting. */ + /* Q.921 State */ int q921_state; int window; /* Max window size */ @@ -97,12 +114,14 @@ int ri; int t200_timer; /* T-200 retransmission timer */ /* All ISDN Timer values */ - int timers[MAX_TIMERS]; + int timers[PRI_MAX_TIMERS]; /* Used by scheduler */ struct timeval tv; int schedev; pri_event ev; /* Static event thingy */ + /*! Subcommands for static event thingy. */ + struct pri_subcommands subcmds; /* Q.921 Re-transmission queue */ struct q921_frame *txqueue; @@ -111,9 +130,19 @@ q931_call **callpool; q931_call *localpool; + /*! + * \brief Q.931 Dummy call reference call associated with this TEI. + * \note If present then this call is allocated as part of the + * D channel control structure. + */ + q931_call *dummy_call; + /* do we do overlap dialing */ int overlapdial; + /* do we support SERVICE messages */ + int service_message_support; + /* do not skip channel 16 */ int chan_mapping_logical; @@ -125,52 +154,227 @@ unsigned int q931_rxcount; #endif - unsigned char last_invoke; /* Last ROSE invoke ID */ + short last_invoke; /* Last ROSE invoke ID (Valid in master record only) */ unsigned char sendfacility; + + /*! For delayed processing of facility ie's. */ + struct { + /*! Array of facility ie locations in the current received message. */ + q931_ie *ie[MAX_FACILITY_IES]; + /*! Codeset facility ie found within. */ + unsigned char codeset[MAX_FACILITY_IES]; + /*! Number of facility ie's in the array from the current received message. */ + unsigned char count; + } facility; }; +/*! \brief Maximum name length plus null terminator (From ECMA-164) */ +#define PRI_MAX_NAME_LEN (50 + 1) + +/*! \brief Q.SIG name information. */ +struct q931_party_name { + /*! \brief TRUE if name data is valid */ + unsigned char valid; + /*! + * \brief Q.931 presentation-indicator encoded field + * \note Must tollerate the Q.931 screening-indicator field values being present. + */ + unsigned char presentation; + /*! + * \brief Character set the name is using. + * \details + * unknown(0), + * iso8859-1(1), + * enum-value-withdrawn-by-ITU-T(2) + * iso8859-2(3), + * iso8859-3(4), + * iso8859-4(5), + * iso8859-5(6), + * iso8859-7(7), + * iso10646-BmpString(8), + * iso10646-utf-8String(9) + */ + unsigned char char_set; + /*! \brief Name data with null terminator. */ + char str[PRI_MAX_NAME_LEN]; +}; + +/*! \brief Maximum phone number (address) length plus null terminator */ +#define PRI_MAX_NUMBER_LEN (31 + 1) + +struct q931_party_number { + /*! \brief TRUE if number data is valid */ + unsigned char valid; + /*! \brief Q.931 presentation-indicator and screening-indicator encoded fields */ + unsigned char presentation; + /*! \brief Q.931 Type-Of-Number and numbering-plan encoded fields */ + unsigned char plan; + /*! \brief Number data with terminator. */ + char str[PRI_MAX_NUMBER_LEN]; +}; + +/*! \brief Maximum subaddress length plus null terminator */ +#define PRI_MAX_SUBADDRESS_LEN (20 + 1) + +struct q931_party_subaddress { + /*! \brief TRUE if the subaddress information is valid/present */ + unsigned char valid; + /*! + * \brief Subaddress type. + * \details + * nsap(0), + * user_specified(2) + */ + unsigned char type; + /*! + * \brief TRUE if odd number of address signals + * \note The odd/even indicator is used when the type of subaddress is + * user_specified and the coding is BCD. + */ + unsigned char odd_even_indicator; + /*! \brief Length of the subaddress data */ + unsigned char length; + /*! + * \brief Subaddress data with null terminator. + * \note The null terminator is a convenience only since the data could be + * BCD/binary and thus have a null byte as part of the contents. + */ + unsigned char data[PRI_MAX_SUBADDRESS_LEN]; +}; + +struct q931_party_address { + /*! \brief Subscriber phone number */ + struct q931_party_number number; + /*! \brief Subscriber subaddress */ + struct q931_party_subaddress subaddress; +}; + +/*! \brief Information needed to identify an endpoint in a call. */ +struct q931_party_id { + /*! \brief Subscriber name */ + struct q931_party_name name; + /*! \brief Subscriber phone number */ + struct q931_party_number number; + /*! \brief Subscriber subaddress */ + struct q931_party_subaddress subaddress; +}; + +enum Q931_REDIRECTING_STATE { + /*! + * \details + * CDO-Idle/CDF-Inv-Idle + */ + Q931_REDIRECTING_STATE_IDLE, + /*! + * \details + * CDF-Inv-Wait - A DivLeg2 has been received and + * we are waiting for valid presentation restriction information to send. + */ + Q931_REDIRECTING_STATE_PENDING_TX_DIV_LEG_3, + /*! + * \details + * CDO-Divert - A DivLeg1 has been received and + * we are waiting for the presentation restriction information to come in. + */ + Q931_REDIRECTING_STATE_EXPECTING_RX_DIV_LEG_3, +}; + +/*! + * \brief Do not increment above this count. + * \details + * It is not our responsibility to enforce the maximum number of redirects. + * However, we cannot allow an increment past this number without breaking things. + * Besides, more than 255 redirects is probably not a good thing. + */ +#define PRI_MAX_REDIRECTS 0xFF + +/*! \brief Redirecting information struct */ +struct q931_party_redirecting { + enum Q931_REDIRECTING_STATE state; + /*! \brief Who is redirecting the call (Sent to the party the call is redirected toward) */ + struct q931_party_id from; + /*! \brief Call is redirecting to a new party (Sent to the caller) */ + struct q931_party_id to; + /*! Originally called party (in cases of multiple redirects) */ + struct q931_party_id orig_called; + /*! + * \brief Number of times the call was redirected + * \note The call is being redirected if the count is non-zero. + */ + unsigned char count; + /*! Original reason for redirect (in cases of multiple redirects) */ + unsigned char orig_reason; + /*! \brief Redirection reasons */ + unsigned char reason; +}; + +/*! \brief New call setup parameter structure */ struct pri_sr { int transmode; int channel; int exclusive; int nonisdn; - char *caller; - int callerplan; - char *callername; - int callerpres; - char *called; - int calledplan; + struct q931_party_redirecting redirecting; + struct q931_party_id caller; + struct q931_party_address called; int userl1; int numcomplete; - char *redirectingnum; - int redirectingplan; - int redirectingpres; - int redirectingreason; - int justsignalling; + int cis_call; + int cis_auto_disconnect; const char *useruserinfo; + const char *keypad_digits; int transferable; + int reversecharge; }; /* Internal switch types */ #define PRI_SWITCH_GR303_EOC_PATH 19 #define PRI_SWITCH_GR303_TMC_SWITCHING 20 -struct apdu_event { - int message; /* What message to send the ADPU in */ - void (*callback)(void *data); /* Callback function for when response is received */ - void *data; /* Data to callback */ - unsigned char apdu[255]; /* ADPU to send */ - int apdu_len; /* Length of ADPU */ - int sent; /* Have we been sent already? */ - struct apdu_event *next; /* Linked list pointer */ +#define Q931_MAX_TEI 8 + +/*! \brief Incoming call transfer states. */ +enum INCOMING_CT_STATE { + /*! + * \details + * Incoming call transfer is not active. + */ + INCOMING_CT_STATE_IDLE, + /*! + * \details + * We have seen an incoming CallTransferComplete(alerting) + * so we are waiting for the expected CallTransferActive + * before updating the connected line about the remote party id. + */ + INCOMING_CT_STATE_EXPECT_CT_ACTIVE, + /*! + * \details + * A call transfer message came in that updated the remote party id + * that we need to post a connected line update. + */ + INCOMING_CT_STATE_POST_CONNECTED_LINE }; +/*! Call hold supplementary states. */ +enum Q931_HOLD_STATE { + /*! \brief No call hold activity. */ + Q931_HOLD_STATE_IDLE, + /*! \brief Request made to hold call. */ + Q931_HOLD_STATE_HOLD_REQ, + /*! \brief Request received to hold call. */ + Q931_HOLD_STATE_HOLD_IND, + /*! \brief Call is held. */ + Q931_HOLD_STATE_CALL_HELD, + /*! \brief Request made to retrieve call. */ + Q931_HOLD_STATE_RETRIEVE_REQ, + /*! \brief Request received to retrieve call. */ + Q931_HOLD_STATE_RETRIEVE_IND, +}; + /* q931_call datastructure */ - struct q931_call { struct pri *pri; /* PRI */ int cr; /* Call Reference */ - int forceinvert; /* Force inversion of call number even if 0 */ q931_call *next; /* Slotmap specified (bitmap of channels 31/24-1) (Channel Identifier IE) (-1 means not specified) */ int slotmap; @@ -199,73 +403,144 @@ int userl2; int userl3; int rateadaption; - - int sentchannel; - int justsignalling; /* for a signalling-only connection */ + /*! + * \brief TRUE if the call is a Call Independent Signalling connection. + * \note The call has no B channel associated with it. (Just signalling) + */ + int cis_call; + /*! \brief TRUE if we will auto disconnect the cis_call we originated. */ + int cis_auto_disconnect; + int progcode; /* Progress coding */ int progloc; /* Progress Location */ int progress; /* Progress indicator */ int progressmask; /* Progress Indicator bitmask */ - int notify; /* Notification */ + int notify; /* Notification indicator. */ int causecode; /* Cause Coding */ int causeloc; /* Cause Location */ int cause; /* Cause of clearing */ - int peercallstate; /* Call state of peer as reported */ - int ourcallstate; /* Our call state */ - int sugcallstate; /* Status call state */ - - int callerplan; - int callerplanani; - int callerpres; /* Caller presentation */ - char callerani[256]; /* Caller */ - char callernum[256]; - char callername[256]; + enum Q931_CALL_STATE peercallstate; /* Call state of peer as reported */ + enum Q931_CALL_STATE ourcallstate; /* Our call state */ + enum Q931_CALL_STATE sugcallstate; /* Status call state */ - char keypad_digits[64]; /* Buffer for digits that come in KEYPAD_FACILITY */ + int ani2; /* ANI II */ - int ani2; /* ANI II */ - - int calledplan; + /*! Buffer for digits that come in KEYPAD_FACILITY */ + char keypad_digits[32 + 1]; + + /*! Current dialed digits to be sent or just received. */ + char overlap_digits[PRI_MAX_NUMBER_LEN]; + + /*! + * \brief Local party ID + * \details + * The Caller-ID and connected-line ID are just roles the local and remote party + * play while a call is being established. Which roll depends upon the direction + * of the call. + * Outgoing party info is to identify the local party to the other end. + * (Caller-ID for originated or connected-line for answered calls.) + * Incoming party info is to identify the remote party to us. + * (Caller-ID for answered or connected-line for originated calls.) + */ + struct q931_party_id local_id; + /*! + * \brief Remote party ID + * \details + * The Caller-ID and connected-line ID are just roles the local and remote party + * play while a call is being established. Which roll depends upon the direction + * of the call. + * Outgoing party info is to identify the local party to the other end. + * (Caller-ID for originated or connected-line for answered calls.) + * Incoming party info is to identify the remote party to us. + * (Caller-ID for answered or connected-line for originated calls.) + */ + struct q931_party_id remote_id; + + /*! + * \brief Staging place for the Q.931 redirection number ie. + * \note + * The number could be the remote_id.number or redirecting.to.number + * depending upon the notification indicator. + */ + struct q931_party_number redirection_number; + + /*! + * \brief Called party address. + * \note The called.number.str is the accumulated overlap dial digits + * and enbloc digits. + * \note The called.number.presentation value is not used. + */ + struct q931_party_address called; int nonisdn; - char callednum[256]; /* Called Number */ int complete; /* no more digits coming */ int newcall; /* if the received message has a new call reference value */ int retranstimer; /* Timer for retransmitting DISC */ int t308_timedout; /* Whether t308 timed out once */ - int redirectingplan; - int redirectingpres; - int redirectingreason; - char redirectingnum[256]; /* Number of redirecting party */ - char redirectingname[256]; /* Name of redirecting party */ + struct q931_party_redirecting redirecting; - /* Filled in cases of multiple diversions */ - int origcalledplan; - int origcalledpres; - int origredirectingreason; /* Original reason for redirect (in cases of multiple redirects) */ - char origcalledname[256]; /* Original name of person being called */ - char origcallednum[256]; /* Orignal number of person being called */ + /*! \brief Incoming call transfer state. */ + enum INCOMING_CT_STATE incoming_ct_state; + /*! Call hold supplementary state. */ + enum Q931_HOLD_STATE hold_state; + /*! Call hold event timer */ + int hold_timer; + int deflection_in_progress; /*!< CallDeflection for NT PTMP in progress. */ + /*! TRUE if the connected number ie was in the current received message. */ + int connected_number_in_message; + /*! TRUE if the redirecting number ie was in the current received message. */ + int redirecting_number_in_message; + int useruserprotocoldisc; char useruserinfo[256]; - char callingsubaddr[256]; /* Calling parties sub address */ long aoc_units; /* Advice of Charge Units */ struct apdu_event *apdus; /* APDU queue for call */ - int transferable; + int transferable; /* RLT call is transferable */ unsigned int rlt_call_id; /* RLT call id */ /* Bridged call info */ - q931_call *bridged_call; /* Pointer to other leg of bridged call */ + q931_call *bridged_call; /* Pointer to other leg of bridged call (Used by Q.SIG when eliminating tromboned calls) */ + + int changestatus; /* SERVICE message changestatus */ + int reversecharge; /* Reverse charging indication: + -1 - No reverse charging + 1 - Reverse charging + 0,2-7 - Reserved for future use */ + int t303_timer; + int t303_expirycnt; + + int hangupinitiated; + /*! \brief TRUE if we broadcast this call's SETUP message. */ + int outboundbroadcast; + int performing_fake_clearing; + /*! + * \brief Master call controlling this call. + * \note Always valid. Master and normal calls point to self. + */ + struct q931_call *master_call; + + /* These valid in master call only */ + struct q931_call *subcalls[Q931_MAX_TEI]; + int pri_winner; }; +/*! D channel control structure with associated dummy call reference record. */ +struct d_ctrl_dummy { + /*! D channel control structure. Must be first in the structure. */ + struct pri ctrl; + /*! Dummy call reference call record. */ + struct q931_call dummy_call; +}; + extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data); extern pri_event *pri_schedule_run(struct pri *pri); @@ -274,14 +549,104 @@ extern pri_event *pri_mkerror(struct pri *pri, char *errstr); -extern void pri_message(struct pri *pri, char *fmt, ...); +void pri_message(struct pri *ctrl, const char *fmt, ...) __attribute__((format(printf, 2, 3))); +void pri_error(struct pri *ctrl, const char *fmt, ...) __attribute__((format(printf, 2, 3))); -extern void pri_error(struct pri *pri, char *fmt, ...); - void libpri_copy_string(char *dst, const char *src, size_t size); struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, pri_io_cb rd, pri_io_cb wr, void *userdata, int tei, int bri); void __pri_free_tei(struct pri *p); +void q931_init_call_record(struct pri *ctrl, struct q931_call *call, int cr); + +void q931_party_name_init(struct q931_party_name *name); +void q931_party_number_init(struct q931_party_number *number); +void q931_party_subaddress_init(struct q931_party_subaddress *subaddr); +void q931_party_address_init(struct q931_party_address *address); +void q931_party_id_init(struct q931_party_id *id); +void q931_party_redirecting_init(struct q931_party_redirecting *redirecting); + +static inline void q931_party_address_to_id(struct q931_party_id *id, struct q931_party_address *address) +{ + id->number = address->number; + id->subaddress = address->subaddress; +} + +int q931_party_name_cmp(const struct q931_party_name *left, const struct q931_party_name *right); +int q931_party_number_cmp(const struct q931_party_number *left, const struct q931_party_number *right); +int q931_party_subaddress_cmp(const struct q931_party_subaddress *left, const struct q931_party_subaddress *right); +int q931_party_id_cmp(const struct q931_party_id *left, const struct q931_party_id *right); + +void q931_party_name_copy_to_pri(struct pri_party_name *pri_name, const struct q931_party_name *q931_name); +void q931_party_number_copy_to_pri(struct pri_party_number *pri_number, const struct q931_party_number *q931_number); +void q931_party_subaddress_copy_to_pri(struct pri_party_subaddress *pri_subaddress, const struct q931_party_subaddress *q931_subaddress); +void q931_party_id_copy_to_pri(struct pri_party_id *pri_id, const struct q931_party_id *q931_id); +void q931_party_redirecting_copy_to_pri(struct pri_party_redirecting *pri_redirecting, const struct q931_party_redirecting *q931_redirecting); + +void q931_party_id_fixup(const struct pri *ctrl, struct q931_party_id *id); +int q931_party_id_presentation(const struct q931_party_id *id); + +const char *q931_call_state_str(enum Q931_CALL_STATE callstate); +const char *msg2str(int msg); + +int q931_is_ptmp(const struct pri *ctrl); +int q931_master_pass_event(struct pri *ctrl, struct q931_call *subcall, int msg_type); +struct pri_subcommand *q931_alloc_subcommand(struct pri *ctrl); + +int q931_notify_redirection(struct pri *ctrl, q931_call *call, int notify, const struct q931_party_number *number); + +static inline struct pri * PRI_MASTER(struct pri *mypri) +{ + struct pri *pri = mypri; + + if (!pri) + return NULL; + + while (pri->master) + pri = pri->master; + + return pri; +} + +static inline int BRI_NT_PTMP(struct pri *mypri) +{ + struct pri *pri; + + pri = PRI_MASTER(mypri); + + return pri->bri && (((pri)->localtype == PRI_NETWORK) && ((pri)->tei == Q921_TEI_GROUP)); +} + +static inline int BRI_TE_PTMP(struct pri *mypri) +{ + struct pri *pri; + + pri = PRI_MASTER(mypri); + + return pri->bri && (((pri)->localtype == PRI_CPE) && ((pri)->tei == Q921_TEI_GROUP)); +} + +static inline int PRI_PTP(struct pri *mypri) +{ + struct pri *pri; + + pri = PRI_MASTER(mypri); + + return !pri->bri; +} + +#define Q931_DUMMY_CALL_REFERENCE -1 + +/*! + * \brief Deterimine if the given call control pointer is a dummy call. + * + * \retval TRUE if given call is a dummy call. + * \retval FALSE otherwise. + */ +static inline int q931_is_dummy_call(const q931_call *call) +{ + return (call->cr == Q931_DUMMY_CALL_REFERENCE) ? 1 : 0; +} + #endif Index: rose_qsig_name.c =================================================================== --- a/rose_qsig_name.c (.../tags/1.4.10.2) (revision 0) +++ b/rose_qsig_name.c (.../branches/1.4) (revision 1357) @@ -0,0 +1,474 @@ +/* + * libpri: An implementation of Primary Rate ISDN + * + * Copyright (C) 2009 Digium, Inc. + * + * Richard Mudgett + * + * 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 Q.SIG ROSE Name operations and elements + * + * Name-Operations ECMA-164 Annex C + * + * \author Richard Mudgett + */ + + +#include "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "rose.h" +#include "rose_internal.h" +#include "asn1.h" + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Encode the Q.SIG NameSet 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 tag Component tag to identify the encoded component. + * The tag should be ASN1_TAG_SEQUENCE unless the caller + * implicitly tags it otherwise. + * \param name + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_NameSet(struct pri *ctrl, unsigned char *pos, + unsigned char *end, unsigned tag, const struct roseQsigName *name) +{ + unsigned char *seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, tag); + + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_TYPE_OCTET_STRING, name->data, + name->length)); + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, name->char_set)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the Q.SIG Name 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 name + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_enc_qsig_Name(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct roseQsigName *name) +{ + switch (name->presentation) { + case 0: /* optional_name_not_present */ + /* Do not encode anything */ + break; + case 1: /* presentation_allowed */ + if (name->char_set == 1) { + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + name->data, name->length)); + } else { + ASN1_CALL(pos, rose_enc_qsig_NameSet(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 1, name)); + } + break; + case 2: /* presentation_restricted */ + if (name->char_set == 1) { + ASN1_CALL(pos, asn1_enc_string_bin(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + name->data, name->length)); + } else { + ASN1_CALL(pos, rose_enc_qsig_NameSet(ctrl, pos, end, + ASN1_CLASS_CONTEXT_SPECIFIC | 3, name)); + } + break; + case 3: /* presentation_restricted_null */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 7)); + break; + case 4: /* name_not_available */ + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 4)); + break; + default: + ASN1_ENC_ERROR(ctrl, "Unknown name presentation"); + return NULL; + } + + return pos; +} + +/*! + * \internal + * \brief Encode the Q.SIG party-Name invoke facility ie 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 party Information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_qsig_PartyName_ARG_Backend(struct pri *ctrl, + unsigned char *pos, unsigned char *end, const struct roseQsigPartyName_ARG *party) +{ + return rose_enc_qsig_Name(ctrl, pos, end, &party->name); +} + +/*! + * \brief Encode the Q.SIG CallingName invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_CallingName_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_PartyName_ARG_Backend(ctrl, pos, end, &args->qsig.CallingName); +} + +/*! + * \brief Encode the Q.SIG CalledName invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_CalledName_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_PartyName_ARG_Backend(ctrl, pos, end, &args->qsig.CalledName); +} + +/*! + * \brief Encode the Q.SIG ConnectedName invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_ConnectedName_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_PartyName_ARG_Backend(ctrl, pos, end, + &args->qsig.ConnectedName); +} + +/*! + * \brief Encode the Q.SIG BusyName invoke facility ie 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. + */ +unsigned char *rose_enc_qsig_BusyName_ARG(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const union rose_msg_invoke_args *args) +{ + return rose_enc_qsig_PartyName_ARG_Backend(ctrl, pos, end, &args->qsig.BusyName); +} + +/*! + * \internal + * \brief Decode the Q.SIG NameData Name argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param fname 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 name Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_NameData(struct pri *ctrl, const char *fname, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigName *name) +{ + size_t str_len; + + ASN1_CALL(pos, asn1_dec_string_bin(ctrl, fname, tag, pos, end, sizeof(name->data), + name->data, &str_len)); + name->length = str_len; + + return pos; +} + +/*! + * \internal + * \brief Decode the Q.SIG NameSet Name argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param fname 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 name Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_NameSet(struct pri *ctrl, const char *fname, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigName *name) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s NameSet %s\n", fname, 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_PC_MASK, ASN1_TYPE_OCTET_STRING); + ASN1_CALL(pos, rose_dec_qsig_NameData(ctrl, "nameData", tag, pos, seq_end, name)); + + if (pos < end && *pos != ASN1_INDEF_TERM) { + 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, "characterSet", tag, pos, seq_end, &value)); + name->char_set = value; + } else { + name->char_set = 1; /* default to iso8859-1 */ + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the Q.SIG Name argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param fname 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 name Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *rose_dec_qsig_Name(struct pri *ctrl, const char *fname, + unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigName *name) +{ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s Name\n", fname); + } + name->char_set = 1; /* default to iso8859-1 */ + switch (tag & ~ASN1_PC_MASK) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + name->presentation = 1; /* presentation_allowed */ + ASN1_CALL(pos, rose_dec_qsig_NameData(ctrl, "namePresentationAllowedSimple", tag, + pos, end, name)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + /* Must be constructed but we will not check for it for simplicity. */ + name->presentation = 1; /* presentation_allowed */ + ASN1_CALL(pos, rose_dec_qsig_NameSet(ctrl, "namePresentationAllowedExtended", + tag, pos, end, name)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + name->presentation = 2; /* presentation_restricted */ + ASN1_CALL(pos, rose_dec_qsig_NameData(ctrl, "namePresentationRestrictedSimple", + tag, pos, end, name)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + /* Must be constructed but we will not check for it for simplicity. */ + name->presentation = 2; /* presentation_restricted */ + ASN1_CALL(pos, rose_dec_qsig_NameSet(ctrl, "namePresentationRestrictedExtended", + tag, pos, end, name)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 4: + /* Must not be constructed but we will not check for it for simplicity. */ + name->presentation = 4; /* name_not_available */ + name->length = 0; + name->data[0] = 0; + ASN1_CALL(pos, asn1_dec_null(ctrl, "nameNotAvailable", tag, pos, end)); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 7: + /* Must not be constructed but we will not check for it for simplicity. */ + name->presentation = 3; /* presentation_restricted_null */ + name->length = 0; + name->data[0] = 0; + ASN1_CALL(pos, asn1_dec_null(ctrl, "namePresentationRestrictedNull", tag, pos, + end)); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + return pos; +} + +/*! + * \internal + * \brief Decode the Q.SIG party-Name invoke argument parameters. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param name Field name + * \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 party Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_dec_qsig_PartyName_ARG_Backend(struct pri *ctrl, + const char *name, unsigned tag, const unsigned char *pos, const unsigned char *end, + struct roseQsigPartyName_ARG *party) +{ + int length; + int seq_offset; + const unsigned char *seq_end; + + if (tag == ASN1_TAG_SEQUENCE) { + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s %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, rose_dec_qsig_Name(ctrl, "name", tag, pos, seq_end, + &party->name)); + + /* Fixup will skip over any OPTIONAL manufacturer extension information */ + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + } else { + ASN1_CALL(pos, rose_dec_qsig_Name(ctrl, name, tag, pos, end, &party->name)); + } + + return pos; +} + +/*! + * \brief Decode the Q.SIG CallingName invoke argument 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. + */ +const unsigned char *rose_dec_qsig_CallingName_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_PartyName_ARG_Backend(ctrl, "callingName", tag, pos, end, + &args->qsig.CallingName); +} + +/*! + * \brief Decode the Q.SIG CalledName invoke argument 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. + */ +const unsigned char *rose_dec_qsig_CalledName_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_PartyName_ARG_Backend(ctrl, "calledName", tag, pos, end, + &args->qsig.CalledName); +} + +/*! + * \brief Decode the Q.SIG ConnectedName invoke argument 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. + */ +const unsigned char *rose_dec_qsig_ConnectedName_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_PartyName_ARG_Backend(ctrl, "connectedName", tag, pos, end, + &args->qsig.ConnectedName); +} + +/*! + * \brief Decode the Q.SIG BusyName invoke argument 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. + */ +const unsigned char *rose_dec_qsig_BusyName_ARG(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, union rose_msg_invoke_args *args) +{ + return rose_dec_qsig_PartyName_ARG_Backend(ctrl, "busyName", tag, pos, end, + &args->qsig.BusyName); +} + +/* ------------------------------------------------------------------- */ +/* end rose_qsig_name.c */ Property changes on: rose_qsig_name.c ___________________________________________________________________ Added: svn:eol-style + native Added: svn:mime-type + text/plain Added: svn:keywords + 'Author Date Id Revision' Index: q921.c =================================================================== --- a/q921.c (.../tags/1.4.10.2) (revision 1357) +++ b/q921.c (.../branches/1.4) (revision 1357) @@ -27,6 +27,7 @@ * terms granted here. */ +#include #include #include #include @@ -139,8 +140,7 @@ #endif pri->ri = random() % 65535; q921_send_tei(pri, Q921_TEI_IDENTITY_REQUEST, pri->ri, Q921_TEI_GROUP, 1); - if (pri->t202_timer) - pri_schedule_del(pri, pri->t202_timer); + pri_schedule_del(pri, pri->t202_timer); pri->t202_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T202], q921_tei_request, pri); } @@ -174,8 +174,8 @@ { struct pri *pri = vpri; q921_h h; + pri_schedule_del(pri, pri->sabme_timer); - pri->sabme_timer = 0; pri->sabme_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], q921_send_sabme_now, pri); if (!now) return; @@ -248,14 +248,12 @@ static void t203_expire(void *); static void t200_expire(void *); -static pri_event *q921_dchannel_down(struct pri *pri); static void reschedule_t200(struct pri *pri) { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Restarting T200 timer\n"); - if (pri->t200_timer) - pri_schedule_del(pri, pri->t200_timer); + pri_schedule_del(pri, pri->t200_timer); pri->t200_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri); } @@ -263,8 +261,7 @@ { if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Restarting T203 timer\n"); - if (pri->t203_timer) - pri_schedule_del(pri, pri->t203_timer); + pri_schedule_del(pri, pri->t203_timer); pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T203], t203_expire, pri); } @@ -312,10 +309,8 @@ if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "-- Since there was nothing left, stopping T200 counter\n"); /* Something was ACK'd. Stop T200 counter */ - if (pri->t200_timer) { - pri_schedule_del(pri, pri->t200_timer); - pri->t200_timer = 0; - } + pri_schedule_del(pri, pri->t200_timer); + pri->t200_timer = 0; } if (pri->t203_timer) { if (pri->debug & PRI_DEBUG_Q921_DUMP) @@ -483,6 +478,45 @@ } } +int q921_transmit_uiframe(struct pri *pri, void *buf, int len) +{ + uint8_t ubuf[512]; + q921_h *h = (void *)&ubuf[0]; + + if (len >= 512) { + pri_error(pri, "Requested to send UI frame larger than 512 bytes!\n"); + return -1; + } + + memset(ubuf, 0, sizeof(ubuf)); + h->h.sapi = 0; + h->h.ea1 = 0; + h->h.ea2 = 1; + h->h.tei = pri->tei; + h->u.m3 = 0; + h->u.m2 = 0; + h->u.p_f = 0; /* Poll bit set */ + h->u.ft = Q921_FRAMETYPE_U; + + switch(pri->localtype) { + case PRI_NETWORK: + h->h.c_r = 1; + break; + case PRI_CPE: + h->h.c_r = 0; + break; + default: + pri_error(pri, "Don't know how to U/A on a type %d node\n", pri->localtype); + return -1; + } + + memcpy(h->u.data, buf, len); + + q921_transmit(pri, h, len + 3); + + return 0; +} + int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr) { q921_frame *f, *prev=NULL; @@ -521,6 +555,10 @@ pri->txqueue = f; /* Immediately transmit unless we're in a recovery state, or the window size is too big */ + if (pri->debug & PRI_DEBUG_Q921_DUMP) { + pri_message(pri, "TEI/SAPI: %d/%d state %d retran %d busy %d\n", + pri->tei, pri->sapi, pri->q921_state, pri->retrans, pri->busy); + } if ((pri->q921_state == Q921_LINK_CONNECTION_ESTABLISHED) && (!pri->retrans && !pri->busy)) { if (pri->windowlen < pri->window) { q921_send_queued_iframes(pri); @@ -564,11 +602,15 @@ /* Start timer T200 to resend our RR if we don't get it */ pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T200], t200_expire, pri); } else { - if (pri->debug & PRI_DEBUG_Q921_DUMP) - pri_message(pri, "T203 counter expired in weird state %d\n", pri->q921_state); + if (pri->debug & PRI_DEBUG_Q921_DUMP) { + pri_message(pri, + "T203 counter expired in weird state %d on pri with SAPI/TEI of %d/%d\n", + pri->q921_state, pri->sapi, pri->tei); + } pri->t203_timer = 0; } } + static pri_event *q921_handle_iframe(struct pri *pri, q921_i *i, int len) { int res; @@ -622,33 +664,34 @@ char *buf = malloc(len * 3 + 1); int buflen = 0; if (buf) { + pri_message(pri, "\n"); for (x=0;xraw[x]); - pri_message(pri, "\n%c [ %s]\n", direction_tag, buf); + pri_message(pri, "%c [ %s]\n", direction_tag, buf); free(buf); } } + pri_message(pri, "\n"); switch (h->h.data[0] & Q921_FRAMETYPE_MASK) { case 0: case 2: - pri_message(pri, "\n%c Informational frame:\n", direction_tag); + pri_message(pri, "%c Informational frame:\n", direction_tag); break; case 1: - pri_message(pri, "\n%c Supervisory frame:\n", direction_tag); + pri_message(pri, "%c Supervisory frame:\n", direction_tag); break; case 3: - pri_message(pri, "\n%c Unnumbered frame:\n", direction_tag); + pri_message(pri, "%c Unnumbered frame:\n", direction_tag); break; } - pri_message(pri, -"%c SAPI: %02d C/R: %d EA: %d\n" -"%c TEI: %03d EA: %d\n", + pri_message(pri, "%c SAPI: %02d C/R: %d EA: %d\n", direction_tag, h->h.sapi, h->h.c_r, - h->h.ea1, + h->h.ea1); + pri_message(pri, "%c TEI: %03d EA: %d\n", direction_tag, h->h.tei, h->h.ea2); @@ -658,15 +701,17 @@ case 2: /* Informational frame */ pri_message(pri, -"%c N(S): %03d 0: %d\n" -"%c N(R): %03d P: %d\n" -"%c %d bytes of data\n", +"%c N(S): %03d 0: %d\n", direction_tag, h->i.n_s, - h->i.ft, + h->i.ft); + pri_message(pri, +"%c N(R): %03d P: %d\n", direction_tag, h->i.n_r, - h->i.p_f, + h->i.p_f); + pri_message(pri, +"%c %d bytes of data\n", direction_tag, len - 4); break; @@ -685,17 +730,19 @@ break; } pri_message(pri, -"%c Zero: %d S: %d 01: %d [ %s ]\n" -"%c N(R): %03d P/F: %d\n" -"%c %d bytes of data\n", +"%c Zero: %d S: %d 01: %d [ %s ]\n", direction_tag, h->s.x0, h->s.ss, h->s.ft, - type, + type); + pri_message(pri, +"%c N(R): %03d P/F: %d\n", direction_tag, h->s.n_r, - h->s.p_f, + h->s.p_f); + pri_message(pri, +"%c %d bytes of data\n", direction_tag, len - 4); break; @@ -731,14 +778,15 @@ } } pri_message(pri, -"%c M3: %d P/F: %d M2: %d 11: %d [ %s ]\n" -"%c %d bytes of data\n", +"%c M3: %d P/F: %d M2: %d 11: %d [ %s ]\n", direction_tag, h->u.m3, h->u.p_f, h->u.m2, h->u.ft, - type, + type); + pri_message(pri, +"%c %d bytes of data\n", direction_tag, len - 3); break; @@ -783,7 +831,7 @@ } } -static pri_event *q921_dchannel_up(struct pri *pri) +pri_event *q921_dchannel_up(struct pri *pri) { if (pri->tei == Q921_TEI_PRI) { q921_reset(pri, 1); @@ -792,10 +840,8 @@ } /* Stop any SABME retransmissions */ - if (pri->sabme_timer) { - pri_schedule_del(pri, pri->sabme_timer); - pri->sabme_timer = 0; - } + pri_schedule_del(pri, pri->sabme_timer); + pri->sabme_timer = 0; /* Reset any rejects */ pri->sentrej = 0; @@ -805,7 +851,12 @@ pri_message(pri, DBGHEAD "q921_state now is Q921_LINK_CONNECTION_ESTABLISHED\n", DBGINFO); pri->q921_state = Q921_LINK_CONNECTION_ESTABLISHED; + /* Ensure that we do not have T200 or T203 running when the link comes up */ + pri_schedule_del(pri, pri->t200_timer); + pri->t200_timer = 0; + /* Start the T203 timer */ + pri_schedule_del(pri, pri->t203_timer); pri->t203_timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T203], t203_expire, pri); /* Notify Layer 3 */ @@ -818,7 +869,7 @@ return &pri->ev; } -static pri_event *q921_dchannel_down(struct pri *pri) +pri_event *q921_dchannel_down(struct pri *pri) { /* Reset counters, reset sabme timer etc */ q921_reset(pri, 1); @@ -826,7 +877,7 @@ /* Notify Layer 3 */ q931_dl_indication(pri, PRI_EVENT_DCHAN_DOWN); - /* Report event that D-Channel is now up */ + /* Report event that D-Channel is now down */ pri->ev.gen.e = PRI_EVENT_DCHAN_DOWN; return &pri->ev; } @@ -842,12 +893,9 @@ pri->v_na = 0; pri->window = pri->timers[PRI_TIMER_K]; pri->windowlen = 0; - if (pri->sabme_timer) - pri_schedule_del(pri, pri->sabme_timer); - if (pri->t203_timer) - pri_schedule_del(pri, pri->t203_timer); - if (pri->t200_timer) - pri_schedule_del(pri, pri->t200_timer); + pri_schedule_del(pri, pri->sabme_timer); + pri_schedule_del(pri, pri->t203_timer); + pri_schedule_del(pri, pri->t200_timer); pri->sabme_timer = 0; pri->sabme_count = 0; pri->t203_timer = 0; @@ -879,8 +927,14 @@ static pri_event *q921_receive_MDL(struct pri *pri, q921_u *h, int len) { int ri; - struct pri *sub; + struct pri *sub = pri; int tei; + + if (!BRI_NT_PTMP(pri) && !BRI_TE_PTMP(pri)) { + pri_error(pri, "Received MDL/TEI managemement message, but configured for mode other than PTMP!\n"); + return NULL; + } + if (pri->debug & PRI_DEBUG_Q921_STATE) pri_message(pri, "Received MDL message\n"); if (h->data[0] != 0x0f) { @@ -895,16 +949,19 @@ tei = (h->data[4] >> 1); switch(h->data[3]) { case Q921_TEI_IDENTITY_REQUEST: + if (!BRI_NT_PTMP(pri)) { + return NULL; + } + if (tei != 127) { pri_error(pri, "Received TEI identity request with invalid TEI %d\n", tei); q921_send_tei(pri, Q921_TEI_IDENTITY_DENIED, ri, tei, 1); } - /* Go to master */ - for (sub = pri; sub->master; sub = sub->master); tei = 64; - while(sub->subchannel) { - if(sub->subchannel->tei == tei) + while (sub->subchannel) { + if (sub->subchannel->tei == tei) ++tei; + sub = sub->subchannel; } sub->subchannel = __pri_new_tei(-1, pri->localtype, pri->switchtype, pri, NULL, NULL, NULL, tei, 1); if (!sub->subchannel) { @@ -914,14 +971,15 @@ q921_send_tei(pri, Q921_TEI_IDENTITY_ASSIGNED, ri, tei, 1); break; case Q921_TEI_IDENTITY_ASSIGNED: + if (!BRI_TE_PTMP(pri)) + return NULL; + if (ri != pri->ri) { pri_message(pri, "TEI assignment received for invalid Ri %02x (our is %02x)\n", ri, pri->ri); return NULL; } - if (pri->t202_timer) { - pri_schedule_del(pri, pri->t202_timer); - pri->t202_timer = 0; - } + pri_schedule_del(pri, pri->t202_timer); + pri->t202_timer = 0; if (pri->subchannel && (pri->subchannel->tei == tei)) { pri_error(pri, "TEI already assigned (new is %d, current is %d)\n", tei, pri->subchannel->tei); q921_tei_release_and_reacquire(pri); @@ -937,6 +995,8 @@ pri->q921_state = Q921_TEI_ASSIGNED; break; case Q921_TEI_IDENTITY_CHECK_REQUEST: + if (!BRI_TE_PTMP(pri)) + return NULL; /* We're assuming one TEI per PRI in TE PTMP mode */ /* If no subchannel (TEI) ignore */ @@ -949,6 +1009,8 @@ break; case Q921_TEI_IDENTITY_REMOVE: + if (!BRI_TE_PTMP(pri)) + return NULL; /* XXX: Assuming multiframe mode has been disconnected already */ if (!pri->subchannel) return NULL; @@ -990,6 +1052,10 @@ pri_error(pri, "!! Received short I-frame (expected 4, got %d)\n", len); break; } + + /* T203 is rescheduled only on reception of I frames or S frames */ + reschedule_t203(pri); + return q921_handle_iframe(pri, &h->i, len); break; case 1: @@ -1001,6 +1067,10 @@ pri_error(pri, "!! Received short S-frame (expected 4, got %d)\n", len); break; } + + /* T203 is rescheduled only on reception of I frames or S frames */ + reschedule_t203(pri); + switch(h->s.ss) { case 0: /* Receiver Ready */ @@ -1091,10 +1161,8 @@ } } /* Reset t200 timer if it was somehow going */ - if (pri->t200_timer) { - pri_schedule_del(pri, pri->t200_timer); - pri->t200_timer = 0; - } + pri_schedule_del(pri, pri->t200_timer); + pri->t200_timer = 0; /* Reset and restart t203 timer */ reschedule_t203(pri); } @@ -1152,7 +1220,9 @@ /* Acknowledge */ q921_send_ua(pri, h->u.p_f); ev = q921_dchannel_down(pri); - q921_restart(pri, 0); + if (BRI_TE_PTMP(pri) || PRI_PTP(pri)) { + q921_restart(pri, PRI_PTP(pri) ? 1 : 0); + } return ev; case 3: if (h->u.m2 == 3) { @@ -1212,6 +1282,34 @@ return NULL; } +static pri_event *q921_handle_unmatched_frame(struct pri *pri, q921_h *h, int len) +{ + pri = PRI_MASTER(pri); + + if (h->h.tei < 64) { + pri_error(pri, "Do not support manual TEI range. Discarding\n"); + return NULL; + } + + if (h->h.sapi != Q921_SAPI_CALL_CTRL) { + pri_error(pri, "Message with SAPI other than CALL CTRL is discarded\n"); + return NULL; + } + + if (pri->debug & PRI_DEBUG_Q921_DUMP) { + pri_message(pri, + "Could not find candidate subchannel for received frame with SAPI/TEI of %d/%d.\n", + h->h.sapi, h->h.tei); + pri_message(pri, "Sending TEI release, in order to re-establish TEI state\n"); + } + + /* Q.921 says we should send the remove message twice, in case of link corruption */ + q921_send_tei(pri, Q921_TEI_IDENTITY_REMOVE, 0, h->h.tei, 1); + q921_send_tei(pri, Q921_TEI_IDENTITY_REMOVE, 0, h->h.tei, 1); + + return NULL; +} + static pri_event *__q921_receive(struct pri *pri, q921_h *h, int len) { pri_event *ev; @@ -1225,26 +1323,22 @@ if (h->h.ea1 || !(h->h.ea2)) return NULL; -#if 0 /* Will be rejected by subchannel analyzis */ - /* Check for broadcasts - not yet handled */ - if (h->h.tei == Q921_TEI_GROUP) - return NULL; -#endif - if (!((h->h.sapi == pri->sapi) && ((h->h.tei == pri->tei) || (h->h.tei == Q921_TEI_GROUP)))) { /* Check for SAPIs we don't yet handle */ /* If it's not us, try any subchannels we have */ if (pri->subchannel) return q921_receive(pri->subchannel, h, len + 2); else { - return NULL; + /* This means we couldn't find a candidate subchannel for it... + * Time for some corrective action */ + + return q921_handle_unmatched_frame(pri, h, len); } } if (pri->debug & PRI_DEBUG_Q921_DUMP) pri_message(pri, "Handling message for SAPI/TEI=%d/%d\n", h->h.sapi, h->h.tei); ev = __q921_receive_qualified(pri, h, len); - reschedule_t203(pri); return ev; } Property changes on: . ___________________________________________________________________ Added: reviewboard:url + https://reviewboard.asterisk.org