* Added xmlns validation
* for eg <a xmlns:x="uri"><x:b/></a>
This commit is contained in:
parent
39538461c2
commit
849e46191e
6 changed files with 124 additions and 52 deletions
|
|
@ -8,6 +8,8 @@
|
||||||
* Example extended with inclusion of iana-if-type RFC 7224 interface identities
|
* Example extended with inclusion of iana-if-type RFC 7224 interface identities
|
||||||
* Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified.
|
* Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified.
|
||||||
### Minor changes:
|
### Minor changes:
|
||||||
|
* Added xmlns validation
|
||||||
|
* for eg <a xmlns:x="uri"><x:b/></a>
|
||||||
* Added yang identityref runtime validation
|
* Added yang identityref runtime validation
|
||||||
* Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h.
|
* Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h.
|
||||||
* Replace functions as follows in CLI SPEC files:
|
* Replace functions as follows in CLI SPEC files:
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ typedef struct xml cxobj; /* struct defined in clicon_xml.c */
|
||||||
* @retval 1 Abort, dont continue with others
|
* @retval 1 Abort, dont continue with others
|
||||||
* @retval 2 Locally, just abort this subtree, continue with others
|
* @retval 2 Locally, just abort this subtree, continue with others
|
||||||
*/
|
*/
|
||||||
typedef int (xml_applyfn_t)(cxobj *yn, void *arg);
|
typedef int (xml_applyfn_t)(cxobj *x, void *arg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* xml_flag() flags:
|
* xml_flag() flags:
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,7 @@ rpc_callback_register(clicon_handle h,
|
||||||
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
memset (rc, 0, sizeof (*rc));
|
memset(rc, 0, sizeof(*rc));
|
||||||
rc->rc_callback = cb;
|
rc->rc_callback = cb;
|
||||||
rc->rc_arg = arg;
|
rc->rc_arg = arg;
|
||||||
rc->rc_tag = strdup(tag); /* XXX strdup memleak */
|
rc->rc_tag = strdup(tag); /* XXX strdup memleak */
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ struct xml{
|
||||||
enum cxobj_type x_type; /* type of node: element, attribute, body */
|
enum cxobj_type x_type; /* type of node: element, attribute, body */
|
||||||
char *x_value; /* attribute and body nodes have values */
|
char *x_value; /* attribute and body nodes have values */
|
||||||
int _x_vector_i; /* internal use: xml_child_each */
|
int _x_vector_i; /* internal use: xml_child_each */
|
||||||
int x_flags; /* Flags according to XML_FLAG_* above */
|
int x_flags; /* Flags according to XML_FLAG_* */
|
||||||
yang_stmt *x_spec; /* Pointer to specification, eg yang, by
|
yang_stmt *x_spec; /* Pointer to specification, eg yang, by
|
||||||
reference, dont free */
|
reference, dont free */
|
||||||
cg_var *x_cv; /* If body this contains the typed value */
|
cg_var *x_cv; /* If body this contains the typed value */
|
||||||
|
|
@ -181,7 +181,7 @@ xml_namespace(cxobj *xn)
|
||||||
return xn->x_namespace;
|
return xn->x_namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Set name of xnode, name is copied
|
/*! Set name space of xnode, namespace is copied
|
||||||
* @param[in] xn xml node
|
* @param[in] xn xml node
|
||||||
* @param[in] namespace new namespace, null-terminated string, copied by function
|
* @param[in] namespace new namespace, null-terminated string, copied by function
|
||||||
* @retval -1 on error with clicon-err set
|
* @retval -1 on error with clicon-err set
|
||||||
|
|
@ -204,6 +204,71 @@ xml_namespace_set(cxobj *xn,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! See if xmlns:<namespace>=<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
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
xmlns_check(cxobj *xn,
|
||||||
|
char *nsn)
|
||||||
|
{
|
||||||
|
cxobj *x = NULL;
|
||||||
|
char *xns;
|
||||||
|
|
||||||
|
while ((x = xml_child_each(xn, x, -1)) != NULL)
|
||||||
|
if ((xns = xml_namespace(x)) && strcmp(xns, "xmlns")==0 &&
|
||||||
|
strcmp(xml_name(x), nsn) == 0)
|
||||||
|
return xml_value(x);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Check namespace of xml node by searhing recursively among ancestors
|
||||||
|
* @param[in] xn xml node
|
||||||
|
* @param[in] namespace check validity of namespace
|
||||||
|
* @retval 0 Found / validated or no yang spec
|
||||||
|
* @retval -1 Not found
|
||||||
|
* @note This function is grossly inefficient
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
xml_namespace_check(cxobj *xn,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
cxobj *xp = NULL;
|
||||||
|
char *nsn;
|
||||||
|
char *n;
|
||||||
|
yang_stmt *ys = xml_spec(xn);
|
||||||
|
|
||||||
|
/* No namespace name - comply */
|
||||||
|
if ((nsn = xml_namespace(xn)) == NULL)
|
||||||
|
return 0;
|
||||||
|
/* Check if NSN defined in same node */
|
||||||
|
if (xmlns_check(xn, nsn) != NULL)
|
||||||
|
return 0;
|
||||||
|
/* Check if NSN defined in some ancestor */
|
||||||
|
while ((xp = xml_parent(xn)) != NULL) {
|
||||||
|
if (xmlns_check(xp, nsn) != NULL)
|
||||||
|
return 0;
|
||||||
|
xn = xp;
|
||||||
|
}
|
||||||
|
if (ys == NULL)
|
||||||
|
return 0; /* If no yang spec */
|
||||||
|
else{
|
||||||
|
/* Check if my namespace */
|
||||||
|
if ((n = yang_find_myprefix(ys)) != NULL && strcmp(nsn,n)==0)
|
||||||
|
return 0;
|
||||||
|
/* Check if any imported module */
|
||||||
|
if (yang_find_module_by_prefix(ys, nsn) != NULL)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Not found, error */
|
||||||
|
clicon_err(OE_XML, ENOENT, "Namespace name %s in %s:%s not found",
|
||||||
|
nsn, nsn, xml_name(xn));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Get parent of xnode
|
/*! Get parent of xnode
|
||||||
* @param[in] xn xml node
|
* @param[in] xn xml node
|
||||||
* @retval parent xml node
|
* @retval parent xml node
|
||||||
|
|
@ -869,7 +934,6 @@ xml_find_value(cxobj *xt,
|
||||||
* Explaining picture:
|
* Explaining picture:
|
||||||
* xt --> x --> bx (x_type=CX_BODY)
|
* xt --> x --> bx (x_type=CX_BODY)
|
||||||
* x_name=name return x_value
|
* x_name=name return x_value
|
||||||
|
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
xml_find_body(cxobj *xt,
|
xml_find_body(cxobj *xt,
|
||||||
|
|
@ -1247,6 +1311,9 @@ _xml_parse(const char *str,
|
||||||
goto done;
|
goto done;
|
||||||
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
|
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
|
||||||
goto done;
|
goto done;
|
||||||
|
/* Verify namespaces after parsing */
|
||||||
|
if (xml_apply0(xt, CX_ELMNT, xml_namespace_check, NULL) < 0)
|
||||||
|
goto done;
|
||||||
/* Sort the complete tree after parsing */
|
/* Sort the complete tree after parsing */
|
||||||
if (yspec){
|
if (yspec){
|
||||||
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
||||||
|
|
@ -1658,7 +1725,6 @@ xml_apply0(cxobj *xn,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Apply a function call recursively on all ancestors
|
/*! Apply a function call recursively on all ancestors
|
||||||
* Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for
|
* Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for
|
||||||
* each object found. The function is called with the xml node and an
|
* each object found. The function is called with the xml node and an
|
||||||
|
|
@ -1861,7 +1927,6 @@ xml_operation2str(enum operation_type op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Turn this on to get a xml parse and pretty print test program
|
* Turn this on to get a xml parse and pretty print test program
|
||||||
* Usage: xpath
|
* Usage: xpath
|
||||||
|
|
@ -1892,7 +1957,7 @@ main(int argc, char **argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (xml_parse_file(0, "</config>", NULL, &xt) < 0){
|
if (xml_parse_file(0, "</config>", NULL, &xt) < 0){
|
||||||
fprintf(stderr, "parsing 2\n");
|
fprintf(stderr, "xml parse error %s\n", clicon_err_reason);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
xc = NULL;
|
xc = NULL;
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,11 @@
|
||||||
|
|
||||||
%token <string> NAME CHARDATA
|
%token <string> NAME CHARDATA
|
||||||
%token VER ENC
|
%token VER ENC
|
||||||
%token BSLASH ESLASH
|
%token BSLASH ESLASH
|
||||||
%token BTEXT ETEXT
|
%token BTEXT ETEXT
|
||||||
%token BCOMMENT ECOMMENT
|
%token BCOMMENT ECOMMENT
|
||||||
|
|
||||||
|
%type <string> attvalue
|
||||||
%type <string> attvalue attqname
|
|
||||||
|
|
||||||
%lex-param {void *_ya} /* Add this argument to parse() and lex() function */
|
%lex-param {void *_ya} /* Add this argument to parse() and lex() function */
|
||||||
%parse-param {void *_ya}
|
%parse-param {void *_ya}
|
||||||
|
|
@ -117,7 +116,7 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
|
||||||
char *ver)
|
char *ver)
|
||||||
{
|
{
|
||||||
if(strcmp(ver, "1.0")){
|
if(strcmp(ver, "1.0")){
|
||||||
clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0\n", ver);
|
clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0", ver);
|
||||||
free(ver);
|
free(ver);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -125,15 +124,41 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Parse Qualified name
|
/*! Parse Qualified name --> Unprefixed name
|
||||||
* @param[in] ya XML parser yacc handler struct
|
* @param[in] ya XML parser yacc handler struct
|
||||||
* @param[in] prefix Prefix, namespace, or NULL
|
* @param[in] prefix Prefix, namespace, or NULL
|
||||||
* @param[in] localpart Name
|
* @param[in] localpart Name
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xml_parse_qname(struct xml_parse_yacc_arg *ya,
|
xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
|
||||||
char *prefix,
|
char *name)
|
||||||
char *name)
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *x;
|
||||||
|
yang_stmt *y = NULL; /* yang node */
|
||||||
|
cxobj *xp; /* xml parent */
|
||||||
|
|
||||||
|
xp = ya->ya_xparent;
|
||||||
|
if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0)
|
||||||
|
goto done;
|
||||||
|
if ((x = xml_new(name, xp, y)) == NULL)
|
||||||
|
goto done;
|
||||||
|
ya->ya_xelement = x;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
free(name);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Parse Qualified name -> PrefixedName
|
||||||
|
* @param[in] ya XML parser yacc handler struct
|
||||||
|
* @param[in] prefix Prefix, namespace, or NULL
|
||||||
|
* @param[in] localpart Name
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya,
|
||||||
|
char *prefix,
|
||||||
|
char *name)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
|
|
@ -197,7 +222,7 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (xml_namespace(x)!=NULL){
|
if (xml_namespace(x)!=NULL){
|
||||||
clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s\n",
|
clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s",
|
||||||
xml_namespace(x), xml_name(x), name);
|
xml_namespace(x), xml_name(x), name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -235,7 +260,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
|
|
||||||
if (strcmp(xml_name(x), name)){
|
if (strcmp(xml_name(x), name)){
|
||||||
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
|
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
|
||||||
xml_namespace(x),
|
xml_namespace(x),
|
||||||
xml_name(x),
|
xml_name(x),
|
||||||
namespace,
|
namespace,
|
||||||
|
|
@ -244,7 +269,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
|
||||||
}
|
}
|
||||||
if (xml_namespace(x)==NULL ||
|
if (xml_namespace(x)==NULL ||
|
||||||
strcmp(xml_namespace(x), namespace)){
|
strcmp(xml_namespace(x), namespace)){
|
||||||
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
|
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
|
||||||
xml_namespace(x),
|
xml_namespace(x),
|
||||||
xml_name(x),
|
xml_name(x),
|
||||||
namespace,
|
namespace,
|
||||||
|
|
@ -276,44 +301,29 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
xml_parse_attr(struct xml_parse_yacc_arg *ya,
|
xml_parse_attr(struct xml_parse_yacc_arg *ya,
|
||||||
char *qname,
|
char *prefix,
|
||||||
|
char *name,
|
||||||
char *attval)
|
char *attval)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xa;
|
cxobj *xa;
|
||||||
|
|
||||||
if ((xa = xml_new(qname, ya->ya_xelement, NULL)) == NULL)
|
if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xml_type_set(xa, CX_ATTR);
|
xml_type_set(xa, CX_ATTR);
|
||||||
|
if (prefix && xml_namespace_set(xa, prefix) < 0)
|
||||||
|
goto done;
|
||||||
if (xml_value_set(xa, attval) < 0)
|
if (xml_value_set(xa, attval) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
free(qname);
|
free(name);
|
||||||
|
if (prefix)
|
||||||
|
free(prefix);
|
||||||
free(attval);
|
free(attval);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Parse Attribue Qualified name, Just transform prefix:name into a new string
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static char*
|
|
||||||
xml_merge_attqname(struct xml_parse_yacc_arg *ya,
|
|
||||||
char *prefix,
|
|
||||||
char *name)
|
|
||||||
{
|
|
||||||
char *str;
|
|
||||||
int len = strlen(prefix)+strlen(name)+2;
|
|
||||||
|
|
||||||
if ((str=malloc(len)) == NULL)
|
|
||||||
return NULL;
|
|
||||||
snprintf(str, len, "%s:%s", prefix, name);
|
|
||||||
free(prefix);
|
|
||||||
free(name);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
@ -344,9 +354,9 @@ element : '<' qname attrs element1
|
||||||
{ clicon_debug(3, "element -> < qname attrs element1"); }
|
{ clicon_debug(3, "element -> < qname attrs element1"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
qname : NAME { if (xml_parse_qname(_YA, NULL, $1) < 0) YYABORT;
|
qname : NAME { if (xml_parse_unprefixed_name(_YA, $1) < 0) YYABORT;
|
||||||
clicon_debug(3, "qname -> NAME %s", $1);}
|
clicon_debug(3, "qname -> NAME %s", $1);}
|
||||||
| NAME ':' NAME { if (xml_parse_qname(_YA, $1, $3) < 0) YYABORT;
|
| NAME ':' NAME { if (xml_parse_prefixed_name(_YA, $1, $3) < 0) YYABORT;
|
||||||
clicon_debug(3, "qname -> NAME : NAME");}
|
clicon_debug(3, "qname -> NAME : NAME");}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -385,15 +395,10 @@ attrs : attrs attr
|
||||||
|
|
|
|
||||||
;
|
;
|
||||||
|
|
||||||
attr : attqname '=' attvalue { if (xml_parse_attr(_YA, $1, $3) < 0) YYABORT; }
|
attr : NAME '=' attvalue { if (xml_parse_attr(_YA, NULL, $1, $3) < 0) YYABORT; }
|
||||||
|
| NAME ':' NAME '=' attvalue { if (xml_parse_attr(_YA, $1, $3, $5) < 0) YYABORT; }
|
||||||
;
|
;
|
||||||
|
|
||||||
attqname : NAME {$$ = $1;}
|
|
||||||
| NAME ':' NAME
|
|
||||||
{ if (($$ = xml_merge_attqname(_YA, $1, $3)) == NULL) YYABORT; }
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
attvalue : '\"' CHARDATA '\"' { $$=$2; /* $2 must be consumed */}
|
attvalue : '\"' CHARDATA '\"' { $$=$2; /* $2 must be consumed */}
|
||||||
| '\"' '\"' { $$=strdup(""); /* $2 must be consumed */}
|
| '\"' '\"' { $$=strdup(""); /* $2 must be consumed */}
|
||||||
;
|
;
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ new "cli configure using encoded chars name <&"
|
||||||
expectfn "$clixon_cli -1 -f $cfg set interfaces interface fddi&< type ianaift:ethernetCsmacd" 0 ""
|
expectfn "$clixon_cli -1 -f $cfg set interfaces interface fddi&< type ianaift:ethernetCsmacd" 0 ""
|
||||||
|
|
||||||
new "cli failed validate"
|
new "cli failed validate"
|
||||||
expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "Missing mandatory variable"
|
expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Missing mandatory variable"
|
||||||
|
|
||||||
new "cli configure more"
|
new "cli configure more"
|
||||||
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" 0 "^$"
|
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" 0 "^$"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue