@@ -1400,6 +1400,96 @@ recv_rpc_check_msgid(struct nc_session *session, const struct lyd_node *envp)
14001400 return NC_MSG_RPC ;
14011401}
14021402
1403+ /**
1404+ * @brief Find lysc node mentioned in schema_path.
1405+ *
1406+ * @param[in] ctx libyang context.
1407+ * @param[in] ly_err last libyang error.
1408+ * @return lysc node.
1409+ */
1410+ static const struct lysc_node *
1411+ nc_rpc_err_find_lysc_node (const struct ly_ctx * ctx , const struct ly_err_item * ly_err )
1412+ {
1413+ char * str , * last ;
1414+ const struct lysc_node * cn ;
1415+
1416+ if (!ly_err -> schema_path ) {
1417+ return NULL ;
1418+ }
1419+
1420+ str = strdup (ly_err -> schema_path );
1421+ if (!str ) {
1422+ return NULL ;
1423+ }
1424+ last = strrchr (str , '/' );
1425+ if (strchr (last , '@' )) {
1426+ /* ignore attribute part */
1427+ * last = '\0' ;
1428+ }
1429+ cn = lys_find_path (ctx , NULL , str , 0 );
1430+ free (str );
1431+
1432+ return cn ;
1433+ }
1434+
1435+ /**
1436+ * @brief Find the nth substring delimited by quotes.
1437+ *
1438+ * For example: abcd"ef"ghij"kl"mn -> index 0 is "ef", index 1 is "kl".
1439+ *
1440+ * @param[in] msg Input string with quoted substring.
1441+ * @param[in] index Number starting from 0 specifying the nth substring.
1442+ * @return Copied nth substring without quotes.
1443+ */
1444+ static char *
1445+ nc_rpc_err_get_quoted_string (const char * msg , uint32_t index )
1446+ {
1447+ char * ret ;
1448+ const char * start = NULL , * end = NULL , * iter , * tmp ;
1449+ uint32_t quote_cnt = 0 , last_quote ;
1450+
1451+ assert (msg );
1452+
1453+ last_quote = (index + 1 ) * 2 ;
1454+ for (iter = msg ; * iter ; ++ iter ) {
1455+ if (* iter != '\"' ) {
1456+ continue ;
1457+ }
1458+ /* updating the start and end pointers - swap */
1459+ tmp = end ;
1460+ end = iter ;
1461+ start = tmp ;
1462+ if (++ quote_cnt == last_quote ) {
1463+ /* nth substring found */
1464+ break ;
1465+ }
1466+ }
1467+
1468+ if (!start ) {
1469+ return NULL ;
1470+ }
1471+
1472+ /* Skip first quote */
1473+ ++ start ;
1474+ /* Copy substring */
1475+ ret = strndup (start , end - start );
1476+
1477+ return ret ;
1478+ }
1479+
1480+ /**
1481+ * @brief Check that the @p str starts with the @p prefix.
1482+ *
1483+ * @param[in] prefix Required prefix.
1484+ * @param[in] str Input string to check.
1485+ * @return True if @p str start with @p prefix otherwise False.
1486+ */
1487+ static ly_bool
1488+ nc_strstarts (const char * prefix , const char * str )
1489+ {
1490+ return strncmp (str , prefix , strlen (prefix )) == 0 ;
1491+ }
1492+
14031493/**
14041494 * @brief Prepare reply for rpc error.
14051495 *
@@ -1410,28 +1500,84 @@ recv_rpc_check_msgid(struct nc_session *session, const struct lyd_node *envp)
14101500static struct nc_server_reply *
14111501nc_server_prepare_rpc_err (struct nc_session * session , struct lyd_node * envp )
14121502{
1413- struct lyd_node * node ;
1503+ struct lyd_node * reply = NULL ;
1504+ const struct lysc_node * cn ;
14141505 const struct ly_err_item * ly_err ;
1506+ NC_ERR_TYPE errtype ;
1507+ const char * attr ;
1508+ char * str = NULL , * errmsg = NULL , * schema_path = NULL ;
1509+ LY_ERR errcode ;
14151510
1511+ /* envelope was not parsed */
14161512 if (!envp && (session -> version != NC_VERSION_11 )) {
14171513 return NULL ;
14181514 }
1419-
14201515 ly_err = ly_err_last (session -> ctx );
1421- if (envp ) {
1422- /* at least the envelopes were parsed */
1423- node = nc_err (session -> ctx , NC_ERR_OP_FAILED , NC_ERR_TYPE_APP );
1424- nc_err_set_msg (node , ly_err -> msg , "en" );
1425- } else if (!strcmp ("Missing XML namespace." , ly_err -> msg )) {
1426- node = nc_err (session -> ctx , NC_ERR_MISSING_ATTR , NC_ERR_TYPE_RPC , "xmlns" , "rpc" );
1427- nc_err_set_msg (node , ly_err -> msg , "en" );
1428- } else {
1516+ if (!envp && !strcmp ("Missing XML namespace." , ly_err -> msg )) {
1517+ reply = nc_err (session -> ctx , NC_ERR_MISSING_ATTR , NC_ERR_TYPE_RPC , "xmlns" , "rpc" );
1518+ goto cleanup ;
1519+ } else if (!envp ) {
14291520 /* completely malformed message, NETCONF version 1.1 defines sending error reply from
14301521 * the server (RFC 6241 sec. 3) */
1431- node = nc_err (session -> ctx , NC_ERR_MALFORMED_MSG );
1522+ reply = nc_err (session -> ctx , NC_ERR_MALFORMED_MSG );
1523+ return nc_server_reply_err (reply );
14321524 }
1525+ /* at least the envelopes were parsed */
1526+ assert (envp );
1527+
1528+ /* store strings, to avoid overwriting ly_err */
1529+ errmsg = strdup (ly_err -> msg );
1530+ if (!errmsg ) {
1531+ reply = nc_err (session -> ctx , NC_ERR_OP_FAILED , NC_ERR_TYPE_APP );
1532+ goto cleanup ;
1533+ }
1534+ if (ly_err -> schema_path ) {
1535+ schema_path = strdup (ly_err -> schema_path );
1536+ if (!schema_path ) {
1537+ reply = nc_err (session -> ctx , NC_ERR_OP_FAILED , NC_ERR_TYPE_APP );
1538+ goto cleanup ;
1539+ }
1540+ }
1541+ errcode = ly_err -> err ;
1542+
1543+ /* find out in which layer the error occurred */
1544+ cn = nc_rpc_err_find_lysc_node (session -> ctx , ly_err );
1545+ if (cn && ((cn -> nodetype & LYS_RPC ) || (cn -> nodetype & LYS_INPUT ))) {
1546+ errtype = NC_ERR_TYPE_PROT ;
1547+ } else {
1548+ errtype = NC_ERR_TYPE_APP ;
1549+ }
1550+
1551+ /* deciding which error to prepare */
1552+ if (cn && (nc_strstarts ("Missing mandatory prefix" , errmsg ) ||
1553+ nc_strstarts ("Unknown XML prefix" , errmsg ))) {
1554+ str = nc_rpc_err_get_quoted_string (errmsg , 1 );
1555+ reply = str ? nc_err (session -> ctx , NC_ERR_UNKNOWN_ATTR , errtype , str , cn -> name ) :
1556+ nc_err (session -> ctx , NC_ERR_OP_FAILED , NC_ERR_TYPE_APP );
1557+ } else if (cn && nc_strstarts ("Annotation definition for attribute" , errmsg )) {
1558+ attr = strrchr (schema_path , ':' ) + 1 ;
1559+ reply = nc_err (session -> ctx , NC_ERR_UNKNOWN_ATTR , errtype , attr , cn -> name );
1560+ } else if (nc_strstarts ("Invalid character sequence" , errmsg )) {
1561+ reply = nc_err (session -> ctx , NC_ERR_MALFORMED_MSG );
1562+ } else if (errcode == LY_EMEM ) {
1563+ /* <error-tag>resource-denied</error-tag> */
1564+ reply = nc_err (session -> ctx , NC_ERR_RES_DENIED , errtype );
1565+ } else {
1566+ /* prepare some generic error */
1567+ reply = nc_err (session -> ctx , NC_ERR_OP_FAILED , NC_ERR_TYPE_APP );
1568+ }
1569+
1570+ cleanup :
1571+ nc_err_set_msg (reply , errmsg , "en" );
1572+
1573+ /* clear for other errors */
1574+ ly_err_clean (session -> ctx , NULL );
1575+
1576+ free (errmsg );
1577+ free (schema_path );
1578+ free (str );
14331579
1434- return nc_server_reply_err (node );
1580+ return nc_server_reply_err (reply );
14351581}
14361582
14371583/* should be called holding the session RPC lock! IO lock will be acquired as needed
0 commit comments