diff --git a/CHANGELOG.md b/CHANGELOG.md index 59dc36e1..6034a570 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,12 +24,19 @@ ## 4.5.0 Expected: May 2020 +### API changes on existing protocol/config features (You may have have to change how you use Clixon) + +* Stricter incoming RPC sanity checking, error messages may have changed. + ### C-API changes on existing features (you may need to change your plugin C-code) * CLI * `clicon_parse()`: Changed signature due to new cligen error and result handling: * Removed: `cli_nomatch()` +### Minor changes + + ## 4.4.0 5 April 2020 @@ -115,8 +122,6 @@ features include optimized search functions and a repair callback. ### Minor changes -* Added a compile-time option `MOVE_TRANS_END` which changes the semantics of the transaction_end callback. Instead of being called after a transaction, it is called prior to the target database is installed. This is to ensure that the source and target databases are same as for other transaction callbacks. - * Moved hello example to [clixon-examples](https://github.com/clicon/clixon-examples) * Sanity check of mandatory key statement for Yang LISTs. * If fails, exit with error message, eg: `Yang error: Sanity check failed: LIST vsDataContainer lacks key statement which MUST be present (See RFC 7950 Sec 7.8.2)` diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index f882fccf..0795cfc3 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -1570,12 +1570,16 @@ from_client_msg(clicon_handle h, goto done; } /* Decode msg from client -> xml top (ct) and session id */ - if (clicon_msg_decode(msg, yspec, &id, &xt) < 0){ - if (netconf_malformed_message(cbret, "XML parse error")< 0) + if ((ret = clicon_msg_decode(msg, yspec, &id, &xt, &xret)) < 0){ + if (netconf_malformed_message(cbret, "XML parse error") < 0) + goto done; + goto reply; + } + if (ret == 0){ + if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) goto done; goto reply; } - if ((x = xpath_first(xt, NULL, "/rpc")) == NULL){ if ((x = xpath_first(xt, NULL, "/hello")) != NULL){ if ((ret = from_client_hello(h, x, ce, cbret)) <0) @@ -1589,12 +1593,6 @@ from_client_msg(clicon_handle h, } } ce->ce_id = id; - /* Populate incoming XML tree with yang - - * should really have been dealt with by decode above - * but it still is needed - test_cli debug test fails - */ - if (xml_bind_yang_rpc(x, yspec, NULL) < 0) - goto done; if ((ret = xml_yang_validate_rpc(h, x, &xret)) < 0) goto done; if (ret == 0){ diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index d511e109..18af8e82 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -509,8 +509,6 @@ from_validate_common(clicon_handle h, * @retval -1 Error - or validation failed * @retval 0 Validation failed (with cbret set) * @retval 1 Validation OK - * @note Need to differentiate between error and validation fail - * (only done for validate_common) */ int candidate_commit(clicon_handle h, diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index a9a0de32..c69eb2c8 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -977,7 +977,8 @@ cli_notification_cb(int s, cxobj *xt = NULL; cxobj *xe; cxobj *x; - enum format_enum format = (enum format_enum)arg; + enum format_enum format = (enum format_enum)arg; + int ret; /* get msg (this is the reason this function is called) */ if (clicon_msg_rcv(s, &reply, &eof) < 0) @@ -989,8 +990,13 @@ cli_notification_cb(int s, event_unreg_fd(s, cli_notification_cb); goto done; } - if (clicon_msg_decode(reply, NULL, NULL, &xt) < 0) /* XXX pass yang_spec */ + /* XXX pass yang_spec and use xerr*/ + if ((ret = clicon_msg_decode(reply, NULL, NULL, &xt, NULL)) < 0) goto done; + if (ret == 0){ /* will not happen since no yspec ^*/ + clicon_err(OE_NETCONF, EFAULT, "Notification malformed"); + goto done; + } if ((xe = xpath_first(xt, NULL, "//event")) != NULL){ x = NULL; while ((x = xml_child_each(xe, x, -1)) != NULL) { diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index b7879a86..20c887c7 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -425,6 +425,8 @@ netconf_notification_cb(int s, clicon_handle h = (clicon_handle)arg; yang_stmt *yspec = NULL; cvec *nsc = NULL; + int ret; + cxobj *xerr = NULL; clicon_debug(1, "%s", __FUNCTION__); /* get msg (this is the reason this function is called) */ @@ -439,9 +441,12 @@ netconf_notification_cb(int s, goto done; } yspec = clicon_dbspec_yang(h); - if (clicon_msg_decode(reply, yspec, NULL, &xt) < 0) + if ((ret = clicon_msg_decode(reply, yspec, NULL, &xt, &xerr)) < 0) goto done; - + if (ret == 0){ /* XXX use xerr */ + clicon_err(OE_NETCONF, EFAULT, "Notification malformed"); + goto done; + } if ((nsc = xml_nsctx_init(NULL, NOTIFICATION_RFC5277_NAMESPACE)) == NULL) goto done; if ((xn = xpath_first(xt, nsc, "notification")) == NULL) @@ -467,6 +472,8 @@ netconf_notification_cb(int s, xml_nsctx_free(nsc); if (xt != NULL) xml_free(xt); + if (xerr != NULL) + xml_free(xerr); if (reply) free(reply); return retval; diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c index ec703c9d..699b5268 100644 --- a/apps/restconf/restconf_methods_post.c +++ b/apps/restconf/restconf_methods_post.c @@ -847,8 +847,17 @@ api_operations_post(clicon_handle h, clicon_log_xml(LOG_DEBUG, xtop, "%s 5. Translate input args:", __FUNCTION__); #endif /* 6. Validate outgoing RPC and fill in defaults */ - if (xml_bind_yang_rpc(xtop, yspec, NULL) < 0) /* */ + if ((ret = xml_bind_yang_rpc(xtop, yspec, &xret)) < 0) /* */ goto done; + if (ret == 0){ + if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){ + clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); + goto ok; + } + if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) + goto done; + goto ok; + } if ((ret = xml_yang_validate_rpc(h, xtop, &xret)) < 0) goto done; diff --git a/apps/restconf/restconf_stream.c b/apps/restconf/restconf_stream.c index 7c8f7c69..325d33d2 100644 --- a/apps/restconf/restconf_stream.c +++ b/apps/restconf/restconf_stream.c @@ -161,6 +161,7 @@ restconf_stream_cb(int s, cxobj *xn; /* notification xml */ cbuf *cb = NULL; int pretty = 0; /* XXX should be via arg */ + int ret; clicon_debug(1, "%s", __FUNCTION__); /* get msg (this is the reason this function is called) */ @@ -180,8 +181,12 @@ restconf_stream_cb(int s, clicon_exit_set(); goto done; } - if (clicon_msg_decode(reply, NULL, NULL, &xtop) < 0) /* XXX pass yang_spec */ + if ((ret = clicon_msg_decode(reply, NULL, NULL, &xtop, NULL)) < 0) /* XXX pass yang_spec */ goto done; + if (ret == 0){ + clicon_err(OE_XML, EFAULT, "Invalid notification"); + goto done; + } /* create event */ if ((cb = cbuf_new()) == NULL){ clicon_err(OE_PLUGIN, errno, "cbuf_new"); diff --git a/include/clixon_custom.h b/include/clixon_custom.h index 9666c2f5..00e57912 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -98,10 +98,3 @@ */ #define STATE_ORDERED_BY_SYSTEM -/*! Separate list merge into two separate rounds, - * First round search for duplicates where objects are saved, and a second round where - * actual merge is done. If in same round, in extreme cases that later - * searches are among earlier objects already added - * clixon-4.4 - */ -#define XML_MERGE_TWO_ROUNDS diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index 0f5e8829..2c0f3a71 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -86,6 +86,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/clixon/clixon_nacm.h b/lib/clixon/clixon_nacm.h index c43ccd69..4589585e 100644 --- a/lib/clixon/clixon_nacm.h +++ b/lib/clixon/clixon_nacm.h @@ -2,7 +2,8 @@ * ***** BEGIN LICENSE BLOCK ***** - Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren + Copyright (C) 2017-2019 Olof Hagsand + Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC This file is part of CLIXON. diff --git a/lib/clixon/clixon_proto.h b/lib/clixon/clixon_proto.h index 97f005e4..bb91f4d3 100644 --- a/lib/clixon/clixon_proto.h +++ b/lib/clixon/clixon_proto.h @@ -67,7 +67,7 @@ struct clicon_msg *clicon_msg_encode(uint32_t id, char *format, ...) __attribute #else struct clicon_msg *clicon_msg_encode(uint32_t id, char *format, ...); #endif -int clicon_msg_decode(struct clicon_msg *msg, yang_stmt *yspec, uint32_t *id, cxobj **xml); +int clicon_msg_decode(struct clicon_msg *msg, yang_stmt *yspec, uint32_t *id, cxobj **xml, cxobj **xerr); int clicon_connect_unix(clicon_handle h, char *sockpath); diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h index f5c13479..0e7d9dda 100644 --- a/lib/clixon/clixon_string.h +++ b/lib/clixon/clixon_string.h @@ -2,7 +2,8 @@ * ***** BEGIN LICENSE BLOCK ***** - Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren + Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren + Copyright (C) 2017-2019 Olof Hagsand Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC This file is part of CLIXON. @@ -102,6 +103,7 @@ int clicon_str2int(const map_str2int *mstab, char *str); int clicon_str2int_search(const map_str2int *mstab, char *str, int upper); int nodeid_split(char *nodeid, char **prefix, char **id); char *clixon_trim(char *str); +char *clixon_trim2(char *str, char *trims); int clicon_strcmp(char *s1, char *s2); #ifndef HAVE_STRNDUP diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index fd400956..9ae2fb60 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -120,10 +120,8 @@ enum yang_bind{ YB_NONE=0, /* Dont do Yang binding */ YB_MODULE, /* Search for matching yang binding among top-level symbols of Yang modules */ YB_PARENT, /* Assume yang binding of existing parent and match its children by name */ + YB_RPC, /* Assume top-level xml is an netconf RPC message (or hello) */ -#ifdef NYI - YB_RPC, /* Assume top-level xml is an netconf RPC message */ -#endif }; typedef enum yang_bind yang_bind; diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index 9a60b709..3e36981a 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -70,7 +70,7 @@ INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \ clixon_string.c clixon_regex.c clixon_handle.c clixon_file.c \ clixon_xml.c clixon_xml_io.c clixon_xml_sort.c clixon_xml_map.c clixon_xml_vec.c \ - clixon_json.c \ + clixon_xml_bind.c clixon_json.c \ clixon_yang.c clixon_yang_type.c clixon_yang_module.c clixon_yang_parse_lib.c \ clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \ clixon_path.c clixon_validate.c \ diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 71b84b68..94bb3550 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -362,6 +362,10 @@ text_modify(clicon_handle h, } } if (x1bstr){ + /* XXX: Do the type lookup here inline instead, there are more + * cases where we check for types, eg where we now call clixon_trim2 + * AND in nacm path where we need proper namespace contexts. + */ if (check_identityref(x1, x0, x0p, x1bstr, y0) < 0) goto done; if ((x0b = xml_body_get(x0)) != NULL){ @@ -496,7 +500,7 @@ text_modify(clicon_handle h, if (xml_copy(x1, x0) < 0) goto done; break; - } + } /* anyxml, anydata */ if (x0==NULL){ if (op==OP_MERGE && !permit && xnacm){ if ((ret = nacm_datanode_write(NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0) diff --git a/lib/src/clixon_instance_id_parse.y b/lib/src/clixon_instance_id_parse.y index aac7fbaa..7f873f7c 100644 --- a/lib/src/clixon_instance_id_parse.y +++ b/lib/src/clixon_instance_id_parse.y @@ -45,6 +45,10 @@ * quoted-string = (DQUOTE string DQUOTE) / (SQUOTE string SQUOTE) * positive-integer-value = (non-zero-digit DIGIT*) * identifier = (ALPHA | "_")(ALPHA | DIGIT | "_" | "-" | ".")* + * + * RFC 8341: All the same rules as an instance-identifier apply, except that predicates + * for keys are optional. If a key predicate is missing, then the + * node-instance-identifier represents all possible server instances for that key. */ %start start diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 07a85f67..8782befc 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -68,6 +68,7 @@ #include "clixon_yang_type.h" #include "clixon_yang_module.h" #include "clixon_xml_sort.h" +#include "clixon_xml_bind.h" #include "clixon_xml_map.h" #include "clixon_xml_nsctx.h" /* namespace context */ #include "clixon_netconf_lib.h" @@ -1249,6 +1250,12 @@ _json_parse(char *str, break; case YB_NONE: break; + case YB_RPC: + if ((ret = xml_bind_yang_rpc(x, yspec, xerr)) < 0) + goto done; + if (ret == 0) + failed++; + break; } /* Now find leafs with identityrefs (+transitive) and translate * prefixes in values to XML namespaces */ diff --git a/lib/src/clixon_nacm.c b/lib/src/clixon_nacm.c index d0476207..6a0a18da 100644 --- a/lib/src/clixon_nacm.c +++ b/lib/src/clixon_nacm.c @@ -2,7 +2,8 @@ * ***** BEGIN LICENSE BLOCK ***** - Copyright (C) 2009-2020 Olof Hagsand + Copyright (C) 2017-2019 Olof Hagsand + Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC This file is part of CLIXON. @@ -351,13 +352,13 @@ nacm_rule_datanode(cxobj *xt, /* 6b) Either (1) the rule does not have a "rule-type" defined or (2) the "rule-type" is "data-node" and the "path" matches the - requested data node, action node, or notification node. A - path is considered to match if the requested node is the node - specified by the path or is a descendant node of the path.*/ + requested data node, action node, or notification node. */ if ((path = xml_find_body(xrule, "path")) == NULL){ if (xml_find_body(xrule, "rpc-name") ||xml_find_body(xrule, "notification-name")) goto nomatch; } + else + path = clixon_trim2(path, " \t\n"); access_operations = xml_find_body(xrule, "access-operations"); switch (access){ case NACM_READ: @@ -387,10 +388,17 @@ nacm_rule_datanode(cxobj *xt, default: break; } - /* Here module is matched, now check for path if any NYI */ + /* Here module is matched, now check for path if any NYI + * A path is considered to match if the requested node is the node + * specified by the path or is a descendant node of the path */ if (path){ +#if 0 + if ((ret = clixon_xml_find_instance_id(xt, yt, xvec, xlen, "%s", path)) == NULL) + goto nomatch; +#else if ((xpath = xpath_first(xt, nsc, "%s", path)) == NULL) goto nomatch; +#endif /* The requested node xr is the node specified by the path or is a * descendant node of the path: * xmatch is one of xvec[] or an ancestor of the xvec[] nodes. diff --git a/lib/src/clixon_path.c b/lib/src/clixon_path.c index 01ce4751..d693a25f 100644 --- a/lib/src/clixon_path.c +++ b/lib/src/clixon_path.c @@ -1321,9 +1321,9 @@ api_path_resolve(clixon_path *cplist, * @retval -1 Error * @retval 0 Fail * @retval 1 OK - * @note: The spec says: prefixes depend on the XML context in which the value occurs. However, - * canonical prefixes/namespaces are used based on loaded yang modules. - * This means that prefix=NULL is not allowed. + * @note: The spec says: prefixes depend on the XML context in which the value occurs. + * However, canonical prefixes/namespaces are used based on loaded yang modules. + * This means that prefix=NULL is not allowed. * Reasons for fail (retval = 0) are: * - No prefix of identifier (keynames may omit prefix) * - Prefix does not correspond to existing module. @@ -1564,7 +1564,8 @@ clixon_xml_find_api_path(cxobj *xt, /*! Given (instance-id) path and XML tree, return matching xml node vector using stdarg * - * Instance-identifier is a subset of XML XPaths and defined in Yang, used in NACM for example. + * Instance-identifier is a subset of XML XPaths and defined in Yang, used in NACM for + * example. * @param[in] xt Top xml-tree where to search * @param[in] yt Yang statement of top symbol (can be yang-spec if top-level) * @param[out] xvec Vector of xml-trees. Vector must be free():d after use @@ -1588,6 +1589,7 @@ clixon_xml_find_api_path(cxobj *xt, * } * clixon_xvec_free(xvec); * @endcode + * @note canonical namespace contexts are used, seexpath2canonical * @see clixon_xml_find_api_path for RESTCONF api-paths * @see RFC7950 Sec 9.13 */ diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 6d129cce..f2a162b4 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -59,6 +59,7 @@ #include #include #include +#include /* cligen */ #include @@ -170,15 +171,21 @@ clicon_msg_encode(uint32_t id, * @param[in] yspec Yang specification, (can be NULL) * @param[out] id Session id * @param[out] xml XML parse tree + * @param[out] xerr Reason for failure (yang assignment not made) if retval =0 + * @retval 1 Parse OK and all yang assignment made + * @retval 0 Parse OK but yang assigment not made (or only partial) + * @retval -1 Error with clicon_err called. Includes parse error */ int clicon_msg_decode(struct clicon_msg *msg, yang_stmt *yspec, uint32_t *id, - cxobj **xml) + cxobj **xml, + cxobj **xerr) { - int retval = -1; - char *xmlstr; + int retval = -1; + char *xmlstr; + int ret; /* hdr */ if (id) @@ -186,11 +193,16 @@ clicon_msg_decode(struct clicon_msg *msg, /* body */ xmlstr = msg->op_body; clicon_debug(1, "%s %s", __FUNCTION__, xmlstr); - if (clixon_xml_parse_string(xmlstr, yspec?YB_MODULE:YB_NONE, yspec, xml, NULL) < 0) + if ((ret = clixon_xml_parse_string(xmlstr, yspec?YB_RPC:YB_NONE, yspec, xml, xerr)) < 0) goto done; - retval = 0; + if (ret == 0) + goto fail; + retval = 1; done: return retval; + fail: + retval = 0; + goto done; } /*! Open local connection using unix domain sockets diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 8d54a75f..4bbfd0af 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -76,7 +76,7 @@ #include "clixon_stream.h" #include "clixon_err_string.h" #include "clixon_xml_nsctx.h" -#include "clixon_xml_map.h" +#include "clixon_xml_bind.h" #include "clixon_xml_sort.h" #include "clixon_xml_io.h" #include "clixon_netconf_lib.h" diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index 111ef2f6..f0298bf0 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -2,7 +2,9 @@ * ***** BEGIN LICENSE BLOCK ***** - Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren + Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren + Copyright (C) 2017-2019 Olof Hagsand + Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC This file is part of CLIXON. @@ -692,6 +694,29 @@ clixon_trim(char *str) return s; } +/*! Trim blanks from front and end of a string, return new string + * @param[in] str + * @param[in] trims Characters to trim: a vector of characters + * @retval s Pointer into existing str after trimming blanks + */ +char * +clixon_trim2(char *str, + char *trims) +{ + char *s = str; + int i; + + while (strlen(s) && index(trims, s[0])) /* trim from front */ + s++; + for (i=strlen(s)-1; i>=0; i--){ /* trim from rear */ + if (index(trims, s[i])) + s[i] = '\0'; + else + break; + } + return s; +} + /*! check string equals (NULL is equal) * @param[in] s1 String 1 * @param[in] s2 String 2 diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c index c803e522..5aa9e430 100644 --- a/lib/src/clixon_validate.c +++ b/lib/src/clixon_validate.c @@ -1090,14 +1090,10 @@ xml_yang_validate_all(clicon_handle h, cprintf(cb, "Failed to find YANG spec of XML node: %s", xml_name(xt)); if ((xp = xml_parent(xt)) != NULL) cprintf(cb, " with parent: %s", xml_name(xp)); - if (namespace){ + if (namespace) cprintf(cb, " in namespace: %s", namespace); - if (netconf_unknown_element_xml(xret, "application", xml_name(xt), cbuf_get(cb)) < 0) - goto done; - } - else - if (netconf_unknown_element_xml(xret, "application", xml_name(xt), cbuf_get(cb)) < 0) - goto done; + if (netconf_unknown_element_xml(xret, "application", xml_name(xt), cbuf_get(cb)) < 0) + goto done; goto fail; } if (yang_config(ys) != 0){ diff --git a/lib/src/clixon_xml_io.c b/lib/src/clixon_xml_io.c index 35dbf71d..16a33151 100644 --- a/lib/src/clixon_xml_io.c +++ b/lib/src/clixon_xml_io.c @@ -66,9 +66,9 @@ #include "clixon_log.h" #include "clixon_yang.h" #include "clixon_xml.h" -#include "clixon_options.h" /* xml_bind_yang */ +#include "clixon_options.h" #include "clixon_yang_module.h" -#include "clixon_xml_map.h" /* xml_bind_yang */ +#include "clixon_xml_bind.h" #include "clixon_xml_vec.h" #include "clixon_xml_sort.h" #include "clixon_xml_nsctx.h" @@ -455,7 +455,13 @@ _xml_parse(const char *str, if (ret == 0) failed++; break; - } + case YB_RPC: + if ((ret = xml_bind_yang_rpc(x, yspec, xerr)) < 0) + goto done; + if (ret == 0) + failed++; + break; + } /* switch */ } if (failed) goto fail; @@ -601,7 +607,7 @@ clixon_xml_parse_file(int fd, * @param[in] yb How to bind yang to XML top-level when parsing * @param[in] yspec Yang specification, or NULL * @param[in,out] xt Pointer to XML parse tree. If empty will be created. - * @param[out] xerr Reason for failure (yang assignment not made) + * @param[out] xerr Reason for failure (yang assignment not made) if retval = 0 * @retval 1 Parse OK and all yang assignment made * @retval 0 Parse OK but yang assigment not made (or only partial) * @retval -1 Error with clicon_err called. Includes parse error diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 73f37668..c0f8cada 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1304,14 +1304,12 @@ xml_merge1(cxobj *x0, /* the target */ char *x1bstr; /* mod body string */ yang_stmt *yc; /* yang child */ cbuf *cbr = NULL; /* Reason buffer */ -#ifdef XML_MERGE_TWO_ROUNDS int i; struct { cxobj *w_x0c; cxobj *w_x1c; yang_stmt *w_yc; } *second_wave = NULL; -#endif assert(x1 && xml_type(x1) == CX_ELMNT); assert(y0); @@ -1347,13 +1345,11 @@ xml_merge1(cxobj *x0, /* the target */ } if (assign_namespaces(x1, x0, x0p) < 0) goto done; -#ifdef XML_MERGE_TWO_ROUNDS if ((second_wave = calloc(xml_child_nr(x1), sizeof(*second_wave))) == NULL){ clicon_err(OE_UNIX, errno, "calloc"); goto done; } i = 0; -#endif /* Loop through children of the modification tree */ x1c = NULL; while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) { @@ -1377,7 +1373,6 @@ xml_merge1(cxobj *x0, /* the target */ x0c = NULL; if (yc && match_base_child(x0, x1c, yc, &x0c) < 0) goto done; -#ifdef XML_MERGE_TWO_ROUNDS /* Save x0c, x1c, yc and merge in second wave, so that x1c entries "interfer" * with itself, ie that later searches are among earlier objects already added * to x0 */ @@ -1385,14 +1380,7 @@ xml_merge1(cxobj *x0, /* the target */ second_wave[i].w_x1c = x1c; second_wave[i].w_yc = yc; i++; -#else - if (xml_merge1(x0c, yc, x0, x1c, reason) < 0) - goto done; - if (*reason != NULL) - goto ok; -#endif } /* while */ -#ifdef XML_MERGE_TWO_ROUNDS /* Second run where actual merging is done * Loop through children of the modification tree */ x1c = NULL; @@ -1408,7 +1396,6 @@ xml_merge1(cxobj *x0, /* the target */ goto ok; i++; } -#endif if (xml_parent(x0) == NULL && xml_insert(x0p, x0, INS_LAST, NULL, NULL) < 0) goto done; @@ -1416,10 +1403,8 @@ xml_merge1(cxobj *x0, /* the target */ ok: retval = 0; done: -#ifdef XML_MERGE_TWO_ROUNDS if (second_wave) free(second_wave); -#endif if (cbr) cbuf_free(cbr); return retval; diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 08d7dffe..7eb07f77 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -660,7 +660,8 @@ cv_validate1(clicon_handle h, if (strcmp(restype, "enumeration") == 0){ found = 0; yi = NULL; - if (str != NULL) + if (str != NULL) { + str = clixon_trim2(str, " \t\n"); /* May be misplaced, strip earlier? */ while ((yi = yn_each(yrestype, yi)) != NULL){ if (yang_keyword_get(yi) != Y_ENUM) continue; @@ -669,6 +670,7 @@ cv_validate1(clicon_handle h, break; } } + } if (!found){ if (reason) *reason = cligen_reason("'%s' does not match enumeration", str); @@ -680,7 +682,7 @@ cv_validate1(clicon_handle h, * of the names of the bits that are set. A zero-length string thus * represents a value where no bits are set. */ - + str = clixon_trim2(str, " \t\n"); /* May be misplaced, strip earlier? */ nvec = 0; if ((vec = clicon_strsep(str, " \t", &nvec)) == NULL) goto done; diff --git a/test/test_identity.sh b/test/test_identity.sh index 57725b07..080e1d4c 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -283,7 +283,7 @@ expectpart "$(curl -s -i -X DELETE http://localhost/restconf/data/example:crypt # 2. set identity in other module with restconf , read it with restconf and netconf new "restconf add POST instead of PUT (should fail)" -expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/example:crypto -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Missing matching yang node"}}}' +expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/example:crypto -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: crypto with parent: crypto in namespace: urn:example:my-crypto"}}}' # Alternative error: #'{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Leaf contains sub-element"}}}' diff --git a/test/test_nacm_module_read.sh b/test/test_nacm_module_read.sh index 02792ace..2bc1784b 100755 --- a/test/test_nacm_module_read.sh +++ b/test/test_nacm_module_read.sh @@ -6,17 +6,6 @@ # @see test_nacm.sh is slightly modified - this follows the RFC more closely # See RFC 8341 A.1 and A.2 # Note: use clixon-example instead of ietf-netconf-monitoring since the latter is -# Tests for -# deny-ncm: This rule prevents the "guest" group from reading any -# monitoring information in the "clixon-example" YANG -# module. -# permit-ncm: This rule allows the "limited" group to read the -# "clixon-example" YANG module. -# permit-exec: This rule allows the "limited" group to invoke any -# protocol operation supported by the server. -# permit-all: This rule allows the "admin" group complete access to -# all content in the server. No subsequent rule will match for the -# "admin" group because of this module rule # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 0e35c86f..bfc97982 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -123,9 +123,10 @@ new "restconf empty rpc, default media type should fail (JSON)" expectpart "$(curl -si -X POST -H "Accept: application/yang-data+json" -d {\"clixon-example:input\":null} http://localhost/restconf/operations/clixon-example:empty)" 0 'HTTP/1.1 415 Unsupported Media Type' new "restconf empty rpc with extra args (should fail)" -expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-example:input\":{\"extra\":null}} http://localhost/restconf/operations/clixon-example:empty)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: extra with parent: empty in namespace: urn:example:clixon"}}} ' +expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-example:input\":{\"extra\":null}} http://localhost/restconf/operations/clixon-example:empty)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error","error-message":"Unrecognized parameter: extra in rpc: empty"}}}' -new "restconf debug rpc" +# Irritiating to get debugs on the terminal +#new "restconf debug rpc" #expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-lib:input\":{\"level\":0}} http://localhost/restconf/operations/clixon-lib:debug)" 0 "HTTP/1.1 204 No Content" new "restconf get empty config + state json" @@ -243,19 +244,19 @@ expecteq "$(curl -s -X POST -H "Content-Type: application/yang-data+json" -d '{" ' new "restconf rpc using POST json wrong" -expecteq "$(curl -s -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"wrongelement"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: wrongelement with parent: example in namespace: urn:example:clixon"}}} ' +expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-element","error-info":{"bad-element":"wrongelement"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: wrongelement with parent: example in namespace: urn:example:clixon"}}}' new "restconf rpc non-existing rpc without namespace" -expecteq "$(curl -s -X POST -H "Content-Type: application/yang-data+json" -d '{}' http://localhost/restconf/operations/kalle)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}} ' +expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{}' http://localhost/restconf/operations/kalle)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}' new "restconf rpc non-existing rpc" -expecteq "$(curl -s -X POST -H "Content-Type: application/yang-data+json" -d '{}' http://localhost/restconf/operations/clixon-example:kalle)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}} ' +expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{}' http://localhost/restconf/operations/clixon-example:kalle)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}' new "restconf rpc missing name" -expecteq "$(curl -s -X POST -H "Content-Type: application/yang-data+json" -d '{}' http://localhost/restconf/operations)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"Operation name expected"}}} ' +expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{}' http://localhost/restconf/operations)" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"Operation name expected"}}}' new "restconf rpc missing input" -expecteq "$(curl -s -X POST -H "Content-Type: application/yang-data+json" -d '{}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"restconf RPC does not have input statement"}}} ' +expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{}' http://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"restconf RPC does not have input statement"}}}' new "restconf rpc using POST xml" ret=$(curl -s -X POST -H "Content-Type: application/yang-data+json" -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"x":42}}' http://localhost/restconf/operations/clixon-example:example) diff --git a/test/test_rpc.sh b/test/test_rpc.sh index 28826183..60f0f051 100755 --- a/test/test_rpc.sh +++ b/test/test_rpc.sh @@ -114,7 +114,7 @@ if [ -z "$match" ]; then fi new "restconf empty rpc with input x" -expectpart "$(curl -iss -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":0}}' http://localhost/restconf/operations/clixon-example:empty)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"x"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: x with parent: empty in namespace: urn:example:clixon"}}} ' +expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":0}}' http://localhost/restconf/operations/clixon-example:empty)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"x"},"error-severity":"error","error-message":"Unrecognized parameter: x in rpc: empty"}}}' # cornercase: optional has yang input/output sections but test without body new "restconf optional rpc with null input and output" @@ -129,7 +129,7 @@ new "restconf omit mandatory" expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' http://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"x"},"error-severity":"error","error-message":"Mandatory variable"}}} ' new "restconf add extra" -expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0","extra":"0"}}' http://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: extra with parent: example in namespace: urn:example:clixon"}}} ' +expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0","extra":"0"}}' http://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-element","error-info":{"bad-element":"extra"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: extra with parent: example in namespace: urn:example:clixon"}}}' new "restconf wrong method" expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:wrong)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"wrong"},"error-severity":"error","error-message":"RPC not defined"}}} '