libyang  2.2.8
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  } else {
316  ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
317  LY_CHECK_GOTO(ret, cleanup);
318  }
319 
320  if (!(options & LYPLG_TYPE_STORE_ONLY)) {
321  /* validate value */
322  ret = lyplg_type_validate_binary(ctx, type, NULL, NULL, storage, err);
323  LY_CHECK_GOTO(ret, cleanup);
324  }
325 
326 cleanup:
327  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
328  free((void *)value);
329  }
330 
331  if (ret) {
332  lyplg_type_free_binary(ctx, storage);
333  }
334  return ret;
335 }
336 
340 static LY_ERR
341 lyplg_type_validate_binary(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *type, const struct lyd_node *UNUSED(ctx_node),
342  const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
343 {
344  struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
345  struct lyd_value_binary *val;
346  const void *value;
347  size_t value_len;
348 
349  LY_CHECK_ARG_RET(NULL, type, storage, err, LY_EINVAL);
350 
351  val = LYPLG_TYPE_VAL_IS_DYN(val) ? (struct lyd_value_binary *)(storage->dyn_mem) : (struct lyd_value_binary *)(storage->fixed_mem);
352  value = storage->_canonical;
353  value_len = strlen(storage->_canonical);
354  *err = NULL;
355 
356  /* length restriction of the binary value */
357  if (type_bin->length) {
358  LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, val->size, value, value_len, err));
359  }
360 
361  return LY_SUCCESS;
362 }
363 
364 LIBYANG_API_DEF LY_ERR
365 lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
366 {
367  struct lyd_value_binary *v1, *v2;
368 
369  LYD_VALUE_GET(val1, v1);
370  LYD_VALUE_GET(val2, v2);
371 
372  if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
373  return LY_ENOT;
374  }
375  return LY_SUCCESS;
376 }
377 
378 LIBYANG_API_DEF int
379 lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
380 {
381  struct lyd_value_binary *v1, *v2;
382  int cmp;
383 
384  LYD_VALUE_GET(val1, v1);
385  LYD_VALUE_GET(val2, v2);
386 
387  if (v1->size < v2->size) {
388  return -1;
389  } else if (v1->size > v2->size) {
390  return 1;
391  }
392 
393  cmp = memcmp(v1->data, v2->data, v1->size);
394 
395  return cmp;
396 }
397 
398 LIBYANG_API_DEF const void *
399 lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
400  void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
401 {
402  struct lyd_value_binary *val;
403  char *ret;
404  size_t ret_len = 0;
405 
406  LYD_VALUE_GET(value, val);
407 
408  if (format == LY_VALUE_LYB) {
409  *dynamic = 0;
410  if (value_len) {
411  *value_len = val->size;
412  }
413  return val->data;
414  }
415 
416  /* generate canonical value if not already */
417  if (!value->_canonical) {
418  /* get the base64 string value */
419  if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
420  return NULL;
421  }
422 
423  /* store it */
424  if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
425  LOGMEM(ctx);
426  return NULL;
427  }
428  }
429 
430  /* use the cached canonical value */
431  if (dynamic) {
432  *dynamic = 0;
433  }
434  if (value_len) {
435  *value_len = ret_len ? ret_len : strlen(value->_canonical);
436  }
437  return value->_canonical;
438 }
439 
440 LIBYANG_API_DEF LY_ERR
441 lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
442 {
443  LY_ERR ret;
444  struct lyd_value_binary *orig_val, *dup_val;
445 
446  memset(dup, 0, sizeof *dup);
447 
448  ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
449  LY_CHECK_GOTO(ret, error);
450 
451  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
452  LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
453 
454  LYD_VALUE_GET(original, orig_val);
455 
456  dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
457  LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
458 
459  memcpy(dup_val->data, orig_val->data, orig_val->size);
460  dup_val->size = orig_val->size;
461  dup->realtype = original->realtype;
462 
463  return LY_SUCCESS;
464 
465 error:
466  lyplg_type_free_binary(ctx, dup);
467  return ret;
468 }
469 
470 LIBYANG_API_DEF void
471 lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
472 {
473  struct lyd_value_binary *val;
474 
475  lydict_remove(ctx, value->_canonical);
476  value->_canonical = NULL;
477  LYD_VALUE_GET(value, val);
478  if (val) {
479  free(val->data);
481  }
482 }
483 
492  {
493  .module = "",
494  .revision = NULL,
495  .name = LY_TYPE_BINARY_STR,
496 
497  .plugin.id = "libyang 2 - binary, version 1",
498  .plugin.store = lyplg_type_store_binary,
499  .plugin.validate = lyplg_type_validate_binary,
500  .plugin.compare = lyplg_type_compare_binary,
501  .plugin.sort = lyplg_type_sort_binary,
502  .plugin.print = lyplg_type_print_binary,
503  .plugin.duplicate = lyplg_type_dup_binary,
504  .plugin.free = lyplg_type_free_binary,
505  .plugin.lyb_data_len = -1,
506  },
507  {0}
508 };
struct lysc_type * realtype
Definition: tree_data.h:575
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:799
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:471
#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:441
int cmp
Definition: binary.c:382
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:571
const char * _canonical
Definition: tree_data.h:572
Libyang full error structure.
Definition: log.h:285
Definition: log.h:277
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition: tree_data.h:614
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:380
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:491
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:653
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.