session CHANGE check libyang validation error in more details

When receiving RPC, check for specific errors and return NETCONF
error message to the client immediately. So far a generic error
message was returned which was against NETCONF spec.
diff --git a/src/session_server.c b/src/session_server.c
index fa1ad85..e4a7e05 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -622,6 +622,11 @@
 {
     struct lyxml_elem *xml = NULL;
     NC_MSG_TYPE msgtype;
+    struct nc_server_reply *reply = NULL;
+    struct nc_server_error *e = NULL;
+    const char *str, *stri, *strj;
+    char *attr;
+    int ret;
 
     if (!session || !rpc) {
         ERRARG;
@@ -635,16 +640,66 @@
 
     switch (msgtype) {
     case NC_MSG_RPC:
-        *rpc = malloc(sizeof **rpc);
+        *rpc = calloc(1, sizeof **rpc);
         if (!*rpc) {
             ERRMEM;
             goto error;
         }
 
+        ly_errno = LY_SUCCESS;
         (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
         if (!(*rpc)->tree) {
-            ERR("Session %u: received message failed to be parsed into a known RPC.", session->id);
-            msgtype = NC_MSG_NONE;
+            /* parsing RPC failed */
+            if (ly_errno == LY_EVALID) {
+                switch (ly_vecode) {
+                case LYVE_INELEM:
+                    str = ly_errpath();
+                    if (!strcmp(str, "/")) {
+                        e = nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_APP);
+                        goto skiplymsg;
+                    } else {
+                        e = nc_err(NC_ERR_UNKNOWN_ELEM, NC_ERR_TYPE_PROT, ly_errpath());
+                    }
+                    break;
+                case LYVE_MISSELEM:
+                case LYVE_INORDER:
+                    e = nc_err(NC_ERR_MISSING_ELEM, NC_ERR_TYPE_PROT, ly_errpath());
+                    break;
+                case LYVE_INVAL:
+                    e = nc_err(NC_ERR_BAD_ELEM, NC_ERR_TYPE_PROT, ly_errpath());
+                    break;
+                case LYVE_INATTR:
+                case LYVE_MISSATTR:
+                    str = ly_errmsg();
+                    stri = strchr(str, '"'); stri++;
+                    strj = strchr(stri, '"'); strj--;
+                    attr = strndup(stri, strj - stri);
+                    e = nc_err(ly_vecode == LYVE_INATTR ? NC_ERR_UNKNOWN_ATTR : NC_ERR_MISSING_ATTR,
+                               NC_ERR_TYPE_PROT, attr, ly_errpath());
+                    free(attr);
+                    break;
+                case LYVE_OORVAL:
+                case LYVE_NOCOND:
+                    e = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_PROT);
+                    break;
+                default:
+                    e = nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
+                    break;
+                }
+            } else {
+                e = nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
+            }
+            nc_err_set_msg(e, ly_errmsg(), "en");
+skiplymsg:
+            reply = nc_server_reply_err(e);
+            ret = nc_write_msg(session, NC_MSG_REPLY, (*rpc)->root, reply);
+            nc_server_reply_free(reply);
+            if (ret == -1) {
+                ERR("Session %u: failed to write reply.", session->id);
+                msgtype = NC_MSG_ERROR;
+            } else {
+                msgtype = NC_MSG_NONE;
+            }
         }
         (*rpc)->root = xml;
         break;
@@ -687,7 +742,7 @@
     }
 
     /* no callback, reply with a not-implemented error */
-    if (!rpc->tree || !rpc->tree->schema->priv) {
+    if (!rpc->tree->schema->priv) {
         reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
     } else {
         clb = (nc_rpc_clb)rpc->tree->schema->priv;
@@ -859,9 +914,12 @@
         }
         ret = -1;
         goto finish;
+    } else if (msgtype == NC_MSG_NONE) {
+        /* already processed, just stop further processing */
+        pthread_mutex_unlock(session->ti_lock);
+        goto done;
     }
 
-    /* NC_MSG_NONE is not a real (known) RPC */
     if (msgtype == NC_MSG_RPC) {
         session->last_rpc = time(NULL);
     }
@@ -876,6 +934,8 @@
         ret = -1;
         goto finish;
     }
+
+done:
     nc_server_rpc_free(rpc, server_opts.ctx);
 
     /* status change takes precedence over leftover events (return 2) */