Compilar ASN.1 a C/C++

Aviso, Este es un articulo muy tecnico, completamente de programacion en C/C++. Si eres nuevo en Linux, seguramente no te interese, como siempre puedes leerlo / o no bajo tu propia responsabilidad. Si no te interesa, En este sitio se tocan muchos temas y seguramente encuentres algun otro que te pueda interesar mas.

En mi trabajo creamos aplicaciones para codificar y decodificar informacion mediante ASN.1, es una de las formas que empleamos para transmitir/recibir informacion entre host que son heterogeneos, por ejemplo para enviar informacion de un Linux a una estacion Sun Solaris, o a cualquier otra plataforma. En este pequeño articulo comento una breve introduccion a ASN.1 y como lo podemos usar en nuestras aplicaciones C/C++.

¿Que es ASN.1?

Como bien dice la wikipedia, ASN.1 (Abstract Syntax Notation One) es una notacion flexible y estandard que describe estructuras de datos para representarlas, codificarlas, transmitirlas y decodificarlas.

Veamos un ejemplo:

Podriamos tener la siguiente definicion de una estructura en ASN.1:

FooProtocol DEFINITIONS ::= BEGIN
FooQuestion ::= SEQUENCE
{
trackingNumber INTEGER,
question VisibleString
}
FooAnswer ::= SEQUENCE
{
questionNumber INTEGER,
answer BOOLEAN
}
END

Y de acuerdo a esta definicion podriamos querer transmitir la siguiente informacion:

myQuestion FooQuestion ::= {
trackingNumber 5,
question "Anybody there?"
}

Es decir, una estructura de tipo «FooQuestion» que tiene dos campos, uno llamado trackingNumber con valor igual a 5 y otro llamado question con valor «Anybody there?»

Esto realmente cuando lo codificamos no se envia como texto plano sino como una secuencia de Bytes:

30 13 02 01 05 1a 0e 41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f

Que significa lo siguiente:

30 -- es un tag que indica SEQUENCE
13 -- longitud en octetos
02 -- tag que indica que lo que sigue es un INTEGER
01 -- longitud en octetos (1 byte)
05 -- valor del dato
1a -- tag que indica que lo que sigue es un VisibleString
0e -- longitud en octetos (14 bytes)
41 6e 79 62 6f 64 79 20 74 68 65 72 65 3f -- el valor ("Anybody there?" codificado en ASCII)

Realmente y basicamente es una secuencia donde 1 byte indica lo que viene a continuacion, otro byte o bytes indica el tamaño de lo que sigue y a continuacion viene el dato. Todo ello codificado como ves en una secuencia de bytes.

Cuando al receptor le llegue ese chorizo de bytes, lo sabe decodificar y rellena la estructura correspondiente.

Pues bien, nuestras aplicaciones para trabajar con este formato codificado deben implementar las funciones que se encarguen de codificar y decodificar estas estructuras. Pero para ello contamos con snacc, que nos hace la vida mas facil y nos autogenera las cabeceras .h y librerias .c necesarias sin tener que escribir el codigo.

snacc es un compilador de sintaxis ASN.1 a C/C++.

Para instalarlo en Ubuntu ejecutamos:

$ sudo apt-get install snacc

Tras instalarlo. si lo ejecutamos sin mas, nos muestra la siguiente informacion:


