diff --git a/CHANGELOG.md b/CHANGELOG.md
index ec4577b1..848e0937 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -73,6 +73,10 @@ Developers may need to change their code
### Minor features
+* Unified netconf input function
+ * Three different implementations were used in external, internal and controller code
+ * The new clixon_netconf_input API unifies all three uses
+ * Code still experimental controlled by `NEW_NETCONF_INPUT`
* RFC 8528 YANG schema mount
* Made cli/autocli mount-point-aware
* Internal NETCONF (client <-> backend)
diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c
index ed45fe8a..894c6dc8 100644
--- a/apps/netconf/netconf_main.c
+++ b/apps/netconf/netconf_main.c
@@ -77,10 +77,13 @@
/* clixon-data value to save buffer between invocations.
* Saving data may be necessary if socket buffer contains partial netconf messages, such as:
* ..wait 1min ]]>]]>
+ * XXX move to data
*/
-#define NETCONF_HASH_BUF "netconf_input_cbuf"
-#define NETCONF_FRAME_STATE "netconf_input_frame_state"
-#define NETCONF_FRAME_SIZE "netconf_input_frame_size"
+/* Unfinished frame */
+#define NETCONF_FRAME_MSG "netconf-frame-msg"
+
+#define NETCONF_FRAME_STATE "netconf-input-frame-state"
+#define NETCONF_FRAME_SIZE "netconf-input-frame-size"
/*! Ignore errors on packet errors: continue */
static int ignore_packet_errors = 1;
@@ -149,6 +152,7 @@ netconf_hello_msg(clicon_handle h,
int foundbase_11 = 0;
char *body;
+ clicon_debug(1, "%s", __FUNCTION__);
_netconf_hello_nr++;
if (xml_find_type(xn, NULL, "session-id", CX_ELMNT) != NULL) {
clicon_err(OE_XML, errno, "Server received hello with session-id from client, terminating (see RFC 6241 Sec 8.1");
@@ -166,12 +170,15 @@ netconf_hello_msg(clicon_handle h,
continue;
/* When comparing protocol version capability URIs, only the base part is used, in the
* event any parameters are encoded at the end of the URI string. */
- if (strncmp(body, NETCONF_BASE_CAPABILITY_1_0, strlen(NETCONF_BASE_CAPABILITY_1_0)) == 0) /* RFC 4741 */
+ if (strncmp(body, NETCONF_BASE_CAPABILITY_1_0, strlen(NETCONF_BASE_CAPABILITY_1_0)) == 0){ /* RFC 4741 */
foundbase_10++;
+ clicon_debug(1, "%s foundbase10", __FUNCTION__);
+ }
else if (strncmp(body, NETCONF_BASE_CAPABILITY_1_1, strlen(NETCONF_BASE_CAPABILITY_1_1)) == 0 &&
clicon_option_int(h, "CLICON_NETCONF_BASE_CAPABILITY") > 0){ /* RFC 6241 */
foundbase_11++;
- clicon_data_int_set(h, "netconf-framing", NETCONF_SSH_CHUNKED); /* enable chunked enc */
+ clicon_debug(1, "%s foundbase11", __FUNCTION__);
+ clicon_data_int_set(h, NETCONF_FRAMING_TYPE, NETCONF_SSH_CHUNKED); /* enable chunked enc */
}
}
}
@@ -208,7 +215,7 @@ netconf_rpc_message(clicon_handle h,
cxobj *xc;
netconf_framing_type framing;
- framing = clicon_data_int_get(h, "netconf-framing");
+ framing = clicon_data_int_get(h, NETCONF_FRAMING_TYPE);
if (_netconf_hello_nr == 0 &&
clicon_option_bool(h, "CLICON_NETCONF_HELLO_OPTIONAL") == 0){
if (netconf_operation_failed_xml(&xret, "rpc", "Client must send an hello element before any RPC")< 0)
@@ -319,9 +326,10 @@ netconf_input_packet(clicon_handle h,
netconf_framing_type framing;
clicon_debug(1, "%s", __FUNCTION__);
+ clicon_debug_xml(1, xreq, "%s", __FUNCTION__);
rpcname = xml_name(xreq);
rpcprefix = xml_prefix(xreq);
- framing = clicon_data_int_get(h, "netconf-framing");
+ framing = clicon_data_int_get(h, NETCONF_FRAMING_TYPE);
if (xml2ns(xreq, rpcprefix, &namespace) < 0)
goto done;
if (strcmp(rpcname, "rpc") == 0){
@@ -365,11 +373,14 @@ netconf_input_packet(clicon_handle h,
ok:
retval = 0;
done:
+ if (xret)
+ xml_free(xret);
if (cbret)
cbuf_free(cbret);
return retval;
}
+#ifndef NETCONF_INPUT_UNIFIED_EXTERN
/*! Process incoming frame, ie a char message framed by ]]>]]>
* Parse string to xml, check only one netconf message within a frame
* @param[in] h Clixon handle
@@ -390,9 +401,9 @@ netconf_input_packet(clicon_handle h,
* - RPC messages: send rpc-error
*/
static int
-netconf_input_frame(clicon_handle h,
- cbuf *cb,
- int *eof)
+netconf_input_frame1(clicon_handle h,
+ cbuf *cb,
+ int *eof)
{
int retval = -1;
char *str = NULL;
@@ -406,7 +417,7 @@ netconf_input_frame(clicon_handle h,
clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
clicon_debug(CLIXON_DBG_MSG, "Recv ext: %s", cbuf_get(cb));
- framing = clicon_data_int_get(h, "netconf-framing");
+ framing = clicon_data_int_get(h, NETCONF_FRAMING_TYPE);
yspec = clicon_dbspec_yang(h);
if ((str = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
@@ -504,6 +515,7 @@ netconf_input_frame(clicon_handle h,
cbuf_free(cbret);
return retval;
}
+#endif /* NETCONF_INPUT_UNIFIED_EXTERN */
/*! Get netconf message: detect end-of-msg
* @param[in] s Socket where input arrived. read from this.
@@ -511,11 +523,144 @@ netconf_input_frame(clicon_handle h,
* This routine continuously reads until no more data on s. There could
* be risk of starvation, but the netconf client does little else than
* read data so I do not see a danger of true starvation here.
- * @note data is saved in clicon-handle at NETCONF_HASH_BUF since there is a potential issue if data
+ * @note data is saved in clicon-handle at NETCONF_FRAME_MSG since there is a potential issue if data
* is not completely present on the s, ie if eg:
* foo ..pause.. ]]>]]>
* then only "" would be delivered to netconf_input_frame().
*/
+#ifdef NETCONF_INPUT_UNIFIED_EXTERN
+static int
+netconf_input_cb(int s,
+ void *arg)
+{
+ int retval = -1;
+ clicon_handle h = arg;
+ cbuf *cbmsg=NULL;
+ cbuf *cberr = NULL;
+ void *ptr;
+ yang_stmt *yspec;
+ clicon_hash_t *cdat = clicon_data(h); /* Save cbuf between calls if not done */
+ size_t cdatlen = 0;
+ int frame_state;
+ size_t frame_size;
+ int i32;
+ int eom = 0;
+ int eof = 0;
+ int framing_type;
+ cxobj *xtop = NULL;
+ cxobj *xreq;
+ cxobj *xerr = NULL;
+ int ret;
+ unsigned char buf[BUFSIZ]; /* from stdio.h, typically 8K */
+ ssize_t buflen = sizeof(buf);
+ unsigned char *p = buf;
+ ssize_t len;
+ size_t plen;
+
+ yspec = clicon_dbspec_yang(h);
+ /* Get unfinished frame */
+ if ((ptr = clicon_hash_value(cdat, NETCONF_FRAME_MSG, &cdatlen)) != NULL){
+ if (cdatlen != sizeof(cbmsg)){
+ clicon_err(OE_XML, errno, "size mismatch %lu %lu",
+ (unsigned long)cdatlen, (unsigned long)sizeof(cbmsg));
+ goto done;
+ }
+ cbmsg = *(cbuf**)ptr;
+ clicon_hash_del(cdat, NETCONF_FRAME_MSG);
+ }
+ else{
+ if ((cbmsg = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ }
+ if ((frame_state = clicon_data_int_get(h, NETCONF_FRAME_STATE)) < 0)
+ frame_state = 0;
+ if ((i32 = clicon_data_int_get(h, NETCONF_FRAME_SIZE)) < 0)
+ frame_size = 0;
+ else
+ frame_size = i32;
+ /* Read input data from socket and append to cbbuf */
+ if ((len = netconf_input_read2(s, buf, buflen, &eof)) < 0)
+ goto done;
+ p = buf;
+ plen = len;
+ while (!eof && plen > 0){
+ framing_type = clicon_data_int_get(h, NETCONF_FRAMING_TYPE); /* Can be set in frame handler */
+ if (netconf_input_msg2(&p, &plen,
+ cbmsg,
+ framing_type,
+ &frame_state,
+ &frame_size,
+ &eom) < 0)
+ goto done;
+ if (eom == 0){ /* frame not complete */
+ clicon_debug(CLIXON_DBG_DETAIL, "%s: frame: %lu", __FUNCTION__, cbuf_len(cbmsg));
+ /* Extra data to read, save data and continue on next round */
+ if (clicon_hash_add(cdat, NETCONF_FRAME_MSG, &cbmsg, sizeof(cbmsg)) == NULL)
+ goto done;
+ cbmsg = NULL;
+ break;
+ }
+ clicon_debug(CLIXON_DBG_MSG, "Recv ext: %s", cbuf_get(cbmsg));
+ if ((ret = netconf_input_frame2(cbmsg, Y_RPC, yspec, &xtop, &xerr)) < 0)
+ goto done;
+ cbuf_reset(cbmsg);
+ if (ret == 0){ /* Invalid frame, parse error, etc */
+ if ((cberr = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ if (clixon_xml2cbuf(cberr, xerr, 0, 0, NULL, -1, 0) < 0)
+ goto done;
+ if (xerr){
+ xml_free(xerr);
+ xerr = NULL;
+ }
+ if (netconf_output_encap(framing_type, cberr) < 0)
+ goto done;
+ if (netconf_output(1, cberr, "rpc-error") < 0)
+ goto done;
+ }
+ else {
+ if ((xreq = xml_child_i_type(xtop, 0, CX_ELMNT)) == NULL){
+ clicon_err(OE_XML, EFAULT, "No xml req (shouldnt happen)");
+ goto done;
+ }
+ if (netconf_input_packet(h, xreq, yspec, &eof) < 0){
+ goto done;
+ }
+ if (xtop){
+ xml_free(xtop);
+ xtop = NULL;
+ }
+ }
+ }
+ if (eof){ /* socket closed / read returns 0 */
+ clicon_debug(1, "%s len==0, closing", __FUNCTION__);
+ clixon_event_unreg_fd(s, netconf_input_cb);
+ close(s);
+ clixon_exit_set(1);
+ }
+ else {
+ clicon_data_int_set(h, NETCONF_FRAME_STATE, frame_state);
+ clicon_data_int_set(h, NETCONF_FRAME_SIZE, frame_size);
+ }
+ retval = 0;
+ done:
+ if (cbmsg)
+ cbuf_free(cbmsg);
+ if (cberr)
+ cbuf_free(cberr);
+ if (xtop)
+ xml_free(xtop);
+ if (xerr)
+ xml_free(xerr);
+ return retval;
+}
+
+#else /* NETCONF_INPUT_UNIFIED_EXTERN */
+
static int
netconf_input_cb(int s,
void *arg)
@@ -547,14 +692,14 @@ netconf_input_cb(int s,
goto done;
frame_size = (size_t)ret;
}
- if ((ptr = clicon_hash_value(cdat, NETCONF_HASH_BUF, &cdatlen)) != NULL){
+ if ((ptr = clicon_hash_value(cdat, NETCONF_FRAME_MSG, &cdatlen)) != NULL){
if (cdatlen != sizeof(cb)){
clicon_err(OE_XML, errno, "size mismatch %lu %lu",
(unsigned long)cdatlen, (unsigned long)sizeof(cb));
goto done;
}
cb = *(cbuf**)ptr;
- clicon_hash_del(cdat, NETCONF_HASH_BUF);
+ clicon_hash_del(cdat, NETCONF_FRAME_MSG);
}
else{
if ((cb = cbuf_new()) == NULL){
@@ -582,7 +727,7 @@ netconf_input_cb(int s,
for (i=0; i]]>")) = '\0';
- if (netconf_input_frame(h, cb, &eof) < 0 &&
+ if (netconf_input_frame1(h, cb, &eof) < 0 &&
!ignore_packet_errors) // default is to ignore errors
goto done;
if (eof)
@@ -627,7 +772,7 @@ netconf_input_cb(int s,
if (poll == 0){
/* No data to read, save data and continue on next round */
if (cbuf_len(cb) != 0){
- if (clicon_hash_add(cdat, NETCONF_HASH_BUF, &cb, sizeof(cb)) == NULL)
+ if (clicon_hash_add(cdat, NETCONF_FRAME_MSG, &cb, sizeof(cb)) == NULL)
goto done;
cb = NULL;
}
@@ -643,6 +788,7 @@ netconf_input_cb(int s,
cbuf_free(cb);
return retval;
}
+#endif /* NETCONF_INPUT_UNIFIED_EXTERN */
/*! Send netconf hello message
* @param[in] h Clixon handle
@@ -663,7 +809,7 @@ send_hello(clicon_handle h,
}
if (netconf_hello_server(h, cb, id) < 0)
goto done;
- framing = clicon_data_int_get(h, "netconf-framing");
+ framing = clicon_data_int_get(h, NETCONF_FRAMING_TYPE);
if (netconf_output_encap(framing, cb) < 0)
goto done;
if (netconf_output(s, cb, "hello") < 0)
@@ -1021,7 +1167,6 @@ main(int argc,
if (clixon_event_reg_timeout(t, timeout_fn, NULL, "timeout") < 0)
goto done;
}
-
if (clixon_event_loop(h) < 0)
goto done;
retval = 0;
diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c
index 1d03c23b..2ded279e 100644
--- a/apps/netconf/netconf_rpc.c
+++ b/apps/netconf/netconf_rpc.c
@@ -482,7 +482,7 @@ netconf_notification_cb(int s,
if (clixon_xml2cbuf(cb, xn, 0, 0, NULL, -1, 0) < 0)
goto done;
/* Send it to listening client on stdout */
- if (netconf_output_encap(clicon_option_int(h, "netconf-framing"), cb) < 0){
+ if (netconf_output_encap(clicon_data_int_get(h, NETCONF_FRAMING_TYPE), cb) < 0){
goto done;
}
if (netconf_output(1, cb, "notification") < 0){
diff --git a/include/clixon_custom.h b/include/clixon_custom.h
index b51b3216..0b7f1617 100644
--- a/include/clixon_custom.h
+++ b/include/clixon_custom.h
@@ -201,3 +201,22 @@
* Introduced in 6.1, remove in 6.2
*/
#define AUTOCLI_DEPRECATED_HIDE
+
+/*! Unified netconf input function
+ * Replace external, internal and controller netconf eventually
+ * New file: clixon_netconf_input.c with functions:
+ * - netconf_input_read2
+ * - netconf_input_msg2
+ * - netconf_input_frame2
+ * The following code should use this:
+ * X - netconf_main.c external netconf
+ * - netconf_proto.c internal netconf
+ * - controller device control
+ */
+/*! Use unified netconf input function for external use
+ */
+#define NETCONF_INPUT_UNIFIED_EXTERN
+
+/*! Use unified netconf input function for internal use
+ */
+#undef NETCONF_INPUT_UNIFIED_INTERNAL
diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in
index cbb8c7f7..ab9df9d4 100644
--- a/lib/clixon/clixon.h.in
+++ b/lib/clixon/clixon.h.in
@@ -89,6 +89,7 @@ extern "C" {
#include
#include
#include
+#include
#include
#include
#include
diff --git a/lib/clixon/clixon_netconf_input.h b/lib/clixon/clixon_netconf_input.h
new file mode 100644
index 00000000..518b12e1
--- /dev/null
+++ b/lib/clixon/clixon_netconf_input.h
@@ -0,0 +1,61 @@
+/*
+ *
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
+
+ This file is part of CLIXON.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Alternatively, the contents of this file may be used under the terms of
+ the GNU General Public License Version 3 or later (the "GPL"),
+ in which case the provisions of the GPL are applicable instead
+ of those above. If you wish to allow use of your version of this file only
+ under the terms of the GPL, and not to allow others to
+ use your version of this file under the terms of Apache License version 2, indicate
+ your decision by deleting the provisions above and replace them with the
+ notice and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this file under
+ the terms of any one of the Apache License version 2 or the GPL.
+
+ ***** END LICENSE BLOCK *****
+ * Netconf input routines
+ */
+
+#ifndef _CLIXON_NETCONF_INPUT_H
+#define _CLIXON_NETCONF_INPUT_H
+
+/* What kind of framing? NETCONF_SSH_EOM or NETCONF_SSH_CHUNKED
+ * Global setting used with clicon_data_int_get()
+ */
+#define NETCONF_FRAMING_TYPE "netconf-framing-type"
+
+/*
+ * Prototypes
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ssize_t netconf_input_read2(int s, unsigned char *buf, ssize_t buflen, int *eof);
+int netconf_input_msg2(unsigned char **bufp, size_t *lenp, cbuf *cbmsg,
+ netconf_framing_type framing, int *frame_state, size_t *frame_size,
+ int *eom);
+int netconf_input_frame2(cbuf *cb, yang_bind yb, yang_stmt *yspec, cxobj **xrecv, cxobj **xerr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLIXON_NETCONF_INPUT_H */
diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in
index edbbffaf..f1dc2962 100644
--- a/lib/src/Makefile.in
+++ b/lib/src/Makefile.in
@@ -92,7 +92,8 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_function.c \
clixon_xpath_optimize.c clixon_xpath_yang.c \
clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \
- clixon_netconf_lib.c clixon_stream.c clixon_nacm.c clixon_client.c clixon_netns.c \
+ clixon_netconf_lib.c clixon_netconf_input.c clixon_stream.c \
+ clixon_nacm.c clixon_client.c clixon_netns.c \
clixon_dispatcher.c clixon_text_syntax.c
YACCOBJS = lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
diff --git a/lib/src/clixon_netconf_input.c b/lib/src/clixon_netconf_input.c
new file mode 100644
index 00000000..b7fa233e
--- /dev/null
+++ b/lib/src/clixon_netconf_input.c
@@ -0,0 +1,270 @@
+/*
+ *
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright (C) 2023 Olof Hagsand
+
+ This file is part of CLIXON.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ ***** END LICENSE BLOCK *****
+ * Unified netconf input routines
+ *
+ * Usage:
+
+ if ((len = netconf_input_read2(s, buf, buflen, &eof)) < 0)
+ goto done;
+ p = buf; plen = len;
+ while (!eof){
+ if (netconf_input_msg2(&p, &plen, cbmsg, framing_type, &frame_state, &frame_size, &eom) < 0)
+ goto done;
+ if (!eom)
+ break;
+ if ((ret = netconf_input_frame2(cbmsg, Y_RPC, yspec, &cbret, &xtop)) < 0)
+ goto done;
+ // process incoming packet xtop
+ }
+
+ if (eom == 0)
+ // frame not complete
+ if ((ret = netconf_input_frame(cb, yspec, &xtop)) < 0)
+ goto done;
+ if (ret == 0)
+ // invalid
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* clicon */
+#include
+
+/* Local includes */
+#include "clixon_queue.h"
+#include "clixon_hash.h"
+#include "clixon_string.h"
+#include "clixon_err.h"
+#include "clixon_handle.h"
+#include "clixon_yang.h"
+#include "clixon_log.h"
+#include "clixon_xml.h"
+#include "clixon_xml_io.h"
+#include "clixon_proto.h"
+#include "clixon_netconf_lib.h"
+#include "clixon_netconf_input.h"
+
+/*! Read from socket and append to cbuf
+ *
+ * @param[in] s Socket where input arrives. Read from this.
+ * @param[in] buf Packet buffer
+ * @param[in] buflen Length of packet buffer
+ * @param[out] eof Socket closed / eof?
+ * @retval 0 OK
+ * @retval -1 Error
+ */
+ssize_t
+netconf_input_read2(int s,
+ unsigned char *buf,
+ ssize_t buflen,
+ int *eof)
+{
+ int retval = -1;
+ ssize_t len;
+
+ memset(buf, 0, buflen);
+ if ((len = read(s, buf, buflen)) < 0){
+ if (errno == ECONNRESET)
+ len = 0; /* emulate EOF */
+ else{
+ clicon_log(LOG_ERR, "%s: read: %s", __FUNCTION__, strerror(errno));
+ goto done;
+ }
+ } /* read */
+ clicon_debug(CLIXON_DBG_DETAIL, "%s len:%ld", __FUNCTION__, len);
+ if (len == 0){ /* EOF */
+ clicon_debug(CLIXON_DBG_DETAIL, "%s len==0, closing", __FUNCTION__);
+ *eof = 1;
+ }
+ retval = len;
+ done:
+ clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
+ return retval;
+}
+
+/*! Get netconf message using NETCONF framing
+ *
+ * @param[in,out] bufp Input data, incremented as read
+ * @param[in,out] lenp Data len, decremented as read
+ * @param[in,out] cbmsg Completed frame (if eom), may contain data on entry
+ * @param[in] framing_type EOM or chunked framing
+ * @param[in,out] frame_state Framing state depending on type
+ * @param[in,out] frame_size Chunked framing size parameter
+ * @param[out] eom If frame found in cb?
+ * @retval 0 OK
+ * @retval -1 Error
+ * The routine should be called continuously with more data from input socket in buf
+ * State of previous reads is saved in:
+ * - bufp/lenp
+ * - cbmsg
+ * - frame_state/frame_size
+ */
+int
+netconf_input_msg2(unsigned char **bufp,
+ size_t *lenp,
+ cbuf *cbmsg,
+ netconf_framing_type framing_type,
+ int *frame_state,
+ size_t *frame_size,
+ int *eom)
+{
+ int retval = -1;
+ int i;
+ int ret;
+ int found = 0;
+ size_t len;
+ char ch;
+
+ clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
+ len = *lenp;
+ for (i=0; i]]>", ch, frame_state)){
+ *frame_state = 0;
+ /* OK, we have an xml string from a client */
+ /* Remove trailer */
+ *(((char*)cbuf_get(cbmsg)) + cbuf_len(cbmsg) - strlen("]]>]]>")) = '\0';
+ found++;
+ }
+ }
+ if (found){
+ i++;
+ break;
+ }
+ } /* for */
+ *bufp += i;
+ *lenp -= i;
+ *eom = found;
+ retval = 0;
+ done:
+ clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
+ return retval;
+}
+
+/*! Process incoming frame, ie a char message framed by ]]>]]>
+ *
+ * Parse string to xml, check only one netconf message within a frame
+ * @param[in] cb Packet buffer
+ * @param[in] yb Yang binding: Y_RPC for server-side, Y_NONE for client-side (for now)
+ * @param[in] yspec Yang spec
+ * @param[out] xrecv XML packet
+ * @param[out] xerr XML error, (if ret = 0)
+ * @retval 1 OK
+ * @retval 0 Invalid, parse error, etc, xerr points to netconf error message
+ * @retval -1 Fatal error
+ */
+int
+netconf_input_frame2(cbuf *cb,
+ yang_bind yb,
+ yang_stmt *yspec,
+ cxobj **xrecv,
+ cxobj **xerr)
+{
+ int retval = -1;
+ char *str = NULL;
+ cxobj *xtop = NULL; /* Request (in) */
+ int ret;
+
+ clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
+ if (xrecv == NULL){
+ clicon_err(OE_PLUGIN, EINVAL, "xrecv is NULL");
+ goto done;
+ }
+ str = cbuf_get(cb);
+ /* Special case: empty XML */
+ if (strlen(str) == 0){
+ if (netconf_operation_failed_xml(xerr, "rpc", "Empty XML")< 0)
+ goto done;
+ goto failed;
+ }
+ /* Fix to distinguish RPC and REPLIES */
+ if ((ret = clixon_xml_parse_string(str, yb, yspec, &xtop, xerr)) < 0){
+ /* XXX possibly should quit on -1? */
+ if (netconf_operation_failed_xml(xerr, "rpc", clicon_err_reason)< 0)
+ goto done;
+ goto failed;
+ }
+ if (ret == 0){
+ /* Note: xtop can be "hello" in which case one (maybe) should drop the session and log
+ * However, its not until netconf_input_packet that rpc vs hello vs other identification is
+ * actually made.
+ * Actually, there are no error replies to hello messages according to any RFC, so
+ * rpc error reply here is non-standard, but may be useful.
+ */
+ goto failed;
+ }
+ /* Check for empty frame (no messages), return empty message, not clear from RFC what to do */
+ if (xml_child_nr_type(xtop, CX_ELMNT) == 0){
+ if (netconf_operation_failed_xml(xerr, "rpc", "Truncated XML")< 0)
+ goto done;
+ goto failed;
+ }
+ if (xml_child_nr_type(xtop, CX_ELMNT) != 1){
+ if (netconf_malformed_message_xml(xerr, "More than one message in netconf rpc frame")< 0)
+ goto done;
+ goto failed;
+ }
+ *xrecv = xtop;
+ xtop = NULL;
+ retval = 1;
+ done:
+ if (xtop)
+ xml_free(xtop);
+ return retval;
+ failed:
+ retval = 0;
+ goto done;
+}
+
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index 3b9e00f2..54a816e9 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -74,8 +74,8 @@
#include "clixon_yang_module.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_plugin.h"
-
#include "clixon_netconf_lib.h"
+#include "clixon_netconf_input.h"
/* Mapping between RFC6243 withdefaults strings <--> ints
*/
@@ -465,6 +465,7 @@ netconf_unknown_attribute(cbuf *cb,
}
/*! Common Netconf element XML tree according to RFC 6241 App A
+ *
* @param[out] xret Error XML tree. Free with xml_free after use
* @param[in] type Error type: "application" or "protocol"
* @param[in] tag Error tag
@@ -495,7 +496,6 @@ netconf_common_xml(cxobj **xret,
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
-
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
goto done;
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s"
@@ -1631,10 +1631,10 @@ netconf_module_load(clicon_handle h)
* But start with default: RFC 4741 EOM ]]>]]>
* For now this only applies to external protocol
*/
- clicon_data_int_set(h, "netconf-framing", NETCONF_SSH_EOM);
+ clicon_data_int_set(h, NETCONF_FRAMING_TYPE, NETCONF_SSH_EOM);
if (clicon_option_bool(h, "CLICON_NETCONF_HELLO_OPTIONAL")){
if (clicon_option_int(h, "CLICON_NETCONF_BASE_CAPABILITY") > 0) /* RFC 6241 */
- clicon_data_int_set(h, "netconf-framing", NETCONF_SSH_CHUNKED);
+ clicon_data_int_set(h, NETCONF_FRAMING_TYPE, NETCONF_SSH_CHUNKED);
}
retval = 0;
done:
@@ -1870,7 +1870,7 @@ netconf_hello_server(clicon_handle h,
return retval;
}
-/*! Generate textual error log from Netconf error message
+/*! Generate and print textual error log from Netconf error message
*
* Get a text error message from netconf error message and generate error on the form:
* : "": or :
diff --git a/test/lib.sh b/test/lib.sh
index 1538793a..12f56ca6 100755
--- a/test/lib.sh
+++ b/test/lib.sh
@@ -494,7 +494,7 @@ function chunked_equal()
fi
}
-# Given a string, add RFC6242 chunked franing around it
+# Given a string, add RFC6242 chunked framing around it
# Args:
# 0: string
function chunked_framing()
@@ -818,6 +818,7 @@ function expecteof(){
retval=$2
input=$3
expect=$4
+
if [ $# -gt 4 ]; then
errfile=$(mktemp)
expecterr=$5
diff --git a/test/test_netconf.sh b/test/test_netconf.sh
index f8b7f485..99df8829 100755
--- a/test/test_netconf.sh
+++ b/test/test_netconf.sh
@@ -174,7 +174,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "urn:ietf:params:netconf:base:1.1
new "netconf rcv hello, disable RFC7895/ietf-yang-library"
-expecteof_netconf "$clixon_netconf -f $cfg -o CLICON_YANG_LIBRARY=0" 0 "$DEFAULTHELLO" "" "" ""
+expecteof_netconf "$clixon_netconf -qD 7 -lf/tmp/netconf0.log -f $cfg -o CLICON_YANG_LIBRARY=0" 0 "$DEFAULTHELLO" "" "" ""
new "netconf get-config nc prefix"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" ""
diff --git a/test/test_netconf_hello.sh b/test/test_netconf_hello.sh
index 545eba4b..b055aad7 100755
--- a/test/test_netconf_hello.sh
+++ b/test/test_netconf_hello.sh
@@ -118,14 +118,14 @@ expecteof "$clixon_netconf -f $cfg" 0 "urn:ietf:params:netconf:base:1.1]]>]]>" '^protocolunknown-elementextra-elementerrorUnrecognized hello/capabilities element]]>]]>$' '^$'
+expecteof "$clixon_netconf -qf $cfg" 0 "urn:ietf:params:netconf:base:1.1]]>]]>" '^protocolunknown-elementextra-elementerrorUnrecognized hello/capabilities element]]>]]>$' '^$'
new "Netconf send rpc without hello error"
-expecteof "$clixon_netconf -qef $cfg" 255 "]]>]]>" "rpcoperation-failederrorClient must send an hello element before any RPC]]>]]>" '^$'
+expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "rpcoperation-failederrorClient must send an hello element before any RPC]]>]]>" '^$'
# same as -H
new "Netconf send rpc without hello w CLICON_NETCONF_HELLO_OPTIONAL"
-expecteof_netconf "$clixon_netconf -qef $cfg -o CLICON_NETCONF_HELLO_OPTIONAL=true" 0 "" "" "" ""
+expecteof_netconf "$clixon_netconf -qf $cfg -o CLICON_NETCONF_HELLO_OPTIONAL=true" 0 "" "" "" ""
if [ $BE -ne 0 ]; then
new "Kill backend"