From 40cda7b6a46df6ce37c28eab7e4f2cb74c12c14a Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 17 Oct 2018 22:36:59 +0200 Subject: [PATCH] * ietf-netconf yang module added with candidate, validate, startup and xpath features enabled. * Added urn:ietf:params:netconf:capability:yang-library:1.0 * Thanks SCadilhac for helping out, see https://github.com/clicon/clixon/issues/39 * uri_percent_encode() and xml_chardata_encode() changed to use stdarg parameters --- CHANGELOG.md | 6 +- apps/backend/backend_main.c | 3 + apps/cli/cli_main.c | 3 + apps/netconf/netconf_hello.c | 56 ++++++++++-- apps/netconf/netconf_lib.c | 3 + apps/netconf/netconf_main.c | 10 ++- apps/restconf/restconf_main.c | 8 +- datastore/keyvalue/clixon_keyvalue.c | 6 +- lib/clixon/clixon_netconf_lib.h | 1 + lib/clixon/clixon_string.h | 11 ++- lib/src/clixon_netconf_lib.c | 75 +++++++++++----- lib/src/clixon_options.c | 5 +- lib/src/clixon_string.c | 122 +++++++++++++++++++-------- lib/src/clixon_xml.c | 4 +- lib/src/clixon_xml_map.c | 2 +- lib/src/clixon_yang_module.c | 1 + test/lib.sh | 9 +- test/test_feature.sh | 5 +- test/test_netconf.sh | 8 +- 19 files changed, 245 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6fd5f4f5..b6977be60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ *:* ``` * logical combination of features not implemented, eg if-feature "not foo or bar and baz"; + * ietf-netconf yang module added with candidate, validate, startup and xpath features enabled. * YANG Module Library support * According to RFC 7895 and implemented by ietf-yang-library.yang * Changed Netconf hello to single capabilty urn:ietf:params:netconf:capability:yang-library:1.0 according to YANG 1.1 RFC7950 Sec 5.6.4. @@ -32,7 +33,8 @@ ### API changes on existing features (you may need to change your code) * Netconf hello capability updated to YANG 1.1 RFC7950 Sec 5.6.4 - * A single capability is announced instead of many. + * Added urn:ietf:params:netconf:capability:yang-library:1.0 + * Thanks SCadilhac for helping out, see https://github.com/clicon/clixon/issues/39 * Major rewrite of event streams * If you used old event callbacks API, you need to switch to the streams API * See clixon_stream.[ch] @@ -72,8 +74,8 @@ goto done; ``` - ### Minor changes +* uri_percent_encode() and xml_chardata_encode() changed to stdarg parameters * Added CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml as option and in example (so you dont need to provide -f command-line option). * Yang 1.1 action syntax added (but function is not supported) * New function: clicon_conf_xml() returns configuration tree diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index efa705fe1..391ecffda 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -751,6 +751,9 @@ main(int argc, /* Load yang module library, RFC7895 */ if (yang_modules_init(h) < 0) goto done; + /* Add netconf yang spec, used by netconf client and as internal protocol */ + if (netconf_module_load(h) < 0) + goto done; /* Load yang Restconf stream discovery */ if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && yang_spec_parse_module(h, "ietf-restconf-monitoring", CLIXON_DATADIR, NULL, yspec)< 0) diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index c63b6e988..40f27d6e2 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -432,6 +432,9 @@ main(int argc, char **argv) clicon_yang_module_revision(h), yspec) < 0) goto done; + /* Load yang module library, RFC7895 */ + if (yang_modules_init(h) < 0) + goto done; if (printspec) yang_print(stdout, (yang_node*)yspec); diff --git a/apps/netconf/netconf_hello.c b/apps/netconf/netconf_hello.c index 6343a2999..61803c3bb 100644 --- a/apps/netconf/netconf_hello.c +++ b/apps/netconf/netconf_hello.c @@ -106,12 +106,46 @@ netconf_hello_dispatch(cxobj *xn) } /*! Create Netconf hello. Single cap and defer individual to querying modules - * This follows YANG 1.1 RFC7950 Sec 5.6.4, where a single capability announced - * and a client may query supported modules using RFC 7895 (Yang Module - * Library). + * @param[in] h Clicon handle * @param[in] cb Msg buffer * @param[in] session_id Id of client session + * Lots of dependencies here. regarding the hello protocol. + * RFC6241 NETCONF Protocol says: (8.1) + * MUST send a element containing a list of that peer's capabilities + * MUST send at least the base NETCONF capability, urn:ietf:params:netconf:base:1.1 + * MAY include capabilities for previous NETCONF versions + * MUST include a + * the example shows urn:ietf:params:netconf:capability:startup:1.0 + + * RFC5277 NETCONF Event Notifications + * urn:ietf:params:netconf:capability:notification:1.0 is advertised during the capability exchange + * + * RFC6022 YANG Module for NETCONF Monitoring + * MUST advertise the capability URI "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring" + * RFC7895 Yang module library defines how to announce module features (not hell capabilities) + * RFC7950 YANG 1.1 says (5.6.4); + * MUST announce the modules it implements by implementing the YANG module + * "ietf-yang-library" (RFC7895) and listing all implemented modules in the + * "/modules-state/module" list. + * MUST advertise urn:ietf:params:netconf:capability:yang-library:1.0? + * revision=&module-set-id= in the message. + * + * Question: should the NETCONF in RFC6241 sections 8.2-8.9 be announced both + * as features and as capabilities in the message according to RFC6241? + * urn:ietf:params:netconf:capability:candidate:1:0 (8.3) + * urn:ietf:params:netconf:capability:validate:1.1 (8.6) + * urn:ietf:params:netconf:capability:startup:1.0 (8.7) + * urn:ietf:params:netconf:capability:xpath:1.0 (8.9) + * urn:ietf:params:netconf:capability:notification:1.0 (RFC5277) + * + * @note the hello message is created bythe netconf application, not the + * backend, and backend may implement more modules - please consider if using + * library routines for detecting capabilities here. In contrast, yang module + * list (RFC7895) is processed by the backend. + * @note encode bodies, see xml_chardata_encode() + * @see yang_modules_state_get + * @see netconf_module_load */ int netconf_create_hello(clicon_handle h, @@ -121,21 +155,27 @@ netconf_create_hello(clicon_handle h, int retval = -1; char *module_set_id; char *ietf_yang_library_revision; + char *encstr = NULL; module_set_id = clicon_option_str(h, "CLICON_MODULE_SET_ID"); if ((ietf_yang_library_revision = yang_modules_revision(h)) == NULL) goto done; add_preamble(cb); - cprintf(cb, ""); + cprintf(cb, ""); cprintf(cb, ""); - cprintf(cb, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=\"%s\"&module-set-id=%s", - ietf_yang_library_revision, - module_set_id); - cprintf(cb, ""); + cprintf(cb, "urn:ietf:params:netconf:base:1.1"); + if (xml_chardata_encode(&encstr, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=\"%s\"&module-set-id=%s", + ietf_yang_library_revision, + module_set_id) < 0) + goto done; + cprintf(cb, "%s", encstr); + cprintf(cb, ""); cprintf(cb, "%lu", (long unsigned int)42+session_id); cprintf(cb, ""); add_postamble(cb); retval = 0; done: + if (encstr) + free(encstr); return retval; } diff --git a/apps/netconf/netconf_lib.c b/apps/netconf/netconf_lib.c index 998b4ef0b..fc24810b8 100644 --- a/apps/netconf/netconf_lib.c +++ b/apps/netconf/netconf_lib.c @@ -182,6 +182,9 @@ netconf_get_target(cxobj *xn, * @param[in] s * @param[in] cb Cligen buffer that contains the XML message * @param[in] msg Only for debug + * @note Assumes "cb" contains valid XML, ie encoding is correct. This is done + * if it is output by a xml render routine (xml_print et al), but NOT + * otherwise. */ int netconf_output(int s, diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 7e7a279c3..804902dfe 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -432,9 +432,11 @@ main(int argc, argc -= optind; argv += optind; + /* Create first yang spec */ if ((yspec = yspec_new()) == NULL) goto done; clicon_dbspec_yang_set(h, yspec); + /* Parse yang database spec file */ if (yang_filename){ if (yang_spec_parse_file(h, yang_filename, clicon_yang_dir(h), yspec) < 0) @@ -446,10 +448,12 @@ main(int argc, yspec) < 0) goto done; + /* Load yang module library, RFC7895 */ + if (yang_modules_init(h) < 0) + goto done; /* Add netconf yang spec, used by netconf client and as internal protocol */ - if (yang_spec_parse_module(h, "ietf-yang-library", CLIXON_DATADIR, NULL, yspec)< 0) - goto done; - + if (netconf_module_load(h) < 0) + goto done; /* Initialize plugins group */ if ((dir = clicon_netconf_dir(h)) != NULL) if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0) diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index aef612574..b107f6ddc 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -635,6 +635,10 @@ main(int argc, clicon_yang_module_revision(h), yspec) < 0) goto done; + + /* Load yang module library, RFC7895 */ + if (yang_modules_init(h) < 0) + goto done; /* Add system modules */ if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && yang_spec_parse_module(h, "ietf-restconf-monitoring", CLIXON_DATADIR, NULL, yspec)< 0) @@ -642,9 +646,7 @@ main(int argc, if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && yang_spec_parse_module(h, "ietf-netconf-notification", CLIXON_DATADIR, NULL, yspec)< 0) goto done; - /* Load yang module library, RFC7895 */ - if (yang_modules_init(h) < 0) - goto done; + if (stream_register(h, "NETCONF", "default NETCONF event stream") < 0) goto done; diff --git a/datastore/keyvalue/clixon_keyvalue.c b/datastore/keyvalue/clixon_keyvalue.c index 4e77418d6..39a111348 100644 --- a/datastore/keyvalue/clixon_keyvalue.c +++ b/datastore/keyvalue/clixon_keyvalue.c @@ -211,7 +211,7 @@ append_listkeys(cbuf *ckey, xml_name(xt), keyname); goto done; } - if (uri_percent_encode(xml_body(xkey), &bodyenc) < 0) + if (uri_percent_encode(&bodyenc, "%s", xml_body(xkey)) < 0) goto done; if (i++) cprintf(ckey, ","); @@ -328,7 +328,7 @@ get(char *dbname, * If xml element is a leaf-list, then the next element is expected to * be a value */ - if (uri_percent_decode(restval, &argdec) < 0) + if (uri_percent_decode(&argdec, restval) < 0) goto done; if ((xc = xml_find(x, name))==NULL || (xb = xml_find(xc, argdec))==NULL){ @@ -681,7 +681,7 @@ put(char *dbfile, goto done; break; case Y_LEAF_LIST: - if (uri_percent_encode(body, &bodyenc) < 0) + if (uri_percent_encode(&bodyenc, "%s", body) < 0) goto done; cprintf(cbxk, "=%s", bodyenc); break; diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h index 063345e30..cdf570e9e 100644 --- a/lib/clixon/clixon_netconf_lib.h +++ b/lib/clixon/clixon_netconf_lib.h @@ -63,5 +63,6 @@ int netconf_operation_failed_xml(cxobj **xret, char *type, char *message); int netconf_malformed_message(cbuf *cb, char *message); int netconf_malformed_message_xml(cxobj **xret, char *message); int netconf_trymerge(cxobj *x, yang_spec *yspec, cxobj **xret); +int netconf_module_load(clicon_handle h); #endif /* _CLIXON_NETCONF_LIB_H */ diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h index 5b5789dd3..cebb0871d 100644 --- a/lib/clixon/clixon_string.h +++ b/lib/clixon/clixon_string.h @@ -76,9 +76,14 @@ static inline char * strdup4(char *str) char **clicon_strsep(char *string, char *delim, int *nvec0); char *clicon_strjoin (int argc, char **argv, char *delim); int str2cvec(char *string, char delim1, char delim2, cvec **cvp); -int uri_percent_encode(char *str, char **escp); -int uri_percent_decode(char *esc, char **str); -int xml_chardata_encode(char *str, char **escp); +#if defined(__GNUC__) && __GNUC__ >= 3 +int uri_percent_encode(char **encp, char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +int xml_chardata_encode(char **escp, char *fmt, ... ) __attribute__ ((format (printf, 2, 3))); +#else +int uri_percent_encode(char **encp, char *str, ...); +int xml_chardata_encode(char **escp, char *fmt, ...); +#endif +int uri_percent_decode(char *enc, char **str); const char *clicon_int2str(const map_str2int *mstab, int i); int clicon_str2int(const map_str2int *mstab, char *str); diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index d59e88c98..9c4f3dca9 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -86,7 +86,7 @@ netconf_in_use(cbuf *cb, type) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -125,7 +125,7 @@ netconf_invalid_value(cbuf *cb, type) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -165,7 +165,7 @@ netconf_too_big(cbuf *cb, type) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -207,7 +207,7 @@ netconf_missing_attribute(cbuf *cb, type, info) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -248,7 +248,7 @@ netconf_bad_attribute(cbuf *cb, type, info) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -290,7 +290,7 @@ netconf_unknown_attribute(cbuf *cb, type, info) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -332,7 +332,7 @@ netconf_missing_element(cbuf *cb, type, info) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -375,7 +375,7 @@ netconf_bad_element(cbuf *cb, type, info) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -417,7 +417,7 @@ netconf_unknown_element(cbuf *cb, type, info) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -459,7 +459,7 @@ netconf_unknown_namespace(cbuf *cb, type, info) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -498,7 +498,7 @@ netconf_access_denied(cbuf *cb, type) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -574,7 +574,7 @@ netconf_lock_denied(cbuf *cb, info) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -613,7 +613,7 @@ netconf_resource_denied(cbuf *cb, type) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -653,7 +653,7 @@ netconf_rollback_failed(cbuf *cb, type) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -691,7 +691,7 @@ netconf_data_exists(cbuf *cb, "error") <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -729,7 +729,7 @@ netconf_data_missing(cbuf *cb, "error") <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -769,7 +769,7 @@ netconf_operation_not_supported(cbuf *cb, type) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -809,7 +809,7 @@ netconf_operation_failed(cbuf *cb, type) <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -884,7 +884,7 @@ netconf_malformed_message(cbuf *cb, "error") <0) goto err; if (message){ - if (xml_chardata_encode(message, &encstr) < 0) + if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; if (cprintf(cb, "%s", encstr) < 0) goto err; @@ -970,3 +970,40 @@ netconf_trymerge(cxobj *x, free(reason); return retval; } + +/*! Load ietf netconf yang module and set enabled features + * The features added are: + * candidate (8.3) + * validate (8.6) + * startup (8.7) + * xpath (8.9) + */ +int +netconf_module_load(clicon_handle h) +{ + int retval = -1; + cxobj *xc; + // cxobj *x; + yang_spec *yspec; + + yspec = clicon_dbspec_yang(h); + /* Load yang spec */ + if (yang_spec_parse_module(h, "ietf-netconf", CLIXON_DATADIR, NULL, yspec)< 0) + goto done; + if ((xc = clicon_conf_xml(h)) == NULL){ + clicon_err(OE_CFG, ENOENT, "Clicon configuration not loaded"); + goto done; + } + /* Enable features (hardcoded here) */ + if (xml_parse_string("ietf-netconf:candidate", yspec, &xc) < 0) + goto done; + if (xml_parse_string("ietf-netconf:validate", yspec, &xc) < 0) + goto done; + if (xml_parse_string("ietf-netconf:startup", yspec, &xc) < 0) + goto done; + if (xml_parse_string("ietf-netconf:xpath", yspec, &xc) < 0) + goto done; + retval = 0; + done: + return retval; +} diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 1c0584d98..9fb9e3af7 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -249,8 +249,10 @@ clicon_options_main(clicon_handle h) xml_child_sort = 0; retval = 0; done: - if (yspec) /* The clixon yang-spec is not used after this */ +#if 0 /* XXX yspec should be part of top-level yang but cant since it will be main module */ + if (yspec) yspec_free(yspec); +#endif return retval; } @@ -589,6 +591,7 @@ clicon_dbspec_yang_set(clicon_handle h, /*! Get YANG specification for Clixon system options and features * Must use hash functions directly since they are not strings. + * Example: features are typically accessed directly in the config tree. */ cxobj * clicon_conf_xml(clicon_handle h) diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index 655bc9f1f..0b0f73dd5 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -174,49 +174,77 @@ uri_unreserved(unsigned char in) } /*! Percent encoding according to RFC 3986 URI Syntax - * @param[in] str Not-encoded input string - * @param[out] escp Encoded/escaped malloced output string + * @param[out] encp Encoded malloced output string + * @param[in] fmt Not-encoded input string (stdarg format string) + * @param[in] ... stdarg variable parameters * @retval 0 OK * @retval -1 Error + * @code + * char *enc; + * if (uri_percent_encode(&enc, "formatstr: <>= %s", "substr<>") < 0) + * err; + * if(enc) + * free(enc); + * @endcode * @see RFC 3986 Uniform Resource Identifier (URI): Generic Syntax * @see uri_percent_decode * @see xml_chardata_encode */ int -uri_percent_encode(char *str, - char **escp) +uri_percent_encode(char **encp, + char *fmt, ...) { - int retval = -1; - char *esc = NULL; - int len; - int i, j; - + int retval = -1; + char *str = NULL; /* Expanded format string w stdarg */ + char *enc = NULL; + int fmtlen; + int len; + int i, j; + va_list args; + + /* Two steps: (1) read in the complete format string */ + va_start(args, fmt); /* dryrun */ + fmtlen = vsnprintf(NULL, 0, fmt, args) + 1; + va_end(args); + if ((str = malloc(fmtlen)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(str, 0, fmtlen); + va_start(args, fmt); /* real */ + fmtlen = vsnprintf(str, fmtlen, fmt, args) + 1; + va_end(args); + /* Now str is the combined fmt + ... */ + + /* Step (2) encode and expand str --> enc */ /* This is max */ len = strlen(str)*3+1; - if ((esc = malloc(len)) == NULL){ + if ((enc = malloc(len)) == NULL){ clicon_err(OE_UNIX, errno, "malloc"); goto done; } - memset(esc, 0, len); + memset(enc, 0, len); j = 0; for (i=0; i 2 && - isxdigit(esc[i+1]) && isxdigit(esc[i+2])){ - hstr[0] = esc[i+1]; - hstr[1] = esc[i+2]; + for (i=0; i 2 && + isxdigit(enc[i+1]) && isxdigit(enc[i+2])){ + hstr[0] = enc[i+1]; + hstr[1] = enc[i+2]; hstr[2] = 0; str[j] = strtoul(hstr, &ptr, 16); i += 2; } else - str[j] = esc[i]; + str[j] = enc[i]; j++; } str[j++] = '\0'; @@ -265,8 +293,9 @@ uri_percent_decode(char *esc, } /*! Escape characters according to XML definition - * @param[in] str Not-encoded input string - * @param[out] escp Encoded/escaped malloced output string + * @param[out] encp Encoded malloced output string + * @param[in] fmt Not-encoded input string (stdarg format string) + * @param[in] ... stdarg variable parameters * @retval 0 OK * @retval -1 Error * @see https://www.w3.org/TR/2008/REC-xml-20081126/#syntax chapter 2.6 @@ -274,8 +303,7 @@ uri_percent_decode(char *esc, * @see AMPERSAND mode in clixon_xml_parse.l * @code * char *encstr = NULL; - * char *val = "a<>b"; - * if (xml_chardata_encode(str, &encstr) < 0) + * if (xml_chardata_encode(&encstr, "fmtstr<>& %s", "substr<>") < 0) * err; * if (encstr) * free(encstr); @@ -289,16 +317,34 @@ uri_percent_decode(char *esc, * Optionally > */ int -xml_chardata_encode(char *str, - char **escp) +xml_chardata_encode(char **escp, + char *fmt,...) { - int retval = -1; - char *esc = NULL; - int l; - int len; - int i, j; - int cdata; /* when set, skip encoding */ + int retval = -1; + char *str = NULL; /* Expanded format string w stdarg */ + int fmtlen; + char *esc = NULL; + int l; + int len; + int i, j; + int cdata; /* when set, skip encoding */ + va_list args; + /* Two steps: (1) read in the complete format string */ + va_start(args, fmt); /* dryrun */ + fmtlen = vsnprintf(NULL, 0, fmt, args) + 1; + va_end(args); + if ((str = malloc(fmtlen)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(str, 0, fmtlen); + va_start(args, fmt); /* real */ + fmtlen = vsnprintf(str, fmtlen, fmt, args) + 1; + va_end(args); + /* Now str is the combined fmt + ... */ + + /* Step (2) encode and expand str --> enc */ /* First compute length (do nothing) */ len = 0; cdata = 0; for (i=0; i]]>]]>" "^]]>]]>$" new "netconf disabled feature" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo]]>]]>" '^operation-failedprotocolerrorXML node config/A has no corresponding yang specification \(Invalid XML or wrong Yang spec?' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo]]>]]>" '^operation-failedprotocolerrorXML node config/A has no corresponding yang specification (Invalid XML or wrong Yang spec?' +# This is difficult test since changes to the module list are frequent new "netconf schema resource, RFC 7895" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^exampleAietf-inet-types2013-07-15urn:ietf:params:xml:ns:yang:ietf-inet-typesietf-interfaces2014-05-08urn:ietf:params:xml:ns:yang:ietf-interfacesietf-routing2014-10-26urn:ietf:params:xml:ns:yang:ietf-routingrouter-idietf-yang-library2016-06-21urn:ietf:params:xml:ns:yang:ietf-yang-libraryietf-yang-types2013-07-15urn:ietf:params:xml:ns:yang:ietf-yang-types]]>]]>$' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^exampleAietf-inet-types2013-07-15urn:ietf:params:xml:ns:yang:ietf-inet-typesietf-interfaces2014-05-08urn:ietf:params:xml:ns:yang:ietf-interfacesietf-netconf2011-06-01urn:ietf:params:xml:ns:netconf:base:1.0ietf-routing2014-10-26urn:ietf:params:xml:ns:yang:ietf-routingrouter-idietf-yang-library2016-06-21urn:ietf:params:xml:ns:yang:ietf-yang-libraryietf-yang-types2013-07-15urn:ietf:params:xml:ns:yang:ietf-yang-types]]>]]>$' new "Kill backend" # kill backend diff --git a/test/test_netconf.sh b/test/test_netconf.sh index df5ac83dd..68efcbdc2 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -98,8 +98,8 @@ if [ $? -ne 0 ]; then fi new "netconf hello" -expecteof "$clixon_netconf -f $cfg -y $fyang" 0 ']]>]]>' '^urn:ietf:params:netconf:capability:yang-library:1.0\?revision="2016-06-21"\&module-set-id=42' -exit +expecteof "$clixon_netconf -f $cfg -y $fyang" 0 ']]>]]>' '^urn:ietf:params:netconf:base:1.1urn:ietf:params:netconf:capability:yang-library:1.0?revision="2016-06-21"& module-set-id=42[0-9]*]]>]]>]]>]]>$' + new "netconf get-config" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^]]>]]>$' @@ -112,6 +112,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" + # Too many quotes, (single inside double inside single) need to fool bash cat < $tmp # new ]]>]]> @@ -243,9 +244,6 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" new "netconf client-side rpc" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "example]]>]]>" "^ok]]>]]>$" -new "netconf subscription" -expectwait "$clixon_netconf -qf $cfg -y $fyang" "NETCONF]]>]]>" '^]]>]]>201' 10 - new "Kill backend" # kill backend sudo clixon_backend -zf $cfg