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