/*- * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. * Redistribution and modifications are permitted subject to BSD license. */ #include #include #include /* * Decode an always-primitive type. */ asn_dec_rval_t ber_decode_primitive(asn_codec_ctx_t * opt_codec_ctx, asn_TYPE_descriptor_t * td, void **sptr, const void *buf_ptr, size_t size, int tag_mode) { ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *) * sptr; asn_dec_rval_t rval; ber_tlv_len_t length; /* * If the structure is not there, allocate it. */ if (st == NULL) { st = (ASN__PRIMITIVE_TYPE_t *) CALLOC(1, sizeof(*st)); if (st == NULL) _ASN_DECODE_FAILED; *sptr = (void *)st; } ASN_DEBUG("Decoding %s as plain primitive (tm=%d)", td->name, tag_mode); /* * Check tags and extract value length. */ rval = ber_check_tags(opt_codec_ctx, td, 0, buf_ptr, size, tag_mode, 0, &length, 0); if (rval.code != RC_OK) return rval; ASN_DEBUG("%s length is %d bytes", td->name, (int)length); /* * Make sure we have this length. */ buf_ptr = ((const char *)buf_ptr) + rval.consumed; size -= rval.consumed; if (length > (ber_tlv_len_t) size) { rval.code = RC_WMORE; rval.consumed = 0; return rval; } st->size = (int)length; /* The following better be optimized away. */ if (sizeof(st->size) != sizeof(length) && (ber_tlv_len_t) st->size != length) { st->size = 0; _ASN_DECODE_FAILED; } st->buf = (uint8_t *) MALLOC(length + 1); if (!st->buf) { st->size = 0; _ASN_DECODE_FAILED; } memcpy(st->buf, buf_ptr, length); st->buf[length] = '\0'; /* Just in case */ rval.code = RC_OK; rval.consumed += length; ASN_DEBUG("Took %ld/%ld bytes to encode %s", (long)rval.consumed, (long)length, td->name); return rval; } /* * Encode an always-primitive type using DER. */ asn_enc_rval_t der_encode_primitive(asn_TYPE_descriptor_t * td, void *sptr, int tag_mode, ber_tlv_tag_t tag, asn_app_consume_bytes_f * cb, void *app_key) { asn_enc_rval_t erval; ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *) sptr; ASN_DEBUG("%s %s as a primitive type (tm=%d)", cb ? "Encoding" : "Estimating", td->name, tag_mode); erval.encoded = der_write_tags(td, st->size, tag_mode, 0, tag, cb, app_key); ASN_DEBUG("%s wrote tags %d", td->name, (int)erval.encoded); if (erval.encoded == -1) { erval.failed_type = td; erval.structure_ptr = sptr; return erval; } if (cb && st->buf) { if (cb(st->buf, st->size, app_key) < 0) { erval.encoded = -1; erval.failed_type = td; erval.structure_ptr = sptr; return erval; } } else { assert(st->buf || st->size == 0); } erval.encoded += st->size; _ASN_ENCODED_OK(erval); } void ASN__PRIMITIVE_TYPE_free(asn_TYPE_descriptor_t * td, void *sptr, int contents_only) { ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *) sptr; if (!td || !sptr) return; ASN_DEBUG("Freeing %s as a primitive type", td->name); if (st->buf) FREEMEM(st->buf); if (!contents_only) FREEMEM(st); } /* * Local internal type passed around as an argument. */ struct xdp_arg_s { asn_TYPE_descriptor_t *type_descriptor; void *struct_key; xer_primitive_body_decoder_f *prim_body_decoder; int decoded_something; int want_more; }; static int xer_decode__unexpected_tag(void *key, const void *chunk_buf, size_t chunk_size) { struct xdp_arg_s *arg = (struct xdp_arg_s *)key; enum xer_pbd_rval bret; if (arg->decoded_something) { if (xer_is_whitespace(chunk_buf, chunk_size)) return 0; /* Skip it. */ /* * Decoding was done once already. Prohibit doing it again. */ return -1; } bret = arg->prim_body_decoder(arg->type_descriptor, arg->struct_key, chunk_buf, chunk_size); switch (bret) { case XPBD_SYSTEM_FAILURE: case XPBD_DECODER_LIMIT: case XPBD_BROKEN_ENCODING: break; case XPBD_BODY_CONSUMED: /* Tag decoded successfully */ arg->decoded_something = 1; /* Fall through */ case XPBD_NOT_BODY_IGNORE: /* Safe to proceed further */ return 0; } return -1; } static ssize_t xer_decode__body(void *key, const void *chunk_buf, size_t chunk_size, int have_more) { struct xdp_arg_s *arg = (struct xdp_arg_s *)key; enum xer_pbd_rval bret; if (arg->decoded_something) { if (xer_is_whitespace(chunk_buf, chunk_size)) return chunk_size; /* * Decoding was done once already. Prohibit doing it again. */ return -1; } if (!have_more) { /* * If we've received something like "1", we can't really * tell whether it is really `1` or `123`, until we know * that there is no more data coming. * The have_more argument will be set to 1 once something * like this is available to the caller of this callback: * "1want_more = 1; return -1; } bret = arg->prim_body_decoder(arg->type_descriptor, arg->struct_key, chunk_buf, chunk_size); switch (bret) { case XPBD_SYSTEM_FAILURE: case XPBD_DECODER_LIMIT: case XPBD_BROKEN_ENCODING: break; case XPBD_BODY_CONSUMED: /* Tag decoded successfully */ arg->decoded_something = 1; /* Fall through */ case XPBD_NOT_BODY_IGNORE: /* Safe to proceed further */ return chunk_size; } return -1; } asn_dec_rval_t xer_decode_primitive(asn_codec_ctx_t * opt_codec_ctx, asn_TYPE_descriptor_t * td, void **sptr, size_t struct_size, const char *opt_mname, const void *buf_ptr, size_t size, xer_primitive_body_decoder_f * prim_body_decoder) { const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; asn_struct_ctx_t s_ctx; struct xdp_arg_s s_arg; asn_dec_rval_t rc; /* * Create the structure if does not exist. */ if (!*sptr) { *sptr = CALLOC(1, struct_size); if (!*sptr) _ASN_DECODE_FAILED; } memset(&s_ctx, 0, sizeof(s_ctx)); s_arg.type_descriptor = td; s_arg.struct_key = *sptr; s_arg.prim_body_decoder = prim_body_decoder; s_arg.decoded_something = 0; s_arg.want_more = 0; rc = xer_decode_general(opt_codec_ctx, &s_ctx, &s_arg, xml_tag, buf_ptr, size, xer_decode__unexpected_tag, xer_decode__body); switch (rc.code) { case RC_OK: if (!s_arg.decoded_something) { char ch; ASN_DEBUG("Primitive body is not recognized, " "supplying empty one"); /* * Decoding opportunity has come and gone. * Where's the result? * Try to feed with empty body, see if it eats it. */ if (prim_body_decoder (s_arg.type_descriptor, s_arg.struct_key, &ch, 0) != XPBD_BODY_CONSUMED) { /* * This decoder does not like empty stuff. */ _ASN_DECODE_FAILED; } } break; case RC_WMORE: /* * Redo the whole thing later. * We don't have a context to save intermediate parsing state. */ rc.consumed = 0; break; case RC_FAIL: rc.consumed = 0; if (s_arg.want_more) rc.code = RC_WMORE; else _ASN_DECODE_FAILED; break; } return rc; }