[libpri-commits] rmudgett: branch rmudgett/facility r740 - /team/rmudgett/facility/

SVN commits to the libpri project libpri-commits at lists.digium.com
Wed Apr 15 16:15:56 CDT 2009


Author: rmudgett
Date: Wed Apr 15 16:15:52 2009
New Revision: 740

URL: http://svn.digium.com/svn-view/libpri?view=rev&rev=740
Log:
ROSE ASN.1 facility encode and decode rewrite of existing messages.

Several components are now parsed correctly.  Most notably:
PartyNumber and Q.SIG Name.

Added:
    team/rmudgett/facility/asn1.h   (with props)
    team/rmudgett/facility/asn1_primitive.c   (with props)
    team/rmudgett/facility/rose.c   (with props)
    team/rmudgett/facility/rose.h   (with props)
    team/rmudgett/facility/rose_address.c   (with props)
    team/rmudgett/facility/rose_etsi_aoc.c   (with props)
    team/rmudgett/facility/rose_internal.h   (with props)
    team/rmudgett/facility/rose_other.c   (with props)
    team/rmudgett/facility/rose_q931.c   (with props)
    team/rmudgett/facility/rose_qsig_ct.c   (with props)
    team/rmudgett/facility/rose_qsig_diversion.c   (with props)
    team/rmudgett/facility/rose_qsig_mwi.c   (with props)
    team/rmudgett/facility/rose_qsig_name.c   (with props)
    team/rmudgett/facility/rosetest.c   (with props)
Modified:
    team/rmudgett/facility/Makefile
    team/rmudgett/facility/libpri.h
    team/rmudgett/facility/pri_facility.c
    team/rmudgett/facility/pri_facility.h
    team/rmudgett/facility/pri_internal.h
    team/rmudgett/facility/q931.c

Modified: team/rmudgett/facility/Makefile
URL: http://svn.digium.com/svn-view/libpri/team/rmudgett/facility/Makefile?view=diff&rev=740&r1=739&r2=740
==============================================================================
--- team/rmudgett/facility/Makefile (original)
+++ team/rmudgett/facility/Makefile Wed Apr 15 16:15:52 2009
@@ -41,8 +41,42 @@
 
 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 @@
 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

Added: team/rmudgett/facility/asn1.h
URL: http://svn.digium.com/svn-view/libpri/team/rmudgett/facility/asn1.h?view=auto&rev=740
==============================================================================
--- team/rmudgett/facility/asn1.h (added)
+++ team/rmudgett/facility/asn1.h Wed Apr 15 16:15:52 2009
@@ -1,0 +1,257 @@
+/*
+ * libpri: An implementation of Primary Rate ISDN
+ *
+ * Copyright (C) 2009 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ *
+ * In addition, when this program is distributed with Asterisk in
+ * any form that would qualify as a 'combined work' or as a
+ * 'derivative work' (but not mere aggregation), you can redistribute
+ * and/or modify the combination under the terms of the license
+ * provided with that copy of Asterisk, instead of the license
+ * terms granted here.
+ */
+
+/*!
+ * \file
+ * \brief 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 <rmudgett at digium.com>
+ */
+
+#ifndef _LIBPRI_ASN1_H
+#define _LIBPRI_ASN1_H
+
+#include <string.h>
+#include <sys/types.h>
+
+#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 */

Propchange: team/rmudgett/facility/asn1.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/rmudgett/facility/asn1.h
------------------------------------------------------------------------------
    svn:keywords = 'Author Date Id Revision'

Propchange: team/rmudgett/facility/asn1.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/rmudgett/facility/asn1_primitive.c
URL: http://svn.digium.com/svn-view/libpri/team/rmudgett/facility/asn1_primitive.c?view=auto&rev=740
==============================================================================
--- team/rmudgett/facility/asn1_primitive.c (added)
+++ team/rmudgett/facility/asn1_primitive.c Wed Apr 15 16:15:52 2009
@@ -1,0 +1,1281 @@
+/*
+ * libpri: An implementation of Primary Rate ISDN
+ *
+ * Copyright (C) 2009 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ *
+ * In addition, when this program is distributed with Asterisk in
+ * any form that would qualify as a 'combined work' or as a
+ * 'derivative work' (but not mere aggregation), you can redistribute
+ * and/or modify the combination under the terms of the license
+ * provided with that copy of Asterisk, instead of the license
+ * terms granted here.
+ */
+
+/*!
+ * \file
+ * \brief ASN.1 BER encode/decode primitives
+ *
+ * \author Richard Mudgett <rmudgett at digium.com>
+ */
+
+
+#include <stdio.h>
+
+#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");
+

[... 19189 lines stripped ...]



More information about the libpri-commits mailing list