libyang  3.6.1
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 
57 static LY_ERR
58 binary_base64_encode(const struct ly_ctx *ctx, const char *data, size_t size, char **str, size_t *str_len)
59 {
60  uint32_t i;
61  char *ptr;
62 
63  *str_len = (size + 2) / 3 * 4;
64  *str = malloc(*str_len + 1);
65  LY_CHECK_ERR_RET(!*str, LOGMEM(ctx), LY_EMEM);
66  if (!(*str_len)) {
67  **str = 0;
68  return LY_SUCCESS;
69  }
70 
71  ptr = *str;
72  for (i = 0; i + 2 < size; i += 3) {
73  *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
74  *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
75  *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
76  *ptr++ = b64_etable[data[i + 2] & 0x3F];
77  }
78  if (i < size) {
79  *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
80  if (i == (size - 1)) {
81  *ptr++ = b64_etable[((data[i] & 0x3) << 4)];
82  *ptr++ = '=';
83  } else {
84  *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
85  *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2)];
86  }
87  *ptr++ = '=';
88  }
89  *ptr = '\0';
90 
91  return LY_SUCCESS;
92 }
93 
97 static const int b64_dtable[256] = {
98  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
100  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
101  56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
102  7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
103  0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
104  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
105 };
106 
118 static LY_ERR
119 binary_base64_decode(const char *value, size_t value_len, void **data, size_t *size)
120 {
121  unsigned char *ptr = (unsigned char *)value;
122  uint32_t pad_chars, octet_count;
123  char *str;
124 
125  if (!value_len || (ptr[value_len - 1] != '=')) {
126  pad_chars = 0;
127  } else if (ptr[value_len - 2] == '=') {
128  pad_chars = 1;
129  } else {
130  pad_chars = 2;
131  }
132 
133  octet_count = ((value_len + 3) / 4 - (pad_chars ? 1 : 0)) * 4;
134  *size = octet_count / 4 * 3 + pad_chars;
135 
136  str = malloc(*size + 1);
137  LY_CHECK_RET(!str, LY_EMEM);
138  str[*size] = '\0';
139 
140  for (uint32_t i = 0, j = 0; i < octet_count; i += 4) {
141  int n = b64_dtable[ptr[i]] << 18 | b64_dtable[ptr[i + 1]] << 12 | b64_dtable[ptr[i + 2]] << 6 | b64_dtable[ptr[i + 3]];
142 
143  str[j++] = n >> 16;
144  str[j++] = n >> 8 & 0xFF;
145  str[j++] = n & 0xFF;
146  }
147  if (pad_chars) {
148  int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
149 
150  str[*size - pad_chars] = n >> 16;
151 
152  if (pad_chars == 2) {
153  n |= b64_dtable[ptr[octet_count + 2]] << 6;
154  n >>= 8 & 0xFF;
155  str[*size - pad_chars + 1] = n;
156  }
157  }
158 
159  *data = str;
160  return LY_SUCCESS;
161 }
162 
171 static LY_ERR
172 binary_base64_validate(const char *value, size_t value_len, struct ly_err_item **err)
173 {
174  uint32_t idx, pad;
175 
176  /* check correct characters in base64 */
177  idx = 0;
178  while ((idx < value_len) &&
179  ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
180  (('a' <= value[idx]) && (value[idx] <= 'z')) ||
181  (('0' <= value[idx]) && (value[idx] <= '9')) ||
182  ('+' == value[idx]) || ('/' == value[idx]))) {
183  idx++;
184  }
185 
186  /* find end of padding */
187  pad = 0;
188  while ((idx + pad < value_len) && (pad < 2) && (value[idx + pad] == '=')) {
189  pad++;
190  }
191 
192  /* check if value is valid base64 value */
193  if (value_len != idx + pad) {
194  if (isprint(value[idx + pad])) {
195  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
196  } else {
197  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
198  }
199  }
200 
201  if (value_len & 3) {
202  /* base64 length must be multiple of 4 chars */
203  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
204  }
205 
206  return LY_SUCCESS;
207 }
208 
218 static LY_ERR
219 binary_base64_newlines(char **value, size_t *value_len, uint32_t *options, struct ly_err_item **err)
220 {
221  char *val;
222  size_t len;
223 
224  if ((*value_len < 65) || ((*value)[64] != '\n')) {
225  /* no newlines */
226  return LY_SUCCESS;
227  }
228 
229  if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
230  /* make the value dynamic so we can modify it */
231  *value = strndup(*value, *value_len);
232  LY_CHECK_RET(!*value, LY_EMEM);
233  *options |= LYPLG_TYPE_STORE_DYNAMIC;
234  }
235 
236  val = *value;
237  len = *value_len;
238  while (len > 64) {
239  if (val[64] != '\n') {
240  /* missing, error */
241  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
242  }
243 
244  /* remove the newline */
245  memmove(val + 64, val + 65, len - 64);
246  --(*value_len);
247  val += 64;
248  len -= 65;
249  }
250 
251  return LY_SUCCESS;
252 }
253 
254 LIBYANG_API_DEF LY_ERR
255 lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
256  uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
257  const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
258  struct ly_err_item **err)
259 {
260  LY_ERR ret = LY_SUCCESS;
261  struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
262  struct lyd_value_binary *val;
263 
264  /* init storage */
265  memset(storage, 0, sizeof *storage);
266  LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
267  LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
268  storage->realtype = type;
269 
270  if (format == LY_VALUE_LYB) {
271  /* store value */
272  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
273  val->data = (void *)value;
274  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
275  } else if (value_len) {
276  val->data = malloc(value_len);
277  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
278  memcpy(val->data, value, value_len);
279  } else {
280  val->data = strdup("");
281  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
282  }
283 
284  /* store size */
285  val->size = value_len;
286 
287  /* success */
288  goto cleanup;
289  }
290 
291  /* check hints */
292  ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
293  LY_CHECK_GOTO(ret, cleanup);
294 
295  if (format != LY_VALUE_CANON) {
296  /* accept newline every 64 characters (PEM data) */
297  ret = binary_base64_newlines((char **)&value, &value_len, &options, err);
298  LY_CHECK_GOTO(ret, cleanup);
299 
300  /* validate */
301  ret = binary_base64_validate(value, value_len, err);
302  LY_CHECK_GOTO(ret, cleanup);
303  }
304 
305  /* get the binary value */
306  ret = binary_base64_decode(value, value_len, &val->data, &val->size);
307  LY_CHECK_GOTO(ret, cleanup);
308 
309  /* store canonical value */
310  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
311  ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
312  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
313  LY_CHECK_GOTO(ret, cleanup);
314 
315  /* value may have been freed */
316  value = storage->_canonical;
317  } else {
318  ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
319  LY_CHECK_GOTO(ret, cleanup);
320  }
321 
322  if (!(options & LYPLG_TYPE_STORE_ONLY)) {
323  /* validate length restriction of the binary value */
324  if (type_bin->length) {
325  ret = lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, val->size, value, value_len, err);
326  LY_CHECK_GOTO(ret, cleanup);
327  }
328  }
329 
330 cleanup:
331  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
332  free((void *)value);
333  }
334 
335  if (ret) {
336  lyplg_type_free_binary(ctx, storage);
337  }
338  return ret;
339 }
340 
341 LIBYANG_API_DEF LY_ERR
342 lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
343 {
344  struct lyd_value_binary *v1, *v2;
345 
346  LYD_VALUE_GET(val1, v1);
347  LYD_VALUE_GET(val2, v2);
348 
349  if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
350  return LY_ENOT;
351  }
352  return LY_SUCCESS;
353 }
354 
355 LIBYANG_API_DEF int
356 lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
357 {
358  struct lyd_value_binary *v1, *v2;
359  int cmp;
360 
361  LYD_VALUE_GET(val1, v1);
362  LYD_VALUE_GET(val2, v2);
363 
364  if (v1->size < v2->size) {
365  return -1;
366  } else if (v1->size > v2->size) {
367  return 1;
368  }
369 
370  cmp = memcmp(v1->data, v2->data, v1->size);
371 
372  return cmp;
373 }
374 
375 LIBYANG_API_DEF const void *
376 lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
377  void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
378 {
379  struct lyd_value_binary *val;
380  char *ret;
381  size_t ret_len = 0;
382 
383  LYD_VALUE_GET(value, val);
384 
385  if (format == LY_VALUE_LYB) {
386  *dynamic = 0;
387  if (value_len) {
388  *value_len = val->size;
389  }
390  return val->data;
391  }
392 
393  /* generate canonical value if not already */
394  if (!value->_canonical) {
395  /* get the base64 string value */
396  if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
397  return NULL;
398  }
399 
400  /* store it */
401  if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
402  LOGMEM(ctx);
403  return NULL;
404  }
405  }
406 
407  /* use the cached canonical value */
408  if (dynamic) {
409  *dynamic = 0;
410  }
411  if (value_len) {
412  *value_len = ret_len ? ret_len : strlen(value->_canonical);
413  }
414  return value->_canonical;
415 }
416 
417 LIBYANG_API_DEF LY_ERR
418 lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
419 {
420  LY_ERR ret;
421  struct lyd_value_binary *orig_val, *dup_val;
422 
423  memset(dup, 0, sizeof *dup);
424 
425  ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
426  LY_CHECK_GOTO(ret, error);
427 
428  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
429  LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
430 
431  LYD_VALUE_GET(original, orig_val);
432 
433  dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
434  LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
435 
436  memcpy(dup_val->data, orig_val->data, orig_val->size);
437  dup_val->size = orig_val->size;
438  dup->realtype = original->realtype;
439 
440  return LY_SUCCESS;
441 
442 error:
443  lyplg_type_free_binary(ctx, dup);
444  return ret;
445 }
446 
447 LIBYANG_API_DEF void
448 lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
449 {
450  struct lyd_value_binary *val;
451 
452  lydict_remove(ctx, value->_canonical);
453  value->_canonical = NULL;
454  LYD_VALUE_GET(value, val);
455  if (val) {
456  free(val->data);
458  }
459 }
460 
469  {
470  .module = "",
471  .revision = NULL,
472  .name = LY_TYPE_BINARY_STR,
473 
474  .plugin.id = "libyang 2 - binary, version 1",
475  .plugin.store = lyplg_type_store_binary,
476  .plugin.validate = NULL,
477  .plugin.compare = lyplg_type_compare_binary,
478  .plugin.sort = lyplg_type_sort_binary,
479  .plugin.print = lyplg_type_print_binary,
480  .plugin.duplicate = lyplg_type_dup_binary,
481  .plugin.free = lyplg_type_free_binary,
482  .plugin.lyb_data_len = -1,
483  },
484  {0}
485 };
struct lysc_type * realtype
Definition: tree_data.h:579
Compiled YANG data node.
Definition: tree_schema.h:1439
memset(value->fixed_mem, 0, LYD_VALUE_FIXED_MEM_SIZE)
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:448
#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:418
int cmp
Definition: binary.c:359
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:575
const char * _canonical
Definition: tree_data.h:576
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:618
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:357
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:468
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.
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:657
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.