libyang  2.1.148
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  LYD_VALUE_GET(val1, v1);
344  LYD_VALUE_GET(val2, v2);
345 
346  if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
347  return LY_ENOT;
348  }
349  return LY_SUCCESS;
350 }
351 
352 LIBYANG_API_DEF const void *
353 lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
354  void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
355 {
356  struct lyd_value_binary *val;
357  char *ret;
358  size_t ret_len = 0;
359 
360  LYD_VALUE_GET(value, val);
361 
362  if (format == LY_VALUE_LYB) {
363  *dynamic = 0;
364  if (value_len) {
365  *value_len = val->size;
366  }
367  return val->data;
368  }
369 
370  /* generate canonical value if not already */
371  if (!value->_canonical) {
372  /* get the base64 string value */
373  if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
374  return NULL;
375  }
376 
377  /* store it */
378  if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
379  LOGMEM(ctx);
380  return NULL;
381  }
382  }
383 
384  /* use the cached canonical value */
385  if (dynamic) {
386  *dynamic = 0;
387  }
388  if (value_len) {
389  *value_len = ret_len ? ret_len : strlen(value->_canonical);
390  }
391  return value->_canonical;
392 }
393 
394 LIBYANG_API_DEF LY_ERR
395 lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
396 {
397  LY_ERR ret;
398  struct lyd_value_binary *orig_val, *dup_val;
399 
400  memset(dup, 0, sizeof *dup);
401 
402  ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
403  LY_CHECK_GOTO(ret, error);
404 
405  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
406  LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
407 
408  LYD_VALUE_GET(original, orig_val);
409 
410  dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
411  LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
412 
413  memcpy(dup_val->data, orig_val->data, orig_val->size);
414  dup_val->size = orig_val->size;
415  dup->realtype = original->realtype;
416 
417  return LY_SUCCESS;
418 
419 error:
420  lyplg_type_free_binary(ctx, dup);
421  return ret;
422 }
423 
424 LIBYANG_API_DEF void
425 lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
426 {
427  struct lyd_value_binary *val;
428 
429  lydict_remove(ctx, value->_canonical);
430  value->_canonical = NULL;
431  LYD_VALUE_GET(value, val);
432  if (val) {
433  free(val->data);
435  }
436 }
437 
446  {
447  .module = "",
448  .revision = NULL,
449  .name = LY_TYPE_BINARY_STR,
450 
451  .plugin.id = "libyang 2 - binary, version 1",
452  .plugin.store = lyplg_type_store_binary,
453  .plugin.validate = NULL,
454  .plugin.compare = lyplg_type_compare_binary,
455  .plugin.sort = NULL,
456  .plugin.print = lyplg_type_print_binary,
457  .plugin.duplicate = lyplg_type_dup_binary,
458  .plugin.free = lyplg_type_free_binary,
459  .plugin.lyb_data_len = -1,
460  },
461  {0}
462 };
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:253
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:425
#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:395
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:296
Definition: log.h:288
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition: tree_data.h:603
Definition: log.h:259
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:445
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:265
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:251
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.