libyang  2.0.194
libyang is YANG data modelling language parser and toolkit written (and providing API) in C.
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 "common.h"
28 #include "compat.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 < size - 2; 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  str[j++] = n >> 16;
143  str[j++] = n >> 8 & 0xFF;
144  str[j++] = n & 0xFF;
145  }
146  if (pad_chars) {
147  int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
148  str[*size - pad_chars] = n >> 16;
149 
150  if (pad_chars == 2) {
151  n |= b64_dtable[ptr[octet_count + 2]] << 6;
152  n >>= 8 & 0xFF;
153  str[*size - pad_chars + 1] = n;
154  }
155  }
156 
157  *data = str;
158  return LY_SUCCESS;
159 }
160 
170 static LY_ERR
171 binary_base64_validate(const char *value, size_t value_len, const struct lysc_type_bin *type, struct ly_err_item **err)
172 {
173  uint32_t idx, pad;
174 
175  /* check correct characters in base64 */
176  idx = 0;
177  while ((idx < value_len) &&
178  ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
179  (('a' <= value[idx]) && (value[idx] <= 'z')) ||
180  (('0' <= value[idx]) && (value[idx] <= '9')) ||
181  ('+' == value[idx]) || ('/' == value[idx]))) {
182  idx++;
183  }
184 
185  /* find end of padding */
186  pad = 0;
187  while ((idx + pad < value_len) && (pad < 2) && (value[idx + pad] == '=')) {
188  pad++;
189  }
190 
191  /* check if value is valid base64 value */
192  if (value_len != idx + pad) {
193  if (isprint(value[idx + pad])) {
194  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
195  } else {
196  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
197  }
198  }
199 
200  if (value_len & 3) {
201  /* base64 length must be multiple of 4 chars */
202  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
203  }
204 
205  /* length restriction of the binary value */
206  if (type->length) {
207  const uint32_t octet_count = ((idx + pad) / 4) * 3 - pad;
208  LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type->length, octet_count, value, value_len, err));
209  }
210 
211  return LY_SUCCESS;
212 }
213 
223 static LY_ERR
224 binary_base64_newlines(char **value, size_t *value_len, uint32_t *options, struct ly_err_item **err)
225 {
226  char *val;
227  size_t len;
228 
229  if ((*value_len < 65) || ((*value)[64] != '\n')) {
230  /* no newlines */
231  return LY_SUCCESS;
232  }
233 
234  if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
235  /* make the value dynamic so we can modify it */
236  *value = strndup(*value, *value_len);
237  LY_CHECK_RET(!*value, LY_EMEM);
238  *options |= LYPLG_TYPE_STORE_DYNAMIC;
239  }
240 
241  val = *value;
242  len = *value_len;
243  while (len > 64) {
244  if (val[64] != '\n') {
245  /* missing, error */
246  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
247  }
248 
249  /* remove the newline */
250  memmove(val + 64, val + 65, len - 64);
251  --(*value_len);
252  val += 64;
253  len -= 65;
254  }
255 
256  return LY_SUCCESS;
257 }
258 
259 LIBYANG_API_DEF LY_ERR
260 lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
261  uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
262  const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
263  struct ly_err_item **err)
264 {
265  LY_ERR ret = LY_SUCCESS;
266  struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
267  struct lyd_value_binary *val;
268 
269  /* init storage */
270  memset(storage, 0, sizeof *storage);
271  LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
272  LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
273  storage->realtype = type;
274 
275  if (format == LY_VALUE_LYB) {
276  /* store value */
277  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
278  val->data = (void *)value;
279  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
280  } else if (value_len) {
281  val->data = malloc(value_len);
282  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
283  memcpy(val->data, value, value_len);
284  } else {
285  val->data = strdup("");
286  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
287  }
288 
289  /* store size */
290  val->size = value_len;
291 
292  /* success */
293  goto cleanup;
294  }
295 
296  /* check hints */
297  ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
298  LY_CHECK_GOTO(ret, cleanup);
299 
300  if (format != LY_VALUE_CANON) {
301  /* accept newline every 64 characters (PEM data) */
302  ret = binary_base64_newlines((char **)&value, &value_len, &options, err);
303  LY_CHECK_GOTO(ret, cleanup);
304 
305  /* validate */
306  ret = binary_base64_validate(value, value_len, type_bin, err);
307  LY_CHECK_GOTO(ret, cleanup);
308  }
309 
310  /* get the binary value */
311  ret = binary_base64_decode(value, value_len, &val->data, &val->size);
312  LY_CHECK_GOTO(ret, cleanup);
313 
314  /* store canonical value */
315  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
316  ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
317  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
318  LY_CHECK_GOTO(ret, cleanup);
319  } else {
320  ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
321  LY_CHECK_GOTO(ret, cleanup);
322  }
323 
324 cleanup:
325  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
326  free((void *)value);
327  }
328 
329  if (ret) {
330  lyplg_type_free_binary(ctx, storage);
331  }
332  return ret;
333 }
334 
335 LIBYANG_API_DEF LY_ERR
336 lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2)
337 {
338  struct lyd_value_binary *v1, *v2;
339 
340  if (val1->realtype != val2->realtype) {
341  return LY_ENOT;
342  }
343 
344  LYD_VALUE_GET(val1, v1);
345  LYD_VALUE_GET(val2, v2);
346 
347  if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
348  return LY_ENOT;
349  }
350  return LY_SUCCESS;
351 }
352 
353 LIBYANG_API_DEF const void *
354 lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
355  void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
356 {
357  struct lyd_value_binary *val;
358  char *ret;
359  size_t ret_len = 0;
360 
361  LYD_VALUE_GET(value, val);
362 
363  if (format == LY_VALUE_LYB) {
364  *dynamic = 0;
365  if (value_len) {
366  *value_len = val->size;
367  }
368  return val->data;
369  }
370 
371  /* generate canonical value if not already */
372  if (!value->_canonical) {
373  /* get the base64 string value */
374  if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
375  return NULL;
376  }
377 
378  /* store it */
379  if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
380  LOGMEM(ctx);
381  return NULL;
382  }
383  }
384 
385  /* use the cached canonical value */
386  if (dynamic) {
387  *dynamic = 0;
388  }
389  if (value_len) {
390  *value_len = ret_len ? ret_len : strlen(value->_canonical);
391  }
392  return value->_canonical;
393 }
394 
395 LIBYANG_API_DEF LY_ERR
396 lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
397 {
398  LY_ERR ret;
399  struct lyd_value_binary *orig_val, *dup_val;
400 
401  memset(dup, 0, sizeof *dup);
402 
403  ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
404  LY_CHECK_GOTO(ret, error);
405 
406  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
407  LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
408 
409  LYD_VALUE_GET(original, orig_val);
410 
411  dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
412  LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
413 
414  memcpy(dup_val->data, orig_val->data, orig_val->size);
415  dup_val->size = orig_val->size;
416  dup->realtype = original->realtype;
417 
418  return LY_SUCCESS;
419 
420 error:
421  lyplg_type_free_binary(ctx, dup);
422  return ret;
423 }
424 
425 LIBYANG_API_DEF void
426 lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
427 {
428  struct lyd_value_binary *val;
429 
430  lydict_remove(ctx, value->_canonical);
431  value->_canonical = NULL;
432  LYD_VALUE_GET(value, val);
433  if (val) {
434  free(val->data);
436  }
437 }
438 
446 const struct lyplg_type_record plugins_binary[] = {
447  {
448  .module = "",
449  .revision = NULL,
450  .name = LY_TYPE_BINARY_STR,
451 
452  .plugin.id = "libyang 2 - binary, version 1",
453  .plugin.store = lyplg_type_store_binary,
454  .plugin.validate = NULL,
455  .plugin.compare = lyplg_type_compare_binary,
456  .plugin.sort = NULL,
457  .plugin.print = lyplg_type_print_binary,
458  .plugin.duplicate = lyplg_type_dup_binary,
459  .plugin.free = lyplg_type_free_binary,
460  .plugin.lyb_data_len = -1,
461  },
462  {0}
463 };
const struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition: binary.c:446
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:260
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:354
libyang context handler.
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...
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_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
libyang's error codes returned by the libyang functions.
Definition: log.h:244
@ LYVE_DATA
Definition: log.h:281
@ LY_EMEM
Definition: log.h:246
@ LY_ENOT
Definition: log.h:258
@ LY_EVALID
Definition: log.h:252
@ LY_SUCCESS
Definition: log.h:245
Libyang full error structure.
Definition: log.h:289
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:426
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:396
LIBYANG_API_DEF LY_ERR lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2)
Implementation of lyplg_type_compare_clb for the built-in binary type.
Definition: binary.c:336
const char * module
#define LYPLG_TYPE_VAL_INLINE_PREPARE(storage, type_val)
Prepare value memory for storing a specific type value, may be allocated dynamically.
LIBYANG_API_DECL LY_ERR ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_format,...) _FORMAT_PRINTF(6
Create and fill error structure.
#define LYPLG_TYPE_VAL_INLINE_DESTROY(type_val)
Destroy a prepared value.
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.
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.
#define LYPLG_TYPE_STORE_DYNAMIC
LY_DATA_TYPE basetype
Definition: tree_schema.h:1536
struct lysc_range * length
Definition: tree_schema.h:1639
Compiled YANG data node.
Definition: tree_schema.h:1650
LY_VALUE_FORMAT
All kinds of supported value formats and prefix mappings to modules.
Definition: tree.h:235
@ LY_TYPE_BINARY
Definition: tree.h:205
@ LY_VALUE_CANON
Definition: tree.h:236
@ LY_VALUE_LYB
Definition: tree.h:241
The main libyang public header.
uint8_t ly_bool
Type to indicate boolean value.
Definition: log.h:27
API for (user) types plugins.
const struct lysc_type * realtype
Definition: tree_data.h:564
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition: tree_data.h:603
const char * _canonical
Definition: tree_data.h:561
YANG data representation.
Definition: tree_data.h:560
Special lyd_value structure for built-in binary values.
Definition: tree_data.h:642
#define LOGMEM(CTX)
Definition: tree_edit.h:22