libyang  2.0.112
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 < 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 
214 API LY_ERR
215 lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
216  uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
217  const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
218  struct ly_err_item **err)
219 {
220  LY_ERR ret = LY_SUCCESS;
221  struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
222  struct lyd_value_binary *val;
223 
224  /* init storage */
225  memset(storage, 0, sizeof *storage);
226  LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
227  LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
228  storage->realtype = type;
229 
230  if (format == LY_VALUE_LYB) {
231  /* store value */
232  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
233  val->data = (void *)value;
234  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
235  } else if (value_len) {
236  val->data = malloc(value_len);
237  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
238  memcpy(val->data, value, value_len);
239  } else {
240  val->data = strdup("");
241  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
242  }
243 
244  /* store size */
245  val->size = value_len;
246 
247  /* success */
248  goto cleanup;
249  }
250 
251  /* check hints */
252  ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
253  LY_CHECK_GOTO(ret, cleanup);
254 
255  /* validate */
256  if (format != LY_VALUE_CANON) {
257  ret = binary_base64_validate(value, value_len, type_bin, err);
258  LY_CHECK_GOTO(ret, cleanup);
259  }
260 
261  /* get the binary value */
262  ret = binary_base64_decode(value, value_len, &val->data, &val->size);
263  LY_CHECK_GOTO(ret, cleanup);
264 
265  /* store canonical value */
266  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
267  ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
268  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
269  LY_CHECK_GOTO(ret, cleanup);
270  } else {
271  ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
272  LY_CHECK_GOTO(ret, cleanup);
273  }
274 
275 cleanup:
276  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
277  free((void *)value);
278  }
279 
280  if (ret) {
281  lyplg_type_free_binary(ctx, storage);
282  }
283  return ret;
284 }
285 
286 API LY_ERR
287 lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2)
288 {
289  struct lyd_value_binary *v1, *v2;
290 
291  if (val1->realtype != val2->realtype) {
292  return LY_ENOT;
293  }
294 
295  LYD_VALUE_GET(val1, v1);
296  LYD_VALUE_GET(val2, v2);
297 
298  if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
299  return LY_ENOT;
300  }
301  return LY_SUCCESS;
302 }
303 
304 API const void *
305 lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
306  void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
307 {
308  struct lyd_value_binary *val;
309  char *ret;
310  size_t ret_len = 0;
311 
312  LYD_VALUE_GET(value, val);
313 
314  if (format == LY_VALUE_LYB) {
315  *dynamic = 0;
316  if (value_len) {
317  *value_len = val->size;
318  }
319  return val->data;
320  }
321 
322  /* generate canonical value if not already */
323  if (!value->_canonical) {
324  /* get the base64 string value */
325  if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
326  return NULL;
327  }
328 
329  /* store it */
330  if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
331  LOGMEM(ctx);
332  return NULL;
333  }
334  }
335 
336  /* use the cached canonical value */
337  if (dynamic) {
338  *dynamic = 0;
339  }
340  if (value_len) {
341  *value_len = ret_len ? ret_len : strlen(value->_canonical);
342  }
343  return value->_canonical;
344 }
345 
346 API LY_ERR
347 lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
348 {
349  LY_ERR ret;
350  struct lyd_value_binary *orig_val, *dup_val;
351 
352  memset(dup, 0, sizeof *dup);
353 
354  ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
355  LY_CHECK_GOTO(ret, error);
356 
357  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
358  LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
359 
360  LYD_VALUE_GET(original, orig_val);
361 
362  dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
363  LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
364 
365  memcpy(dup_val->data, orig_val->data, orig_val->size);
366  dup_val->size = orig_val->size;
367  dup->realtype = original->realtype;
368 
369  return LY_SUCCESS;
370 
371 error:
372  lyplg_type_free_binary(ctx, dup);
373  return ret;
374 }
375 
376 API void
377 lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
378 {
379  struct lyd_value_binary *val;
380 
381  lydict_remove(ctx, value->_canonical);
382  value->_canonical = NULL;
383  LYD_VALUE_GET(value, val);
384  if (val) {
385  free(val->data);
387  }
388 }
389 
398  {
399  .module = "",
400  .revision = NULL,
401  .name = LY_TYPE_BINARY_STR,
402 
403  .plugin.id = "libyang 2 - binary, version 1",
404  .plugin.store = lyplg_type_store_binary,
405  .plugin.validate = NULL,
406  .plugin.compare = lyplg_type_compare_binary,
407  .plugin.sort = NULL,
408  .plugin.print = lyplg_type_print_binary,
409  .plugin.duplicate = lyplg_type_dup_binary,
410  .plugin.free = lyplg_type_free_binary,
411  .plugin.lyb_data_len = -1,
412  },
413  {0}
414 };
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:347
struct lysc_type * realtype
Definition: tree_data.h:539
Compiled YANG data node.
Definition: tree_schema.h:1666
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:248
uint8_t ly_bool
Type to indicate boolean value.
Definition: log.h:25
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 ...
#define LYPLG_TYPE_STORE_DYNAMIC
#define LOGMEM(CTX)
Definition: tree_edit.h:22
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...
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.
struct lysc_range * length
Definition: tree_schema.h:1655
The main libyang public header.
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.
YANG data representation.
Definition: tree_data.h:535
const char * _canonical
Definition: tree_data.h:536
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:287
Libyang full error structure.
Definition: log.h:291
Definition: log.h:283
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition: tree_data.h:578
Definition: log.h:254
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.
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:377
struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition: binary.c:397
const char * module
Definition: log.h:260
#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:235
#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:1552
LY_ERR
libyang&#39;s error codes returned by the libyang functions.
Definition: log.h:245
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.
API for (user) types plugins.
libyang context handler.
Special lyd_value structure for built-in binary values.
Definition: tree_data.h:617
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.