libyang 2.1.148
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 LYD_VALUE_GET(val1, v1);
344 LYD_VALUE_GET(val2, v2);
345
346 if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
347 return LY_ENOT;
348 }
349 return LY_SUCCESS;
350}
351
352LIBYANG_API_DEF const void *
353lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
354 void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
355{
356 struct lyd_value_binary *val;
357 char *ret;
358 size_t ret_len = 0;
359
360 LYD_VALUE_GET(value, val);
361
362 if (format == LY_VALUE_LYB) {
363 *dynamic = 0;
364 if (value_len) {
365 *value_len = val->size;
366 }
367 return val->data;
368 }
369
370 /* generate canonical value if not already */
371 if (!value->_canonical) {
372 /* get the base64 string value */
373 if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
374 return NULL;
375 }
376
377 /* store it */
378 if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
379 LOGMEM(ctx);
380 return NULL;
381 }
382 }
383
384 /* use the cached canonical value */
385 if (dynamic) {
386 *dynamic = 0;
387 }
388 if (value_len) {
389 *value_len = ret_len ? ret_len : strlen(value->_canonical);
390 }
391 return value->_canonical;
392}
393
394LIBYANG_API_DEF LY_ERR
395lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
396{
397 LY_ERR ret;
398 struct lyd_value_binary *orig_val, *dup_val;
399
400 memset(dup, 0, sizeof *dup);
401
402 ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
403 LY_CHECK_GOTO(ret, error);
404
405 LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
406 LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
407
408 LYD_VALUE_GET(original, orig_val);
409
410 dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
411 LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
412
413 memcpy(dup_val->data, orig_val->data, orig_val->size);
414 dup_val->size = orig_val->size;
415 dup->realtype = original->realtype;
416
417 return LY_SUCCESS;
418
419error:
420 lyplg_type_free_binary(ctx, dup);
421 return ret;
422}
423
424LIBYANG_API_DEF void
425lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
426{
427 struct lyd_value_binary *val;
428
429 lydict_remove(ctx, value->_canonical);
430 value->_canonical = NULL;
431 LYD_VALUE_GET(value, val);
432 if (val) {
433 free(val->data);
435 }
436}
437
446 {
447 .module = "",
448 .revision = NULL,
449 .name = LY_TYPE_BINARY_STR,
450
451 .plugin.id = "libyang 2 - binary, version 1",
452 .plugin.store = lyplg_type_store_binary,
453 .plugin.validate = NULL,
454 .plugin.compare = lyplg_type_compare_binary,
455 .plugin.sort = NULL,
456 .plugin.print = lyplg_type_print_binary,
457 .plugin.duplicate = lyplg_type_dup_binary,
458 .plugin.free = lyplg_type_free_binary,
459 .plugin.lyb_data_len = -1,
460 },
461 {0}
462};
const struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition binary.c:445
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:353
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:248
@ LYVE_DATA
Definition log.h:285
@ LY_EMEM
Definition log.h:250
@ LY_ENOT
Definition log.h:262
@ LY_EVALID
Definition log.h:256
@ LY_SUCCESS
Definition log.h:249
Libyang full error structure.
Definition log.h:293
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:425
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:395
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
#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