Unified netconf input function
First for external use, later internal
This commit is contained in:
parent
04d5f52d90
commit
e7c9f3d075
12 changed files with 534 additions and 32 deletions
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue