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