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