From 059fa0c1c00cb0a242d20cca873b6c81267f7699 Mon Sep 17 00:00:00 2001 From: Roman Janota Date: Wed, 11 Nov 2026 11:10:56 +0100 Subject: [PATCH 1/6] parser data REFACTOR yang-push envelope flag name The message parsers also read the NETCONF and RESTCONF envelopes, so name the internal flag after the YANG-Push envelope it stands for. --- src/parser_cbor.c | 2 +- src/parser_common.c | 4 ++-- src/parser_internal.h | 2 +- src/parser_json.c | 2 +- src/parser_xml.c | 2 +- src/tree_data.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/parser_cbor.c b/src/parser_cbor.c index 44eafa6b2..3d9114a10 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -1213,7 +1213,7 @@ lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st if (lydctx->parse_opts & LYD_PARSE_ANYDATA_STRICT) { /* explicit strict data parsing */ lydctx->parse_opts |= LYD_PARSE_STRICT; - } else if ((lydctx->int_opts & LYD_INTOPT_ENVELOPE) && !(lydctx->int_opts & LYD_INTOPT_ANY)) { + } else if ((lydctx->int_opts & LYD_INTOPT_YP_ENVELOPE) && !(lydctx->int_opts & LYD_INTOPT_ANY)) { /* the notification in the envelope contents, parsed as data like any other notification */ } else { /* make the parser process data as opaq nodes */ diff --git a/src/parser_common.c b/src/parser_common.c index c341be5db..128e10a76 100644 --- a/src/parser_common.c +++ b/src/parser_common.c @@ -140,7 +140,7 @@ lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode) LY_ERR rc = LY_SUCCESS; if (lydctx->int_opts & LYD_INTOPT_ANY) { - if ((lydctx->int_opts & LYD_INTOPT_ENVELOPE) && (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))) { + if ((lydctx->int_opts & LYD_INTOPT_YP_ENVELOPE) && (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))) { /* only one notification and no RPC/action in the envelope */ if (snode->nodetype != LYS_NOTIF) { goto error_node_inval; @@ -154,7 +154,7 @@ lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode) goto cleanup; } - if ((lydctx->int_opts & LYD_INTOPT_ENVELOPE) && !snode->parent && (strcmp(snode->name, "envelope") || + if ((lydctx->int_opts & LYD_INTOPT_YP_ENVELOPE) && !snode->parent && (strcmp(snode->name, "envelope") || strcmp(snode->module->name, "ietf-yp-notification"))) { /* the root must be the envelope */ goto error_node_inval; diff --git a/src/parser_internal.h b/src/parser_internal.h index afa62980a..9fb521189 100644 --- a/src/parser_internal.h +++ b/src/parser_internal.h @@ -66,7 +66,7 @@ typedef void (*lyd_ctx_free_clb)(struct lyd_ctx *ctx); #define LYD_INTOPT_SKIP_SIBLINGS 0x40 /**< Perform the validation task only for the specfic subtree, skip other siblings. */ #define LYD_INTOPT_NO_SIBLINGS 0x80 /**< If there are any siblings, return an error. */ #define LYD_INTOPT_EVENTTIME 0x0100 /**< Parse notification eventTime node. */ -#define LYD_INTOPT_ENVELOPE 0x0200 /**< YANG-Push notification envelope is being parsed. */ +#define LYD_INTOPT_YP_ENVELOPE 0x0200 /**< YANG-Push notification envelope is being parsed. */ /** * @brief Internal (common) context for YANG data parsers. diff --git a/src/parser_json.c b/src/parser_json.c index 7d15abebb..0fd4bd499 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -1382,7 +1382,7 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, co if (lydctx->parse_opts & LYD_PARSE_ANYDATA_STRICT) { /* explicit strict data parsing */ lydctx->parse_opts |= LYD_PARSE_STRICT; - } else if ((lydctx->int_opts & LYD_INTOPT_ENVELOPE) && !(lydctx->int_opts & LYD_INTOPT_ANY)) { + } else if ((lydctx->int_opts & LYD_INTOPT_YP_ENVELOPE) && !(lydctx->int_opts & LYD_INTOPT_ANY)) { /* the notification in the envelope contents, parsed as data like any other notification */ } else { /* make the parser process data as opaq nodes */ diff --git a/src/parser_xml.c b/src/parser_xml.c index f27d4fdcf..26a8c3bcc 100644 --- a/src/parser_xml.c +++ b/src/parser_xml.c @@ -970,7 +970,7 @@ lydxml_subtree_any(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode, co if (lydctx->parse_opts & LYD_PARSE_ANYDATA_STRICT) { /* explicit strict data parsing */ lydctx->parse_opts |= LYD_PARSE_STRICT; - } else if ((lydctx->int_opts & LYD_INTOPT_ENVELOPE) && !(lydctx->int_opts & LYD_INTOPT_ANY)) { + } else if ((lydctx->int_opts & LYD_INTOPT_YP_ENVELOPE) && !(lydctx->int_opts & LYD_INTOPT_ANY)) { /* the notification in the envelope contents, parsed as data like any other notification */ } else { /* make the parser process data as opaq nodes */ diff --git a/src/tree_data.c b/src/tree_data.c index c1e813869..0334747ee 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -449,7 +449,7 @@ lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in LY_EINVAL); LY_CHECK_ERR_RET(!ly_ctx_get_module_implemented(ctx, "ietf-yp-notification"), LOGERR(ctx, LY_EINVAL, "Module \"ietf-yp-notification\" is not implemented."), LY_EINVAL); - int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_ENVELOPE; + int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_YP_ENVELOPE; break; default: LOGINT(ctx); From 5b6d92942f771f4c47bd4fc70d2c20121f4c740a Mon Sep 17 00:00:00 2001 From: Roman Janota Date: Tue, 10 Nov 2026 15:03:42 +0100 Subject: [PATCH 2/6] tree data FEATURE notification envelope detection A UDP-Notif collector learns only the encoding of a notification, not its envelope, and had to sniff the root element itself to choose the data type. LYD_TYPE_NOTIF_AUTO parses a notification in the YANG-Push envelope or in the NETCONF/RESTCONF one. The message parsers decide by the root element they read first anyway, so the input is not tokenized and CBOR not decoded twice. The 'ietf-yp-notification' module is required only once its envelope is found. LYD_TYPE_NOTIF_ENVELOPE goes through the same message parsers with the detection skipped, so both types share one envelope path. --- src/parser_cbor.c | 94 ++++++++++++++++++++++++++++++++++--------- src/parser_common.c | 11 +++++ src/parser_data.h | 16 +++++++- src/parser_internal.h | 17 ++++++-- src/parser_json.c | 74 ++++++++++++++++++++++++++++++---- src/parser_xml.c | 57 +++++++++++++++++++++----- src/tree_data.c | 88 ++++++++++++++++++---------------------- 7 files changed, 269 insertions(+), 88 deletions(-) diff --git a/src/parser_cbor.c b/src/parser_cbor.c index 3d9114a10..53339401c 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -1752,29 +1752,28 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st return rc; } -LY_ERR -lyd_parse_cbor_restconf(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, - struct lyd_node **first_p, struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p) +/** + * @brief Parse the RESTCONF notification envelope with eventTime. + * + * @param[in] ctx libyang context. + * @param[in] root Root CBOR map of the input. + * @param[out] envp Parsed envelope with eventTime, may be returned possibly even on an error. + * @param[out] notif CBOR map with the notification. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_env_restconf_notif(const struct ly_ctx *ctx, const cbor_item_t *root, struct lyd_node **envp, + const cbor_item_t **notif) { - LY_ERR r, rc = LY_SUCCESS; - struct lyd_cbor_ctx *lydctx = NULL; - const cbor_item_t *root, *notif, *event_time = NULL; + LY_ERR rc = LY_SUCCESS; + const cbor_item_t *event_time = NULL; struct cbor_pair *pairs; struct lyd_node *node; char *str = NULL; size_t str_len; uint32_t i; - assert(ctx && in && first_p && envp && lydctx_p); - - *envp = NULL; - - rc = lyd_parse_cbor_init(ctx, in, parse_opts, val_opts, &lydctx); - LY_CHECK_GOTO(rc, cleanup); - lydctx->int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME; - /* parse "notification" */ - root = lydctx->cborctx->cbor_data; pairs = cbor_map_handle(root); if ((cbor_map_size(root) != 1) || !cbor_isa_string(pairs[0].key)) { LOGVAL(ctx, NULL, LYVE_DATA, "Expected a single CBOR map member \"ietf-restconf:notification\"."); @@ -1793,15 +1792,15 @@ lyd_parse_cbor_restconf(const struct ly_ctx *ctx, struct ly_in *in, uint32_t par rc = LY_EVALID; goto cleanup; } - notif = pairs[0].value; + *notif = pairs[0].value; rc = lyd_create_opaq(ctx, "notification", 12, "ietf-restconf", 13, "ietf-restconf", 13, NULL, 0, NULL, LY_VALUE_CBOR, NULL, LYD_VALHINT_STRING, envp); LY_CHECK_GOTO(rc, cleanup); /* find eventTime, it can be anywhere in the map */ - pairs = cbor_map_handle(notif); - for (i = 0; i < cbor_map_size(notif); ++i) { + pairs = cbor_map_handle(*notif); + for (i = 0; i < cbor_map_size(*notif); ++i) { if (!cbor_isa_string(pairs[i].key)) { continue; } @@ -1839,10 +1838,69 @@ lyd_parse_cbor_restconf(const struct ly_ctx *ctx, struct ly_in *in, uint32_t par lyd_insert_child(*envp, node); LY_CHECK_GOTO(rc = lyd_parser_notif_eventtime_validate(node), cleanup); +cleanup: + free(str); + return rc; +} + +LY_ERR +lyd_parse_cbor_restconf(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, + enum lyd_type data_type, struct lyd_node **first_p, struct lyd_node **envp, struct ly_set *parsed, + struct lyd_ctx **lydctx_p) +{ + LY_ERR r, rc = LY_SUCCESS; + struct lyd_cbor_ctx *lydctx = NULL; + const cbor_item_t *root, *notif; + struct cbor_pair *pairs; + char *str = NULL; + size_t str_len; + ly_bool yp_envelope = 0; + + assert(ctx && in && first_p && envp && lydctx_p); + assert((data_type == LYD_TYPE_NOTIF_RESTCONF) || (data_type == LYD_TYPE_NOTIF_ENVELOPE) || + (data_type == LYD_TYPE_NOTIF_AUTO)); + + rc = lyd_parse_cbor_init(ctx, in, parse_opts, val_opts, &lydctx); + LY_CHECK_GOTO(rc, cleanup); + root = lydctx->cborctx->cbor_data; + + if (data_type == LYD_TYPE_NOTIF_ENVELOPE) { + yp_envelope = 1; + } else if (data_type == LYD_TYPE_NOTIF_AUTO) { + /* peek at the name of the first member */ + pairs = cbor_map_handle(root); + if (cbor_map_size(root) && pairs && cbor_isa_string(pairs[0].key)) { + LY_CHECK_GOTO(rc = lydcbor_item_to_string(ctx, pairs[0].key, &str, &str_len), cleanup); + yp_envelope = !ly_strncmp("ietf-yp-notification:envelope", str, str_len); + if (!yp_envelope && ly_strncmp("ietf-restconf:notification", str, str_len)) { + LOGVAL(ctx, NULL, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"ietf-restconf:notification\" " + "or \"ietf-yp-notification:envelope\".", (int)str_len, str); + rc = LY_EVALID; + goto cleanup; + } + } + } + + if (yp_envelope) { + /* YANG-Push notification envelope */ + LY_CHECK_GOTO(rc = lyd_parser_yp_envelope_check(ctx), cleanup); + lydctx->int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_YP_ENVELOPE; + notif = root; + } else { + /* RESTCONF notification envelope */ + lydctx->int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME; + LY_CHECK_GOTO(rc = lydcbor_env_restconf_notif(ctx, root, envp, ¬if), cleanup); + } + /* parse the notification */ r = lydcbor_subtree_r(lydctx, NULL, first_p, parsed, notif); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + if ((lydctx->int_opts & LYD_INTOPT_NO_SIBLINGS) && *first_p && (*first_p)->next) { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Unexpected sibling node."); + rc = LY_EVALID; + goto cleanup; + } if (!lydctx->op_node) { LOGVAL(ctx, NULL, LYVE_DATA, "Missing the operation node."); r = LY_EVALID; diff --git a/src/parser_common.c b/src/parser_common.c index 128e10a76..bc3ee1c77 100644 --- a/src/parser_common.c +++ b/src/parser_common.c @@ -86,6 +86,17 @@ lyd_parser_notif_eventtime_validate(const struct lyd_node *node) return LY_SUCCESS; } +LY_ERR +lyd_parser_yp_envelope_check(const struct ly_ctx *ctx) +{ + if (!ly_ctx_get_module_implemented(ctx, "ietf-yp-notification")) { + LOGERR(ctx, LY_EINVAL, "Module \"ietf-yp-notification\" is not implemented."); + return LY_EINVAL; + } + + return LY_SUCCESS; +} + LY_ERR lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op) { diff --git a/src/parser_data.h b/src/parser_data.h index ae0e8e87c..255f0a2e9 100644 --- a/src/parser_data.h +++ b/src/parser_data.h @@ -373,9 +373,12 @@ enum lyd_type { LYD_TYPE_REPLY_RESTCONF, /**< message-body of a RESTCONF operation output parameters ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.2)) */ - LYD_TYPE_NOTIF_ENVELOPE /**< YANG-Push notification envelope, the 'envelope' structure of the + LYD_TYPE_NOTIF_ENVELOPE, /**< YANG-Push notification envelope, the 'envelope' structure of the 'ietf-yp-notification' module ([ref](https://datatracker.ietf.org/doc/draft-ietf-netconf-notif-envelope/)) */ + LYD_TYPE_NOTIF_AUTO /**< notification in the YANG-Push envelope (::LYD_TYPE_NOTIF_ENVELOPE) or in the + NETCONF/RESTCONF one (::LYD_TYPE_NOTIF_NETCONF, ::LYD_TYPE_NOTIF_RESTCONF), + detected from the input */ }; /** @} datatype */ @@ -440,6 +443,14 @@ enum lyd_type { * - @p op - must be provided, the notification data tree separated from the envelope 'contents' will be returned * here, pointing to the operation; * + * - ::LYD_TYPE_NOTIF_AUTO: + * - @p ctx - must have the 'ietf-yp-notification' module implemented to parse the YANG-Push envelope; + * - @p parent - must be NULL, the whole notification is expected; + * - @p format - can be ::LYD_XML, ::LYD_JSON, or ::LYD_CBOR; + * - @p tree - must be provided, see ::LYD_TYPE_NOTIF_ENVELOPE, ::LYD_TYPE_NOTIF_NETCONF, and + * ::LYD_TYPE_NOTIF_RESTCONF, only the YANG-Push envelope has a schema; + * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation; + * * - ::LYD_TYPE_RPC_YANG: * - ::LYD_TYPE_NOTIF_YANG: * - ::LYD_TYPE_REPLY_YANG: @@ -458,7 +469,8 @@ enum lyd_type { * @param[out] tree Optional full parsed data tree. If @p parent is set, set to NULL. * @param[out] op Optional pointer to the operation (action/RPC/notification) node. * @return LY_ERR value. - * @return LY_ENOT if @p data_type is a NETCONF message and the root XML element is not the expected one. + * @return LY_ENOT if @p data_type is a NETCONF message or ::LYD_TYPE_NOTIF_AUTO in XML and the root XML element is + * not the expected one. */ LIBYANG_API_DECL LY_ERR lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format, enum lyd_type data_type, uint32_t parse_options, struct lyd_node **tree, struct lyd_node **op); diff --git a/src/parser_internal.h b/src/parser_internal.h index 9fb521189..e6a81e4a3 100644 --- a/src/parser_internal.h +++ b/src/parser_internal.h @@ -288,7 +288,7 @@ LY_ERR lyd_parse_xml(const struct ly_ctx *ctx, struct lyd_node *parent, struct l * @param[in] in Input structure. * @param[in] parse_opts Options for parser, see @ref dataparseroptions. * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions. - * @param[in] data_type Expected NETCONF data type of the data. + * @param[in] data_type Expected NETCONF data type of the data, ::LYD_TYPE_NOTIF_ENVELOPE, or ::LYD_TYPE_NOTIF_AUTO. * @param[out] envp Individual parsed envelopes tree, may be returned possibly even on an error. * @param[out] parsed Set to add all the parsed siblings into. * @param[out] lydctx_p Data parser context to finish validation. @@ -326,7 +326,7 @@ LY_ERR lyd_parse_json(const struct ly_ctx *ctx, struct lyd_node *parent, const s * @param[in] in Input structure. * @param[in] parse_opts Options for parser, see @ref dataparseroptions. * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions. - * @param[in] data_type Expected RESTCONF data type of the data. + * @param[in] data_type Expected RESTCONF data type of the data, ::LYD_TYPE_NOTIF_ENVELOPE, or ::LYD_TYPE_NOTIF_AUTO. * @param[out] envp Individual parsed envelopes tree, may be returned possibly even on an error. * @param[out] parsed Set to add all the parsed siblings into. * @param[out] lydctx_p Data parser context to finish validation. @@ -383,6 +383,7 @@ LY_ERR lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance * * @param[in] in Input structure. * @param[in] parse_opts Options for parser, see @ref dataparseroptions. * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions. + * @param[in] data_type Expected RESTCONF data type of the data, ::LYD_TYPE_NOTIF_ENVELOPE, or ::LYD_TYPE_NOTIF_AUTO. * @param[in,out] first_p Pointer to the first top-level parsed node. * @param[out] envp Parsed envelope with eventTime, may be returned possibly even on an error. * @param[out] parsed Set to add all the parsed siblings into. @@ -390,7 +391,8 @@ LY_ERR lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance * * @return LY_ERR value. */ LY_ERR lyd_parse_cbor_restconf(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, - struct lyd_node **first_p, struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p); + enum lyd_type data_type, struct lyd_node **first_p, struct lyd_node **envp, struct ly_set *parsed, + struct lyd_ctx **lydctx_p); /** * @brief Validate eventTime date-and-time value. @@ -401,6 +403,15 @@ LY_ERR lyd_parse_cbor_restconf(const struct ly_ctx *ctx, struct ly_in *in, uint3 */ LY_ERR lyd_parser_notif_eventtime_validate(const struct lyd_node *node); +/** + * @brief Check that a YANG-Push notification envelope can be parsed. + * + * @param[in] ctx libyang context. + * @return LY_SUCCESS on success. + * @return LY_EINVAL if the 'ietf-yp-notification' module is not implemented. + */ +LY_ERR lyd_parser_yp_envelope_check(const struct ly_ctx *ctx); + /** * @brief Search all the parents for an operation node, check validity based on internal parser flags. * diff --git a/src/parser_json.c b/src/parser_json.c index 0fd4bd499..b6ab3266a 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -2173,6 +2173,42 @@ lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *modul return rc; } +/** + * @brief Learn which notification envelope is the first member of the current JSON object. + * + * @param[in] jsonctx JSON parser context, its state is kept. + * @param[out] yp_envelope Whether the member is the 'envelope' of 'ietf-yp-notification', otherwise the RESTCONF + * 'notification' is expected. + * @return LY_SUCCESS on success. + * @return LY_EVALID if the member is neither of the envelopes. + * @return LY_ERR value on other errors. + */ +static LY_ERR +lydjson_peek_notif_envelope(struct lyjson_ctx *jsonctx, ly_bool *yp_envelope) +{ + LY_ERR rc = LY_SUCCESS; + enum LYJSON_PARSER_STATUS status; + + *yp_envelope = 0; + + /* peek at the name of the first member */ + lyjson_ctx_backup(jsonctx); + LY_CHECK_GOTO(rc = lyjson_ctx_next(jsonctx, &status), cleanup); + if (status == LYJSON_OBJECT_NAME) { + *yp_envelope = !ly_strncmp("ietf-yp-notification:envelope", jsonctx->value, jsonctx->value_len); + if (!*yp_envelope && ly_strncmp("ietf-restconf:notification", jsonctx->value, jsonctx->value_len)) { + LOGVAL(jsonctx->ctx, NULL, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"ietf-restconf:notification\" " + "or \"ietf-yp-notification:envelope\".", (int)jsonctx->value_len, jsonctx->value); + rc = LY_EVALID; + goto cleanup; + } + } + +cleanup: + lyjson_ctx_restore(jsonctx); + return rc; +} + LY_ERR lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, struct lyd_node **envp, struct ly_set *parsed, @@ -2182,13 +2218,15 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struc struct lyd_json_ctx *lydctx = NULL; struct lyd_node *node; uint32_t i, int_opts = 0, close_elem = 0; + ly_bool yp_envelope = 0; assert(ctx && in && lydctx_p); assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK)); assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK)); assert((data_type == LYD_TYPE_RPC_RESTCONF) || (data_type == LYD_TYPE_NOTIF_RESTCONF) || - (data_type == LYD_TYPE_REPLY_RESTCONF)); + (data_type == LYD_TYPE_REPLY_RESTCONF) || (data_type == LYD_TYPE_NOTIF_ENVELOPE) || + (data_type == LYD_TYPE_NOTIF_AUTO)); /* init context */ rc = lyd_parse_json_init(ctx, NULL, in, parse_opts, val_opts, NULL, &lydctx); @@ -2206,15 +2244,28 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struc close_elem = 1; break; case LYD_TYPE_NOTIF_RESTCONF: + case LYD_TYPE_NOTIF_ENVELOPE: + case LYD_TYPE_NOTIF_AUTO: assert(!parent); - /* parse "notification" */ - rc = lydjson_envelope(lydctx->jsonctx, "notification", "ietf-restconf", envp); - LY_CHECK_GOTO(rc, cleanup); + if (data_type == LYD_TYPE_NOTIF_ENVELOPE) { + yp_envelope = 1; + } else if (data_type == LYD_TYPE_NOTIF_AUTO) { + LY_CHECK_GOTO(rc = lydjson_peek_notif_envelope(lydctx->jsonctx, &yp_envelope), cleanup); + } + if (yp_envelope) { + /* YANG-Push notification envelope */ + LY_CHECK_GOTO(rc = lyd_parser_yp_envelope_check(ctx), cleanup); + int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_YP_ENVELOPE; + } else { + /* parse "notification" */ + rc = lydjson_envelope(lydctx->jsonctx, "notification", "ietf-restconf", envp); + LY_CHECK_GOTO(rc, cleanup); - /* RESTCONF notification and eventTime */ - int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME; - close_elem = 1; + /* RESTCONF notification and eventTime */ + int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME; + close_elem = 1; + } break; case LYD_TYPE_REPLY_RESTCONF: assert(parent); @@ -2241,6 +2292,10 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struc do { r = lydjson_subtree_r(lydctx, parent, first_p, parsed); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) { + break; + } } while (lyjson_ctx_status(lydctx->jsonctx) == LYJSON_OBJECT_NEXT); /* close all opened elements */ @@ -2256,6 +2311,11 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struc LY_CHECK_ERR_GOTO(r, rc = r, cleanup); } + if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && (lyjson_ctx_status(lydctx->jsonctx) != LYJSON_OBJECT_CLOSED)) { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Unexpected sibling node."); + rc = LY_EVALID; + goto cleanup; + } if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) { LOGVAL(ctx, NULL, LYVE_DATA, "Missing the operation node."); r = LY_EVALID; diff --git a/src/parser_xml.c b/src/parser_xml.c index 26a8c3bcc..a2dd3e5a8 100644 --- a/src/parser_xml.c +++ b/src/parser_xml.c @@ -1226,6 +1226,26 @@ lydxml_envelope(struct lyxml_ctx *xmlctx, const char *name, const char *uri, ly_ return rc; } +/** + * @brief Learn whether the current XML element is the YANG-Push notification envelope. + * + * @param[in] xmlctx XML parser context. + * @return Whether the element is the 'envelope' of 'ietf-yp-notification'. + */ +static ly_bool +lydxml_is_yp_envelope(const struct lyxml_ctx *xmlctx) +{ + const struct lyxml_ns *ns; + + if (xmlctx->status != LYXML_ELEMENT) { + return 0; + } + + ns = lyxml_ns_get(&xmlctx->ns, xmlctx->prefix, xmlctx->prefix_len); + return ns && !strcmp(ns->uri, "urn:ietf:params:xml:ns:yang:ietf-yp-notification") && + !ly_strncmp("envelope", xmlctx->name, xmlctx->name_len); +} + LY_ERR lyd_parse_xml(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, struct lyd_ctx **lydctx_p) @@ -1466,7 +1486,7 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, struct lyd_node *parent, struct struct lyd_xml_ctx *lydctx; struct lyd_node *node; uint32_t i, int_opts = 0, close_elem = 0; - ly_bool parsed_data_nodes = 0; + ly_bool parsed_data_nodes = 0, yp_envelope = 0; assert(ctx && in && lydctx_p); assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK)); @@ -1490,18 +1510,37 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, struct lyd_node *parent, struct LY_CHECK_GOTO(rc, cleanup); break; case LYD_TYPE_NOTIF_NETCONF: + case LYD_TYPE_NOTIF_ENVELOPE: + case LYD_TYPE_NOTIF_AUTO: assert(!parent); - /* parse "notification" */ - rc = lydxml_envelope(lydctx->xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp); - if (rc == LY_ENOT) { - LOGVAL(ctx, NULL, LYVE_DATA, "Missing NETCONF envelope or in incorrect namespace."); + if (data_type == LYD_TYPE_NOTIF_ENVELOPE) { + yp_envelope = 1; + } else if (data_type == LYD_TYPE_NOTIF_AUTO) { + yp_envelope = lydxml_is_yp_envelope(lydctx->xmlctx); } - LY_CHECK_GOTO(rc, cleanup); + if (yp_envelope) { + /* YANG-Push notification envelope */ + LY_CHECK_GOTO(rc = lyd_parser_yp_envelope_check(ctx), cleanup); + int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_YP_ENVELOPE; + } else { + /* parse "notification" */ + rc = lydxml_envelope(lydctx->xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, + envp); + if (rc == LY_ENOT) { + if (data_type == LYD_TYPE_NOTIF_AUTO) { + LOGVAL(ctx, NULL, LYVE_DATA, "Missing NETCONF or YANG-Push , or in " + "incorrect namespace."); + } else { + LOGVAL(ctx, NULL, LYVE_DATA, "Missing NETCONF envelope or in incorrect namespace."); + } + } + LY_CHECK_GOTO(rc, cleanup); - /* NETCONF notification */ - int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME; - close_elem = 1; + /* NETCONF notification */ + int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME; + close_elem = 1; + } break; case LYD_TYPE_REPLY_NETCONF: assert(parent); diff --git a/src/tree_data.c b/src/tree_data.c index 0334747ee..51f027186 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -433,6 +433,12 @@ lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in LY_CHECK_ARG_RET(ctx, (format == LYD_JSON) || (format == LYD_CBOR), !parent, tree, op, LY_EINVAL); proto_msg = 1; break; + case LYD_TYPE_NOTIF_ENVELOPE: + case LYD_TYPE_NOTIF_AUTO: + LY_CHECK_ARG_RET(ctx, (format == LYD_XML) || (format == LYD_JSON) || (format == LYD_CBOR), !parent, tree, op, + LY_EINVAL); + proto_msg = 1; + break; /* set internal opts */ case LYD_TYPE_RPC_YANG: @@ -444,21 +450,14 @@ lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in case LYD_TYPE_REPLY_YANG: int_opts = LYD_INTOPT_REPLY | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS); break; - case LYD_TYPE_NOTIF_ENVELOPE: - LY_CHECK_ARG_RET(ctx, (format == LYD_XML) || (format == LYD_JSON) || (format == LYD_CBOR), !parent, tree, op, - LY_EINVAL); - LY_CHECK_ERR_RET(!ly_ctx_get_module_implemented(ctx, "ietf-yp-notification"), - LOGERR(ctx, LY_EINVAL, "Module \"ietf-yp-notification\" is not implemented."), LY_EINVAL); - int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_YP_ENVELOPE; - break; default: LOGINT(ctx); rc = LY_EINT; goto cleanup; } - /* parse a full protocol message */ if (proto_msg) { + /* parse a full protocol message */ if (format == LYD_XML) { /* parse the NETCONF (or RESTCONF XML) message */ rc = lyd_parse_xml_netconf(ctx, parent, &first, in, parse_options, val_opts, data_type, &envp, &parsed, &lydctx); @@ -468,7 +467,7 @@ lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in } else { #ifdef LY_HAVE_CBOR /* parse the RESTCONF CBOR notification */ - rc = lyd_parse_cbor_restconf(ctx, in, parse_options, val_opts, &first, &envp, &parsed, &lydctx); + rc = lyd_parse_cbor_restconf(ctx, in, parse_options, val_opts, data_type, &first, &envp, &parsed, &lydctx); #else LOGARG(ctx, format); rc = LY_EINVAL; @@ -481,56 +480,47 @@ lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in } goto cleanup; } - - /* set out params correctly */ - if (envp) { - /* special out param meaning */ - *tree = envp; - } else { - *tree = parent ? NULL : first; - } - if (op) { - *op = lydctx->op_node; - } - goto cleanup; - } - - /* parse the data */ - switch (format) { - case LYD_XML: - rc = lyd_parse_xml(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); - break; - case LYD_JSON: - rc = lyd_parse_json(ctx, parent, NULL, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); - break; - case LYD_LYB: - rc = lyd_parse_lyb(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); - break; + } else { + /* parse the data */ + switch (format) { + case LYD_XML: + rc = lyd_parse_xml(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); + break; + case LYD_JSON: + rc = lyd_parse_json(ctx, parent, NULL, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); + break; + case LYD_LYB: + rc = lyd_parse_lyb(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); + break; #ifdef LY_HAVE_CBOR - case LYD_CBOR: - rc = lyd_parse_cbor(ctx, NULL, parent, &first, in, parse_options, val_opts, int_opts, &parsed, NULL, &lydctx); - break; + case LYD_CBOR: + rc = lyd_parse_cbor(ctx, NULL, parent, &first, in, parse_options, val_opts, int_opts, &parsed, NULL, &lydctx); + break; #else - case LYD_CBOR: - LOGARG(ctx, format); - rc = LY_EINVAL; - break; + case LYD_CBOR: + LOGARG(ctx, format); + rc = LY_EINVAL; + break; #endif /* LY_HAVE_CBOR */ - case LYD_UNKNOWN: - LOGARG(ctx, format); - rc = LY_EINVAL; - break; + case LYD_UNKNOWN: + LOGARG(ctx, format); + rc = LY_EINVAL; + break; + } + LY_CHECK_GOTO(rc, cleanup); } - LY_CHECK_GOTO(rc, cleanup); - if (data_type == LYD_TYPE_NOTIF_ENVELOPE) { - /* unlink the notification from the envelope contents */ + if (lydctx->int_opts & LYD_INTOPT_YP_ENVELOPE) { + /* unlink the notification from the YANG-Push envelope contents */ for (top = lydctx->op_node; top->parent->parent != first; top = lyd_parent(top)) {} lyd_unlink(top); } /* set out params correctly */ - if (tree) { + if (envp) { + /* special out param meaning */ + *tree = envp; + } else if (tree) { *tree = parent ? NULL : first; } if (op) { From df78bacbe61d316f6085716382a78714222a097a Mon Sep 17 00:00:00 2001 From: Roman Janota Date: Tue, 10 Nov 2026 15:03:42 +0100 Subject: [PATCH 3/6] tests FEATURE notification envelope detection --- tests/utests/data/test_notif_envelope.c | 174 ++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/tests/utests/data/test_notif_envelope.c b/tests/utests/data/test_notif_envelope.c index 059fbf683..a3044df84 100644 --- a/tests/utests/data/test_notif_envelope.c +++ b/tests/utests/data/test_notif_envelope.c @@ -51,6 +51,59 @@ setup(void **state) ly_in_free(_in, 0); \ } +#define CHECK_PARSE_AUTO_ERR(DATA, FORMAT, RC) \ + { \ + struct ly_in *_in; \ + struct lyd_node *_tree, *_op; \ + assert_int_equal(LY_SUCCESS, ly_in_new_memory_chunk(DATA, strlen(DATA), &_in)); \ + assert_int_equal(RC, lyd_parse_op(UTEST_LYCTX, NULL, _in, FORMAT, LYD_TYPE_NOTIF_AUTO, 0, &_tree, &_op)); \ + assert_null(_tree); \ + assert_null(_op); \ + ly_in_free(_in, 0); \ + } + +/** + * @brief Parse a notification in an envelope detected from the input and check the result. + * + * @param[in] state Test state. + * @param[in] data Notification to parse, NUL-terminated also in CBOR. + * @param[in] format Format of @p data. + * @param[in] yp_envelope Whether @p data is in the YANG-Push envelope. + */ +static void +check_parse_auto(void **state, const char *data, LYD_FORMAT format, ly_bool yp_envelope) +{ + struct lyd_node *tree, *op; + struct ly_in *in; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory_chunk(data, strlen(data), &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, format, LYD_TYPE_NOTIF_AUTO, 0, &tree, &op)); + ly_in_free(in, 0); + + if (yp_envelope) { + /* envelope with its schema and an empty contents */ + assert_non_null(tree->schema); + assert_string_equal(LYD_NAME(tree), "envelope"); + assert_null(((struct lyd_node_any *)lyd_child(tree)->next)->child); + assert_int_equal(LY_SUCCESS, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL)); + } else { + /* opaque envelope with eventTime */ + assert_null(tree->schema); + assert_string_equal(LYD_NAME(tree), "notification"); + assert_string_equal(LYD_NAME(lyd_child(tree)), "eventTime"); + } + assert_null(tree->next); + + /* separate notification */ + assert_string_equal(LYD_NAME(op), "push-update"); + assert_null(lyd_parent(op)); + assert_null(op->next); + assert_int_equal(LY_SUCCESS, lyd_validate_op(op, NULL, LYD_TYPE_NOTIF_YANG, NULL)); + + lyd_free_all(tree); + lyd_free_all(op); +} + /** * @brief Put the notification separated by the parser back into the envelope contents, to print the whole message. * @@ -419,6 +472,100 @@ test_invalid(void **state) ly_in_free(in, 0); } +static void +test_auto(void **state) +{ + const char *data; + + /* YANG-Push envelope */ + data = "" + "2026-11-02T10:00:00Z" + "1" + ""; + check_parse_auto(state, data, LYD_XML, 1); + + data = "" + "2026-11-02T10:00:00Z" + "1" + ""; + check_parse_auto(state, data, LYD_XML, 1); + + data = "{\"ietf-yp-notification:envelope\":{\"event-time\":\"2026-11-02T10:00:00Z\",\"contents\":{" + "\"ietf-yang-push:push-update\":{\"id\":1}}}}"; + check_parse_auto(state, data, LYD_JSON, 1); + + /* NETCONF and RESTCONF envelopes */ + data = "" + "2026-11-02T10:00:00Z" + "1" + ""; + check_parse_auto(state, data, LYD_XML, 0); + + data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2026-11-02T10:00:00Z\"," + "\"ietf-yang-push:push-update\":{\"id\":1}}}"; + check_parse_auto(state, data, LYD_JSON, 0); + +#ifdef LY_HAVE_CBOR + data = "\xa1\x78\x1d" "ietf-yp-notification:envelope" "\xa2" + "\x6a" "event-time" "\x74" "2026-11-02T10:00:00Z" + "\x68" "contents" "\xa1\x78\x1a" "ietf-yang-push:push-update" "\xa1\x62" "id" "\x01"; + check_parse_auto(state, data, LYD_CBOR, 1); + + data = "\xa1\x78\x1a" "ietf-restconf:notification" "\xa2" + "\x69" "eventTime" "\x74" "2026-11-02T10:00:00Z" + "\x78\x1a" "ietf-yang-push:push-update" "\xa1\x62" "id" "\x01"; + check_parse_auto(state, data, LYD_CBOR, 0); +#endif /* LY_HAVE_CBOR */ + + /* neither envelope */ + data = "1"; + CHECK_PARSE_AUTO_ERR(data, LYD_XML, LY_ENOT); + CHECK_LOG_CTX("Missing NETCONF or YANG-Push , or in incorrect namespace.", NULL, 1); + + data = ""; + CHECK_PARSE_AUTO_ERR(data, LYD_XML, LY_ENOT); + CHECK_LOG_CTX("Missing NETCONF or YANG-Push , or in incorrect namespace.", NULL, 1); + + data = ""; + CHECK_PARSE_AUTO_ERR(data, LYD_XML, LY_ENOT); + CHECK_LOG_CTX("Missing NETCONF or YANG-Push , or in incorrect namespace.", NULL, 1); + + data = "{\"ietf-yang-push:push-update\":{\"id\":1}}"; + CHECK_PARSE_AUTO_ERR(data, LYD_JSON, LY_EVALID); + CHECK_LOG_CTX("Unexpected object \"ietf-yang-push:push-update\" instead of \"ietf-restconf:notification\" " + "or \"ietf-yp-notification:envelope\".", NULL, 1); + +#ifdef LY_HAVE_CBOR + data = "\xa1\x78\x1a" "ietf-yang-push:push-update" "\xa1\x62" "id" "\x01"; + CHECK_PARSE_AUTO_ERR(data, LYD_CBOR, LY_EVALID); + CHECK_LOG_CTX("Unexpected object \"ietf-yang-push:push-update\" instead of \"ietf-restconf:notification\" " + "or \"ietf-yp-notification:envelope\".", NULL, 1); +#endif /* LY_HAVE_CBOR */ + + /* sibling of the YANG-Push envelope */ + data = "" + "2026-11-02T10:00:00Z" + "1" + "" + "2"; + CHECK_PARSE_AUTO_ERR(data, LYD_XML, LY_EVALID); + CHECK_LOG_CTX("Unexpected sibling node.", NULL, 1); + + data = "{\"ietf-yp-notification:envelope\":{\"event-time\":\"2026-11-02T10:00:00Z\",\"contents\":{" + "\"ietf-yang-push:push-update\":{\"id\":1}}},\"ietf-yang-push:push-update\":{\"id\":2}}"; + CHECK_PARSE_AUTO_ERR(data, LYD_JSON, LY_EVALID); + CHECK_LOG_CTX("Unexpected sibling node.", NULL, 1); + +#ifdef LY_HAVE_CBOR + data = "\xa2\x78\x1d" "ietf-yp-notification:envelope" "\xa2" + "\x6a" "event-time" "\x74" "2026-11-02T10:00:00Z" + "\x68" "contents" "\xa1\x78\x1a" "ietf-yang-push:push-update" "\xa1\x62" "id" "\x01" + "\x78\x1a" "ietf-yang-push:push-update" "\xa1\x62" "id" "\x02"; + CHECK_PARSE_AUTO_ERR(data, LYD_CBOR, LY_EVALID); + CHECK_LOG_CTX("Unexpected notification element \"push-update\".", "/ietf-yang-push:push-update", 1); +#endif /* LY_HAVE_CBOR */ +} + static void test_invalid_validate(void **state) { @@ -453,6 +600,32 @@ test_no_module(void **state) "\"ietf-yang-push:push-update\":{\"id\":1}}}}"; CHECK_PARSE_ENVELOPE_ERR(data, LYD_JSON, LY_EINVAL); CHECK_LOG_CTX("Module \"ietf-yp-notification\" is not implemented.", NULL, 0); + + /* detected YANG-Push envelope */ + CHECK_PARSE_AUTO_ERR(data, LYD_JSON, LY_EINVAL); + CHECK_LOG_CTX("Module \"ietf-yp-notification\" is not implemented.", NULL, 0); + + data = "" + "2026-11-02T10:00:00Z" + "1" + ""; + CHECK_PARSE_AUTO_ERR(data, LYD_XML, LY_EINVAL); + CHECK_LOG_CTX("Module \"ietf-yp-notification\" is not implemented.", NULL, 0); + +#ifdef LY_HAVE_CBOR + data = "\xa1\x78\x1d" "ietf-yp-notification:envelope" "\xa2" + "\x6a" "event-time" "\x74" "2026-11-02T10:00:00Z" + "\x68" "contents" "\xa1\x78\x1a" "ietf-yang-push:push-update" "\xa1\x62" "id" "\x01"; + CHECK_PARSE_AUTO_ERR(data, LYD_CBOR, LY_EINVAL); + CHECK_LOG_CTX("Module \"ietf-yp-notification\" is not implemented.", NULL, 0); +#endif /* LY_HAVE_CBOR */ + + /* the NETCONF envelope needs no module */ + data = "" + "2026-11-02T10:00:00Z" + "1" + ""; + check_parse_auto(state, data, LYD_XML, 0); } int @@ -468,6 +641,7 @@ main(void) UTEST(test_nested, setup), UTEST(test_augment, setup), UTEST(test_invalid, setup), + UTEST(test_auto, setup), UTEST(test_invalid_validate, setup), UTEST(test_no_module), }; From d2c3bd2d0742ff65259527a93a25c72ace750ba1 Mon Sep 17 00:00:00 2001 From: Roman Janota Date: Wed, 11 Nov 2026 10:36:51 +0100 Subject: [PATCH 4/6] json BUGFIX restore input line from backup The JSON parser backup kept the input position but not its line, so newlines read before a restore were counted twice and every later error reported a line too far. LYD_TYPE_NOTIF_AUTO detection peeks at the first member this way. --- src/json.c | 2 ++ src/json.h | 1 + tests/utests/data/test_notif_envelope.c | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/src/json.c b/src/json.c index 042ea9b51..c31003563 100644 --- a/src/json.c +++ b/src/json.c @@ -1067,6 +1067,7 @@ lyjson_ctx_backup(struct lyjson_ctx *jsonctx) jsonctx->backup.value = jsonctx->value; jsonctx->backup.value_len = jsonctx->value_len; jsonctx->backup.input = jsonctx->in->current; + jsonctx->backup.line = jsonctx->in->line; jsonctx->backup.dynamic = jsonctx->dynamic; jsonctx->dynamic = 0; } @@ -1082,6 +1083,7 @@ lyjson_ctx_restore(struct lyjson_ctx *jsonctx) jsonctx->value = jsonctx->backup.value; jsonctx->value_len = jsonctx->backup.value_len; jsonctx->in->current = jsonctx->backup.input; + jsonctx->in->line = jsonctx->backup.line; jsonctx->dynamic = jsonctx->backup.dynamic; jsonctx->backup.dynamic = 0; } diff --git a/src/json.h b/src/json.h index c13b7f6a9..a00802648 100644 --- a/src/json.h +++ b/src/json.h @@ -79,6 +79,7 @@ struct lyjson_ctx { uint32_t value_len; ly_bool dynamic; const char *input; + uint64_t line; } backup; }; diff --git a/tests/utests/data/test_notif_envelope.c b/tests/utests/data/test_notif_envelope.c index a3044df84..31823e40a 100644 --- a/tests/utests/data/test_notif_envelope.c +++ b/tests/utests/data/test_notif_envelope.c @@ -542,6 +542,15 @@ test_auto(void **state) "or \"ietf-yp-notification:envelope\".", NULL, 1); #endif /* LY_HAVE_CBOR */ + /* line of an error after the envelope detection */ + data = "{\"ietf-yp-notification:envelope\":\n" + "{\n" + "\"event-time\":\"2026-11-02T10:00:00Z\",\"contents\":{\n" + "\"ietf-yang-push:push-update\":{\"id\":\"abc\"}}}}"; + CHECK_PARSE_AUTO_ERR(data, LYD_JSON, LY_EVALID); + CHECK_LOG_CTX("Invalid non-number-encoded uint32 value \"abc\".", + "/ietf-yp-notification:envelope/contents/ietf-yang-push:push-update/id", 4); + /* sibling of the YANG-Push envelope */ data = "" "2026-11-02T10:00:00Z" From 40a86be7f69b053245454787d2a9b5572ba34791 Mon Sep 17 00:00:00 2001 From: Roman Janota Date: Wed, 11 Nov 2026 18:26:55 +0100 Subject: [PATCH 5/6] SOVERSION bump to version 5.13.0 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ab025ea3b..b0eed2242 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,8 +66,8 @@ set(LIBYANG_VERSION ${LIBYANG_MAJOR_VERSION}.${LIBYANG_MINOR_VERSION}.${LIBYANG_ # set version of the library set(LIBYANG_MAJOR_SOVERSION 5) -set(LIBYANG_MINOR_SOVERSION 12) -set(LIBYANG_MICRO_SOVERSION 1) +set(LIBYANG_MINOR_SOVERSION 13) +set(LIBYANG_MICRO_SOVERSION 0) set(LIBYANG_SOVERSION_FULL ${LIBYANG_MAJOR_SOVERSION}.${LIBYANG_MINOR_SOVERSION}.${LIBYANG_MICRO_SOVERSION}) set(LIBYANG_SOVERSION ${LIBYANG_MAJOR_SOVERSION}) From 21a78d97f4edc15518ffbc53ee378b01687072e9 Mon Sep 17 00:00:00 2001 From: Roman Janota Date: Wed, 11 Nov 2026 18:26:55 +0100 Subject: [PATCH 6/6] VERSION bump to version 6.4.26 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b0eed2242..a867b1260 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,7 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) # set version of the project set(LIBYANG_MAJOR_VERSION 6) set(LIBYANG_MINOR_VERSION 4) -set(LIBYANG_MICRO_VERSION 25) +set(LIBYANG_MICRO_VERSION 26) set(LIBYANG_VERSION ${LIBYANG_MAJOR_VERSION}.${LIBYANG_MINOR_VERSION}.${LIBYANG_MICRO_VERSION}) # set version of the library