jose@soledad:~/Desktop/snacc4J$ snacc
Usage: snacc [-h] [-P] [-t] [-v] [-e] [-d] [-p] [-f]
[-c | -C | -[T|O] <table output file> | -idl ]
[-u <useful types ASN.1 file>]
[-mm] [-mf <max file name length>]
[-l <neg number>]
<ASN.1 file list>
-h prints this msg
-c generate C encoders and decoders (default)
-C generate C++ encoders and decoders
-novolat for broken C++ compilers: return *this after calling abort()
-T <filename> write a type table file for the ASN.1 modules to file filename
-O <filename> writes the type table file in the original (<1.3b2) format
-idl generate CORBA IDL
-u <filename> specifies the ASN.1 file with definition of the useful types
(i.e. PrintableString). See the useful.asn1 file (in the
snacc/asn1specs/ directory).
-P print the parsed ASN.1 modules to stdout from their parse trees
(helpful debugging)
-t generate type definitions
-v generate value definitions (limited)
-e generate encode routines
-d generate decode routines
-p generate print routines
-f generate hierarchical free routines (C only)
note: if none of -t -v -e -d -p -f are given, all are generated.
These do not affect type tables.
-mm mangle output file name into module name (by default, the output file
inherits the input file's name, with only the suffix replaced)
-mf <num> num is maximum file name length for the generated source files
-l <neg num> where to start error longjmp values decending from (obscure).
Use `-' as the ASN.1 source file name to parse stdin.
This ASN.1 compiler produces C or C++ BER encoders and decoders or type tables.
Version 1.3, 1997-10-20.
Please see snacc@cs.ubc.ca for new versions and where to send bug reports and comments.
Copyright (C) 1993 Michael Sample and UBC
Copyright (C) 1994, 1995 by Robert Joop and GMD FOKUS
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
jose@soledad:~/Desktop/snacc4J$

Ahora bien, vamos a escribir un fichero de definicion ASN.1 como ejemplo y ver como generar la cabecera y fichero con el cuerpo que luego emplearemos en nuestros propios programas.

Por ejemplo escribi el siguiente fichero y llamalo «Persona.ASN1»


Ejemplo DEFINITIONS ::= BEGIN
Persona ::= SEQUENCE {
nombre [0] PrintableString (SIZE(1..30)),
edad [1] INTEGER,
sexo [2] Sexo OPTIONAL
}
Sexo ::= ENUMERATED {
desconocido (0),
masculino (1),
femenino (2)
}
END

Para compilarlo ejecutamos:

$ snacc -u /usr/include/snacc/asn1/asn-useful.asn1 Persona.ASN1

y automaticamente nos genera 2 ficheros:

/*
* Persona.h
*
* "Ejemplo" ASN.1 module C type definitions and prototypes
*
* This .h file was generated by snacc on Wed May 30 13:03:29 2007
*
* UBC snacc written compiler by Mike Sample
*
* NOTE: This is a machine generated file--editing not recommended
*/
#ifndef _Persona_h_
#define _Persona_h_
typedef enum
{
DESCONOCIDO = 0,
MASCULINO = 1,
FEMENINO = 2
} Sexo; /* ENUMERATED { DESCONOCIDO (0), MASCULINO (1), FEMENINO (2) } */
#define BEncSexoContent BEncAsnEnumContent
#define BDecSexoContent BDecAsnEnumContent
#define PrintSexo PrintAsnEnum
#define FreeSexo FreeAsnEnum
typedef struct Persona /* SEQUENCE */
{
PrintableString nombre; /* [0] PrintableString (SIZE (1..30)) */
AsnInt edad; /* [1] INTEGER */
Sexo* sexo; /* [2] Sexo OPTIONAL */
} Persona;
AsnLen BEncPersonaContent PROTO ((BUF_TYPE b, Persona *v));
void BDecPersonaContent PROTO ((BUF_TYPE b, AsnTag tagId0, AsnLen elmtLen0, Persona *v, AsnLen *bytesDecoded, ENV_TYPE env));
void PrintPersona PROTO ((FILE* f, Persona *v, unsigned short int indent));
void FreePersona PROTO ((Persona *v));
#endif /* conditional include of Persona.h */

y

/*
* Persona.c
*
* "Ejemplo" ASN.1 module encode/decode/print/free C src.
*
* This file was generated by snacc on Wed May 30 13:03:29 2007
*
* UBC snacc written by Mike Sample
*
* NOTE: This is a machine generated file - editing not recommended
*/
#include "asn-incl.h"
#include "Persona.h"
AsnLen
BEncPersonaContent PARAMS ((b, v),
BUF_TYPE b _AND_
Persona *v)
{
AsnLen totalLen = 0;
AsnLen itemLen;
AsnLen listLen;
void *component;
if (NOT_NULL ((v->sexo)))
{
BEncEocIfNec (b);
itemLen = BEncSexoContent (b, (v->sexo));
BEncDefLenTo127 (b, itemLen);
itemLen++;
itemLen += BEncTag1 (b, UNIV, PRIM, 10);
itemLen += BEncConsLen (b, itemLen);
itemLen += BEncTag1 (b, CNTX, CONS, 2);
totalLen += itemLen;
}
BEncEocIfNec (b);
itemLen = BEncAsnIntContent (b, (&v->edad));
BEncDefLenTo127 (b, itemLen);
itemLen++;
itemLen += BEncTag1 (b, UNIV, PRIM, 2);
itemLen += BEncConsLen (b, itemLen);
itemLen += BEncTag1 (b, CNTX, CONS, 1);
totalLen += itemLen;
BEncEocIfNec (b);
itemLen = BEncPrintableStringContent (b, (&v->nombre));
itemLen += BEncDefLen (b, itemLen);
itemLen += BEncTag1 (b, UNIV, PRIM, 19);
itemLen += BEncConsLen (b, itemLen);
itemLen += BEncTag1 (b, CNTX, CONS, 0);
totalLen += itemLen;
return totalLen;
} /* BEncPersonaContent */
void
BDecPersonaContent PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, env),
BUF_TYPE b _AND_
AsnTag tagId0 _AND_
AsnLen elmtLen0 _AND_
Persona *v _AND_
AsnLen *bytesDecoded _AND_
ENV_TYPE env)
{
int seqDone = FALSE;
AsnLen totalElmtsLen1 = 0;
AsnLen elmtLen1;
AsnTag tagId1;
int mandatoryElmtCount1 = 0;
AsnLen totalElmtsLen2 = 0;
AsnLen elmtLen2;
AsnTag tagId2;
tagId1 = BDecTag (b, &totalElmtsLen1, env);
if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
{
elmtLen1 = BDecLen (b, &totalElmtsLen1, env);
tagId2 = BDecTag (b, &totalElmtsLen1, env);
if ((tagId2 != MAKE_TAG_ID (UNIV, PRIM, PRINTABLESTRING_TAG_CODE)) &&
(tagId2 != MAKE_TAG_ID (UNIV, CONS, PRINTABLESTRING_TAG_CODE)))
{
Asn1Error ("Unexpected Tag\n");
longjmp (env, -100);
}
elmtLen2 = BDecLen (b, &totalElmtsLen1, env);
BDecPrintableStringContent (b, tagId2, elmtLen2, (&v->nombre), &totalElmtsLen1, env);
if (elmtLen1 == INDEFINITE_LEN)
BDecEoc (b, &totalElmtsLen1, env);
tagId1 = BDecTag (b, &totalElmtsLen1, env);
}
else
longjmp (env, -101);
if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 1))))
{
elmtLen1 = BDecLen (b, &totalElmtsLen1, env);
tagId2 = BDecTag (b, &totalElmtsLen1, env);
if (tagId2 != MAKE_TAG_ID (UNIV, PRIM, INTEGER_TAG_CODE))
{
Asn1Error ("Unexpected Tag\n");
longjmp (env, -102);
}
elmtLen2 = BDecLen (b, &totalElmtsLen1, env);
BDecAsnIntContent (b, tagId2, elmtLen2, (&v->edad), &totalElmtsLen1, env);
if (elmtLen1 == INDEFINITE_LEN)
BDecEoc (b, &totalElmtsLen1, env);
if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
seqDone = TRUE;
else
{
tagId1 = BDecTag (b, &totalElmtsLen1, env);
if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
{
BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1, env)
seqDone = TRUE;
}
}
}
else
longjmp (env, -103);
if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, CONS, 2))))
{
elmtLen1 = BDecLen (b, &totalElmtsLen1, env);
tagId2 = BDecTag (b, &totalElmtsLen1, env);
if (tagId2 != MAKE_TAG_ID (UNIV, PRIM, ENUM_TAG_CODE))
{
Asn1Error ("Unexpected Tag\n");
longjmp (env, -104);
}
elmtLen2 = BDecLen (b, &totalElmtsLen1, env);
(v->sexo) = (Sexo*) Asn1Alloc (sizeof (Sexo));
CheckAsn1Alloc ((v->sexo), env);
BDecSexoContent (b, tagId2, elmtLen2, (v->sexo), &totalElmtsLen1, env);
if (elmtLen1 == INDEFINITE_LEN)
BDecEoc (b, &totalElmtsLen1, env);
seqDone = TRUE;
if (elmtLen0 == INDEFINITE_LEN)
BDecEoc (b, &totalElmtsLen1, env);
else if (totalElmtsLen1 != elmtLen0)
longjmp (env, -105);
}
if (!seqDone)
longjmp (env, -106);
(*bytesDecoded) += totalElmtsLen1;
} /* BDecPersonaContent */
void
PrintPersona PARAMS ((f, v, indent),
FILE* f _AND_
Persona *v _AND_
unsigned short int indent)
{
if (v == NULL)
return;
fprintf (f,"{ -- SEQUENCE --\n");
Indent (f, indent + stdIndentG);
fprintf (f,"nombre ");
PrintPrintableString (f, (&v->nombre), indent + stdIndentG);
fprintf (f, ",\n");
Indent (f, indent + stdIndentG);
fprintf (f,"edad ");
PrintAsnInt (f, (&v->edad), indent + stdIndentG);
if (NOT_NULL ((v->sexo)))
{
fprintf (f,",\n");
Indent (f, indent + stdIndentG);
fprintf (f,"sexo ");
PrintSexo (f, (v->sexo), indent + stdIndentG);
}
fprintf (f,"\n");
Indent (f, indent);
fprintf (f,"}");
} /* PrintPersona */
void
FreePersona PARAMS ((v),
Persona *v)
{
if (v == NULL)
return;
FreePrintableString ((&v->nombre));
FreeAsnInt ((&v->edad));
if (NOT_NULL ((v->sexo)))
{
FreeSexo ((v->sexo));
Asn1Free ((v->sexo));
}
} /* FreePersona */

Que ya podremos emplear en nuestros programas para implementar las rutinas de codificacion y decodificacion.

El ejemplo para el fichero ASN.1 lo he sacado de la siguiente direccion.

13 Respuestas to “Compilar ASN.1 a C/C++”


  1. 1 cristian agosto 13, 2009 a las 1:47 pm

    una consulta…. esa codificacion del mensaje es propia de snacc o todos los mensajes asn1 independiente del parseador se codifican de esa forma?

    • 2 superpiwi agosto 13, 2009 a las 2:09 pm

      Bueno, yo realmente estoy empleando mas la version java de Snacc (Snacc4J) pero la codificacion y decodificacion es unica. ASN1 es una especificacion, asi si por ejemplo me pasan una secuencia de bytes codificada en ASN1, su decodificacion es unica independientemente del parser que se haya usado para generarla. No se si te referias a eso.

      • 3 WUILO abril 22, 2010 a las 1:22 am

        Amigo ayudamE como utilizar SNACC4J para java CON UN EJEMPLO . Como se genera las clases para enviar al ANS1

  2. 4 cristian agosto 13, 2009 a las 2:24 pm

    si, lo que pasa es que se me encomendo crear una aplicacion que envie un mensaje codificado en asn1 pero no se como funciona la maquina que lo recibe. entonces si entendi bien, solo tengo k crear una estructura para mis datos (estoy usando una libreria de boost.asn1) codificarla y enviarla. si?

    • 5 superpiwi agosto 13, 2009 a las 2:45 pm

      En teoria si.
      Bastaria con abrir un socket al puerto de la maquina que esta a la escucha, enviar el contenido y cerrar la conexion.
      Me imagino que alla, esa aplicacion estara esperando a recibir el buffer codificado y lo decodificara.
      Eso deberia funcionar a menos que se metan mas bytes adicionales a modo de cabecera, por ejemplo. Pero si quieres asegurarte que lo que codificas esta bien, siempre puedes usar un editor hexadecimal, volcar lo que codificas a un fichero y luego decodificar ese fichero con alguna utilidad como «dumpasn1»

      $ sudo apt-get install dumpasn1

      Para esto, ojo el fichero debe ser binario. P.ej 30 81 03 40 … con el contenido de la codificacion ASN1. lo llamas p.ej «volcado.bin» y ya puedes decodificarlo con dumpasn1 de la forma:

      $ dumpasn1 volcado.bin

      Hay tambien aplicaciones graficas como asn1 viewer:

      http://www.obj-sys.com/products_asn1ve.php

      que puedes ejecutar en version java o con wine.

      (Yo prefiero asn1dump ^^)

  3. 7 cristian agosto 13, 2009 a las 4:41 pm

    segun el ejemplo que pusiste arriba, podrias mostrar el codigo que usa el codigo generado para crear y enviar el mensaje… please. en otras palabras el main. y asi comparar el archivo codificado con el resultado de tu ejemplo:

    myQuestion FooQuestion ::= {
    trackingNumber 5,
    question «Anybody there?»
    }

    gracias.

    • 8 superpiwi agosto 14, 2009 a las 9:02 am

      Pues ando algo liado y tendria que mirarlo el finde.
      Si te da igual en Java si que lo tengo mas a mano.
      De todas formas instalate estos paquetes:

      jose@soledad:/$apt-cache search snacc
      libsnacc-dev – ASN.1 to C or C++ or IDL compiler, development files
      libsnacc0c2 – ASN.1 to C or C++ or IDL compiler, shared libraries
      snacc – ASN.1 to C or C++ or IDL compiler
      snacc-doc – ASN.1 to C or C++ or IDL compiler, documentation

      Asi tendras documentacion y ejemplos en el directorio:

      /usr/share/doc/libsnacc-dev/examples

      • 9 Cristian agosto 17, 2009 a las 4:06 pm

        uhm.. mira no estoy trabajando en linux aun. estoy trabajando en windows con visual c++. y estoy aproblemado por lo siguiente:

        actualmente estoy trabajando con la libreria asn1 de boost para c++ donde tengo 2 problemas: al hacer la estructura en C equivalente a ASN1 no se como asignarle un tag id a un elemento de mi estructura. Ponte tu este caso:

        Datos ::= SEQUENCE
        {
        nombre [1] IMPLICIT PrintableString(Size(15)),
        apellido [2] IMPLICIT PrintableString (SIZE(15))
        }

        En C tengo lo siguiente:

        struct datos
        {
        printablestring_t nombre;
        printablestring_t apellido;
        };
        typedef sequence Datos;

        Entonces el tag [1] y [2] no se como asignarselos a nombre y apellido. Lo otro es que no entiendo completamente el IMPLICIT y que diferencia tiene con el EXPLICIT que tengo entendido es el valor por defecto. Y me imagino que el tamaño de los valores lo controlo simplemente mas adelante en el codigo.

        Como no podia avanzar empece a mirar parseadores de asn1 a c++, pero una vez que tengo los archivos generados no se como ocuparlos… cuack! Tengo 1 ejemplo pero no me compila.

        De preferencia me gustaria seguir con la libreria boost, pues creo que es mas facil de comprender y agregar al proyecto actual.

        Necesito tu ayuda…. soy muuuy nuevo en esto de asn1.

  4. 10 wuilo abril 22, 2010 a las 8:40 am

    POR FAVOR ALGUIEN QUIEN ME AYUDA COMO USAR EL SNACC4J EN WINDOW XP

  5. 11 wuilo abril 22, 2010 a las 8:41 am

    COMO INSTALARLO Y COMO GENERAR LAS CLASES JAVA PARA LA INGTERFACE

  6. 12 morning workout motivation diciembre 27, 2017 a las 9:28 am

    Quality posts is the secret to invite the people to pay a visit the web site, that’s what this web site is providing.


  1. 1 Compilar ASN.1 con Snacc4J « Ubuntu Life Trackback en abril 22, 2010 a las 6:58 pm

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s







¿Es compatible tu equipo con Ubuntu?


( Muchos fondos de pantalla, aqui )

DESCARGATE SCIFI LIFE

365 Dias de Soledad
Me debes los sueños, las promesas y las noches rotas. Me debes la paz, la sonrisa y la esperanza robadas. Me debes la sangre, las lágrimas y el sudor vertido. Me debes las noches vacías, los abrazos anhelados. Me debes un beso de ajenjo de tu amarga boca.

The Ubuntu Counter Project - user number # 11961
Geo Visitors Map

Archivos

mayo 2007
L M X J V S D
 123456
78910111213
14151617181920
21222324252627
28293031  

Blog Stats

  • 31.310.804 hits

A %d blogueros les gusta esto: