From 84bf1b86d58a888e6507025ba1cdb8b41be31261 Mon Sep 17 00:00:00 2001 From: Mislav Novakovic Date: Wed, 16 Jan 2019 14:58:45 +0100 Subject: [PATCH] libyang: update to 0.16-r3 Signed-off-by: Mislav Novakovic --- libs/libyang/Makefile | 4 +- ...CHANGE-convert-internal-types-to-canonical | 1030 +++++++++++++++++ 2 files changed, 1032 insertions(+), 2 deletions(-) create mode 100644 libs/libyang/patches/001-user-types-CHANGE-convert-internal-types-to-canonical diff --git a/libs/libyang/Makefile b/libs/libyang/Makefile index 25fc1a5d21..d11803ec6d 100644 --- a/libs/libyang/Makefile +++ b/libs/libyang/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=libyang -PKG_VERSION:=0.16-r2 +PKG_VERSION:=0.16-r3 PKG_RELEASE:=2 PKG_LICENSE:=GPL-2.0+ @@ -16,7 +16,7 @@ PKG_MAINTAINER:=Mislav Novakovic PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/CESNET/libyang/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=cf354481788f224c58ebe4785a08f992ef00af503529c8d516fdc4d0592996e0 +PKG_HASH:=4745460dedc4ba17d8bcfc39ad9ba0d1b91bbe82b55b9417a090390909ba8ca5 CMAKE_INSTALL:=1 diff --git a/libs/libyang/patches/001-user-types-CHANGE-convert-internal-types-to-canonical b/libs/libyang/patches/001-user-types-CHANGE-convert-internal-types-to-canonical new file mode 100644 index 0000000000..20692065d6 --- /dev/null +++ b/libs/libyang/patches/001-user-types-CHANGE-convert-internal-types-to-canonical @@ -0,0 +1,1030 @@ +Index: libyang-0.16-r3/CMakeLists.txt +=================================================================== +--- libyang-0.16-r3.orig/CMakeLists.txt ++++ libyang-0.16-r3/CMakeLists.txt +@@ -351,8 +351,8 @@ else() + add_subdirectory(src/extensions) + endif(ENABLE_STATIC) + +-# YANG user types plugins ("user_ipv4" is just an example, not installed by default) +-set(USER_TYPE_LIST "user_date_and_time") ++# YANG user types plugins ++set(USER_TYPE_LIST "user_yang_types" "user_inet_types") + if(ENABLE_STATIC) + set(USER_TYPE_LIST_SIZE " 0 ") + foreach(USER_TYPE ${USER_TYPE_LIST}) +Index: libyang-0.16-r3/src/parser.c +=================================================================== +--- libyang-0.16-r3.orig/src/parser.c ++++ libyang-0.16-r3/src/parser.c +@@ -1936,7 +1936,7 @@ lyp_parse_value(struct lys_type *type, c + + /* search user types in case this value is supposed to be stored in a custom way */ + if (store && type->der && type->der->module) { +- c = lytype_store(type->der->module, type->der->name, *value_, val); ++ c = lytype_store(type->der->module, type->der->name, value_, val); + if (c == -1) { + goto error; + } else if (!c) { +Index: libyang-0.16-r3/src/parser.h +=================================================================== +--- libyang-0.16-r3.orig/src/parser.h ++++ libyang-0.16-r3/src/parser.h +@@ -258,11 +258,11 @@ struct lyext_plugin *ext_get_plugin(cons + * + * @param[in] mod Module of the type. + * @param[in] type_name Type (typedef) name. +- * @param[in] value_str Value to store as a string. ++ * @param[in,out] value_str Stored string value, can be overwritten by the user store callback. + * @param[in,out] value Filled value to be overwritten by the user store callback. + * @return 0 on successful storing, 1 if the type is not a user type, -1 on error. + */ +-int lytype_store(const struct lys_module *mod, const char *type_name, const char *value_str, lyd_val *value); ++int lytype_store(const struct lys_module *mod, const char *type_name, const char **value_str, lyd_val *value); + + /** + * @brief Free a user type stored value. +Index: libyang-0.16-r3/src/plugins.c +=================================================================== +--- libyang-0.16-r3.orig/src/plugins.c ++++ libyang-0.16-r3/src/plugins.c +@@ -574,7 +574,7 @@ lytype_find(const char *module, const ch + } + + int +-lytype_store(const struct lys_module *mod, const char *type_name, const char *value_str, lyd_val *value) ++lytype_store(const struct lys_module *mod, const char *type_name, const char **value_str, lyd_val *value) + { + struct lytype_plugin_list *p; + char *err_msg = NULL; +@@ -583,9 +583,9 @@ lytype_store(const struct lys_module *mo + + p = lytype_find(mod->name, mod->rev_size ? mod->rev[0].date : NULL, type_name); + if (p) { +- if (p->store_clb(type_name, value_str, value, &err_msg)) { ++ if (p->store_clb(mod->ctx, type_name, value_str, value, &err_msg)) { + if (!err_msg) { +- if (asprintf(&err_msg, "Failed to store value \"%s\" of user type \"%s\".", value_str, type_name) == -1) { ++ if (asprintf(&err_msg, "Failed to store value \"%s\" of user type \"%s\".", *value_str, type_name) == -1) { + LOGMEM(mod->ctx); + return -1; + } +Index: libyang-0.16-r3/src/tree_data.c +=================================================================== +--- libyang-0.16-r3.orig/src/tree_data.c ++++ libyang-0.16-r3/src/tree_data.c +@@ -5476,7 +5476,7 @@ _lyd_dup_node(const struct lyd_node *nod + } + + if (sleaf->type.der && sleaf->type.der->module) { +- r = lytype_store(sleaf->type.der->module, sleaf->type.der->name, new_leaf->value_str, &new_leaf->value); ++ r = lytype_store(sleaf->type.der->module, sleaf->type.der->name, &new_leaf->value_str, &new_leaf->value); + if (r == -1) { + goto error; + } else if (!r) { +Index: libyang-0.16-r3/src/user_types.h +=================================================================== +--- libyang-0.16-r3.orig/src/user_types.h ++++ libyang-0.16-r3/src/user_types.h +@@ -33,13 +33,15 @@ extern "C" { + * This callback should overwrite the value stored in \p value using some custom encoding. Be careful, + * if the type is #LY_TYPE_BITS, the bits must be freed before overwritting the union value. + * ++ * @param[in] ctx libyang ctx to enable correct manipulation with values that are in the dictionary. + * @param[in] type_name Name of the type being stored. +- * @param[in] value_str String value to be stored. ++ * @param[in,out] value_str String value to be stored. + * @param[in,out] value Value union for the value to be stored in (already is but in the standard way). + * @param[out] err_msg Can be filled on error. If not, a generic error message will be printed. + * @return 0 on success, non-zero if an error occured and the value could not be stored for any reason. + */ +-typedef int (*lytype_store_clb)(const char *type_name, const char *value_str, lyd_val *value, char **err_msg); ++typedef int (*lytype_store_clb)(struct ly_ctx *ctx, const char *type_name, const char **value_str, lyd_val *value, ++ char **err_msg); + + struct lytype_plugin_list { + const char *module; /**< Name of the module where the type is defined. */ +Index: libyang-0.16-r3/src/user_types/user_inet_types.c +=================================================================== +--- /dev/null ++++ libyang-0.16-r3/src/user_types/user_inet_types.c +@@ -0,0 +1,235 @@ ++/** ++ * @file user_inet_types.c ++ * @author Michal Vasko ++ * @brief ietf-inet-types typedef conversion to canonical format ++ * ++ * Copyright (c) 2018 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 "../user_types.h" ++ ++#ifdef __GNUC__ ++# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) ++#else ++# define UNUSED(x) UNUSED_ ## x ++#endif ++ ++static char * ++convert_ipv6_addr(const char *ipv6_addr, char **err_msg) ++{ ++ char buf[sizeof(struct in6_addr)], *str; ++ ++ str = malloc(INET6_ADDRSTRLEN); ++ if (!str) { ++ *err_msg = NULL; ++ return NULL; ++ } ++ ++ if (!inet_pton(AF_INET6, ipv6_addr, buf)) { ++ asprintf(err_msg, "Failed to convert IPv6 address \"%s\".", ipv6_addr); ++ free(str); ++ return NULL; ++ } ++ ++ if (!inet_ntop(AF_INET6, buf, str, INET6_ADDRSTRLEN)) { ++ asprintf(err_msg, "Failed to convert IPv6 address (%s).", strerror(errno)); ++ free(str); ++ return NULL; ++ } ++ ++ return str; ++} ++ ++static int ++ip_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const char **value_str, lyd_val *value, char **err_msg) ++{ ++ char *ptr, *ipv6_addr, *result, *tmp; ++ ++ if (!strchr(*value_str, ':')) { ++ /* not an IPv6 address */ ++ return 0; ++ } ++ ++ if ((ptr = strchr(*value_str, '%'))) { ++ /* there is a zone index */ ++ ipv6_addr = strndup(*value_str, ptr - *value_str); ++ } else { ++ ipv6_addr = (char *)*value_str; ++ } ++ ++ /* convert to canonical format */ ++ result = convert_ipv6_addr(ipv6_addr, err_msg); ++ if (ptr) { ++ free(ipv6_addr); ++ } ++ ++ /* failure */ ++ if (!result) { ++ return 1; ++ } ++ ++ if (strncmp(*value_str, result, strlen(result))) { ++ /* some conversion took place, update the value */ ++ if (ptr) { ++ tmp = result; ++ if (asprintf(&result, "%s%s", tmp, ptr) == -1) { ++ free(tmp); ++ *err_msg = NULL; ++ return 1; ++ } ++ free(tmp); ++ } ++ ++ lydict_remove(ctx, *value_str); ++ *value_str = lydict_insert_zc(ctx, result); ++ value->string = *value_str; ++ } else { ++ free(result); ++ } ++ ++ return 0; ++} ++ ++static int ++ipv4_prefix_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const char **value_str, lyd_val *value, char **err_msg) ++{ ++ char *pref_str, *ptr, *result; ++ int result_len, i, j, num; ++ unsigned long int pref; ++ ++ pref_str = strchr(*value_str, '/'); ++ if (!pref_str) { ++ asprintf(err_msg, "Invalid IPv4 prefix \"%s\".", *value_str); ++ return 1; ++ } ++ ++ pref = strtoul(pref_str + 1, &ptr, 10); ++ if (ptr[0]) { ++ asprintf(err_msg, "Invalid IPv4 prefix \"%s\".", *value_str); ++ return 1; ++ } ++ ++ result = malloc(INET_ADDRSTRLEN + 3); ++ if (!result) { ++ *err_msg = NULL; ++ return 1; ++ } ++ ++ /* generate ip prefix mask */ ++ result_len = 0; ++ for (i = 0; i < 4; ++i) { ++ num = 0; ++ for (j = 0; (j < 8) && pref; ++j) { ++ num += (1 << j); ++ --pref; ++ } ++ ++ result_len += sprintf(result + result_len, "%s%d", i ? "." : "", num); ++ } ++ ++ /* add the prefix */ ++ result_len += sprintf(result + result_len, "%s", pref_str); ++ ++ if (strcmp(result, *value_str)) { ++ /* some conversion took place, update the value */ ++ lydict_remove(ctx, *value_str); ++ *value_str = lydict_insert_zc(ctx, result); ++ value->string = *value_str; ++ } else { ++ free(result); ++ } ++ ++ return 0; ++} ++ ++static int ++ipv6_prefix_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const char **value_str, lyd_val *value, char **err_msg) ++{ ++ char *pref_str, *ptr, *result; ++ int result_len, i, j, num; ++ unsigned long int pref; ++ ++ pref_str = strchr(*value_str, '/'); ++ if (!pref_str) { ++ asprintf(err_msg, "Invalid IPv6 prefix \"%s\".", *value_str); ++ return 1; ++ } ++ ++ pref = strtoul(pref_str + 1, &ptr, 10); ++ if (ptr[0]) { ++ asprintf(err_msg, "Invalid IPv6 prefix \"%s\".", *value_str); ++ return 1; ++ } ++ ++ result = malloc(INET6_ADDRSTRLEN + 4); ++ if (!result) { ++ *err_msg = NULL; ++ return 1; ++ } ++ ++ /* generate ipv6 prefix mask */ ++ result_len = 0; ++ for (i = 0; i < 8; ++i) { ++ num = 0; ++ for (j = 0; (j < 16) && pref; ++j) { ++ num += (1 << j); ++ --pref; ++ } ++ ++ result_len += sprintf(result + result_len, "%s%x", i ? ":" : "", num); ++ ++ if (!pref && (i < 6)) { ++ /* shorten ending zeros */ ++ result_len += sprintf(result + result_len, "::"); ++ break; ++ } ++ } ++ ++ /* add the prefix */ ++ result_len += sprintf(result + result_len, "%s", pref_str); ++ ++ if (strcmp(result, *value_str)) { ++ /* some conversion took place, update the value */ ++ lydict_remove(ctx, *value_str); ++ *value_str = lydict_insert_zc(ctx, result); ++ value->string = *value_str; ++ } else { ++ free(result); ++ } ++ ++ return 0; ++} ++ ++static int ++ip_prefix_store_clb(struct ly_ctx *ctx, const char *type_name, const char **value_str, lyd_val *value, char **err_msg) ++{ ++ if (strchr(*value_str, ':')) { ++ return ipv6_prefix_store_clb(ctx, type_name, value_str, value, err_msg); ++ } ++ return ipv4_prefix_store_clb(ctx, type_name, value_str, value, err_msg); ++} ++ ++/* Name of this array must match the file name! */ ++struct lytype_plugin_list user_inet_types[] = { ++ {"ietf-inet-types", "2013-07-15", "ip-address", ip_store_clb, NULL}, ++ {"ietf-inet-types", "2013-07-15", "ipv6-address", ip_store_clb, NULL}, ++ {"ietf-inet-types", "2013-07-15", "ip-address-no-zone", ip_store_clb, NULL}, ++ {"ietf-inet-types", "2013-07-15", "ipv6-address-no-zone", ip_store_clb, NULL}, ++ {"ietf-inet-types", "2013-07-15", "ip-prefix", ip_prefix_store_clb, NULL}, ++ {"ietf-inet-types", "2013-07-15", "ipv4-prefix", ipv4_prefix_store_clb, NULL}, ++ {"ietf-inet-types", "2013-07-15", "ipv6-prefix", ipv6_prefix_store_clb, NULL}, ++ {NULL, NULL, NULL, NULL, NULL} /* terminating item */ ++}; +Index: libyang-0.16-r3/src/user_types/user_ipv4.c +=================================================================== +--- libyang-0.16-r3.orig/src/user_types/user_ipv4.c ++++ /dev/null +@@ -1,42 +0,0 @@ +-/** +- * @file user_ipv4.c +- * @author Michal Vasko +- * @brief Example implementation of an ipv4-address as a user type +- * +- * Copyright (c) 2018 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 +- */ +- +-#include +-#include +-#include +-#include +- +-#include "../user_types.h" +- +-static int +-ipv4_store_clb(const char *type_name, const char *value_str, lyd_val *value, char **err_msg) +-{ +- value->ptr = malloc(sizeof(struct in_addr)); +- if (!value->ptr) { +- return 1; +- } +- +- if (inet_pton(AF_INET, value_str, value->ptr) != 1) { +- free(value->ptr); +- return 1; +- } +- return 0; +-} +- +-/* Name of this array must match the file name! */ +-struct lytype_plugin_list user_ipv4[] = { +- {"ietf-inet-types", "2013-07-15", "ipv4-address", ipv4_store_clb, free}, +- {"ietf-inet-types", "2013-07-15", "ipv4-address-no-zone", ipv4_store_clb, free}, +- {NULL, NULL, NULL, NULL, NULL} /* terminating item */ +-}; +Index: libyang-0.16-r3/src/user_types/user_yang_types.c +=================================================================== +--- /dev/null ++++ libyang-0.16-r3/src/user_types/user_yang_types.c +@@ -0,0 +1,303 @@ ++/** ++ * @file user_yang_types.c ++ * @author Michal Vasko ++ * @brief ietf-yang-types typedef validation and conversion to canonical format ++ * ++ * Copyright (c) 2018 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 "../user_types.h" ++ ++#ifdef __GNUC__ ++# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) ++#else ++# define UNUSED(x) UNUSED_ ## x ++#endif ++ ++static const char *gmt_offsets[] = { ++ "+00:00", ++ "+00:20", ++ "+00:30", ++ "+01:00", ++ "+01:24", ++ "+01:30", ++ "+02:00", ++ "+02:30", ++ "+03:00", ++ "+03:30", ++ "+04:00", ++ "+04:30", ++ "+04:51", ++ "+05:00", ++ "+05:30", ++ "+05:40", ++ "+05:45", ++ "+06:00", ++ "+06:30", ++ "+07:00", ++ "+07:20", ++ "+07:30", ++ "+08:00", ++ "+08:30", ++ "+08:45", ++ "+09:00", ++ "+09:30", ++ "+09:45", ++ "+10:00", ++ "+10:30", ++ "+11:00", ++ "+11:30", ++ "+12:00", ++ "+12:45", ++ "+13:00", ++ "+13:45", ++ "+14:00", ++ "-00:00", ++ "-00:44", ++ "-01:00", ++ "-02:00", ++ "-02:30", ++ "-03:00", ++ "-03:30", ++ "-04:00", ++ "-04:30", ++ "-05:00", ++ "-06:00", ++ "-07:00", ++ "-08:00", ++ "-08:30", ++ "-09:00", ++ "-09:30", ++ "-10:00", ++ "-10:30", ++ "-11:00", ++ "-12:00", ++}; ++ ++static int ++date_and_time_store_clb(struct ly_ctx *UNUSED(ctx), const char *UNUSED(type_name), const char **value_str, ++ lyd_val *UNUSED(value), char **err_msg) ++{ ++ struct tm tm, tm2; ++ uint32_t i, j, k; ++ const char *val_str = *value_str; ++ int ret; ++ ++ /* \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2}) ++ * 2018-03-21T09:11:05(.55785...)(Z|+02:00) */ ++ memset(&tm, 0, sizeof tm); ++ i = 0; ++ ++ /* year */ ++ tm.tm_year = atoi(val_str + i); ++ /* if there was some invalid number, it will either be discovered in the loop below or by mktime() */ ++ tm.tm_year -= 1900; ++ for (j = i + 4; i < j; ++i) { ++ if (!isdigit(val_str[i])) { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ } ++ if (val_str[i] != '-') { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", '-' expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ ++i; ++ ++ /* month */ ++ tm.tm_mon = atoi(val_str + i); ++ tm.tm_mon -= 1; ++ for (j = i + 2; i < j; ++i) { ++ if (!isdigit(val_str[i])) { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ } ++ if (val_str[i] != '-') { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", '-' expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ ++i; ++ ++ /* day */ ++ tm.tm_mday = atoi(val_str + i); ++ for (j = i + 2; i < j; ++i) { ++ if (!isdigit(val_str[i])) { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ } ++ if (val_str[i] != 'T') { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", 'T' expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ ++i; ++ ++ /* hours */ ++ tm.tm_hour = atoi(val_str + i); ++ for (j = i + 2; i < j; ++i) { ++ if (!isdigit(val_str[i])) { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ } ++ if (val_str[i] != ':') { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", ':' expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ ++i; ++ ++ /* minutes */ ++ tm.tm_min = atoi(val_str + i); ++ for (j = i + 2; i < j; ++i) { ++ if (!isdigit(val_str[i])) { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ } ++ if (val_str[i] != ':') { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", ':' expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ ++i; ++ ++ /* seconds */ ++ tm.tm_sec = atoi(val_str + i); ++ for (j = i + 2; i < j; ++i) { ++ if (!isdigit(val_str[i])) { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ } ++ if ((val_str[i] != '.') && (val_str[i] != 'Z') && (val_str[i] != '+') && (val_str[i] != '-')) { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", '.', 'Z', '+', or '-' expected.", ++ val_str[i], i, val_str); ++ goto error; ++ } ++ ++ /* validate using mktime() */ ++ tm2 = tm; ++ if (mktime(&tm) == -1) { ++ ret = asprintf(err_msg, "Checking date-and-time value \"%s\" failed (%s).", val_str, strerror(errno)); ++ goto error; ++ } ++ /* we now have correctly filled the remaining values, use them */ ++ memcpy(((char *)&tm2) + (6 * sizeof(int)), ((char *)&tm) + (6 * sizeof(int)), sizeof(struct tm) - (6 * sizeof(int))); ++ /* back it up again */ ++ tm = tm2; ++ /* let mktime() correct date & time with having the other values correct now */ ++ if (mktime(&tm) == -1) { ++ ret = asprintf(err_msg, "Checking date-and-time value \"%s\" failed (%s).", val_str, strerror(errno)); ++ goto error; ++ } ++ /* detect changes in the filled values */ ++ if (memcmp(&tm, &tm2, 6 * sizeof(int))) { ++ ret = asprintf(err_msg, "Checking date-and-time value \"%s\" failed, canonical date and time is \"%04d-%02d-%02dT%02d:%02d:%02d\".", ++ val_str, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); ++ goto error; ++ } ++ ++ /* tenth of a second */ ++ if (val_str[i] == '.') { ++ ++i; ++ if (!isdigit(val_str[i])) { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ do { ++ ++i; ++ } while (isdigit(val_str[i])); ++ } ++ ++ switch (val_str[i]) { ++ case 'Z': ++ /* done */ ++ break; ++ case '+': ++ case '-': ++ /* timezone shift */ ++ k = sizeof gmt_offsets / sizeof *gmt_offsets; ++ for (j = 0; j < k ; ++j) { ++ if (!strncmp(val_str + i, gmt_offsets[j], 6)) { ++ break; ++ } ++ } ++ if (j == k) { ++ ret = asprintf(err_msg, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".", val_str + i, val_str); ++ goto error; ++ } ++ i += 5; ++ break; ++ default: ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", 'Z', '+', or '-' expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ ++ /* no other characters expected */ ++ ++i; ++ if (val_str[i]) { ++ ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", no characters expected.", val_str[i], i, val_str); ++ goto error; ++ } ++ ++ /* validation succeeded and we do not want to change how it is stored */ ++ return 0; ++ ++error: ++ if (ret == -1) { ++ err_msg = NULL; ++ } ++ return 1; ++} ++ ++static int ++hex_string_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const char **value_str, lyd_val *value, char **err_msg) ++{ ++ char *str; ++ uint32_t i, len; ++ ++ str = strdup(*value_str); ++ if (!str) { ++ /* we can hardly allocate an error message */ ++ *err_msg = NULL; ++ return 1; ++ } ++ ++ len = strlen(str); ++ for (i = 0; i < len; ++i) { ++ if ((str[i] >= 'A') && (str[i] <= 'Z')) { ++ /* make it lowercase (canonical format) */ ++ str[i] += 32; ++ } ++ } ++ ++ /* update the value correctly */ ++ lydict_remove(ctx, *value_str); ++ *value_str = lydict_insert_zc(ctx, str); ++ value->string = *value_str; ++ return 0; ++} ++ ++/* Name of this array must match the file name! */ ++struct lytype_plugin_list user_yang_types[] = { ++ {"ietf-yang-types", "2013-07-15", "date-and-time", date_and_time_store_clb, NULL}, ++ {"ietf-yang-types", "2013-07-15", "phys-address", hex_string_store_clb, NULL}, ++ {"ietf-yang-types", "2013-07-15", "mac-address", hex_string_store_clb, NULL}, ++ {"ietf-yang-types", "2013-07-15", "hex-string", hex_string_store_clb, NULL}, ++ {"ietf-yang-types", "2013-07-15", "uuid", hex_string_store_clb, NULL}, ++ {NULL, NULL, NULL, NULL, NULL} /* terminating item */ ++}; +Index: libyang-0.16-r3/tests/CMakeLists.txt +=================================================================== +--- libyang-0.16-r3.orig/tests/CMakeLists.txt ++++ libyang-0.16-r3/tests/CMakeLists.txt +@@ -7,7 +7,7 @@ set(CMAKE_MACOSX_RPATH TRUE) + get_filename_component(TESTS_DIR "${CMAKE_SOURCE_DIR}/tests" REALPATH) + + set(api_tests test_libyang test_tree_schema test_xml test_dict test_tree_data test_tree_data_dup test_tree_data_merge test_xpath test_xpath_1.1 test_diff) +-set(data_tests test_data_initialization test_leafref_remove test_instid_remove test_keys test_autodel test_when test_when_1.1 test_must_1.1 test_defaults test_emptycont test_unique test_mandatory test_json test_parse_print test_values test_metadata test_yangtypes_xpath test_yang_data test_unknown_element) ++set(data_tests test_data_initialization test_leafref_remove test_instid_remove test_keys test_autodel test_when test_when_1.1 test_must_1.1 test_defaults test_emptycont test_unique test_mandatory test_json test_parse_print test_values test_metadata test_yangtypes_xpath test_yang_data test_unknown_element test_user_types) + set(schema_yin_tests test_print_transform) + set(schema_tests test_ietf test_augment test_deviation test_refine test_typedef test_import test_include test_feature test_conformance test_leaflist test_status test_printer test_invalid) + if(CMAKE_BUILD_TYPE MATCHES debug) +Index: libyang-0.16-r3/tests/api/test_libyang.c +=================================================================== +--- libyang-0.16-r3.orig/tests/api/test_libyang.c ++++ libyang-0.16-r3/tests/api/test_libyang.c +@@ -1245,7 +1245,13 @@ test_ly_get_loaded_plugins(void **state) + } + assert_non_null(plugins[i]); + for (i = 0; plugins[i]; ++i) { +- if (!strcmp(plugins[i], "user_date_and_time")) { ++ if (!strcmp(plugins[i], "user_yang_types")) { ++ break; ++ } ++ } ++ assert_non_null(plugins[i]); ++ for (i = 0; plugins[i]; ++i) { ++ if (!strcmp(plugins[i], "user_inet_types")) { + break; + } + } +Index: libyang-0.16-r3/tests/data/files/user-types.yang +=================================================================== +--- /dev/null ++++ libyang-0.16-r3/tests/data/files/user-types.yang +@@ -0,0 +1,61 @@ ++module user-types { ++ namespace "urn:user-types"; ++ prefix ut; ++ ++ import ietf-yang-types { ++ prefix yang; ++ } ++ ++ import ietf-inet-types { ++ prefix inet; ++ } ++ ++ ++ leaf yang1 { ++ type yang:date-and-time; ++ } ++ ++ leaf yang2 { ++ type yang:phys-address; ++ } ++ ++ leaf yang3 { ++ type yang:mac-address; ++ } ++ ++ leaf yang4 { ++ type yang:hex-string; ++ } ++ ++ leaf yang5 { ++ type yang:uuid; ++ } ++ ++ leaf inet1 { ++ type inet:ip-address; ++ } ++ ++ leaf inet2 { ++ type inet:ipv6-address; ++ } ++ ++ leaf inet3 { ++ type inet:ip-address-no-zone; ++ } ++ ++ leaf inet4 { ++ type inet:ipv6-address-no-zone; ++ } ++ ++ leaf inet5 { ++ type inet:ip-prefix; ++ } ++ ++ leaf inet6 { ++ type inet:ipv4-prefix; ++ } ++ ++ leaf inet7 { ++ type inet:ipv6-prefix; ++ } ++} +Index: libyang-0.16-r3/tests/data/test_user_types.c +=================================================================== +--- /dev/null ++++ libyang-0.16-r3/tests/data/test_user_types.c +@@ -0,0 +1,226 @@ ++/** ++ * @file test_user_types.c ++ * @author Michal Vasko ++ * @brief Cmocka tests for libyang internal user types. ++ * ++ * Copyright (c) 2018 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "tests/config.h" ++#include "libyang.h" ++ ++struct state { ++ struct ly_ctx *ctx; ++ const struct lys_module *mod; ++ struct lyd_node *dt; ++}; ++ ++static int ++setup_f(void **state) ++{ ++ struct state *st; ++ ++ (*state) = st = calloc(1, sizeof *st); ++ if (!st) { ++ fprintf(stderr, "Memory allocation error"); ++ return -1; ++ } ++ ++ /* libyang context */ ++ st->ctx = ly_ctx_new(TESTS_DIR"/data/files", 0); ++ if (!st->ctx) { ++ fprintf(stderr, "Failed to create context.\n"); ++ goto error; ++ } ++ ++ st->mod = ly_ctx_load_module(st->ctx, "user-types", NULL); ++ if (!st->mod) { ++ fprintf(stderr, "Failed to load schema.\n"); ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ ly_ctx_destroy(st->ctx, NULL); ++ free(st); ++ (*state) = NULL; ++ ++ return -1; ++} ++ ++static int ++teardown_f(void **state) ++{ ++ struct state *st = (*state); ++ ++ lyd_free_withsiblings(st->dt); ++ ly_ctx_destroy(st->ctx, NULL); ++ free(st); ++ (*state) = NULL; ++ ++ return 0; ++} ++ ++static void ++test_yang_types(void **state) ++{ ++ struct state *st = (struct state *)*state; ++ ++ /* date-and-time */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "yang1", "2005-05-25T23:15:15.88888Z"); ++ assert_non_null(st->dt); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "yang1", "2005-05-31T23:15:15-08:00"); ++ assert_non_null(st->dt); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "yang1", "2005-05-31T23:15:15.-08:00"); ++ assert_null(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "yang1", "2005-02-29T23:15:15-08:00"); ++ assert_null(st->dt); ++ ++ /* phys-address */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "yang2", "aa:bb:cc:dd"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "aa:bb:cc:dd"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "yang2", "AA:BB:1D:2F:CA:52"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "aa:bb:1d:2f:ca:52"); ++ lyd_free_withsiblings(st->dt); ++ ++ /* mac-address */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "yang3", "12:34:56:78:9A:BC"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "12:34:56:78:9a:bc"); ++ lyd_free_withsiblings(st->dt); ++ ++ /* hex-string */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "yang4", "AB:CD:eF:fE:dc:Ba:Ab"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ab:cd:ef:fe:dc:ba:ab"); ++ lyd_free_withsiblings(st->dt); ++ ++ /* uuid */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "yang5", "12AbCDef-3456-58cd-9ABC-8796cdACdfEE"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "12abcdef-3456-58cd-9abc-8796cdacdfee"); ++} ++ ++static void ++test_inet_types(void **state) ++{ ++ struct state *st = (struct state *)*state; ++ ++ /* ip-address */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet1", "192.168.0.1"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "192.168.0.1"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet1", "192.168.0.1%12"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "192.168.0.1%12"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet1", "2008:15:0:0:0:0:feAC:1"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "2008:15::feac:1"); ++ lyd_free_withsiblings(st->dt); ++ ++ /* ipv6-address */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet2", "FAAC:21:011:Da85::87:daaF%1"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "faac:21:11:da85::87:daaf%1"); ++ lyd_free_withsiblings(st->dt); ++ ++ /* ip-address-no-zone */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet3", "127.0.0.1"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "127.0.0.1"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet3", "0:00:000:0000:000:00:0:1"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "::1"); ++ lyd_free_withsiblings(st->dt); ++ ++ /* ipv6-address-no-zone */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet4", "A:B:c:D:e:f:1:0"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "a:b:c:d:e:f:1:0"); ++ lyd_free_withsiblings(st->dt); ++ ++ /* ip-prefix */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet5", "12.1.58.4/1"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "1.0.0.0/1"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet5", "12.1.58.4/24"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "255.255.255.0/24"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet5", "2000:A:B:C:D:E:f:a/16"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff::/16"); ++ lyd_free_withsiblings(st->dt); ++ ++ /* ipv4-prefix */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet6", "0.1.58.4/32"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "255.255.255.255/32"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet6", "12.1.58.4/8"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "255.0.0.0/8"); ++ lyd_free_withsiblings(st->dt); ++ ++ /* ipv6-prefix */ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet7", "::C:D:E:f:a/112"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0/112"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet7", "::C:D:E:f:a/110"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff:ffff:ffff:ffff:ffff:ffff:3fff:0/110"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet7", "::C:D:E:f:a/96"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff:ffff:ffff:ffff:ffff:ffff::/96"); ++ lyd_free_withsiblings(st->dt); ++ ++ st->dt = lyd_new_leaf(NULL, st->mod, "inet7", "::C:D:E:f:a/55"); ++ assert_non_null(st->dt); ++ assert_string_equal(((struct lyd_node_leaf_list *)st->dt)->value_str, "ffff:ffff:ffff:7f::/55"); ++} ++ ++int main(void) ++{ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test_setup_teardown(test_yang_types, setup_f, teardown_f), ++ cmocka_unit_test_setup_teardown(test_inet_types, setup_f, teardown_f), ++ }; ++ ++ return cmocka_run_group_tests(tests, NULL, NULL); ++}