Skip to content

Commit 2f90a4f

Browse files
committed
BNER support: Add bner tag {en,de}coding
1 parent 6ddf993 commit 2f90a4f

File tree

3 files changed

+467
-0
lines changed

3 files changed

+467
-0
lines changed

skeletons/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ check_PROGRAMS = \
107107

108108
# BNER Support
109109
libasn1cskeletons_la_SOURCES += \
110+
bner_support.c bner_support.h \
110111
constr_CHOICE_bner.c \
111112
constr_SEQUENCE_bner.c \
112113
constr_SEQUENCE_OF_bner.c \

skeletons/bner_support.c

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
/*
2+
* Copyright (c) 2017 Jon Ringle <[email protected]>. All rights reserved.
3+
* Redistribution and modifications are permitted subject to BSD license.
4+
*/
5+
#include <asn_internal.h>
6+
#include <bner_support.h>
7+
#include <errno.h>
8+
#include <regex.h>
9+
10+
static int is_bner_pdu_regex_init = 0;
11+
static regex_t bner_pdu_regex;
12+
13+
/*
14+
* BACnet defines two different encodings:
15+
* 1) Fixed encoding (Clause 20.1)
16+
* The fixed encoding is used on the following PDUs:
17+
* BACnetPDU
18+
* BACnet-Confirmed-Request-PDU
19+
* BACnet-Unconfirmed-Request-PDU
20+
* BACnet-SimpleACK-PDU
21+
* BACnet-ComplexACK-PDU
22+
* BACnet-SegmentACK-PDU
23+
* BACnet-Error-PDU
24+
* BACnet-Reject-PDU
25+
* BACnet-Abort-PDU
26+
* These PDUs can be matched with the regular expression: "BACnet.*PDU"
27+
* The fixed encoding is outside the scope of the asn1 compiler, and
28+
* only a weak function that fails encoding/decoding these PDUs is provided here
29+
*
30+
* 2) Variable encoding (Clause 20.2)
31+
* All other BACnet rules are encoded with the BNER variable encoding.
32+
* This encoding is provided for in the asn1 compiler
33+
*/
34+
35+
int
36+
init_bner(void) {
37+
int ret = 0;
38+
39+
if(!is_bner_pdu_regex_init) {
40+
ret = regcomp(&bner_pdu_regex, "BACnet.*PDU", 0);
41+
if(ret == 0) is_bner_pdu_regex_init = 1;
42+
}
43+
44+
return ret;
45+
}
46+
47+
void
48+
fini_bner(void) {
49+
if(is_bner_pdu_regex_init) {
50+
regfree(&bner_pdu_regex);
51+
}
52+
is_bner_pdu_regex_init = 0;
53+
}
54+
55+
int
56+
is_bner_fixed_pdu(const char *pdu_type_name) {
57+
init_bner();
58+
return (regexec(&bner_pdu_regex, pdu_type_name, 0, NULL, 0) == 0);
59+
}
60+
61+
62+
ssize_t
63+
bner_tag_lvt_snprint(const bner_tag_lvt_t *tag_lvt, char *buf, size_t size) {
64+
char lvt_info[20];
65+
int ret;
66+
67+
switch(tag_lvt->lvt_type) {
68+
case BNER_LVT_LENGTH:
69+
snprintf(lvt_info, sizeof(lvt_info), "Length:%d", tag_lvt->u.length);
70+
break;
71+
case BNER_LVT_VALUE:
72+
assert(BER_TAG_CLASS(tag_lvt->tag) == ASN_TAG_CLASS_APPLICATION);
73+
assert(BER_TAG_VALUE(tag_lvt->tag) == BNER_APPLICATION_TAG_BOOLEAN);
74+
assert(tag_lvt->u.value == BNER_FALSE || tag_lvt->u.value == BNER_TRUE);
75+
snprintf(lvt_info, sizeof(lvt_info), "Value:%s",
76+
(tag_lvt->u.value == BNER_FALSE) ? "false" : "true");
77+
break;
78+
case BNER_LVT_TYPE:
79+
assert(BER_TAG_CLASS(tag_lvt->tag) == ASN_TAG_CLASS_CONTEXT);
80+
assert(tag_lvt->u.type == BNER_OPENING_TAG
81+
|| tag_lvt->u.type == BNER_CLOSING_TAG);
82+
snprintf(lvt_info, sizeof(lvt_info), "Tag:%s",
83+
(tag_lvt->u.type == BNER_OPENING_TAG) ? "opening" : "closing");
84+
}
85+
86+
ret = snprintf(buf, size, "[%s%u] %s",
87+
(BER_TAG_CLASS(tag_lvt->tag) == ASN_TAG_CLASS_APPLICATION)
88+
? "APPLICATION "
89+
: "",
90+
BER_TAG_VALUE(tag_lvt->tag), lvt_info);
91+
if(ret <= 0 && size) buf[0] = '\0'; /* against broken libc's */
92+
93+
return ret;
94+
}
95+
96+
ssize_t
97+
bner_tag_lvt_fwrite(const bner_tag_lvt_t *tag, FILE *f) {
98+
char buf[sizeof("[APPLICATION ] ") + 32];
99+
ssize_t ret = bner_tag_lvt_snprint(tag, buf, sizeof(buf));
100+
if(ret >= (ssize_t)sizeof(buf) || ret < 2) {
101+
errno = EPERM;
102+
return -1;
103+
}
104+
105+
return fwrite(buf, 1, ret, f);
106+
}
107+
108+
char *
109+
bner_tag_lvt_string(const bner_tag_lvt_t *tag) {
110+
static char buf[sizeof("[APPLICATION ] ") + 32];
111+
112+
(void)bner_tag_lvt_snprint(tag, buf, sizeof(buf));
113+
114+
return buf;
115+
}
116+
117+
ssize_t
118+
bner_fetch_tag_lvt(const void *bufp, size_t size, bner_tag_lvt_t *tag_lvt_r) {
119+
const uint8_t *buf = (const uint8_t *)bufp;
120+
ber_tlv_tag_t tclass;
121+
uint8_t lvt = buf[0] & 0x7;
122+
size_t skipped = 1;
123+
124+
if(size == 0) return 0;
125+
126+
/* 20.2.1.1 */
127+
tclass = ((buf[0] >> 3) & 0x1) + 1;
128+
129+
/* 20.2.1.2 */
130+
if((buf[0] & 0xF0) != 0xF0)
131+
tag_lvt_r->tag = (((buf[0] >> 4) & 0xF) << 2) | tclass;
132+
else {
133+
if(size < 2) return 0;
134+
135+
if(buf[1] == 0xff) /* Reserved by ASHRAE for future use. */
136+
return -1;
137+
138+
tag_lvt_r->tag = (buf[1] << 2) | tclass;
139+
skipped++;
140+
}
141+
142+
/* 20.2.1.3 */
143+
if(BER_TAG_CLASS(tag_lvt_r->tag) == ASN_TAG_CLASS_APPLICATION
144+
&& BER_TAG_VALUE(tag_lvt_r->tag) == BNER_APPLICATION_TAG_BOOLEAN) {
145+
tag_lvt_r->lvt_type = BNER_LVT_VALUE;
146+
147+
/* 20.2.1.3.1 */
148+
if((lvt != BNER_FALSE) && (lvt != BNER_TRUE)) /* Invalid value */
149+
return -1;
150+
151+
tag_lvt_r->u.value = lvt;
152+
153+
} else if((lvt == BNER_OPENING_TAG) || (lvt == BNER_CLOSING_TAG)) {
154+
/* 20.2.1.3.2 */
155+
tag_lvt_r->lvt_type = BNER_LVT_TYPE;
156+
tag_lvt_r->u.type = lvt;
157+
158+
} else {
159+
/* 20.2.1.3.1 */
160+
tag_lvt_r->lvt_type = BNER_LVT_LENGTH;
161+
if(lvt < 5)
162+
tag_lvt_r->u.length = lvt;
163+
else if(lvt == 5) {
164+
if(buf[skipped] == 254) {
165+
if(size < skipped + 2) return 0;
166+
167+
tag_lvt_r->u.length =
168+
(buf[skipped + 1] << 8) + buf[skipped + 2];
169+
skipped += 2;
170+
171+
} else if(buf[skipped] == 255) {
172+
if(size < skipped + 4) return 0;
173+
174+
tag_lvt_r->u.length =
175+
(buf[skipped + 1] << 24) + (buf[skipped + 2] << 16)
176+
+ (buf[skipped + 3] << 8) + buf[skipped + 4];
177+
skipped += 4;
178+
179+
} else
180+
tag_lvt_r->u.length = buf[skipped];
181+
182+
skipped++;
183+
}
184+
}
185+
186+
return skipped;
187+
}
188+
189+
size_t
190+
bner_tag_lvt_serialize(bner_tag_lvt_t tag_lvt, void *bufp, size_t size) {
191+
uint8_t *buf = (uint8_t *)bufp;
192+
size_t required_size = 1;
193+
194+
if(BER_TAG_VALUE(tag_lvt.tag) < 15) {
195+
/* Encoded tag in 1st octet */
196+
if(size) buf[0] = (BER_TAG_VALUE(tag_lvt.tag) & 0xf) << 4;
197+
} else {
198+
if(size > 1) {
199+
buf[0] = 0xf0;
200+
buf[1] = BER_TAG_VALUE(tag_lvt.tag);
201+
}
202+
required_size++;
203+
}
204+
205+
if(size) buf[0] |= (BER_TAG_CLASS(tag_lvt.tag) - 1) << 3;
206+
207+
switch(tag_lvt.lvt_type) {
208+
case BNER_LVT_LENGTH:
209+
if(tag_lvt.u.length < 5) {
210+
/* 0 to 4 */
211+
if(size) buf[0] |= (tag_lvt.u.length & 0x7);
212+
} else if(tag_lvt.u.length < 254) {
213+
/* 5 to 253 */
214+
required_size++;
215+
if(size >= required_size) {
216+
buf[0] |= 5;
217+
buf[required_size - 1] = (uint8_t)tag_lvt.u.length;
218+
}
219+
} else if(tag_lvt.u.length < 65536) {
220+
/* 254 to 65535 */
221+
required_size++;
222+
if(size >= required_size + 2) {
223+
buf[0] |= 5;
224+
buf[required_size - 1] = 254;
225+
buf[required_size + 0] =
226+
(uint8_t)(tag_lvt.u.length >> 8) & 0xff;
227+
buf[required_size + 1] = (uint8_t)tag_lvt.u.length & 0xff;
228+
}
229+
required_size += 2;
230+
} else {
231+
/* 65536 to 2^32-1 */
232+
required_size++;
233+
if(size >= required_size + 4) {
234+
buf[0] |= 5;
235+
buf[required_size - 1] = 255;
236+
buf[required_size + 0] =
237+
(uint8_t)(tag_lvt.u.length >> 24) & 0xff;
238+
buf[required_size + 1] =
239+
(uint8_t)(tag_lvt.u.length >> 16) & 0xff;
240+
buf[required_size + 2] =
241+
(uint8_t)(tag_lvt.u.length >> 8) & 0xff;
242+
buf[required_size + 3] = (uint8_t)tag_lvt.u.length & 0xff;
243+
}
244+
required_size += 4;
245+
}
246+
break;
247+
case BNER_LVT_VALUE:
248+
if(size) buf[0] |= tag_lvt.u.value;
249+
break;
250+
case BNER_LVT_TYPE:
251+
if(size) buf[0] |= tag_lvt.u.type;
252+
break;
253+
}
254+
255+
return required_size;
256+
}
257+
258+
ber_tlv_tag_t
259+
convert_ber_to_bner_tag(ber_tlv_tag_t ber_tag) {
260+
/* The asn1c compiler will produce UNIVERSAL tags for primative types
261+
* that need to be translated into APPLICATION tags for BACnet encoding
262+
* rules
263+
*/
264+
265+
/*
266+
* -- ***************** Application Types ********************
267+
* -- The following productions are the definitions of the Application
268+
* datatypes.
269+
* -- See Clause 20.2.1.4.
270+
* -- NULL [APPLICATION 0], equivalent to [UNIVERSAL 5]
271+
* -- BOOLEAN [APPLICATION 1], equivalent to [UNIVERSAL 1]
272+
* Unsigned ::= [APPLICATION 2] INTEGER (0..MAX)
273+
* Unsigned8 ::= Unsigned (0..255)
274+
* Unsigned16 ::= Unsigned (0..65535)
275+
* Unsigned32 ::= Unsigned (0..4294967295)
276+
* -- INTEGER [APPLICATION 3], equivalent to [UNIVERSAL 2]
277+
* INTEGER16 ::= INTEGER (-32768..32767)
278+
* -- REAL [APPLICATION 4], equivalent to [UNIVERSAL 9]
279+
* ANSI/IEEE-754 single precision floating point
280+
* Double ::= [APPLICATION 5] OCTET STRING (SIZE(8)) -- ANSI/IEEE-754
281+
* double precision floating point
282+
* -- OCTET STRING [APPLICATION 6], equivalent to [UNIVERSAL 4]
283+
* CharacterString ::= [APPLICATION 7] OCTET STRING -- see Clause 20.2.9
284+
* for supported types
285+
* -- BIT STRING [APPLICATION 8], equivalent to [UNIVERSAL 3]
286+
* -- ENUMERATED [APPLICATION 9], equivalent to [UNIVERSAL 10]
287+
* Date ::= [APPLICATION 10] OCTET STRING (SIZE(4)) -- see Clause
288+
* 20.2.12
289+
* -- first octet year minus 1900, X'FF' = unspecified
290+
* -- second octet month (1.. 14), 1 = January
291+
* -- 13 = odd months
292+
* -- 14 = even months
293+
* -- X'FF' = unspecified
294+
* -- third octet day of month (1..34), 32 = last day of month
295+
* -- 33 = odd days of month
296+
* -- 34 = even days of month
297+
* -- X'FF' = unspecified
298+
* -- fourth octet day of week (1..7) 1 = Monday
299+
* -- 7 = Sunday
300+
* -- X'FF' = unspecified
301+
* Time ::= [APPLICATION 11] OCTET STRING (SIZE(4)) -- see Clause
302+
* 20.2.13
303+
* -- first octet hour (0..23), X'FF' = unspecified
304+
* -- second octet minute (0..59), X'FF' = unspecified
305+
* -- third octet second (0..59), X'FF' = unspecified
306+
* -- fourth octet hundredths (0..99) X'FF' = unspecified
307+
* BACnetObjectIdentifier ::= [APPLICATION 12] OCTET STRING (SIZE(4)) -- see
308+
* Clause 20.2.14
309+
*/
310+
311+
ber_tlv_tag_t bner_tag;
312+
313+
switch(BER_TAG_CLASS(ber_tag)) {
314+
case ASN_TAG_CLASS_UNIVERSAL:
315+
switch(BER_TAG_VALUE(ber_tag)) {
316+
case 5:
317+
bner_tag =
318+
(BNER_APPLICATION_TAG_NULL << 2) | ASN_TAG_CLASS_APPLICATION;
319+
break;
320+
case 1:
321+
bner_tag =
322+
(BNER_APPLICATION_TAG_BOOLEAN << 2) | ASN_TAG_CLASS_APPLICATION;
323+
break;
324+
case 2:
325+
bner_tag =
326+
(BNER_APPLICATION_TAG_SIGNED << 2) | ASN_TAG_CLASS_APPLICATION;
327+
break;
328+
case 9:
329+
bner_tag =
330+
(BNER_APPLICATION_TAG_REAL << 2) | ASN_TAG_CLASS_APPLICATION;
331+
break;
332+
case 4:
333+
bner_tag = (BNER_APPLICATION_TAG_OCTET_STR << 2)
334+
| ASN_TAG_CLASS_APPLICATION;
335+
break;
336+
case 3:
337+
bner_tag =
338+
(BNER_APPLICATION_TAG_BIT_STR << 2) | ASN_TAG_CLASS_APPLICATION;
339+
break;
340+
case 10:
341+
bner_tag = (BNER_APPLICATION_TAG_ENUMERATED << 2)
342+
| ASN_TAG_CLASS_APPLICATION;
343+
break;
344+
default:
345+
bner_tag = (255 << 2) | ASN_TAG_CLASS_APPLICATION;
346+
break; /* Invalid */
347+
}
348+
break;
349+
case ASN_TAG_CLASS_APPLICATION:
350+
case ASN_TAG_CLASS_CONTEXT:
351+
bner_tag = ber_tag;
352+
break;
353+
case ASN_TAG_CLASS_PRIVATE:
354+
default:
355+
bner_tag = (255 << 2) | ASN_TAG_CLASS_APPLICATION; /* Invalid */
356+
break;
357+
}
358+
359+
return bner_tag;
360+
}

0 commit comments

Comments
 (0)