Loading...
Searching...
No Matches
binary.c
Go to the documentation of this file.
#define _GNU_SOURCE /* strdup */
#include "plugins_types.h"
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "libyang.h"
/* additional internal headers for some useful simple macros */
#include "compat.h"
#include "ly_common.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
static const char b64_etable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static LY_ERR lyplg_type_validate_binary(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *type, const struct lyd_node *UNUSED(ctx_node), const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err);
static LY_ERR
binary_base64_encode(const struct ly_ctx *ctx, const char *data, size_t size, char **str, size_t *str_len)
{
uint32_t i;
char *ptr;
*str_len = (size + 2) / 3 * 4;
*str = malloc(*str_len + 1);
LY_CHECK_ERR_RET(!*str, LOGMEM(ctx), LY_EMEM);
if (!(*str_len)) {
**str = 0;
return LY_SUCCESS;
}
ptr = *str;
for (i = 0; i + 2 < size; i += 3) {
*ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
*ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
*ptr++ = b64_etable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
*ptr++ = b64_etable[data[i + 2] & 0x3F];
}
if (i < size) {
*ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
if (i == (size - 1)) {
*ptr++ = b64_etable[((data[i] & 0x3) << 4)];
*ptr++ = '=';
} else {
*ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
*ptr++ = b64_etable[((data[i + 1] & 0xF) << 2)];
}
*ptr++ = '=';
}
*ptr = '\0';
return LY_SUCCESS;
}
static const int b64_dtable[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
static LY_ERR
binary_base64_decode(const char *value, size_t value_len, void **data, size_t *size)
{
unsigned char *ptr = (unsigned char *)value;
uint32_t pad_chars, octet_count;
char *str;
if (!value_len || (ptr[value_len - 1] != '=')) {
pad_chars = 0;
} else if (ptr[value_len - 2] == '=') {
pad_chars = 1;
} else {
pad_chars = 2;
}
octet_count = ((value_len + 3) / 4 - (pad_chars ? 1 : 0)) * 4;
*size = octet_count / 4 * 3 + pad_chars;
str = malloc(*size + 1);
LY_CHECK_RET(!str, LY_EMEM);
str[*size] = '\0';
for (uint32_t i = 0, j = 0; i < octet_count; i += 4) {
int n = b64_dtable[ptr[i]] << 18 | b64_dtable[ptr[i + 1]] << 12 | b64_dtable[ptr[i + 2]] << 6 | b64_dtable[ptr[i + 3]];
str[j++] = n >> 16;
str[j++] = n >> 8 & 0xFF;
str[j++] = n & 0xFF;
}
if (pad_chars) {
int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
str[*size - pad_chars] = n >> 16;
if (pad_chars == 2) {
n |= b64_dtable[ptr[octet_count + 2]] << 6;
n >>= 8 & 0xFF;
str[*size - pad_chars + 1] = n;
}
}
*data = str;
return LY_SUCCESS;
}
static LY_ERR
binary_base64_validate(const char *value, size_t value_len, struct ly_err_item **err)
{
uint32_t idx, pad;
/* check correct characters in base64 */
idx = 0;
while ((idx < value_len) &&
((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
(('a' <= value[idx]) && (value[idx] <= 'z')) ||
(('0' <= value[idx]) && (value[idx] <= '9')) ||
('+' == value[idx]) || ('/' == value[idx]))) {
idx++;
}
/* find end of padding */
pad = 0;
while ((idx + pad < value_len) && (pad < 2) && (value[idx + pad] == '=')) {
pad++;
}
/* check if value is valid base64 value */
if (value_len != idx + pad) {
if (isprint(value[idx + pad])) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
}
}
if (value_len & 3) {
/* base64 length must be multiple of 4 chars */
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
}
return LY_SUCCESS;
}
static LY_ERR
binary_base64_newlines(char **value, size_t *value_len, uint32_t *options, struct ly_err_item **err)
{
char *val;
size_t len;
if ((*value_len < 65) || ((*value)[64] != '\n')) {
/* no newlines */
return LY_SUCCESS;
}
if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
/* make the value dynamic so we can modify it */
*value = strndup(*value, *value_len);
LY_CHECK_RET(!*value, LY_EMEM);
*options |= LYPLG_TYPE_STORE_DYNAMIC;
}
val = *value;
len = *value_len;
while (len > 64) {
if (val[64] != '\n') {
/* missing, error */
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
}
/* remove the newline */
memmove(val + 64, val + 65, len - 64);
--(*value_len);
val += 64;
len -= 65;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
struct ly_err_item **err)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_value_binary *val;
/* init storage */
memset(storage, 0, sizeof *storage);
LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
storage->realtype = type;
if (format == LY_VALUE_LYB) {
/* store value */
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
val->data = (void *)value;
options &= ~LYPLG_TYPE_STORE_DYNAMIC;
} else if (value_len) {
val->data = malloc(value_len);
LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
memcpy(val->data, value, value_len);
} else {
val->data = strdup("");
LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
}
/* store size */
val->size = value_len;
/* success */
goto cleanup;
}
/* check hints */
ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
LY_CHECK_GOTO(ret, cleanup);
if (format != LY_VALUE_CANON) {
/* accept newline every 64 characters (PEM data) */
ret = binary_base64_newlines((char **)&value, &value_len, &options, err);
LY_CHECK_GOTO(ret, cleanup);
/* validate */
ret = binary_base64_validate(value, value_len, err);
LY_CHECK_GOTO(ret, cleanup);
}
/* get the binary value */
ret = binary_base64_decode(value, value_len, &val->data, &val->size);
LY_CHECK_GOTO(ret, cleanup);
/* store canonical value */
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
options &= ~LYPLG_TYPE_STORE_DYNAMIC;
LY_CHECK_GOTO(ret, cleanup);
/* value may have been freed */
value = storage->_canonical;
} else {
ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
LY_CHECK_GOTO(ret, cleanup);
}
if (!(options & LYPLG_TYPE_STORE_ONLY)) {
/* validate value */
ret = lyplg_type_validate_binary(ctx, type, NULL, NULL, storage, err);
LY_CHECK_GOTO(ret, cleanup);
}
cleanup:
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
free((void *)value);
}
if (ret) {
lyplg_type_free_binary(ctx, storage);
}
return ret;
}
static LY_ERR
lyplg_type_validate_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *UNUSED(ctx_node),
const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
{
struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
struct lyd_value_binary *val;
const void *value;
size_t value_len;
LY_CHECK_ARG_RET(NULL, type, storage, err, LY_EINVAL);
val = LYPLG_TYPE_VAL_IS_DYN(val) ? (struct lyd_value_binary *)(storage->dyn_mem) : (struct lyd_value_binary *)(storage->fixed_mem);
value = lyd_value_get_canonical(ctx, storage);
value_len = strlen(value);
*err = NULL;
/* length restriction of the binary value */
if (type_bin->length) {
LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, val->size, value, value_len, err));
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
{
struct lyd_value_binary *v1, *v2;
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
return LY_ENOT;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF int
lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
{
struct lyd_value_binary *v1, *v2;
int cmp;
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
if (v1->size < v2->size) {
return -1;
} else if (v1->size > v2->size) {
return 1;
}
cmp = memcmp(v1->data, v2->data, v1->size);
return cmp;
}
LIBYANG_API_DEF const void *
lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
{
struct lyd_value_binary *val;
char *ret;
size_t ret_len = 0;
LYD_VALUE_GET(value, val);
if (format == LY_VALUE_LYB) {
*dynamic = 0;
if (value_len) {
*value_len = val->size;
}
return val->data;
}
/* generate canonical value if not already */
if (!value->_canonical) {
/* get the base64 string value */
if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
return NULL;
}
/* store it */
if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
LOGMEM(ctx);
return NULL;
}
}
/* use the cached canonical value */
if (dynamic) {
*dynamic = 0;
}
if (value_len) {
*value_len = ret_len ? ret_len : strlen(value->_canonical);
}
return value->_canonical;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
{
LY_ERR ret;
struct lyd_value_binary *orig_val, *dup_val;
memset(dup, 0, sizeof *dup);
ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
LY_CHECK_GOTO(ret, error);
LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
LYD_VALUE_GET(original, orig_val);
dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
memcpy(dup_val->data, orig_val->data, orig_val->size);
dup_val->size = orig_val->size;
dup->realtype = original->realtype;
return LY_SUCCESS;
error:
lyplg_type_free_binary(ctx, dup);
return ret;
}
LIBYANG_API_DEF void
lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
{
struct lyd_value_binary *val;
lydict_remove(ctx, value->_canonical);
value->_canonical = NULL;
LYD_VALUE_GET(value, val);
if (val) {
free(val->data);
LYPLG_TYPE_VAL_INLINE_DESTROY(val);
}
}
const struct lyplg_type_record plugins_binary[] = {
{
.module = "",
.revision = NULL,
.name = LY_TYPE_BINARY_STR,
.plugin.id = "libyang 2 - binary, version 1",
.plugin.store = lyplg_type_store_binary,
.plugin.validate = lyplg_type_validate_binary,
.plugin.compare = lyplg_type_compare_binary,
.plugin.sort = lyplg_type_sort_binary,
.plugin.print = lyplg_type_print_binary,
.plugin.duplicate = lyplg_type_dup_binary,
.plugin.free = lyplg_type_free_binary,
.plugin.lyb_data_len = -1,
},
{0}
};
plugins_binary
const struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition binary.c:494
lyplg_type_store_binary
LIBYANG_API_DEF LY_ERR lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints, const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres), struct ly_err_item **err)
Definition binary.c:257
lyplg_type_sort_binary
LIBYANG_API_DEF int lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
Definition binary.c:382
lyplg_type_print_binary
LIBYANG_API_DEF const void * lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
Definition binary.c:402
lyplg_type_compare_binary
LIBYANG_API_DEF LY_ERR lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
Definition binary.c:368
ly_ctx
libyang context handler.
lydict_insert
LIBYANG_API_DECL LY_ERR lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
Insert string into dictionary. If the string is already present, only a reference counter is incremen...
lydict_remove
LIBYANG_API_DECL LY_ERR lydict_remove(const struct ly_ctx *ctx, const char *value)
Remove specified string from the dictionary. It decrement reference counter for the string and if it ...
lydict_insert_zc
LIBYANG_API_DECL LY_ERR lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
Insert string into dictionary - zerocopy version. If the string is already present,...
LY_ERR
LY_ERR
libyang's error codes returned by the libyang functions.
Definition log.h:237
LYVE_DATA
@ LYVE_DATA
Definition log.h:274
LY_EINVAL
@ LY_EINVAL
Definition log.h:241
LY_EMEM
@ LY_EMEM
Definition log.h:239
LY_ENOT
@ LY_ENOT
Definition log.h:251
LY_EVALID
@ LY_EVALID
Definition log.h:245
LY_SUCCESS
@ LY_SUCCESS
Definition log.h:238
ly_err_item
Libyang full error structure.
Definition log.h:282
lyplg_type_print_binary
LIBYANG_API_DECL const void * lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len)
Implementation of lyplg_type_print_clb for the built-in binary type.
lyplg_type_free_binary
LIBYANG_API_DEF void lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
Implementation of lyplg_type_free_clb for the built-in binary type.
Definition binary.c:474
lyplg_type_dup_binary
LIBYANG_API_DEF LY_ERR lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
Implementation of lyplg_type_dup_clb for the built-in binary type.
Definition binary.c:444
lyplg_type_compare_binary
LIBYANG_API_DECL LY_ERR lyplg_type_compare_binary(const struct ly_ctx *ctx, const struct lyd_value *val1, const struct lyd_value *val2)
Implementation of lyplg_type_compare_clb for the built-in binary type.
lyplg_type_store_binary
LIBYANG_API_DECL LY_ERR lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
Implementation of lyplg_type_store_clb for the built-in binary type.
lyplg_type_sort_binary
LIBYANG_API_DECL int lyplg_type_sort_binary(const struct ly_ctx *ctx, const struct lyd_value *val1, const struct lyd_value *val2)
Implementation of lyplg_type_sort_clb for the built-in binary type.
LYPLG_TYPE_VAL_INLINE_PREPARE
#define LYPLG_TYPE_VAL_INLINE_PREPARE(storage, type_val)
Prepare value memory for storing a specific type value, may be allocated dynamically.
Definition plugins_types.h:194
LYPLG_TYPE_VAL_INLINE_DESTROY
#define LYPLG_TYPE_VAL_INLINE_DESTROY(type_val)
Destroy a prepared value.
Definition plugins_types.h:206
lyplg_type_validate_range
LIBYANG_API_DECL LY_ERR lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval, size_t strval_len, struct ly_err_item **err)
Data type validator for a range/length-restricted values.
LYPLG_TYPE_VAL_IS_DYN
#define LYPLG_TYPE_VAL_IS_DYN(type_val)
Check whether specific type value needs to be allocated dynamically.
Definition plugins_types.h:182
lyplg_type_check_hints
LIBYANG_API_DECL LY_ERR lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_DATA_TYPE type, int *base, struct ly_err_item **err)
Check that the type is suitable for the parser's hints (if any) in the specified format.
ly_err_new
LIBYANG_API_DECL LY_ERR ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *data_path, char *apptag, const char *err_format,...) _FORMAT_PRINTF(6
Create and fill error structure.
lyplg_type_record
Definition plugins_types.h:635
LYPLG_TYPE_STORE_DYNAMIC
#define LYPLG_TYPE_STORE_DYNAMIC
Definition plugins_types.h:471
LYPLG_TYPE_STORE_ONLY
#define LYPLG_TYPE_STORE_ONLY
Definition plugins_types.h:478
lysc_type::basetype
LY_DATA_TYPE basetype
Definition tree_schema.h:1287
lysc_type_bin::length
struct lysc_range * length
Definition tree_schema.h:1410
lysc_node
Compiled YANG data node.
Definition tree_schema.h:1421
lysc_type
Definition tree_schema.h:1283
lysc_type_bin
Definition tree_schema.h:1403
LY_VALUE_FORMAT
LY_VALUE_FORMAT
All kinds of supported value formats and prefix mappings to modules.
Definition tree.h:234
LY_TYPE_BINARY
@ LY_TYPE_BINARY
Definition tree.h:204
LY_VALUE_CANON
@ LY_VALUE_CANON
Definition tree.h:235
LY_VALUE_LYB
@ LY_VALUE_LYB
Definition tree.h:240
libyang.h
The main libyang public header.
ly_bool
uint8_t ly_bool
Type to indicate boolean value.
Definition log.h:35
plugins_types.h
API for (user) types plugins.
lyd_value_get_canonical
LIBYANG_API_DECL const char * lyd_value_get_canonical(const struct ly_ctx *ctx, const struct lyd_value *value)
Get the (canonical) value of a lyd_value.
lyd_value::realtype
const struct lysc_type * realtype
Definition tree_data.h:571
lyd_value_binary::data
void * data
Definition tree_data.h:650
lyd_value_binary::size
size_t size
Definition tree_data.h:651
LYD_VALUE_GET
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition tree_data.h:610
lyd_value::_canonical
const char * _canonical
Definition tree_data.h:568
lyd_node
Generic structure for a data node.
Definition tree_data.h:795
lyd_value
YANG data representation.
Definition tree_data.h:567
lyd_value_binary
Special lyd_value structure for built-in binary values.
Definition tree_data.h:649
LOGMEM
#define LOGMEM(CTX)
Definition tree_edit.h:22