Unified netconf input function

First for external use, later internal
This commit is contained in:
Olof hagsand 2023-03-06 10:48:46 +01:00
parent 04d5f52d90
commit e7c9f3d075
12 changed files with 534 additions and 32 deletions

View file

@ -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:
* <foo/> ..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:
* <a>foo ..pause.. </a>]]>]]>
* then only "</a>" 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<len; i++){
if (buf[i] == 0)
continue; /* Skip NULL chars (eg from terminals) */
if (clicon_data_int_get(h, "netconf-framing") == NETCONF_SSH_CHUNKED){
if (clicon_data_int_get(h, NETCONF_FRAMING_TYPE) == NETCONF_SSH_CHUNKED){
/* Track chunked framing defined in RFC6242 */
if ((ret = netconf_input_chunked_framing(buf[i], &frame_state, &frame_size)) < 0)
goto done;
@ -594,7 +739,7 @@ netconf_input_cb(int s,
/* Somewhat complex error-handling:
* Ignore packet errors, UNLESS an explicit termination request (eof)
*/
if (netconf_input_frame(h, cb, &eof) < 0 &&
if (netconf_input_frame1(h, cb, &eof) < 0 &&
!ignore_packet_errors)
goto done;
if (eof)
@ -612,7 +757,7 @@ netconf_input_cb(int s,
/* OK, we have an xml string from a client */
/* Remove trailer */
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\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;