libyang 3.4.2
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
57static LY_ERR
58binary_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 + 2 < size; 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
97static 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
118static LY_ERR
119binary_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
143 str[j++] = n >> 16;
144 str[j++] = n >> 8 & 0xFF;
145 str[j++] = n & 0xFF;
146 }
147 if (pad_chars) {
148 int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
149
150 str[*size - pad_chars] = n >> 16;
151
152 if (pad_chars == 2) {
153 n |= b64_dtable[ptr[octet_count + 2]] << 6;
154 n >>= 8 & 0xFF;
155 str[*size - pad_chars + 1] = n;
156 }
157 }
158
159 *data = str;
160 return LY_SUCCESS;
161}
162
171static LY_ERR
172binary_base64_validate(const char *value, size_t value_len, struct ly_err_item **err)
173{
174 uint32_t idx, pad;
175
176 /* check correct characters in base64 */
177 idx = 0;
178 while ((idx < value_len) &&
179 ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
180 (('a' <= value[idx]) && (value[idx] <= 'z')) ||
181 (('0' <= value[idx]) && (value[idx] <= '9')) ||
182 ('+' == value[idx]) || ('/' == value[idx]))) {
183 idx++;
184 }
185
186 /* find end of padding */
187 pad = 0;
188 while ((idx + pad < value_len) && (pad < 2) && (value[idx + pad] == '=')) {
189 pad++;
190 }
191
192 /* check if value is valid base64 value */
193 if (value_len != idx + pad) {
194 if (isprint(value[idx + pad])) {
195 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
196 } else {
197 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
198 }
199 }
200
201 if (value_len & 3) {
202 /* base64 length must be multiple of 4 chars */
203 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
204 }
205
206 return LY_SUCCESS;
207}
208
218static LY_ERR
219binary_base64_newlines(char **value, size_t *value_len, uint32_t *options, struct ly_err_item **err)
220{
221 char *val;
222 size_t len;
223
224 if ((*value_len < 65) || ((*value)[64] != '\n')) {
225 /* no newlines */
226 return LY_SUCCESS;
227 }
228
229 if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
230 /* make the value dynamic so we can modify it */
231 *value = strndup(*value, *value_len);
232 LY_CHECK_RET(!*value, LY_EMEM);
233 *options |= LYPLG_TYPE_STORE_DYNAMIC;
234 }
235
236 val = *value;
237 len = *value_len;
238 while (len > 64) {
239 if (val[64] != '\n') {
240 /* missing, error */
241 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
242 }
243
244 /* remove the newline */
245 memmove(val + 64, val + 65, len - 64);
246 --(*value_len);
247 val += 64;
248 len -= 65;
249 }
250
251 return LY_SUCCESS;
252}
253
254LIBYANG_API_DEF LY_ERR
255lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
256 uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
257 const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
258 struct ly_err_item **err)
259{
260 LY_ERR ret = LY_SUCCESS;
261 struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
262 struct lyd_value_binary *val;
263
264 /* init storage */
265 memset(storage, 0, sizeof *storage);
266 LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
267 LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
268 storage->realtype = type;
269
270 if (format == LY_VALUE_LYB) {
271 /* store value */
272 if (options & LYPLG_TYPE_STORE_DYNAMIC) {
273 val->data = (void *)value;
274 options &= ~LYPLG_TYPE_STORE_DYNAMIC;
275 } else if (value_len) {
276 val->data = malloc(value_len);
277 LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
278 memcpy(val->data, value, value_len);
279 } else {
280 val->data = strdup("");
281 LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
282 }
283
284 /* store size */
285 val->size = value_len;
286
287 /* success */
288 goto cleanup;
289 }
290
291 /* check hints */
292 ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
293 LY_CHECK_GOTO(ret, cleanup);
294
295 if (format != LY_VALUE_CANON) {
296 /* accept newline every 64 characters (PEM data) */
297 ret = binary_base64_newlines((char **)&value, &value_len, &options, err);
298 LY_CHECK_GOTO(ret, cleanup);
299
300 /* validate */
301 ret = binary_base64_validate(value, value_len, err);
302 LY_CHECK_GOTO(ret, cleanup);
303 }
304
305 /* get the binary value */
306 ret = binary_base64_decode(value, value_len, &val->data, &val->size);
307 LY_CHECK_GOTO(ret, cleanup);
308
309 /* store canonical value */
310 if (options & LYPLG_TYPE_STORE_DYNAMIC) {
311 ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
312 options &= ~LYPLG_TYPE_STORE_DYNAMIC;
313 LY_CHECK_GOTO(ret, cleanup);
314
315 /* value may have been freed */
316 value = storage->_canonical;
317 } else {
318 ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
319 LY_CHECK_GOTO(ret, cleanup);
320 }
321
322 if (!(options & LYPLG_TYPE_STORE_ONLY)) {
323 /* validate length restriction of the binary value */
324 if (type_bin->length) {
325 ret = lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, val->size, value, value_len, err);
326 LY_CHECK_GOTO(ret, cleanup);
327 }
328 }
329
330cleanup:
331 if (options & LYPLG_TYPE_STORE_DYNAMIC) {
332 free((void *)value);
333 }
334
335 if (ret) {
336 lyplg_type_free_binary(ctx, storage);
337 }
338 return ret;
339}
340
341LIBYANG_API_DEF LY_ERR
342lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
343{
344 struct lyd_value_binary *v1, *v2;
345
346 LYD_VALUE_GET(val1, v1);
347 LYD_VALUE_GET(val2, v2);
348
349 if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
350 return LY_ENOT;
351 }
352 return LY_SUCCESS;
353}
354
355LIBYANG_API_DEF int
356lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
357{
358 struct lyd_value_binary *v1, *v2;
359 int cmp;
360
361 LYD_VALUE_GET(val1, v1);
362 LYD_VALUE_GET(val2, v2);
363
364 if (v1->size < v2->size) {
365 return -1;
366 } else if (v1->size > v2->size) {
367 return 1;
368 }
369
370 cmp = memcmp(v1->data, v2->data, v1->size);
371
372 return cmp;
373}
374
375LIBYANG_API_DEF const void *
376lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
377 void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
378{
379 struct lyd_value_binary *val;
380 char *ret;
381 size_t ret_len = 0;
382
383 LYD_VALUE_GET(value, val);
384
385 if (format == LY_VALUE_LYB) {
386 *dynamic = 0;
387 if (value_len) {
388 *value_len = val->size;
389 }
390 return val->data;
391 }
392
393 /* generate canonical value if not already */
394 if (!value->_canonical) {
395 /* get the base64 string value */
396 if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
397 return NULL;
398 }
399
400 /* store it */
401 if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
402 LOGMEM(ctx);
403 return NULL;
404 }
405 }
406
407 /* use the cached canonical value */
408 if (dynamic) {
409 *dynamic = 0;
410 }
411 if (value_len) {
412 *value_len = ret_len ? ret_len : strlen(value->_canonical);
413 }
414 return value->_canonical;
415}
416
417LIBYANG_API_DEF LY_ERR
418lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
419{
420 LY_ERR ret;
421 struct lyd_value_binary *orig_val, *dup_val;
422
423 memset(dup, 0, sizeof *dup);
424
425 ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
426 LY_CHECK_GOTO(ret, error);
427
428 LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
429 LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
430
431 LYD_VALUE_GET(original, orig_val);
432
433 dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
434 LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
435
436 memcpy(dup_val->data, orig_val->data, orig_val->size);
437 dup_val->size = orig_val->size;
438 dup->realtype = original->realtype;
439
440 return LY_SUCCESS;
441
442error:
443 lyplg_type_free_binary(ctx, dup);
444 return ret;
445}
446
447LIBYANG_API_DEF void
448lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
449{
450 struct lyd_value_binary *val;
451
452 lydict_remove(ctx, value->_canonical);
453 value->_canonical = NULL;
454 LYD_VALUE_GET(value, val);
455 if (val) {
456 free(val->data);
458 }
459}
460
469 {
470 .module = "",
471 .revision = NULL,
472 .name = LY_TYPE_BINARY_STR,
473
474 .plugin.id = "libyang 2 - binary, version 1",
475 .plugin.store = lyplg_type_store_binary,
476 .plugin.validate = NULL,
477 .plugin.compare = lyplg_type_compare_binary,
478 .plugin.sort = lyplg_type_sort_binary,
479 .plugin.print = lyplg_type_print_binary,
480 .plugin.duplicate = lyplg_type_dup_binary,
481 .plugin.free = lyplg_type_free_binary,
482 .plugin.lyb_data_len = -1,
483 },
484 {0}
485};
const struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition binary.c:468
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:255
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:356
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:376
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:342
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_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:448
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:418
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.
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.
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
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