libyang 2.1.80
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 "common.h"
28#include "compat.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
172static LY_ERR
173binary_base64_validate(const char *value, size_t value_len, const struct lysc_type_bin *type, struct ly_err_item **err)
174{
175 uint32_t idx, pad;
176
177 /* check correct characters in base64 */
178 idx = 0;
179 while ((idx < value_len) &&
180 ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
181 (('a' <= value[idx]) && (value[idx] <= 'z')) ||
182 (('0' <= value[idx]) && (value[idx] <= '9')) ||
183 ('+' == value[idx]) || ('/' == value[idx]))) {
184 idx++;
185 }
186
187 /* find end of padding */
188 pad = 0;
189 while ((idx + pad < value_len) && (pad < 2) && (value[idx + pad] == '=')) {
190 pad++;
191 }
192
193 /* check if value is valid base64 value */
194 if (value_len != idx + pad) {
195 if (isprint(value[idx + pad])) {
196 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
197 } else {
198 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
199 }
200 }
201
202 if (value_len & 3) {
203 /* base64 length must be multiple of 4 chars */
204 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
205 }
206
207 /* length restriction of the binary value */
208 if (type->length) {
209 const uint32_t octet_count = ((idx + pad) / 4) * 3 - pad;
210
211 LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type->length, octet_count, value, value_len, err));
212 }
213
214 return LY_SUCCESS;
215}
216
226static LY_ERR
227binary_base64_newlines(char **value, size_t *value_len, uint32_t *options, struct ly_err_item **err)
228{
229 char *val;
230 size_t len;
231
232 if ((*value_len < 65) || ((*value)[64] != '\n')) {
233 /* no newlines */
234 return LY_SUCCESS;
235 }
236
237 if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
238 /* make the value dynamic so we can modify it */
239 *value = strndup(*value, *value_len);
240 LY_CHECK_RET(!*value, LY_EMEM);
241 *options |= LYPLG_TYPE_STORE_DYNAMIC;
242 }
243
244 val = *value;
245 len = *value_len;
246 while (len > 64) {
247 if (val[64] != '\n') {
248 /* missing, error */
249 return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
250 }
251
252 /* remove the newline */
253 memmove(val + 64, val + 65, len - 64);
254 --(*value_len);
255 val += 64;
256 len -= 65;
257 }
258
259 return LY_SUCCESS;
260}
261
262LIBYANG_API_DEF LY_ERR
263lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
264 uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
265 const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
266 struct ly_err_item **err)
267{
268 LY_ERR ret = LY_SUCCESS;
269 struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
270 struct lyd_value_binary *val;
271
272 /* init storage */
273 memset(storage, 0, sizeof *storage);
274 LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
275 LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
276 storage->realtype = type;
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_len) {
284 val->data = malloc(value_len);
285 LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
286 memcpy(val->data, value, value_len);
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_len;
294
295 /* success */
296 goto cleanup;
297 }
298
299 /* check hints */
300 ret = lyplg_type_check_hints(hints, value, value_len, 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_len, &options, err);
306 LY_CHECK_GOTO(ret, cleanup);
307
308 /* validate */
309 ret = binary_base64_validate(value, value_len, type_bin, err);
310 LY_CHECK_GOTO(ret, cleanup);
311 }
312
313 /* get the binary value */
314 ret = binary_base64_decode(value, value_len, &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 } else {
323 ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
324 LY_CHECK_GOTO(ret, cleanup);
325 }
326
327cleanup:
328 if (options & LYPLG_TYPE_STORE_DYNAMIC) {
329 free((void *)value);
330 }
331
332 if (ret) {
333 lyplg_type_free_binary(ctx, storage);
334 }
335 return ret;
336}
337
338LIBYANG_API_DEF LY_ERR
339lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2)
340{
341 struct lyd_value_binary *v1, *v2;
342
343 if (val1->realtype != val2->realtype) {
344 return LY_ENOT;
345 }
346
347 LYD_VALUE_GET(val1, v1);
348 LYD_VALUE_GET(val2, v2);
349
350 if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
351 return LY_ENOT;
352 }
353 return LY_SUCCESS;
354}
355
356LIBYANG_API_DEF const void *
357lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
358 void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
359{
360 struct lyd_value_binary *val;
361 char *ret;
362 size_t ret_len = 0;
363
364 LYD_VALUE_GET(value, val);
365
366 if (format == LY_VALUE_LYB) {
367 *dynamic = 0;
368 if (value_len) {
369 *value_len = val->size;
370 }
371 return val->data;
372 }
373
374 /* generate canonical value if not already */
375 if (!value->_canonical) {
376 /* get the base64 string value */
377 if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
378 return NULL;
379 }
380
381 /* store it */
382 if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
383 LOGMEM(ctx);
384 return NULL;
385 }
386 }
387
388 /* use the cached canonical value */
389 if (dynamic) {
390 *dynamic = 0;
391 }
392 if (value_len) {
393 *value_len = ret_len ? ret_len : strlen(value->_canonical);
394 }
395 return value->_canonical;
396}
397
398LIBYANG_API_DEF LY_ERR
399lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
400{
401 LY_ERR ret;
402 struct lyd_value_binary *orig_val, *dup_val;
403
404 memset(dup, 0, sizeof *dup);
405
406 ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
407 LY_CHECK_GOTO(ret, error);
408
409 LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
410 LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
411
412 LYD_VALUE_GET(original, orig_val);
413
414 dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
415 LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
416
417 memcpy(dup_val->data, orig_val->data, orig_val->size);
418 dup_val->size = orig_val->size;
419 dup->realtype = original->realtype;
420
421 return LY_SUCCESS;
422
423error:
424 lyplg_type_free_binary(ctx, dup);
425 return ret;
426}
427
428LIBYANG_API_DEF void
429lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
430{
431 struct lyd_value_binary *val;
432
433 lydict_remove(ctx, value->_canonical);
434 value->_canonical = NULL;
435 LYD_VALUE_GET(value, val);
436 if (val) {
437 free(val->data);
439 }
440}
441
450 {
451 .module = "",
452 .revision = NULL,
453 .name = LY_TYPE_BINARY_STR,
454
455 .plugin.id = "libyang 2 - binary, version 1",
456 .plugin.store = lyplg_type_store_binary,
457 .plugin.validate = NULL,
458 .plugin.compare = lyplg_type_compare_binary,
459 .plugin.sort = NULL,
460 .plugin.print = lyplg_type_print_binary,
461 .plugin.duplicate = lyplg_type_dup_binary,
462 .plugin.free = lyplg_type_free_binary,
463 .plugin.lyb_data_len = -1,
464 },
465 {0}
466};
const struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition binary.c:449
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:263
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:357
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:251
@ LYVE_DATA
Definition log.h:288
@ LY_EMEM
Definition log.h:253
@ LY_ENOT
Definition log.h:265
@ LY_EVALID
Definition log.h:259
@ LY_SUCCESS
Definition log.h:252
Libyang full error structure.
Definition log.h:296
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:429
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:399
LIBYANG_API_DEF LY_ERR lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2)
Implementation of lyplg_type_compare_clb for the built-in binary type.
Definition binary.c:339
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 ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag, const char *err_format,...) _FORMAT_PRINTF(6
Create and fill error structure.
#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.
#define LYPLG_TYPE_STORE_DYNAMIC
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:564
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition tree_data.h:603
const char * _canonical
Definition tree_data.h:561
YANG data representation.
Definition tree_data.h:560
Special lyd_value structure for built-in binary values.
Definition tree_data.h:642
#define LOGMEM(CTX)
Definition tree_edit.h:22