blob: 2cfac3b6f2da68639e0ab0f642991b5d318179ed [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];
459 void *memb_ptr;
460
461 if(elm->optional) {
462 memb_ptr = *(void **)((char *)ptr + elm->memb_offset);
463 } else {
464 memb_ptr = (void *)((char *)ptr + elm->memb_offset);
465 }
466
467 return asn1_TYPE_outmost_tag(elm->type, memb_ptr,
468 elm->tag_mode, elm->tag);
469 } else {
470 return -1;
471 }
472}
473
474int
475CHOICE_constraint(asn1_TYPE_descriptor_t *td, const void *sptr,
476 asn_app_consume_bytes_f *app_errlog, void *app_key) {
477 asn1_CHOICE_specifics_t *specs = td->specifics;
478 int present;
479
480 if(!sptr) {
481 _ASN_ERRLOG("%s: value not given", td->name);
482 return -1;
483 }
484
485 /*
486 * Figure out which CHOICE element is encoded.
487 */
488 present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);
489 if(present > 0 && present <= specs->elements_count) {
490 asn1_CHOICE_element_t *elm = &specs->elements[present-1];
491 const void *memb_ptr;
492
493 if(elm->optional) {
494 memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset);
495 if(!memb_ptr) return 0;
496 } else {
497 memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
498 }
499
500 return elm->type->check_constraints(elm->type, memb_ptr,
501 app_errlog, app_key);
502 } else {
503 _ASN_ERRLOG("%s: no CHOICE element given", td->name);
504 return -1;
505 }
506}
507
508int
509CHOICE_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
510 asn_app_consume_bytes_f *cb, void *app_key) {
511 asn1_CHOICE_specifics_t *specs = td->specifics;
512 int present;
513
514 if(!sptr) return cb("<absent>", 8, app_key);
515
516 /*
517 * Figure out which CHOICE element is encoded.
518 */
519 present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);
520
521 /*
522 * Free that element.
523 */
524 if(present > 0 && present <= specs->elements_count) {
525 asn1_CHOICE_element_t *elm = &specs->elements[present-1];
526 const void *memb_ptr;
527
528 if(elm->optional) {
529 memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset);
530 if(!memb_ptr) return cb("<absent>", 8, app_key);
531 } else {
532 memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
533 }
534
535 /* Print member's name and stuff */
536 if(cb(elm->name, strlen(elm->name), app_key)
537 || cb(": ", 2, app_key))
538 return -1;
539
540 return elm->type->print_struct(elm->type, memb_ptr, ilevel,
541 cb, app_key);
542 } else {
543 return cb("<absent>", 8, app_key);
544 }
545}
546
547void
548CHOICE_free(asn1_TYPE_descriptor_t *td, void *ptr, int contents_only) {
549 asn1_CHOICE_specifics_t *specs = td->specifics;
550 int present;
551
552 if(!td || !ptr)
553 return;
554
555 ASN_DEBUG("Freeing %s as CHOICE", td->name);
556
557 /*
558 * Figure out which CHOICE element is encoded.
559 */
560 present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size);
561
562 /*
563 * Free that element.
564 */
565 if(present > 0 && present <= specs->elements_count) {
566 asn1_CHOICE_element_t *elm = &specs->elements[present-1];
567 void *memb_ptr;
568
569 if(elm->optional) {
570 memb_ptr = *(void **)((char *)ptr + elm->memb_offset);
571 if(memb_ptr)
572 elm->type->free_struct(elm->type, memb_ptr, 0);
573 } else {
574 memb_ptr = (void *)((char *)ptr + elm->memb_offset);
575 elm->type->free_struct(elm->type, memb_ptr, 1);
576 }
577 }
578
579 if(!contents_only) {
580 FREEMEM(ptr);
581 }
582}
583
584
585/*
586 * The following functions functions offer protection against -fshort-enums,
587 * compatible with little- and big-endian machines.
588 * If assertion is triggered, either disable -fshort-enums, or add an entry
589 * here with the ->pres_size of your target stracture.
590 * Unless the target structure is packed, the ".present" member
591 * is guaranteed to be aligned properly. ASN.1 compiler itself does not
592 * produce packed code.
593 */
594static inline int
595_fetch_present_idx(const void *struct_ptr, int pres_offset, int pres_size) {
596 const void *present_ptr;
597 int present;
598
599 present_ptr = ((const char *)struct_ptr) + pres_offset;
600
601 switch(pres_size) {
602 case sizeof(int): present = *(const int *)present_ptr; break;
603 case sizeof(short): present = *(const short *)present_ptr; break;
604 case sizeof(char): present = *(const char *)present_ptr; break;
605 default:
606 /* ANSI C mandates enum to be equivalent to integer */
607 assert(pres_size != sizeof(int));
608 return 0; /* If not aborted, pass back safe value */
609 }
610
611 return present;
612}
613
614static inline void
615_set_present_idx(void *struct_ptr, int pres_offset, int pres_size, int present) {
616 void *present_ptr;
617 present_ptr = ((char *)struct_ptr) + pres_offset;
618
619 switch(pres_size) {
620 case sizeof(int): *(int *)present_ptr = present; break;
621 case sizeof(short): *(short *)present_ptr = present; break;
622 case sizeof(char): *(char *)present_ptr = present; break;
623 default:
624 /* ANSI C mandates enum to be equivalent to integer */
625 assert(pres_size != sizeof(int));
626 }
627}