libyang 6.4.3
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 "compat.h"
26#include "dict.h"
27#include "ly_common.h"
28#include "plugins_internal.h" /* LY_TYPE_*_STR */
29
42static const char b64_etable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
43
44static void lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value);
45static LY_ERR lyplg_type_validate_value_binary(const struct ly_ctx *ctx, const struct lysc_type *type,
46 struct lyd_value *storage, struct ly_err_item **err);
47
60static LY_ERR
61binary_base64_encode(const struct ly_ctx *ctx, const char *data, uint32_t size, char **str, uint32_t *str_len)
62{
63 uint32_t i;
64 char *ptr;
65
66 *str_len = (size + 2) / 3 * 4;
67 *str = malloc(*str_len + 1);
68 LY_CHECK_ERR_RET(!*str, LOGMEM(ctx), LY_EMEM);
69 if (!(*str_len)) {
70 **str = 0;
71 return LY_SUCCESS;
72 }
73
74 ptr = *str;
75 for (i = 0; i + 2 < size; i += 3) {
76 *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
77 *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
78 *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
79 *ptr++ = b64_etable[data[i + 2] & 0x3F];
80 }
81 if (i < size) {
82 *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
83 if (i == (size - 1)) {
84 *ptr++ = b64_etable[((data[i] & 0x3) << 4)];
85 *ptr++ = '=';
86 } else {
87 *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
88 *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2)];
89 }
90 *ptr++ = '=';
91 }
92 *ptr = '\0';
93
94 return LY_SUCCESS;
95}
96
100static const int b64_dtable[256] = {
101 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
104 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
105 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
106 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
107 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
108};
109
121static LY_ERR
122binary_base64_decode(const char *value, uint32_t value_size, void **data, uint32_t *size)
123{
124 unsigned char *ptr = (unsigned char *)value;
125 uint32_t pad_chars, octet_count;
126 char *str;
127
128 if (!value_size || (ptr[value_size - 1] != '=')) {
129 pad_chars = 0;
130 } else if (ptr[value_size - 2] == '=') {
131 pad_chars = 1;
132 } else {
133 pad_chars = 2;
134 }
135
136 octet_count = ((value_size + 3) / 4 - (pad_chars ? 1 : 0)) * 4;
137 *size = octet_count / 4 * 3 + pad_chars;
138
139 str = malloc(*size + 1);
140 LY_CHECK_RET(!str, LY_EMEM);
141 str[*size] = '\0';
142
143 for (uint32_t i = 0, j = 0; i < octet_count; i += 4) {
144 int n = b64_dtable[ptr[i]] << 18 | b64_dtable[ptr[i + 1]] << 12 | b64_dtable[ptr[i + 2]] << 6 | b64_dtable[ptr[i + 3]];
145
146 str[j++] = n >> 16;
147 str[j++] = n >> 8 & 0xFF;
148 str[j++] = n & 0xFF;
149 }
150 if (pad_chars) {
151 int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
152
153 str[*size - pad_chars] = n >> 16;
154
155 if (pad_chars == 2) {
156 n |= b64_dtable[ptr[octet_count + 2]] << 6;
157 n >>= 8 & 0xFF;
158 str[*size - pad_chars + 1] = n;
159 }
160 }
161
162 *data = str;
163 return LY_SUCCESS;
164}
165
174static LY_ERR
175binary_base64_validate(const char *value, uint32_t value_size, struct ly_err_item **err)
176{
177 uint32_t idx, pad;
178
179 /* check correct characters in base64 */
180 idx = 0;
181 while ((idx < value_size) &&
182 ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
183 (('a' <= value[idx]) && (value[idx] <= 'z')) ||
184 (('0' <= value[idx]) && (value[idx] <= '9')) ||
185 ('+' == value[idx]) || ('/' == value[idx]))) {
186 idx++;
187 }
188
189 /* find end of padding */
190 pad = 0;
191 while ((idx + pad < value_size) && (pad < 2) && (value[idx + pad] == '=')) {
192 pad++;
193 }
194
195 /* check if value is valid base64 value */
196 if (value_size != idx + pad) {
197 if (isprint(value[idx + pad])) {
198 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
199 } else {
200 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
201 }
202 }
203
204 if (value_size & 3) {
205 /* base64 length must be multiple of 4 chars */
206 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
207 }
208
209 return LY_SUCCESS;
210}
211
221static LY_ERR
222binary_base64_newlines(char **value, uint32_t *value_size, uint32_t *options, struct ly_err_item **err)
223{
224 char *val;
225 uint32_t size;
226
227 if ((*value_size < 65) || ((*value)[64] != '\n')) {
228 /* no newlines */
229 return LY_SUCCESS;
230 }
231
232 if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
233 /* make the value dynamic so we can modify it */
234 *value = strndup(*value, *value_size);
235 LY_CHECK_RET(!*value, LY_EMEM);
236 *options |= LYPLG_TYPE_STORE_DYNAMIC;
237 }
238
239 val = *value;
240 size = *value_size;
241 while (size > 64) {
242 if (val[64] != '\n') {
243 /* missing, error */
244 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
245 }
246
247 /* remove the newline */
248 memmove(val + 64, val + 65, size - 64);
249 --(*value_size);
250 val += 64;
251 size -= 65;
252 }
253
254 return LY_SUCCESS;
255}
256
257static LY_ERR
258lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, uint64_t value_size_bits,
259 uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
260 const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
261 struct ly_err_item **err)
262{
263 LY_ERR ret = LY_SUCCESS;
264 struct lyd_value_binary *val;
265 uint32_t value_size;
266
267 /* init storage */
268 memset(storage, 0, sizeof *storage);
269 LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
270 LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
271 storage->realtype = type;
272
273 /* check value length */
274 ret = lyplg_type_check_value_size("binary", format, value_size_bits, LYPLG_LYB_SIZE_VARIABLE_BYTES, 0, &value_size,
275 err);
276 LY_CHECK_GOTO(ret, cleanup);
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_size) {
284 val->data = malloc(value_size);
285 LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
286 memcpy(val->data, value, value_size);
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_size;
294
295 /* success */
296 goto cleanup;
297 }
298
299 /* check hints */
300 ret = lyplg_type_check_hints(hints, value, value_size, 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_size, &options, err);
306 LY_CHECK_GOTO(ret, cleanup);
307
308 /* validate */
309 ret = binary_base64_validate(value, value_size, err);
310 LY_CHECK_GOTO(ret, cleanup);
311 }
312
313 /* get the binary value */
314 ret = binary_base64_decode(value, value_size, &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
323 /* value may have been freed */
324 value = storage->_canonical;
325 } else {
326 ret = lydict_insert(ctx, value_size ? value : "", value_size, &storage->_canonical);
327 LY_CHECK_GOTO(ret, cleanup);
328 }
329
330 if (!(options & LYPLG_TYPE_STORE_ONLY)) {
331 /* validate value */
332 ret = lyplg_type_validate_value_binary(ctx, type, storage, err);
333 LY_CHECK_GOTO(ret, cleanup);
334 }
335
336cleanup:
337 if (options & LYPLG_TYPE_STORE_DYNAMIC) {
338 free((void *)value);
339 }
340
341 if (ret) {
342 lyplg_type_free_binary(ctx, storage);
343 }
344 return ret;
345}
346
350static LY_ERR
351lyplg_type_validate_value_binary(const struct ly_ctx *ctx, const struct lysc_type *type, struct lyd_value *storage,
352 struct ly_err_item **err)
353{
354 struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
355 struct lyd_value_binary *val;
356 const void *value;
357 size_t value_len;
358
359 LY_CHECK_ARG_RET(NULL, type, storage, err, LY_EINVAL);
360
361 val = LYPLG_TYPE_VAL_IS_DYN(val) ? (struct lyd_value_binary *)(storage->dyn_mem) : (struct lyd_value_binary *)(storage->fixed_mem);
362 value = lyd_value_get_canonical(ctx, storage);
363 value_len = strlen(value);
364 *err = NULL;
365
366 /* length restriction of the binary value */
367 if (type_bin->length) {
368 LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, val->size, value, value_len, err));
369 }
370
371 return LY_SUCCESS;
372}
373
374static LY_ERR
375lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
376{
377 struct lyd_value_binary *v1, *v2;
378
379 LYD_VALUE_GET(val1, v1);
380 LYD_VALUE_GET(val2, v2);
381
382 if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
383 return LY_ENOT;
384 }
385 return LY_SUCCESS;
386}
387
388static int
389lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
390{
391 struct lyd_value_binary *v1, *v2;
392 int cmp;
393
394 LYD_VALUE_GET(val1, v1);
395 LYD_VALUE_GET(val2, v2);
396
397 if (v1->size < v2->size) {
398 return -1;
399 } else if (v1->size > v2->size) {
400 return 1;
401 }
402
403 cmp = memcmp(v1->data, v2->data, v1->size);
404
405 return cmp;
406}
407
408static const void *
409lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
410 void *UNUSED(prefix_data), ly_bool *dynamic, uint64_t *value_size_bits)
411{
412 struct lyd_value_binary *val;
413 char *ret;
414 uint32_t ret_size = 0;
415
416 LYD_VALUE_GET(value, val);
417
418 if (format == LY_VALUE_LYB) {
419 *dynamic = 0;
420 if (value_size_bits) {
421 *value_size_bits = (uint64_t)val->size * 8;
422 }
423 return val->data;
424 }
425
426 /* generate canonical value if not already */
427 if (!value->_canonical) {
428 /* get the base64 string value */
429 if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_size)) {
430 return NULL;
431 }
432
433 /* store it */
434 if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
435 LOGMEM(ctx);
436 return NULL;
437 }
438 }
439
440 /* use the cached canonical value */
441 if (dynamic) {
442 *dynamic = 0;
443 }
444 if (value_size_bits) {
445 *value_size_bits = ret_size ? ret_size * 8 : strlen(value->_canonical) * 8;
446 }
447 return value->_canonical;
448}
449
450static LY_ERR
451lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
452{
453 LY_ERR ret;
454 struct lyd_value_binary *orig_val, *dup_val;
455
456 memset(dup, 0, sizeof *dup);
457
458 ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
459 LY_CHECK_GOTO(ret, error);
460
461 LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
462 LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
463
464 LYD_VALUE_GET(original, orig_val);
465
466 dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
467 LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
468
469 memcpy(dup_val->data, orig_val->data, orig_val->size);
470 dup_val->size = orig_val->size;
471 dup->realtype = original->realtype;
472
473 return LY_SUCCESS;
474
475error:
476 lyplg_type_free_binary(ctx, dup);
477 return ret;
478}
479
480static void
481lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
482{
483 struct lyd_value_binary *val;
484
485 lydict_remove(ctx, value->_canonical);
486 value->_canonical = NULL;
487 LYD_VALUE_GET(value, val);
488 if (val) {
489 free(val->data);
491 }
492}
493
502 {
503 .module = "",
504 .revision = NULL,
505 .name = LY_TYPE_BINARY_STR,
506
507 .plugin.id = "ly2 binary",
508 .plugin.lyb_size = lyplg_type_lyb_size_variable_bytes,
509 .plugin.store = lyplg_type_store_binary,
510 .plugin.validate_value = lyplg_type_validate_value_binary,
511 .plugin.validate_tree = NULL,
512 .plugin.compare = lyplg_type_compare_binary,
513 .plugin.sort = lyplg_type_sort_binary,
514 .plugin.print = lyplg_type_print_binary,
515 .plugin.duplicate = lyplg_type_dup_binary,
516 .plugin.free = lyplg_type_free_binary,
517 },
518 {0}
519};
const struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition binary.c:501
libyang dictionary
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:290
@ 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:298
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.
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:614
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition tree_data.h:653
const char * _canonical
Definition tree_data.h:611
YANG data representation.
Definition tree_data.h:610
Special lyd_value structure for built-in binary values.
Definition tree_data.h:692
@ LY_TYPE_BINARY
Definition utils.h:63
LY_VALUE_FORMAT
All kinds of supported value formats and prefix mappings to modules.
Definition utils.h:93
@ LY_VALUE_CANON
Definition utils.h:94
@ LY_VALUE_LYB
Definition utils.h:100