X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/4271ab433cd55bbd2612292bcf39e4dc3d7274f1..0c097ef3435d2a45c1ee4ac80f8bd3f254fb8df5:/libs/smux/INTEGER.c?ds=inline diff --git a/libs/smux/INTEGER.c b/libs/smux/INTEGER.c index eed82176..2b43cdf7 100644 --- a/libs/smux/INTEGER.c +++ b/libs/smux/INTEGER.c @@ -1,5 +1,5 @@ -/*- - * Copyright (c) 2003-2014 Lev Walkin . +/* + * Copyright (c) 2003-2019 Lev Walkin . * All rights reserved. * Redistribution and modifications are permitted subject to BSD license. */ @@ -14,16 +14,21 @@ static const ber_tlv_tag_t asn_DEF_INTEGER_tags[] = { (ASN_TAG_CLASS_UNIVERSAL | (2 << 2)) }; -asn_TYPE_descriptor_t asn_DEF_INTEGER = { - "INTEGER", - "INTEGER", - ASN__PRIMITIVE_TYPE_free, +asn_TYPE_operation_t asn_OP_INTEGER = { + INTEGER_free, INTEGER_print, - asn_generic_no_constraint, + INTEGER_compare, ber_decode_primitive, INTEGER_encode_der, INTEGER_decode_xer, INTEGER_encode_xer, +#ifdef ASN_DISABLE_OER_SUPPORT + 0, + 0, +#else + INTEGER_decode_oer, /* OER decoder */ + INTEGER_encode_oer, /* Canonical OER encoder */ +#endif /* ASN_DISABLE_OER_SUPPORT */ #ifdef ASN_DISABLE_PER_SUPPORT 0, 0, @@ -31,12 +36,18 @@ asn_TYPE_descriptor_t asn_DEF_INTEGER = { INTEGER_decode_uper, /* Unaligned PER decoder */ INTEGER_encode_uper, /* Unaligned PER encoder */ #endif /* ASN_DISABLE_PER_SUPPORT */ - 0, /* Use generic outmost tag fetcher */ + INTEGER_random_fill, + 0 /* Use generic outmost tag fetcher */ +}; +asn_TYPE_descriptor_t asn_DEF_INTEGER = { + "INTEGER", + "INTEGER", + &asn_OP_INTEGER, asn_DEF_INTEGER_tags, sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]), asn_DEF_INTEGER_tags, /* Same as above */ sizeof(asn_DEF_INTEGER_tags) / sizeof(asn_DEF_INTEGER_tags[0]), - 0, /* No PER visible constraints */ + { 0, 0, asn_generic_no_constraint }, 0, 0, /* No members */ 0 /* No specifics */ }; @@ -45,10 +56,12 @@ asn_TYPE_descriptor_t asn_DEF_INTEGER = { * Encode INTEGER type using DER. */ asn_enc_rval_t -INTEGER_encode_der(asn_TYPE_descriptor_t *td, void *sptr, - int tag_mode, ber_tlv_tag_t tag, - asn_app_consume_bytes_f *cb, void *app_key) { - INTEGER_t *st = (INTEGER_t *)sptr; +INTEGER_encode_der(const asn_TYPE_descriptor_t *td, const void *sptr, + int tag_mode, ber_tlv_tag_t tag, asn_app_consume_bytes_f *cb, + void *app_key) { + const INTEGER_t *st = (const INTEGER_t *)sptr; + asn_enc_rval_t rval; + INTEGER_t effective_integer; ASN_DEBUG("%s %s as INTEGER (tm=%d)", cb?"Encoding":"Estimating", td->name, tag_mode); @@ -85,58 +98,60 @@ INTEGER_encode_der(asn_TYPE_descriptor_t *td, void *sptr, /* Remove leading superfluous bytes from the integer */ shift = buf - st->buf; if(shift) { - uint8_t *nb = st->buf; - uint8_t *end; - - st->size -= shift; /* New size, minus bad bytes */ - end = nb + st->size; - - for(; nb < end; nb++, buf++) - *nb = *buf; - } - - } /* if(1) */ + union { + const uint8_t *c_buf; + uint8_t *nc_buf; + } unconst; + unconst.c_buf = st->buf; + effective_integer.buf = unconst.nc_buf + shift; + effective_integer.size = st->size - shift; + + st = &effective_integer; + } + } - return der_encode_primitive(td, sptr, tag_mode, tag, cb, app_key); + rval = der_encode_primitive(td, st, tag_mode, tag, cb, app_key); + if(rval.structure_ptr == &effective_integer) { + rval.structure_ptr = sptr; + } + return rval; } -static const asn_INTEGER_enum_map_t *INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop); +static const asn_INTEGER_enum_map_t *INTEGER_map_enum2value( + const asn_INTEGER_specifics_t *specs, const char *lstart, + const char *lstop); /* * INTEGER specific human-readable output. */ static ssize_t INTEGER__dump(const asn_TYPE_descriptor_t *td, const INTEGER_t *st, asn_app_consume_bytes_f *cb, void *app_key, int plainOrXER) { - asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; - char scratch[32]; /* Enough for 64-bit integer */ + const asn_INTEGER_specifics_t *specs = + (const asn_INTEGER_specifics_t *)td->specifics; + char scratch[32]; uint8_t *buf = st->buf; uint8_t *buf_end = st->buf + st->size; - signed long value; + intmax_t value; ssize_t wrote = 0; char *p; int ret; if(specs && specs->field_unsigned) - ret = asn_INTEGER2ulong(st, (unsigned long *)&value); + ret = asn_INTEGER2umax(st, (uintmax_t *)&value); else - ret = asn_INTEGER2long(st, &value); + ret = asn_INTEGER2imax(st, &value); /* Simple case: the integer size is small */ if(ret == 0) { const asn_INTEGER_enum_map_t *el; - size_t scrsize; - char *scr; - el = (value >= 0 || !specs || !specs->field_unsigned) ? INTEGER_map_value2enum(specs, value) : 0; if(el) { - scrsize = el->enum_len + 32; - scr = (char *)alloca(scrsize); if(plainOrXER == 0) - ret = snprintf(scr, scrsize, - "%ld (%s)", value, el->enum_name); + return asn__format_to_callback(cb, app_key, + "%" ASN_PRIdMAX " (%s)", value, el->enum_name); else - ret = snprintf(scr, scrsize, + return asn__format_to_callback(cb, app_key, "<%s/>", el->enum_name); } else if(plainOrXER && specs && specs->strict_enumeration) { ASN_DEBUG("ASN.1 forbids dealing with " @@ -144,14 +159,12 @@ INTEGER__dump(const asn_TYPE_descriptor_t *td, const INTEGER_t *st, asn_app_cons errno = EPERM; return -1; } else { - scrsize = sizeof(scratch); - scr = scratch; - ret = snprintf(scr, scrsize, - (specs && specs->field_unsigned) - ?"%lu":"%ld", value); - } - assert(ret > 0 && (size_t)ret < scrsize); - return (cb(scr, ret, app_key) < 0) ? -1 : ret; + return asn__format_to_callback(cb, app_key, + (specs && specs->field_unsigned) + ? "%" ASN_PRIuMAX + : "%" ASN_PRIdMAX, + value); + } } else if(plainOrXER && specs && specs->strict_enumeration) { /* * Here and earlier, we cannot encode the ENUMERATED values @@ -189,12 +202,11 @@ INTEGER__dump(const asn_TYPE_descriptor_t *td, const INTEGER_t *st, asn_app_cons * INTEGER specific human-readable output. */ int -INTEGER_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, - asn_app_consume_bytes_f *cb, void *app_key) { - const INTEGER_t *st = (const INTEGER_t *)sptr; +INTEGER_print(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, + asn_app_consume_bytes_f *cb, void *app_key) { + const INTEGER_t *st = (const INTEGER_t *)sptr; ssize_t ret; - (void)td; (void)ilevel; if(!st || !st->buf) @@ -223,7 +235,7 @@ INTEGER__compar_enum2value(const void *kp, const void *am) { /* Compare strings */ for(ptr = key->start, end = key->stop, name = el->enum_name; ptr < end; ptr++, name++) { - if(*ptr != *name) + if(*ptr != *name || !*name) return *(const unsigned char *)ptr - *(const unsigned char *)name; } @@ -231,8 +243,9 @@ INTEGER__compar_enum2value(const void *kp, const void *am) { } static const asn_INTEGER_enum_map_t * -INTEGER_map_enum2value(asn_INTEGER_specifics_t *specs, const char *lstart, const char *lstop) { - const asn_INTEGER_enum_map_t *el_found; +INTEGER_map_enum2value(const asn_INTEGER_specifics_t *specs, const char *lstart, + const char *lstop) { + const asn_INTEGER_enum_map_t *el_found; int count = specs ? specs->map_count : 0; struct e2v_key key; const char *lp; @@ -279,7 +292,7 @@ INTEGER__compar_value2enum(const void *kp, const void *am) { } const asn_INTEGER_enum_map_t * -INTEGER_map_value2enum(asn_INTEGER_specifics_t *specs, long value) { +INTEGER_map_value2enum(const asn_INTEGER_specifics_t *specs, long value) { int count = specs ? specs->map_count : 0; if(!count) return 0; return (asn_INTEGER_enum_map_t *)bsearch(&value, specs->value2enum, @@ -305,10 +318,11 @@ INTEGER_st_prealloc(INTEGER_t *st, int min_size) { * Decode the chunk of XML text encoding INTEGER. */ static enum xer_pbd_rval -INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) { - INTEGER_t *st = (INTEGER_t *)sptr; - long dec_value; - long hex_value = 0; +INTEGER__xer_body_decode(const asn_TYPE_descriptor_t *td, void *sptr, + const void *chunk_buf, size_t chunk_size) { + INTEGER_t *st = (INTEGER_t *)sptr; + intmax_t dec_value; + intmax_t hex_value = 0; const char *lp; const char *lstart = (const char *)chunk_buf; const char *lstop = lstart + chunk_size; @@ -407,7 +421,7 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chun if(state == ST_LEADSPACE) { const asn_INTEGER_enum_map_t *el; el = INTEGER_map_enum2value( - (asn_INTEGER_specifics_t *) + (const asn_INTEGER_specifics_t *) td->specifics, lstart, lstop); if(el) { ASN_DEBUG("Found \"%s\" => %ld", @@ -485,14 +499,24 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chun /* FALL THROUGH */ case ST_DIGITS_TRAILSPACE: /* The last symbol encountered was a digit. */ - switch(asn_strtol_lim(dec_value_start, &dec_value_end, &dec_value)) { - case ASN_STRTOL_OK: - break; - case ASN_STRTOL_ERROR_RANGE: - return XPBD_DECODER_LIMIT; - case ASN_STRTOL_ERROR_INVAL: - case ASN_STRTOL_EXPECT_MORE: - case ASN_STRTOL_EXTRA_DATA: + switch(asn_strtoimax_lim(dec_value_start, &dec_value_end, &dec_value)) { + case ASN_STRTOX_OK: + if(dec_value >= LONG_MIN && dec_value <= LONG_MAX) { + break; + } else { + /* + * We model INTEGER on long for XER, + * to avoid rewriting all the tests at once. + */ + ASN_DEBUG("INTEGER exceeds long range"); + } + /* Fall through */ + case ASN_STRTOX_ERROR_RANGE: + ASN_DEBUG("INTEGER decode %s hit range limit", td->name); + return XPBD_DECODER_LIMIT; + case ASN_STRTOX_ERROR_INVAL: + case ASN_STRTOX_EXPECT_MORE: + case ASN_STRTOX_EXTRA_DATA: return XPBD_BROKEN_ENCODING; } break; @@ -517,27 +541,28 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chun * Convert the result of parsing of enumeration or a straight * decimal value into a BER representation. */ - if(asn_long2INTEGER(st, dec_value)) + if(asn_imax2INTEGER(st, dec_value)) { + ASN_DEBUG("INTEGER decode %s conversion failed", td->name); return XPBD_SYSTEM_FAILURE; + } return XPBD_BODY_CONSUMED; } asn_dec_rval_t -INTEGER_decode_xer(asn_codec_ctx_t *opt_codec_ctx, - asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, - const void *buf_ptr, size_t size) { - - return xer_decode_primitive(opt_codec_ctx, td, +INTEGER_decode_xer(const asn_codec_ctx_t *opt_codec_ctx, + const asn_TYPE_descriptor_t *td, void **sptr, + const char *opt_mname, const void *buf_ptr, size_t size) { + return xer_decode_primitive(opt_codec_ctx, td, sptr, sizeof(INTEGER_t), opt_mname, buf_ptr, size, INTEGER__xer_body_decode); } asn_enc_rval_t -INTEGER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, - int ilevel, enum xer_encoder_flags_e flags, - asn_app_consume_bytes_f *cb, void *app_key) { - const INTEGER_t *st = (const INTEGER_t *)sptr; +INTEGER_encode_xer(const asn_TYPE_descriptor_t *td, const void *sptr, + int ilevel, enum xer_encoder_flags_e flags, + asn_app_consume_bytes_f *cb, void *app_key) { + const INTEGER_t *st = (const INTEGER_t *)sptr; asn_enc_rval_t er; (void)ilevel; @@ -555,12 +580,15 @@ INTEGER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, #ifndef ASN_DISABLE_PER_SUPPORT asn_dec_rval_t -INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, - asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) { - asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; - asn_dec_rval_t rval = { RC_OK, 0 }; +INTEGER_decode_uper(const asn_codec_ctx_t *opt_codec_ctx, + const asn_TYPE_descriptor_t *td, + const asn_per_constraints_t *constraints, void **sptr, + asn_per_data_t *pd) { + const asn_INTEGER_specifics_t *specs = + (const asn_INTEGER_specifics_t *)td->specifics; + asn_dec_rval_t rval = { RC_OK, 0 }; INTEGER_t *st = (INTEGER_t *)*sptr; - asn_per_constraint_t *ct; + const asn_per_constraint_t *ct; int repeat; (void)opt_codec_ctx; @@ -570,7 +598,7 @@ INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, if(!st) ASN__DECODE_FAILED; } - if(!constraints) constraints = td->per_constraints; + if(!constraints) constraints = td->encoding_constraints.per_constraints; ct = constraints ? &constraints->value : 0; if(ct && ct->flags & APC_EXTENSIBLE) { @@ -604,7 +632,7 @@ INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, ASN__DECODE_FAILED; if(specs && specs->field_unsigned) { - unsigned long uvalue; + unsigned long uvalue = 0; if(uper_get_constrained_whole_number(pd, &uvalue, ct->range_bits)) ASN__DECODE_STARVED; @@ -614,15 +642,18 @@ INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, if(asn_ulong2INTEGER(st, uvalue)) ASN__DECODE_FAILED; } else { - unsigned long svalue; + unsigned long uvalue = 0; + long svalue; if(uper_get_constrained_whole_number(pd, - &svalue, ct->range_bits)) + &uvalue, ct->range_bits)) ASN__DECODE_STARVED; - ASN_DEBUG("Got value %ld + low %ld", - svalue, ct->lower_bound); - svalue += ct->lower_bound; - if(asn_long2INTEGER(st, svalue)) - ASN__DECODE_FAILED; + ASN_DEBUG("Got value %lu + low %ld", + uvalue, ct->lower_bound); + if(per_long_range_unrebase(uvalue, ct->lower_bound, + ct->upper_bound, &svalue) + || asn_long2INTEGER(st, svalue)) { + ASN__DECODE_FAILED; + } } return rval; } @@ -632,12 +663,12 @@ INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, /* X.691, #12.2.3, #12.2.4 */ do { - ssize_t len; - void *p; - int ret; + ssize_t len = 0; + void *p = NULL; + int ret = 0; /* Get the PER length */ - len = uper_get_length(pd, -1, &repeat); + len = uper_get_length(pd, -1, 0, &repeat); if(len < 0) ASN__DECODE_STARVED; p = REALLOC(st->buf, st->size + len + 1); @@ -655,10 +686,10 @@ INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, /* * TODO: replace by in-place arithmetics. */ - long value; + long value = 0; if(asn_INTEGER2long(st, &value)) ASN__DECODE_FAILED; - if(asn_long2INTEGER(st, value + ct->lower_bound)) + if(asn_imax2INTEGER(st, value + ct->lower_bound)) ASN__DECODE_FAILED; } @@ -666,20 +697,21 @@ INTEGER_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, } asn_enc_rval_t -INTEGER_encode_uper(asn_TYPE_descriptor_t *td, - asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) { - asn_INTEGER_specifics_t *specs=(asn_INTEGER_specifics_t *)td->specifics; - asn_enc_rval_t er; - INTEGER_t *st = (INTEGER_t *)sptr; +INTEGER_encode_uper(const asn_TYPE_descriptor_t *td, + const asn_per_constraints_t *constraints, const void *sptr, + asn_per_outp_t *po) { + const asn_INTEGER_specifics_t *specs = + (const asn_INTEGER_specifics_t *)td->specifics; + asn_enc_rval_t er; + const INTEGER_t *st = (const INTEGER_t *)sptr; const uint8_t *buf; const uint8_t *end; - asn_per_constraint_t *ct; + const asn_per_constraint_t *ct; long value = 0; - unsigned long v = 0; if(!st || st->size == 0) ASN__ENCODE_FAILED; - if(!constraints) constraints = td->per_constraints; + if(!constraints) constraints = td->encoding_constraints.per_constraints; ct = constraints ? &constraints->value : 0; er.encoded = 0; @@ -699,7 +731,7 @@ INTEGER_encode_uper(asn_TYPE_descriptor_t *td, || uval > (unsigned long)ct->upper_bound) inext = 1; } - ASN_DEBUG("Value %lu (%02x/%d) lb %lu ub %lu %s", + ASN_DEBUG("Value %lu (%02x/%" ASN_PRI_SIZE ") lb %lu ub %lu %s", uval, st->buf[0], st->size, ct->lower_bound, ct->upper_bound, inext ? "ext" : "fix"); @@ -716,7 +748,7 @@ INTEGER_encode_uper(asn_TYPE_descriptor_t *td, || value > ct->upper_bound) inext = 1; } - ASN_DEBUG("Value %ld (%02x/%d) lb %ld ub %ld %s", + ASN_DEBUG("Value %ld (%02x/%" ASN_PRI_SIZE ") lb %ld ub %ld %s", value, st->buf[0], st->size, ct->lower_bound, ct->upper_bound, inext ? "ext" : "fix"); @@ -733,12 +765,15 @@ INTEGER_encode_uper(asn_TYPE_descriptor_t *td, /* X.691-11/2008, #13.2.2, test if constrained whole number */ if(ct && ct->range_bits >= 0) { + unsigned long v; /* #11.5.6 -> #11.3 */ ASN_DEBUG("Encoding integer %ld (%lu) with range %d bits", value, value - ct->lower_bound, ct->range_bits); - v = value - ct->lower_bound; - if(uper_put_constrained_whole_number_u(po, v, ct->range_bits)) - ASN__ENCODE_FAILED; + if(per_long_range_rebase(value, ct->lower_bound, ct->upper_bound, &v)) { + ASN__ENCODE_FAILED; + } + if(uper_put_constrained_whole_number_u(po, v, ct->range_bits)) + ASN__ENCODE_FAILED; ASN__ENCODED_OK(er); } @@ -749,24 +784,45 @@ INTEGER_encode_uper(asn_TYPE_descriptor_t *td, } for(buf = st->buf, end = st->buf + st->size; buf < end;) { - ssize_t mayEncode = uper_put_length(po, end - buf); - if(mayEncode < 0) + int need_eom = 0; + ssize_t mayEncode = uper_put_length(po, end - buf, &need_eom); + if(mayEncode < 0) ASN__ENCODE_FAILED; if(per_put_many_bits(po, buf, 8 * mayEncode)) ASN__ENCODE_FAILED; buf += mayEncode; - } + if(need_eom && uper_put_length(po, 0, 0)) ASN__ENCODE_FAILED; + } ASN__ENCODED_OK(er); } #endif /* ASN_DISABLE_PER_SUPPORT */ +static intmax_t +asn__integer_convert(const uint8_t *b, const uint8_t *end) { + uintmax_t value; + + /* Perform the sign initialization */ + /* Actually value = -(*b >> 7); gains nothing, yet unreadable! */ + if((*b >> 7)) { + value = (uintmax_t)(-1); + } else { + value = 0; + } + + /* Conversion engine */ + for(; b < end; b++) { + value = (value << 8) | *b; + } + + return value; +} + int -asn_INTEGER2long(const INTEGER_t *iptr, long *lptr) { +asn_INTEGER2imax(const INTEGER_t *iptr, intmax_t *lptr) { uint8_t *b, *end; size_t size; - long l; /* Sanity checking */ if(!iptr || !iptr->buf || !lptr) { @@ -779,26 +835,26 @@ asn_INTEGER2long(const INTEGER_t *iptr, long *lptr) { size = iptr->size; end = b + size; /* Where to stop */ - if(size > sizeof(long)) { + if(size > sizeof(intmax_t)) { uint8_t *end1 = end - 1; /* * Slightly more advanced processing, - * able to >sizeof(long) bytes, - * when the actual value is small - * (0x0000000000abcdef would yield a fine 0x00abcdef) + * able to process INTEGERs with >sizeof(intmax_t) bytes + * when the actual value is small, e.g. for intmax_t == int32_t + * (0x0000000000abcdef INTEGER would yield a fine 0x00abcdef int32_t) */ /* Skip out the insignificant leading bytes */ for(; b < end1; b++) { switch(*b) { - case 0x00: if((b[1] & 0x80) == 0) continue; break; - case 0xff: if((b[1] & 0x80) != 0) continue; break; + case 0x00: if((b[1] & 0x80) == 0) continue; break; + case 0xff: if((b[1] & 0x80) != 0) continue; break; } break; } size = end - b; - if(size > sizeof(long)) { - /* Still cannot fit the long */ + if(size > sizeof(intmax_t)) { + /* Still cannot fit the sizeof(intmax_t) */ errno = ERANGE; return -1; } @@ -810,22 +866,15 @@ asn_INTEGER2long(const INTEGER_t *iptr, long *lptr) { return 0; } - /* Perform the sign initialization */ - /* Actually l = -(*b >> 7); gains nothing, yet unreadable! */ - if((*b >> 7)) l = -1; else l = 0; - - /* Conversion engine */ - for(; b < end; b++) - l = (l << 8) | *b; - - *lptr = l; + *lptr = asn__integer_convert(b, end); return 0; } +/* FIXME: negative INTEGER values are silently interpreted as large unsigned ones. */ int -asn_INTEGER2ulong(const INTEGER_t *iptr, unsigned long *lptr) { +asn_INTEGER2umax(const INTEGER_t *iptr, uintmax_t *lptr) { uint8_t *b, *end; - unsigned long l; + uintmax_t value; size_t size; if(!iptr || !iptr->buf || !lptr) { @@ -838,49 +887,50 @@ asn_INTEGER2ulong(const INTEGER_t *iptr, unsigned long *lptr) { end = b + size; /* If all extra leading bytes are zeroes, ignore them */ - for(; size > sizeof(unsigned long); b++, size--) { + for(; size > sizeof(value); b++, size--) { if(*b) { - /* Value won't fit unsigned long */ + /* Value won't fit into uintmax_t */ errno = ERANGE; return -1; } } /* Conversion engine */ - for(l = 0; b < end; b++) - l = (l << 8) | *b; + for(value = 0; b < end; b++) + value = (value << 8) | *b; - *lptr = l; + *lptr = value; return 0; } int -asn_ulong2INTEGER(INTEGER_t *st, unsigned long value) { - uint8_t *buf; - uint8_t *end; - uint8_t *b; - int shr; - - if(value <= LONG_MAX) - return asn_long2INTEGER(st, value); +asn_umax2INTEGER(INTEGER_t *st, uintmax_t value) { + uint8_t *buf; + uint8_t *end; + uint8_t *b; + int shr; + + if(value <= ((~(uintmax_t)0) >> 1)) { + return asn_imax2INTEGER(st, value); + } - buf = (uint8_t *)MALLOC(1 + sizeof(value)); - if(!buf) return -1; + buf = (uint8_t *)MALLOC(1 + sizeof(value)); + if(!buf) return -1; - end = buf + (sizeof(value) + 1); - buf[0] = 0; - for(b = buf + 1, shr = (sizeof(long)-1)*8; b < end; shr -= 8, b++) - *b = (uint8_t)(value >> shr); + end = buf + (sizeof(value) + 1); + buf[0] = 0; /* INTEGERs are signed. 0-byte indicates positive. */ + for(b = buf + 1, shr = (sizeof(value) - 1) * 8; b < end; shr -= 8, b++) + *b = (uint8_t)(value >> shr); - if(st->buf) FREEMEM(st->buf); - st->buf = buf; - st->size = 1 + sizeof(value); + if(st->buf) FREEMEM(st->buf); + st->buf = buf; + st->size = 1 + sizeof(value); return 0; } int -asn_long2INTEGER(INTEGER_t *st, long value) { +asn_imax2INTEGER(INTEGER_t *st, intmax_t value) { uint8_t *buf, *bp; uint8_t *p; uint8_t *pstart; @@ -893,7 +943,7 @@ asn_long2INTEGER(INTEGER_t *st, long value) { return -1; } - buf = (uint8_t *)MALLOC(sizeof(value)); + buf = (uint8_t *)(long *)MALLOC(sizeof(value)); if(!buf) return -1; if(*(char *)&littleEndian) { @@ -924,7 +974,7 @@ asn_long2INTEGER(INTEGER_t *st, long value) { break; } /* Copy the integer body */ - for(pstart = p, bp = buf, pend1 += add; p != pend1; p += add) + for(bp = buf, pend1 += add; p != pend1; p += add) *bp++ = *p; if(st->buf) FREEMEM(st->buf); @@ -934,92 +984,370 @@ asn_long2INTEGER(INTEGER_t *st, long value) { return 0; } +int +asn_INTEGER2long(const INTEGER_t *iptr, long *l) { + intmax_t v; + if(asn_INTEGER2imax(iptr, &v) == 0) { + if(v < LONG_MIN || v > LONG_MAX) { + errno = ERANGE; + return -1; + } + *l = v; + return 0; + } else { + return -1; + } +} + +int +asn_INTEGER2ulong(const INTEGER_t *iptr, unsigned long *l) { + uintmax_t v; + if(asn_INTEGER2umax(iptr, &v) == 0) { + if(v > ULONG_MAX) { + errno = ERANGE; + return -1; + } + *l = v; + return 0; + } else { + return -1; + } +} + +int +asn_long2INTEGER(INTEGER_t *st, long value) { + return asn_imax2INTEGER(st, value); +} + +int +asn_ulong2INTEGER(INTEGER_t *st, unsigned long value) { + return asn_imax2INTEGER(st, value); +} + /* - * This function is going to be DEPRECATED soon. + * Parse the number in the given string until the given *end position, + * returning the position after the last parsed character back using the + * same (*end) pointer. + * WARNING: This behavior is different from the standard strtol/strtoimax(3). */ -enum asn_strtol_result_e -asn_strtol(const char *str, const char *end, long *lp) { - const char *endp = end; - - switch(asn_strtol_lim(str, &endp, lp)) { - case ASN_STRTOL_ERROR_RANGE: - return ASN_STRTOL_ERROR_RANGE; - case ASN_STRTOL_ERROR_INVAL: - return ASN_STRTOL_ERROR_INVAL; - case ASN_STRTOL_EXPECT_MORE: - return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ - case ASN_STRTOL_OK: - return ASN_STRTOL_OK; - case ASN_STRTOL_EXTRA_DATA: - return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ +enum asn_strtox_result_e +asn_strtoimax_lim(const char *str, const char **end, intmax_t *intp) { + int sign = 1; + intmax_t value; + + const intmax_t asn1_intmax_max = ((~(uintmax_t)0) >> 1); + const intmax_t upper_boundary = asn1_intmax_max / 10; + intmax_t last_digit_max = asn1_intmax_max % 10; + + if(str >= *end) return ASN_STRTOX_ERROR_INVAL; + + switch(*str) { + case '-': + last_digit_max++; + sign = -1; + /* FALL THROUGH */ + case '+': + str++; + if(str >= *end) { + *end = str; + return ASN_STRTOX_EXPECT_MORE; + } + } + + for(value = 0; str < (*end); str++) { + if(*str >= 0x30 && *str <= 0x39) { + int d = *str - '0'; + if(value < upper_boundary) { + value = value * 10 + d; + } else if(value == upper_boundary) { + if(d <= last_digit_max) { + if(sign > 0) { + value = value * 10 + d; + } else { + sign = 1; + value = -value * 10 - d; + } + str += 1; + if(str < *end) { + // If digits continue, we're guaranteed out of range. + *end = str; + if(*str >= 0x30 && *str <= 0x39) { + return ASN_STRTOX_ERROR_RANGE; + } else { + *intp = sign * value; + return ASN_STRTOX_EXTRA_DATA; + } + } + break; + } else { + *end = str; + return ASN_STRTOX_ERROR_RANGE; + } + } else { + *end = str; + return ASN_STRTOX_ERROR_RANGE; + } + } else { + *end = str; + *intp = sign * value; + return ASN_STRTOX_EXTRA_DATA; + } } - return ASN_STRTOL_ERROR_INVAL; /* Retain old behavior */ + *end = str; + *intp = sign * value; + return ASN_STRTOX_OK; } /* * Parse the number in the given string until the given *end position, * returning the position after the last parsed character back using the * same (*end) pointer. - * WARNING: This behavior is different from the standard strtol(3). + * WARNING: This behavior is different from the standard strtoul/strtoumax(3). */ -enum asn_strtol_result_e +enum asn_strtox_result_e +asn_strtoumax_lim(const char *str, const char **end, uintmax_t *uintp) { + uintmax_t value; + + const uintmax_t asn1_uintmax_max = ((~(uintmax_t)0)); + const uintmax_t upper_boundary = asn1_uintmax_max / 10; + uintmax_t last_digit_max = asn1_uintmax_max % 10; + + if(str >= *end) return ASN_STRTOX_ERROR_INVAL; + + switch(*str) { + case '-': + return ASN_STRTOX_ERROR_INVAL; + case '+': + str++; + if(str >= *end) { + *end = str; + return ASN_STRTOX_EXPECT_MORE; + } + } + + for(value = 0; str < (*end); str++) { + if(*str >= 0x30 && *str <= 0x39) { + unsigned int d = *str - '0'; + if(value < upper_boundary) { + value = value * 10 + d; + } else if(value == upper_boundary) { + if(d <= last_digit_max) { + value = value * 10 + d; + str += 1; + if(str < *end) { + // If digits continue, we're guaranteed out of range. + *end = str; + if(*str >= 0x30 && *str <= 0x39) { + return ASN_STRTOX_ERROR_RANGE; + } else { + *uintp = value; + return ASN_STRTOX_EXTRA_DATA; + } + } + break; + } else { + *end = str; + return ASN_STRTOX_ERROR_RANGE; + } + } else { + *end = str; + return ASN_STRTOX_ERROR_RANGE; + } + } else { + *end = str; + *uintp = value; + return ASN_STRTOX_EXTRA_DATA; + } + } + + *end = str; + *uintp = value; + return ASN_STRTOX_OK; +} + +enum asn_strtox_result_e asn_strtol_lim(const char *str, const char **end, long *lp) { - int sign = 1; - long l; + intmax_t value; + switch(asn_strtoimax_lim(str, end, &value)) { + case ASN_STRTOX_ERROR_RANGE: + return ASN_STRTOX_ERROR_RANGE; + case ASN_STRTOX_ERROR_INVAL: + return ASN_STRTOX_ERROR_INVAL; + case ASN_STRTOX_EXPECT_MORE: + return ASN_STRTOX_EXPECT_MORE; + case ASN_STRTOX_OK: + if(value >= LONG_MIN && value <= LONG_MAX) { + *lp = value; + return ASN_STRTOX_OK; + } else { + return ASN_STRTOX_ERROR_RANGE; + } + case ASN_STRTOX_EXTRA_DATA: + if(value >= LONG_MIN && value <= LONG_MAX) { + *lp = value; + return ASN_STRTOX_EXTRA_DATA; + } else { + return ASN_STRTOX_ERROR_RANGE; + } + } - const long upper_boundary = LONG_MAX / 10; - long last_digit_max = LONG_MAX % 10; + assert(!"Unreachable"); + return ASN_STRTOX_ERROR_INVAL; +} - if(str >= *end) return ASN_STRTOL_ERROR_INVAL; +enum asn_strtox_result_e +asn_strtoul_lim(const char *str, const char **end, unsigned long *ulp) { + uintmax_t value; + switch(asn_strtoumax_lim(str, end, &value)) { + case ASN_STRTOX_ERROR_RANGE: + return ASN_STRTOX_ERROR_RANGE; + case ASN_STRTOX_ERROR_INVAL: + return ASN_STRTOX_ERROR_INVAL; + case ASN_STRTOX_EXPECT_MORE: + return ASN_STRTOX_EXPECT_MORE; + case ASN_STRTOX_OK: + if(value <= ULONG_MAX) { + *ulp = value; + return ASN_STRTOX_OK; + } else { + return ASN_STRTOX_ERROR_RANGE; + } + case ASN_STRTOX_EXTRA_DATA: + if(value <= ULONG_MAX) { + *ulp = value; + return ASN_STRTOX_EXTRA_DATA; + } else { + return ASN_STRTOX_ERROR_RANGE; + } + } - switch(*str) { - case '-': - last_digit_max++; - sign = -1; - /* FALL THROUGH */ - case '+': - str++; - if(str >= *end) { - *end = str; - return ASN_STRTOL_EXPECT_MORE; - } - } + assert(!"Unreachable"); + return ASN_STRTOX_ERROR_INVAL; +} - for(l = 0; str < (*end); str++) { - switch(*str) { - case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: - case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: { - int d = *str - '0'; - if(l < upper_boundary) { - l = l * 10 + d; - } else if(l == upper_boundary) { - if(d <= last_digit_max) { - if(sign > 0) { - l = l * 10 + d; - } else { - sign = 1; - l = -l * 10 - d; - } - } else { - *end = str; - return ASN_STRTOL_ERROR_RANGE; - } - } else { - *end = str; - return ASN_STRTOL_ERROR_RANGE; - } - } - continue; - default: - *end = str; - *lp = sign * l; - return ASN_STRTOL_EXTRA_DATA; - } - } +int +INTEGER_compare(const asn_TYPE_descriptor_t *td, const void *aptr, + const void *bptr) { + const INTEGER_t *a = aptr; + const INTEGER_t *b = bptr; + + (void)td; + + if(a && b) { + if(a->size && b->size) { + int sign_a = (a->buf[0] & 0x80) ? -1 : 1; + int sign_b = (b->buf[0] & 0x80) ? -1 : 1; + + if(sign_a < sign_b) return -1; + if(sign_a > sign_b) return 1; + + /* The shortest integer wins, unless comparing negatives */ + if(a->size < b->size) { + return -1 * sign_a; + } else if(a->size > b->size) { + return 1 * sign_b; + } + + return sign_a * memcmp(a->buf, b->buf, a->size); + } else if(a->size) { + int sign = (a->buf[0] & 0x80) ? -1 : 1; + return (1) * sign; + } else if(b->size) { + int sign = (a->buf[0] & 0x80) ? -1 : 1; + return (-1) * sign; + } else { + return 0; + } + } else if(!a && !b) { + return 0; + } else if(!a) { + return -1; + } else { + return 1; + } - *end = str; - *lp = sign * l; - return ASN_STRTOL_OK; } +asn_random_fill_result_t +INTEGER_random_fill(const asn_TYPE_descriptor_t *td, void **sptr, + const asn_encoding_constraints_t *constraints, + size_t max_length) { + const asn_INTEGER_specifics_t *specs = + (const asn_INTEGER_specifics_t *)td->specifics; + asn_random_fill_result_t result_ok = {ARFILL_OK, 1}; + asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0}; + asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0}; + INTEGER_t *st = *sptr; + const asn_INTEGER_enum_map_t *emap; + size_t emap_len; + intmax_t value; + int find_inside_map; + + if(max_length == 0) return result_skipped; + + if(st == NULL) { + st = (INTEGER_t *)CALLOC(1, sizeof(*st)); + if(st == NULL) { + return result_failed; + } + } + + if(specs) { + emap = specs->value2enum; + emap_len = specs->map_count; + if(specs->strict_enumeration) { + find_inside_map = emap_len > 0; + } else { + find_inside_map = emap_len ? asn_random_between(0, 1) : 0; + } + } else { + emap = 0; + emap_len = 0; + find_inside_map = 0; + } + + if(find_inside_map) { + assert(emap_len > 0); + value = emap[asn_random_between(0, emap_len - 1)].nat_value; + } else { + const asn_per_constraints_t *ct; + + static const long variants[] = { + -65536, -65535, -65534, -32769, -32768, -32767, -16385, -16384, + -16383, -257, -256, -255, -254, -129, -128, -127, + -126, -1, 0, 1, 126, 127, 128, 129, + 254, 255, 256, 257, 16383, 16384, 16385, 32767, + 32768, 32769, 65534, 65535, 65536, 65537}; + if(specs && specs->field_unsigned) { + assert(variants[18] == 0); + value = variants[asn_random_between( + 18, sizeof(variants) / sizeof(variants[0]) - 1)]; + } else { + value = variants[asn_random_between( + 0, sizeof(variants) / sizeof(variants[0]) - 1)]; + } + + if(!constraints) constraints = &td->encoding_constraints; + ct = constraints ? constraints->per_constraints : 0; + if(ct && (ct->value.flags & APC_CONSTRAINED)) { + if(value < ct->value.lower_bound || value > ct->value.upper_bound) { + value = asn_random_between(ct->value.lower_bound, + ct->value.upper_bound); + } + } + } + + if(asn_imax2INTEGER(st, value)) { + if(st == *sptr) { + ASN_STRUCT_RESET(*td, st); + } else { + ASN_STRUCT_FREE(*td, st); + } + return result_failed; + } else { + *sptr = st; + result_ok.length = st->size; + return result_ok; + } +}