ASN.1 c/cpp code
What is ASN.1
Abstract Syntax Notation One 이란 뜻.
ITU에서 네트웍 데이터 교환을 정의한 프로토콜로 사용하는 표준화 포맷임. (X.680, X.681, X.682, X.683, X.690, X.691, X.692, X.693 등에 설명)
이 기종 장치들 간의 데이터 교환시 표준화가 필요하다. (byte ordering 등 여러가지 문제)
여러가지 ISO 표준 문서에서 사용하는 자료 구조 형태를 보면 ASN.1 문법으로 사용한다.
ASN.1 에서는 자료 구조들을 정의하는데, 길이라던지 범위라던지 등의 제약 조건 등도 포함된다. 자료 구조를 정의하는 문법의 문서 형태라서 소프트웨어 구현시에는 자체로 사용할 수 없고, ASN.1을 인코딩하여 사용한다. 이런 인코딩 들을 하기 위한 작업을 개발 언어별로 소스를 자동으로 만들어 주는 역할을 하는 것이 ASN.1 컴파일러 이다.
BER는 ASN.1의 기본 인코딩 규칙을 나타낸다. ASN.1 데이터를 인코딩하는 다른 방법으로는 DER, CER, PER 등이 있다.
ASN.1 compiler는 ASN.1 문법으로 작성된 문서를 읽어 원하는 언어의 소스 파일로 만들어 주는 역할을 한다. (asn1c는 c 소스 코드를 생성한다.)
compiler download
Download asn1c(Compiler) source code and build.
버전에 따라 생성되는 코드가 다를 수 있다. 가장 최신 버전을 사용하는 것이 좋다. 상용으로 컴파일러를 파는 곳도 많다.
# wget https://lionet.info/soft/asn1c-0.9.28.tar.gz
# tar xvfz asn1c-0.9.28.tar.gz
# cd asn1c-0.9.28
./configure
./make
./make install
# asn1c -h
ASN.1 Compiler, v0.9.28
# Installation path may be /usr/local/bin.
Syntax
문법은 ASN.1 Syntax 검색하여 자세한 것은 찾아보고, 여기서는 간단한 예제 맛 보기.
Order ::= SEQUENCE {
header Order-header,
items SEQUENCE OF Order-line
}
Order-header ::= SEQUENCE {
number Order-number,
date Date,
client Client,
payment Payment-method
}
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
}
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
...
}
Test ASN1c
asn 파일을 만들었으면 asn1c로 c코드를 생성한다.
Makefile을 만들어 사용하기.
--build.sh
make -f Makefile.asn
--Makefile.asn
CC=cc
CPP=g++
.SUFFIXES=.c .o
.SUFFIXES=.cpp .o
all:
asn1c format_iso.asn
-rm converter-sample.c converter-sample.o
$(CC) -c -fPIC -g *.c -I.
$(CPP) -c -fPIC -g *.cpp -I.
--Makefile.api
...
all:
$(CC) -c -fPIC *.c -I.
$(CPP) -c -fPIC asnapi.cpp -I . -D_LITTLE_ENDIAN_
test:
$(CPP) -c -fPIC asnapitest.cpp -I.
$(CPP) -o a.out *.o
위에서 converter-sample.c를 참고하여 인코딩을 하는 것을 쉽게 따라해 볼 수 있다.
실제 사용하지는 않을 테니까 rm으로 빌드시 삭제하였다.
ASN api wrapper
ASN1 문서가 asn1c에 의해 컴파일되면 소스 코드들이 잔뜩 생긴다. 정의한 자료구조마다 헤더파일과 소스 파일이 각각 생성되어 생성된 소스 파일들이 너무 많다.
자료구조들을 사용하기 위해서는 wrapper하여 변환하는 함수를 만드는 것이 좋다.
ASN1 문서에 자료 구조 정의한 이름으로 소스 파일들이 생성되니, 최상위에 정의한 자료구조 명으로된 헤더 파일을 include하여 작성한다. 타입은 자료구조명에 _t가 붙어서 자동으로 생성된다.
wrapper example)
-- asnapi.h
#include "myData.h"
...
myData_t* mynotation2iso(string mynote) ;
myData_t* xer2iso(string mynote) ;
string iso2xer(myData_t *mydata) ;
myData_t* bin2iso(char *bin) ;
char* iso2bin(myData_t *mydata, OUT int *retbufsize) ;
...
--asnapi.cpp
위 wrapper 함수들을 구현
ASN 자료구조 생성
mynotation2iso():
mydata_t *mydata ;
mydata = (mydata_t*) calloc(1, sizeof *mydata) ;
mydata->bodies.list.free = free_body ;
OCTET_STRING_fromBuf(&mydata->header.formatId, "abc", 4) ;
mydata->header.number = 1 ;
Body_t* body=NULL;
body = (Body_t*) calloc(1, sizeof *body) ;
body->reprenentation.qualityRecord.qualityBlock.list.free=free_qualityBlock ;
OCTET_STRING_fromBuf( &body->representation.captureDateTime,
(const char*)&utc, 9) ;
QualityBlock_t *qb=NULL ;
qb = (QualityBlock_t*) calloc(1, sizeof *qb) ;
..
asn_sequence_add(&body->representation.qualityRecord.qualityBlocks, qb) ;
...
int zero=0 ;
body->representationBody.extendedData =
OCTET_STRING_new_fromBuf(&asn_DEF_OCTET_STRING, (const char*)&zero, 2) ;
asn_sequence_add(&mydata->bodies, body) ;
xer_fprintf(stdout, &asn_DEF_mydata, mydata) ;
return mydata ;
free_body(Body_t *b):
free_scd(b->representation.channelDescription.descriptions.x) ;
asn_sequence_empty(&b->representation.qualityRecord.qualityBlocks)
OCTET_STRING_free(&asn_DEF_OCTET_STRINTG, b->representationBody.extendedData,0) ;
OCTET_STRING_free(&asn_DEF_OCTET_STRINTG, &b->representation.captureDateTime,1) ;
free(b);
set_nodefree(mydata_t *mydata):
mydata->bodies.list.free=free_body ;
asn_anonymous_sequence_ *ss = _A_SEQUENCE_FROM_VOID( &mydata->bodies) ;
for (int j=0; j<ss->count; j++) {
Body_t *body = (Body_t*)ss->array[j] ;
body->representation.qualityRecord.qualityBlocks.list.free =
free_qualityBlock ;
}
위의 예제를 보면 복잡해 보인다. 실제 생성된 소스코드들을 분석해야지만 사용할 수 가 있다. 특히 리스트 사용이나 스트링 처리, 메모리 관리가 처음에 어려울 수 있다. 위의 예제를 참고하면 스트링이나 리스트 사용시 도움이 될 것이다. (주의: asn1c는 버전에 따라 Integer_t에 int 값 할당하는 것도 에러가 날 수 있다.)
Example
original page http://lionet.info/asn1c/examples.html
MyModule DEFINITIONS ::=
BEGIN
MyTypes ::= SEQUENCE {
myObjectId OBJECT IDENTIFIER,
mySeqOf SEQUENCE OF MyInt,
myBitString BIT STRING {
muxToken(0),
modemToken(1)
}
}
MyInt ::= INTEGER (0..65535)
END
-
asn1c MyModule.asn1
-
create structure files.
-
my-program.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "MyTypes.h"
int main() {
int oid[] = { 1, 3, 6, 1, 4, 1, 9363, 1, 5, 0 };
MyTypes_t *myType;
MyInt_t *myInt;
int ret;
myType = calloc(1, sizeof *myType);
assert(myType);
ret = OBJECT_IDENTIFIER_set_arcs(&myType->myObjectId,
oid, sizeof(oid[0]), sizeof(oid) / sizeof(oid[0]));
assert(ret == 0);
myInt = calloc(1, sizeof *myInt);
assert(myInt);
*myInt = 123;
ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt);
assert(ret == 0);
myInt = calloc(1, sizeof *myInt);
assert(myInt);
*myInt = 111222333;
ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt);
assert(ret == 0);
myType->myBitString.buf = calloc(1, 1);
assert(myType->myBitString.buf);
myType->myBitString.size = 1;
myType->myBitString.buf[0] |= 1 << (7 - myBitString_muxToken);
myType->myBitString.buf[0] |= 1 << (7 - myBitString_modemToken);
myType->myBitString.bits_unused = 6;
xer_fprint(stdout, &asn_DEF_MyTypes, myType);
return 0;
}
- $ cc -o a.out -I. *.c
- ./a.out
<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>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "MyTypes.h"
int main(int argc, char *argv[]) {
char buf[1024];
MyTypes_t *myType = 0;
asn_dec_rval_t rval;
char *filename;
size_t size;
FILE *f;
int *oid_array;
int oid_size;
int *int_array;
int int_size;
int muxToken_set;
int modemToken_set;
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);
}
rval = xer_decode(0, &asn_DEF_MyTypes, &myType, buf, size);
assert(rval.code == RC_OK);
oid_size = OBJECT_IDENTIFIER_get_arcs(&myType->myObjectId,
0, sizeof(oid_array[0]), 0);
assert(oid_size >= 0);
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);
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;
}
return 0;
}
Authors
crazyj7@gmail.com
Written with StackEdit.