/*- * Copyright (c) 2015--2018 Taylor R. Campbell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(__NetBSD__) && defined(_KERNEL) #include #include #include #else #include #include #include #endif #include #include #include #include "pb_prop_decode.h" #define PB_ABI_VERSION 0x00000000 static int pb_prop_decode_by_hdr(struct pb_msg_hdr *, const struct pb_prop_msgdesc *, prop_object_t); static int pb_prop_decode_at(unsigned char *, const struct pb_prop_msgdesc *, prop_object_t); static int pb_prop_decode_array(unsigned char *, const struct pb_prop_array *, prop_array_t); static int pb_prop_decode_record(unsigned char *, const struct pb_prop_record *, prop_dictionary_t); static int pb_prop_decode_record_field(unsigned char *, const struct pb_prop_record_field *, prop_dictionary_t); static int pb_prop_decode_repeated(unsigned char *, const struct pb_prop_field *, prop_array_t); static int pb_prop_decode_field_value(unsigned char *, const struct pb_type *, const struct pb_prop_field *, prop_object_t); static int pb_prop_decode_enum(unsigned char *, const struct pb_prop_enumeration *, prop_object_t); int pb_prop_decode(struct pb_msg msg, prop_object_t obj) { const struct pb_prop_msgdesc *prop; struct pb_msg_hdr *const msg_hdr = (struct pb_msg_hdr *)msg.pbm_ptr; pb_assert(msg.pbm_msgdesc == msg_hdr->pbmh_msgdesc); if (!pb_abi_compatible(msg.pbm_msgdesc->pbmd_abi_version, PB_ABI_VERSION)) /* XXX pb_bug */ return EINVAL; prop = pb_get_format(msg.pbm_msgdesc, &pb_prop_format); if (prop == NULL) return EINVAL; return pb_prop_decode_by_hdr(msg_hdr, prop, obj); } static int pb_prop_decode_by_hdr(struct pb_msg_hdr *msg_hdr, const struct pb_prop_msgdesc *prop, prop_object_t obj) { pb_assert(msg_hdr->pbmh_msgdesc == prop->pbpm_msgdesc); return pb_prop_decode_at((unsigned char *)msg_hdr, prop, obj); } static int pb_prop_decode_at(unsigned char *ptr, const struct pb_prop_msgdesc *prop, prop_object_t obj) { switch (prop->pbpm_t) { case PB_PROP_MSG_ARRAY: if (prop_object_type(obj) != PROP_TYPE_ARRAY) return EIO; return pb_prop_decode_array(ptr, &prop->pbpm_u.array, obj); case PB_PROP_MSG_RECORD: if (prop_object_type(obj) != PROP_TYPE_DICTIONARY) return EIO; return pb_prop_decode_record(ptr, &prop->pbpm_u.record, obj); case PB_PROP_MSG_SPLICE: { const struct pb_field *field; const struct pb_prop_msgdesc *const prop0 = prop->pbpm_u.splice.pbps_msgdesc; pb_assert(prop->pbpm_msgdesc->pbmd_nfields == 1); field = &prop->pbpm_msgdesc->pbmd_fields[0]; pb_assert(field->pbf_quant == PBQ_REQUIRED); pb_assert(field->pbf_type.pbt_type == PB_TYPE_MSG); pb_assert(field->pbf_type.pbt_u.msg.msgdesc == prop0->pbpm_msgdesc); return pb_prop_decode_at(ptr + field->pbf_qu.required.offset, prop0, obj); } default: return EIO; } } static int pb_prop_decode_array(unsigned char *ptr, const struct pb_prop_array *arraydesc, prop_array_t arrayobj) { const struct pb_prop_field *field; pb_assert(arraydesc->pbpa_msgdesc->pbmd_nfields == 1); pb_assert(arraydesc->pbpa_msgdesc->pbmd_fields[0].pbf_quant == PBQ_REPEATED); field = arraydesc->pbpa_field; pb_assert(field->pbpf_msgdesc == arraydesc->pbpa_msgdesc); pb_assert(field->pbpf_fieldno == 0); return pb_prop_decode_repeated(ptr, field, arrayobj); } static int pb_prop_decode_record(unsigned char *ptr, const struct pb_prop_record *record, prop_dictionary_t dict) { size_t i; int error; for (i = 0; i < record->pbpr_nfields; i++) { error = pb_prop_decode_record_field(ptr, &record->pbpr_fields[i], dict); if (error) return error; } return 0; } static int pb_prop_decode_record_field(unsigned char *ptr, const struct pb_prop_record_field *rfield, prop_dictionary_t dict) { const struct pb_prop_field *pfield = &rfield->pbprf_field; const struct pb_field *field; const struct pb_type *type; prop_object_t value; int error; pb_assert(pfield->pbpf_fieldno < pfield->pbpf_msgdesc->pbmd_nfields); field = &pfield->pbpf_msgdesc->pbmd_fields[pfield->pbpf_fieldno]; type = &field->pbf_type; value = prop_dictionary_get(dict, rfield->pbprf_key); switch (field->pbf_quant) { case PBQ_REQUIRED: { const size_t offset = field->pbf_qu.required.offset; if (value == NULL) return EIO; return pb_prop_decode_field_value(ptr + offset, type, pfield, value); } case PBQ_OPTIONAL: { const size_t present_offset = field->pbf_qu.optional.present_offset; const size_t value_offset = field->pbf_qu.optional.value_offset; if (value == NULL) { *(bool *)(ptr + present_offset) = false; return 0; } error = pb_prop_decode_field_value(ptr + value_offset, type, pfield, value); if (error) { *(bool *)(ptr + present_offset) = false; return error; } *(bool *)(ptr + present_offset) = true; return 0; } case PBQ_REPEATED: if (value == NULL) return EIO; if (prop_object_type(value) != PROP_TYPE_ARRAY) return EIO; return pb_prop_decode_repeated(ptr, pfield, value); default: return EIO; /* XXX */ } } static int pb_prop_decode_repeated(unsigned char *ptr, const struct pb_prop_field *pfield, prop_array_t array) { const struct pb_field *field; struct pb_repeated *repeated; unsigned char *elemptr; size_t i, elemsize; int error; pb_assert(pfield->pbpf_fieldno < pfield->pbpf_msgdesc->pbmd_nfields); field = &pfield->pbpf_msgdesc->pbmd_fields[pfield->pbpf_fieldno]; repeated = (struct pb_repeated *)(ptr + field->pbf_qu.repeated.hdr_offset); elemsize = pb_type_size(&field->pbf_type); error = pb_repeated_alloc(repeated, prop_array_count(array)); if (error) return error; elemptr = *(void *const *)(ptr + field->pbf_qu.repeated.ptr_offset); for (i = 0; i < prop_array_count(array); i++) { const prop_object_t value = prop_array_get(array, i); pb_assert(value != NULL); error = pb_prop_decode_field_value(elemptr + i*elemsize, &field->pbf_type, pfield, value); if (error) return error; } return 0; } static int pb_prop_decode_field_value(unsigned char *ptr, const struct pb_type *type, const struct pb_prop_field *pfield, prop_object_t value) { int error; #define DECODE_INT(TYPE, SIZE, IF_U, IF_S) do \ { \ if (prop_object_type(value) != PROP_TYPE_NUMBER) \ return EIO; \ if ((SIZE) < prop_number_size(value)) \ return EIO; \ if (prop_number_unsigned(value)) \ IF_U; \ else \ IF_S; \ return 0; \ } while (0) #define DECODE_UINT(TYPE, SIZE) \ DECODE_INT(TYPE, SIZE, \ *(TYPE *)ptr = prop_number_unsigned_integer_value(value), \ do { \ if (prop_number_integer_value(value) < 0) \ return EIO; \ *(TYPE *)ptr = prop_number_integer_value(value); \ } while (0)) #define DECODE_SINT(TYPE, SIZE, MAX) \ DECODE_INT(TYPE, SIZE, \ do { \ if ((MAX) < prop_number_unsigned_integer_value(value)) \ return EIO; \ *(TYPE *)ptr = prop_number_unsigned_integer_value(value); \ } while (0), \ *(TYPE *)ptr = prop_number_integer_value(value)) switch (type->pbt_type) { case PB_TYPE_BOOL: if (prop_object_type(value) != PROP_TYPE_BOOL) return EIO; *(bool *)ptr = prop_bool_true(value); return 0; case PB_TYPE_UINT32: case PB_TYPE_FIXED32: DECODE_UINT(uint32_t, 32); case PB_TYPE_UINT64: case PB_TYPE_FIXED64: DECODE_UINT(uint64_t, 64); case PB_TYPE_INT32: case PB_TYPE_SINT32: case PB_TYPE_SFIXED32: DECODE_SINT(int32_t, 32, INT32_MAX); case PB_TYPE_INT64: case PB_TYPE_SINT64: case PB_TYPE_SFIXED64: DECODE_SINT(int64_t, 64, INT64_MAX); case PB_TYPE_ENUM: return pb_prop_decode_enum(ptr, pfield->pbpf_u.enumeration, value); case PB_TYPE_FLOAT: pb_assert(!"protobuf proplib does not deal in floats"); return EIO; case PB_TYPE_DOUBLE: pb_assert(!"protobuf proplib does not deal in doubles"); return EIO; case PB_TYPE_BYTES: if (prop_object_type(value) != PROP_TYPE_DATA) return EIO; return pb_bytes_set_copy((struct pb_bytes *)ptr, prop_data_data(value), prop_data_size(value)); case PB_TYPE_STRING: if (prop_object_type(value) != PROP_TYPE_STRING) return EIO; error = pb_utf8_validate(prop_string_cstring_nocopy(value), prop_string_size(value)); if (error) return error; return pb_string_set_copy((struct pb_string *)ptr, prop_string_cstring_nocopy(value), prop_string_size(value)); case PB_TYPE_MSG: { struct pb_msg_hdr *const msg_hdr = (struct pb_msg_hdr *)ptr; pb_assert(msg_hdr->pbmh_msgdesc == type->pbt_u.msg.msgdesc); return pb_prop_decode_by_hdr(msg_hdr, pfield->pbpf_u.message, value); } default: return EIO; /* XXX */ } } static int pb_prop_decode_enum(unsigned char *ptr, const struct pb_prop_enumeration *enumeration, prop_object_t value) { const char *string; size_t start = 0, end = enumeration->pbpen_nenumerands; if (prop_object_type(value) != PROP_TYPE_STRING) return EIO; string = prop_string_cstring_nocopy(value); while (start < end) { const size_t i = (start + ((end - start) / 2)); int order; order = strcmp(string, enumeration->pbpen_by_string[i].pbped_string); if (order < 0) { end = i; } else if (order > 0) { start = (i + 1); } else { *(int32_t *)ptr = enumeration->pbpen_by_string[i].pbped_number; return 0; } } return EIO; }