| /* |
| * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>. All rights reserved. |
| * Redistribution and modifications are permitted subject to BSD license. |
| */ |
| #include <asn_internal.h> |
| #include <asn_application.h> |
| #include <errno.h> |
| |
| static asn_enc_rval_t asn_encode_internal(const asn_codec_ctx_t *opt_codec_ctx, |
| enum asn_transfer_syntax syntax, |
| const asn_TYPE_descriptor_t *td, |
| const void *sptr, |
| asn_app_consume_bytes_f *callback, |
| void *callback_key); |
| |
| |
| struct callback_count_bytes_key { |
| asn_app_consume_bytes_f *callback; |
| void *callback_key; |
| size_t computed_size; |
| }; |
| |
| /* |
| * Encoder which just counts bytes that come through it. |
| */ |
| static int |
| callback_count_bytes_cb(const void *data, size_t size, void *keyp) { |
| struct callback_count_bytes_key *key = keyp; |
| int ret; |
| |
| ret = key->callback(data, size, key->callback_key); |
| if(ret >= 0) { |
| key->computed_size += size; |
| } |
| |
| return ret; |
| } |
| |
| struct overrun_encoder_key { |
| void *buffer; |
| size_t buffer_size; |
| size_t computed_size; |
| }; |
| |
| struct dynamic_encoder_key { |
| void *buffer; |
| size_t buffer_size; |
| size_t computed_size; |
| }; |
| |
| struct callback_failure_catch_key { |
| asn_app_consume_bytes_f *callback; |
| void *callback_key; |
| int callback_failed; |
| }; |
| |
| /* |
| * Encoder which doesn't stop counting bytes |
| * even if it reaches the end of the buffer. |
| */ |
| static int |
| overrun_encoder_cb(const void *data, size_t size, void *keyp) { |
| struct overrun_encoder_key *key = keyp; |
| |
| if(key->computed_size + size > key->buffer_size) { |
| /* |
| * Avoid accident on the next call: |
| * stop adding bytes to the buffer. |
| */ |
| key->buffer_size = 0; |
| } else { |
| memcpy((char *)key->buffer + key->computed_size, data, size); |
| } |
| key->computed_size += size; |
| |
| return 0; |
| } |
| |
| /* |
| * Encoder which dynamically allocates output, and continues |
| * to count even if allocation failed. |
| */ |
| static int |
| dynamic_encoder_cb(const void *data, size_t size, void *keyp) { |
| struct dynamic_encoder_key *key = keyp; |
| |
| if(key->buffer) { |
| if(key->computed_size + size >= key->buffer_size) { |
| void *p; |
| size_t new_size = key->buffer_size; |
| |
| do { |
| new_size *= 2; |
| } while(new_size <= key->computed_size + size); |
| |
| p = REALLOC(key->buffer, new_size); |
| if(p) { |
| key->buffer = p; |
| key->buffer_size = new_size; |
| } else { |
| FREEMEM(key->buffer); |
| key->buffer = 0; |
| key->buffer_size = 0; |
| key->computed_size += size; |
| return 0; |
| } |
| } |
| memcpy((char *)key->buffer + key->computed_size, data, size); |
| } |
| |
| key->computed_size += size; |
| |
| return 0; |
| } |
| |
| /* |
| * Encoder which help convert the application level encoder failure into EIO. |
| */ |
| static int |
| callback_failure_catch_cb(const void *data, size_t size, void *keyp) { |
| struct callback_failure_catch_key *key = keyp; |
| int ret; |
| |
| ret = key->callback(data, size, key->callback_key); |
| if(ret < 0) { |
| key->callback_failed = 1; |
| } |
| |
| return ret; |
| } |
| |
| asn_enc_rval_t |
| asn_encode(const asn_codec_ctx_t *opt_codec_ctx, |
| enum asn_transfer_syntax syntax, const asn_TYPE_descriptor_t *td, |
| const void *sptr, asn_app_consume_bytes_f *callback, void *callback_key) { |
| struct callback_failure_catch_key cb_key; |
| asn_enc_rval_t er = {0,0,0}; |
| |
| if(!callback) { |
| errno = EINVAL; |
| ASN__ENCODE_FAILED; |
| } |
| |
| cb_key.callback = callback; |
| cb_key.callback_key = callback_key; |
| cb_key.callback_failed = 0; |
| |
| er = asn_encode_internal(opt_codec_ctx, syntax, td, sptr, |
| callback_failure_catch_cb, &cb_key); |
| if(cb_key.callback_failed) { |
| assert(er.encoded == -1); |
| assert(errno == EBADF); |
| errno = EIO; |
| } |
| |
| return er; |
| } |
| |
| asn_enc_rval_t |
| asn_encode_to_buffer(const asn_codec_ctx_t *opt_codec_ctx, |
| enum asn_transfer_syntax syntax, |
| const asn_TYPE_descriptor_t *td, const void *sptr, |
| void *buffer, size_t buffer_size) { |
| struct overrun_encoder_key buf_key; |
| asn_enc_rval_t er = {0,0,0}; |
| |
| if(buffer_size > 0 && !buffer) { |
| errno = EINVAL; |
| ASN__ENCODE_FAILED; |
| } |
| |
| buf_key.buffer = buffer; |
| buf_key.buffer_size = buffer_size; |
| buf_key.computed_size = 0; |
| |
| er = asn_encode_internal(opt_codec_ctx, syntax, td, sptr, |
| overrun_encoder_cb, &buf_key); |
| |
| if(er.encoded >= 0 && (size_t)er.encoded != buf_key.computed_size) { |
| ASN_DEBUG("asn_encode() returned %" ASN_PRI_SSIZE |
| " yet produced %" ASN_PRI_SIZE " bytes", |
| er.encoded, buf_key.computed_size); |
| assert(er.encoded < 0 || (size_t)er.encoded == buf_key.computed_size); |
| } |
| |
| return er; |
| } |
| |
| asn_encode_to_new_buffer_result_t |
| asn_encode_to_new_buffer(const asn_codec_ctx_t *opt_codec_ctx, |
| enum asn_transfer_syntax syntax, |
| const asn_TYPE_descriptor_t *td, const void *sptr) { |
| struct dynamic_encoder_key buf_key; |
| asn_encode_to_new_buffer_result_t res; |
| |
| buf_key.buffer_size = 16; |
| buf_key.buffer = MALLOC(buf_key.buffer_size); |
| buf_key.computed_size = 0; |
| |
| res.result = asn_encode_internal(opt_codec_ctx, syntax, td, sptr, |
| dynamic_encoder_cb, &buf_key); |
| |
| if(res.result.encoded >= 0 |
| && (size_t)res.result.encoded != buf_key.computed_size) { |
| ASN_DEBUG("asn_encode() returned %" ASN_PRI_SSIZE |
| " yet produced %" ASN_PRI_SIZE " bytes", |
| res.result.encoded, buf_key.computed_size); |
| assert(res.result.encoded < 0 |
| || (size_t)res.result.encoded == buf_key.computed_size); |
| } |
| |
| res.buffer = buf_key.buffer; |
| |
| /* 0-terminate just in case. */ |
| if(res.buffer) { |
| assert(buf_key.computed_size < buf_key.buffer_size); |
| ((char *)res.buffer)[buf_key.computed_size] = '\0'; |
| } |
| |
| return res; |
| } |
| |
| static asn_enc_rval_t |
| asn_encode_internal(const asn_codec_ctx_t *opt_codec_ctx, |
| enum asn_transfer_syntax syntax, |
| const asn_TYPE_descriptor_t *td, const void *sptr, |
| asn_app_consume_bytes_f *callback, void *callback_key) { |
| asn_enc_rval_t er = {0,0,0}; |
| enum xer_encoder_flags_e xer_flags = XER_F_CANONICAL; |
| |
| (void)opt_codec_ctx; /* Parameters are not checked on encode yet. */ |
| |
| if(!td || !sptr) { |
| errno = EINVAL; |
| ASN__ENCODE_FAILED; |
| } |
| |
| switch(syntax) { |
| case ATS_NONSTANDARD_PLAINTEXT: |
| if(td->op->print_struct) { |
| struct callback_count_bytes_key cb_key; |
| cb_key.callback = callback; |
| cb_key.callback_key = callback_key; |
| cb_key.computed_size = 0; |
| if(td->op->print_struct(td, sptr, 1, callback_count_bytes_cb, |
| &cb_key) |
| < 0 |
| || callback_count_bytes_cb("\n", 1, &cb_key) < 0) { |
| errno = EBADF; /* Structure has incorrect form. */ |
| er.encoded = -1; |
| er.failed_type = td; |
| er.structure_ptr = sptr; |
| } else { |
| er.encoded = cb_key.computed_size; |
| er.failed_type = 0; |
| er.structure_ptr = 0; |
| } |
| } else { |
| errno = ENOENT; /* Transfer syntax is not defined for this type. */ |
| ASN__ENCODE_FAILED; |
| } |
| break; |
| |
| case ATS_RANDOM: |
| errno = ENOENT; /* Randomization doesn't make sense on output. */ |
| ASN__ENCODE_FAILED; |
| |
| case ATS_BER: |
| /* BER is a superset of DER. */ |
| /* Fall through. */ |
| case ATS_DER: |
| if(td->op->der_encoder) { |
| er = der_encode(td, sptr, callback, callback_key); |
| if(er.encoded == -1) { |
| if(er.failed_type && er.failed_type->op->der_encoder) { |
| errno = EBADF; /* Structure has incorrect form. */ |
| } else { |
| errno = ENOENT; /* DER is not defined for this type. */ |
| } |
| } |
| } else { |
| errno = ENOENT; /* Transfer syntax is not defined for this type. */ |
| ASN__ENCODE_FAILED; |
| } |
| break; |
| case ATS_CER: |
| errno = ENOENT; /* Transfer syntax is not defined for any type. */ |
| ASN__ENCODE_FAILED; |
| |
| #ifdef ASN_DISABLE_OER_SUPPORT |
| case ATS_BASIC_OER: |
| case ATS_CANONICAL_OER: |
| errno = ENOENT; /* PER is not defined. */ |
| ASN__ENCODE_FAILED; |
| break; |
| #else /* ASN_DISABLE_OER_SUPPORT */ |
| case ATS_BASIC_OER: |
| /* CANONICAL-OER is a superset of BASIC-OER. */ |
| /* Fall through. */ |
| case ATS_CANONICAL_OER: |
| if(td->op->oer_encoder) { |
| er = oer_encode(td, sptr, callback, callback_key); |
| if(er.encoded == -1) { |
| if(er.failed_type && er.failed_type->op->oer_encoder) { |
| errno = EBADF; /* Structure has incorrect form. */ |
| } else { |
| errno = ENOENT; /* OER is not defined for this type. */ |
| } |
| } |
| } else { |
| errno = ENOENT; /* Transfer syntax is not defined for this type. */ |
| ASN__ENCODE_FAILED; |
| } |
| break; |
| #endif /* ASN_DISABLE_OER_SUPPORT */ |
| |
| #ifdef ASN_DISABLE_PER_SUPPORT |
| case ATS_UNALIGNED_BASIC_PER: |
| case ATS_UNALIGNED_CANONICAL_PER: |
| case ATS_ALIGNED_BASIC_PER: |
| case ATS_ALIGNED_CANONICAL_PER: |
| errno = ENOENT; /* PER is not defined. */ |
| ASN__ENCODE_FAILED; |
| break; |
| #else /* ASN_DISABLE_PER_SUPPORT */ |
| case ATS_UNALIGNED_BASIC_PER: |
| /* CANONICAL-UPER is a superset of BASIC-UPER. */ |
| /* Fall through. */ |
| case ATS_UNALIGNED_CANONICAL_PER: |
| if(td->op->uper_encoder) { |
| er = uper_encode(td, 0, sptr, callback, callback_key); |
| if(er.encoded == -1) { |
| if(er.failed_type && er.failed_type->op->uper_encoder) { |
| errno = EBADF; /* Structure has incorrect form. */ |
| } else { |
| errno = ENOENT; /* UPER is not defined for this type. */ |
| } |
| } else { |
| ASN_DEBUG("Complete encoded in %ld bits", (long)er.encoded); |
| if(er.encoded == 0) { |
| /* Enforce "Complete Encoding" of X.691 #11.1 */ |
| if(callback("\0", 1, callback_key) < 0) { |
| errno = EBADF; |
| ASN__ENCODE_FAILED; |
| } |
| er.encoded = 8; /* Exactly 8 zero bits is added. */ |
| } |
| /* Convert bits into bytes */ |
| er.encoded = (er.encoded + 7) >> 3; |
| } |
| } else { |
| errno = ENOENT; /* Transfer syntax is not defined for this type. */ |
| ASN__ENCODE_FAILED; |
| } |
| break; |
| case ATS_ALIGNED_BASIC_PER: |
| /* CANONICAL-APER is a superset of BASIC-APER. */ |
| /* Fall through. */ |
| case ATS_ALIGNED_CANONICAL_PER: |
| if(td->op->aper_encoder) { |
| er = aper_encode(td, 0, sptr, callback, callback_key); |
| if(er.encoded == -1) { |
| if(er.failed_type && er.failed_type->op->aper_encoder) { |
| errno = EBADF; /* Structure has incorrect form. */ |
| } else { |
| errno = ENOENT; /* APER is not defined for this type. */ |
| } |
| } else { |
| ASN_DEBUG("Complete encoded in %ld bits", (long)er.encoded); |
| if(er.encoded == 0) { |
| /* Enforce "Complete Encoding" of X.691 #11.1 */ |
| if(callback("\0", 1, callback_key) < 0) { |
| errno = EBADF; |
| ASN__ENCODE_FAILED; |
| } |
| er.encoded = 8; /* Exactly 8 zero bits is added. */ |
| } |
| /* Convert bits into bytes */ |
| er.encoded = (er.encoded + 7) >> 3; |
| } |
| } else { |
| errno = ENOENT; /* Transfer syntax is not defined for this type. */ |
| ASN__ENCODE_FAILED; |
| } |
| break; |
| #endif /* ASN_DISABLE_PER_SUPPORT */ |
| |
| case ATS_BASIC_XER: |
| /* CANONICAL-XER is a superset of BASIC-XER. */ |
| xer_flags &= ~XER_F_CANONICAL; |
| xer_flags |= XER_F_BASIC; |
| /* Fall through. */ |
| case ATS_CANONICAL_XER: |
| if(td->op->xer_encoder) { |
| er = xer_encode(td, sptr, xer_flags, callback, callback_key); |
| if(er.encoded == -1) { |
| if(er.failed_type && er.failed_type->op->xer_encoder) { |
| errno = EBADF; /* Structure has incorrect form. */ |
| } else { |
| errno = ENOENT; /* XER is not defined for this type. */ |
| } |
| } |
| } else { |
| errno = ENOENT; /* Transfer syntax is not defined for this type. */ |
| ASN__ENCODE_FAILED; |
| } |
| break; |
| |
| default: |
| errno = ENOENT; |
| ASN__ENCODE_FAILED; |
| } |
| |
| return er; |
| } |
| |
| asn_dec_rval_t |
| asn_decode(const asn_codec_ctx_t *opt_codec_ctx, |
| enum asn_transfer_syntax syntax, const asn_TYPE_descriptor_t *td, |
| void **sptr, const void *buffer, size_t size) { |
| if(!td || !td->op || !sptr || (size && !buffer)) { |
| ASN__DECODE_FAILED; |
| } |
| |
| switch(syntax) { |
| case ATS_CER: |
| case ATS_NONSTANDARD_PLAINTEXT: |
| default: |
| errno = ENOENT; |
| ASN__DECODE_FAILED; |
| |
| case ATS_RANDOM: |
| if(!td->op->random_fill) { |
| ASN__DECODE_FAILED; |
| } else { |
| if(asn_random_fill(td, sptr, 16000) == 0) { |
| asn_dec_rval_t ret = {RC_OK, 0}; |
| return ret; |
| } else { |
| ASN__DECODE_FAILED; |
| } |
| } |
| break; |
| |
| case ATS_DER: |
| case ATS_BER: |
| return ber_decode(opt_codec_ctx, td, sptr, buffer, size); |
| |
| case ATS_BASIC_OER: |
| case ATS_CANONICAL_OER: |
| #ifdef ASN_DISABLE_OER_SUPPORT |
| errno = ENOENT; |
| ASN__DECODE_FAILED; |
| #else |
| return oer_decode(opt_codec_ctx, td, sptr, buffer, size); |
| #endif |
| |
| case ATS_UNALIGNED_BASIC_PER: |
| case ATS_UNALIGNED_CANONICAL_PER: |
| #ifdef ASN_DISABLE_PER_SUPPORT |
| errno = ENOENT; |
| ASN__DECODE_FAILED; |
| #else |
| return uper_decode_complete(opt_codec_ctx, td, sptr, buffer, size); |
| #endif |
| |
| case ATS_ALIGNED_BASIC_PER: |
| case ATS_ALIGNED_CANONICAL_PER: |
| #ifdef ASN_DISABLE_PER_SUPPORT |
| errno = ENOENT; |
| ASN__DECODE_FAILED; |
| #else |
| return aper_decode_complete(opt_codec_ctx, td, sptr, buffer, size); |
| #endif |
| |
| case ATS_BASIC_XER: |
| case ATS_CANONICAL_XER: |
| return xer_decode(opt_codec_ctx, td, sptr, buffer, size); |
| } |
| } |
| |