blob: a620b0ceb9b69a86fedea2831b64ba724a878a3a [file] [log] [blame]
Lev Walkinf15320b2004-06-03 03:38:44 +00001/*-
2 * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
3 * Redistribution and modifications are permitted subject to BSD license.
4 */
5#include <constr_CHOICE.h>
Lev Walkinf15320b2004-06-03 03:38:44 +00006#include <assert.h>
7
8/*
9 * Number of bytes left for this structure.
10 * (ctx->left) indicates the number of bytes _transferred_ for the structure.
11 * (size) contains the number of bytes in the buffer passed.
12 */
Lev Walkind9bd7752004-06-05 08:17:50 +000013#define LEFT ((size<(size_t)ctx->left)?size:ctx->left)
Lev Walkinf15320b2004-06-03 03:38:44 +000014
15/*
16 * If the subprocessor function returns with an indication that it wants
17 * more data, it may well be a fatal decoding problem, because the
18 * size is constrained by the <TLV>'s L, even if the buffer size allows
19 * reading more data.
20 * For example, consider the buffer containing the following TLVs:
21 * <T:5><L:1><V> <T:6>...
22 * The TLV length clearly indicates that one byte is expected in V, but
23 * if the V processor returns with "want more data" even if the buffer
24 * contains way more data than the V processor have seen.
25 */
Lev Walkind9bd7752004-06-05 08:17:50 +000026#define SIZE_VIOLATION (ctx->left >= 0 && (size_t)ctx->left <= size)
Lev Walkinf15320b2004-06-03 03:38:44 +000027
28/*
29 * This macro "eats" the part of the buffer which is definitely "consumed",
30 * i.e. was correctly converted into local representation or rightfully skipped.
31 */
32#define ADVANCE(num_bytes) do { \
33 size_t num = num_bytes; \
Lev Walkin4d9528c2004-08-11 08:10:13 +000034 (char *)ptr += num; \
Lev Walkinf15320b2004-06-03 03:38:44 +000035 size -= num; \
36 if(ctx->left >= 0) \
37 ctx->left -= num; \
38 consumed_myself += num; \
39 } while(0)
40
41/*
42 * Switch to the next phase of parsing.
43 */
44#define NEXT_PHASE(ctx) do { \
45 ctx->phase++; \
46 ctx->step = 0; \
47 } while(0)
48
49/*
50 * Return a standardized complex structure.
51 */
52#define RETURN(_code) do { \
53 rval.code = _code; \
54 rval.consumed = consumed_myself;\
55 return rval; \
56 } while(0)
57
58/*
59 * See the definitions.
60 */
61static inline int _fetch_present_idx(const void *struct_ptr, int off, int size);
62static inline void _set_present_idx(void *sptr, int offset, int size, int pres);
63
64/*
65 * Tags are canonically sorted in the tag to member table.
66 */
67static int
68_search4tag(const void *ap, const void *bp) {
Lev Walkin4c36e302004-06-06 07:20:02 +000069 const asn1_TYPE_tag2member_t *a = ap;
70 const asn1_TYPE_tag2member_t *b = bp;
Lev Walkinf15320b2004-06-03 03:38:44 +000071 int a_class = BER_TAG_CLASS(a->el_tag);
72 int b_class = BER_TAG_CLASS(b->el_tag);
73
74 if(a_class == b_class) {
75 ber_tlv_tag_t a_value = BER_TAG_VALUE(a->el_tag);
76 ber_tlv_tag_t b_value = BER_TAG_VALUE(b->el_tag);
77
78 if(a_value == b_value)
79 return 0;
80 else if(a_value < b_value)
81 return -1;
82 else
83 return 1;
84 } else if(a_class < b_class) {
85 return -1;
86 } else {
87 return 1;
88 }
89}
90
91/*
92 * The decoder of the CHOICE type.
93 */
94ber_dec_rval_t
95CHOICE_decode_ber(asn1_TYPE_descriptor_t *sd,
96 void **struct_ptr, void *ptr, size_t size, int tag_mode) {
97 /*
98 * Bring closer parts of structure description.
99 */
100 asn1_CHOICE_specifics_t *specs = sd->specifics;
101 asn1_CHOICE_element_t *elements = specs->elements;
102
103 /*
104 * Parts of the structure being constructed.
105 */
106 void *st = *struct_ptr; /* Target structure. */
107 ber_dec_ctx_t *ctx; /* Decoder context */
108
109 ber_tlv_tag_t tlv_tag; /* T from TLV */
110 ssize_t tag_len; /* Length of TLV's T */
111 //ber_tlv_len_t tlv_len; /* L from TLV */
112 ber_dec_rval_t rval; /* Return code from subparsers */
113
114 ssize_t consumed_myself = 0; /* Consumed bytes from ptr */
115
116 ASN_DEBUG("Decoding %s as CHOICE", sd->name);
117
118 /*
119 * Create the target structure if it is not present already.
120 */
121 if(st == 0) {
122 st = *struct_ptr = CALLOC(1, specs->struct_size);
123 if(st == 0) {
124 RETURN(RC_FAIL);
125 }
126 }
127
128 /*
129 * Restore parsing context.
130 */
Lev Walkin4d9528c2004-08-11 08:10:13 +0000131 ctx = (ber_dec_ctx_t *)((char *)st + specs->ctx_offset);
Lev Walkinf15320b2004-06-03 03:38:44 +0000132
133 /*
134 * Start to parse where left previously
135 */
136 switch(ctx->phase) {
137 case 0:
138 /*
139 * PHASE 0.
140 * Check that the set of tags associated with given structure
141 * perfectly fits our expectations.
142 */
143
144 if(tag_mode || sd->tags_count) {
145 rval = ber_check_tags(sd, ctx, ptr, size,
146 tag_mode, &ctx->left, 0);
147 if(rval.code != RC_OK) {
148 ASN_DEBUG("%s tagging check failed: %d",
149 sd->name, rval.code);
150 consumed_myself += rval.consumed;
151 RETURN(rval.code);
152 }
153
154 if(ctx->left >= 0) {
155 /* ?Substracted below! */
156 ctx->left += rval.consumed;
157 }
158 ADVANCE(rval.consumed);
159 } else {
160 ctx->left = -1;
161 }
162
163 NEXT_PHASE(ctx);
164
165 ASN_DEBUG("Structure consumes %ld bytes, buffer %ld",
166 (long)ctx->left, (long)size);
167
168 /* Fall through */
169 case 1:
170 /*
171 * Fetch the T from TLV.
172 */
173 tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag);
174 ASN_DEBUG("In %s CHOICE tag length %d", sd->name, (int)tag_len);
175 switch(tag_len) {
176 case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
177 /* Fall through */
178 case -1: RETURN(RC_FAIL);
179 }
180
181 do {
Lev Walkin4c36e302004-06-06 07:20:02 +0000182 asn1_TYPE_tag2member_t *t2m;
183 asn1_TYPE_tag2member_t key;
Lev Walkinf15320b2004-06-03 03:38:44 +0000184
185 key.el_tag = tlv_tag;
186 t2m = bsearch(&key, specs->tag2el, specs->tag2el_count,
187 sizeof(specs->tag2el[0]), _search4tag);
188 if(t2m) {
189 /*
190 * Found the element corresponding to the tag.
191 */
192 NEXT_PHASE(ctx);
193 ctx->step = t2m->el_no;
194 break;
195 } else if(specs->extensible == 0) {
196 ASN_DEBUG("Unexpected tag %s "
197 "in non-extensible CHOICE %s",
198 ber_tlv_tag_string(tlv_tag), sd->name);
199 RETURN(RC_FAIL);
200 } else {
201 /* Skip this tag */
202 ssize_t skip;
203
204 ASN_DEBUG("Skipping unknown tag %s",
205 ber_tlv_tag_string(tlv_tag));
206
207 skip = ber_skip_length(
208 BER_TLV_CONSTRUCTED(ptr),
Lev Walkin4d9528c2004-08-11 08:10:13 +0000209 (char *)ptr + tag_len, LEFT - tag_len);
Lev Walkinf15320b2004-06-03 03:38:44 +0000210
211 switch(skip) {
212 case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
213 /* Fall through */
214 case -1: RETURN(RC_FAIL);
215 }
216
217 ADVANCE(skip + tag_len);
218 RETURN(RC_OK);
219 }
220 } while(0);
221
222 case 2:
223 /*
224 * PHASE 2.
225 * Read in the element.
226 */
227 do {
228 asn1_CHOICE_element_t *elm; /* CHOICE's element */
229 void *memb_ptr; /* Pointer to the member */
230 void *memb_ptr2; /* Pointer to that pointer */
231
232 elm = &elements[ctx->step];
233
234 /*
235 * Compute the position of the member inside a structure,
236 * and also a type of containment (it may be contained
237 * as pointer or using inline inclusion).
238 */
239 if(elm->optional) {
240 /* Optional member, hereby, a simple pointer */
241 memb_ptr2 = (char *)st + elm->memb_offset;
242 } else {
243 /*
244 * A pointer to a pointer
245 * holding the start of the structure
246 */
247 memb_ptr = (char *)st + elm->memb_offset;
248 memb_ptr2 = &memb_ptr;
249 }
250 /*
251 * Invoke the member fetch routine according to member's type
252 */
253 rval = elm->type->ber_decoder(
254 (void *)elm->type,
255 memb_ptr2, ptr, LEFT,
256 elm->tag_mode);
257 switch(rval.code) {
258 case RC_OK:
259 _set_present_idx(st, specs->pres_offset,
260 specs->pres_size, ctx->step + 1);
261 break;
262 case RC_WMORE: /* More data expected */
263 if(!SIZE_VIOLATION) {
264 ADVANCE(rval.consumed);
265 RETURN(RC_WMORE);
266 }
267 RETURN(RC_FAIL);
268 case RC_FAIL: /* Fatal error */
269 RETURN(rval.code);
270 } /* switch(rval) */
271
272 ADVANCE(rval.consumed);
273 } while(0);
274
275 NEXT_PHASE(ctx);
276
277 /* Fall through */
278 case 3:
279 ASN_DEBUG("CHOICE %s Leftover: %ld, size = %ld, tm=%d, tc=%d",
280 sd->name, (long)ctx->left, (long)size,
281 tag_mode, sd->tags_count);
282
283 if(ctx->left > 0) {
284 /*
285 * The type must be fully decoded
286 * by the CHOICE member-specific decoder.
287 */
288 RETURN(RC_FAIL);
289 }
290
291 if(ctx->left == -1
292 && !(tag_mode || sd->tags_count)) {
293 /*
294 * This is an untagged CHOICE.
295 * It doesn't contain nothing
296 * except for the member itself, including all its tags.
297 * The decoding is completed.
298 */
299 NEXT_PHASE(ctx);
300 break;
301 }
302
303 /*
304 * Read in the "end of data chunks"'s.
305 */
306 while(ctx->left < 0) {
307 ssize_t tl;
308
309 tl = ber_fetch_tag(ptr, LEFT, &tlv_tag);
310 switch(tl) {
311 case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
312 /* Fall through */
313 case -1: RETURN(RC_FAIL);
314 }
315
316 /*
317 * Expected <0><0>...
318 */
319 if(((uint8_t *)ptr)[0] == 0) {
320 if(LEFT < 2) {
321 if(SIZE_VIOLATION)
322 RETURN(RC_FAIL);
323 else
324 RETURN(RC_WMORE);
325 } else if(((uint8_t *)ptr)[1] == 0) {
326 /*
327 * Correctly finished with <0><0>.
328 */
329 continue;
330 }
331 } else {
332 ASN_DEBUG("Unexpected continuation in %s",
333 sd->name);
334 RETURN(RC_FAIL);
335 }
336
337 ADVANCE(2);
338 ctx->left++;
339 }
340
341 NEXT_PHASE(ctx);
342 case 4:
343 /* No meaningful work here */
344 break;
345 }
346
347 RETURN(RC_OK);
348}
349
350der_enc_rval_t
351CHOICE_encode_der(asn1_TYPE_descriptor_t *sd,
352 void *struct_ptr,
353 int tag_mode, ber_tlv_tag_t tag,
354 asn_app_consume_bytes_f *cb, void *app_key) {
355 asn1_CHOICE_specifics_t *specs = sd->specifics;
356 asn1_CHOICE_element_t *elm; /* CHOICE element */
357 der_enc_rval_t erval;
358 void *memb_ptr;
359 size_t computed_size = 0;
360 int present;
361
362 ASN_DEBUG("%s %s as CHOICE",
363 cb?"Encoding":"Estimating", sd->name);
364
365 present = _fetch_present_idx(struct_ptr,
366 specs->pres_offset, specs->pres_size);
367
368 /*
369 * If the structure was not initialized, it cannot be encoded:
370 * can't deduce what to encode in the choice type.
371 */
372 if(present <= 0 || present > specs->elements_count) {
373 if(present == 0 && specs->elements_count == 0) {
374 /* The CHOICE is empty?! */
375 erval.encoded = 0;
376 return erval;
377 }
378 erval.encoded = -1;
379 erval.failed_type = sd;
380 erval.structure_ptr = struct_ptr;
381 return erval;
382 }
383
384 /*
385 * Seek over the present member of the structure.
386 */
387 elm = &specs->elements[present-1];
388 if(elm->optional) {
389 memb_ptr = *(void **)((char *)struct_ptr + elm->memb_offset);
390 if(memb_ptr == 0) {
391 erval.encoded = 0;
392 return erval;
393 }
394 } else {
395 memb_ptr = (void *)((char *)struct_ptr + elm->memb_offset);
396 }
397
398 /*
399 * If the CHOICE itself is tagged EXPLICIT:
400 * T ::= [2] EXPLICIT CHOICE { ... }
401 * Then emit the appropriate tags.
402 */
403 if(tag_mode == 1 || sd->tags_count) {
404 /*
405 * For this, we need to pre-compute the member.
406 */
407 ssize_t ret;
408
409 /* Encode member with its tag */
410 erval = elm->type->der_encoder(elm->type, memb_ptr,
411 elm->tag_mode, elm->tag, 0, 0);
412 if(erval.encoded == -1)
413 return erval;
414
415 /* Encode CHOICE with parent or my own tag */
416 ret = der_write_tags(sd, erval.encoded, tag_mode, tag,
417 cb, app_key);
418 if(ret == -1) {
419 erval.encoded = -1;
420 erval.failed_type = sd;
421 erval.structure_ptr = struct_ptr;
422 return erval;
423 }
424 computed_size += ret;
425 }
426
427 /*
428 * Encode the single underlying member.
429 */
430 erval = elm->type->der_encoder(elm->type, memb_ptr,
431 elm->tag_mode, elm->tag, cb, app_key);
432 if(erval.encoded == -1)
433 return erval;
434
435 ASN_DEBUG("Encoded CHOICE member in %ld bytes (+%ld)",
436 (long)erval.encoded, (long)computed_size);
437
438 erval.encoded += computed_size;
439
440 return erval;
441}
442
443ber_tlv_tag_t
444CHOICE_outmost_tag(asn1_TYPE_descriptor_t *td, const void *ptr, int tag_mode, ber_tlv_tag_t tag) {
445 asn1_CHOICE_specifics_t *specs = td->specifics;
446 int present;
447
448 assert(tag_mode == 0);
449 assert(tag == 0);
450
451 /*
452 * Figure out which CHOICE element is encoded.
453 */
454 present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size);
455
456 if(present > 0 || present <= specs->elements_count) {
457 asn1_CHOICE_element_t *elm = &specs->elements[present-1];
Lev Walkin70e70282004-06-05 01:45:48 +0000458 const void *memb_ptr;
Lev Walkinf15320b2004-06-03 03:38:44 +0000459
460 if(elm->optional) {
Lev Walkin70e70282004-06-05 01:45:48 +0000461 memb_ptr = *(const void * const *)
462 ((const char *)ptr + elm->memb_offset);
Lev Walkinf15320b2004-06-03 03:38:44 +0000463 } else {
Lev Walkin70e70282004-06-05 01:45:48 +0000464 memb_ptr = (const void *)
465 ((const char *)ptr + elm->memb_offset);
Lev Walkinf15320b2004-06-03 03:38:44 +0000466 }
467
468 return asn1_TYPE_outmost_tag(elm->type, memb_ptr,
469 elm->tag_mode, elm->tag);
470 } else {
Lev Walkin4d9528c2004-08-11 08:10:13 +0000471 return (ber_tlv_tag_t)-1;
Lev Walkinf15320b2004-06-03 03:38:44 +0000472 }
473}
474
475int
476CHOICE_constraint(asn1_TYPE_descriptor_t *td, const void *sptr,
477 asn_app_consume_bytes_f *app_errlog, void *app_key) {
478 asn1_CHOICE_specifics_t *specs = td->specifics;
479 int present;
480
481 if(!sptr) {
482 _ASN_ERRLOG("%s: value not given", td->name);
483 return -1;
484 }
485
486 /*
487 * Figure out which CHOICE element is encoded.
488 */
489 present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);
490 if(present > 0 && present <= specs->elements_count) {
491 asn1_CHOICE_element_t *elm = &specs->elements[present-1];
492 const void *memb_ptr;
493
494 if(elm->optional) {
495 memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset);
496 if(!memb_ptr) return 0;
497 } else {
498 memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
499 }
500
501 return elm->type->check_constraints(elm->type, memb_ptr,
502 app_errlog, app_key);
503 } else {
504 _ASN_ERRLOG("%s: no CHOICE element given", td->name);
505 return -1;
506 }
507}
508
509int
510CHOICE_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
511 asn_app_consume_bytes_f *cb, void *app_key) {
512 asn1_CHOICE_specifics_t *specs = td->specifics;
513 int present;
514
515 if(!sptr) return cb("<absent>", 8, app_key);
516
517 /*
518 * Figure out which CHOICE element is encoded.
519 */
520 present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);
521
522 /*
523 * Free that element.
524 */
525 if(present > 0 && present <= specs->elements_count) {
526 asn1_CHOICE_element_t *elm = &specs->elements[present-1];
527 const void *memb_ptr;
528
529 if(elm->optional) {
530 memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset);
531 if(!memb_ptr) return cb("<absent>", 8, app_key);
532 } else {
533 memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
534 }
535
536 /* Print member's name and stuff */
537 if(cb(elm->name, strlen(elm->name), app_key)
538 || cb(": ", 2, app_key))
539 return -1;
540
541 return elm->type->print_struct(elm->type, memb_ptr, ilevel,
542 cb, app_key);
543 } else {
544 return cb("<absent>", 8, app_key);
545 }
546}
547
548void
549CHOICE_free(asn1_TYPE_descriptor_t *td, void *ptr, int contents_only) {
550 asn1_CHOICE_specifics_t *specs = td->specifics;
551 int present;
552
553 if(!td || !ptr)
554 return;
555
556 ASN_DEBUG("Freeing %s as CHOICE", td->name);
557
558 /*
559 * Figure out which CHOICE element is encoded.
560 */
561 present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size);
562
563 /*
564 * Free that element.
565 */
566 if(present > 0 && present <= specs->elements_count) {
567 asn1_CHOICE_element_t *elm = &specs->elements[present-1];
568 void *memb_ptr;
569
570 if(elm->optional) {
571 memb_ptr = *(void **)((char *)ptr + elm->memb_offset);
572 if(memb_ptr)
573 elm->type->free_struct(elm->type, memb_ptr, 0);
574 } else {
575 memb_ptr = (void *)((char *)ptr + elm->memb_offset);
576 elm->type->free_struct(elm->type, memb_ptr, 1);
577 }
578 }
579
580 if(!contents_only) {
581 FREEMEM(ptr);
582 }
583}
584
585
586/*
587 * The following functions functions offer protection against -fshort-enums,
588 * compatible with little- and big-endian machines.
589 * If assertion is triggered, either disable -fshort-enums, or add an entry
590 * here with the ->pres_size of your target stracture.
591 * Unless the target structure is packed, the ".present" member
592 * is guaranteed to be aligned properly. ASN.1 compiler itself does not
593 * produce packed code.
594 */
595static inline int
596_fetch_present_idx(const void *struct_ptr, int pres_offset, int pres_size) {
597 const void *present_ptr;
598 int present;
599
600 present_ptr = ((const char *)struct_ptr) + pres_offset;
601
602 switch(pres_size) {
603 case sizeof(int): present = *(const int *)present_ptr; break;
604 case sizeof(short): present = *(const short *)present_ptr; break;
605 case sizeof(char): present = *(const char *)present_ptr; break;
606 default:
607 /* ANSI C mandates enum to be equivalent to integer */
608 assert(pres_size != sizeof(int));
609 return 0; /* If not aborted, pass back safe value */
610 }
611
612 return present;
613}
614
615static inline void
616_set_present_idx(void *struct_ptr, int pres_offset, int pres_size, int present) {
617 void *present_ptr;
618 present_ptr = ((char *)struct_ptr) + pres_offset;
619
620 switch(pres_size) {
621 case sizeof(int): *(int *)present_ptr = present; break;
622 case sizeof(short): *(short *)present_ptr = present; break;
623 case sizeof(char): *(char *)present_ptr = present; break;
624 default:
625 /* ANSI C mandates enum to be equivalent to integer */
626 assert(pres_size != sizeof(int));
627 }
628}