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