From ba45095c5a649340f4cd55e637048d2c5071f2cb Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Tue, 21 Apr 2009 22:08:45 +0000 Subject: [PATCH] ROSE ASN.1 facility encode and decode rewrite of existing messages. Several components are now parsed correctly. Most notably: PartyNumber and Q.SIG Name. git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@766 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- Makefile | 41 +- asn1.h | 257 +++ asn1_primitive.c | 1281 ++++++++++++++ libpri.h | 70 +- pri_facility.c | 3872 ++++++++++++++++------------------------- pri_facility.h | 257 +-- pri_internal.h | 6 +- q931.c | 189 +- rose.c | 2219 +++++++++++++++++++++++ rose.h | 2454 ++++++++++++++++++++++++++ rose_address.c | 983 +++++++++++ rose_etsi_aoc.c | 1929 ++++++++++++++++++++ rose_internal.h | 332 ++++ rose_other.c | 277 +++ rose_q931.c | 100 ++ rose_qsig_ct.c | 883 ++++++++++ rose_qsig_diversion.c | 1390 +++++++++++++++ rose_qsig_mwi.c | 790 +++++++++ rose_qsig_name.c | 474 +++++ rosetest.c | 1713 ++++++++++++++++++ 20 files changed, 16801 insertions(+), 2716 deletions(-) create mode 100644 asn1.h create mode 100644 asn1_primitive.c create mode 100644 rose.c create mode 100644 rose.h create mode 100644 rose_address.c create mode 100644 rose_etsi_aoc.c create mode 100644 rose_internal.h create mode 100644 rose_other.c create mode 100644 rose_q931.c create mode 100644 rose_qsig_ct.c create mode 100644 rose_qsig_diversion.c create mode 100644 rose_qsig_mwi.c create mode 100644 rose_qsig_name.c create mode 100644 rosetest.c diff --git a/Makefile b/Makefile index 15e01c6..e2ef8fa 100644 --- a/Makefile +++ b/Makefile @@ -41,8 +41,42 @@ SONAME:=1.4 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 +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_other.o \ + rose_q931.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_other.lo \ + rose_q931.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) INSTALL_PREFIX=$(DESTDIR) INSTALL_BASE=/usr @@ -132,6 +166,9 @@ testprilib: testprilib.o 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 diff --git a/asn1.h b/asn1.h new file mode 100644 index 0000000..4eb19fb --- /dev/null +++ b/asn1.h @@ -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 */ diff --git a/asn1_primitive.c b/asn1_primitive.c new file mode 100644 index 0000000..8044b9a --- /dev/null +++ b/asn1_primitive.c @@ -0,0 +1,1281 @@ +/* + * 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 "compat.h" +#include "libpri.h" +#include "pri_internal.h" +#include "asn1.h" + +/* ------------------------------------------------------------------- */ + +/*! + * \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 *end; + unsigned delimiter; + unsigned count; + + 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; + } + pri_message(ctrl, "\n"); + } + } else { + pri_message(ctrl, "%*s<", indent, ""); + } + pri_message(ctrl, ">\n"); +} + +/*! + * \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 */ diff --git a/libpri.h b/libpri.h index 533244a..55009fd 100644 --- a/libpri.h +++ b/libpri.h @@ -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 @@ -103,13 +110,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 @@ -137,15 +144,44 @@ #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) /* Causes for disconnection */ #define PRI_CAUSE_UNALLOCATED 1 diff --git a/pri_facility.c b/pri_facility.c index dfc33f9..3b4bea9 100644 --- a/pri_facility.c +++ b/pri_facility.c @@ -33,872 +33,511 @@ #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; - } + 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]); - } - 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; - } - } - else if ((comp->type & ASN1_CLAN_MASK) == ASN1_CONTEXT_SPECIFIC) { - pri_message(pri, " (CONTEXT SPECIFIC [%d])", comp->type & ASN1_TYPE_MASK); - } - else { - pri_message(pri, " (component %02x)", comp->type); - } - 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; - } - return clen; -} - -int asn1_dump(struct pri *pri, void *comp, int len) -{ - return asn1_dumprecursive(pri, comp, len, 0); -} - -static unsigned char get_invokeid(struct pri *pri) -{ - return ++pri->last_invoke; -} - -struct addressingdataelements_presentednumberunscreened { - char partyaddress[21]; - char partysubaddress[21]; - int npi; /* Numbering Plan Indicator */ - int ton; /* Type Of Number */ - int pres; /* Presentation */ -}; - -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 */ -}; - -#define PRI_CHECKOVERFLOW(size) \ - if (msgptr - message + (size) >= sizeof(message)) { \ - *msgptr = '\0'; \ - pri_message(pri, "%s", message); \ - msgptr = message; \ - } - -static void dump_apdu(struct pri *pri, unsigned char *c, int len) -{ - #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]; - } - PRI_CHECKOVERFLOW(6); - strcpy(msgptr, " ] - ["); - msgptr += strlen(msgptr); - for (i=0; i '~')) ? '.' : c[i]; - } - PRI_CHECKOVERFLOW(2); - *msgptr++ = ']'; - *msgptr++ = '\n'; - *msgptr = '\0'; - pri_message(pri, "%s", message); -} -#undef PRI_CHECKOVERFLOW - -int redirectingreason_from_q931(struct pri *pri, int redirectingreason) -{ - 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; - } + 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: - 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; - } - } -} - -static int redirectingreason_for_q931(struct pri *pri, int redirectingreason) -{ - 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; - } + value = QSIG_DIVERT_REASON_UNKNOWN; + 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: - 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; - } + value = Q952_DIVERT_REASON_UNKNOWN; + break; + } + break; } + + return value; } -int typeofnumber_from_q931(struct pri *pri, int ton) +static int redirectingreason_for_q931(struct pri *ctrl, int redirectingreason) { - 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: + 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(pri, "!! Unsupported Q.931 TypeOfNumber value (%d)\n", ton); - /* fall through */ - case PRI_TON_UNKNOWN: - return Q932_TON_UNKNOWN; + pri_message(ctrl, "!! Unknown Q.SIG diversion reason %d\n", + redirectingreason); + value = PRI_REDIR_UNKNOWN; + break; + } + 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; + } + break; } + + return value; } -static int typeofnumber_for_q931(struct pri *pri, int ton) +/*! + * \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) { + 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 int typeofnumber_for_q931(struct pri *ctrl, int ton) +{ + int value; + 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; + 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; } -int asn1_name_decode(void * data, int len, char *namebuf, int buflen) +/*! + * \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) { - struct rose_component *comp = (struct rose_component*)data; - int datalen = 0, res = 0; + int value; - if (comp->len == ASN1_LEN_INDEF) { - datalen = strlen((char *)comp->data); - res = datalen + 2; - } else - datalen = res = comp->len; - - if (datalen > buflen) { - /* Truncate */ - datalen = buflen; + 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; } - memcpy(namebuf, comp->data, datalen); - return res + 2; + + return value; } -int asn1_string_encode(unsigned char asn1_type, void *data, int len, int max_len, void *src, int src_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) { - struct rose_component *comp = NULL; - - if (len < 2 + src_len) - return -1; + int value; - if (max_len && (src_len > max_len)) - src_len = max_len; + 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; + } - comp = (struct rose_component *)data; - comp->type = asn1_type; - comp->len = src_len; - memcpy(comp->data, src, src_len); - - return 2 + src_len; + return value; } -int asn1_copy_string(char * buf, int buflen, struct rose_component *comp) +/*! + * \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 res; - int datalen; + int value; - if ((comp->len > buflen) && (comp->len != ASN1_LEN_INDEF)) - return -1; - - if (comp->len == ASN1_LEN_INDEF) { - datalen = strlen((char*)comp->data); - res = datalen + 2; - } else - res = datalen = comp->len; - - memcpy(buf, comp->data, datalen); - buf[datalen] = 0; - - return res; -} - -static int rose_number_digits_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) -{ - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - int datalen = 0; - int res = 0; - - 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; + 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 */ } - 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'; - - return res + 2; - } - 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) -{ - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - int ton; - int res = 0; - - if (len < 2) - return -1; - - 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); - - res = rose_number_digits_decode(pri, call, &vdata[i], len-i, value); - if (res < 0) - return -1; - value->ton = ton; - - return res + 3; - - } while(0); - return -1; -} - -static int rose_private_party_number_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) -{ - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - int ton; - int res = 0; - - if (len < 2) - return -1; - - 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); - - res = rose_number_digits_decode(pri, call, &vdata[i], len-i, value); - if (res < 0) - return -1; - value->ton = ton; - - return res + 3; - - } while(0); - return -1; -} - -static int rose_address_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) -{ - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - int res = 0; - - do { - GET_COMPONENT(comp, i, vdata, len); - - 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; - } - 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; - } - while (0); - - return -1; -} - -static int rose_presented_number_unscreened_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberunscreened *value) -{ - int i = 0; - int size = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - - /* Fill in default values */ - value->ton = PRI_TON_UNKNOWN; - value->npi = PRI_NPI_E163_E164; - value->pres = -1; /* Data is not available */ - - do { - GET_COMPONENT(comp, i, vdata, len); - - 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; - } - while (0); - - return -1; -} - -static int rose_diverting_leg_information2_decode(struct pri *pri, q931_call *call, struct rose_component *sequence, int len) -{ - 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)); - - /* Data checks */ - if (sequence->type != (ASN1_CONSTRUCTOR | ASN1_SEQUENCE)) { /* Constructed Sequence */ - pri_message(pri, "Invalid DivertingLegInformation2Type argument\n"); - return -1; + break; + case PRI_PRES_UNAVAILABLE: + value = 2; /* numberNotAvailableDueToInterworking */ + 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; - - 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); - - /* 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); - - 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); - - 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; - } - } - - 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]); - } - 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; + return value; } - -static int rose_diverting_leg_information2_encode(struct pri *pri, q931_call *call) + +/*! + * \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) +{ + 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; +} + +/*! + * \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 always is 1 because other isn't available in the current design */ + msg.args.qsig.DivertingLegInformation2.diversion_counter = 1; + + msg.args.qsig.DivertingLegInformation2.diversion_reason = + redirectingreason_from_q931(ctrl, call->redirectingreason); + + /* divertingNr */ + msg.args.qsig.DivertingLegInformation2.diverting_present = 1; + msg.args.qsig.DivertingLegInformation2.diverting.presentation = + presentation_from_q931(ctrl, call->redirectingpres, call->redirectingnum[0]); + msg.args.qsig.DivertingLegInformation2.diverting.number.plan = + numbering_plan_from_q931(ctrl, call->redirectingplan); + msg.args.qsig.DivertingLegInformation2.diverting.number.ton = + typeofnumber_from_q931(ctrl, call->redirectingplan); + libpri_copy_string((char *) + msg.args.qsig.DivertingLegInformation2.diverting.number.str, + call->redirectingnum, + sizeof(msg.args.qsig.DivertingLegInformation2.diverting.number.str)); + msg.args.qsig.DivertingLegInformation2.diverting.number.length = + strlen((char *) msg.args.qsig.DivertingLegInformation2.diverting.number.str); + + /* redirectingName */ + if (call->redirectingname[0]) { + msg.args.qsig.DivertingLegInformation2.redirecting_name_present = 1; + msg.args.qsig.DivertingLegInformation2.redirecting_name.presentation = 1; /* presentation_allowed */ + msg.args.qsig.DivertingLegInformation2.redirecting_name.char_set = 1; /* iso8859-1 */ + libpri_copy_string((char *) + msg.args.qsig.DivertingLegInformation2.redirecting_name.data, + call->redirectingname, + sizeof(msg.args.qsig.DivertingLegInformation2.redirecting_name.data)); + msg.args.qsig.DivertingLegInformation2.redirecting_name.length = strlen((char *) + msg.args.qsig.DivertingLegInformation2.redirecting_name.data); + } + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Encode and queue the Q.SIG 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, 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)) { + unsigned char *end; + + end = + enc_qsig_diverting_leg_information2(ctrl, buffer, buffer + sizeof(buffer), call); + if (!end) { return -1; } -#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); - - /* 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; - } - 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; - } - ASN1_FIXUP(compstk, compsp, buffer, i); - - /* Fix length of stacked components */ - while(compsp > 0) { - ASN1_FIXUP(compstk, compsp, buffer, i); - } - - if (pri_call_apdu_queue(call, Q931_SETUP, buffer, i, NULL, NULL)) - return -1; - - return 0; + return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL, 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) +{ + 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) { - int i = 0; 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,172 +545,214 @@ int rlt_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2) } 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 */ - - 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)) + end = + enc_dms100_rlt_initiate_transfer(ctrl, buffer, buffer + sizeof(buffer), + callwithid); + if (!end) { return -1; + } + + if (pri_call_apdu_queue(apdubearer, Q931_FACILITY, buffer, end - buffer, NULL, 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) +{ + 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_OperationInd; + msg.invoke_id = ROSE_DMS100_RLT_OPERATION_IND; + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \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 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; + unsigned char *end; - buffer[i++] = (Q932_PROTOCOL_ROSE); /* Note to self: DON'T set the EXT bit */ - buffer[i++] = (0x80 | RLT_SERVICE_ID); /* Service Identifier octet */ - - 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_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)) + end = enc_dms100_rlt_transfer_ability(ctrl, buffer, buffer + sizeof(buffer)); + if (!end) { return -1; - else + } + + return pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL, NULL); +} + +/*! + * \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; + } + + 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); + + return pos; +} + +/*! + * \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 call Call leg from 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, q931_call *call) +{ + struct fac_extension_header header; + struct rose_msg_invoke msg; + + 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); + msg.args.qsig.CallingName.name.presentation = 1; /* presentation_allowed */ + msg.args.qsig.CallingName.name.char_set = 1; /* iso8859-1 */ + /* Truncate the callername if necessary. */ + libpri_copy_string((char *) msg.args.qsig.CallingName.name.data, call->callername, + sizeof(msg.args.qsig.CallingName.name.data)); + msg.args.qsig.CallingName.name.length = + strlen((char *) msg.args.qsig.CallingName.name.data); + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \internal + * \brief Send callername 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->callername[0]) { return 0; -} - -/* Sending callername information functions */ -static int add_callername_facility_ies(struct pri *pri, q931_call *c, int cpe) -{ - 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)) { - return -1; } - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - /* Interpretation component */ - - 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); - } - - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); - - 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)); - - /* 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; - - 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)) + if (ctrl->switchtype == PRI_SWITCH_NI2 && !cpe) { + end = enc_ni2_information_following(ctrl, buffer, buffer + sizeof(buffer)); + if (!end) { return -1; + } + + if (pri_call_apdu_queue(call, Q931_SETUP, buffer, end - buffer, NULL, NULL)) { + return -1; + } + + /* + * We can reuse the buffer since the queue function doesn't + * need it. + */ } - - /* Now the APDU that contains the information that needs sent. - * We can reuse the buffer since the queue function doesn't - * need it. */ - - i = 0; - namelen = strlen(c->callername); - if (namelen > 50) { - namelen = 50; /* truncate the name */ - } - - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - /* Interpretation component */ - - 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); - } - - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); - - 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)); - - /* 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) + end = enc_qsig_calling_name(ctrl, buffer, buffer + sizeof(buffer), call); + 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, NULL); } /* End Callername */ @@ -1081,104 +762,198 @@ static void mwi_activate_encode_cb(void *data) return; } -int mwi_message_send(struct pri* pri, q931_call *call, struct pri_sr *req, int activate) +/*! + * \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) { - 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) { + 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); + + msg.args.qsig.MWIActivate.served_user_number.plan = 0; /* unknown */ + libpri_copy_string((char *) msg.args.qsig.MWIActivate.served_user_number.str, + req->called, sizeof(msg.args.qsig.MWIActivate.served_user_number.str)); + msg.args.qsig.MWIActivate.served_user_number.length = strlen((char *) + msg.args.qsig.MWIActivate.served_user_number.str); + + msg.args.qsig.MWIActivate.basic_service = 1; /* speech */ + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \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) +{ + 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_MWIDeactivate; + msg.invoke_id = get_invokeid(ctrl); + + msg.args.qsig.MWIDeactivate.served_user_number.plan = 0; /* unknown */ + libpri_copy_string((char *) msg.args.qsig.MWIDeactivate.served_user_number.str, + req->called, sizeof(msg.args.qsig.MWIDeactivate.served_user_number.str)); + msg.args.qsig.MWIDeactivate.served_user_number.length = strlen((char *) + msg.args.qsig.MWIDeactivate.served_user_number.str); + + msg.args.qsig.MWIDeactivate.basic_service = 1; /* speech */ + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \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[0]) { return -1; - } else if (destlen > 20) - destlen = 20; /* Destination number cannot be greater then 20 digits */ + } - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - /* Interpretation component */ - - 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); - - ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, buffer, i, 0); - - 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)); - - 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) + 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; - 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); - - 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, + mwi_activate_encode_cb, 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, 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,190 +962,144 @@ int eect_initiate_transfer(struct pri *pri, q931_call *c1, q931_call *c2) /* 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 calling Calling number. + * \param dest Destination number. + * \param original Original called number. + * \param reason Rerouting reason: cfu, cfb, cfnr + * + * \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, const char *calling, const char *dest, const char *original, + const char *reason) { -/*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; - callingPartySubaddress [3] PartySubaddress OPTIONAL, - - callingNumber [4] PresentedNumberScreened, - - 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 } -*/ - - 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, + static const unsigned char q931ie[] = { + 0x04, /* Bearer Capability IE */ + 0x03, /* len */ + 0x80, /* ETSI Standard, Speech */ + 0x90, /* circuit mode, 64kbit/s */ + 0xa3, /* level1 protocol, a-law */ + 0x95, /* locking shift to codeset 5 (national use) */ + 0x32, /* Unknown ie */ + 0x01, /* Unknown ie len */ + 0x81, /* Unknown ie body */ }; - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_EXTENSIONS); - /* Interpretation component */ - - 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); - - 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) - return -1; - i += res; - - /* call rerouting argument */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - - /* reroutingReason DiversionReason */ - - 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 */ + 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; } + memset(&msg, 0, sizeof(msg)); + msg.operation = ROSE_QSIG_CallRerouting; + msg.invoke_id = get_invokeid(ctrl); - /* 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; + /* The rerouting_reason defaults to unknown */ + if (reason) { + if (!strcasecmp(reason, "cfu")) { + msg.args.qsig.CallRerouting.rerouting_reason = 1; /* cfu */ + } else if (!strcasecmp(reason, "cfb")) { + msg.args.qsig.CallRerouting.rerouting_reason = 2; /* cfb */ + } else if (!strcasecmp(reason, "cfnr")) { + msg.args.qsig.CallRerouting.rerouting_reason = 3; /* cfnr */ + } + } - i += j; - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); + /* calledAddress */ + msg.args.qsig.CallRerouting.called.number.plan = 1; /* public */ + msg.args.qsig.CallRerouting.called.number.ton = 0; /* unknown */ + libpri_copy_string((char *) msg.args.qsig.CallRerouting.called.number.str, dest, + sizeof(msg.args.qsig.CallRerouting.called.number.str)); + msg.args.qsig.CallRerouting.called.number.length = strlen((char *) + msg.args.qsig.CallRerouting.called.number.str); - /* diversionCounter INTEGER (1..15) */ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, 1); + msg.args.qsig.CallRerouting.diversion_counter = 1; /* 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); + msg.args.qsig.CallRerouting.q931ie.length = sizeof(q931ie); + memcpy(msg.args.qsig.CallRerouting.q931ie_contents, q931ie, sizeof(q931ie)); - /* 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); + /* lastReroutingNr */ + msg.args.qsig.CallRerouting.last_rerouting.presentation = 0; /* presentationAllowedNumber */ + msg.args.qsig.CallRerouting.last_rerouting.number.plan = 1; /* public */ + msg.args.qsig.CallRerouting.last_rerouting.number.ton = 0; /* unknown */ + libpri_copy_string((char *) msg.args.qsig.CallRerouting.last_rerouting.number.str, + original, sizeof(msg.args.qsig.CallRerouting.last_rerouting.number.str)); + msg.args.qsig.CallRerouting.last_rerouting.number.length = strlen((char *) + msg.args.qsig.CallRerouting.last_rerouting.number.str); - /* 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); + msg.args.qsig.CallRerouting.subscription_option = 0; /* noNotification */ - /* 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) + /* callingNumber */ + msg.args.qsig.CallRerouting.calling.presentation = 0; /* presentationAllowedNumber */ + msg.args.qsig.CallRerouting.calling.screened.number.plan = 1; /* public */ + msg.args.qsig.CallRerouting.calling.screened.number.ton = 0; /* unknown */ + libpri_copy_string((char *) msg.args.qsig.CallRerouting.calling.screened.number.str, + calling, sizeof(msg.args.qsig.CallRerouting.calling.screened.number.str)); + msg.args.qsig.CallRerouting.calling.screened.number.length = strlen((char *) + msg.args.qsig.CallRerouting.calling.screened.number.str); + msg.args.qsig.CallRerouting.calling.screened.screening_indicator = 3; /* networkProvided */ + + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \brief Send the Q.SIG CallRerouting invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param call Call leg from which to encode name. + * \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) +{ + unsigned char buffer[255]; + unsigned char *end; + int res; + + end = + enc_qsig_call_rerouting(ctrl, buffer, buffer + sizeof(buffer), call->callernum, + dest, original ? original : call->callednum, reason); + if (!end) { return -1; + } - i += j; - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - - /* subscriptionOption [2]*/ - /* implicit optional lastReroutingNr tag */ - ASN1_ADD_BYTECOMP(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_2), buffer, i, 0); /* noNotification */ - - /* 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); - - /* 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); - - /* 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; - - i += j; - ASN1_FIXUP(compstk, compsp, buffer, i); - - /* Screeening Indicator network provided */ - ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, buffer, i, 3); - - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - - /**/ - - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - - res = pri_call_apdu_queue(c, Q931_FACILITY, buffer, i, NULL, NULL); + res = pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, 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(c->pri, c); + res = q931_facility(call->pri, call); if (res) { - pri_message(pri, "Could not schedule facility message for call %d\n", c->cr); + pri_message(ctrl, "Could not schedule facility message for call %d\n", call->cr); return -1; } @@ -1378,375 +1107,217 @@ int qsig_cf_callrerouting(struct pri *pri, q931_call *c, const char* dest, const } /* End QSIG CC-CallRerouting */ +/* + * 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 *pri, 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_message(pri, "Could not Clear queue ADPU\n"); + return -1; } - + /* 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, + NULL); if (res) { - pri_message(pri, "Could not queue ADPU in facility message\n"); - return -1; + pri_message(pri, "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(pri, "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) - 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 */ + unsigned char buffer[255]; + unsigned char *pos; + unsigned char *end; + int res; + struct fac_extension_header header; + struct rose_msg_invoke msg; - /* - * 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); + end = buffer + sizeof(buffer); - 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 ADPU in facility message\n"); + 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; } - + + 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; + } + + res = pri_call_apdu_queue(c1, Q931_FACILITY, buffer, pos - buffer, NULL, NULL); + if (res) { + 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, 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); - - 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 */ + pos = facility_encode_header(ctrl, pos, end, NULL); + if (!pos) { + return NULL; } - return 0; -} - -static int aoc_aoce_charging_unit_decode(struct pri *pri, q931_call *call, unsigned char *data, int len) -{ - 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; - - if (pri->debug & PRI_DEBUG_AOC) - dump_apdu (pri, data, len); - - 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); - - 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 */ - } - call->aoc_units = chargingunits; - - return 0; -} - -static int aoc_aoce_charging_unit_encode(struct pri *pri, q931_call *c, long chargedunits) -{ - /* 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]; - - /* ROSE protocol (0x91)*/ - buffer[i++] = (ASN1_CONTEXT_SPECIFIC | Q932_PROTOCOL_ROSE); - - /* ROSE Component (0xA1,len)*/ - ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, buffer, i); - ASN1_PUSH(compstk, compsp, comp); - - /* ROSE invokeId component (0x02,len,id)*/ - ASN1_ADD_WORDCOMP(comp, INVOKE_IDENTIFIER, buffer, i, ++pri->last_invoke); - - /* ROSE operationId component (0x02,0x01,0x24)*/ - ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, buffer, i, ROSE_AOC_AOCE_CHARGING_UNIT); - - /* AOCEChargingUnitInfo (0x30,len) */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - - if (chargedunits > 0) { - /* SpecificChargingUnits (0x30,len) */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONSTRUCTOR | ASN1_SEQUENCE), buffer, i); - ASN1_PUSH(compstk, compsp, comp); - - /* 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); - - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); - ASN1_FIXUP(compstk, compsp, buffer, i); + 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 { - /* freeOfCharge (0x81,0) */ - ASN1_ADD_SIMPLE(comp, (ASN1_CONTEXT_SPECIFIC | ASN1_TAG_1), buffer, i); + 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; } - 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"); + pos = rose_encode_invoke(ctrl, pos, end, &msg); + + return pos; +} + +/*! + * \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) +{ + unsigned char buffer[255]; + unsigned char *end; + + /* sample data: [ 91 a1 12 02 02 3a 78 02 01 24 30 09 30 07 a1 05 30 03 02 01 01 ] */ + + end = + enc_etsi_aoce_charging_unit(ctrl, buffer, buffer + sizeof(buffer), chargedunits); + 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 */ - res = q931_facility(c->pri, c); - if (res) { - pri_message(pri, "Could not schedule facility message for call %d\n", c->cr); + if (pri_call_apdu_queue(call, Q931_FACILITY, buffer, end - buffer, NULL, NULL) + || q931_facility(call->pri, call)) { + pri_message(ctrl, "Could not schedule facility message for call %d\n", call->cr); return -1; } @@ -1754,836 +1325,25 @@ static int aoc_aoce_charging_unit_encode(struct pri *pri, q931_call *c, long cha } /* End AOC */ -static int rose_calling_name_decode(struct pri *pri, q931_call *call, struct rose_component *choice, int len) -{ - 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; - - 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); - - 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); - - 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; - } -} /* ===== 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) -{ - int i = 0; - int size = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - - - do { - GET_COMPONENT(comp, i, vdata, len); - - 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; - - 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; - - 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; - - 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; - - 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; - - 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; - - 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; - } - while (0); - - return -1; -} - - -static int rose_number_screened_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberscreened *value) -{ - int i = 0; - int size = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - - 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) - return -1; - comp->len = size; - NEXT_COMPONENT(comp, i); - - /* 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); - - value->scrind = scrind; - - 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. - } - while (0); - - return -1; -} - - -static int rose_presented_number_screened_decode(struct pri *pri, q931_call *call, unsigned char *data, int len, struct addressingdataelements_presentednumberscreened *value) -{ - int i = 0; - int size = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = data; - - /* Fill in default values */ - value->ton = PRI_TON_UNKNOWN; - value->npi = PRI_NPI_UNKNOWN; - value->pres = -1; /* Data is not available */ - - do { - GET_COMPONENT(comp, i, vdata, len); - - 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; - - 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; - - 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; - - 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; - - default: - pri_message(pri, "Invalid PresentedNumberScreened component 0x%X\n", comp->type); - } - 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) -{ - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = sequence->data; - int res = 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; - - - /* Data checks */ - if (sequence->type != (ASN1_CONSTRUCTOR | ASN1_SEQUENCE)) { /* Constructed Sequence */ - pri_message(pri, "Invalid callTransferComplete argument. (Not a sequence)\n"); - 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; - - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: len=%d\n", len); - - 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); - - - /* 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); - - -#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 - - - /* 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); - - - /* 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); - - - /* 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; - - 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; - - 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(i < len) - pri_message(pri, " CT-Complete: !! not all information is handled !! i=%d / len=%d\n", i, len); - - return 0; - } - while (0); - - return -1; -} - - -static int rose_call_transfer_update_decode(struct pri *pri, q931_call *call, struct rose_component *sequence, int len) -{ - int i = 0; - struct rose_component *comp = NULL; - unsigned char *vdata = sequence->data; - int res = 0; - - 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; - - - /* Data checks */ - if (sequence->type != (ASN1_CONSTRUCTOR | ASN1_SEQUENCE)) { /* Constructed Sequence */ - pri_message(pri, "Invalid callTransferComplete argument. (Not a sequence)\n"); - 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; - - if (pri->debug & PRI_DEBUG_APDU) - pri_message(pri, " CT-Complete: len=%d\n", len); - - 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); - - /* 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); - - -#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 - - - /* 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; - - 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; - - 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(i < len) - pri_message(pri, " CT-Complete: !! not all information is handled !! i=%d / len=%d\n", i, len); - - return 0; - } - while (0); - - return -1; -} - - /* ===== End Call Transfer Supplementary Service (ECMA-178) ===== */ - - -int rose_reject_decode(struct pri *pri, q931_call *call, q931_ie *ie, unsigned char *data, int len) -{ - 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); - - 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"; - 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"; - } - - 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; - } - - } while(0); - - return -1; -} -int rose_return_error_decode(struct pri *pri, q931_call *call, q931_ie *ie, unsigned char *data, int len) -{ - 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); - - 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); - - 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"; - } - - 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"; - } - - pri_error(pri, "ROSE RETURN ERROR:\n"); - pri_error(pri, "\tOPERATION: %s\n", invokeidstr); - pri_error(pri, "\tERROR: %s\n", errorstr); - - return 0; - } else { - pri_message(pri, "Unable to handle return error on switchtype %d!\n", pri->switchtype); - } - - } while(0); - - return -1; -} - -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); - - 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"); - - /* 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); - - if (operationidvalue != RLT_OPERATION_IND) { - pri_message(pri, "Invalid Operation ID value (0x%x) in return result!\n", operationidvalue); - return -1; - } - - /* 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; - - return 0; - - default: - pri_message(pri, "Could not parse invoke of type 0x%x!\n", invokeidvalue); - return -1; - } - } 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; - } - - } while(0); - - return -1; -} - -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); - - /* 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); - - /* No argument - return with error */ - if (i >= len) - return -1; - - /* Arguement Tag */ - GET_COMPONENT(comp, i, vdata, len); - if (!comp->type) - return -1; - - 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); - } - 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); - } - 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; - 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); - } - return -1; - } - } while(0); - - return -1; -} - -int pri_call_apdu_queue(q931_call *call, int messagetype, void *apdu, int apdu_len, void (*function)(void *data), void *data) +/*! + * \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 function Callback function for when response is received. (Not implemented) + * \param data Parameter to callback function. + * + * \retval 0 on success. + * \retval -1 on error. + */ +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; @@ -2601,7 +1361,7 @@ int pri_call_apdu_queue(q931_call *call, int messagetype, void *apdu, int apdu_l 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) { @@ -2632,13 +1392,15 @@ int pri_call_apdu_queue_cleanup(q931_call *call) return 0; } +/*! \note Only called when sending the SETUP message. */ 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]) + 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; @@ -2647,20 +1409,20 @@ int pri_call_add_standard_apdus(struct pri *pri, q931_call *call) #if 0 if (pri->localtype == PRI_NETWORK) { switch (pri->switchtype) { - case PRI_SWITCH_NI2: - add_callername_facility_ies(pri, call, 0); - break; - default: - break; + case PRI_SWITCH_NI2: + add_callername_facility_ies(pri, call, 0); + 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; + case PRI_SWITCH_NI2: + add_callername_facility_ies(pri, call, 1); + break; + default: + break; } return 0; } @@ -2678,3 +1440,431 @@ int pri_call_add_standard_apdus(struct pri *pri, q931_call *call) 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 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, q931_ie *ie, + const struct fac_extension_header *header, const struct rose_msg_reject *reject) +{ + pri_error(ctrl, "ROSE REJECT:\n"); + if (reject->invoke_id_present) { + pri_error(ctrl, "\tINVOKE ID: %d\n", reject->invoke_id); + } + pri_error(ctrl, "\tPROBLEM: %s\n", rose_reject2str(reject->code)); +} + +/*! + * \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 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, q931_ie *ie, + const struct fac_extension_header *header, const struct rose_msg_error *error) +{ + const char *dms100_operation; + + 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)); +} + +/*! + * \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 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, q931_ie *ie, + const struct fac_extension_header *header, const struct rose_msg_result *result) +{ + switch (ctrl->switchtype) { + case PRI_SWITCH_DMS100: + 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; + } + + /* 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"); + } + break; + default: + pri_message(ctrl, "Could not parse invoke of type %d!\n", result->invoke_id); + break; + } + return; + default: + break; + } + + switch (result->operation) { +#if 0 /* Not handled yet */ + case ROSE_None: + /* + * This is simply a positive ACK to the invoke request. + * The invoke ID must be used to distinguish between outstanding + * invoke requests. + */ + break; +#endif /* Not handled yet */ +#if 0 /* Not handled yet */ + case ROSE_ETSI_ChargingRequest: + break; +#endif /* Not handled yet */ +#if 0 /* Not handled yet */ + case ROSE_QSIG_CallTransferIdentify: + break; + case ROSE_QSIG_CallTransferInitiate: + break; + case ROSE_QSIG_CallTransferSetup: + break; +#endif /* Not handled yet */ +#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 (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "Successfully completed QSIG CF callRerouting!\n"); + } + break; +#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 result operation not handled! %s\n", + rose_operation2str(result->operation)); + } + break; + } +} + +/*! + * \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 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, q931_ie *ie, + const struct fac_extension_header *header, const struct rose_msg_invoke *invoke) +{ + switch (invoke->operation) { + 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; + } + } + } + /* 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 */ + case ROSE_QSIG_CallingName: + if (invoke->args.qsig.CallingName.name.presentation == 1) { + libpri_copy_string(call->callername, + (char *) invoke->args.qsig.CallingName.name.data, + sizeof(call->callername)); + } else { + call->callername[0] = '\0'; + } + break; +#if 0 /* Not handled yet */ + case ROSE_QSIG_CalledName: + break; + case ROSE_QSIG_ConnectedName: + break; + case ROSE_QSIG_BusyName: + 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; + case ROSE_QSIG_CallTransferActive: + break; +#endif /* Not handled yet */ +#if 0 /* This was incomplete */ + case ROSE_QSIG_CallTransferComplete: + switch (invoke->args.qsig.CallTransferComplete.redirection.presentation) { + case 0: /* presentationAllowedNumber */ + case 3: /* presentationRestrictedNumber */ + libpri_copy_string(call->callernum, (char *) + invoke->args.qsig.CallTransferComplete.redirection.screened.number.str, + sizeof(call->callernum)); + break; + default: + call->callernum[0] = '\0'; + break; + } + call->callername[0] = '\0'; + if (invoke->args.qsig.CallTransferComplete.redirection_name_present) { + switch (invoke->args.qsig.CallTransferComplete.redirection_name.presentation) { + case 1: /* presentation_allowed */ + case 2: /* presentation_restricted */ + libpri_copy_string(call->callername, + (char *) invoke->args.qsig.CallTransferComplete.redirection_name. + data, sizeof(call->callername)); + break; + default: + break; + } + } + break; +#endif /* This was incomplete */ +#if 0 /* This was incomplete */ + case ROSE_QSIG_CallTransferUpdate: + switch (invoke->args.qsig.CallTransferUpdate.redirection.presentation) { + case 0: /* presentationAllowedNumber */ + case 3: /* presentationRestrictedNumber */ + libpri_copy_string(call->callernum, (char *) + invoke->args.qsig.CallTransferUpdate.redirection.screened.number.str, + sizeof(call->callernum)); + break; + default: + call->callernum[0] = '\0'; + break; + } + call->callername[0] = '\0'; + if (invoke->args.qsig.CallTransferUpdate.redirection_name_present) { + switch (invoke->args.qsig.CallTransferUpdate.redirection_name.presentation) { + case 1: /* presentation_allowed */ + case 2: /* presentation_restricted */ + libpri_copy_string(call->callername, + (char *) invoke->args.qsig.CallTransferUpdate.redirection_name.data, + sizeof(call->callername)); + break; + default: + break; + } + } + break; +#endif /* This was incomplete */ +#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; + case ROSE_QSIG_CallRerouting: + break; + case ROSE_QSIG_DivertingLegInformation1: + break; +#endif /* Not handled yet */ + case ROSE_QSIG_DivertingLegInformation2: + call->origredirectingreason = QSIG_DIVERT_REASON_UNKNOWN; + if (invoke->args.qsig.DivertingLegInformation2.original_diversion_reason_present) { + call->origredirectingreason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.original_diversion_reason); + } + call->redirectingreason = redirectingreason_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.diversion_reason); + call->redirectingpres = PRI_PRES_UNAVAILABLE; + call->redirectingnum[0] = '\0'; + call->redirectingplan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + if (invoke->args.qsig.DivertingLegInformation2.diverting_present) { + call->redirectingpres = + presentation_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.diverting.presentation); + switch (invoke->args.qsig.DivertingLegInformation2.diverting.presentation) { + case 0: /* presentationAllowedNumber */ + case 3: /* presentationRestrictedNumber */ + libpri_copy_string(call->redirectingnum, (char *) + invoke->args.qsig.DivertingLegInformation2.diverting.number.str, + sizeof(call->redirectingnum)); + call->redirectingplan = + numbering_plan_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.diverting.number.plan) + | typeofnumber_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.diverting.number.ton); + break; + default: + break; + } + } + if (invoke->args.qsig.DivertingLegInformation2.original_called_present) { + call->origcalledpres = + presentation_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.original_called. + presentation); + switch (invoke->args.qsig.DivertingLegInformation2.original_called. + presentation) { + case 0: /* presentationAllowedNumber */ + case 3: /* presentationRestrictedNumber */ + libpri_copy_string(call->origcallednum, (char *) + invoke->args.qsig.DivertingLegInformation2.original_called.number. + str, sizeof(call->origcallednum)); + call->origcalledplan = + numbering_plan_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.original_called. + number.plan) + | typeofnumber_for_q931(ctrl, + invoke->args.qsig.DivertingLegInformation2.original_called. + number.ton); + break; + default: + call->origcallednum[0] = '\0'; + call->origcalledplan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_E163_E164; + break; + } + } + call->redirectingname[0] = '\0'; + if (invoke->args.qsig.DivertingLegInformation2.redirecting_name_present) { + switch (invoke->args.qsig.DivertingLegInformation2.redirecting_name. + presentation) { + case 1: /* presentation_allowed */ + libpri_copy_string(call->redirectingname, (char *) + invoke->args.qsig.DivertingLegInformation2.redirecting_name.data, + sizeof(call->redirectingname)); + break; + default: + break; + } + } + call->origcalledname[0] = '\0'; + if (invoke->args.qsig.DivertingLegInformation2.original_called_name_present) { + switch (invoke->args.qsig.DivertingLegInformation2.original_called_name. + presentation) { + case 1: /* presentation_allowed */ + libpri_copy_string(call->origcalledname, (char *) + invoke->args.qsig.DivertingLegInformation2.original_called_name.data, + sizeof(call->origcalledname)); + break; + default: + break; + } + } + break; +#if 0 /* Not handled yet */ + case ROSE_QSIG_DivertingLegInformation3: + break; + 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; + } +} diff --git a/pri_facility.h b/pri_facility.h index bd6fd75..9364ce3 100644 --- a/pri_facility.h +++ b/pri_facility.h @@ -32,122 +32,13 @@ #include "pri_q931.h" /* 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 +60,6 @@ #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 - -struct rose_component { - u_int8_t type; - u_int8_t len; - u_int8_t data[0]; -}; - -#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 - -#define NEXT_COMPONENT(component, idx) \ - (idx) += (component)->len + 2 - -#define SUB_COMPONENT(component, idx) \ - (idx) += 2 - -#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) - -#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); @@ -325,6 +84,18 @@ int pri_call_apdu_queue_cleanup(q931_call *call); /* 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); + +/* Forward declare some ROSE structures for the following prototypes */ +struct fac_extension_header; +struct rose_msg_invoke; +struct rose_msg_result; +struct rose_msg_error; +struct rose_msg_reject; + +void rose_handle_invoke(struct pri *ctrl, q931_call *call, 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, 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, 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, q931_ie *ie, const struct fac_extension_header *header, const struct rose_msg_reject *reject); #endif /* _PRI_FACILITY_H */ diff --git a/pri_internal.h b/pri_internal.h index c3435f6..5d489a6 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -33,6 +33,8 @@ #include #include +#define ARRAY_LEN(arr) (sizeof(arr) / sizeof((arr)[0])) + #define DBGHEAD __FILE__ ":%d %s: " #define DBGINFO __LINE__,__PRETTY_FUNCTION__ @@ -51,6 +53,7 @@ enum q931_mode; #define MAX_TIMERS 32 +/*! \brief D channel controller structure */ struct pri { int fd; /* File descriptor for D-Channel */ pri_io_cb read_func; /* Read data callback */ @@ -128,10 +131,11 @@ struct pri { unsigned int q931_rxcount; #endif - unsigned char last_invoke; /* Last ROSE invoke ID */ + short last_invoke; /* Last ROSE invoke ID */ unsigned char sendfacility; }; +/*! \brief New call setup parameter structure */ struct pri_sr { int transmode; int channel; diff --git a/q931.c b/q931.c index 3d4d067..737c129 100644 --- a/q931.c +++ b/q931.c @@ -33,6 +33,7 @@ #include "pri_q921.h" #include "pri_q931.h" #include "pri_facility.h" +#include "rose.h" #include #include @@ -1015,8 +1016,7 @@ static FUNC_DUMP(dump_redirecting_number) 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); } @@ -1038,8 +1038,7 @@ static FUNC_DUMP(dump_connected_number) 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); } @@ -1063,8 +1062,7 @@ static FUNC_RECV(receive_redirecting_number) call->redirectingreason = ie->data[i] & 0x0f; break; } - } - while(!(ie->data[i++] & 0x80)); + } while(!(ie->data[i++] & 0x80)); q931_get_number((unsigned char *) call->redirectingnum, sizeof(call->redirectingnum), ie->data + i, ie->len - i); return 0; } @@ -1353,15 +1351,19 @@ static FUNC_SEND(transmit_facility) if ((tmp->message == msgtype) && !tmp->sent) break; } - if (!tmp) /* No APDU found */ return 0; + if (pri->debug & PRI_DEBUG_APDU) { + pri_message(pri, "Adding facility ie contents to send message:\n"); + facility_decode_dump(pri, tmp->apdu, tmp->apdu_len); + } + 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); return 0; } - + memcpy(&ie->data[i], tmp->apdu, tmp->apdu_len); i += tmp->apdu_len; tmp->sent = 1; @@ -1369,114 +1371,77 @@ static FUNC_SEND(transmit_facility) return i + 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; + struct fac_extension_header header; + struct rose_message rose; + const unsigned char *pos; + const unsigned char *end; - if (ie->len < 1) - return -1; + pos = ie->data; + end = ie->data + ie->len; - 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); + /* Make sure we have enough room for the protocol profile ie octet(s) */ + if (end < pos + 2) { return -1; - case Q932_PROTOCOL_EXTENSIONS: - state = Q932_STATE_NFE; - next_protocol = Q932_PROTOCOL_ROSE; - break; + } + switch (*pos & Q932_PROTOCOL_MASK) { case Q932_PROTOCOL_ROSE: + case Q932_PROTOCOL_EXTENSIONS: break; default: - pri_error(pri, "!! Invalid Q.932 Protocol Profile of type 0x%X received\n", protocol); - return -1; - } - /* Service indicator octet - Just ignore for now */ - if (!(ie->data[i] & 0x80)) - i++; - i++; - - if (ie->len < 3) - 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; - } + 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); } - i += (comp->len + 2); + return -1; } -#undef Q932_HANDLE + 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); + if (!pos) { + return -1; + } + 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); + } + return -1; + } + + pos = rose_decode(ctrl, pos, end, &rose); + if (!pos) { + return -1; + } + switch (rose.type) { + case ROSE_COMP_TYPE_INVOKE: + rose_handle_invoke(ctrl, call, ie, &header, &rose.component.invoke); + break; + case ROSE_COMP_TYPE_RESULT: + rose_handle_result(ctrl, call, ie, &header, &rose.component.result); + break; + case ROSE_COMP_TYPE_ERROR: + rose_handle_error(ctrl, call, ie, &header, &rose.component.error); + break; + case ROSE_COMP_TYPE_REJECT: + rose_handle_reject(ctrl, call, ie, &header, &rose.component.reject); + break; + default: + return -1; + } return 0; } @@ -1711,12 +1676,13 @@ static void dump_ie_data(struct pri *pri, unsigned char *c, int len) static FUNC_DUMP(dump_facility) { 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"); 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); + pri_message(pri, "PROTOCOL %02X\n", ie->data[0] & Q932_PROTOCOL_MASK); + asn1_dump(pri, ie->data + dataat, ie->data + ie->len); } } @@ -1725,7 +1691,7 @@ static FUNC_DUMP(dump_network_spec_fac) { pri_message(pri, "%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(pri, "%s", code2str(ie->data[1], facilities, ARRAY_LEN(facilities))); } else dump_ie_data(pri, ie->data, ie->len); @@ -2374,7 +2340,7 @@ static inline void q931_dumpie(struct pri *pri, int codeset, q931_ie *ie, char p base_ie = (((full_ie & ~0x7f) == Q931_FULL_IE(0, 0x80)) && ((full_ie & 0x70) != 0x20)) ? full_ie & ~0x0f : full_ie; - for (x=0;x 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); diff --git a/rose.c b/rose.c new file mode 100644 index 0000000..7ee2554 --- /dev/null +++ b/rose.c @@ -0,0 +1,2219 @@ +/* + * 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 specific invoke/result encode/decode message table */ +static const struct rose_convert_msg rose_etsi_msgs[] = { +/* *INDENT-OFF* */ +/* + * operation, oid_prefix, value, + * encode_invoke_args, encode_result_args, + * decode_invoke_args, decode_result_args + */ + /* + * localValue's from Advice-of-Charge-Operations + * {ccitt identified-organization etsi (0) 182 operations-and-errors (1)} + * + * Advice-Of-Charge-at-call-Setup(AOCS) + * Advice-Of-Charge-During-the-call(AOCD) + * Advice-Of-Charge-at-the-End-of-the-call(AOCE) + */ + { + ROSE_ETSI_ChargingRequest, NULL, 30, + rose_enc_etsi_ChargingRequest_ARG, rose_enc_etsi_ChargingRequest_RES, + rose_dec_etsi_ChargingRequest_ARG, rose_dec_etsi_ChargingRequest_RES + }, + { + ROSE_ETSI_AOCSCurrency, NULL, 31, + rose_enc_etsi_AOCSCurrency_ARG, NULL, + rose_dec_etsi_AOCSCurrency_ARG, NULL + }, + { + ROSE_ETSI_AOCSSpecialArr, NULL, 32, + rose_enc_etsi_AOCSSpecialArr_ARG, NULL, + rose_dec_etsi_AOCSSpecialArr_ARG, NULL + }, + { + ROSE_ETSI_AOCDCurrency, NULL, 33, + rose_enc_etsi_AOCDCurrency_ARG, NULL, + rose_dec_etsi_AOCDCurrency_ARG, NULL + }, + { + ROSE_ETSI_AOCDChargingUnit, NULL, 34, + rose_enc_etsi_AOCDChargingUnit_ARG, NULL, + rose_dec_etsi_AOCDChargingUnit_ARG, NULL + }, + { + ROSE_ETSI_AOCECurrency, NULL, 35, + rose_enc_etsi_AOCECurrency_ARG, NULL, + rose_dec_etsi_AOCECurrency_ARG, NULL + }, + { + ROSE_ETSI_AOCEChargingUnit, NULL, 36, + rose_enc_etsi_AOCEChargingUnit_ARG, NULL, + rose_dec_etsi_AOCEChargingUnit_ARG, NULL + }, +/* *INDENT-ON* */ +}; + + +/*! \brief ETSI specific error-value converion table */ +static const struct rose_convert_error rose_etsi_errors[] = { +/* *INDENT-OFF* */ +/* + * error-code, oid_prefix, value + * encode_error_args, decode_error_args + */ + /* + * localValue Errors from General-Errors + * {ccitt identified-organization etsi(0) 196 general-errors(2)} + */ + { + ROSE_ERROR_Gen_NotSubscribed, NULL, 0, + NULL, NULL + }, + { + ROSE_ERROR_Gen_NotAvailable, NULL, 3, + NULL, NULL + }, + { + ROSE_ERROR_Gen_NotImplemented, NULL, 4, + NULL, NULL + }, + { + ROSE_ERROR_Gen_InvalidServedUserNr, NULL, 6, + NULL, NULL + }, + { + ROSE_ERROR_Gen_InvalidCallState, NULL, 7, + NULL, NULL + }, + { + ROSE_ERROR_Gen_BasicServiceNotProvided, NULL, 8, + NULL, NULL + }, + { + ROSE_ERROR_Gen_NotIncomingCall, NULL, 9, + NULL, NULL + }, + { + ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,NULL, 10, + NULL, NULL + }, + { + ROSE_ERROR_Gen_ResourceUnavailable, NULL, 11, + NULL, NULL + }, + + /* + * localValue Errors from Advice-of-Charge-Operations + * {ccitt identified-organization etsi (0) 182 operations-and-errors (1)} + */ + { + ROSE_ERROR_AOC_NoChargingInfoAvailable, NULL, 26, + NULL, NULL + }, +/* *INDENT-ON* */ +}; + + +/* ------------------------------------------------------------------- */ + + +/*! \brief Q.SIG specific invoke/result encode/decode message table */ +static const struct rose_convert_msg rose_qsig_msgs[] = { +/* *INDENT-OFF* */ +/* + * operation, oid_prefix, value, + * encode_invoke_args, encode_result_args, + * decode_invoke_args, decode_result_args + */ + /* + * localValue's from Q.SIG Name-Operations + * { iso(1) standard(0) pss1-name(13868) name-operations(0) } + */ + { + ROSE_QSIG_CallingName, NULL, 0, + rose_enc_qsig_CallingName_ARG, NULL, + rose_dec_qsig_CallingName_ARG, NULL + }, + { + ROSE_QSIG_CalledName, NULL, 1, + rose_enc_qsig_CalledName_ARG, NULL, + rose_dec_qsig_CalledName_ARG, NULL + }, + { + ROSE_QSIG_ConnectedName, NULL, 2, + rose_enc_qsig_ConnectedName_ARG, NULL, + rose_dec_qsig_ConnectedName_ARG, NULL + }, + { + ROSE_QSIG_BusyName, NULL, 3, + rose_enc_qsig_BusyName_ARG, NULL, + rose_dec_qsig_BusyName_ARG, NULL + }, + + /* + * localValue's from Q.SIG Call-Transfer-Operations + * { iso(1) standard(0) pss1-call-transfer(13869) call-transfer-operations(0) } + */ + { + ROSE_QSIG_CallTransferIdentify, NULL, 7, + rose_enc_qsig_DummyArg_ARG, rose_enc_qsig_CallTransferIdentify_RES, + rose_dec_qsig_DummyArg_ARG, rose_dec_qsig_CallTransferIdentify_RES + }, + { + ROSE_QSIG_CallTransferAbandon, NULL, 8, + rose_enc_qsig_DummyArg_ARG, NULL, + rose_dec_qsig_DummyArg_ARG, NULL + }, + { + ROSE_QSIG_CallTransferInitiate, NULL, 9, + rose_enc_qsig_CallTransferInitiate_ARG, rose_enc_qsig_DummyRes_RES, + rose_dec_qsig_CallTransferInitiate_ARG, rose_dec_qsig_DummyRes_RES + }, + { + ROSE_QSIG_CallTransferSetup, NULL, 10, + rose_enc_qsig_CallTransferSetup_ARG, rose_enc_qsig_DummyRes_RES, + rose_dec_qsig_CallTransferSetup_ARG, rose_dec_qsig_DummyRes_RES + }, + { + ROSE_QSIG_CallTransferActive, NULL, 11, + rose_enc_qsig_CallTransferActive_ARG, NULL, + rose_dec_qsig_CallTransferActive_ARG, NULL + }, + { + ROSE_QSIG_CallTransferComplete, NULL, 12, + rose_enc_qsig_CallTransferComplete_ARG, NULL, + rose_dec_qsig_CallTransferComplete_ARG, NULL + }, + { + ROSE_QSIG_CallTransferUpdate, NULL, 13, + rose_enc_qsig_CallTransferUpdate_ARG, NULL, + rose_dec_qsig_CallTransferUpdate_ARG, NULL + }, + { + ROSE_QSIG_SubaddressTransfer, NULL, 14, + rose_enc_qsig_SubaddressTransfer_ARG, NULL, + rose_dec_qsig_SubaddressTransfer_ARG, NULL + }, + + /* + * NOTE: I do not have the specification needed to fully support this + * message. Fortunately, all I have to do for this message is to switch + * it to the bridged call leg for 2BCT support. + */ + { + ROSE_QSIG_PathReplacement, NULL, 4, + NULL, NULL, + NULL, NULL + }, + + /* + * localValue's from Q.SIG Call-Diversion-Operations + * { iso(1) standard(0) pss1-call-diversion(13873) call-diversion-operations(0) } + */ + { + ROSE_QSIG_ActivateDiversionQ, NULL, 15, + rose_enc_qsig_ActivateDiversionQ_ARG, rose_enc_qsig_DummyRes_RES, + rose_dec_qsig_ActivateDiversionQ_ARG, rose_dec_qsig_DummyRes_RES + }, + { + ROSE_QSIG_DeactivateDiversionQ, NULL, 16, + rose_enc_qsig_DeactivateDiversionQ_ARG, rose_enc_qsig_DummyRes_RES, + rose_dec_qsig_DeactivateDiversionQ_ARG, rose_dec_qsig_DummyRes_RES + }, + { + ROSE_QSIG_InterrogateDiversionQ, NULL, 17, + rose_enc_qsig_InterrogateDiversionQ_ARG,rose_enc_qsig_InterrogateDiversionQ_RES, + rose_dec_qsig_InterrogateDiversionQ_ARG,rose_dec_qsig_InterrogateDiversionQ_RES + }, + { + ROSE_QSIG_CheckRestriction, NULL, 18, + rose_enc_qsig_CheckRestriction_ARG, rose_enc_qsig_DummyRes_RES, + rose_dec_qsig_CheckRestriction_ARG, rose_dec_qsig_DummyRes_RES + }, + { + ROSE_QSIG_CallRerouting, NULL, 19, + rose_enc_qsig_CallRerouting_ARG, rose_enc_qsig_DummyRes_RES, + rose_dec_qsig_CallRerouting_ARG, rose_dec_qsig_DummyRes_RES + }, + { + ROSE_QSIG_DivertingLegInformation1, NULL, 20, + rose_enc_qsig_DivertingLegInformation1_ARG,NULL, + rose_dec_qsig_DivertingLegInformation1_ARG,NULL + }, + { + ROSE_QSIG_DivertingLegInformation2, NULL, 21, + rose_enc_qsig_DivertingLegInformation2_ARG,NULL, + rose_dec_qsig_DivertingLegInformation2_ARG,NULL + }, + { + ROSE_QSIG_DivertingLegInformation3, NULL, 22, + rose_enc_qsig_DivertingLegInformation3_ARG,NULL, + rose_dec_qsig_DivertingLegInformation3_ARG,NULL + }, + { + ROSE_QSIG_CfnrDivertedLegFailed, NULL, 23, + rose_enc_qsig_DummyArg_ARG, NULL, + rose_dec_qsig_DummyArg_ARG, NULL + }, + + /* + * localValue's from Q.SIG SS-MWI-Operations + * { iso(1) standard(0) pss1-message-waiting-indication(15506) message-waiting-operations(0) } + */ + { + ROSE_QSIG_MWIActivate, NULL, 80, + rose_enc_qsig_MWIActivate_ARG, rose_enc_qsig_DummyRes_RES, + rose_dec_qsig_MWIActivate_ARG, rose_dec_qsig_DummyRes_RES + }, + { + ROSE_QSIG_MWIDeactivate, NULL, 81, + rose_enc_qsig_MWIDeactivate_ARG, rose_enc_qsig_DummyRes_RES, + rose_dec_qsig_MWIDeactivate_ARG, rose_dec_qsig_DummyRes_RES + }, + { + ROSE_QSIG_MWIInterrogate, NULL, 82, + rose_enc_qsig_MWIInterrogate_ARG, rose_enc_qsig_MWIInterrogate_RES, + rose_dec_qsig_MWIInterrogate_ARG, rose_dec_qsig_MWIInterrogate_RES + }, +/* *INDENT-ON* */ +}; + + +/*! \brief Q.SIG specific error-value converion table */ +static const struct rose_convert_error rose_qsig_errors[] = { +/* *INDENT-OFF* */ +/* + * error-code, oid_prefix, value + * encode_error_args, decode_error_args + */ + /* + * localValue Errors from General-Error-List + * {ccitt identified-organization q 950 general-error-list(1)} + */ + { + ROSE_ERROR_Gen_NotSubscribed, NULL, 0, + NULL, NULL + }, + { + ROSE_ERROR_Gen_RejectedByNetwork, NULL, 1, + NULL, NULL + }, + { + ROSE_ERROR_Gen_RejectedByUser, NULL, 2, + NULL, NULL + }, + { + ROSE_ERROR_Gen_NotAvailable, NULL, 3, + NULL, NULL + }, + { + ROSE_ERROR_Gen_InsufficientInformation, NULL, 5, + NULL, NULL + }, + { + ROSE_ERROR_Gen_InvalidServedUserNr, NULL, 6, + NULL, NULL + }, + { + ROSE_ERROR_Gen_InvalidCallState, NULL, 7, + NULL, NULL + }, + { + ROSE_ERROR_Gen_BasicServiceNotProvided, NULL, 8, + NULL, NULL + }, + { + ROSE_ERROR_Gen_NotIncomingCall, NULL, 9, + NULL, NULL + }, + { + ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,NULL, 10, + NULL, NULL + }, + { + ROSE_ERROR_Gen_ResourceUnavailable, NULL, 11, + NULL, NULL + }, + { + ROSE_ERROR_Gen_CallFailure, NULL, 25, + NULL, NULL + }, + { + ROSE_ERROR_Gen_ProceduralError, NULL, 43, + NULL, NULL + }, + + /* + * From various Q.SIG specifications. + * We will ignore the manufacturer specific extension information. + */ + { + ROSE_ERROR_QSIG_Unspecified, NULL, 1008, + NULL, NULL + }, + + /* + * localValue's from Q.SIG Call-Transfer-Operations + * { iso(1) standard(0) pss1-call-transfer(13869) call-transfer-operations(0) } + */ + { + ROSE_ERROR_QSIG_CT_InvalidReroutingNumber, NULL, 1004, + NULL, NULL + }, + { + ROSE_ERROR_QSIG_CT_UnrecognizedCallIdentity,NULL, 1005, + NULL, NULL + }, + { + ROSE_ERROR_QSIG_CT_EstablishmentFailure, NULL, 1006, + NULL, NULL + }, + + /* + * localValue's from Q.SIG Call-Diversion-Operations + * { iso(1) standard(0) pss1-call-diversion(13873) call-diversion-operations(0) } + */ + { + ROSE_ERROR_Div_InvalidDivertedToNr, NULL, 12, + NULL, NULL + }, + { + ROSE_ERROR_Div_SpecialServiceNr, NULL, 14, + NULL, NULL + }, + { + ROSE_ERROR_Div_DiversionToServedUserNr, NULL, 15, + NULL, NULL + }, + { + ROSE_ERROR_Div_NumberOfDiversionsExceeded, NULL, 24, + NULL, NULL + }, + { + ROSE_ERROR_QSIG_Div_TemporarilyUnavailable, NULL, 1000, + NULL, NULL + }, + { + ROSE_ERROR_QSIG_Div_NotAuthorized, NULL, 1007, + NULL, NULL + }, + + /* + * localValue's from Q.SIG SS-MWI-Operations + * { iso(1) standard(0) pss1-message-waiting-indication(15506) message-waiting-operations(0) } + */ + { + ROSE_ERROR_QSIG_InvalidMsgCentreId, NULL, 1018, + NULL, NULL + }, +/* *INDENT-ON* */ +}; + + +/* ------------------------------------------------------------------- */ + + +/*! \brief DMS-100 specific invoke/result encode/decode message table */ +static const struct rose_convert_msg rose_dms100_msgs[] = { +/* *INDENT-OFF* */ +/* + * operation, oid_prefix, value, + * encode_invoke_args, encode_result_args, + * decode_invoke_args, decode_result_args + */ + { + ROSE_DMS100_RLT_OperationInd, NULL, ROSE_DMS100_RLT_OPERATION_IND, + NULL, rose_enc_dms100_RLT_OperationInd_RES, + NULL, rose_dec_dms100_RLT_OperationInd_RES + }, + { + ROSE_DMS100_RLT_ThirdParty, NULL, ROSE_DMS100_RLT_THIRD_PARTY, + rose_enc_dms100_RLT_ThirdParty_ARG, NULL, + rose_dec_dms100_RLT_ThirdParty_ARG, NULL + }, +/* *INDENT-ON* */ +}; + + +/*! \brief DMS-100 specific error-value converion table */ +static const struct rose_convert_error rose_dms100_errors[] = { +/* *INDENT-OFF* */ +/* + * error-code, oid_prefix, value + * encode_error_args, decode_error_args + */ + { + ROSE_ERROR_DMS100_RLT_BridgeFail, NULL, 0x10, + NULL, NULL + }, + { + ROSE_ERROR_DMS100_RLT_CallIDNotFound, NULL, 0x11, + NULL, NULL + }, + { + ROSE_ERROR_DMS100_RLT_NotAllowed, NULL, 0x12, + NULL, NULL + }, + { + ROSE_ERROR_DMS100_RLT_SwitchEquipCongs, NULL, 0x13, + NULL, NULL + }, +/* *INDENT-ON* */ +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * Note the first value in oid.values[] is really the first two + * OID subidentifiers. They are compressed using this formula: + * First_Value = (First_Subidentifier * 40) + Second_Subidentifier + */ + +static const struct asn1_oid rose_ni2_oid = { +/* *INDENT-OFF* */ + /* { iso(1) member-body(2) usa(840) ansi-t1(10005) operations(0) } */ + 4, { 42, 840, 10005, 0 } +/* *INDENT-ON* */ +}; + +/*! \brief NI2 specific invoke/result encode/decode message table */ +static const struct rose_convert_msg rose_ni2_msgs[] = { +/* *INDENT-OFF* */ +/* + * operation, oid_prefix, value, + * encode_invoke_args, encode_result_args, + * decode_invoke_args, decode_result_args + */ + /* NI2 seems to have pirated several Q.SIG messages */ + /* + * localValue's from Q.SIG Name-Operations + * { iso(1) standard(0) pss1-name(13868) name-operations(0) } + */ + { + ROSE_QSIG_CallingName, NULL, 0, + rose_enc_qsig_CallingName_ARG, NULL, + rose_dec_qsig_CallingName_ARG, NULL + }, + { + ROSE_QSIG_CalledName, NULL, 1, + rose_enc_qsig_CalledName_ARG, NULL, + rose_dec_qsig_CalledName_ARG, NULL + }, + { + ROSE_QSIG_ConnectedName, NULL, 2, + rose_enc_qsig_ConnectedName_ARG, NULL, + rose_dec_qsig_ConnectedName_ARG, NULL + }, + { + ROSE_QSIG_BusyName, NULL, 3, + rose_enc_qsig_BusyName_ARG, NULL, + rose_dec_qsig_BusyName_ARG, NULL + }, + + { + ROSE_NI2_InformationFollowing, &rose_ni2_oid, 4, + rose_enc_ni2_InformationFollowing_ARG, NULL, + rose_dec_ni2_InformationFollowing_ARG, NULL + }, + + /* Also used by PRI_SWITCH_ATT4ESS and PRI_SWITCH_LUCENT5E */ + { + ROSE_NI2_InitiateTransfer, &rose_ni2_oid, 8, + rose_enc_ni2_InitiateTransfer_ARG, NULL, + rose_dec_ni2_InitiateTransfer_ARG, NULL + }, +/* *INDENT-ON* */ +}; + + +/*! \brief NI2 specific error-value converion table */ +static const struct rose_convert_error rose_ni2_errors[] = { +/* *INDENT-OFF* */ +/* + * error-code, oid_prefix, value + * encode_error_args, decode_error_args + */ + /* + * localValue Errors from General-Error-List + * {ccitt identified-organization q 950 general-error-list(1)} + */ + { + ROSE_ERROR_Gen_NotSubscribed, NULL, 0, + NULL, NULL + }, + { + ROSE_ERROR_Gen_RejectedByNetwork, NULL, 1, + NULL, NULL + }, + { + ROSE_ERROR_Gen_RejectedByUser, NULL, 2, + NULL, NULL + }, + { + ROSE_ERROR_Gen_NotAvailable, NULL, 3, + NULL, NULL + }, + { + ROSE_ERROR_Gen_InsufficientInformation, NULL, 5, + NULL, NULL + }, + { + ROSE_ERROR_Gen_InvalidServedUserNr, NULL, 6, + NULL, NULL + }, + { + ROSE_ERROR_Gen_InvalidCallState, NULL, 7, + NULL, NULL + }, + { + ROSE_ERROR_Gen_BasicServiceNotProvided, NULL, 8, + NULL, NULL + }, + { + ROSE_ERROR_Gen_NotIncomingCall, NULL, 9, + NULL, NULL + }, + { + ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,NULL, 10, + NULL, NULL + }, + { + ROSE_ERROR_Gen_ResourceUnavailable, NULL, 11, + NULL, NULL + }, + { + ROSE_ERROR_Gen_CallFailure, NULL, 25, + NULL, NULL + }, + { + ROSE_ERROR_Gen_ProceduralError, NULL, 43, + NULL, NULL + }, +/* *INDENT-ON* */ +}; + + +/* ------------------------------------------------------------------- */ + +/*! + * \internal + * \brief Convert the given code value to a string. + * + * \param code Code value to convert to a string. + * \param arr Array to convert the code to a string. + * \param num_elements Number of elements in the conversion array. + * + * \retval String version of the given code value. + */ +static const char *rose_code2str(int code, const struct rose_code_strings *arr, + unsigned num_elements) +{ + static char invalid_code[40]; + + unsigned index; + + for (index = 0; index < num_elements; ++index) { + if (arr[index].code == code) { + return arr[index].name; + } + } + + snprintf(invalid_code, sizeof(invalid_code), "Invalid code:%d 0x%X", code, code); + return invalid_code; +} + +/*! + * \brief Convert the given operation-value to a string. + * + * \param operation Operation-value to convert to a string. + * + * \retval String version of the given operation-value. + */ +const char *rose_operation2str(enum rose_operation operation) +{ + static const struct rose_code_strings arr[] = { +/* *INDENT-OFF* */ + { ROSE_None, "ROSE_None" }, + { ROSE_Unknown, "ROSE_Unknown" }, + + { ROSE_ETSI_ChargingRequest, "ROSE_ETSI_ChargingRequest" }, + { ROSE_ETSI_AOCSCurrency, "ROSE_ETSI_AOCSCurrency" }, + { ROSE_ETSI_AOCSSpecialArr, "ROSE_ETSI_AOCSSpecialArr" }, + { ROSE_ETSI_AOCDCurrency, "ROSE_ETSI_AOCDCurrency" }, + { ROSE_ETSI_AOCDChargingUnit, "ROSE_ETSI_AOCDChargingUnit" }, + { ROSE_ETSI_AOCECurrency, "ROSE_ETSI_AOCECurrency" }, + { ROSE_ETSI_AOCEChargingUnit, "ROSE_ETSI_AOCEChargingUnit" }, + + { ROSE_QSIG_CallingName, "ROSE_QSIG_CallingName" }, + { ROSE_QSIG_CalledName, "ROSE_QSIG_CalledName" }, + { ROSE_QSIG_ConnectedName, "ROSE_QSIG_ConnectedName" }, + { ROSE_QSIG_BusyName, "ROSE_QSIG_BusyName" }, + + { ROSE_QSIG_CallTransferIdentify, "ROSE_QSIG_CallTransferIdentify" }, + { ROSE_QSIG_CallTransferAbandon, "ROSE_QSIG_CallTransferAbandon" }, + { ROSE_QSIG_CallTransferInitiate, "ROSE_QSIG_CallTransferInitiate" }, + { ROSE_QSIG_CallTransferSetup, "ROSE_QSIG_CallTransferSetup" }, + { ROSE_QSIG_CallTransferActive, "ROSE_QSIG_CallTransferActive" }, + { ROSE_QSIG_CallTransferComplete, "ROSE_QSIG_CallTransferComplete" }, + { ROSE_QSIG_CallTransferUpdate, "ROSE_QSIG_CallTransferUpdate" }, + { ROSE_QSIG_SubaddressTransfer, "ROSE_QSIG_SubaddressTransfer" }, + + { ROSE_QSIG_PathReplacement, "ROSE_QSIG_PathReplacement" }, + + { ROSE_QSIG_ActivateDiversionQ, "ROSE_QSIG_ActivateDiversionQ" }, + { ROSE_QSIG_DeactivateDiversionQ, "ROSE_QSIG_DeactivateDiversionQ" }, + { ROSE_QSIG_InterrogateDiversionQ, "ROSE_QSIG_InterrogateDiversionQ" }, + { ROSE_QSIG_CheckRestriction, "ROSE_QSIG_CheckRestriction" }, + { ROSE_QSIG_CallRerouting, "ROSE_QSIG_CallRerouting" }, + { ROSE_QSIG_DivertingLegInformation1, "ROSE_QSIG_DivertingLegInformation1" }, + { ROSE_QSIG_DivertingLegInformation2, "ROSE_QSIG_DivertingLegInformation2" }, + { ROSE_QSIG_DivertingLegInformation3, "ROSE_QSIG_DivertingLegInformation3" }, + { ROSE_QSIG_CfnrDivertedLegFailed, "ROSE_QSIG_CfnrDivertedLegFailed" }, + + { ROSE_QSIG_MWIActivate, "ROSE_QSIG_MWIActivate" }, + { ROSE_QSIG_MWIDeactivate, "ROSE_QSIG_MWIDeactivate" }, + { ROSE_QSIG_MWIInterrogate, "ROSE_QSIG_MWIInterrogate" }, + + { ROSE_DMS100_RLT_OperationInd, "ROSE_DMS100_RLT_OperationInd" }, + { ROSE_DMS100_RLT_ThirdParty, "ROSE_DMS100_RLT_ThirdParty" }, + + { ROSE_NI2_InformationFollowing, "ROSE_NI2_InformationFollowing" }, + { ROSE_NI2_InitiateTransfer, "ROSE_NI2_InitiateTransfer" }, +/* *INDENT-ON* */ + }; + + return rose_code2str(operation, arr, ARRAY_LEN(arr)); +} + +/*! + * \brief Convert the given error-value to a string. + * + * \param code Error-value to convert to a string. + * + * \retval String version of the given error-value. + */ +const char *rose_error2str(enum rose_error_code code) +{ + static const struct rose_code_strings arr[] = { +/* *INDENT-OFF* */ + { ROSE_ERROR_None, "No error occurred" }, + { ROSE_ERROR_Unknown, "Unknown error-value code" }, + + { ROSE_ERROR_Gen_NotSubscribed, "General: Not Subscribed" }, + { ROSE_ERROR_Gen_NotAvailable, "General: Not Available" }, + { ROSE_ERROR_Gen_NotImplemented, "General: Not Implemented" }, + { ROSE_ERROR_Gen_InvalidServedUserNr, "General: Invalid Served User Number" }, + { ROSE_ERROR_Gen_InvalidCallState, "General: Invalid Call State" }, + { ROSE_ERROR_Gen_BasicServiceNotProvided, "General: Basic Service Not Provided" }, + { ROSE_ERROR_Gen_NotIncomingCall, "General: Not Incoming Call" }, + { ROSE_ERROR_Gen_SupplementaryServiceInteractionNotAllowed,"General: Supplementary Service Interaction Not Allowed" }, + { ROSE_ERROR_Gen_ResourceUnavailable, "General: Resource Unavailable" }, + + /* Additional Q.950 General-Errors for Q.SIG */ + { ROSE_ERROR_Gen_RejectedByNetwork, "General: Rejected By Network" }, + { ROSE_ERROR_Gen_RejectedByUser, "General: Rejected By User" }, + { ROSE_ERROR_Gen_InsufficientInformation, "General: Insufficient Information" }, + { ROSE_ERROR_Gen_CallFailure, "General: Call Failure" }, + { ROSE_ERROR_Gen_ProceduralError, "General: Procedural Error" }, + + { ROSE_ERROR_Div_InvalidDivertedToNr, "Diversion: Invalid Diverted To Number" }, + { ROSE_ERROR_Div_SpecialServiceNr, "Diversion: Special Service Number" }, + { ROSE_ERROR_Div_DiversionToServedUserNr, "Diversion: Diversion To Served User Number" }, + { ROSE_ERROR_Div_NumberOfDiversionsExceeded, "Diversion: Number Of Diversions Exceeded" }, + + { ROSE_ERROR_AOC_NoChargingInfoAvailable, "AOC: No Charging Info Available" }, + + /* Q.SIG specific errors */ + { ROSE_ERROR_QSIG_Unspecified, "Unspecified" }, + + { ROSE_ERROR_QSIG_CT_InvalidReroutingNumber, "CT: Invalid Rerouting Number" }, + { ROSE_ERROR_QSIG_CT_UnrecognizedCallIdentity,"CT: Unrecognized Call Identity" }, + { ROSE_ERROR_QSIG_CT_EstablishmentFailure, "CT: Establishment Failure" }, + + { ROSE_ERROR_QSIG_Div_TemporarilyUnavailable, "Diversion: Temporarily Unavailable" }, + { ROSE_ERROR_QSIG_Div_NotAuthorized, "Diversion: Not Authorized" }, + + { ROSE_ERROR_QSIG_InvalidMsgCentreId, "MWI: Invalid Message Center ID" }, + + /* DMS-100 specific errors */ + { ROSE_ERROR_DMS100_RLT_BridgeFail, "RLT: Bridge Fail" }, + { ROSE_ERROR_DMS100_RLT_CallIDNotFound, "RLT: Call ID Not Found" }, + { ROSE_ERROR_DMS100_RLT_NotAllowed, "RLT: Not Allowed" }, + { ROSE_ERROR_DMS100_RLT_SwitchEquipCongs, "RLT: Switch Equip Congs" }, +/* *INDENT-ON* */ + }; + + return rose_code2str(code, arr, ARRAY_LEN(arr)); +} + +/*! + * \brief Convert the given reject problem-value to a string. + * + * \param code Reject problem-value to convert to a string. + * + * \retval String version of the given reject problem-value. + */ +const char *rose_reject2str(enum rose_reject_code code) +{ + static const struct rose_code_strings arr[] = { +/* *INDENT-OFF* */ + { ROSE_REJECT_None, "No reject occurred" }, + { ROSE_REJECT_Unknown, "Unknown reject code" }, + + { ROSE_REJECT_Gen_UnrecognizedComponent, "General: Unrecognized Component" }, + { ROSE_REJECT_Gen_MistypedComponent, "General: Mistyped Component" }, + { ROSE_REJECT_Gen_BadlyStructuredComponent, "General: Badly Structured Component" }, + + { ROSE_REJECT_Inv_DuplicateInvocation, "Invoke: Duplicate Invocation" }, + { ROSE_REJECT_Inv_UnrecognizedOperation, "Invoke: Unrecognized Operation" }, + { ROSE_REJECT_Inv_MistypedArgument, "Invoke: Mistyped Argument" }, + { ROSE_REJECT_Inv_ResourceLimitation, "Invoke: Resource Limitation" }, + { ROSE_REJECT_Inv_InitiatorReleasing, "Invoke: Initiator Releasing" }, + { ROSE_REJECT_Inv_UnrecognizedLinkedID, "Invoke: Unrecognized Linked ID" }, + { ROSE_REJECT_Inv_LinkedResponseUnexpected, "Invoke: Linked Response Unexpected" }, + { ROSE_REJECT_Inv_UnexpectedChildOperation, "Invoke: Unexpected Child Operation" }, + + { ROSE_REJECT_Res_UnrecognizedInvocation, "Result: Unrecognized Invocation" }, + { ROSE_REJECT_Res_ResultResponseUnexpected, "Result: Result Response Unexpected" }, + { ROSE_REJECT_Res_MistypedResult, "Result: Mistyped Result" }, + + { ROSE_REJECT_Err_UnrecognizedInvocation, "Error: Unrecognized Invocation" }, + { ROSE_REJECT_Err_ErrorResponseUnexpected, "Error: Error Response Unexpected" }, + { ROSE_REJECT_Err_UnrecognizedError, "Error: Unrecognized Error" }, + { ROSE_REJECT_Err_UnexpectedError, "Error: Unexpected Error" }, + { ROSE_REJECT_Err_MistypedParameter, "Error: Mistyped Parameter" }, +/* *INDENT-ON* */ + }; + + return rose_code2str(code, arr, ARRAY_LEN(arr)); +} + +/*! + * \internal + * \brief Find an operation message conversion entry using the operation code. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param operation Library operation-value code. + * + * \retval Message conversion entry on success. + * \retval NULL on error. + */ +static const struct rose_convert_msg *rose_find_msg_by_op_code(struct pri *ctrl, + enum rose_operation operation) +{ + const struct rose_convert_msg *found; + const struct rose_convert_msg *table; + size_t num_entries; + size_t index; + + /* Determine which message conversion table to use */ + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_T1: + case PRI_SWITCH_EUROISDN_E1: + table = rose_etsi_msgs; + num_entries = ARRAY_LEN(rose_etsi_msgs); + break; + case PRI_SWITCH_QSIG: + table = rose_qsig_msgs; + num_entries = ARRAY_LEN(rose_qsig_msgs); + break; + case PRI_SWITCH_DMS100: + table = rose_dms100_msgs; + num_entries = ARRAY_LEN(rose_dms100_msgs); + break; + case PRI_SWITCH_ATT4ESS: + case PRI_SWITCH_LUCENT5E: + case PRI_SWITCH_NI2: + table = rose_ni2_msgs; + num_entries = ARRAY_LEN(rose_ni2_msgs); + break; + default: + return NULL; + } + + /* Search for the table entry */ + found = NULL; + for (index = 0; index < num_entries; ++index) { + if (table[index].operation == operation) { + found = &table[index]; + break; + } + } + + return found; +} + +/*! + * \internal + * \brief Find an operation message conversion entry using the + * operation-value OID value or localValue. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param oid Search for the full OID if not NULL. + * \param local Search for the localValue if OID is NULL. + * + * \retval Message conversion entry on success. + * \retval NULL on error. + */ +static const struct rose_convert_msg *rose_find_msg_by_op_val(struct pri *ctrl, + const struct asn1_oid *oid, unsigned local) +{ + const struct rose_convert_msg *found; + const struct rose_convert_msg *table; + size_t num_entries; + size_t index; + int sub_index; + + /* Determine which message conversion table to use */ + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_T1: + case PRI_SWITCH_EUROISDN_E1: + table = rose_etsi_msgs; + num_entries = ARRAY_LEN(rose_etsi_msgs); + break; + case PRI_SWITCH_QSIG: + table = rose_qsig_msgs; + num_entries = ARRAY_LEN(rose_qsig_msgs); + break; + case PRI_SWITCH_DMS100: + table = rose_dms100_msgs; + num_entries = ARRAY_LEN(rose_dms100_msgs); + break; + case PRI_SWITCH_ATT4ESS: + case PRI_SWITCH_LUCENT5E: + case PRI_SWITCH_NI2: + table = rose_ni2_msgs; + num_entries = ARRAY_LEN(rose_ni2_msgs); + break; + default: + return NULL; + } + + /* Search for the table entry */ + found = NULL; + if (oid) { + /* Search for an OID entry */ + local = oid->value[oid->num_values - 1]; + for (index = 0; index < num_entries; ++index) { + if (table[index].value == local && table[index].oid_prefix + && table[index].oid_prefix->num_values == oid->num_values - 1) { + /* Now lets match the OID prefix subidentifiers */ + for (sub_index = oid->num_values - 2; 0 <= sub_index; --sub_index) { + if (oid->value[sub_index] + != table[index].oid_prefix->value[sub_index]) { + break; + } + } + if (sub_index == -1) { + /* All of the OID subidentifiers matched */ + found = &table[index]; + break; + } + } + } + } else { + /* Search for a localValue entry */ + for (index = 0; index < num_entries; ++index) { + if (table[index].value == local && !table[index].oid_prefix) { + found = &table[index]; + break; + } + } + } + + return found; +} + +/*! + * \internal + * \brief Find an error conversion entry using the error code. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param code Library error-value code. + * + * \retval Error conversion entry on success. + * \retval NULL on error. + */ +static const struct rose_convert_error *rose_find_error_by_op_code(struct pri *ctrl, + enum rose_error_code code) +{ + const struct rose_convert_error *found; + const struct rose_convert_error *table; + size_t num_entries; + size_t index; + + /* Determine which error conversion table to use */ + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_T1: + case PRI_SWITCH_EUROISDN_E1: + table = rose_etsi_errors; + num_entries = ARRAY_LEN(rose_etsi_errors); + break; + case PRI_SWITCH_QSIG: + table = rose_qsig_errors; + num_entries = ARRAY_LEN(rose_qsig_errors); + break; + case PRI_SWITCH_DMS100: + table = rose_dms100_errors; + num_entries = ARRAY_LEN(rose_dms100_errors); + break; + case PRI_SWITCH_ATT4ESS: + case PRI_SWITCH_LUCENT5E: + case PRI_SWITCH_NI2: + table = rose_ni2_errors; + num_entries = ARRAY_LEN(rose_ni2_errors); + break; + default: + return NULL; + } + + /* Search for the table entry */ + found = NULL; + for (index = 0; index < num_entries; ++index) { + if (table[index].code == code) { + found = &table[index]; + break; + } + } + + return found; +} + +/*! + * \internal + * \brief Find an error conversion entry using the + * error-value OID value or localValue. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param oid Search for the full OID if not NULL. + * \param local Search for the localValue if OID is NULL. + * + * \retval Error conversion entry on success. + * \retval NULL on error. + */ +static const struct rose_convert_error *rose_find_error_by_op_val(struct pri *ctrl, + const struct asn1_oid *oid, unsigned local) +{ + const struct rose_convert_error *found; + const struct rose_convert_error *table; + size_t num_entries; + size_t index; + int sub_index; + + /* Determine which error conversion table to use */ + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_T1: + case PRI_SWITCH_EUROISDN_E1: + table = rose_etsi_errors; + num_entries = ARRAY_LEN(rose_etsi_errors); + break; + case PRI_SWITCH_QSIG: + table = rose_qsig_errors; + num_entries = ARRAY_LEN(rose_qsig_errors); + break; + case PRI_SWITCH_DMS100: + table = rose_dms100_errors; + num_entries = ARRAY_LEN(rose_dms100_errors); + break; + case PRI_SWITCH_ATT4ESS: + case PRI_SWITCH_LUCENT5E: + case PRI_SWITCH_NI2: + table = rose_ni2_errors; + num_entries = ARRAY_LEN(rose_ni2_errors); + break; + default: + return NULL; + } + + /* Search for the table entry */ + found = NULL; + if (oid) { + /* Search for an OID entry */ + local = oid->value[oid->num_values - 1]; + for (index = 0; index < num_entries; ++index) { + if (table[index].value == local && table[index].oid_prefix + && table[index].oid_prefix->num_values == oid->num_values - 1) { + /* Now lets match the OID prefix subidentifiers */ + for (sub_index = oid->num_values - 2; 0 <= sub_index; --sub_index) { + if (oid->value[sub_index] + != table[index].oid_prefix->value[sub_index]) { + break; + } + } + if (sub_index == -1) { + /* All of the OID subidentifiers matched */ + found = &table[index]; + break; + } + } + } + } else { + /* Search for a localValue entry */ + for (index = 0; index < num_entries; ++index) { + if (table[index].value == local && !table[index].oid_prefix) { + found = &table[index]; + break; + } + } + } + + return found; +} + +/*! + * \internal + * \brief Encode the Facility ie component operation-value. + * + * \param pos Starting position to encode the operation-value. + * \param end End of ASN.1 encoding data buffer. + * \param oid_prefix Encode as an OID if not NULL. + * \param local Encode as a localValue if oid_prefix is NULL + * else it is the last OID subidentifier. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *rose_enc_operation_value(unsigned char *pos, unsigned char *end, + const struct asn1_oid *oid_prefix, unsigned local) +{ + struct asn1_oid oid; + + if (oid_prefix) { + if (ARRAY_LEN(oid_prefix->value) <= oid_prefix->num_values) { + return NULL; + } + oid = *oid_prefix; + oid.value[oid.num_values++] = local; + return asn1_enc_oid(pos, end, ASN1_TYPE_OBJECT_IDENTIFIER, &oid); + } else { + return asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, local); + } +} + +/*! \brief Mapped to rose_enc_operation_value() */ +#define rose_enc_error_value(pos, end, oid_prefix, local) \ + rose_enc_operation_value(pos, end, oid_prefix, local) + +/*! + * \brief Encode the invoke component for a ROSE message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 message. + * \param end End of ASN.1 encoding data buffer. + * \param msg ROSE invoke message to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_encode_invoke(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rose_msg_invoke *msg) +{ + const struct rose_convert_msg *convert; + unsigned char *seq_len; + + convert = rose_find_msg_by_op_code(ctrl, msg->operation); + if (!convert) { + return NULL; + } + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_INVOKE); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id)); + if (msg->linked_id_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + msg->linked_id)); + } + ASN1_CALL(pos, rose_enc_operation_value(pos, end, convert->oid_prefix, + convert->value)); + + if (convert->encode_invoke_args) { + ASN1_CALL(pos, convert->encode_invoke_args(ctrl, pos, end, &msg->args)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the result component for a ROSE message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 message. + * \param end End of ASN.1 encoding data buffer. + * \param msg ROSE result message to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_encode_result(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rose_msg_result *msg) +{ + const struct rose_convert_msg *convert; + unsigned char *seq_len; + unsigned char *op_seq_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_RESULT); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id)); + + if (msg->operation != ROSE_None) { + convert = rose_find_msg_by_op_code(ctrl, msg->operation); + if (!convert) { + return NULL; + } + + ASN1_CONSTRUCTED_BEGIN(op_seq_len, pos, end, ASN1_TYPE_SEQUENCE); + + ASN1_CALL(pos, rose_enc_operation_value(pos, end, convert->oid_prefix, + convert->value)); + + if (convert->encode_result_args) { + ASN1_CALL(pos, convert->encode_result_args(ctrl, pos, end, &msg->args)); + } + + ASN1_CONSTRUCTED_END(op_seq_len, pos, end); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the error component for a ROSE message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 message. + * \param end End of ASN.1 encoding data buffer. + * \param msg ROSE error message to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_encode_error(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rose_msg_error *msg) +{ + const struct rose_convert_error *convert; + unsigned char *seq_len; + + convert = rose_find_error_by_op_code(ctrl, msg->code); + if (!convert) { + return NULL; + } + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_ERROR); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id)); + ASN1_CALL(pos, rose_enc_error_value(pos, end, convert->oid_prefix, convert->value)); + if (convert->encode_error_args) { + ASN1_CALL(pos, convert->encode_error_args(ctrl, pos, end, &msg->args)); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the reject component for a ROSE message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 message. + * \param end End of ASN.1 encoding data buffer. + * \param msg ROSE reject message to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *rose_encode_reject(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct rose_msg_reject *msg) +{ + unsigned char *seq_len; + unsigned tag; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ROSE_TAG_COMPONENT_REJECT); + + /* Encode Invoke ID */ + if (msg->invoke_id_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_TYPE_INTEGER, msg->invoke_id)); + } else { + ASN1_CALL(pos, asn1_enc_null(pos, end, ASN1_TYPE_NULL)); + } + + /* Encode the reject problem */ + switch (msg->code & ~0xFF) { + case ROSE_REJECT_BASE(ROSE_REJECT_BASE_General): + tag = ASN1_CLASS_CONTEXT_SPECIFIC | 0; + break; + case ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke): + tag = ASN1_CLASS_CONTEXT_SPECIFIC | 1; + break; + case ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result): + tag = ASN1_CLASS_CONTEXT_SPECIFIC | 2; + break; + case ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error): + tag = ASN1_CLASS_CONTEXT_SPECIFIC | 3; + break; + default: + return NULL; + } + ASN1_CALL(pos, asn1_enc_int(pos, end, tag, msg->code & 0xFF)); + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the ROSE message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 message. + * \param end End of ASN.1 encoding data buffer. + * \param msg ROSE message to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + * + * \note This function only encodes the ROSE contents. It does not include + * the protocol profile, NFE, NPP, and interpretation octets defined in + * a facility ie that may precede the ROSE contents. These header octets + * may already be stored in the encompassing buffer before the starting + * position given here. + */ +unsigned char *rose_encode(struct pri *ctrl, unsigned char *pos, unsigned char *end, + const struct rose_message *msg) +{ + switch (msg->type) { + case ROSE_COMP_TYPE_INVOKE: + pos = rose_encode_invoke(ctrl, pos, end, &msg->component.invoke); + break; + case ROSE_COMP_TYPE_RESULT: + pos = rose_encode_result(ctrl, pos, end, &msg->component.result); + break; + case ROSE_COMP_TYPE_ERROR: + pos = rose_encode_error(ctrl, pos, end, &msg->component.error); + break; + case ROSE_COMP_TYPE_REJECT: + pos = rose_encode_reject(ctrl, pos, end, &msg->component.reject); + break; + default: + pos = NULL; + break; + } + + return pos; +} + +/*! + * \internal + * \brief Encode the NetworkFacilityExtension type. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param nfe Network Facility Extension information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +static unsigned char *fac_enc_nfe(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct facNetworkFacilityExtension *nfe) +{ + unsigned char *seq_len; + unsigned char *explicit_len; + + ASN1_CONSTRUCTED_BEGIN(seq_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 10); + + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 0, + nfe->source_entity)); + if (nfe->source_number.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 1); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &nfe->source_number)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 2, + nfe->destination_entity)); + if (nfe->destination_number.length) { + /* EXPLICIT tag */ + ASN1_CONSTRUCTED_BEGIN(explicit_len, pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 3); + ASN1_CALL(pos, rose_enc_PartyNumber(ctrl, pos, end, &nfe->destination_number)); + ASN1_CONSTRUCTED_END(explicit_len, pos, end); + } + + ASN1_CONSTRUCTED_END(seq_len, pos, end); + + return pos; +} + +/*! + * \brief Encode the facility extension header. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode ASN.1 component. + * \param end End of ASN.1 encoding data buffer. + * \param header Facility extension information to encode. + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *fac_enc_extension_header(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct fac_extension_header *header) +{ + if (header->nfe_present) { + ASN1_CALL(pos, fac_enc_nfe(ctrl, pos, end, &header->nfe)); + } + if (header->npp_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 18, + header->npp)); + } + if (header->interpretation_present) { + ASN1_CALL(pos, asn1_enc_int(pos, end, ASN1_CLASS_CONTEXT_SPECIFIC | 11, + header->interpretation)); + } + + return pos; +} + +/*! + * \brief Encode the facility ie contents header. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position to encode the facility ie contents. + * \param end End of facility ie contents encoding data buffer. + * \param header Facility extension header data to encode (NULL if none). + * + * \retval Start of the next ASN.1 component to encode on success. + * \retval NULL on error. + */ +unsigned char *facility_encode_header(struct pri *ctrl, unsigned char *pos, + unsigned char *end, const struct fac_extension_header *header) +{ + /* Make sure we have some room. */ + if (end < pos + 2) { + return NULL; + } + + switch (ctrl->switchtype) { + case PRI_SWITCH_EUROISDN_T1: + case PRI_SWITCH_EUROISDN_E1: + *pos++ = 0x80 | Q932_PROTOCOL_ROSE; + header = NULL; + break; + case PRI_SWITCH_QSIG: + *pos++ = 0x80 | Q932_PROTOCOL_EXTENSIONS; + break; + case PRI_SWITCH_DMS100: + *pos++ = Q932_PROTOCOL_ROSE; /* DON'T set the EXT bit yet. */ + *pos++ = 0x80 | ROSE_DMS100_RLT_SERVICE_ID; + header = NULL; + break; + case PRI_SWITCH_ATT4ESS: + case PRI_SWITCH_LUCENT5E: + case PRI_SWITCH_NI2: + if (header) { + *pos++ = 0x80 | Q932_PROTOCOL_EXTENSIONS; + } else { + *pos++ = 0x80 | Q932_PROTOCOL_ROSE; + } + break; + default: + return NULL; + } + + if (header) { + ASN1_CALL(pos, fac_enc_extension_header(ctrl, pos, end, header)); + } + + return pos; +} + +/*! + * \internal + * \brief Decode the ROSE invoke message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param msg ROSE invoke message data to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_decode_invoke(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct rose_msg_invoke *msg) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + const struct rose_convert_msg *convert; + struct asn1_oid oid; + unsigned local; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "INVOKE Component %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value)); + msg->invoke_id = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | 0)) { + ASN1_CALL(pos, asn1_dec_int(ctrl, "linkedId", tag, pos, seq_end, &value)); + msg->linked_id = value; + msg->linked_id_present = 1; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + } else { + msg->linked_id_present = 0; + } + + /* Decode operation-value */ + switch (tag) { + case ASN1_TYPE_INTEGER: + ASN1_CALL(pos, asn1_dec_int(ctrl, "operationValue", tag, pos, seq_end, &value)); + local = value; + oid.num_values = 0; + break; + case ASN1_TYPE_OBJECT_IDENTIFIER: + ASN1_CALL(pos, asn1_dec_oid(ctrl, "operationValue", tag, pos, seq_end, &oid)); + local = 0; + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + convert = rose_find_msg_by_op_val(ctrl, (oid.num_values == 0) ? NULL : &oid, local); + if (convert) { + msg->operation = convert->operation; + } else { + msg->operation = ROSE_Unknown; + } + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " operationValue = %s\n", rose_operation2str(msg->operation)); + } + + /* Decode any expected invoke arguments */ + if (convert && convert->decode_invoke_args) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, convert->decode_invoke_args(ctrl, tag, pos, seq_end, &msg->args)); + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the ROSE result message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param msg ROSE result message data to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_decode_result(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct rose_msg_result *msg) +{ + int32_t value; + int length; + int seq_offset; + int op_seq_offset; + const unsigned char *seq_end; + const unsigned char *op_seq_end; + const struct rose_convert_msg *convert; + struct asn1_oid oid; + unsigned local; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "RESULT Component %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value)); + msg->invoke_id = value; + + /* Decode optional operation sequence */ + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TAG_SEQUENCE); + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " operation %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(op_seq_end, op_seq_offset, length, pos, seq_end); + + /* Decode operation-value */ + ASN1_CALL(pos, asn1_dec_tag(pos, op_seq_end, &tag)); + switch (tag) { + case ASN1_TYPE_INTEGER: + ASN1_CALL(pos, asn1_dec_int(ctrl, "operationValue", tag, pos, op_seq_end, + &value)); + local = value; + oid.num_values = 0; + break; + case ASN1_TYPE_OBJECT_IDENTIFIER: + ASN1_CALL(pos, asn1_dec_oid(ctrl, "operationValue", tag, pos, op_seq_end, + &oid)); + local = 0; + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + convert = + rose_find_msg_by_op_val(ctrl, (oid.num_values == 0) ? NULL : &oid, local); + if (convert) { + msg->operation = convert->operation; + } else { + msg->operation = ROSE_Unknown; + } + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " operationValue = %s\n", + rose_operation2str(msg->operation)); + } + + /* Decode any expected result arguments */ + if (convert && convert->decode_result_args) { + ASN1_CALL(pos, asn1_dec_tag(pos, op_seq_end, &tag)); + ASN1_CALL(pos, convert->decode_result_args(ctrl, tag, pos, op_seq_end, + &msg->args)); + } + + ASN1_END_FIXUP(ctrl, pos, op_seq_offset, op_seq_end, seq_end); + } else { + msg->operation = ROSE_None; + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the ROSE error message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param msg ROSE error message data to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_decode_error(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct rose_msg_error *msg) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + const struct rose_convert_error *convert; + struct asn1_oid oid; + unsigned local; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "ERROR Component %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_TYPE_INTEGER); + ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value)); + msg->invoke_id = value; + + /* Decode error-value */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_TYPE_INTEGER: + ASN1_CALL(pos, asn1_dec_int(ctrl, "errorValue", tag, pos, seq_end, &value)); + local = value; + oid.num_values = 0; + break; + case ASN1_TYPE_OBJECT_IDENTIFIER: + ASN1_CALL(pos, asn1_dec_oid(ctrl, "errorValue", tag, pos, seq_end, &oid)); + local = 0; + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + convert = + rose_find_error_by_op_val(ctrl, (oid.num_values == 0) ? NULL : &oid, local); + if (convert) { + msg->code = convert->code; + } else { + msg->code = ROSE_ERROR_Unknown; + } + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " errorValue = %s\n", rose_error2str(msg->code)); + } + + /* Decode any expected error parameters */ + if (convert && convert->decode_error_args) { + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CALL(pos, convert->decode_error_args(ctrl, tag, pos, seq_end, &msg->args)); + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \internal + * \brief Decode the ROSE reject message. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param tag Component tag that identified this structure. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param msg ROSE reject message data to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *rose_decode_reject(struct pri *ctrl, unsigned tag, + const unsigned char *pos, const unsigned char *end, struct rose_msg_reject *msg) +{ + int32_t value; + int length; + int seq_offset; + const unsigned char *seq_end; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, "REJECT Component %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + /* Invoke ID choice */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_TYPE_INTEGER: + ASN1_CALL(pos, asn1_dec_int(ctrl, "invokeId", tag, pos, seq_end, &value)); + msg->invoke_id = value; + msg->invoke_id_present = 1; + break; + case ASN1_TYPE_NULL: + ASN1_CALL(pos, asn1_dec_null(ctrl, "invokeId", tag, pos, seq_end)); + msg->invoke_id_present = 0; + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + /* Problem choice */ + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | 0: + ASN1_CALL(pos, asn1_dec_int(ctrl, "problemGeneral", tag, pos, seq_end, &value)); + msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_General) | (value & 0xFF); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 1: + ASN1_CALL(pos, asn1_dec_int(ctrl, "problemInvoke", tag, pos, seq_end, &value)); + msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Invoke) | (value & 0xFF); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 2: + ASN1_CALL(pos, asn1_dec_int(ctrl, "problemResult", tag, pos, seq_end, &value)); + msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Result) | (value & 0xFF); + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 3: + ASN1_CALL(pos, asn1_dec_int(ctrl, "problemError", tag, pos, seq_end, &value)); + msg->code = ROSE_REJECT_BASE(ROSE_REJECT_BASE_Error) | (value & 0xFF); + break; + default: + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " problem = %s\n", rose_reject2str(msg->code)); + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the ROSE message into the given buffer. + * + * \param ctrl D channel controller for diagnostic messages or global options. + * \param pos Starting position of the ASN.1 component. + * \param end End of ASN.1 decoding data buffer. + * \param msg Decoded ROSE message contents. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + * + * \note This function only decodes the ROSE contents. It does not check + * for the protocol profile, NFE, NPP, and interpretation octets defined in + * a facility ie that may preceed the ROSE contents. These header octets + * may already have been consumed from the encompasing buffer before the + * buffer given here. + */ +const unsigned char *rose_decode(struct pri *ctrl, const unsigned char *pos, + const unsigned char *end, struct rose_message *msg) +{ + unsigned tag; + + ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); + switch (tag) { + case ROSE_TAG_COMPONENT_INVOKE: + msg->type = ROSE_COMP_TYPE_INVOKE; + ASN1_CALL(pos, rose_decode_invoke(ctrl, tag, pos, end, &msg->component.invoke)); + break; + case ROSE_TAG_COMPONENT_RESULT: + msg->type = ROSE_COMP_TYPE_RESULT; + ASN1_CALL(pos, rose_decode_result(ctrl, tag, pos, end, &msg->component.result)); + break; + case ROSE_TAG_COMPONENT_ERROR: + msg->type = ROSE_COMP_TYPE_ERROR; + ASN1_CALL(pos, rose_decode_error(ctrl, tag, pos, end, &msg->component.error)); + break; + case ROSE_TAG_COMPONENT_REJECT: + msg->type = ROSE_COMP_TYPE_REJECT; + ASN1_CALL(pos, rose_decode_reject(ctrl, tag, pos, end, &msg->component.reject)); + break; + default: + msg->type = ROSE_COMP_TYPE_INVALID; + ASN1_DID_NOT_EXPECT_TAG(ctrl, tag); + return NULL; + } + + if (pos < end) { + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %u byte(s) of trailing data not consumed.\n", + (unsigned) (end - pos)); + } + } + + return pos; +} + +/*! + * \internal + * \brief Decode the NetworkFacilityExtension argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param name Field name + * \param tag Component tag that identified this production. + * \param pos Starting position of the ASN.1 component length. + * \param end End of ASN.1 decoding data buffer. + * \param nfe Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +static const unsigned char *fac_dec_nfe(struct pri *ctrl, const char *name, unsigned tag, + const unsigned char *pos, const unsigned char *end, + struct facNetworkFacilityExtension *nfe) +{ + int length; + int seq_offset; + int explicit_offset; + const unsigned char *seq_end; + const unsigned char *explicit_end; + const unsigned char *save_pos; + int32_t value; + + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " %s NetworkFacilityExtension %s\n", name, asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, end, &length)); + ASN1_END_SETUP(seq_end, seq_offset, length, pos, end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 0); + ASN1_CALL(pos, asn1_dec_int(ctrl, "sourceEntity", tag, pos, seq_end, &value)); + nfe->source_entity = value; + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 1)) { + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "sourceEntityAddress", tag, pos, + seq_end, &nfe->source_number)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + } else { + nfe->source_number.length = 0; + } + + ASN1_CHECK_TAG(ctrl, tag, tag, ASN1_CLASS_CONTEXT_SPECIFIC | 2); + ASN1_CALL(pos, asn1_dec_int(ctrl, "destinationEntity", tag, pos, seq_end, &value)); + nfe->destination_entity = value; + + nfe->destination_number.length = 0; + if (pos < seq_end && *pos != ASN1_INDEF_TERM) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, seq_end, &tag)); + if (tag == (ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 3)) { + /* Remove EXPLICIT tag */ + if (ctrl->debug & PRI_DEBUG_APDU) { + pri_message(ctrl, " Explicit %s\n", asn1_tag2str(tag)); + } + ASN1_CALL(pos, asn1_dec_length(pos, seq_end, &length)); + ASN1_END_SETUP(explicit_end, explicit_offset, length, pos, seq_end); + + ASN1_CALL(pos, asn1_dec_tag(pos, explicit_end, &tag)); + ASN1_CALL(pos, rose_dec_PartyNumber(ctrl, "destinationEntityAddress", tag, + pos, seq_end, &nfe->destination_number)); + + ASN1_END_FIXUP(ctrl, pos, explicit_offset, explicit_end, seq_end); + } else { + pos = save_pos; + } + } + + ASN1_END_FIXUP(ctrl, pos, seq_offset, seq_end, end); + + return pos; +} + +/*! + * \brief Decode the extension header argument parameters. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param pos Starting position of the ASN.1 component. + * \param end End of ASN.1 decoding data buffer. + * \param header Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success. + * \retval NULL on error. + */ +const unsigned char *fac_dec_extension_header(struct pri *ctrl, const unsigned char *pos, + const unsigned char *end, struct fac_extension_header *header) +{ + int32_t value; + unsigned tag; + const unsigned char *save_pos; + + /* + * For simplicity we are not checking the order of + * the optional header components. + */ + header->nfe_present = 0; + header->npp_present = 0; + header->interpretation_present = 0; + while (pos < end) { + save_pos = pos; + ASN1_CALL(pos, asn1_dec_tag(pos, end, &tag)); + switch (tag) { + case ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_PC_CONSTRUCTED | 10: + ASN1_CALL(pos, fac_dec_nfe(ctrl, "nfe", tag, pos, end, &header->nfe)); + header->nfe_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 18: + ASN1_CALL(pos, asn1_dec_int(ctrl, "networkProtocolProfile", tag, pos, end, + &value)); + header->npp = value; + header->npp_present = 1; + break; + case ASN1_CLASS_CONTEXT_SPECIFIC | 11: + ASN1_CALL(pos, asn1_dec_int(ctrl, "interpretation", tag, pos, end, &value)); + header->interpretation = value; + header->interpretation_present = 1; + break; + default: + pos = save_pos; + goto cancel_options; + } + } +cancel_options:; + + return pos; +} + +/*! + * \brief Decode the facility ie contents header. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param pos Starting position of the facility ie contents. + * \param end End of facility ie contents. + * \param header Parameter storage to fill. + * + * \retval Start of the next ASN.1 component on success (ROSE message). + * \retval NULL on error. + */ +const unsigned char *facility_decode_header(struct pri *ctrl, const unsigned char *pos, + const unsigned char *end, struct fac_extension_header *header) +{ + /* Make sure we have enough room for the protocol profile ie octet(s) */ + if (end < pos + 2) { + return NULL; + } + switch (*pos & Q932_PROTOCOL_MASK) { + case Q932_PROTOCOL_ROSE: + case Q932_PROTOCOL_EXTENSIONS: + break; + default: + return NULL; + } + if (!(*pos & 0x80)) { + /* DMS-100 Service indicator octet - Just ignore for now */ + ++pos; + } + ++pos; + + if (ctrl->debug & PRI_DEBUG_APDU) { + asn1_dump(ctrl, pos, end); + } + + pos = fac_dec_extension_header(ctrl, pos, end, header); + return pos; +} + +/*! + * \brief Decode the facility ie contents for debug purposes. + * + * \param ctrl D channel controller for any diagnostic messages. + * \param buf Buffer containing the facility ie contents. + * \param length Length of facility ie contents. + * + * \return Nothing + * + * \note Should only be called if PRI_DEBUG_APDU is enabled. Otherwise, + * it it does nothing useful. + */ +void facility_decode_dump(struct pri *ctrl, const unsigned char *buf, size_t length) +{ + const unsigned char *pos; + const unsigned char *end; + union { + struct fac_extension_header header; + struct rose_message rose; + } discard; + + end = buf + length; + pos = facility_decode_header(ctrl, buf, end, &discard.header); + while (pos && pos < end) { + pos = rose_decode(ctrl, pos, end, &discard.rose); + } +} + +/* ------------------------------------------------------------------- */ +/* end rose.c */ diff --git a/rose.h b/rose.h new file mode 100644 index 0000000..bb31259 --- /dev/null +++ b/rose.h @@ -0,0 +1,2454 @@ +/* + * 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 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 */ + + /* 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 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_NumberOfDiversionsExceeded, + + /* ETSI Advice-of-Charge-Operations */ + ROSE_ERROR_AOC_NoChargingInfoAvailable, + + /* Q.SIG from various specifications */ + ROSE_ERROR_QSIG_Unspecified, + + /* 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; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * 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; +}; + + +/* ------------------------------------------------------------------- */ + + +/* + * 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; +}; + +/*! \brief Facility ie result etsi messages with arguments. */ +union rose_msg_result_etsi_args { + /* ETSI Advice Of Charge (AOC) */ + struct roseEtsiChargingRequest_RES ChargingRequest; +}; + +/*! \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 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 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 */ diff --git a/rose_address.c b/rose_address.c new file mode 100644 index 0000000..4cda2d8 --- /dev/null +++ b/rose_address.c @@ -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 */ diff --git a/rose_etsi_aoc.c b/rose_etsi_aoc.c new file mode 100644 index 0000000..8e387a4 --- /dev/null +++ b/rose_etsi_aoc.c @@ -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 */ diff --git a/rose_internal.h b/rose_internal.h new file mode 100644 index 0000000..9267bce --- /dev/null +++ b/rose_internal.h @@ -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 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); + +/* 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: + * 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 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 */ diff --git a/rose_other.c b/rose_other.c new file mode 100644 index 0000000..3e0cfa2 --- /dev/null +++ b/rose_other.c @@ -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 */ diff --git a/rose_q931.c b/rose_q931.c new file mode 100644 index 0000000..3b60b50 --- /dev/null +++ b/rose_q931.c @@ -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 */ diff --git a/rose_qsig_ct.c b/rose_qsig_ct.c new file mode 100644 index 0000000..693fa0f --- /dev/null +++ b/rose_qsig_ct.c @@ -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 */ diff --git a/rose_qsig_diversion.c b/rose_qsig_diversion.c new file mode 100644 index 0000000..47a0e92 --- /dev/null +++ b/rose_qsig_diversion.c @@ -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-167 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 */ diff --git a/rose_qsig_mwi.c b/rose_qsig_mwi.c new file mode 100644 index 0000000..98280db --- /dev/null +++ b/rose_qsig_mwi.c @@ -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 */ diff --git a/rose_qsig_name.c b/rose_qsig_name.c new file mode 100644 index 0000000..195f625 --- /dev/null +++ b/rose_qsig_name.c @@ -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 */ diff --git a/rosetest.c b/rosetest.c new file mode 100644 index 0000000..dd41eb5 --- /dev/null +++ b/rosetest.c @@ -0,0 +1,1713 @@ +/* + * 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* */ + /* 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, + }, +/* *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 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 roseQsigMWIInterrogateRes) = %u\n", + (unsigned) sizeof(struct roseQsigMWIInterrogateRes)); + + pri_message(&dummy_ctrl, "\n"); + pri_message(&dummy_ctrl, "sizeof(struct roseEtsiAOCSCurrencyInfoList) = %u\n", + (unsigned) sizeof(struct roseEtsiAOCSCurrencyInfoList)); + +/* ------------------------------------------------------------------- */ + + return 0; +} + +/* ------------------------------------------------------------------- */ +/* end rosetest.c */