libyang  3.13.5
libyang is YANG data modelling language parser and toolkit written (and providing API) in C.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
binary.c
Go to the documentation of this file.
1 
15 #define _GNU_SOURCE /* strdup */
16 
17 #include "plugins_types.h"
18 
19 #include <ctype.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "libyang.h"
25 
26 /* additional internal headers for some useful simple macros */
27 #include "compat.h"
28 #include "ly_common.h"
29 #include "plugins_internal.h" /* LY_TYPE_*_STR */
30 
43 static const char b64_etable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
44 
45 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);
46 
59 static LY_ERR
60 binary_base64_encode(const struct ly_ctx *ctx, const char *data, size_t size, char **str, size_t *str_len)
61 {
62  uint32_t i;
63  char *ptr;
64 
65  *str_len = (size + 2) / 3 * 4;
66  *str = malloc(*str_len + 1);
67  LY_CHECK_ERR_RET(!*str, LOGMEM(ctx), LY_EMEM);
68  if (!(*str_len)) {
69  **str = 0;
70  return LY_SUCCESS;
71  }
72 
73  ptr = *str;
74  for (i = 0; i + 2 < size; i += 3) {
75  *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
76  *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
77  *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
78  *ptr++ = b64_etable[data[i + 2] & 0x3F];
79  }
80  if (i < size) {
81  *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
82  if (i == (size - 1)) {
83  *ptr++ = b64_etable[((data[i] & 0x3) << 4)];
84  *ptr++ = '=';
85  } else {
86  *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
87  *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2)];
88  }
89  *ptr++ = '=';
90  }
91  *ptr = '\0';
92 
93  return LY_SUCCESS;
94 }
95 
99 static const int b64_dtable[256] = {
100  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
101  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
103  56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
104  7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
105  0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
106  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
107 };
108 
120 static LY_ERR
121 binary_base64_decode(const char *value, size_t value_len, void **data, size_t *size)
122 {
123  unsigned char *ptr = (unsigned char *)value;
124  uint32_t pad_chars, octet_count;
125  char *str;
126 
127  if (!value_len || (ptr[value_len - 1] != '=')) {
128  pad_chars = 0;
129  } else if (ptr[value_len - 2] == '=') {
130  pad_chars = 1;
131  } else {
132  pad_chars = 2;
133  }
134 
135  octet_count = ((value_len + 3) / 4 - (pad_chars ? 1 : 0)) * 4;
136  *size = octet_count / 4 * 3 + pad_chars;
137 
138  str = malloc(*size + 1);
139  LY_CHECK_RET(!str, LY_EMEM);
140  str[*size] = '\0';
141 
142  for (uint32_t i = 0, j = 0; i < octet_count; i += 4) {
143  int n = b64_dtable[ptr[i]] << 18 | b64_dtable[ptr[i + 1]] << 12 | b64_dtable[ptr[i + 2]] << 6 | b64_dtable[ptr[i + 3]];
144 
145  str[j++] = n >> 16;
146  str[j++] = n >> 8 & 0xFF;
147  str[j++] = n & 0xFF;
148  }
149  if (pad_chars) {
150  int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
151 
152  str[*size - pad_chars] = n >> 16;
153 
154  if (pad_chars == 2) {
155  n |= b64_dtable[ptr[octet_count + 2]] << 6;
156  n >>= 8 & 0xFF;
157  str[*size - pad_chars + 1] = n;
158  }
159  }
160 
161  *data = str;
162  return LY_SUCCESS;
163 }
164 
173 static LY_ERR
174 binary_base64_validate(const char *value, size_t value_len, struct ly_err_item **err)
175 {
176  uint32_t idx, pad;
177 
178  /* check correct characters in base64 */
179  idx = 0;
180  while ((idx < value_len) &&
181  ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
182  (('a' <= value[idx]) && (value[idx] <= 'z')) ||
183  (('0' <= value[idx]) && (value[idx] <= '9')) ||
184  ('+' == value[idx]) || ('/' == value[idx]))) {
185  idx++;
186  }
187 
188  /* find end of padding */
189  pad = 0;
190  while ((idx + pad < value_len) && (pad < 2) && (value[idx + pad] == '=')) {
191  pad++;
192  }
193 
194  /* check if value is valid base64 value */
195  if (value_len != idx + pad) {
196  if (isprint(value[idx + pad])) {
197  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
198  } else {
199  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
200  }
201  }
202 
203  if (value_len & 3) {
204  /* base64 length must be multiple of 4 chars */
205  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
206  }
207 
208  return LY_SUCCESS;
209 }
210 
220 static LY_ERR
221 binary_base64_newlines(char **value, size_t *value_len, uint32_t *options, struct ly_err_item **err)
222 {
223  char *val;
224  size_t len;
225 
226  if ((*value_len < 65) || ((*value)[64] != '\n')) {
227  /* no newlines */
228  return LY_SUCCESS;
229  }
230 
231  if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
232  /* make the value dynamic so we can modify it */
233  *value = strndup(*value, *value_len);
234  LY_CHECK_RET(!*value, LY_EMEM);
235  *options |= LYPLG_TYPE_STORE_DYNAMIC;
236  }
237 
238  val = *value;
239  len = *value_len;
240  while (len > 64) {
241  if (val[64] != '\n') {
242  /* missing, error */
243  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
244  }
245 
246  /* remove the newline */
247  memmove(val + 64, val + 65, len - 64);
248  --(*value_len);
249  val += 64;
250  len -= 65;
251  }
252 
253  return LY_SUCCESS;
254 }
255 
256 LIBYANG_API_DEF LY_ERR
257 lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
258  uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
259  const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
260  struct ly_err_item **err)
261 {
262  LY_ERR ret = LY_SUCCESS;
263  struct lyd_value_binary *val;
264 
265  /* init storage */
266  memset(storage, 0, sizeof *storage);
267  LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
268  LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
269  storage->realtype = type;
270 
271  if (format == LY_VALUE_LYB) {
272  /* store value */
273  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
274  val->data = (void *)value;
275  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
276  } else if (value_len) {
277  val->data = malloc(value_len);
278  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
279  memcpy(val->data, value, value_len);
280  } else {
281  val->data = strdup("");
282  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
283  }
284 
285  /* store size */
286  val->size = value_len;
287 
288  /* success */
289  goto cleanup;
290  }
291 
292  /* check hints */
293  ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
294  LY_CHECK_GOTO(ret, cleanup);
295 
296  if (format != LY_VALUE_CANON) {
297  /* accept newline every 64 characters (PEM data) */
298  ret = binary_base64_newlines((char **)&value, &value_len, &options, err);
299  LY_CHECK_GOTO(ret, cleanup);
300 
301  /* validate */
302  ret = binary_base64_validate(value, value_len, err);
303  LY_CHECK_GOTO(ret, cleanup);
304  }
305 
306  /* get the binary value */
307  ret = binary_base64_decode(value, value_len, &val->data, &val->size);
308  LY_CHECK_GOTO(ret, cleanup);
309 
310  /* store canonical value */
311  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
312  ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
313  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
314  LY_CHECK_GOTO(ret, cleanup);
315 
316  /* value may have been freed */
317  value = storage->_canonical;
318  } else {
319  ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
320  LY_CHECK_GOTO(ret, cleanup);
321  }
322 
323  if (!(options & LYPLG_TYPE_STORE_ONLY)) {
324  /* validate value */
325  ret = lyplg_type_validate_binary(ctx, type, NULL, NULL, storage, err);
326  LY_CHECK_GOTO(ret, cleanup);
327  }
328 
329 cleanup:
330  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
331  free((void *)value);
332  }
333 
334  if (ret) {
335  lyplg_type_free_binary(ctx, storage);
336  }
337  return ret;
338 }
339 
343 static LY_ERR
344 lyplg_type_validate_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *UNUSED(ctx_node),
345  const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
346 {
347  struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
348  struct lyd_value_binary *val;
349  const void *value;
350  size_t value_len;
351 
352  LY_CHECK_ARG_RET(NULL, type, storage, err, LY_EINVAL);
353 
354  val = LYPLG_TYPE_VAL_IS_DYN(val) ? (struct lyd_value_binary *)(storage->dyn_mem) : (struct lyd_value_binary *)(storage->fixed_mem);
355  value = lyd_value_get_canonical(ctx, storage);
356  value_len = strlen(value);
357  *err = NULL;
358 
359  /* length restriction of the binary value */
360  if (type_bin->length) {
361  LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, val->size, value, value_len, err));
362  }
363 
364  return LY_SUCCESS;
365 }
366 
367 LIBYANG_API_DEF LY_ERR
368 lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
369 {
370  struct lyd_value_binary *v1, *v2;
371 
372  LYD_VALUE_GET(val1, v1);
373  LYD_VALUE_GET(val2, v2);
374 
375  if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
376  return LY_ENOT;
377  }
378  return LY_SUCCESS;
379 }
380 
381 LIBYANG_API_DEF int
382 lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
383 {
384  struct lyd_value_binary *v1, *v2;
385  int cmp;
386 
387  LYD_VALUE_GET(val1, v1);
388  LYD_VALUE_GET(val2, v2);
389 
390  if (v1->size < v2->size) {
391  return -1;
392  } else if (v1->size > v2->size) {
393  return 1;
394  }
395 
396  cmp = memcmp(v1->data, v2->data, v1->size);
397 
398  return cmp;
399 }
400 
401 LIBYANG_API_DEF const void *
402 lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
403  void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
404 {
405  struct lyd_value_binary *val;
406  char *ret;
407  size_t ret_len = 0;
408 
409  LYD_VALUE_GET(value, val);
410 
411  if (format == LY_VALUE_LYB) {
412  *dynamic = 0;
413  if (value_len) {
414  *value_len = val->size;
415  }
416  return val->data;
417  }
418 
419  /* generate canonical value if not already */
420  if (!value->_canonical) {
421  /* get the base64 string value */
422  if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
423  return NULL;
424  }
425 
426  /* store it */
427  if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
428  LOGMEM(ctx);
429  return NULL;
430  }
431  }
432 
433  /* use the cached canonical value */
434  if (dynamic) {
435  *dynamic = 0;
436  }
437  if (value_len) {
438  *value_len = ret_len ? ret_len : strlen(value->_canonical);
439  }
440  return value->_canonical;
441 }
442 
443 LIBYANG_API_DEF LY_ERR
444 lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
445 {
446  LY_ERR ret;
447  struct lyd_value_binary *orig_val, *dup_val;
448 
449  memset(dup, 0, sizeof *dup);
450 
451  ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
452  LY_CHECK_GOTO(ret, error);
453 
454  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
455  LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
456 
457  LYD_VALUE_GET(original, orig_val);
458 
459  dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
460  LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
461 
462  memcpy(dup_val->data, orig_val->data, orig_val->size);
463  dup_val->size = orig_val->size;
464  dup->realtype = original->realtype;
465 
466  return LY_SUCCESS;
467 
468 error:
469  lyplg_type_free_binary(ctx, dup);
470  return ret;
471 }
472 
473 LIBYANG_API_DEF void
474 lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
475 {
476  struct lyd_value_binary *val;
477 
478  lydict_remove(ctx, value->_canonical);
479  value->_canonical = NULL;
480  LYD_VALUE_GET(value, val);
481  if (val) {
482  free(val->data);
484  }
485 }
486 
495  {
496  .module = "",
497  .revision = NULL,
498  .name = LY_TYPE_BINARY_STR,
499 
500  .plugin.id = "libyang 2 - binary, version 1",
501  .plugin.store = lyplg_type_store_binary,
502  .plugin.validate = lyplg_type_validate_binary,
503  .plugin.compare = lyplg_type_compare_binary,
504  .plugin.sort = lyplg_type_sort_binary,
505  .plugin.print = lyplg_type_print_binary,
506  .plugin.duplicate = lyplg_type_dup_binary,
507  .plugin.free = lyplg_type_free_binary,
508  .plugin.lyb_data_len = -1,
509  },
510  {0}
511 };
struct lysc_type * realtype
Definition: tree_data.h:571
Compiled YANG data node.
Definition: tree_schema.h:1439
memset(value->fixed_mem, 0, LYD_VALUE_FIXED_MEM_SIZE)
Definition: log.h:244
Generic structure for a data node.
Definition: tree_data.h:795
uint8_t ly_bool
Type to indicate boolean value.
Definition: log.h:28
Definition: log.h:242
LIBYANG_API_DECL 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
#define LYPLG_TYPE_STORE_DYNAMIC
#define LOGMEM(CTX)
Definition: tree_edit.h:22
LIBYANG_API_DECL 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
int cmp
Definition: binary.c:385
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_VAL_INLINE_DESTROY(val)
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.
struct lysc_range * length
Definition: tree_schema.h:1428
The main libyang public header.
YANG data representation.
Definition: tree_data.h:567
const char * _canonical
Definition: tree_data.h:568
Libyang full error structure.
Definition: log.h:285
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.
Definition: log.h:277
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition: tree_data.h:610
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.
LIBYANG_API_DEF int
Definition: binary.c:383
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 ...
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...
struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition: binary.c:494
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&#39;s hints (if any) in the specified format.
const char * module
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.
#define LYPLG_TYPE_VAL_IS_DYN(type_val)
Check whether specific type value needs to be allocated dynamically.
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, only a reference counter is incremented and no memory allocation is performed. This insert function variant avoids duplication of specified value - it is inserted into the dictionary directly.
LY_VALUE_FORMAT
All kinds of supported value formats and prefix mappings to modules.
Definition: tree.h:234
Definition: log.h:248
#define LYPLG_TYPE_VAL_INLINE_PREPARE(storage, type_val)
Prepare value memory for storing a specific type value, may be allocated dynamically.
LY_DATA_TYPE basetype
Definition: tree_schema.h:1305
API for (user) types plugins.
LY_ERR
libyang&#39;s error codes returned by the libyang functions.
Definition: log.h:240
libyang context handler.
Special lyd_value structure for built-in binary values.
Definition: tree_data.h:649
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.
#define LYPLG_TYPE_STORE_ONLY
Definition: log.h:254
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.