* HTTP/1 native parser as part of the RESTCONF client
* Fixed memory error in opendir/readdir in clicon_file_dirent * Remove MAXPATH in parsers * New string-del function
This commit is contained in:
parent
0ed34b4fab
commit
dadf4a778a
53 changed files with 1061 additions and 1273 deletions
|
|
@ -49,18 +49,6 @@
|
|||
* +--------------------+
|
||||
* Note restconf_request may need to be extended eg backpointer to rs?
|
||||
*
|
||||
* +--------------------+ +--------------------+
|
||||
* | evhtp_connection | -----> | evhtp_t (core) |
|
||||
* +--------------------+ +--------------------+
|
||||
* |request ^
|
||||
* v | conn
|
||||
* +--------------------+ +--------------------+
|
||||
* | evhtp_request | --> uri | evhtp_uri |
|
||||
* +--------------------+ +--------------------+
|
||||
* | (created by parser) | | |
|
||||
* v v v v
|
||||
* headers/buffers/method/... authority path query
|
||||
*
|
||||
* Buffer handling:
|
||||
* c
|
||||
* |
|
||||
|
|
@ -121,12 +109,6 @@
|
|||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
/* The clixon evhtp code can be compiled with or without threading support
|
||||
* The choice is set at libevhtp compile time by cmake. Eg:
|
||||
* cmake -DEVHTP_DISABLE_EVTHR=ON # Disable threads.
|
||||
* Default in testing is disabled threads.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
|
@ -151,16 +133,6 @@
|
|||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
/* evhtp */
|
||||
#include <event2/buffer.h> /* evbuffer */
|
||||
#define EVHTP_DISABLE_REGEX
|
||||
#define EVHTP_DISABLE_EVTHR
|
||||
|
||||
#include <evhtp/evhtp.h>
|
||||
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
/* nghttp2 */
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
|
@ -173,12 +145,12 @@
|
|||
#include "restconf_err.h"
|
||||
#include "restconf_root.h"
|
||||
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
#include "restconf_evhtp.h" /* http/1 */
|
||||
#endif
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
#include "restconf_nghttp2.h" /* http/2 */
|
||||
#endif
|
||||
#ifdef HAVE_HTTP1
|
||||
#include "clixon_http1.h"
|
||||
#endif
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define RESTCONF_OPTS "hD:f:E:l:p:y:a:u:rW:R:o:"
|
||||
|
|
@ -352,9 +324,7 @@ clixon_openssl_log_cb(void *handle,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* see restconf_config ->cv_evhtp_init(x2) -> cx_evhtp_socket ->
|
||||
* evhtp_ssl_init:4757
|
||||
/*! Init openSSL
|
||||
*/
|
||||
static int
|
||||
init_openssl(void)
|
||||
|
|
@ -425,7 +395,7 @@ restconf_verify_certs(int preverify_ok,
|
|||
* - 0 (preferity_ok) the session terminates here in SSL negotiation, an http client
|
||||
* will get a low level error (not http reply)
|
||||
* - 1 Check if the cert is valid using SSL_get_verify_result(rc->rc_ssl)
|
||||
* @see restconf_evhtp_sanity and restconf_nghttp2_sanity where this is done for http/1 and http/2
|
||||
* @see restconf_nghttp2_sanity where this is done for http/1 and http/2
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -479,7 +449,7 @@ alpn_select_proto_cb(SSL *ssl,
|
|||
inp++;
|
||||
if (clicon_debug_get()) /* debug print the protoocol */
|
||||
alpn_proto_dump(__FUNCTION__, (const char*)inp, len);
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
#ifdef HAVE_HTTP1
|
||||
if (pref < 10 && len == 8 && strncmp((char*)inp, "http/1.1", len) == 0){
|
||||
*outlen = len;
|
||||
*out = inp;
|
||||
|
|
@ -503,8 +473,6 @@ alpn_select_proto_cb(SSL *ssl,
|
|||
}
|
||||
|
||||
/*
|
||||
* see restconf_config ->cv_evhtp_init(x2) -> cx_evhtp_socket ->
|
||||
* evhtp_ssl_init:4794
|
||||
*/
|
||||
static SSL_CTX *
|
||||
restconf_ssl_context_create(clicon_handle h)
|
||||
|
|
@ -535,8 +503,6 @@ restconf_ssl_context_create(clicon_handle h)
|
|||
}
|
||||
|
||||
/*
|
||||
* see restconf_config ->cv_evhtp_init(x2) -> cx_evhtp_socket ->
|
||||
* evhtp_ssl_init: 4886
|
||||
* @param[in] ctx SSL context
|
||||
* @param[in] server_cert_path Server cert
|
||||
* @param[in] server_key_path Server private key
|
||||
|
|
@ -595,7 +561,7 @@ restconf_ssl_context_configure(clixon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Utility function to close restconf server ssl/evhtp socket.
|
||||
/*! Utility function to close restconf server ssl socket.
|
||||
* There are many variants to closing, one could probably make this more generic
|
||||
* and always use this function, but it is difficult.
|
||||
*/
|
||||
|
|
@ -618,10 +584,6 @@ Note that in this case SSL_ERROR_ZERO_RETURN does not necessarily indicate that
|
|||
}
|
||||
SSL_free(rc->rc_ssl);
|
||||
rc->rc_ssl = NULL;
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
if (rc->rc_evconn)
|
||||
rc->rc_evconn->ssl = NULL;
|
||||
#endif
|
||||
}
|
||||
if (close(rc->rc_s) < 0){
|
||||
clicon_err(OE_UNIX, errno, "close");
|
||||
|
|
@ -675,7 +637,67 @@ send_badrequest(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! New data connection after accept, receive and reply on data sockte
|
||||
#if 0
|
||||
#define IFILE "/var/tmp/clixon-mirror/ifile"
|
||||
#define FMTDIR "/var/tmp/clixon-mirror/"
|
||||
|
||||
static FILE *myf = NULL;
|
||||
|
||||
static int
|
||||
mirror_pkt(const char *buf,
|
||||
ssize_t n)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (fwrite(buf, 1, n, myf) != n){
|
||||
perror("fopen");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
mirror_new(void)
|
||||
{
|
||||
int retval = -1;
|
||||
static uint64_t u64 = 0;
|
||||
cbuf *cb = cbuf_new();
|
||||
FILE *ifile;
|
||||
|
||||
if ((ifile = fopen(IFILE, "r+")) == NULL){
|
||||
perror("fopen r+ ifile");
|
||||
}
|
||||
else {
|
||||
if (fscanf(ifile, "%" PRIu64, &u64) < 0){
|
||||
perror("fscanf ifile");
|
||||
goto done;
|
||||
}
|
||||
fclose(ifile);
|
||||
}
|
||||
if (myf != NULL)
|
||||
fclose(myf);
|
||||
cprintf(cb, FMTDIR "%" PRIu64 ".dump", u64);
|
||||
if ((myf = fopen(cbuf_get(cb), "w")) == NULL){
|
||||
perror("fopen");
|
||||
goto done;
|
||||
}
|
||||
cbuf_free(cb);
|
||||
u64++;
|
||||
if ((ifile = fopen(IFILE, "w")) == NULL){
|
||||
perror("fopen w+ ifile");
|
||||
goto done;
|
||||
}
|
||||
fprintf(ifile, "%" PRIu64, u64);
|
||||
fclose(ifile);
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! New data connection after accept, receive and reply on data socket
|
||||
*
|
||||
* @param[in] s Socket where message arrived. read from this.
|
||||
* @param[in] arg Client entry (from).
|
||||
|
|
@ -683,10 +705,10 @@ send_badrequest(clicon_handle h,
|
|||
* @retval -1 Error Terminates backend and is never called). Instead errors are
|
||||
* propagated back to client.
|
||||
* @see restconf_accept_client where this callback is registered
|
||||
* @note read buffer is limited. More data can be read in two ways: either evhtp returns a buffer
|
||||
* @note read buffer is limited. More data can be read in two ways: returns a buffer
|
||||
* with 100 Continue, in which case that is replied and the function returns and the client sends
|
||||
* more data.
|
||||
* OR evhtp returns 0 with no reply, then this is assumed to mean read more data from the socket.
|
||||
* OR returns 0 with no reply, then this is assumed to mean read more data from the socket.
|
||||
*/
|
||||
static int
|
||||
restconf_connection(int s,
|
||||
|
|
@ -701,9 +723,8 @@ restconf_connection(int s,
|
|||
#ifdef HAVE_LIBNGHTTP2
|
||||
int ret;
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
#ifdef HAVE_HTTP1
|
||||
clicon_handle h;
|
||||
evhtp_connection_t *evconn = NULL;
|
||||
restconf_stream_data *sd;
|
||||
#endif
|
||||
|
||||
|
|
@ -765,7 +786,7 @@ restconf_connection(int s,
|
|||
continue;
|
||||
}
|
||||
}
|
||||
clicon_debug(1, "%s (ssl)read:%zd", __FUNCTION__, n);
|
||||
clicon_debug(1, "%s read:%zd", __FUNCTION__, n);
|
||||
if (n == 0){
|
||||
clicon_debug(1, "%s n=0 closing socket", __FUNCTION__);
|
||||
if (restconf_close_ssl_socket(rc, 0) < 0)
|
||||
|
|
@ -774,99 +795,43 @@ restconf_connection(int s,
|
|||
rc = NULL;
|
||||
goto ok;
|
||||
}
|
||||
#if 0
|
||||
if (mirror_pkt(buf, n) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
switch (rc->rc_proto){
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
#ifdef HAVE_HTTP1
|
||||
case HTTP_10:
|
||||
case HTTP_11:
|
||||
h = rc->rc_h;
|
||||
/* parse incoming packet using evhtp
|
||||
* signature:
|
||||
*/
|
||||
evconn = rc->rc_evconn;
|
||||
/* This is the main call to EVHTP parser */
|
||||
if (connection_parse_nobev(buf, n, evconn) < 0){
|
||||
clicon_debug(1, "%s connection_parse error", __FUNCTION__);
|
||||
/* XXX To get more nuanced evhtp error check
|
||||
* htparser_get_error(conn->parser)
|
||||
*/
|
||||
if (clixon_http1_parse_buf(h, rc, buf, n) < 0){
|
||||
if (send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml",
|
||||
"<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>protocol</error-type><error-tag>malformed-message</error-tag><error-message>The requested URL or a header is in some way badly formed</error-message></error></errors>") < 0)
|
||||
goto done;
|
||||
SSL_free(rc->rc_ssl);
|
||||
rc->rc_ssl = NULL;
|
||||
evconn->ssl = NULL;
|
||||
if (close(rc->rc_s) < 0){
|
||||
clicon_err(OE_UNIX, errno, "close");
|
||||
}
|
||||
else{
|
||||
if (restconf_http1_path_root(h, rc) < 0)
|
||||
goto done;
|
||||
}
|
||||
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
|
||||
clicon_debug(1, "%s evconn-free (%p) 2", __FUNCTION__, evconn);
|
||||
restconf_conn_free(rc);
|
||||
goto ok;
|
||||
} /* connection_parse_nobev */
|
||||
}
|
||||
clicon_debug(1, "%s connection_parse OK", __FUNCTION__);
|
||||
/* default stream */
|
||||
if ((sd = restconf_stream_find(rc, 0)) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "restconf stream not found");
|
||||
goto done;
|
||||
}
|
||||
if (evconn->bev != NULL){
|
||||
struct evbuffer *ev;
|
||||
size_t buflen0;
|
||||
size_t buflen1;
|
||||
char *buf = NULL;
|
||||
|
||||
if ((ev = bufferevent_get_output(evconn->bev)) != NULL){
|
||||
buflen0 = evbuffer_get_length(ev);
|
||||
buflen1 = buflen0 - rc->rc_bufferevent_output_offset;
|
||||
if (buflen1 > 0){
|
||||
buf = (char*)evbuffer_pullup(ev, -1);
|
||||
/* XXX Here if -1 in api_root
|
||||
* HTTP/1.1 0 UNKNOWN\r\nContent-Length: 0
|
||||
* And output_buffer is NULL
|
||||
*/
|
||||
/* If evhtp has print an output buffer, clixon whould not have done it
|
||||
* Shouldnt happen
|
||||
*/
|
||||
if (cbuf_len(sd->sd_outp_buf)){
|
||||
clicon_debug(1, "%s Warning: evhtp printed output buffer, but clixon output buffer is non-empty %s",
|
||||
__FUNCTION__, cbuf_get(sd->sd_outp_buf));
|
||||
cbuf_reset(sd->sd_outp_buf);
|
||||
}
|
||||
if (cbuf_append_buf(sd->sd_outp_buf, buf, buflen1) < 0){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_append_buf");
|
||||
goto done;
|
||||
}
|
||||
/* XXX Cant get drain to work, need to keep an offset */
|
||||
evbuffer_drain(ev, -1);
|
||||
rc->rc_bufferevent_output_offset += buflen1;
|
||||
}
|
||||
}
|
||||
if (cbuf_len(sd->sd_outp_buf) == 0)
|
||||
readmore = 1;
|
||||
else {
|
||||
if (buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf),
|
||||
rc->rc_s, rc->rc_ssl) < 0)
|
||||
goto done;
|
||||
cvec_reset(sd->sd_outp_hdrs); /* Can be done in native_send_reply */
|
||||
cbuf_reset(sd->sd_outp_buf);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml",
|
||||
"<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>protocol</error-type><error-tag>malformed-message</error-tag><error-message>No evhtp output</error-message></error></errors>") < 0)
|
||||
goto done;
|
||||
}
|
||||
if (buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf),
|
||||
rc->rc_s, rc->rc_ssl) < 0)
|
||||
goto done;
|
||||
cvec_reset(sd->sd_outp_hdrs); /* Can be done in native_send_reply */
|
||||
cbuf_reset(sd->sd_outp_buf);
|
||||
if (rc->rc_exit){ /* Server-initiated exit for http/2 */
|
||||
SSL_free(rc->rc_ssl);
|
||||
rc->rc_ssl = NULL;
|
||||
evconn->ssl = NULL;
|
||||
if (close(rc->rc_s) < 0){
|
||||
clicon_err(OE_UNIX, errno, "close");
|
||||
goto done;
|
||||
}
|
||||
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
|
||||
clicon_debug(1, "%s evconn-free (%p) 2", __FUNCTION__, evconn);
|
||||
restconf_conn_free(rc);
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -916,7 +881,7 @@ restconf_connection(int s,
|
|||
}
|
||||
#endif
|
||||
break;
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
#endif /* HAVE_HTTP1 */
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
case HTTP_2:
|
||||
if (rc->rc_exit){ /* Server-initiated exit for http/2 */
|
||||
|
|
@ -1119,10 +1084,9 @@ restconf_accept_client(int fd,
|
|||
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, fd);
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
#ifndef HAVE_LIBEVHTP
|
||||
#ifndef HAVE_HTTP1
|
||||
proto = HTTP_2; /* If nghttp2 only let default be 2.0 */
|
||||
#endif
|
||||
/* If also evhtp, keep HTTP_11 */
|
||||
#endif
|
||||
if ((rsock = (restconf_socket *)arg) == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "rsock is NULL");
|
||||
|
|
@ -1313,28 +1277,14 @@ restconf_accept_client(int fd,
|
|||
} /* if ssl */
|
||||
rc->rc_proto = proto;
|
||||
switch (rc->rc_proto){
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
#ifdef HAVE_HTTP1
|
||||
case HTTP_10:
|
||||
case HTTP_11:{
|
||||
evhtp_t *evhtp = (evhtp_t *)rh->rh_arg;
|
||||
evhtp_connection_t *evconn;
|
||||
|
||||
/* Create evhtp-specific struct */
|
||||
if ((evconn = evhtp_connection_new_server(evhtp, rc->rc_s)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "evhtp_connection_new_server");
|
||||
goto done;
|
||||
}
|
||||
/* Mutual pointers, from generic rc to evhtp specific and from evhtp conn to generic
|
||||
*/
|
||||
rc->rc_evconn = evconn; /* Generic to specific */
|
||||
evconn->arg = rc; /* Specific to generic */
|
||||
evconn->ssl = rc->rc_ssl; /* evhtp */
|
||||
case HTTP_11:
|
||||
/* Create a default stream for http/1 */
|
||||
if (restconf_stream_data_new(rc, 0) == NULL)
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
#endif /* HAVE_HTTP1 */
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
case HTTP_2:{
|
||||
if (http2_session_init(rc) < 0){
|
||||
|
|
@ -1365,6 +1315,10 @@ restconf_accept_client(int fd,
|
|||
default:
|
||||
break;
|
||||
} /* switch proto */
|
||||
#if 0
|
||||
if (mirror_new() < 0)
|
||||
goto done;
|
||||
#endif
|
||||
if (clixon_event_reg_fd(rc->rc_s, restconf_connection, (void*)rc, "restconf client socket") < 0)
|
||||
goto done;
|
||||
ok:
|
||||
|
|
@ -1376,6 +1330,8 @@ restconf_accept_client(int fd,
|
|||
return retval;
|
||||
} /* restconf_accept_client */
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
restconf_native_terminate(clicon_handle h)
|
||||
{
|
||||
|
|
@ -1396,18 +1352,6 @@ restconf_native_terminate(clicon_handle h)
|
|||
}
|
||||
if (rh->rh_ctx)
|
||||
SSL_CTX_free(rh->rh_ctx);
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
{
|
||||
evhtp_t *evhtp = (evhtp_t *)rh->rh_arg;
|
||||
if (evhtp){
|
||||
if (evhtp->evbase)
|
||||
event_base_free(evhtp->evbase);
|
||||
evhtp_free(evhtp);
|
||||
rh->rh_arg = NULL;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
||||
free(rh);
|
||||
}
|
||||
EVP_cleanup();
|
||||
|
|
@ -1581,10 +1525,6 @@ restconf_openssl_init(clicon_handle h,
|
|||
cxobj **vec = NULL;
|
||||
size_t veclen;
|
||||
int i;
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
evhtp_t *evhtp = NULL;
|
||||
struct event_base *evbase = NULL;
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
/* flag used for sanity of certs */
|
||||
|
|
@ -1639,28 +1579,6 @@ restconf_openssl_init(clicon_handle h,
|
|||
}
|
||||
rh = restconf_native_handle_get(h);
|
||||
rh->rh_ctx = ctx;
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
/* evhtp stuff */ /* XXX move this to global level */
|
||||
if ((evbase = event_base_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "event_base_new");
|
||||
goto done;
|
||||
}
|
||||
/* This is socket create a new evhtp_t instance */
|
||||
if ((evhtp = evhtp_new((void*)evbase, h)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "evhtp_new");
|
||||
goto done;
|
||||
}
|
||||
rh->rh_arg = evhtp;
|
||||
if (evhtp_set_cb(evhtp, "/" RESTCONF_API, restconf_path_root, h) == NULL){
|
||||
clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
|
||||
goto done;
|
||||
}
|
||||
/* Callback to be executed for all /restconf api calls */
|
||||
if (evhtp_set_cb(evhtp, RESTCONF_WELL_KNOWN, restconf_path_wellknown, h) == NULL){
|
||||
clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
|
||||
goto done;
|
||||
}
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
/* get the list of socket config-data */
|
||||
if (xpath_vec(xrestconf, nsc, "socket", &vec, &veclen) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue