/** * @file parser.c * @author Radek Krejci * @brief common libyang parsers routines implementations * * Copyright (c) 2015-2017 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "context.h" #include "libyang.h" #include "parser.h" #include "resolve.h" #include "tree_internal.h" #include "parser_yang.h" #include "xpath.h" #define LYP_URANGE_LEN 19 static char *lyp_ublock2urange[][2] = { {"BasicLatin", "[\\x{0000}-\\x{007F}]"}, {"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"}, {"LatinExtended-A", "[\\x{0100}-\\x{017F}]"}, {"LatinExtended-B", "[\\x{0180}-\\x{024F}]"}, {"IPAExtensions", "[\\x{0250}-\\x{02AF}]"}, {"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"}, {"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"}, {"Greek", "[\\x{0370}-\\x{03FF}]"}, {"Cyrillic", "[\\x{0400}-\\x{04FF}]"}, {"Armenian", "[\\x{0530}-\\x{058F}]"}, {"Hebrew", "[\\x{0590}-\\x{05FF}]"}, {"Arabic", "[\\x{0600}-\\x{06FF}]"}, {"Syriac", "[\\x{0700}-\\x{074F}]"}, {"Thaana", "[\\x{0780}-\\x{07BF}]"}, {"Devanagari", "[\\x{0900}-\\x{097F}]"}, {"Bengali", "[\\x{0980}-\\x{09FF}]"}, {"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"}, {"Gujarati", "[\\x{0A80}-\\x{0AFF}]"}, {"Oriya", "[\\x{0B00}-\\x{0B7F}]"}, {"Tamil", "[\\x{0B80}-\\x{0BFF}]"}, {"Telugu", "[\\x{0C00}-\\x{0C7F}]"}, {"Kannada", "[\\x{0C80}-\\x{0CFF}]"}, {"Malayalam", "[\\x{0D00}-\\x{0D7F}]"}, {"Sinhala", "[\\x{0D80}-\\x{0DFF}]"}, {"Thai", "[\\x{0E00}-\\x{0E7F}]"}, {"Lao", "[\\x{0E80}-\\x{0EFF}]"}, {"Tibetan", "[\\x{0F00}-\\x{0FFF}]"}, {"Myanmar", "[\\x{1000}-\\x{109F}]"}, {"Georgian", "[\\x{10A0}-\\x{10FF}]"}, {"HangulJamo", "[\\x{1100}-\\x{11FF}]"}, {"Ethiopic", "[\\x{1200}-\\x{137F}]"}, {"Cherokee", "[\\x{13A0}-\\x{13FF}]"}, {"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"}, {"Ogham", "[\\x{1680}-\\x{169F}]"}, {"Runic", "[\\x{16A0}-\\x{16FF}]"}, {"Khmer", "[\\x{1780}-\\x{17FF}]"}, {"Mongolian", "[\\x{1800}-\\x{18AF}]"}, {"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"}, {"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"}, {"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"}, {"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"}, {"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"}, {"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"}, {"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"}, {"NumberForms", "[\\x{2150}-\\x{218F}]"}, {"Arrows", "[\\x{2190}-\\x{21FF}]"}, {"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"}, {"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"}, {"ControlPictures", "[\\x{2400}-\\x{243F}]"}, {"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"}, {"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"}, {"BoxDrawing", "[\\x{2500}-\\x{257F}]"}, {"BlockElements", "[\\x{2580}-\\x{259F}]"}, {"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"}, {"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"}, {"Dingbats", "[\\x{2700}-\\x{27BF}]"}, {"BraillePatterns", "[\\x{2800}-\\x{28FF}]"}, {"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"}, {"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"}, {"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"}, {"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"}, {"Hiragana", "[\\x{3040}-\\x{309F}]"}, {"Katakana", "[\\x{30A0}-\\x{30FF}]"}, {"Bopomofo", "[\\x{3100}-\\x{312F}]"}, {"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"}, {"Kanbun", "[\\x{3190}-\\x{319F}]"}, {"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"}, {"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"}, {"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"}, {"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"}, {"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"}, {"YiSyllables", "[\\x{A000}-\\x{A48F}]"}, {"YiRadicals", "[\\x{A490}-\\x{A4CF}]"}, {"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"}, {"PrivateUse", "[\\x{E000}-\\x{F8FF}]"}, {"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"}, {"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"}, {"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"}, {"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"}, {"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"}, {"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"}, {"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"}, {"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"}, {"Specials", "[\\x{FEFF}|\\x{FFF0}-\\x{FFFD}]"}, {NULL, NULL} }; const char *ly_stmt_str[] = { [LY_STMT_UNKNOWN] = "", [LY_STMT_ARGUMENT] = "argument", [LY_STMT_BASE] = "base", [LY_STMT_BELONGSTO] = "belongs-to", [LY_STMT_CONTACT] = "contact", [LY_STMT_DEFAULT] = "default", [LY_STMT_DESCRIPTION] = "description", [LY_STMT_ERRTAG] = "error-app-tag", [LY_STMT_ERRMSG] = "error-message", [LY_STMT_KEY] = "key", [LY_STMT_NAMESPACE] = "namespace", [LY_STMT_ORGANIZATION] = "organization", [LY_STMT_PATH] = "path", [LY_STMT_PREFIX] = "prefix", [LY_STMT_PRESENCE] = "presence", [LY_STMT_REFERENCE] = "reference", [LY_STMT_REVISIONDATE] = "revision-date", [LY_STMT_UNITS] = "units", [LY_STMT_VALUE] = "value", [LY_STMT_VERSION] = "yang-version", [LY_STMT_MODIFIER] = "modifier", [LY_STMT_REQINSTANCE] = "require-instance", [LY_STMT_YINELEM] = "yin-element", [LY_STMT_CONFIG] = "config", [LY_STMT_MANDATORY] = "mandatory", [LY_STMT_ORDEREDBY] = "ordered-by", [LY_STMT_STATUS] = "status", [LY_STMT_DIGITS] = "fraction-digits", [LY_STMT_MAX] = "max-elements", [LY_STMT_MIN] = "min-elements", [LY_STMT_POSITION] = "position", [LY_STMT_UNIQUE] = "unique", [LY_STMT_MODULE] = "module", [LY_STMT_SUBMODULE] = "submodule", [LY_STMT_ACTION] = "action", [LY_STMT_ANYDATA] = "anydata", [LY_STMT_ANYXML] = "anyxml", [LY_STMT_CASE] = "case", [LY_STMT_CHOICE] = "choice", [LY_STMT_CONTAINER] = "container", [LY_STMT_GROUPING] = "grouping", [LY_STMT_INPUT] = "input", [LY_STMT_LEAF] = "leaf", [LY_STMT_LEAFLIST] = "leaf-list", [LY_STMT_LIST] = "list", [LY_STMT_NOTIFICATION] = "notification", [LY_STMT_OUTPUT] = "output", [LY_STMT_RPC] = "rpc", [LY_STMT_USES] = "uses", [LY_STMT_TYPEDEF] = "typedef", [LY_STMT_TYPE] = "type", [LY_STMT_BIT] = "bit", [LY_STMT_ENUM] = "enum", [LY_STMT_REFINE] = "refine", [LY_STMT_AUGMENT] = "augment", [LY_STMT_DEVIATE] = "deviate", [LY_STMT_DEVIATION] = "deviation", [LY_STMT_EXTENSION] = "extension", [LY_STMT_FEATURE] = "feature", [LY_STMT_IDENTITY] = "identity", [LY_STMT_IFFEATURE] = "if-feature", [LY_STMT_IMPORT] = "import", [LY_STMT_INCLUDE] = "include", [LY_STMT_LENGTH] = "length", [LY_STMT_MUST] = "must", [LY_STMT_PATTERN] = "pattern", [LY_STMT_RANGE] = "range", [LY_STMT_WHEN] = "when", [LY_STMT_REVISION] = "revision" }; int lyp_is_rpc_action(struct lys_node *node) { assert(node); while (lys_parent(node)) { node = lys_parent(node); if (node->nodetype == LYS_ACTION) { break; } } if (node->nodetype & (LYS_RPC | LYS_ACTION)) { return 1; } else { return 0; } } int lyp_data_check_options(struct ly_ctx *ctx, int options, const char *func) { int x = options & LYD_OPT_TYPEMASK; /* LYD_OPT_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG */ if (options & LYD_OPT_WHENAUTODEL) { if ((x == LYD_OPT_EDIT) || (x == LYD_OPT_NOTIF_FILTER)) { LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG)", func, options); return 1; } } if (options & (LYD_OPT_DATA_ADD_YANGLIB | LYD_OPT_DATA_NO_YANGLIB)) { if (x != LYD_OPT_DATA) { LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_*_YANGLIB can be used only with LYD_OPT_DATA)", func, options); return 1; } } /* "is power of 2" algorithm, with 0 exception */ if (x && !(x && !(x & (x - 1)))) { LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (multiple data type flags set).", func, options); return 1; } return 0; } int lyp_mmap(struct ly_ctx *ctx, int fd, size_t addsize, size_t *length, void **addr) { struct stat sb; long pagesize; size_t m; assert(fd >= 0); if (fstat(fd, &sb) == -1) { LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno)); return 1; } if (!S_ISREG(sb.st_mode)) { LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file."); return 1; } if (!sb.st_size) { *addr = NULL; return 0; } pagesize = sysconf(_SC_PAGESIZE); ++addsize; /* at least one additional byte for terminating NULL byte */ m = sb.st_size % pagesize; if (m && pagesize - m >= addsize) { /* there will be enough space after the file content mapping to provide zeroed additional bytes */ *length = sb.st_size + addsize; *addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); } else { /* there will not be enough bytes after the file content mapping for the additional bytes and some of them * would overflow into another page that would not be zeroed and any access into it would generate SIGBUS. * Therefore we have to do the following hack with double mapping. First, the required number of bytes * (including the additional bytes) is required as anonymous and thus they will be really provided (actually more * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address * where the anonymous mapping starts. */ *length = sb.st_size + pagesize; *addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); *addr = mmap(*addr, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0); } if (*addr == MAP_FAILED) { LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno)); return 1; } return 0; } int lyp_munmap(void *addr, size_t length) { if (!addr) { /* nothing to unmap */ return 0; } return munmap(addr, length); } int lyp_add_ietf_netconf_annotations_config(struct lys_module *mod) { void *reallocated; struct lys_ext_instance_complex *op; struct lys_type **type; struct lys_node_anydata *anyxml; int i; struct ly_ctx *ctx = mod->ctx; /* shortcut */ reallocated = realloc(mod->ext, (mod->ext_size + 3) * sizeof *mod->ext); LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), EXIT_FAILURE); mod->ext = reallocated; /* 1) edit-config's operation */ op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; op->arg_value = lydict_insert(ctx, "operation", 9); op->def = &ctx->models.list[0]->extensions[0]; op->ext_type = LYEXT_COMPLEX; op->module = op->parent = mod; op->parent_type = LYEXT_PAR_MODULE; op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; op->nodetype = LYS_EXT; type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ *type = calloc(1, sizeof(struct lys_type)); LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); (*type)->base = LY_TYPE_ENUM; (*type)->der = ly_types[LY_TYPE_ENUM]; (*type)->parent = (struct lys_tpdf *)op; (*type)->info.enums.count = 5; (*type)->info.enums.enm = calloc(5, sizeof *(*type)->info.enums.enm); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE); (*type)->info.enums.enm[0].value = 0; (*type)->info.enums.enm[0].name = lydict_insert(ctx, "merge", 5); (*type)->info.enums.enm[1].value = 1; (*type)->info.enums.enm[1].name = lydict_insert(ctx, "replace", 7); (*type)->info.enums.enm[2].value = 2; (*type)->info.enums.enm[2].name = lydict_insert(ctx, "create", 6); (*type)->info.enums.enm[3].value = 3; (*type)->info.enums.enm[3].name = lydict_insert(ctx, "delete", 6); (*type)->info.enums.enm[4].value = 4; (*type)->info.enums.enm[4].name = lydict_insert(ctx, "remove", 6); mod->ext_size++; /* 2) filter's type */ op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; op->arg_value = lydict_insert(ctx, "type", 4); op->def = &ctx->models.list[0]->extensions[0]; op->ext_type = LYEXT_COMPLEX; op->module = op->parent = mod; op->parent_type = LYEXT_PAR_MODULE; op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; op->nodetype = LYS_EXT; type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ *type = calloc(1, sizeof(struct lys_type)); LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); (*type)->base = LY_TYPE_ENUM; (*type)->der = ly_types[LY_TYPE_ENUM]; (*type)->parent = (struct lys_tpdf *)op; (*type)->info.enums.count = 2; (*type)->info.enums.enm = calloc(2, sizeof *(*type)->info.enums.enm); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE); (*type)->info.enums.enm[0].value = 0; (*type)->info.enums.enm[0].name = lydict_insert(ctx, "subtree", 7); (*type)->info.enums.enm[1].value = 1; (*type)->info.enums.enm[1].name = lydict_insert(ctx, "xpath", 5); for (i = mod->features_size; i > 0; i--) { if (!strcmp(mod->features[i - 1].name, "xpath")) { (*type)->info.enums.enm[1].iffeature_size = 1; (*type)->info.enums.enm[1].iffeature = calloc(1, sizeof(struct lys_feature)); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature, LOGMEM(ctx), EXIT_FAILURE); (*type)->info.enums.enm[1].iffeature[0].expr = malloc(sizeof(uint8_t)); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].expr, LOGMEM(ctx), EXIT_FAILURE); *(*type)->info.enums.enm[1].iffeature[0].expr = 3; /* LYS_IFF_F */ (*type)->info.enums.enm[1].iffeature[0].features = malloc(sizeof(struct lys_feature*)); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].features, LOGMEM(ctx), EXIT_FAILURE); (*type)->info.enums.enm[1].iffeature[0].features[0] = &mod->features[i - 1]; break; } } mod->ext_size++; /* 3) filter's select */ op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; op->arg_value = lydict_insert(ctx, "select", 6); op->def = &ctx->models.list[0]->extensions[0]; op->ext_type = LYEXT_COMPLEX; op->module = op->parent = mod; op->parent_type = LYEXT_PAR_MODULE; op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; op->nodetype = LYS_EXT; type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ *type = calloc(1, sizeof(struct lys_type)); LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); (*type)->base = LY_TYPE_STRING; (*type)->der = ly_types[LY_TYPE_STRING]; (*type)->parent = (struct lys_tpdf *)op; mod->ext_size++; /* 4) URL config */ anyxml = calloc(1, sizeof *anyxml); LY_CHECK_ERR_RETURN(!anyxml, LOGMEM(ctx), EXIT_FAILURE); anyxml->nodetype = LYS_ANYXML; anyxml->prev = (struct lys_node *)anyxml; anyxml->name = lydict_insert(ctx, "config", 0); anyxml->module = mod; anyxml->flags = LYS_CONFIG_W; if (lys_node_addchild(NULL, mod, (struct lys_node *)anyxml, 0)) { return EXIT_FAILURE; } return EXIT_SUCCESS; } /* logs directly * base: 0 - to accept decimal, octal, hexadecimal (in default value) * 10 - to accept only decimal (instance value) */ static int parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret, struct lyd_node *node) { char *strptr; assert(node); if (!val_str || !val_str[0]) { goto error; } /* convert to 64-bit integer, all the redundant characters are handled */ errno = 0; strptr = NULL; /* parse the value */ *ret = strtoll(val_str, &strptr, base); if (errno || (*ret < min) || (*ret > max)) { goto error; } else if (strptr && *strptr) { while (isspace(*strptr)) { ++strptr; } if (*strptr) { goto error; } } return EXIT_SUCCESS; error: LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name); return EXIT_FAILURE; } /* logs directly * base: 0 - to accept decimal, octal, hexadecimal (in default value) * 10 - to accept only decimal (instance value) */ static int parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret, struct lyd_node *node) { char *strptr; uint64_t u; assert(node); if (!val_str || !val_str[0]) { goto error; } errno = 0; strptr = NULL; u = strtoull(val_str, &strptr, base); if (errno || (u > max)) { goto error; } else if (strptr && *strptr) { while (isspace(*strptr)) { ++strptr; } if (*strptr) { goto error; } } else if (u != 0 && val_str[0] == '-') { goto error; } *ret = u; return EXIT_SUCCESS; error: LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name); return EXIT_FAILURE; } /* logs directly * * kind == 0 - unsigned (unum used), 1 - signed (snum used), 2 - floating point (fnum used) */ static int validate_length_range(uint8_t kind, uint64_t unum, int64_t snum, int64_t fnum, uint8_t fnum_dig, struct lys_type *type, const char *val_str, struct lyd_node *node) { struct lys_restr *restr = NULL; struct len_ran_intv *intv = NULL, *tmp_intv; struct lys_type *cur_type; struct ly_ctx *ctx = type->parent->module->ctx; int match; if (resolve_len_ran_interval(ctx, NULL, type, &intv)) { /* already done during schema parsing */ LOGINT(ctx); return EXIT_FAILURE; } if (!intv) { return EXIT_SUCCESS; } /* I know that all intervals belonging to a single restriction share one type pointer */ tmp_intv = intv; cur_type = intv->type; do { match = 0; for (; tmp_intv && (tmp_intv->type == cur_type); tmp_intv = tmp_intv->next) { if (match) { /* just iterate through the rest of this restriction intervals */ continue; } if (((kind == 0) && (unum < tmp_intv->value.uval.min)) || ((kind == 1) && (snum < tmp_intv->value.sval.min)) || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) < 0))) { break; } if (((kind == 0) && (unum >= tmp_intv->value.uval.min) && (unum <= tmp_intv->value.uval.max)) || ((kind == 1) && (snum >= tmp_intv->value.sval.min) && (snum <= tmp_intv->value.sval.max)) || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) > -1) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.max, cur_type->info.dec64.dig) < 1))) { match = 1; } } if (!match) { break; } else if (tmp_intv) { cur_type = tmp_intv->type; } } while (tmp_intv); while (intv) { tmp_intv = intv->next; free(intv); intv = tmp_intv; } if (!match) { switch (cur_type->base) { case LY_TYPE_BINARY: restr = cur_type->info.binary.length; break; case LY_TYPE_DEC64: restr = cur_type->info.dec64.range; break; case LY_TYPE_INT8: case LY_TYPE_INT16: case LY_TYPE_INT32: case LY_TYPE_INT64: case LY_TYPE_UINT8: case LY_TYPE_UINT16: case LY_TYPE_UINT32: case LY_TYPE_UINT64: restr = cur_type->info.num.range; break; case LY_TYPE_STRING: restr = cur_type->info.str.length; break; default: LOGINT(ctx); return EXIT_FAILURE; } LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYS, node->schema, (val_str ? val_str : ""), restr ? restr->expr : ""); if (restr && restr->emsg) { ly_vlog_str(ctx, LY_VLOG_PREV, restr->emsg); } if (restr && restr->eapptag) { ly_err_last_set_apptag(ctx, restr->eapptag); } return EXIT_FAILURE; } return EXIT_SUCCESS; } /* logs directly */ static int validate_pattern(struct ly_ctx *ctx, const char *val_str, struct lys_type *type, struct lyd_node *node) { int rc; unsigned int i; #ifndef LY_ENABLED_CACHE pcre *precomp; #endif assert(ctx && (type->base == LY_TYPE_STRING)); if (!val_str) { val_str = ""; } if (type->der && validate_pattern(ctx, val_str, &type->der->type, node)) { return EXIT_FAILURE; } #ifdef LY_ENABLED_CACHE /* there is no cache, build it */ if (!type->info.str.patterns_pcre && type->info.str.pat_count) { type->info.str.patterns_pcre = malloc(2 * type->info.str.pat_count * sizeof *type->info.str.patterns_pcre); LY_CHECK_ERR_RETURN(!type->info.str.patterns_pcre, LOGMEM(ctx), -1); for (i = 0; i < type->info.str.pat_count; ++i) { if (lyp_precompile_pattern(ctx, &type->info.str.patterns[i].expr[1], (pcre**)&type->info.str.patterns_pcre[i * 2], (pcre_extra**)&type->info.str.patterns_pcre[i * 2 + 1])) { return EXIT_FAILURE; } } } #endif for (i = 0; i < type->info.str.pat_count; ++i) { #ifdef LY_ENABLED_CACHE rc = pcre_exec((pcre *)type->info.str.patterns_pcre[2 * i], (pcre_extra *)type->info.str.patterns_pcre[2 * i + 1], val_str, strlen(val_str), 0, 0, NULL, 0); #else if (lyp_check_pattern(ctx, &type->info.str.patterns[i].expr[1], &precomp)) { return EXIT_FAILURE; } rc = pcre_exec(precomp, NULL, val_str, strlen(val_str), 0, 0, NULL, 0); free(precomp); #endif if ((rc && type->info.str.patterns[i].expr[0] == 0x06) || (!rc && type->info.str.patterns[i].expr[0] == 0x15)) { LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYD, node, val_str, &type->info.str.patterns[i].expr[1]); if (type->info.str.patterns[i].emsg) { ly_vlog_str(ctx, LY_VLOG_PREV, type->info.str.patterns[i].emsg); } if (type->info.str.patterns[i].eapptag) { ly_err_last_set_apptag(ctx, type->info.str.patterns[i].eapptag); } return EXIT_FAILURE; } } return EXIT_SUCCESS; } static void check_number(const char *str_num, const char **num_end, LY_DATA_TYPE base) { if (!isdigit(str_num[0]) && (str_num[0] != '-') && (str_num[0] != '+')) { *num_end = str_num; return; } if ((str_num[0] == '-') || (str_num[0] == '+')) { ++str_num; } while (isdigit(str_num[0])) { ++str_num; } if ((base != LY_TYPE_DEC64) || (str_num[0] != '.') || !isdigit(str_num[1])) { *num_end = str_num; return; } ++str_num; while (isdigit(str_num[0])) { ++str_num; } *num_end = str_num; } /** * @brief Checks the syntax of length or range statement, * on success checks the semantics as well. Does not log. * * @param[in] expr Length or range expression. * @param[in] type Type with the restriction. * * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. */ int lyp_check_length_range(struct ly_ctx *ctx, const char *expr, struct lys_type *type) { struct len_ran_intv *intv = NULL, *tmp_intv; const char *c = expr, *tail; int ret = EXIT_FAILURE, flg = 1; /* first run flag */ assert(expr); lengthpart: while (isspace(*c)) { c++; } /* lower boundary or explicit number */ if (!strncmp(c, "max", 3)) { max: c += 3; while (isspace(*c)) { c++; } if (*c != '\0') { goto error; } goto syntax_ok; } else if (!strncmp(c, "min", 3)) { if (!flg) { /* min cannot be used elsewhere than in the first length-part */ goto error; } else { flg = 0; } c += 3; while (isspace(*c)) { c++; } if (*c == '|') { c++; /* process next length-part */ goto lengthpart; } else if (*c == '\0') { goto syntax_ok; } else if (!strncmp(c, "..", 2)) { upper: c += 2; while (isspace(*c)) { c++; } if (*c == '\0') { goto error; } /* upper boundary */ if (!strncmp(c, "max", 3)) { goto max; } check_number(c, &tail, type->base); if (c == tail) { goto error; } c = tail; while (isspace(*c)) { c++; } if (*c == '\0') { goto syntax_ok; } else if (*c == '|') { c++; /* process next length-part */ goto lengthpart; } else { goto error; } } else { goto error; } } else if (isdigit(*c) || (*c == '-') || (*c == '+')) { /* number */ check_number(c, &tail, type->base); if (c == tail) { goto error; } c = tail; while (isspace(*c)) { c++; } if (*c == '|') { c++; /* process next length-part */ goto lengthpart; } else if (*c == '\0') { goto syntax_ok; } else if (!strncmp(c, "..", 2)) { goto upper; } } else { goto error; } syntax_ok: if (resolve_len_ran_interval(ctx, expr, type, &intv)) { goto error; } ret = EXIT_SUCCESS; error: while (intv) { tmp_intv = intv->next; free(intv); intv = tmp_intv; } return ret; } /** * @brief Checks pattern syntax. Logs directly. * * @param[in] pattern Pattern to check. * @param[out] pcre_precomp Precompiled PCRE pattern. Can be NULL. * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. */ int lyp_check_pattern(struct ly_ctx *ctx, const char *pattern, pcre **pcre_precomp) { int idx, idx2, start, end, err_offset, count, end_anchor = 0; char *perl_regex, *ptr; const char *err_msg, *orig_ptr; pcre *precomp; /* * adjust the expression to a Perl equivalent * * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs */ /* we need to replace all "$" with "\$", count them now */ for (count = 0, ptr = strchr(pattern, '$'); ptr; ++count, ptr = strchr(ptr + 1, '$')); perl_regex = malloc((strlen(pattern) + 4 + count) * sizeof(char)); LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx), EXIT_FAILURE); perl_regex[0] = '\0'; ptr = perl_regex; if ((strlen(pattern) > 1) && strncmp(pattern + strlen(pattern) - 2, ".*", 2)) { /* we wil add line-end anchoring */ end_anchor = 1; ptr[0] = '('; ++ptr; } for (orig_ptr = pattern; orig_ptr[0]; ++orig_ptr) { if (orig_ptr[0] == '$') { ptr += sprintf(ptr, "\\$"); } else { ptr[0] = orig_ptr[0]; ++ptr; } } if (end_anchor) { ptr += sprintf(ptr, ")$"); } else { ptr[0] = '\0'; ++ptr; } /* substitute Unicode Character Blocks with exact Character Ranges */ while ((ptr = strstr(perl_regex, "\\p{Is"))) { start = ptr - perl_regex; ptr = strchr(ptr, '}'); if (!ptr) { LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 2, "unterminated character property"); free(perl_regex); return EXIT_FAILURE; } end = (ptr - perl_regex) + 1; /* need more space */ if (end - start < LYP_URANGE_LEN) { perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (LYP_URANGE_LEN - (end - start)) + 1); LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx); free(perl_regex), EXIT_FAILURE); } /* find our range */ for (idx = 0; lyp_ublock2urange[idx][0]; ++idx) { if (!strncmp(perl_regex + start + 5, lyp_ublock2urange[idx][0], strlen(lyp_ublock2urange[idx][0]))) { break; } } if (!lyp_ublock2urange[idx][0]) { LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 5, "unknown block name"); free(perl_regex); return EXIT_FAILURE; } /* make the space in the string and replace the block (but we cannot include brackets if it was already enclosed in them) */ for (idx2 = 0, count = 0; idx2 < start; ++idx2) { if ((perl_regex[idx2] == '[') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) { ++count; } if ((perl_regex[idx2] == ']') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) { --count; } } if (count) { /* skip brackets */ memmove(perl_regex + start + (LYP_URANGE_LEN - 2), perl_regex + end, strlen(perl_regex + end) + 1); memcpy(perl_regex + start, lyp_ublock2urange[idx][1] + 1, LYP_URANGE_LEN - 2); } else { memmove(perl_regex + start + LYP_URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1); memcpy(perl_regex + start, lyp_ublock2urange[idx][1], LYP_URANGE_LEN); } } /* must return 0, already checked during parsing */ precomp = pcre_compile(perl_regex, PCRE_UTF8 | PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE, &err_msg, &err_offset, NULL); if (!precomp) { LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + err_offset, err_msg); free(perl_regex); return EXIT_FAILURE; } free(perl_regex); if (pcre_precomp) { *pcre_precomp = precomp; } else { free(precomp); } return EXIT_SUCCESS; } int lyp_precompile_pattern(struct ly_ctx *ctx, const char *pattern, pcre** pcre_cmp, pcre_extra **pcre_std) { const char *err_msg = NULL; if (lyp_check_pattern(ctx, pattern, pcre_cmp)) { return EXIT_FAILURE; } if (pcre_std && pcre_cmp) { (*pcre_std) = pcre_study(*pcre_cmp, 0, &err_msg); if (err_msg) { LOGWRN(ctx, "Studying pattern \"%s\" failed (%s).", pattern, err_msg); } } return EXIT_SUCCESS; } /** * @brief Change the value into its canonical form. In libyang, additionally to the RFC, * all identities have their module as a prefix in their canonical form. * * @param[in] ctx * @param[in] type Type of the value. * @param[in,out] value Original and then canonical value. * @param[in] data1 If \p type is #LY_TYPE_BITS: (struct lys_type_bit **) type bit field, * #LY_TYPE_DEC64: (int64_t *) parsed digits of the number itself without floating point, * #LY_TYPE_IDENT: (const char *) local module name (identityref node module), * #LY_TYPE_INT*: (int64_t *) parsed int number itself, * #LY_TYPE_UINT*: (uint64_t *) parsed uint number itself, * otherwise ignored. * @param[in] data2 If \p type is #LY_TYPE_BITS: (int *) type bit field length, * #LY_TYPE_DEC64: (uint8_t *) number of fraction digits (position of the floating point), * otherwise ignored. * @return 1 if a conversion took place, 0 if the value was kept the same, -1 on error. */ static int make_canonical(struct ly_ctx *ctx, int type, const char **value, void *data1, void *data2) { const uint16_t buf_len = 511; char buf[buf_len + 1]; struct lys_type_bit **bits = NULL; struct lyxp_expr *exp; const char *module_name, *cur_expr, *end; int i, j, count; int64_t num; uint64_t unum; uint8_t c; #define LOGBUF(str) LOGERR(ctx, LY_EINVAL, "Value \"%s\" is too long.", str) switch (type) { case LY_TYPE_BITS: bits = (struct lys_type_bit **)data1; count = *((int *)data2); /* in canonical form, the bits are ordered by their position */ buf[0] = '\0'; for (i = 0; i < count; i++) { if (!bits[i]) { /* bit not set */ continue; } if (buf[0]) { LY_CHECK_ERR_RETURN(strlen(buf) + 1 + strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1); sprintf(buf + strlen(buf), " %s", bits[i]->name); } else { LY_CHECK_ERR_RETURN(strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1); strcpy(buf, bits[i]->name); } } break; case LY_TYPE_IDENT: module_name = (const char *)data1; /* identity must always have a prefix */ if (!strchr(*value, ':')) { LY_CHECK_ERR_RETURN(strlen(module_name) + 1 + strlen(*value) > buf_len, LOGBUF(*value), -1); sprintf(buf, "%s:%s", module_name, *value); } else { LY_CHECK_ERR_RETURN(strlen(*value) > buf_len, LOGBUF(*value), -1); strcpy(buf, *value); } break; case LY_TYPE_INST: exp = lyxp_parse_expr(ctx, *value); LY_CHECK_ERR_RETURN(!exp, LOGINT(ctx), -1); module_name = NULL; count = 0; for (i = 0; (unsigned)i < exp->used; ++i) { cur_expr = &exp->expr[exp->expr_pos[i]]; /* copy WS */ if (i && ((end = exp->expr + exp->expr_pos[i - 1] + exp->tok_len[i - 1]) != cur_expr)) { if (count + (cur_expr - end) > buf_len) { LOGBUF(end); lyxp_expr_free(exp); return -1; } strncpy(&buf[count], end, cur_expr - end); count += cur_expr - end; } if ((exp->tokens[i] == LYXP_TOKEN_NAMETEST) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) { /* get the module name with ":" */ ++end; j = end - cur_expr; if (!module_name || strncmp(cur_expr, module_name, j)) { /* print module name with colon, it does not equal to the parent one */ if (count + j > buf_len) { LOGBUF(cur_expr); lyxp_expr_free(exp); return -1; } strncpy(&buf[count], cur_expr, j); count += j; } module_name = cur_expr; /* copy the rest */ if (count + (exp->tok_len[i] - j) > buf_len) { LOGBUF(end); lyxp_expr_free(exp); return -1; } strncpy(&buf[count], end, exp->tok_len[i] - j); count += exp->tok_len[i] - j; } else { if (count + exp->tok_len[i] > buf_len) { LOGBUF(&exp->expr[exp->expr_pos[i]]); lyxp_expr_free(exp); return -1; } strncpy(&buf[count], &exp->expr[exp->expr_pos[i]], exp->tok_len[i]); count += exp->tok_len[i]; } } if (count > buf_len) { LOGINT(ctx); lyxp_expr_free(exp); return -1; } buf[count] = '\0'; lyxp_expr_free(exp); break; case LY_TYPE_DEC64: num = *((int64_t *)data1); c = *((uint8_t *)data2); if (num) { count = sprintf(buf, "%"PRId64" ", num); if ( (num > 0 && (count - 1) <= c) || (count - 2) <= c ) { /* we have 0. value, print the value with the leading zeros * (one for 0. and also keep the correct with of num according * to fraction-digits value) * for (num<0) - extra character for '-' sign */ count = sprintf(buf, "%0*"PRId64" ", (num > 0) ? (c + 1) : (c + 2), num); } for (i = c, j = 1; i > 0 ; i--) { if (j && i > 1 && buf[count - 2] == '0') { /* we have trailing zero to skip */ buf[count - 1] = '\0'; } else { j = 0; buf[count - 1] = buf[count - 2]; } count--; } buf[count - 1] = '.'; } else { /* zero */ sprintf(buf, "0.0"); } break; case LY_TYPE_INT8: case LY_TYPE_INT16: case LY_TYPE_INT32: case LY_TYPE_INT64: num = *((int64_t *)data1); sprintf(buf, "%"PRId64, num); break; case LY_TYPE_UINT8: case LY_TYPE_UINT16: case LY_TYPE_UINT32: case LY_TYPE_UINT64: unum = *((uint64_t *)data1); sprintf(buf, "%"PRIu64, unum); break; default: /* should not be even called - just do nothing */ return 0; } if (strcmp(buf, *value)) { lydict_remove(ctx, *value); *value = lydict_insert(ctx, buf, 0); return 1; } return 0; #undef LOGBUF } static const char * ident_val_add_module_prefix(const char *value, const struct lyxml_elem *xml, struct ly_ctx *ctx) { const struct lyxml_ns *ns; const struct lys_module *mod; char *str; do { LY_TREE_FOR((struct lyxml_ns *)xml->attr, ns) { if ((ns->type == LYXML_ATTR_NS) && !ns->prefix) { /* match */ break; } } if (!ns) { xml = xml->parent; } } while (!ns && xml); if (!ns) { /* no default namespace */ LOGERR(ctx, LY_EINVAL, "Identity \"%s\" has no namespace.", value); return NULL; } /* find module */ mod = ly_ctx_get_module_by_ns(ctx, ns->value, NULL, 1); if (!mod) { LOGINT(ctx); return NULL; } if (asprintf(&str, "%s:%s", mod->name, value) == -1) { LOGMEM(ctx); return NULL; } lydict_remove(ctx, value); return lydict_insert_zc(ctx, str); } /* * xml - optional for converting instance-identifier and identityref into JSON format * leaf - mandatory to know the context (necessary e.g. for prefixes in idenitytref values) * attr - alternative to leaf in case of parsing value in annotations (attributes) * local_mod - optional if the local module dos not match the module of leaf/attr * store - flag for union resolution - we do not want to store the result, we are just learning the type * dflt - whether the value is a default value from the schema */ struct lys_type * lyp_parse_value(struct lys_type *type, const char **value_, struct lyxml_elem *xml, struct lyd_node_leaf_list *leaf, struct lyd_attr *attr, struct lys_module *local_mod, int store, int dflt) { struct lys_type *ret = NULL, *t; struct lys_tpdf *tpdf; enum int_log_opts prev_ilo; int c, len, found = 0; unsigned int i, j; int64_t num; uint64_t unum, uind, u = 0; const char *ptr, *value = *value_, *itemname, *old_val_str = NULL; struct lys_type_bit **bits = NULL; struct lys_ident *ident; lyd_val *val, old_val; LY_DATA_TYPE *val_type, old_val_type; uint8_t *val_flags, old_val_flags; struct lyd_node *contextnode; struct ly_ctx *ctx = type->parent->module->ctx; assert(leaf || attr); if (leaf) { assert(!attr); if (!local_mod) { local_mod = leaf->schema->module; } val = &leaf->value; val_type = &leaf->value_type; val_flags = &leaf->value_flags; contextnode = (struct lyd_node *)leaf; itemname = leaf->schema->name; } else { assert(!leaf); if (!local_mod) { local_mod = attr->annotation->module; } val = &attr->value; val_type = &attr->value_type; val_flags = &attr->value_flags; contextnode = attr->parent; itemname = attr->name; } /* fully clear the value */ if (store) { if (leaf) { old_val_str = lydict_insert(ctx, leaf->value_str, 0); } else { old_val_str = lydict_insert(ctx, attr->value_str, 0); } lyd_free_value(*val, *val_type, *val_flags, type, old_val_str, &old_val, &old_val_type, &old_val_flags); *val_flags &= ~LY_VALUE_UNRES; *val_flags &= ~LY_VALUE_USER; } ret = type; switch (type->base) { case LY_TYPE_BINARY: /* get number of octets for length validation */ unum = 0; ptr = NULL; if (value) { /* silently skip leading/trailing whitespaces */ for (uind = 0; isspace(value[uind]); ++uind); ptr = &value[uind]; u = strlen(ptr); while (u && isspace(ptr[u - 1])) { --u; } unum = u; for (uind = 0; uind < u; ++uind) { if (ptr[uind] == '\n') { unum--; } else if ((ptr[uind] < '/' && ptr[uind] != '+') || (ptr[uind] > '9' && ptr[uind] < 'A') || (ptr[uind] > 'Z' && ptr[uind] < 'a') || ptr[uind] > 'z') { if (ptr[uind] == '=') { /* padding */ if (uind == u - 2 && ptr[uind + 1] == '=') { found = 2; uind++; } else if (uind == u - 1) { found = 1; } } if (!found) { /* error */ LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYD, contextnode, ptr[uind], &ptr[uind]); LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Invalid Base64 character."); goto error; } } } } if (unum & 3) { /* base64 length must be multiple of 4 chars */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Base64 encoded value length must be divisible by 4."); goto error; } /* length of the encoded string */ len = ((unum / 4) * 3) - found; if (validate_length_range(0, len, 0, 0, 0, type, value, contextnode)) { goto error; } if (value && (ptr != value || ptr[u] != '\0')) { /* update the changed value */ ptr = lydict_insert(ctx, ptr, u); lydict_remove(ctx, *value_); *value_ = ptr; } if (store) { /* store the result */ val->binary = value; *val_type = LY_TYPE_BINARY; } break; case LY_TYPE_BITS: /* locate bits structure with the bits definitions * since YANG 1.1 allows restricted bits, it is the first * bits type with some explicit bit specification */ for (; !type->info.bits.count; type = &type->der->type); if (value || store) { /* allocate the array of pointers to bits definition */ bits = calloc(type->info.bits.count, sizeof *bits); LY_CHECK_ERR_GOTO(!bits, LOGMEM(ctx), error); } if (!value) { /* no bits set */ if (store) { /* store empty array */ val->bit = bits; *val_type = LY_TYPE_BITS; } break; } c = 0; i = 0; while (value[c]) { /* skip leading whitespaces */ while (isspace(value[c])) { c++; } if (!value[c]) { /* trailing white spaces */ break; } /* get the length of the bit identifier */ for (len = 0; value[c] && !isspace(value[c]); c++, len++); /* go back to the beginning of the identifier */ c = c - len; /* find bit definition, identifiers appear ordered by their position */ for (found = i = 0; i < type->info.bits.count; i++) { if (!strncmp(type->info.bits.bit[i].name, &value[c], len) && !type->info.bits.bit[i].name[len]) { if (!dflt) { /* we have match, check if the value is enabled ... */ for (j = 0; j < type->info.bits.bit[i].iffeature_size; j++) { if (!resolve_iffeature(&type->info.bits.bit[i].iffeature[j])) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Bit \"%s\" is disabled by its %d. if-feature condition.", type->info.bits.bit[i].name, j + 1); free(bits); goto error; } } } /* check that the value was not already set */ if (bits[i]) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Bit \"%s\" used multiple times.", type->info.bits.bit[i].name); free(bits); goto error; } /* ... and then store the pointer */ bits[i] = &type->info.bits.bit[i]; /* stop searching */ found = 1; break; } } if (!found) { /* referenced bit value does not exist */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } free(bits); goto error; } c = c + len; } if (make_canonical(ctx, LY_TYPE_BITS, value_, bits, &type->info.bits.count) == -1) { free(bits); goto error; } if (store) { /* store the result */ val->bit = bits; *val_type = LY_TYPE_BITS; } else { free(bits); } break; case LY_TYPE_BOOL: if (value && !strcmp(value, "true")) { if (store) { val->bln = 1; } } else if (!value || strcmp(value, "false")) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value ? value : ""); } goto error; } else { if (store) { val->bln = 0; } } if (store) { *val_type = LY_TYPE_BOOL; } break; case LY_TYPE_DEC64: if (!value || !value[0]) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); } goto error; } ptr = value; if (parse_range_dec64(&ptr, type->info.dec64.dig, &num) || ptr[0]) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } goto error; } if (validate_length_range(2, 0, 0, num, type->info.dec64.dig, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_DEC64, value_, &num, &type->info.dec64.dig) == -1) { goto error; } if (store) { /* store the result */ val->dec64 = num; *val_type = LY_TYPE_DEC64; } break; case LY_TYPE_EMPTY: if (value && value[0]) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } goto error; } if (store) { *val_type = LY_TYPE_EMPTY; } break; case LY_TYPE_ENUM: /* locate enums structure with the enumeration definitions, * since YANG 1.1 allows restricted enums, it is the first * enum type with some explicit enum specification */ for (; !type->info.enums.count; type = &type->der->type); /* find matching enumeration value */ for (i = found = 0; i < type->info.enums.count; i++) { if (value && !strcmp(value, type->info.enums.enm[i].name)) { if (!dflt) { /* we have match, check if the value is enabled ... */ for (j = 0; j < type->info.enums.enm[i].iffeature_size; j++) { if (!resolve_iffeature(&type->info.enums.enm[i].iffeature[j])) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Enum \"%s\" is disabled by its %d. if-feature condition.", value, j + 1); goto error; } } } /* ... and store pointer to the definition */ if (store) { val->enm = &type->info.enums.enm[i]; *val_type = LY_TYPE_ENUM; } found = 1; break; } } if (!found) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value ? value : ""); } goto error; } break; case LY_TYPE_IDENT: if (!value) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); } goto error; } if (xml) { ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); /* first, convert value into the json format, silently */ value = transform_xml2json(ctx, value, xml, 0, 0); ly_ilo_restore(NULL, prev_ilo, NULL, 0); if (!value) { /* invalid identityref format */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); } goto error; } /* the value has no prefix (default namespace), but the element's namespace has a prefix, find default namespace */ if (!strchr(value, ':') && xml->ns->prefix) { value = ident_val_add_module_prefix(value, xml, ctx); if (!value) { goto error; } } } else if (dflt) { ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); /* the value actually uses module's prefixes instead of the module names as in JSON format, * we have to convert it */ value = transform_schema2json(local_mod, value); ly_ilo_restore(NULL, prev_ilo, NULL, 0); if (!value) { /* invalid identityref format or it was already transformed, so ignore the error here */ value = lydict_insert(ctx, *value_, 0); } } else { value = lydict_insert(ctx, *value_, 0); } /* value is now in the dictionary, whether it differs from *value_ or not */ ident = resolve_identref(type, value, contextnode, local_mod, dflt); if (!ident) { lydict_remove(ctx, value); goto error; } else if (store) { /* store the result */ val->ident = ident; *val_type = LY_TYPE_IDENT; } /* the value is always changed and includes prefix */ if (dflt) { type->parent->flags |= LYS_DFLTJSON; } if (make_canonical(ctx, LY_TYPE_IDENT, &value, (void*)lys_main_module(local_mod)->name, NULL) == -1) { lydict_remove(ctx, value); goto error; } /* replace the old value with the new one (even if they may be the same) */ lydict_remove(ctx, *value_); *value_ = value; break; case LY_TYPE_INST: if (!value) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); } goto error; } if (xml) { ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); /* first, convert value into the json format, silently */ value = transform_xml2json(ctx, value, xml, 1, 1); ly_ilo_restore(NULL, prev_ilo, NULL, 0); if (!value) { /* invalid instance-identifier format */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); } goto error; } else if (ly_strequal(value, *value_, 1)) { /* we have actually created the same expression (prefixes are the same as the module names) * so we have just increased dictionary's refcount - fix it */ lydict_remove(ctx, value); } } else if (dflt) { /* turn logging off */ ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); /* the value actually uses module's prefixes instead of the module names as in JSON format, * we have to convert it */ value = transform_schema2json(local_mod, value); if (!value) { /* invalid identityref format or it was already transformed, so ignore the error here */ value = *value_; } else if (ly_strequal(value, *value_, 1)) { /* we have actually created the same expression (prefixes are the same as the module names) * so we have just increased dictionary's refcount - fix it */ lydict_remove(ctx, value); } /* turn logging back on */ ly_ilo_restore(NULL, prev_ilo, NULL, 0); } else { if ((c = make_canonical(ctx, LY_TYPE_INST, &value, NULL, NULL))) { if (c == -1) { goto error; } /* if a change occurred, value was removed from the dictionary so fix the pointers */ *value_ = value; } } if (store) { /* note that the data node is an unresolved instance-identifier */ val->instance = NULL; *val_type = LY_TYPE_INST; *val_flags |= LY_VALUE_UNRES; } if (!ly_strequal(value, *value_, 1)) { /* update the changed value */ lydict_remove(ctx, *value_); *value_ = value; /* we have to remember the conversion into JSON format to be able to print it in correct form */ if (dflt) { type->parent->flags |= LYS_DFLTJSON; } } break; case LY_TYPE_LEAFREF: if (!value) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); } goto error; } /* it is called not only to get the final type, but mainly to update value to canonical or JSON form * if needed */ t = lyp_parse_value(&type->info.lref.target->type, value_, xml, leaf, attr, NULL, store, dflt); value = *value_; /* refresh possibly changed value */ if (!t) { /* already logged */ goto error; } if (store) { /* make the note that the data node is an unresolved leafref (value union was already filled) */ *val_flags |= LY_VALUE_UNRES; } ret = t; break; case LY_TYPE_STRING: if (validate_length_range(0, (value ? ly_strlen_utf8(value) : 0), 0, 0, 0, type, value, contextnode)) { goto error; } if (validate_pattern(ctx, value, type, contextnode)) { goto error; } /* special handling of ietf-yang-types xpath1.0 */ for (tpdf = type->der; tpdf->module && (strcmp(tpdf->name, "xpath1.0") || strcmp(tpdf->module->name, "ietf-yang-types")); tpdf = tpdf->type.der); if (tpdf->module && xml) { /* convert value into the json format */ value = transform_xml2json(ctx, value ? value : "", xml, 1, 1); if (!value) { /* invalid instance-identifier format */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); } goto error; } if (!ly_strequal(value, *value_, 1)) { /* update the changed value */ lydict_remove(ctx, *value_); *value_ = value; } } if (store) { /* store the result */ val->string = value; *val_type = LY_TYPE_STRING; } break; case LY_TYPE_INT8: if (parse_int(value, __INT64_C(-128), __INT64_C(127), dflt ? 0 : 10, &num, contextnode) || validate_length_range(1, 0, num, 0, 0, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_INT8, value_, &num, NULL) == -1) { goto error; } if (store) { /* store the result */ val->int8 = (int8_t)num; *val_type = LY_TYPE_INT8; } break; case LY_TYPE_INT16: if (parse_int(value, __INT64_C(-32768), __INT64_C(32767), dflt ? 0 : 10, &num, contextnode) || validate_length_range(1, 0, num, 0, 0, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_INT16, value_, &num, NULL) == -1) { goto error; } if (store) { /* store the result */ val->int16 = (int16_t)num; *val_type = LY_TYPE_INT16; } break; case LY_TYPE_INT32: if (parse_int(value, __INT64_C(-2147483648), __INT64_C(2147483647), dflt ? 0 : 10, &num, contextnode) || validate_length_range(1, 0, num, 0, 0, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_INT32, value_, &num, NULL) == -1) { goto error; } if (store) { /* store the result */ val->int32 = (int32_t)num; *val_type = LY_TYPE_INT32; } break; case LY_TYPE_INT64: if (parse_int(value, __INT64_C(-9223372036854775807) - __INT64_C(1), __INT64_C(9223372036854775807), dflt ? 0 : 10, &num, contextnode) || validate_length_range(1, 0, num, 0, 0, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_INT64, value_, &num, NULL) == -1) { goto error; } if (store) { /* store the result */ val->int64 = num; *val_type = LY_TYPE_INT64; } break; case LY_TYPE_UINT8: if (parse_uint(value, __UINT64_C(255), dflt ? 0 : 10, &unum, contextnode) || validate_length_range(0, unum, 0, 0, 0, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_UINT8, value_, &unum, NULL) == -1) { goto error; } if (store) { /* store the result */ val->uint8 = (uint8_t)unum; *val_type = LY_TYPE_UINT8; } break; case LY_TYPE_UINT16: if (parse_uint(value, __UINT64_C(65535), dflt ? 0 : 10, &unum, contextnode) || validate_length_range(0, unum, 0, 0, 0, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_UINT16, value_, &unum, NULL) == -1) { goto error; } if (store) { /* store the result */ val->uint16 = (uint16_t)unum; *val_type = LY_TYPE_UINT16; } break; case LY_TYPE_UINT32: if (parse_uint(value, __UINT64_C(4294967295), dflt ? 0 : 10, &unum, contextnode) || validate_length_range(0, unum, 0, 0, 0, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_UINT32, value_, &unum, NULL) == -1) { goto error; } if (store) { /* store the result */ val->uint32 = (uint32_t)unum; *val_type = LY_TYPE_UINT32; } break; case LY_TYPE_UINT64: if (parse_uint(value, __UINT64_C(18446744073709551615), dflt ? 0 : 10, &unum, contextnode) || validate_length_range(0, unum, 0, 0, 0, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_UINT64, value_, &unum, NULL) == -1) { goto error; } if (store) { /* store the result */ val->uint64 = unum; *val_type = LY_TYPE_UINT64; } break; case LY_TYPE_UNION: if (store) { /* unresolved union type */ memset(val, 0, sizeof(lyd_val)); *val_type = LY_TYPE_UNION; } if (type->info.uni.has_ptr_type) { /* we are not resolving anything here, only parsing, and in this case we cannot decide * the type without resolving it -> we return the union type (resolve it with resolve_union()) */ if (xml) { /* in case it should resolve into a instance-identifier, we can only do the JSON conversion here */ ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); value = transform_xml2json(ctx, value, xml, 1, 1); ly_ilo_restore(NULL, prev_ilo, NULL, 0); /* update the changed value */ if (value) { lydict_remove(ctx, *value_); *value_ = value; } else { value = *value_; } if (store) { /* store the (unresolved) result */ val->string = lydict_insert(ctx, value, 0); } } break; } t = NULL; found = 0; /* turn logging off, we are going to try to validate the value with all the types in order */ ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); while ((t = lyp_get_next_union_type(type, t, &found))) { found = 0; ret = lyp_parse_value(t, value_, xml, leaf, attr, NULL, store, dflt); if (ret) { /* we have the result */ break; } if (store) { /* erase possible present and invalid value data */ lyd_free_value(*val, *val_type, *val_flags, t, *value_, NULL, NULL, NULL); memset(val, 0, sizeof(lyd_val)); } } /* turn logging back on */ ly_ilo_restore(NULL, prev_ilo, NULL, 0); if (!t) { /* not found */ if (store) { *val_type = 0; } if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_ ? *value_ : "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); } goto error; } break; default: LOGINT(ctx); goto error; } /* search user types in case this value is supposed to be stored in a custom way */ if (store && ret->der && ret->der->module) { c = lytype_store(ret->der->module, ret->der->name, value_, val); if (c == -1) { if (leaf) { LOGPATH(ctx, LY_VLOG_LYD, leaf); } goto error; } else if (!c) { *val_flags |= LY_VALUE_USER; } } /* free backup (using the original type, unless we were parsing a leafref - then this was freed in the recursive call) */ if (store) { if (type->base != LY_TYPE_LEAFREF) { lyd_free_value(old_val, old_val_type, old_val_flags, type, old_val_str, NULL, NULL, NULL); } lydict_remove(ctx, old_val_str); } return ret; error: /* restore the backup */ if (store) { *val = old_val; *val_type = old_val_type; *val_flags = old_val_flags; lydict_remove(ctx, old_val_str); } return NULL; } /* does not log, cannot fail */ struct lys_type * lyp_get_next_union_type(struct lys_type *type, struct lys_type *prev_type, int *found) { unsigned int i; struct lys_type *ret = NULL; while (!type->info.uni.count) { assert(type->der); /* at least the direct union type has to have type specified */ type = &type->der->type; } for (i = 0; i < type->info.uni.count; ++i) { if (type->info.uni.types[i].base == LY_TYPE_UNION) { ret = lyp_get_next_union_type(&type->info.uni.types[i], prev_type, found); if (ret) { break; } continue; } if (!prev_type || *found) { ret = &type->info.uni.types[i]; break; } if (&type->info.uni.types[i] == prev_type) { *found = 1; } } return ret; } /* ret 0 - ret set, ret 1 - ret not set, no log, ret -1 - ret not set, fatal error */ int lyp_fill_attr(struct ly_ctx *ctx, struct lyd_node *parent, const char *module_ns, const char *module_name, const char *attr_name, const char *attr_value, struct lyxml_elem *xml, struct lyd_attr **ret) { const struct lys_module *mod = NULL; const struct lys_submodule *submod = NULL; struct lys_type **type; struct lyd_attr *dattr; int pos, i, j, k; /* first, get module where the annotation should be defined */ if (module_ns) { mod = (struct lys_module *)ly_ctx_get_module_by_ns(ctx, module_ns, NULL, 0); } else if (module_name) { mod = (struct lys_module *)ly_ctx_get_module(ctx, module_name, NULL, 0); } else { LOGINT(ctx); return -1; } if (!mod) { return 1; } /* then, find the appropriate annotation definition */ pos = -1; for (i = 0, j = 0; i < mod->ext_size; i = i + j + 1) { j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &mod->ext[i], mod->ext_size - i); if (j == -1) { break; } if (ly_strequal(mod->ext[i + j]->arg_value, attr_name, 0)) { pos = i + j; break; } } /* try submodules */ if (pos == -1) { for (k = 0; k < mod->inc_size; ++k) { submod = mod->inc[k].submodule; for (i = 0, j = 0; i < submod->ext_size; i = i + j + 1) { j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &submod->ext[i], submod->ext_size - i); if (j == -1) { break; } if (ly_strequal(submod->ext[i + j]->arg_value, attr_name, 0)) { pos = i + j; break; } } } } if (pos == -1) { return 1; } /* allocate and fill the data attribute structure */ dattr = calloc(1, sizeof *dattr); LY_CHECK_ERR_RETURN(!dattr, LOGMEM(ctx), -1); dattr->parent = parent; dattr->next = NULL; dattr->annotation = submod ? (struct lys_ext_instance_complex *)submod->ext[pos] : (struct lys_ext_instance_complex *)mod->ext[pos]; dattr->name = lydict_insert(ctx, attr_name, 0); dattr->value_str = lydict_insert(ctx, attr_value, 0); /* the value is here converted to a JSON format if needed in case of LY_TYPE_IDENT and LY_TYPE_INST or to a * canonical form of the value */ type = lys_ext_complex_get_substmt(LY_STMT_TYPE, dattr->annotation, NULL); if (!type || !lyp_parse_value(*type, &dattr->value_str, xml, NULL, dattr, NULL, 1, 0)) { lydict_remove(ctx, dattr->name); lydict_remove(ctx, dattr->value_str); free(dattr); return -1; } *ret = dattr; return 0; } int lyp_check_edit_attr(struct ly_ctx *ctx, struct lyd_attr *attr, struct lyd_node *parent, int *editbits) { struct lyd_attr *last = NULL; int bits = 0; /* 0x01 - insert attribute present * 0x02 - insert is relative (before or after) * 0x04 - value attribute present * 0x08 - key attribute present * 0x10 - operation attribute present * 0x20 - operation not allowing insert attribute (delete or remove) */ LY_TREE_FOR(attr, attr) { last = NULL; if (!strcmp(attr->annotation->arg_value, "operation") && !strcmp(attr->annotation->module->name, "ietf-netconf")) { if (bits & 0x10) { LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "operation attributes", parent->schema->name); return -1; } bits |= 0x10; if (attr->value.enm->value >= 3) { /* delete or remove */ bits |= 0x20; } } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ !strcmp(attr->annotation->arg_value, "insert")) { /* 'insert' attribute present */ if (!(parent->schema->flags & LYS_USERORDERED)) { /* ... but it is not expected */ LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert"); return -1; } if (bits & 0x01) { LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "insert attributes", parent->schema->name); return -1; } bits |= 0x01; if (attr->value.enm->value >= 2) { /* before or after */ bits |= 0x02; } last = attr; } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ !strcmp(attr->annotation->arg_value, "value")) { if (bits & 0x04) { LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "value attributes", parent->schema->name); return -1; } else if (parent->schema->nodetype & LYS_LIST) { LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name); return -1; } bits |= 0x04; last = attr; } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ !strcmp(attr->annotation->arg_value, "key")) { if (bits & 0x08) { LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "key attributes", parent->schema->name); return -1; } else if (parent->schema->nodetype & LYS_LEAFLIST) { LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name); return -1; } bits |= 0x08; last = attr; } } /* report errors */ if (last && (!(parent->schema->nodetype & (LYS_LEAFLIST | LYS_LIST)) || !(parent->schema->flags & LYS_USERORDERED))) { /* moving attributes in wrong elements (not an user ordered list or not a list at all) */ LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, last->name); return -1; } else if (bits == 3) { /* 0x01 | 0x02 - relative position, but value/key is missing */ if (parent->schema->nodetype & LYS_LIST) { LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "key", parent->schema->name); } else { /* LYS_LEAFLIST */ LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "value", parent->schema->name); } return -1; } else if ((bits & (0x04 | 0x08)) && !(bits & 0x02)) { /* key/value without relative position */ LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, (bits & 0x04) ? "value" : "key"); return -1; } else if ((bits & 0x21) == 0x21) { /* insert in delete/remove */ LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert"); return -1; } if (editbits) { *editbits = bits; } return 0; } /* does not log */ static int dup_identity_check(const char *id, struct lys_ident *ident, uint32_t size) { uint32_t i; for (i = 0; i < size; i++) { if (ly_strequal(id, ident[i].name, 1)) { /* name collision */ return EXIT_FAILURE; } } return EXIT_SUCCESS; } int dup_identities_check(const char *id, struct lys_module *module) { struct lys_module *mainmod; int i; if (dup_identity_check(id, module->ident, module->ident_size)) { LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id); return EXIT_FAILURE; } /* check identity in submodules */ mainmod = lys_main_module(module); for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; ++i) { if (dup_identity_check(id, mainmod->inc[i].submodule->ident, mainmod->inc[i].submodule->ident_size)) { LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id); return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* does not log */ int dup_typedef_check(const char *type, struct lys_tpdf *tpdf, int size) { int i; for (i = 0; i < size; i++) { if (!strcmp(type, tpdf[i].name)) { /* name collision */ return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* does not log */ static int dup_feature_check(const char *id, struct lys_module *module) { int i; for (i = 0; i < module->features_size; i++) { if (!strcmp(id, module->features[i].name)) { return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* does not log */ static int dup_prefix_check(const char *prefix, struct lys_module *module) { int i; if (module->prefix && !strcmp(module->prefix, prefix)) { return EXIT_FAILURE; } for (i = 0; i < module->imp_size; i++) { if (!strcmp(module->imp[i].prefix, prefix)) { return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* logs directly */ int lyp_check_identifier(struct ly_ctx *ctx, const char *id, enum LY_IDENT type, struct lys_module *module, struct lys_node *parent) { int i, j; int size; struct lys_tpdf *tpdf; struct lys_node *node; struct lys_module *mainmod; struct lys_submodule *submod; assert(ctx && id); /* check id syntax */ if (!(id[0] >= 'A' && id[0] <= 'Z') && !(id[0] >= 'a' && id[0] <= 'z') && id[0] != '_') { LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid start character"); return EXIT_FAILURE; } for (i = 1; id[i]; i++) { if (!(id[i] >= 'A' && id[i] <= 'Z') && !(id[i] >= 'a' && id[i] <= 'z') && !(id[i] >= '0' && id[i] <= '9') && id[i] != '_' && id[i] != '-' && id[i] != '.') { LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid character"); return EXIT_FAILURE; } } if (i > 64) { LOGWRN(ctx, "Identifier \"%s\" is long, you should use something shorter.", id); } switch (type) { case LY_IDENT_NAME: /* check uniqueness of the node within its siblings */ if (!parent) { break; } LY_TREE_FOR(parent->child, node) { if (ly_strequal(node->name, id, 1)) { LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "name duplication"); return EXIT_FAILURE; } } break; case LY_IDENT_TYPE: assert(module); mainmod = lys_main_module(module); /* check collision with the built-in types */ if (!strcmp(id, "binary") || !strcmp(id, "bits") || !strcmp(id, "boolean") || !strcmp(id, "decimal64") || !strcmp(id, "empty") || !strcmp(id, "enumeration") || !strcmp(id, "identityref") || !strcmp(id, "instance-identifier") || !strcmp(id, "int8") || !strcmp(id, "int16") || !strcmp(id, "int32") || !strcmp(id, "int64") || !strcmp(id, "leafref") || !strcmp(id, "string") || !strcmp(id, "uint8") || !strcmp(id, "uint16") || !strcmp(id, "uint32") || !strcmp(id, "uint64") || !strcmp(id, "union")) { LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, id, "typedef"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Typedef name duplicates a built-in type."); return EXIT_FAILURE; } /* check locally scoped typedefs (avoid name shadowing) */ for (; parent; parent = lys_parent(parent)) { switch (parent->nodetype) { case LYS_CONTAINER: size = ((struct lys_node_container *)parent)->tpdf_size; tpdf = ((struct lys_node_container *)parent)->tpdf; break; case LYS_LIST: size = ((struct lys_node_list *)parent)->tpdf_size; tpdf = ((struct lys_node_list *)parent)->tpdf; break; case LYS_GROUPING: size = ((struct lys_node_grp *)parent)->tpdf_size; tpdf = ((struct lys_node_grp *)parent)->tpdf; break; default: continue; } if (dup_typedef_check(id, tpdf, size)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); return EXIT_FAILURE; } } /* check top-level names */ if (dup_typedef_check(id, module->tpdf, module->tpdf_size)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); return EXIT_FAILURE; } /* check submodule's top-level names */ for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) { if (dup_typedef_check(id, mainmod->inc[i].submodule->tpdf, mainmod->inc[i].submodule->tpdf_size)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); return EXIT_FAILURE; } } break; case LY_IDENT_PREFIX: assert(module); /* check the module itself */ if (dup_prefix_check(id, module)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "prefix", id); return EXIT_FAILURE; } break; case LY_IDENT_FEATURE: assert(module); mainmod = lys_main_module(module); /* check feature name uniqueness*/ /* check features in the current module */ if (dup_feature_check(id, module)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id); return EXIT_FAILURE; } /* and all its submodules */ for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) { if (dup_feature_check(id, (struct lys_module *)mainmod->inc[i].submodule)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id); return EXIT_FAILURE; } } break; case LY_IDENT_EXTENSION: assert(module); mainmod = lys_main_module(module); /* check extension name uniqueness in the main module ... */ for (i = 0; i < mainmod->extensions_size; i++) { if (ly_strequal(id, mainmod->extensions[i].name, 1)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id); return EXIT_FAILURE; } } /* ... and all its submodules */ for (j = 0; j < mainmod->inc_size && mainmod->inc[j].submodule; j++) { submod = mainmod->inc[j].submodule; /* shortcut */ for (i = 0; i < submod->extensions_size; i++) { if (ly_strequal(id, submod->extensions[i].name, 1)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id); return EXIT_FAILURE; } } } break; default: /* no check required */ break; } return EXIT_SUCCESS; } /* logs directly */ int lyp_check_date(struct ly_ctx *ctx, const char *date) { int i; struct tm tm, tm_; time_t t; char *r; assert(date); /* check format */ for (i = 0; i < LY_REV_SIZE - 1; i++) { if (i == 4 || i == 7) { if (date[i] != '-') { goto error; } } else if (!isdigit(date[i])) { goto error; } } /* pre-fill tm with valid data */ t = time(NULL); localtime_r(&t, &tm); /* check content, e.g. 2018-02-31 */ r = strptime(date, "%Y-%m-%d", &tm); if (!r || r != &date[LY_REV_SIZE - 1]) { goto error; } memcpy(&tm_, &tm, sizeof tm); tm_.tm_isdst = -1; /* mktime corrects DST mismatches, this stops it from doing that */ mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */ if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */ /* checking days is enough, since other errors * have been checked by strptime() */ goto error; } return EXIT_SUCCESS; error: LOGVAL(ctx, LYE_INDATE, LY_VLOG_NONE, NULL, date); return EXIT_FAILURE; } /** * @return * NULL - success * root - not yet resolvable * other node - mandatory node under the root */ static const struct lys_node * lyp_check_mandatory_(const struct lys_node *root) { int mand_flag = 0; const struct lys_node *iter = NULL; while ((iter = lys_getnext(iter, root, NULL, LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHUSES | LYS_GETNEXT_INTOUSES | LYS_GETNEXT_INTONPCONT | LYS_GETNEXT_NOSTATECHECK))) { if (iter->nodetype == LYS_USES) { if (!((struct lys_node_uses *)iter)->grp) { /* not yet resolved uses */ return root; } else { /* go into uses */ continue; } } if (iter->nodetype == LYS_CHOICE) { /* skip it, it was already checked for direct mandatory node in default */ continue; } if (iter->nodetype == LYS_LIST) { if (((struct lys_node_list *)iter)->min) { mand_flag = 1; } } else if (iter->nodetype == LYS_LEAFLIST) { if (((struct lys_node_leaflist *)iter)->min) { mand_flag = 1; } } else if (iter->flags & LYS_MAND_TRUE) { mand_flag = 1; } if (mand_flag) { return iter; } } return NULL; } /* logs directly */ int lyp_check_mandatory_augment(struct lys_node_augment *aug, const struct lys_node *target) { const struct lys_node *node; if (aug->when || target->nodetype == LYS_CHOICE) { /* - mandatory nodes in new cases are ok; * clarification from YANG 1.1 - augmentation can add mandatory nodes when it is * conditional with a when statement */ return EXIT_SUCCESS; } if ((node = lyp_check_mandatory_((struct lys_node *)aug))) { if (node != (struct lys_node *)aug) { LOGVAL(target->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory"); LOGVAL(target->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Mandatory node \"%s\" appears in augment of \"%s\" without when condition.", node->name, aug->target_name); return -1; } return EXIT_FAILURE; } return EXIT_SUCCESS; } /** * @brief check that a mandatory node is not directly under the default case. * @param[in] node choice with default node * @return EXIT_SUCCESS if the constraint is fulfilled, EXIT_FAILURE otherwise */ int lyp_check_mandatory_choice(struct lys_node *node) { const struct lys_node *mand, *dflt = ((struct lys_node_choice *)node)->dflt; if ((mand = lyp_check_mandatory_(dflt))) { if (mand != dflt) { LOGVAL(node->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory"); LOGVAL(node->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Mandatory node \"%s\" is directly under the default case \"%s\" of the \"%s\" choice.", mand->name, dflt->name, node->name); return -1; } return EXIT_FAILURE; } return EXIT_SUCCESS; } /** * @brief Check status for invalid combination. * * @param[in] flags1 Flags of the referencing node. * @param[in] mod1 Module of the referencing node, * @param[in] name1 Schema node name of the referencing node. * @param[in] flags2 Flags of the referenced node. * @param[in] mod2 Module of the referenced node, * @param[in] name2 Schema node name of the referenced node. * @return EXIT_SUCCES on success, EXIT_FAILURE on invalid reference. */ int lyp_check_status(uint16_t flags1, struct lys_module *mod1, const char *name1, uint16_t flags2, struct lys_module *mod2, const char *name2, const struct lys_node *node) { uint16_t flg1, flg2; flg1 = (flags1 & LYS_STATUS_MASK) ? (flags1 & LYS_STATUS_MASK) : LYS_STATUS_CURR; flg2 = (flags2 & LYS_STATUS_MASK) ? (flags2 & LYS_STATUS_MASK) : LYS_STATUS_CURR; if ((flg1 < flg2) && (lys_main_module(mod1) == lys_main_module(mod2))) { LOGVAL(mod1->ctx, LYE_INSTATUS, node ? LY_VLOG_LYS : LY_VLOG_NONE, node, flg1 == LYS_STATUS_CURR ? "current" : "deprecated", name1, "references", flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", name2); return EXIT_FAILURE; } return EXIT_SUCCESS; } void lyp_del_includedup(struct lys_module *mod, int free_subs) { struct ly_modules_list *models = &mod->ctx->models; uint8_t i; assert(mod && !mod->type); if (models->parsed_submodules_count) { for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i); if (models->parsed_submodules[i] == mod) { if (free_subs) { for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i) { lys_sub_module_remove_devs_augs((struct lys_module *)models->parsed_submodules[i]); lys_submodule_module_data_free((struct lys_submodule *)models->parsed_submodules[i]); lys_submodule_free((struct lys_submodule *)models->parsed_submodules[i], NULL); } } models->parsed_submodules_count = i; if (!models->parsed_submodules_count) { free(models->parsed_submodules); models->parsed_submodules = NULL; } } } } static void lyp_add_includedup(struct lys_module *sub_mod, struct lys_submodule *parsed_submod) { struct ly_modules_list *models = &sub_mod->ctx->models; int16_t i; /* store main module if first include */ if (models->parsed_submodules_count) { for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i); } else { i = -1; } if ((i == -1) || (models->parsed_submodules[i] != lys_main_module(sub_mod))) { ++models->parsed_submodules_count; models->parsed_submodules = ly_realloc(models->parsed_submodules, models->parsed_submodules_count * sizeof *models->parsed_submodules); LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), ); models->parsed_submodules[models->parsed_submodules_count - 1] = lys_main_module(sub_mod); } /* store parsed submodule */ ++models->parsed_submodules_count; models->parsed_submodules = ly_realloc(models->parsed_submodules, models->parsed_submodules_count * sizeof *models->parsed_submodules); LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), ); models->parsed_submodules[models->parsed_submodules_count - 1] = (struct lys_module *)parsed_submod; } /* * types: 0 - include, 1 - import */ static int lyp_check_circmod(struct lys_module *module, const char *value, int type) { LY_ECODE code = type ? LYE_CIRC_IMPORTS : LYE_CIRC_INCLUDES; struct ly_modules_list *models = &module->ctx->models; uint8_t i; /* include/import itself */ if (ly_strequal(module->name, value, 1)) { LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value); return -1; } /* currently parsed modules */ for (i = 0; i < models->parsing_sub_modules_count; i++) { if (ly_strequal(models->parsing_sub_modules[i]->name, value, 1)) { LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value); return -1; } } return 0; } int lyp_check_circmod_add(struct lys_module *module) { struct ly_modules_list *models = &module->ctx->models; /* storing - enlarge the list of modules being currently parsed */ ++models->parsing_sub_modules_count; models->parsing_sub_modules = ly_realloc(models->parsing_sub_modules, models->parsing_sub_modules_count * sizeof *models->parsing_sub_modules); LY_CHECK_ERR_RETURN(!models->parsing_sub_modules, LOGMEM(module->ctx), -1); models->parsing_sub_modules[models->parsing_sub_modules_count - 1] = module; return 0; } void lyp_check_circmod_pop(struct ly_ctx *ctx) { if (!ctx->models.parsing_sub_modules_count) { LOGINT(ctx); return; } /* update the list of currently being parsed modules */ ctx->models.parsing_sub_modules_count--; if (!ctx->models.parsing_sub_modules_count) { free(ctx->models.parsing_sub_modules); ctx->models.parsing_sub_modules = NULL; } } /* * -1 - error - invalid duplicities) * 0 - success, no duplicity * 1 - success, valid duplicity found and stored in *sub */ static int lyp_check_includedup(struct lys_module *mod, const char *name, struct lys_include *inc, struct lys_submodule **sub) { struct lys_module **parsed_sub = mod->ctx->models.parsed_submodules; uint8_t i, parsed_sub_count = mod->ctx->models.parsed_submodules_count; assert(sub); for (i = 0; i < mod->inc_size; ++i) { if (ly_strequal(mod->inc[i].submodule->name, name, 1)) { /* the same module is already included in the same module - error */ LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include"); LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Submodule \"%s\" included twice in the same module \"%s\".", name, mod->name); return -1; } } if (parsed_sub_count) { assert(!parsed_sub[0]->type); for (i = parsed_sub_count - 1; parsed_sub[i]->type; --i) { if (ly_strequal(parsed_sub[i]->name, name, 1)) { /* check revisions, including multiple revisions of a single module is error */ if (inc->rev[0] && (!parsed_sub[i]->rev_size || strcmp(parsed_sub[i]->rev[0].date, inc->rev))) { /* the already included submodule has * - no revision, but here we require some * - different revision than the one required here */ LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include"); LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Including multiple revisions of submodule \"%s\".", name); return -1; } /* the same module is already included in some other submodule, return it */ (*sub) = (struct lys_submodule *)parsed_sub[i]; return 1; } } } /* no duplicity found */ return 0; } /* returns: * 0 - inc successfully filled * -1 - error */ int lyp_check_include(struct lys_module *module, const char *value, struct lys_include *inc, struct unres_schema *unres) { int i; /* check that the submodule was not included yet */ i = lyp_check_includedup(module, value, inc, &inc->submodule); if (i == -1) { return -1; } else if (i == 1) { return 0; } /* submodule is not yet loaded */ /* circular include check */ if (lyp_check_circmod(module, value, 0)) { return -1; } /* try to load the submodule */ inc->submodule = (struct lys_submodule *)ly_ctx_load_sub_module(module->ctx, module, value, inc->rev[0] ? inc->rev : NULL, 1, unres); /* check the result */ if (!inc->submodule) { if (ly_errno != LY_EVALID) { LOGVAL(module->ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "include"); } LOGERR(module->ctx, LY_EVALID, "Including \"%s\" module into \"%s\" failed.", value, module->name); return -1; } /* check the revision */ if (inc->rev[0] && inc->submodule->rev_size && strcmp(inc->rev, inc->submodule->rev[0].date)) { LOGERR(module->ctx, LY_EVALID, "\"%s\" include of submodule \"%s\" in revision \"%s\" not found.", module->name, value, inc->rev); unres_schema_free((struct lys_module *)inc->submodule, &unres, 0); lys_sub_module_remove_devs_augs((struct lys_module *)inc->submodule); lys_submodule_module_data_free((struct lys_submodule *)inc->submodule); lys_submodule_free(inc->submodule, NULL); inc->submodule = NULL; return -1; } /* store the submodule as successfully parsed */ lyp_add_includedup(module, inc->submodule); return 0; } static int lyp_check_include_missing_recursive(struct lys_module *main_module, struct lys_submodule *sub) { uint8_t i, j; void *reallocated; int ret = 0, tmp; struct ly_ctx *ctx = main_module->ctx; for (i = 0; i < sub->inc_size; i++) { /* check that the include is also present in the main module */ for (j = 0; j < main_module->inc_size; j++) { if (main_module->inc[j].submodule == sub->inc[i].submodule) { break; } } if (j == main_module->inc_size) { /* match not found */ if (main_module->version >= LYS_VERSION_1_1) { LOGVAL(ctx, LYE_MISSSTMT, LY_VLOG_NONE, NULL, "include"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".", main_module->name, sub->inc[i].submodule->name, sub->name); /* now we should return error, but due to the issues with freeing the module, we actually have * to go through the all includes and, as in case of 1.0, add them into the main module and fail * at the end when all the includes are in the main module and we can free them */ ret = 1; } else { /* not strictly an error in YANG 1.0 */ LOGWRN(ctx, "The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".", main_module->name, sub->inc[i].submodule->name, sub->name); LOGWRN(ctx, "To avoid further issues, adding submodule \"%s\" into the main module \"%s\".", sub->inc[i].submodule->name, main_module->name); /* but since it is a good practise and because we expect all the includes in the main module * when searching it and also when freeing the module, put it into it */ } main_module->inc_size++; reallocated = realloc(main_module->inc, main_module->inc_size * sizeof *main_module->inc); LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), 1); main_module->inc = reallocated; memset(&main_module->inc[main_module->inc_size - 1], 0, sizeof *main_module->inc); /* to avoid unexpected consequences, copy just a link to the submodule and the revision, * all other substatements of the include are ignored */ memcpy(&main_module->inc[main_module->inc_size - 1].rev, sub->inc[i].rev, LY_REV_SIZE - 1); main_module->inc[main_module->inc_size - 1].submodule = sub->inc[i].submodule; } /* recursion */ tmp = lyp_check_include_missing_recursive(main_module, sub->inc[i].submodule); if (!ret && tmp) { ret = 1; } } return ret; } int lyp_check_include_missing(struct lys_module *main_module) { int ret = 0; uint8_t i; /* in YANG 1.1, all the submodules must be in the main module, check it even for * 1.0 where it will be printed as warning and the include will be added into the main module */ for (i = 0; i < main_module->inc_size; i++) { if (lyp_check_include_missing_recursive(main_module, main_module->inc[i].submodule)) { ret = 1; } } return ret; } /* returns: * 0 - imp successfully filled * -1 - error, imp not cleaned */ int lyp_check_import(struct lys_module *module, const char *value, struct lys_import *imp) { int i; struct lys_module *dup = NULL; struct ly_ctx *ctx = module->ctx; /* check for importing a single module in multiple revisions */ for (i = 0; i < module->imp_size; i++) { if (!module->imp[i].module) { /* skip the not yet filled records */ continue; } if (ly_strequal(module->imp[i].module->name, value, 1)) { /* check revisions, including multiple revisions of a single module is error */ if (imp->rev[0] && (!module->imp[i].module->rev_size || strcmp(module->imp[i].module->rev[0].date, imp->rev))) { /* the already imported module has * - no revision, but here we require some * - different revision than the one required here */ LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value); return -1; } else if (!imp->rev[0]) { /* no revision, remember the duplication, but check revisions after loading the module * because the current revision can be the same (then it is ok) or it can differ (then it * is error */ dup = module->imp[i].module; break; } /* there is duplication, but since prefixes differs (checked in caller of this function), * it is ok */ imp->module = module->imp[i].module; return 0; } } /* circular import check */ if (lyp_check_circmod(module, value, 1)) { return -1; } /* load module - in specific situations it tries to get the module from the context */ imp->module = (struct lys_module *)ly_ctx_load_sub_module(module->ctx, NULL, value, imp->rev[0] ? imp->rev : NULL, module->ctx->models.flags & LY_CTX_ALLIMPLEMENTED ? 1 : 0, NULL); /* check the result */ if (!imp->module) { LOGERR(ctx, LY_EVALID, "Importing \"%s\" module into \"%s\" failed.", value, module->name); return -1; } if (imp->rev[0] && imp->module->rev_size && strcmp(imp->rev, imp->module->rev[0].date)) { LOGERR(ctx, LY_EVALID, "\"%s\" import of module \"%s\" in revision \"%s\" not found.", module->name, value, imp->rev); return -1; } if ((module->version < 2) && imp->rev[0] && (imp->module->version == 2)) { LOGERR(ctx, LY_EVALID, "YANG 1.0 module \"%s\" import with revision of YANG 1.1 module \"%s\".", module->name, imp->module->name); return -1; } if (dup) { /* check the revisions */ if ((dup != imp->module) || (dup->rev_size != imp->module->rev_size && (!dup->rev_size || imp->module->rev_size)) || (dup->rev_size && strcmp(dup->rev[0].date, imp->module->rev[0].date))) { /* - modules are not the same * - one of modules has no revision (except they both has no revision) * - revisions of the modules are not the same */ LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value); return -1; } else { LOGWRN(ctx, "Module \"%s\" is imported by \"%s\" multiple times with different prefixes.", dup->name, module->name); } } return 0; } /* * put the newest revision to the first position */ void lyp_sort_revisions(struct lys_module *module) { uint8_t i, r; struct lys_revision rev; for (i = 1, r = 0; i < module->rev_size; i++) { if (strcmp(module->rev[i].date, module->rev[r].date) > 0) { r = i; } } if (r) { /* the newest revision is not on position 0, switch them */ memcpy(&rev, &module->rev[0], sizeof rev); memcpy(&module->rev[0], &module->rev[r], sizeof rev); memcpy(&module->rev[r], &rev, sizeof rev); } } void lyp_ext_instance_rm(struct ly_ctx *ctx, struct lys_ext_instance ***ext, uint8_t *size, uint8_t index) { uint8_t i; lys_extension_instances_free(ctx, (*ext)[index]->ext, (*ext)[index]->ext_size, NULL); lydict_remove(ctx, (*ext)[index]->arg_value); free((*ext)[index]); /* move the rest of the array */ for (i = index + 1; i < (*size); i++) { (*ext)[i - 1] = (*ext)[i]; } /* clean the last cell in the array structure */ (*ext)[(*size) - 1] = NULL; /* the array is not reallocated here, just change its size */ (*size) = (*size) - 1; if (!(*size)) { /* ext array is empty */ free((*ext)); ext = NULL; } } static int lyp_rfn_apply_ext_(struct lys_refine *rfn, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef) { struct ly_ctx *ctx; int m, n; struct lys_ext_instance *new; void *reallocated; ctx = target->module->ctx; /* shortcut */ m = n = -1; while ((m = lys_ext_iter(rfn->ext, rfn->ext_size, m + 1, substmt)) != -1) { /* refine's substatement includes extensions, copy them to the target, replacing the previous * substatement's extensions if any. In case of refining the extension itself, we are going to * replace only the same extension (pointing to the same definition) */ if (substmt == LYEXT_SUBSTMT_SELF && rfn->ext[m]->def != extdef) { continue; } /* get the index of the extension to replace in the target node */ do { n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); } while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef); /* TODO cover complex extension instances */ if (n == -1) { /* nothing to replace, we are going to add it - reallocate */ new = malloc(sizeof **target->ext); LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext); LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE); target->ext = reallocated; target->ext_size++; /* init */ n = target->ext_size - 1; target->ext[n] = new; target->ext[n]->parent = target; target->ext[n]->parent_type = LYEXT_PAR_NODE; target->ext[n]->flags = 0; target->ext[n]->insubstmt = substmt; target->ext[n]->priv = NULL; target->ext[n]->nodetype = LYS_EXT; target->ext[n]->module = target->module; } else { /* replacing - first remove the allocated data from target */ lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL); lydict_remove(ctx, target->ext[n]->arg_value); } /* common part for adding and replacing */ target->ext[n]->def = rfn->ext[m]->def; /* parent and parent_type do not change */ target->ext[n]->arg_value = lydict_insert(ctx, rfn->ext[m]->arg_value, 0); /* flags do not change */ target->ext[n]->ext_size = rfn->ext[m]->ext_size; lys_ext_dup(ctx, target->module, rfn->ext[m]->ext, rfn->ext[m]->ext_size, target, LYEXT_PAR_NODE, &target->ext[n]->ext, 0, NULL); /* substmt does not change, but the index must be taken from the refine */ target->ext[n]->insubstmt_index = rfn->ext[m]->insubstmt_index; } /* remove the rest of extensions belonging to the original substatement in the target node */ while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) { if (substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef) { /* keep this extension */ continue; } /* remove the item */ lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n); --n; } return EXIT_SUCCESS; } /* * apply extension instances defined under refine's substatements. * It cannot be done immediately when applying the refine because there can be * still unresolved data (e.g. type) and mainly the targeted extension instances. */ int lyp_rfn_apply_ext(struct lys_module *module) { int i, k, a = 0; struct lys_node *root, *nextroot, *next, *node; struct lys_node *target; struct lys_node_uses *uses; struct lys_refine *rfn; struct ly_set *extset; /* refines in uses */ LY_TREE_FOR_SAFE(module->data, nextroot, root) { /* go through the data tree of the module and all the defined augments */ LY_TREE_DFS_BEGIN(root, next, node) { if (node->nodetype == LYS_USES) { uses = (struct lys_node_uses *)node; for (i = 0; i < uses->refine_size; i++) { if (!uses->refine[i].ext_size) { /* no extensions in refine */ continue; } rfn = &uses->refine[i]; /* shortcut */ /* get the target node */ target = NULL; resolve_descendant_schema_nodeid(rfn->target_name, uses->child, LYS_NO_RPC_NOTIF_NODE | LYS_ACTION | LYS_NOTIF, 0, (const struct lys_node **)&target); if (!target) { /* it should always succeed since the target_name was already resolved at least * once when the refine itself was being resolved */ LOGINT(module->ctx);; return EXIT_FAILURE; } /* extensions */ extset = ly_set_new(); k = -1; while ((k = lys_ext_iter(rfn->ext, rfn->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { ly_set_add(extset, rfn->ext[k]->def, 0); } for (k = 0; (unsigned int)k < extset->number; k++) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) { ly_set_free(extset); return EXIT_FAILURE; } } ly_set_free(extset); /* description */ if (rfn->dsc && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DESCRIPTION, NULL)) { return EXIT_FAILURE; } /* reference */ if (rfn->ref && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_REFERENCE, NULL)) { return EXIT_FAILURE; } /* config, in case of notification or rpc/action{notif, the config is not applicable * (there is no config status) */ if ((rfn->flags & LYS_CONFIG_MASK) && (target->flags & LYS_CONFIG_MASK)) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_CONFIG, NULL)) { return EXIT_FAILURE; } } /* default value */ if (rfn->dflt_size && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DEFAULT, NULL)) { return EXIT_FAILURE; } /* mandatory */ if (rfn->flags & LYS_MAND_MASK) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MANDATORY, NULL)) { return EXIT_FAILURE; } } /* presence */ if ((target->nodetype & LYS_CONTAINER) && rfn->mod.presence) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_PRESENCE, NULL)) { return EXIT_FAILURE; } } /* min/max */ if (rfn->flags & LYS_RFN_MINSET) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MIN, NULL)) { return EXIT_FAILURE; } } if (rfn->flags & LYS_RFN_MAXSET) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MAX, NULL)) { return EXIT_FAILURE; } } /* must and if-feature contain extensions on their own, not needed to be solved here */ if (target->ext_size) { /* the allocated target's extension array can be now longer than needed in case * there is less refine substatement's extensions than in original. Since we are * going to reduce or keep the same memory, it is not necessary to test realloc's result */ target->ext = realloc(target->ext, target->ext_size * sizeof *target->ext); } } } LY_TREE_DFS_END(root, next, node) } if (!nextroot && a < module->augment_size) { nextroot = module->augment[a].child; a++; } } return EXIT_SUCCESS; } /* * check mandatory substatements defined under extension instances. */ int lyp_mand_check_ext(struct lys_ext_instance_complex *ext, const char *ext_name) { void *p; int i; struct ly_ctx *ctx = ext->module->ctx; /* check for mandatory substatements */ for (i = 0; ext->substmt[i].stmt; i++) { if (ext->substmt[i].cardinality == LY_STMT_CARD_OPT || ext->substmt[i].cardinality == LY_STMT_CARD_ANY) { /* not a mandatory */ continue; } else if (ext->substmt[i].cardinality == LY_STMT_CARD_SOME) { goto array; } /* * LY_STMT_ORDEREDBY - not checked, has a default value which is the same as explicit system order * LY_STMT_MODIFIER, LY_STMT_STATUS, LY_STMT_MANDATORY, LY_STMT_CONFIG - checked, but mandatory requirement * does not make sense since there is also a default value specified */ switch(ext->substmt[i].stmt) { case LY_STMT_ORDEREDBY: /* always ok */ break; case LY_STMT_REQINSTANCE: case LY_STMT_DIGITS: case LY_STMT_MODIFIER: p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!*(uint8_t*)p) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; case LY_STMT_STATUS: p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!(*(uint16_t*)p & LYS_STATUS_MASK)) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; case LY_STMT_MANDATORY: p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!(*(uint16_t*)p & LYS_MAND_MASK)) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; case LY_STMT_CONFIG: p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!(*(uint16_t*)p & LYS_CONFIG_MASK)) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; default: array: /* stored as a pointer */ p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!(*(void**)p)) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; } } return EXIT_SUCCESS; error: return EXIT_FAILURE; } static int check_deviate_config(struct lys_node *node) { struct ly_ctx *ctx = node->module->ctx; struct lys_node *parent = lys_parent(node); /* from rfc7950#page-86: All key leafs in a list MUST have the same value * for their "config" as the list itself. */ if (lys_is_key((struct lys_node_leaf *)node, NULL) && ((parent->flags & LYS_CONFIG_MASK) != (node->flags & LYS_CONFIG_MASK))) { LOGVAL(ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "config"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "The deviate statement causes a violation of \"config\" rules in" " a key leaf. The \"config\" value in the key leaf must be the same as in the list to which belongs."); return EXIT_FAILURE; } /* from rfc7950#page-135: If the parent node is a case node, * the value is the same as the case node's parent choice node. */ if (node->nodetype == LYS_CASE) { assert(parent && (parent->nodetype == LYS_CHOICE)); if ((parent->flags & LYS_CONFIG_MASK) != (node->flags & LYS_CONFIG_MASK)) { LOGVAL(ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "config"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "The deviate statement causes a violation of \"config\" rules in a case node. " "The \"config\" value in the case must be the same as in the choice to which belongs."); return EXIT_FAILURE; } } /* from rfc7950#page-135: If a node has "config" set to "false", * no node underneath it can have "config" set to "true". */ if (parent && (node->flags & LYS_CONFIG_W) && (parent->flags & LYS_CONFIG_R)) { LOGVAL(ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "config"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "The deviate statement causes a violation of \"config\" nesting rules. The config for node \"%s\" is" " \"true\" while for its parent \"%s\" is \"false\".", node->name, parent->name); return EXIT_FAILURE; } return EXIT_SUCCESS; } int lyp_deviate_inherit_config_r(struct lys_node *node) { struct lys_node *child; /* check node */ if (check_deviate_config(node)) { return EXIT_FAILURE; } /* recursive check and inherit (operations ignore config so it can be skipped) */ if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA | LYS_RPC | LYS_ACTION | LYS_NOTIF))) { LY_TREE_FOR(node->child, child) { if (child->flags & LYS_CONFIG_SET) { /* keep the explicit config, but check it is fine because it may depend on the parent config */ if (check_deviate_config(child)) { return EXIT_FAILURE; } } else { /* inherit config from the parent */ child->flags &= ~LYS_CONFIG_MASK; child->flags |= (node->flags & LYS_CONFIG_MASK); /* recursive call */ if (lyp_deviate_inherit_config_r(child)) { return EXIT_FAILURE; } } } } return EXIT_SUCCESS; } static int lyp_deviate_del_ext(struct lys_node *target, struct lys_ext_instance *ext) { int n = -1, found = 0; char *path; while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, ext->insubstmt)) != -1) { if (target->ext[n]->def != ext->def) { continue; } if (ext->def->argument) { /* check matching arguments */ if (!ly_strequal(target->ext[n]->arg_value, ext->arg_value, 1)) { continue; } } /* we have the matching extension - remove it */ ++found; lyp_ext_instance_rm(target->module->ctx, &target->ext, &target->ext_size, n); --n; } if (!found) { path = lys_path(target, LYS_PATH_FIRST_PREFIX); LOGERR(target->module->ctx, LY_EVALID, "Extension deviation: extension \"%s\" to delete not found in \"%s\".", ext->def->name, path) free(path); return EXIT_FAILURE; } return EXIT_SUCCESS; } static int lyp_deviate_apply_ext(struct lys_deviate *dev, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef) { struct ly_ctx *ctx; int m, n; struct lys_ext_instance *new; void *reallocated; /* LY_DEVIATE_ADD and LY_DEVIATE_RPL are very similar so they are implement the same way - in replacing, * there can be some extension instances in the target, in case of adding, there should not be any so we * will be just adding. */ ctx = target->module->ctx; /* shortcut */ m = n = -1; while ((m = lys_ext_iter(dev->ext, dev->ext_size, m + 1, substmt)) != -1) { /* deviate and its substatements include extensions, copy them to the target, replacing the previous * extensions if any. In case of deviating extension itself, we have to deviate only the same type * of the extension as specified in the deviation */ if (substmt == LYEXT_SUBSTMT_SELF && dev->ext[m]->def != extdef) { continue; } if (substmt == LYEXT_SUBSTMT_SELF && dev->mod == LY_DEVIATE_ADD) { /* in case of adding extension, we will be replacing only the inherited extensions */ do { n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); } while (n != -1 && (target->ext[n]->def != extdef || !(target->ext[n]->flags & LYEXT_OPT_INHERIT))); } else { /* get the index of the extension to replace in the target node */ do { n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); /* if we are applying extension deviation, we have to deviate only the same type of the extension */ } while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef); } if (n == -1) { /* nothing to replace, we are going to add it - reallocate */ new = malloc(sizeof **target->ext); LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext); LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE); target->ext = reallocated; target->ext_size++; n = target->ext_size - 1; } else { /* replacing - the original set of extensions is actually backuped together with the * node itself, so we are supposed only to free the allocated data here ... */ lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL); lydict_remove(ctx, target->ext[n]->arg_value); free(target->ext[n]); /* and prepare the new structure */ new = malloc(sizeof **target->ext); LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); } /* common part for adding and replacing - fill the newly created / replaced cell */ target->ext[n] = new; target->ext[n]->def = dev->ext[m]->def; target->ext[n]->arg_value = lydict_insert(ctx, dev->ext[m]->arg_value, 0); target->ext[n]->flags = 0; target->ext[n]->parent = target; target->ext[n]->parent_type = LYEXT_PAR_NODE; target->ext[n]->insubstmt = substmt; target->ext[n]->insubstmt_index = dev->ext[m]->insubstmt_index; target->ext[n]->ext_size = dev->ext[m]->ext_size; lys_ext_dup(ctx, target->module, dev->ext[m]->ext, dev->ext[m]->ext_size, target, LYEXT_PAR_NODE, &target->ext[n]->ext, 1, NULL); target->ext[n]->nodetype = LYS_EXT; target->ext[n]->module = target->module; target->ext[n]->priv = NULL; /* TODO cover complex extension instances */ } /* remove the rest of extensions belonging to the original substatement in the target node, * due to possible reverting of the deviation effect, they are actually not removed, just moved * to the backup of the original node when the original node is backuped, here we just have to * free the replaced / deleted originals */ while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) { if (substmt == LYEXT_SUBSTMT_SELF) { /* if we are applying extension deviation, we are going to remove only * - the same type of the extension in case of replacing * - the same type of the extension which was inherited in case of adding * note - delete deviation is covered in lyp_deviate_del_ext */ if (target->ext[n]->def != extdef || (dev->mod == LY_DEVIATE_ADD && !(target->ext[n]->flags & LYEXT_OPT_INHERIT))) { /* keep this extension */ continue; } } /* remove the item */ lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n); --n; } return EXIT_SUCCESS; } /* * not-supported deviations are not processed since they affect the complete node, not just their substatements */ int lyp_deviation_apply_ext(struct lys_module *module) { int i, j, k; struct lys_deviate *dev; struct lys_node *target; struct ly_set *extset; for (i = 0; i < module->deviation_size; i++) { target = NULL; extset = NULL; j = resolve_schema_nodeid(module->deviation[i].target_name, NULL, module, &extset, 0, 0); if (j == -1) { return EXIT_FAILURE; } else if (!extset) { /* LY_DEVIATE_NO */ ly_set_free(extset); continue; } target = extset->set.s[0]; ly_set_free(extset); for (j = 0; j < module->deviation[i].deviate_size; j++) { dev = &module->deviation[i].deviate[j]; if (!dev->ext_size) { /* no extensions in deviate and its substatement, nothing to do here */ continue; } /* extensions */ if (dev->mod == LY_DEVIATE_DEL) { k = -1; while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { if (lyp_deviate_del_ext(target, dev->ext[k])) { return EXIT_FAILURE; } } /* In case of LY_DEVIATE_DEL, we are applying only extension deviation, removing * of the substatement's extensions was already done when the substatement was applied. * Extension deviation could not be applied by the parser since the extension could be unresolved, * which is not the issue of the other substatements. */ continue; } else { extset = ly_set_new(); k = -1; while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { ly_set_add(extset, dev->ext[k]->def, 0); } for (k = 0; (unsigned int)k < extset->number; k++) { if (lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) { ly_set_free(extset); return EXIT_FAILURE; } } ly_set_free(extset); } /* unique */ if (dev->unique_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNIQUE, NULL)) { return EXIT_FAILURE; } /* units */ if (dev->units && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNITS, NULL)) { return EXIT_FAILURE; } /* default */ if (dev->dflt_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_DEFAULT, NULL)) { return EXIT_FAILURE; } /* config */ if ((dev->flags & LYS_CONFIG_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_CONFIG, NULL)) { return EXIT_FAILURE; } /* mandatory */ if ((dev->flags & LYS_MAND_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MANDATORY, NULL)) { return EXIT_FAILURE; } /* min/max */ if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MIN, NULL)) { return EXIT_FAILURE; } if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MAX, NULL)) { return EXIT_FAILURE; } /* type and must contain extension instances in their structures */ } } return EXIT_SUCCESS; } int lyp_ctx_check_module(struct lys_module *module) { struct ly_ctx *ctx; int i, match_i = -1, to_implement; const char *last_rev = NULL; assert(module); to_implement = 0; ctx = module->ctx; /* find latest revision */ for (i = 0; i < module->rev_size; ++i) { if (!last_rev || (strcmp(last_rev, module->rev[i].date) < 0)) { last_rev = module->rev[i].date; } } for (i = 0; i < ctx->models.used; i++) { /* check name (name/revision) and namespace uniqueness */ if (!strcmp(ctx->models.list[i]->name, module->name)) { if (to_implement) { if (i == match_i) { continue; } LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.", module->name, last_rev ? last_rev : "", ctx->models.list[i]->rev[0].date); return -1; } else if (!ctx->models.list[i]->rev_size && module->rev_size) { LOGERR(ctx, LY_EINVAL, "Module \"%s\" without revision already in context.", module->name); return -1; } else if (ctx->models.list[i]->rev_size && !module->rev_size) { LOGERR(ctx, LY_EINVAL, "Module \"%s\" with revision \"%s\" already in context.", module->name, ctx->models.list[i]->rev[0].date); return -1; } else if ((!module->rev_size && !ctx->models.list[i]->rev_size) || !strcmp(ctx->models.list[i]->rev[0].date, last_rev)) { LOGVRB("Module \"%s@%s\" already in context.", module->name, last_rev ? last_rev : ""); /* if disabled, enable first */ if (ctx->models.list[i]->disabled) { lys_set_enabled(ctx->models.list[i]); } to_implement = module->implemented; match_i = i; if (to_implement && !ctx->models.list[i]->implemented) { /* check first that it is okay to change it to implemented */ i = -1; continue; } return 1; } else if (module->implemented && ctx->models.list[i]->implemented) { LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.", module->name, last_rev ? last_rev : "", ctx->models.list[i]->rev[0].date); return -1; } /* else keep searching, for now the caller is just adding * another revision of an already present schema */ } else if (!strcmp(ctx->models.list[i]->ns, module->ns)) { LOGERR(ctx, LY_EINVAL, "Two different modules (\"%s\" and \"%s\") have the same namespace \"%s\".", ctx->models.list[i]->name, module->name, module->ns); return -1; } } if (to_implement) { if (lys_set_implemented(ctx->models.list[match_i])) { return -1; } return 1; } return 0; } int lyp_ctx_add_module(struct lys_module *module) { struct lys_module **newlist = NULL; int i; assert(!lyp_ctx_check_module(module)); #ifndef NDEBUG int j; /* check that all augments are resolved */ for (i = 0; i < module->augment_size; ++i) { assert(module->augment[i].target); } for (i = 0; i < module->inc_size; ++i) { for (j = 0; j < module->inc[i].submodule->augment_size; ++j) { assert(module->inc[i].submodule->augment[j].target); } } #endif /* add to the context's list of modules */ if (module->ctx->models.used == module->ctx->models.size) { newlist = realloc(module->ctx->models.list, (2 * module->ctx->models.size) * sizeof *newlist); LY_CHECK_ERR_RETURN(!newlist, LOGMEM(module->ctx), -1); for (i = module->ctx->models.size; i < module->ctx->models.size * 2; i++) { newlist[i] = NULL; } module->ctx->models.size *= 2; module->ctx->models.list = newlist; } module->ctx->models.list[module->ctx->models.used++] = module; module->ctx->models.module_set_id++; return 0; } /** * Store UTF-8 character specified as 4byte integer into the dst buffer. * Returns number of written bytes (4 max), expects that dst has enough space. * * UTF-8 mapping: * 00000000 -- 0000007F: 0xxxxxxx * 00000080 -- 000007FF: 110xxxxx 10xxxxxx * 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx * 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx * * Includes checking for valid characters (following RFC 7950, sec 9.4) */ unsigned int pututf8(struct ly_ctx *ctx, char *dst, int32_t value) { if (value < 0x80) { /* one byte character */ if (value < 0x20 && value != 0x09 && value != 0x0a && value != 0x0d) { goto error; } dst[0] = value; return 1; } else if (value < 0x800) { /* two bytes character */ dst[0] = 0xc0 | (value >> 6); dst[1] = 0x80 | (value & 0x3f); return 2; } else if (value < 0xfffe) { /* three bytes character */ if (((value & 0xf800) == 0xd800) || (value >= 0xfdd0 && value <= 0xfdef)) { /* exclude surrogate blocks %xD800-DFFF */ /* exclude noncharacters %xFDD0-FDEF */ goto error; } dst[0] = 0xe0 | (value >> 12); dst[1] = 0x80 | ((value >> 6) & 0x3f); dst[2] = 0x80 | (value & 0x3f); return 3; } else if (value < 0x10fffe) { if ((value & 0xffe) == 0xffe) { /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF, * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF, * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */ goto error; } /* four bytes character */ dst[0] = 0xf0 | (value >> 18); dst[1] = 0x80 | ((value >> 12) & 0x3f); dst[2] = 0x80 | ((value >> 6) & 0x3f); dst[3] = 0x80 | (value & 0x3f); return 4; } error: /* out of range */ LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, NULL); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); return 0; } unsigned int copyutf8(struct ly_ctx *ctx, char *dst, const char *src) { uint32_t value; /* unicode characters */ if (!(src[0] & 0x80)) { /* one byte character */ if (src[0] < 0x20 && src[0] != 0x09 && src[0] != 0x0a && src[0] != 0x0d) { LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%02x", src[0]); return 0; } dst[0] = src[0]; return 1; } else if ((src[0] & 0xe0) == 0xc0) { /* two bytes character */ if ((src[1] & 0xc0) != 0x80) { LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%02x 0x%02x", (unsigned char)src[0], (unsigned char)src[1]) return 0; } dst[0] = src[0]; dst[1] = src[1]; return 2; } else if ((src[0] & 0xf0) == 0xe0) { /* three bytes character */ if ((src[1] & 0xc0) != 0x80 || (src[2] & 0xc0) != 0x80) { LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%02x 0x%02x 0x%02x", (unsigned char)src[0], (unsigned char)src[1], (src[1] ? (unsigned char)src[2] : 0)) return 0; } value = ((uint32_t)(src[0] & 0xf) << 12) | ((uint32_t)(src[1] & 0x3f) << 6) | (src[2] & 0x3f); if (((value & 0xf800) == 0xd800) || (value >= 0xfdd0 && value <= 0xfdef) || (value & 0xffe) == 0xffe) { /* exclude surrogate blocks %xD800-DFFF */ /* exclude noncharacters %xFDD0-FDEF */ /* exclude noncharacters %xFFFE-FFFF */ LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); return 0; } dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; return 3; } else if ((src[0] & 0xf8) == 0xf0) { /* four bytes character */ if ((src[1] & 0xc0) != 0x80 || (src[2] & 0xc0) != 0x80 || (src[3] & 0xc0) != 0x80) { LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%02x 0x%02x 0x%02x 0x%02x", (unsigned char)src[0], (unsigned char)src[1], (src[1] ? (unsigned char)src[2] : 0), ((src[1] && src[2]) ? (unsigned char)src[3] : 0)); return 0; } value = ((uint32_t)(src[0] & 0x7) << 18) | ((uint32_t)(src[1] & 0x3f) << 12) | ((uint32_t)(src[2] & 0x3f) << 6) | (src[3] & 0x3f); if ((value & 0xffe) == 0xffe) { /* exclude noncharacters %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF, * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF, * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */ LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); return 0; } dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; return 4; } else { LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 leading byte 0x%02x", src[0]); return 0; } } const struct lys_module * lyp_get_module(const struct lys_module *module, const char *prefix, int pref_len, const char *name, int name_len, int in_data) { const struct lys_module *main_module; char *str; int i; assert(!prefix || !name); if (prefix && !pref_len) { pref_len = strlen(prefix); } if (name && !name_len) { name_len = strlen(name); } main_module = lys_main_module(module); /* module own prefix, submodule own prefix, (sub)module own name */ if ((!prefix || (!module->type && !strncmp(main_module->prefix, prefix, pref_len) && !main_module->prefix[pref_len]) || (module->type && !strncmp(module->prefix, prefix, pref_len) && !module->prefix[pref_len])) && (!name || (!strncmp(main_module->name, name, name_len) && !main_module->name[name_len]))) { return main_module; } /* standard import */ for (i = 0; i < module->imp_size; ++i) { if ((!prefix || (!strncmp(module->imp[i].prefix, prefix, pref_len) && !module->imp[i].prefix[pref_len])) && (!name || (!strncmp(module->imp[i].module->name, name, name_len) && !module->imp[i].module->name[name_len]))) { return module->imp[i].module; } } /* module required by a foreign grouping, deviation, or submodule */ if (name) { str = strndup(name, name_len); if (!str) { LOGMEM(module->ctx); return NULL; } main_module = ly_ctx_get_module(module->ctx, str, NULL, 0); /* try data callback */ if (!main_module && in_data && module->ctx->data_clb) { main_module = module->ctx->data_clb(module->ctx, str, NULL, 0, module->ctx->data_clb_data); } free(str); return main_module; } return NULL; } const struct lys_module * lyp_get_import_module_ns(const struct lys_module *module, const char *ns) { int i; const struct lys_module *mod = NULL; assert(module && ns); if (module->type) { /* the module is actually submodule and to get the namespace, we need the main module */ if (ly_strequal(((struct lys_submodule *)module)->belongsto->ns, ns, 0)) { return ((struct lys_submodule *)module)->belongsto; } } else { /* module's own namespace */ if (ly_strequal(module->ns, ns, 0)) { return module; } } /* imported modules */ for (i = 0; i < module->imp_size; ++i) { if (ly_strequal(module->imp[i].module->ns, ns, 0)) { return module->imp[i].module; } } return mod; } const char * lyp_get_yang_data_template_name(const struct lyd_node *node) { struct lys_node *snode; snode = lys_parent(node->schema); while (snode && snode->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE)) { snode = lys_parent(snode); } if (snode && snode->nodetype == LYS_EXT && strcmp(((struct lys_ext_instance_complex *)snode)->def->name, "yang-data") == 0) { return ((struct lys_ext_instance_complex *)snode)->arg_value; } else { return NULL; } } const struct lys_node * lyp_get_yang_data_template(const struct lys_module *module, const char *yang_data_name, int yang_data_name_len) { int i, j; const struct lys_node *ret = NULL; const struct lys_submodule *submodule; for(i = 0; i < module->ext_size; ++i) { if (!strcmp(module->ext[i]->def->name, "yang-data") && !strncmp(module->ext[i]->arg_value, yang_data_name, yang_data_name_len) && !module->ext[i]->arg_value[yang_data_name_len]) { ret = (struct lys_node *)module->ext[i]; break; } } for(j = 0; !ret && j < module->inc_size; ++j) { submodule = module->inc[j].submodule; for(i = 0; i < submodule->ext_size; ++i) { if (!strcmp(submodule->ext[i]->def->name, "yang-data") && !strncmp(submodule->ext[i]->arg_value, yang_data_name, yang_data_name_len) && !submodule->ext[i]->arg_value[yang_data_name_len]) { ret = (struct lys_node *)submodule->ext[i]; break; } } } return ret; } int lyp_get_ext_list(struct ly_ctx *ctx, void *elem, LYEXT_PAR elem_type, struct lys_ext_instance ****ext_list, uint8_t **ext_size, const char **stmt) { const char *statement = NULL; switch (elem_type) { case LYEXT_PAR_MODULE: *ext_size = &((struct lys_module *)elem)->ext_size; *ext_list = &((struct lys_module *)elem)->ext; statement = ((struct lys_module *)elem)->type ? "submodule" : "module"; break; case LYEXT_PAR_IMPORT: *ext_size = &((struct lys_import *)elem)->ext_size; *ext_list = &((struct lys_import *)elem)->ext; statement = "import"; break; case LYEXT_PAR_INCLUDE: *ext_size = &((struct lys_include *)elem)->ext_size; *ext_list = &((struct lys_include *)elem)->ext; statement = "include"; break; case LYEXT_PAR_REVISION: *ext_size = &((struct lys_revision *)elem)->ext_size; *ext_list = &((struct lys_revision *)elem)->ext; statement = "revision"; break; case LYEXT_PAR_NODE: *ext_size = &((struct lys_node *)elem)->ext_size; *ext_list = &((struct lys_node *)elem)->ext; statement = strnodetype(((struct lys_node *)elem)->nodetype); break; case LYEXT_PAR_IDENT: *ext_size = &((struct lys_ident *)elem)->ext_size; *ext_list = &((struct lys_ident *)elem)->ext; statement = "identity"; break; case LYEXT_PAR_TYPE: *ext_size = &((struct lys_type *)elem)->ext_size; *ext_list = &((struct lys_type *)elem)->ext; statement = "type"; break; case LYEXT_PAR_TYPE_BIT: *ext_size = &((struct lys_type_bit *)elem)->ext_size; *ext_list = &((struct lys_type_bit *)elem)->ext; statement = "bit"; break; case LYEXT_PAR_TYPE_ENUM: *ext_size = &((struct lys_type_enum *)elem)->ext_size; *ext_list = &((struct lys_type_enum *)elem)->ext; statement = "enum"; break; case LYEXT_PAR_TPDF: *ext_size = &((struct lys_tpdf *)elem)->ext_size; *ext_list = &((struct lys_tpdf *)elem)->ext; statement = "typedef"; break; case LYEXT_PAR_EXT: *ext_size = &((struct lys_ext *)elem)->ext_size; *ext_list = &((struct lys_ext *)elem)->ext; statement = "extension"; break; case LYEXT_PAR_EXTINST: *ext_size = &((struct lys_ext_instance *)elem)->ext_size; *ext_list = &((struct lys_ext_instance *)elem)->ext; statement = "extension instance"; break; case LYEXT_PAR_FEATURE: *ext_size = &((struct lys_feature *)elem)->ext_size; *ext_list = &((struct lys_feature *)elem)->ext; statement = "feature"; break; case LYEXT_PAR_REFINE: *ext_size = &((struct lys_refine *)elem)->ext_size; *ext_list = &((struct lys_refine *)elem)->ext; statement = "refine"; break; case LYEXT_PAR_RESTR: *ext_size = &((struct lys_restr *)elem)->ext_size; *ext_list = &((struct lys_restr *)elem)->ext; statement = "YANG restriction"; break; case LYEXT_PAR_WHEN: *ext_size = &((struct lys_when *)elem)->ext_size; *ext_list = &((struct lys_when *)elem)->ext; statement = "when"; break; case LYEXT_PAR_DEVIATE: *ext_size = &((struct lys_deviate *)elem)->ext_size; *ext_list = &((struct lys_deviate *)elem)->ext; statement = "deviate"; break; case LYEXT_PAR_DEVIATION: *ext_size = &((struct lys_deviation *)elem)->ext_size; *ext_list = &((struct lys_deviation *)elem)->ext; statement = "deviation"; break; default: LOGERR(ctx, LY_EINT, "parent type %d", elem_type); return -1; } if (stmt) { *stmt = statement; } return 0; } inline void lyp_reduce_ext_list(struct lys_ext_instance ***ext, uint8_t new_size, uint8_t orig_size) { struct lys_ext_instance **tmp; if (new_size != orig_size) { if (new_size == 0) { free(*ext); *ext = NULL; } else { tmp = realloc(*ext, new_size * sizeof(*tmp)); if (!tmp) { /* we just reduce the size, so this failure is harmless. */ return; } *ext = tmp; } } }