libyang 5.4.9
libyang is YANG data modelling language parser and toolkit written (and providing API) in C.
Loading...
Searching...
No Matches
binary.c
Go to the documentation of this file.
1
16#define _GNU_SOURCE /* strdup */
17
18#include "plugins_types.h"
19
20#include <ctype.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "libyang.h"
26
27/* additional internal headers for some useful simple macros */
28#include "compat.h"
29#include "ly_common.h"
30#include "plugins_internal.h" /* LY_TYPE_*_STR */
31
44static const char b64_etable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
45
46static void lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value);
47static LY_ERR lyplg_type_validate_value_binary(const struct ly_ctx *ctx, const struct lysc_type *type,
48 struct lyd_value *storage, struct ly_err_item **err);
49
62static LY_ERR
63binary_base64_encode(const struct ly_ctx *ctx, const char *data, uint32_t size, char **str, uint32_t *str_len)
64{
65 uint32_t i;
66 char *ptr;
67
68 *str_len = (size + 2) / 3 * 4;
69 *str = malloc(*str_len + 1);
70 LY_CHECK_ERR_RET(!*str, LOGMEM(ctx), LY_EMEM);
71 if (!(*str_len)) {
72 **str = 0;
73 return LY_SUCCESS;
74 }
75
76 ptr = *str;
77 for (i = 0; i + 2 < size; i += 3) {
78 *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
79 *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
80 *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
81 *ptr++ = b64_etable[data[i + 2] & 0x3F];
82 }
83 if (i < size) {
84 *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
85 if (i == (size - 1)) {
86 *ptr++ = b64_etable[((data[i] & 0x3) << 4)];
87 *ptr++ = '=';
88 } else {
89 *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
90 *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2)];
91 }
92 *ptr++ = '=';
93 }
94 *ptr = '\0';
95
96 return LY_SUCCESS;
97}
98
102static const int b64_dtable[256] = {
103 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
106 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
107 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
108 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
109 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
110};
111
123static LY_ERR
124binary_base64_decode(const char *value, uint32_t value_size, void **data, uint32_t *size)
125{
126 unsigned char *ptr = (unsigned char *)value;
127 uint32_t pad_chars, octet_count;
128 char *str;
129
130 if (!value_size || (ptr[value_size - 1] != '=')) {
131 pad_chars = 0;
132 } else if (ptr[value_size - 2] == '=') {
133 pad_chars = 1;
134 } else {
135 pad_chars = 2;
136 }
137
138 octet_count = ((value_size + 3) / 4 - (pad_chars ? 1 : 0)) * 4;
139 *size = octet_count / 4 * 3 + pad_chars;
140
141 str = malloc(*size + 1);
142 LY_CHECK_RET(!str, LY_EMEM);
143 str[*size] = '\0';
144
145 for (uint32_t i = 0, j = 0; i < octet_count; i += 4) {
146 int n = b64_dtable[ptr[i]] << 18 | b64_dtable[ptr[i + 1]] << 12 | b64_dtable[ptr[i + 2]] << 6 | b64_dtable[ptr[i + 3]];
147
148 str[j++] = n >> 16;
149 str[j++] = n >> 8 & 0xFF;
150 str[j++] = n & 0xFF;
151 }
152 if (pad_chars) {
153 int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
154
155 str[*size - pad_chars] = n >> 16;
156
157 if (pad_chars == 2) {
158 n |= b64_dtable[ptr[octet_count + 2]] << 6;
159 n >>= 8 & 0xFF;
160 str[*size - pad_chars + 1] = n;
161 }
162 }
163
164 *data = str;
165 return LY_SUCCESS;
166}
167
176static LY_ERR
177binary_base64_validate(const char *value, uint32_t value_size, struct ly_err_item **err)
178{
179 uint32_t idx, pad;
180
181 /* check correct characters in base64 */
182 idx = 0;
183 while ((idx < value_size) &&
184 ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
185 (('a' <= value[idx]) && (value[idx] <= 'z')) ||
186 (('0' <= value[idx]) && (value[idx] <= '9')) ||
187 ('+' == value[idx]) || ('/' == value[idx]))) {
188 idx++;
189 }
190
191 /* find end of padding */
192 pad = 0;
193 while ((idx + pad < value_size) && (pad < 2) && (value[idx + pad] == '=')) {
194 pad++;
195 }
196
197 /* check if value is valid base64 value */
198 if (value_size != idx + pad) {
199 if (isprint(value[idx + pad])) {
200 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
201 } else {
202 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
203 }
204 }
205
206 if (value_size & 3) {
207 /* base64 length must be multiple of 4 chars */
208 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
209 }
210
211 return LY_SUCCESS;
212}
213
223static LY_ERR
224binary_base64_newlines(char **value, uint32_t *value_size, uint32_t *options, struct ly_err_item **err)
225{
226 char *val;
227 uint32_t size;
228
229 if ((*value_size < 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_size);
237 LY_CHECK_RET(!*value, LY_EMEM);
238 *options |= LYPLG_TYPE_STORE_DYNAMIC;
239 }
240
241 val = *value;
242 size = *value_size;
243 while (size > 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, size - 64);
251 --(*value_size);
252 val += 64;
253 size -= 65;
254 }
255
256 return LY_SUCCESS;
257}
258
259static LY_ERR
260lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, uint64_t value_size_bits,
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 lyd_value_binary *val;
267 uint32_t value_size;
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 /* check value length */
276 ret = lyplg_type_check_value_size("binary", format, value_size_bits, LYPLG_LYB_SIZE_VARIABLE_BYTES, 0, &value_size,
277 err);
278 LY_CHECK_GOTO(ret, cleanup);
279
280 if (format == LY_VALUE_LYB) {
281 /* store value */
282 if (options & LYPLG_TYPE_STORE_DYNAMIC) {
283 val->data = (void *)value;
284 options &= ~LYPLG_TYPE_STORE_DYNAMIC;
285 } else if (value_size) {
286 val->data = malloc(value_size);
287 LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
288 memcpy(val->data, value, value_size);
289 } else {
290 val->data = strdup("");
291 LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
292 }
293
294 /* store size */
295 val->size = value_size;
296
297 /* success */
298 goto cleanup;
299 }
300
301 /* check hints */
302 ret = lyplg_type_check_hints(hints, value, value_size, type->basetype, NULL, err);
303 LY_CHECK_GOTO(ret, cleanup);
304
305 if (format != LY_VALUE_CANON) {
306 /* accept newline every 64 characters (PEM data) */
307 ret = binary_base64_newlines((char **)&value, &value_size, &options, err);
308 LY_CHECK_GOTO(ret, cleanup);
309
310 /* validate */
311 ret = binary_base64_validate(value, value_size, err);
312 LY_CHECK_GOTO(ret, cleanup);
313 }
314
315 /* get the binary value */
316 ret = binary_base64_decode(value, value_size, &val->data, &val->size);
317 LY_CHECK_GOTO(ret, cleanup);
318
319 /* store canonical value */
320 if (options & LYPLG_TYPE_STORE_DYNAMIC) {
321 ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
322 options &= ~LYPLG_TYPE_STORE_DYNAMIC;
323 LY_CHECK_GOTO(ret, cleanup);
324
325 /* value may have been freed */
326 value = storage->_canonical;
327 } else {
328 ret = lydict_insert(ctx, value_size ? value : "", value_size, &storage->_canonical);
329 LY_CHECK_GOTO(ret, cleanup);
330 }
331
332 if (!(options & LYPLG_TYPE_STORE_ONLY)) {
333 /* validate value */
334 ret = lyplg_type_validate_value_binary(ctx, type, storage, err);
335 LY_CHECK_GOTO(ret, cleanup);
336 }
337
338cleanup:
339 if (options & LYPLG_TYPE_STORE_DYNAMIC) {
340 free((void *)value);
341 }
342
343 if (ret) {
344 lyplg_type_free_binary(ctx, storage);
345 }
346 return ret;
347}
348
352static LY_ERR
353lyplg_type_validate_value_binary(const struct ly_ctx *ctx, const struct lysc_type *type, struct lyd_value *storage,
354 struct ly_err_item **err)
355{
356 struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
357 struct lyd_value_binary *val;
358 const void *value;
359 size_t value_len;
360
361 LY_CHECK_ARG_RET(NULL, type, storage, err, LY_EINVAL);
362
363 val = LYPLG_TYPE_VAL_IS_DYN(val) ? (struct lyd_value_binary *)(storage->dyn_mem) : (struct lyd_value_binary *)(storage->fixed_mem);
364 value = lyd_value_get_canonical(ctx, storage);
365 value_len = strlen(value);
366 *err = NULL;
367
368 /* length restriction of the binary value */
369 if (type_bin->length) {
370 LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, val->size, value, value_len, err));
371 }
372
373 return LY_SUCCESS;
374}
375
376static LY_ERR
377lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
378{
379 struct lyd_value_binary *v1, *v2;
380
381 LYD_VALUE_GET(val1, v1);
382 LYD_VALUE_GET(val2, v2);
383
384 if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
385 return LY_ENOT;
386 }
387 return LY_SUCCESS;
388}
389
390static int
391lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
392{
393 struct lyd_value_binary *v1, *v2;
394 int cmp;
395
396 LYD_VALUE_GET(val1, v1);
397 LYD_VALUE_GET(val2, v2);
398
399 if (v1->size < v2->size) {
400 return -1;
401 } else if (v1->size > v2->size) {
402 return 1;
403 }
404
405 cmp = memcmp(v1->data, v2->data, v1->size);
406
407 return cmp;
408}
409
410static const void *
411lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
412 void *UNUSED(prefix_data), ly_bool *dynamic, uint64_t *value_size_bits)
413{
414 struct lyd_value_binary *val;
415 char *ret;
416 uint32_t ret_size = 0;
417
418 LYD_VALUE_GET(value, val);
419
420 if (format == LY_VALUE_LYB) {
421 *dynamic = 0;
422 if (value_size_bits) {
423 *value_size_bits = (uint64_t)val->size * 8;
424 }
425 return val->data;
426 }
427
428 /* generate canonical value if not already */
429 if (!value->_canonical) {
430 /* get the base64 string value */
431 if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_size)) {
432 return NULL;
433 }
434
435 /* store it */
436 if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
437 LOGMEM(ctx);
438 return NULL;
439 }
440 }
441
442 /* use the cached canonical value */
443 if (dynamic) {
444 *dynamic = 0;
445 }
446 if (value_size_bits) {
447 *value_size_bits = ret_size ? ret_size * 8 : strlen(value->_canonical) * 8;
448 }
449 return value->_canonical;
450}
451
452static LY_ERR
453lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
454{
455 LY_ERR ret;
456 struct lyd_value_binary *orig_val, *dup_val;
457
458 memset(dup, 0, sizeof *dup);
459
460 ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
461 LY_CHECK_GOTO(ret, error);
462
463 LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
464 LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
465
466 LYD_VALUE_GET(original, orig_val);
467
468 dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
469 LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
470
471 memcpy(dup_val->data, orig_val->data, orig_val->size);
472 dup_val->size = orig_val->size;
473 dup->realtype = original->realtype;
474
475 return LY_SUCCESS;
476
477error:
478 lyplg_type_free_binary(ctx, dup);
479 return ret;
480}
481
482static void
483lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
484{
485 struct lyd_value_binary *val;
486
487 lydict_remove(ctx, value->_canonical);
488 value->_canonical = NULL;
489 LYD_VALUE_GET(value, val);
490 if (val) {
491 free(val->data);
493 }
494}
495
504 {
505 .module = "",
506 .revision = NULL,
507 .name = LY_TYPE_BINARY_STR,
508
509 .plugin.id = "ly2 binary",
510 .plugin.lyb_size = lyplg_type_lyb_size_variable_bytes,
511 .plugin.store = lyplg_type_store_binary,
512 .plugin.validate_value = lyplg_type_validate_value_binary,
513 .plugin.validate_tree = NULL,
514 .plugin.compare = lyplg_type_compare_binary,
515 .plugin.sort = lyplg_type_sort_binary,
516 .plugin.print = lyplg_type_print_binary,
517 .plugin.duplicate = lyplg_type_dup_binary,
518 .plugin.free = lyplg_type_free_binary,
519 },
520 {0}
521};
const struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition binary.c:503
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:252
@ LYVE_DATA
Definition log.h:289
@ LY_EINVAL
Definition log.h:256
@ LY_EMEM
Definition log.h:254
@ LY_ENOT
Definition log.h:266
@ LY_EVALID
Definition log.h:260
@ LY_SUCCESS
Definition log.h:253
Libyang full error structure.
Definition log.h:297
const char *const char * revision
#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 lyplg_type_check_hints(uint32_t hints, const char *value, uint32_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_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, uint32_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_value_size(const char *type_name, LY_VALUE_FORMAT format, uint64_t value_size_bits, enum lyplg_lyb_size_type lyb_size_type, uint64_t lyb_fixed_size_bits, uint32_t *value_size, struct ly_err_item **err)
Check a value type in bits is correct and as expected.
#define LYPLG_TYPE_VAL_IS_DYN(type_val)
Check whether specific type value needs to be allocated dynamically.
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.
@ LYPLG_LYB_SIZE_VARIABLE_BYTES
LIBYANG_API_DECL void lyplg_type_lyb_size_variable_bytes(const struct lysc_type *type, enum lyplg_lyb_size_type *size_type, uint64_t *fixed_size_bits)
Implementation of lyplg_type_lyb_size_clb for a type with variable length rounded to bytes.
#define LYPLG_TYPE_STORE_DYNAMIC
#define LYPLG_TYPE_STORE_ONLY
LY_DATA_TYPE basetype
struct lysc_range * length
Compiled YANG data node.
LY_VALUE_FORMAT
All kinds of supported value formats and prefix mappings to modules.
Definition tree.h:234
@ LY_TYPE_BINARY
Definition tree.h:204
@ LY_VALUE_CANON
Definition tree.h:235
@ LY_VALUE_LYB
Definition tree.h:240
The main libyang public header.
uint8_t ly_bool
Type to indicate boolean value.
Definition log.h:36
API for (user) types plugins.
LIBYANG_API_DECL const char * lyd_value_get_canonical(const struct ly_ctx *ctx, const struct lyd_value *value)
Get the (canonical) value of a lyd_value.
const struct lysc_type * realtype
Definition tree_data.h:527
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition tree_data.h:566
const char * _canonical
Definition tree_data.h:524
YANG data representation.
Definition tree_data.h:523
Special lyd_value structure for built-in binary values.
Definition tree_data.h:605
#define LOGMEM(CTX)
Definition tree_edit.h:22