libyang 3.1.0
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
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
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 } else {
316 ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
317 LY_CHECK_GOTO(ret, cleanup);
318 }
319
320 if (!(options & LYPLG_TYPE_STORE_ONLY)) {
321 /* validate value */
322 ret = lyplg_type_validate_binary(ctx, type, NULL, NULL, storage, err);
323 LY_CHECK_GOTO(ret, cleanup);
324 }
325
326cleanup:
327 if (options & LYPLG_TYPE_STORE_DYNAMIC) {
328 free((void *)value);
329 }
330
331 if (ret) {
332 lyplg_type_free_binary(ctx, storage);
333 }
334 return ret;
335}
336
340static LY_ERR
341lyplg_type_validate_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *UNUSED(ctx_node),
342 const struct lyd_node *UNUSED(tree), struct lyd_value *storage, struct ly_err_item **err)
343{
344 struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
345 struct lyd_value_binary *val;
346 const void *value;
347 size_t value_len;
348
349 LY_CHECK_ARG_RET(NULL, type, storage, err, LY_EINVAL);
350
351 val = LYPLG_TYPE_VAL_IS_DYN(val) ? (struct lyd_value_binary *)(storage->dyn_mem) : (struct lyd_value_binary *)(storage->fixed_mem);
352 value = lyd_value_get_canonical(ctx, storage);
353 value_len = strlen(value);
354 *err = NULL;
355
356 /* length restriction of the binary value */
357 if (type_bin->length) {
358 LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, val->size, value, value_len, err));
359 }
360
361 return LY_SUCCESS;
362}
363
364LIBYANG_API_DEF LY_ERR
365lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
366{
367 struct lyd_value_binary *v1, *v2;
368
369 LYD_VALUE_GET(val1, v1);
370 LYD_VALUE_GET(val2, v2);
371
372 if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
373 return LY_ENOT;
374 }
375 return LY_SUCCESS;
376}
377
378LIBYANG_API_DEF int
379lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
380{
381 struct lyd_value_binary *v1, *v2;
382 int cmp;
383
384 LYD_VALUE_GET(val1, v1);
385 LYD_VALUE_GET(val2, v2);
386
387 if (v1->size < v2->size) {
388 return -1;
389 } else if (v1->size > v2->size) {
390 return 1;
391 }
392
393 cmp = memcmp(v1->data, v2->data, v1->size);
394
395 return cmp;
396}
397
398LIBYANG_API_DEF const void *
399lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
400 void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
401{
402 struct lyd_value_binary *val;
403 char *ret;
404 size_t ret_len = 0;
405
406 LYD_VALUE_GET(value, val);
407
408 if (format == LY_VALUE_LYB) {
409 *dynamic = 0;
410 if (value_len) {
411 *value_len = val->size;
412 }
413 return val->data;
414 }
415
416 /* generate canonical value if not already */
417 if (!value->_canonical) {
418 /* get the base64 string value */
419 if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
420 return NULL;
421 }
422
423 /* store it */
424 if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
425 LOGMEM(ctx);
426 return NULL;
427 }
428 }
429
430 /* use the cached canonical value */
431 if (dynamic) {
432 *dynamic = 0;
433 }
434 if (value_len) {
435 *value_len = ret_len ? ret_len : strlen(value->_canonical);
436 }
437 return value->_canonical;
438}
439
440LIBYANG_API_DEF LY_ERR
441lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
442{
443 LY_ERR ret;
444 struct lyd_value_binary *orig_val, *dup_val;
445
446 memset(dup, 0, sizeof *dup);
447
448 ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
449 LY_CHECK_GOTO(ret, error);
450
451 LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
452 LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
453
454 LYD_VALUE_GET(original, orig_val);
455
456 dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
457 LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
458
459 memcpy(dup_val->data, orig_val->data, orig_val->size);
460 dup_val->size = orig_val->size;
461 dup->realtype = original->realtype;
462
463 return LY_SUCCESS;
464
465error:
466 lyplg_type_free_binary(ctx, dup);
467 return ret;
468}
469
470LIBYANG_API_DEF void
471lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
472{
473 struct lyd_value_binary *val;
474
475 lydict_remove(ctx, value->_canonical);
476 value->_canonical = NULL;
477 LYD_VALUE_GET(value, val);
478 if (val) {
479 free(val->data);
481 }
482}
483
492 {
493 .module = "",
494 .revision = NULL,
495 .name = LY_TYPE_BINARY_STR,
496
497 .plugin.id = "libyang 2 - binary, version 1",
498 .plugin.store = lyplg_type_store_binary,
499 .plugin.validate = lyplg_type_validate_binary,
500 .plugin.compare = lyplg_type_compare_binary,
501 .plugin.sort = lyplg_type_sort_binary,
502 .plugin.print = lyplg_type_print_binary,
503 .plugin.duplicate = lyplg_type_dup_binary,
504 .plugin.free = lyplg_type_free_binary,
505 .plugin.lyb_data_len = -1,
506 },
507 {0}
508};
const struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition binary.c:491
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:379
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:399
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:365
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_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:471
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:441
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.
#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:575
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition tree_data.h:614
const char * _canonical
Definition tree_data.h:572
Generic structure for a data node.
Definition tree_data.h:799
YANG data representation.
Definition tree_data.h:571
Special lyd_value structure for built-in binary values.
Definition tree_data.h:653
#define LOGMEM(CTX)
Definition tree_edit.h:22