* NACM extension (RFC8341)

* NACM module support (RFC8341 A1+A2)
   * Recovery user "_nacm_recovery" added.
     * Example use is restconf PUT when NACM edit-config is permitted, then automatic commit and discard are permitted using recovery user.
   * Example user changed adm1 to andy to comply with RFC8341 example

 * Yang code upgrade (RFC7950)
   * RPC method input parameters validated
     * see https://github.com/clicon/clixon/issues/4
* Correct XML namespace handling
   * XML multiple modules was based on "loose" semantics so that yang modules were found by iterating thorugh namespaces until a match was made. This did not adhere to proper [XML namespace handling](https://www.w3.org/TR/2009/REC-xml-names-20091208), and causes problems with overlapping names and false positives. Below see XML accepted (but wrong), and correct namespace declaration:
```
      <rpc><my-own-method></rpc> # Wrong but accepted
      <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> # Correct
        <my-own-method xmlns="http://example.net/me/my-own/1.0">
      </rpc>
```
   * To keep old loose semantics set config option CLICON_XML_NS_ITERATE (true by default)
   * XML to JSON translator support for mapping xmlns attribute to module name prefix.
   * Default namespace is still "urn:ietf:params:xml:ns:netconf:base:1.0"
   * See https://github.com/clicon/clixon/issues/49
* Changed all make tags --> make TAGS
* Keyvalue datastore removed (it has been disabled since 3.3.3)
* debug rpc added in example application (should be in clixon-config).
This commit is contained in:
Olof hagsand 2018-12-16 19:46:26 +01:00
parent e5c0b06cf9
commit ae1af8da9e
63 changed files with 1852 additions and 3492 deletions

View file

@ -108,17 +108,13 @@
* - Namespace name: For a name N in a namespace identified by a URI I, the
* "namespace name" is I.
* For a name N that is not in a namespace, the "namespace name" has no value.
* - Local name: In either case the "local name" is N.
* - Local name: In either case the "local name" is N (also "prefix")
* It is this combination of the universally managed URI namespace with the
* vocabulary's local names that is effective in avoiding name clashes.
*/
struct xml{
char *x_name; /* name of node */
char *x_namespace; /* namespace, if any */
#ifdef notyet
char *x_namespacename; /* namespace name (or NULL) */
char *x_localname; /* Local name N as defined above */
#endif
char *x_prefix; /* namespace localname N, called prefix */
struct xml *x_up; /* parent node in hierarchy if any */
struct xml **x_childvec; /* vector of children nodes */
int x_childvec_len;/* length of vector */
@ -130,6 +126,17 @@ struct xml{
reference, dont free */
};
/*
* Variables
*/
/* Iterate through modules to find the matching datanode
* or rpc if no xmlns attribute specifies namespace.
* This is loose semantics of finding namespaces.
* And it is wrong, but is the way Clixon originally was written."
* @see CLICON_XML_NS_ITERATE clixon configure option
*/
int _CLICON_XML_NS_ITERATE = 0;
/* Mapping between xml type <--> string */
static const map_str2int xsmap[] = {
{"error", CX_ERROR},
@ -189,29 +196,31 @@ xml_name_set(cxobj *xn,
/*! Get namespace of xnode
* @param[in] xn xml node
* @retval namespace of xml node
* XXX change to xml_localname
*/
char*
xml_namespace(cxobj *xn)
{
return xn->x_namespace;
return xn->x_prefix;
}
/*! Set name space of xnode, namespace is copied
* @param[in] xn xml node
* @param[in] namespace new namespace, null-terminated string, copied by function
* @param[in] localname new namespace, null-terminated string, copied by function
* @retval -1 on error with clicon-err set
* @retval 0 OK
* XXX change to xml_localname_set
*/
int
xml_namespace_set(cxobj *xn,
char *namespace)
char *localname)
{
if (xn->x_namespace){
free(xn->x_namespace);
xn->x_namespace = NULL;
if (xn->x_prefix){
free(xn->x_prefix);
xn->x_prefix = NULL;
}
if (namespace){
if ((xn->x_namespace = strdup(namespace)) == NULL){
if (localname){
if ((xn->x_prefix = strdup(localname)) == NULL){
clicon_err(OE_XML, errno, "strdup");
return -1;
}
@ -219,12 +228,57 @@ xml_namespace_set(cxobj *xn,
return 0;
}
/*! See if xmlns:<namespace>=<uri> exists, if so return <uri>
/*! Given an xml tree return URI namespace: default or localname given
*
* Given an XML tree and a prefix (or NULL) return URI namespace.
* @param[in] x XML tree
* @param[in] prefix prefix/ns localname. If NULL then return default.
* @param[out] namespace URI namespace (or NULL). Note pointer into xml tree
* @retval 0 OK
* @retval -1 Error
* @see xmlns_check XXX coordinate
*/
int
xml2ns(cxobj *x,
char *prefix,
char **namespace)
{
int retval = -1;
char *ns;
cxobj *xp;
if (prefix != NULL) /* xmlns:<prefix> */
ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
else /* default ns */
ns = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
/* namespace not found, try parent */
if (ns == NULL){
if ((xp = xml_parent(x)) != NULL){
if (xml2ns(xp, prefix, &ns) < 0)
goto done;
}
/* If no parent, return default namespace if defined */
#if defined(DEFAULT_XML_RPC_NAMESPACE)
else
ns = DEFAULT_XML_RPC_NAMESPACE;
#endif
}
if (namespace)
*namespace = ns;
retval = 0;
done:
return retval;
}
/*! See if xmlns:[<localname>=]<uri> exists, if so return <uri>
*
* @param[in] xn XML node
* @param[in] nsn Namespace name
* @retval URI return associated URI if found
* @retval NULL No namespace name binding found for nsn
* @see xml2ns XXX coordinate
*/
static char *
xmlns_check(cxobj *xn,
@ -233,7 +287,7 @@ xmlns_check(cxobj *xn,
cxobj *x = NULL;
char *xns;
while ((x = xml_child_each(xn, x, -1)) != NULL)
while ((x = xml_child_each(xn, x, CX_ATTR)) != NULL)
if ((xns = xml_namespace(x)) && strcmp(xns, "xmlns")==0 &&
strcmp(xml_name(x), nsn) == 0)
return xml_value(x);
@ -248,7 +302,7 @@ xmlns_check(cxobj *xn,
* @note This function is grossly inefficient
*/
static int
xml_namespace_check(cxobj *xn,
xml_localname_check(cxobj *xn,
void *arg)
{
cxobj *xp = NULL;
@ -886,6 +940,42 @@ xml_body_get(cxobj *xt)
return NULL;
}
/*! Find and return the value of an xml child of specific type
*
* The value can be of an attribute only
* @param[in] xt xml tree node
* @param[in] prefix Prefix (namespace local name) or NULL
* @param[in] name name of xml tree node (eg attr name or "body")
* @retval val Pointer to the name string
* @retval NULL No such node or no value in node
* @code
* char *str = xml_find_type_value(x, "prefix", "name", CX_ATTR);
* @endcode
* @note, make a copy of the return value to use it properly
* @see xml_find_value where a body can be found as well
*/
char *
xml_find_type_value(cxobj *xt,
char *prefix,
char *name,
enum cxobj_type type)
{
cxobj *x = NULL;
int pmatch; /* prefix match */
char *xprefix; /* xprefix */
while ((x = xml_child_each(xt, x, type)) != NULL) {
xprefix = xml_namespace(x);
if (prefix)
pmatch = xprefix?strcmp(prefix,xprefix)==0:0;
else
pmatch = 1;
if (pmatch && strcmp(name, xml_name(x)) == 0)
return xml_value(x);
}
return NULL;
}
/*! Find and return the value of a sub xml node
*
* The value can be of an attribute or body.
@ -895,7 +985,7 @@ xml_body_get(cxobj *xt)
* @retval NULL No such node or no value in node
*
* Note, make a copy of the return value to use it properly
* See also xml_find_body
* @see xml_find_body
* Explaining picture:
* xt --> x
* x_name=name
@ -983,8 +1073,8 @@ xml_free(cxobj *x)
free(x->x_name);
if (x->x_value)
free(x->x_value);
if (x->x_namespace)
free(x->x_namespace);
if (x->x_prefix)
free(x->x_prefix);
for (i=0; i<x->x_childvec_len; i++){
if ((xc = x->x_childvec[i]) != NULL){
xml_free(xc);
@ -1028,6 +1118,8 @@ clicon_xml2file(FILE *f,
char *val;
char *encstr = NULL; /* xml encoded string */
if (x == NULL)
goto ok;
name = xml_name(x);
namespace = xml_namespace(x);
switch(xml_type(x)){
@ -1097,6 +1189,7 @@ clicon_xml2file(FILE *f,
default:
break;
}/* switch */
ok:
retval = 0;
done:
if (encstr)
@ -1302,7 +1395,7 @@ _xml_parse(const char *str,
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
goto done;
/* Verify namespaces after parsing */
if (xml_apply0(xt, CX_ELMNT, xml_namespace_check, NULL) < 0)
if (xml_apply0(xt, CX_ELMNT, xml_localname_check, NULL) < 0)
goto done;
/* Sort the complete tree after parsing */
if (yspec){
@ -1507,15 +1600,20 @@ int
xml_copy_one(cxobj *x0,
cxobj *x1)
{
char *s;
xml_type_set(x1, xml_type(x0));
if (xml_value(x0)){ /* malloced string */
if ((x1->x_value = strdup(x0->x_value)) == NULL){
if ((s = xml_value(x0))){ /* malloced string */
if ((x1->x_value = strdup(s)) == NULL){
clicon_err(OE_XML, errno, "strdup");
return -1;
}
}
if (xml_name(x0)) /* malloced string */
if ((xml_name_set(x1, xml_name(x0))) < 0)
if ((s = xml_name(x0))) /* malloced string */
if ((xml_name_set(x1, s)) < 0)
return -1;
if ((s = xml_namespace(x0))) /* malloced string */
if ((xml_namespace_set(x1, s)) < 0)
return -1;
return 0;
}
@ -1794,7 +1892,6 @@ xml_body_parse(cxobj *xb,
if (retval < 0 && cv != NULL)
cv_free(cv);
return retval;
}
/*! Parse an xml body as int32