Rong Tao August 23, 2021
The first part mainly talked about ASN 1 code< ASN.1 encoding, decoding and programming>
This paper mainly introduces ASN 1 coding C language implementation, asn1c. ASN.1 is very critical in the field of communication.
1. ASN.1 Foundation
If you want to transfer a standard data format between computers, which contains string tag, integer and floating point, this structure can use ASN 1, as follows:
CertainStructure ::= SEQUENCE { tag VisibleString, val1 INTEGER, val2 INTEGER OPTIONAL, reals SET OF REAL }
Compile with asn1c to generate C language code:
typedef struct CertainStructure { VisibleString_t tag; long val1; long *val2; /* OPTIONAL */ A_SET_OF(double) reals; } CertainStructure_t;
The above variables may be modified directly.
Importantly, the conversion between this complex data format and binary format is more complex. (snapshot snap)
The following shows the process of receiving, editing and converting the above data structure through the network:
CertainStructure_t *cs = 0; ber_decode(0, &asn_DEF_CertainStructure, &cs, buffer, buffer_length); cs->val1 = 123; /* Modify the contents */ der_encode(&asn_DEF_CertainStructure, cs, write_handle, 0);
write_handle means tips for the asn1c compiler.
The following can be directly converted to xml format:
xer_fprint(stdout, &asn_DEF_CertainStructure, cs);
The output result of the above code is:
<CertainStructure> <tag>This is a random tag</tag> <val1>123</val1> <reals> <REAL>3.14159265</REAL> <REAL><MINUS-INFINITY/></REAL> <REAL>2.7182818284</REAL> </reals> </CertainStructure>
ASN.1 is used in the communication industry. If SSL (HTTPS) is needed to access bank and mail accounts, ensure that ASN.1 is also involved.
2. ASN.1 compiler
asn1c is a free ASN 1 structure into C language format compiler. It supports ASN 1 syntax, including: ISO / IEC / ITU ASN 1 1988, '94,' 97, 2002 and later. The supported coding rules are:
- BER: ITU-T Rec. X.690 | ISO/IEC 8825-1 (2002) (BER/DER/CER)
- PER: X.691|8825-2 (2002) (PER).
- XER: X.693|8825-3 (2001) (BASIC-XER/CXER).
License:
/*- * Copyright (c) 2003-2013 Lev Walkin <vlm@lionet.info> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */
3. Documentation
http://lionet.info/asn1c/documentation.html
Documents can be downloaded on demand:
4. F&Q
http://lionet.info/asn1c/faq.html
5. Examples
This chapter gives a simple coding example from ASN 1 format to C language code, and then generate xml format.
5.1. Write ASN 1 document
Suppose we already have mymodule ASN1 file:
MyModule DEFINITIONS ::= BEGIN MyTypes ::= SEQUENCE { myObjectId OBJECT IDENTIFIER, mySeqOf SEQUENCE OF MyInt, myBitString BIT STRING { muxToken(0), modemToken(1) } } MyInt ::= INTEGER (0..65535) END
5.2. Compile with asn1c instruction
asn1c MyModule.asn1
Several files will be generated and some files will be copied at the same time
[rongtao@localhost demo]$ asn1c MyModule.asn1 Compiled MyTypes.c Compiled MyTypes.h Compiled MyInt.c Compiled MyInt.h Copied /usr/local/share/asn1c/INTEGER.h -> INTEGER.h Copied /usr/local/share/asn1c/NativeEnumerated.h -> NativeEnumerated.h Copied /usr/local/share/asn1c/INTEGER.c -> INTEGER.c Copied /usr/local/share/asn1c/NativeEnumerated.c -> NativeEnumerated.c Copied /usr/local/share/asn1c/NativeInteger.h -> NativeInteger.h Copied /usr/local/share/asn1c/NativeInteger.c -> NativeInteger.c Copied /usr/local/share/asn1c/OBJECT_IDENTIFIER.h -> OBJECT_IDENTIFIER.h Copied /usr/local/share/asn1c/OBJECT_IDENTIFIER.c -> OBJECT_IDENTIFIER.c Copied /usr/local/share/asn1c/asn_SEQUENCE_OF.h -> asn_SEQUENCE_OF.h Copied /usr/local/share/asn1c/asn_SEQUENCE_OF.c -> asn_SEQUENCE_OF.c Copied /usr/local/share/asn1c/asn_SET_OF.h -> asn_SET_OF.h Copied /usr/local/share/asn1c/asn_SET_OF.c -> asn_SET_OF.c Copied /usr/local/share/asn1c/constr_SEQUENCE.h -> constr_SEQUENCE.h Copied /usr/local/share/asn1c/constr_SEQUENCE.c -> constr_SEQUENCE.c Copied /usr/local/share/asn1c/constr_SEQUENCE_OF.h -> constr_SEQUENCE_OF.h Copied /usr/local/share/asn1c/constr_SEQUENCE_OF.c -> constr_SEQUENCE_OF.c Copied /usr/local/share/asn1c/constr_SET_OF.h -> constr_SET_OF.h Copied /usr/local/share/asn1c/constr_SET_OF.c -> constr_SET_OF.c Copied /usr/local/share/asn1c/asn_application.h -> asn_application.h Copied /usr/local/share/asn1c/asn_system.h -> asn_system.h Copied /usr/local/share/asn1c/asn_codecs.h -> asn_codecs.h Copied /usr/local/share/asn1c/asn_internal.h -> asn_internal.h Copied /usr/local/share/asn1c/OCTET_STRING.h -> OCTET_STRING.h Copied /usr/local/share/asn1c/OCTET_STRING.c -> OCTET_STRING.c Copied /usr/local/share/asn1c/BIT_STRING.h -> BIT_STRING.h Copied /usr/local/share/asn1c/BIT_STRING.c -> BIT_STRING.c Copied /usr/local/share/asn1c/asn_codecs_prim.c -> asn_codecs_prim.c Copied /usr/local/share/asn1c/asn_codecs_prim.h -> asn_codecs_prim.h Copied /usr/local/share/asn1c/ber_tlv_length.h -> ber_tlv_length.h Copied /usr/local/share/asn1c/ber_tlv_length.c -> ber_tlv_length.c Copied /usr/local/share/asn1c/ber_tlv_tag.h -> ber_tlv_tag.h Copied /usr/local/share/asn1c/ber_tlv_tag.c -> ber_tlv_tag.c Copied /usr/local/share/asn1c/ber_decoder.h -> ber_decoder.h Copied /usr/local/share/asn1c/ber_decoder.c -> ber_decoder.c Copied /usr/local/share/asn1c/der_encoder.h -> der_encoder.h Copied /usr/local/share/asn1c/der_encoder.c -> der_encoder.c Copied /usr/local/share/asn1c/constr_TYPE.h -> constr_TYPE.h Copied /usr/local/share/asn1c/constr_TYPE.c -> constr_TYPE.c Copied /usr/local/share/asn1c/constraints.h -> constraints.h Copied /usr/local/share/asn1c/constraints.c -> constraints.c Copied /usr/local/share/asn1c/xer_support.h -> xer_support.h Copied /usr/local/share/asn1c/xer_support.c -> xer_support.c Copied /usr/local/share/asn1c/xer_decoder.h -> xer_decoder.h Copied /usr/local/share/asn1c/xer_decoder.c -> xer_decoder.c Copied /usr/local/share/asn1c/xer_encoder.h -> xer_encoder.h Copied /usr/local/share/asn1c/xer_encoder.c -> xer_encoder.c Copied /usr/local/share/asn1c/per_support.h -> per_support.h Copied /usr/local/share/asn1c/per_support.c -> per_support.c Copied /usr/local/share/asn1c/per_decoder.h -> per_decoder.h Copied /usr/local/share/asn1c/per_decoder.c -> per_decoder.c Copied /usr/local/share/asn1c/per_encoder.h -> per_encoder.h Copied /usr/local/share/asn1c/per_encoder.c -> per_encoder.c Copied /usr/local/share/asn1c/per_opentype.h -> per_opentype.h Copied /usr/local/share/asn1c/per_opentype.c -> per_opentype.c Copied /usr/local/share/asn1c/converter-sample.c -> converter-sample.c Generated Makefile.am.sample
As can be seen from the above output results, some files are generated:
Compiled MyTypes.c Compiled MyTypes.h Compiled MyInt.c Compiled MyInt.h Generated Makefile.am.sample
Some files will also be copied:
Copied /usr/local/share/asn1c/per_encoder.c -> per_encoder.c
I don't know why asn1c makes such a copy file format. I think it can be made into asn1c devel and compiled into a dynamic library, which may not be so large for the amount of project code.
5.3. Analyze data structure
5.3.1. MyInt
Let's start with a simple:
MyInt ::= INTEGER (0..65535)
But let's look at Myint H header file, see the following data types:
/* MyInt */ typedef long MyInt_t;
In addition, there are some function pointers:
/* Implementation */ extern asn_TYPE_descriptor_t asn_DEF_MyInt; asn_struct_free_f MyInt_free; asn_struct_print_f MyInt_print; asn_constr_check_f MyInt_constraint; ber_type_decoder_f MyInt_decode_ber; der_type_encoder_f MyInt_encode_der; xer_type_decoder_f MyInt_decode_xer; xer_type_encoder_f MyInt_encode_xer;
Here, is it very similar to the use of net SNMP? I think it is similar.
Then, let's look at the c file, which implements the method of the above header file and intercepts part of it:
asn_enc_rval_t MyInt_encode_der(asn_TYPE_descriptor_t *td, void *structure, int tag_mode, ber_tlv_tag_t tag, asn_app_consume_bytes_f *cb, void *app_key) { MyInt_1_inherit_TYPE_descriptor(td); return td->der_encoder(td, structure, tag_mode, tag, cb, app_key); } asn_dec_rval_t MyInt_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void **structure, const char *opt_mname, const void *bufptr, size_t size) { MyInt_1_inherit_TYPE_descriptor(td); return td->xer_decoder(opt_codec_ctx, td, structure, opt_mname, bufptr, size); } asn_enc_rval_t MyInt_encode_xer(asn_TYPE_descriptor_t *td, void *structure, int ilevel, enum xer_encoder_flags_e flags, asn_app_consume_bytes_f *cb, void *app_key) { MyInt_1_inherit_TYPE_descriptor(td); return td->xer_encoder(td, structure, ilevel, flags, cb, app_key); }
A forward is still relatively simple. Next, the more complex is the data structure.
5.3.2. MyTypes
ASN for MyType 1 is defined as:
MyTypes ::= SEQUENCE { myObjectId OBJECT IDENTIFIER, mySeqOf SEQUENCE OF MyInt, myBitString BIT STRING { muxToken(0), modemToken(1) } }
Let's first look at the C language structure definition:
/* Dependencies */ typedef enum myBitString { myBitString_muxToken = 0, myBitString_modemToken = 1 } e_myBitString; /* MyTypes */ typedef struct MyTypes { OBJECT_IDENTIFIER_t myObjectId; struct mySeqOf { A_SEQUENCE_OF(MyInt_t) list; /* Context for parsing across buffer boundaries */ asn_struct_ctx_t _asn_ctx; } mySeqOf; BIT_STRING_t myBitString; /* Context for parsing across buffer boundaries */ asn_struct_ctx_t _asn_ctx; } MyTypes_t;
In the above data structure_ asn_ctx is used to manage, and we can't modify it explicitly during writing. Other labels, and ASN 1 format match.
5.4. Write asn program
#include <stdio.h> /* for stdout */ #include <stdlib.h> /* for malloc() */ #include <assert.h> /* for run-time control */ #include "MyTypes.h" /* Include MyTypes definition */ int main() { /* Define an OBJECT IDENTIFIER value */ int oid[] = { 1, 3, 6, 1, 4, 1, 9363, 1, 5, 0 }; /* or whatever */ /* Declare a pointer to a new instance of MyTypes type */ MyTypes_t *myType; /* Declare a pointer to a MyInt type */ MyInt_t *myInt; /* Temporary return value */ int ret; /* Allocate an instance of MyTypes */ myType = calloc(1, sizeof *myType); assert(myType); /* Assume infinite memory */ /* * Fill in myObjectId */ ret = OBJECT_IDENTIFIER_set_arcs(&myType->myObjectId, oid, sizeof(oid[0]), sizeof(oid) / sizeof(oid[0])); assert(ret == 0); /* * Fill in mySeqOf with a couple of integers. */ /* Prepare a certain INTEGER */ myInt = calloc(1, sizeof *myInt); assert(myInt); *myInt = 123; /* Set integer value */ /* Fill in mySeqOf with the prepared INTEGER */ ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt); assert(ret == 0); /* Prepare another integer */ myInt = calloc(1, sizeof *myInt); assert(myInt); *myInt = 111222333; /* Set integer value */ /* Append another INTEGER into mySeqOf */ ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt); assert(ret == 0); /* * Fill in myBitString */ /* Allocate some space for bitmask */ myType->myBitString.buf = calloc(1, 1); assert(myType->myBitString.buf); myType->myBitString.size = 1; /* 1 byte */ /* Set the value of muxToken */ myType->myBitString.buf[0] |= 1 << (7 - myBitString_muxToken); /* Also set the value of modemToken */ myType->myBitString.buf[0] |= 1 << (7 - myBitString_modemToken); /* Trim unused bits (optional) */ myType->myBitString.bits_unused = 6; /* * Print the resulting structure as XER (XML) */ xer_fprint(stdout, &asn_DEF_MyTypes, myType); return 0; }
Because the above compiled ASN 1 syntax, a makefile is generated am. Sample file. In fact, the main program copies the file converter sample C, I put the contents of the above file into the file converter sample C, and compile and execute:
[rongtao@localhost demo]$ make -f Makefile.am.sample [rongtao@localhost demo]$ ./progname <MyTypes> <myObjectId>1.3.6.1.4.1.9363.1.5.0</myObjectId> <mySeqOf> <MyInt>123</MyInt> <MyInt>111222333</MyInt> </mySeqOf> <myBitString> 11 </myBitString> </MyTypes>
5.5. Fetch data from xer file
I saved the above text to the file mytype In Xer:
[rongtao@localhost demo]$ cat MyType.xer <MyTypes> <myObjectId>1.3.6.1.4.1.9363.1.5.0</myObjectId> <mySeqOf> <MyInt>123</MyInt> <MyInt>111222333</MyInt> </mySeqOf> <myBitString> 11 </myBitString> </MyTypes>
Using the above compilation steps, run the following program, and the source code is as follows:
#include <stdio.h> /* for stdout */ #include <stdlib.h> /* for malloc() */ #include <assert.h> /* for run-time control */ #include "MyTypes.h" /* Include MyTypes definition */ int main(int argc, char *argv[]) { char buf[1024]; /* Hope, sufficiently large buffer */ MyTypes_t *myType = 0; asn_dec_rval_t rval; char *filename; size_t size; FILE *f; /* * Target variables. */ int *oid_array; /* holds myObjectId */ int oid_size; int *int_array; /* holds mySeqOf */ int int_size; int muxToken_set; /* holds single bit */ int modemToken_set; /* holds single bit */ /* * Read in the input file. */ assert(argc == 2); filename = argv[1]; f = fopen(filename, "r"); assert(f); size = fread(buf, 1, sizeof buf, f); if(size == 0 || size == sizeof buf) { fprintf(stderr, "%s: Too large input\n", filename); exit(1); } /* * Decode the XER buffer. */ rval = xer_decode(0, &asn_DEF_MyTypes, &myType, buf, size); assert(rval.code == RC_OK); /* * Convert the OBJECT IDENTIFIER into oid_array/oid_size pair. */ /* Figure out the number of arcs inside OBJECT IDENTIFIER */ oid_size = OBJECT_IDENTIFIER_get_arcs(&myType->myObjectId, 0, sizeof(oid_array[0]), 0); assert(oid_size >= 0); /* Create the array of arcs and fill it in */ oid_array = malloc(oid_size * sizeof(oid_array[0])); assert(oid_array); (void)OBJECT_IDENTIFIER_get_arcs(&myType->myObjectId, oid_array, sizeof(oid_array[0]), oid_size); /* * Convert the sequence of integers into array of integers. */ int_size = myType->mySeqOf.list.count; int_array = malloc(int_size * sizeof(int_array[0])); assert(int_array); for(int_size = 0; int_size < myType->mySeqOf.list.count; int_size++) int_array[int_size] = *myType->mySeqOf.list.array[int_size]; if(myType->myBitString.buf) { muxToken_set = myType->myBitString.buf[0] & (1 << (7 - myBitString_muxToken)); modemToken_set = myType->myBitString.buf[0] & (1 << (7 - myBitString_modemToken)); } else { muxToken_set = modemToken_set = 0; /* Nothing is set */ } /* * Print the resulting structure as XER (XML) */ xer_fprint(stdout, &asn_DEF_MyTypes, myType); return 0; }
The only difference from the original website is that I added the printed code at the end of the main program:
/* * Print the resulting structure as XER (XML) */ xer_fprint(stdout, &asn_DEF_MyTypes, myType);
We run the above procedure and get:
[rongtao@localhost demo]$ ./progname MyType.xer <MyTypes> <myObjectId>1.3.6.1.4.1.9363.1.5.0</myObjectId> <mySeqOf> <MyInt>123</MyInt> <MyInt>111222333</MyInt> </mySeqOf> <myBitString> 11 </myBitString> </MyTypes>