- Restconf nghttp2 compiles
This commit is contained in:
parent
37da0aa45e
commit
b680e3c5ac
13 changed files with 1375 additions and 378 deletions
|
|
@ -35,7 +35,7 @@ Expected: June 2021
|
|||
### New features
|
||||
|
||||
* Started EXPERIMENTAL HTTP/2 work using nghttp2
|
||||
* Added autoconf config options, temporary for nghttp2 development: `--disable-evhtp`and `--enable-nghttp2` enabling http/1 only / http/2 only linking
|
||||
* Added autoconf config options, temporary for nghttp2 development: `--disable-evhtp`and `--enable-nghttp2` enabling http/1 only / http/2 only linki and compile.
|
||||
* YANG when statement in conjunction with grouping/uses/augment
|
||||
* Several cases were not implemented fully according to RFC 7950:
|
||||
* Do not extend default values if when statements evaluate to false
|
||||
|
|
@ -72,6 +72,12 @@ Users may have to change how they access the system
|
|||
* Previous meaning (wrong): Return all `a` elements.
|
||||
* New meaning (correct): Return the `a` instance with empty key string: "".
|
||||
|
||||
### C/CLI-API changes on existing features
|
||||
|
||||
Developers may need to change their code
|
||||
|
||||
*
|
||||
|
||||
### Minor features
|
||||
|
||||
* Added new startup-mode: `running-startup`: First try running db, if it is empty try startup db.
|
||||
|
|
|
|||
|
|
@ -99,6 +99,10 @@ APPSRC += restconf_methods_post.c
|
|||
APPSRC += restconf_methods_get.c
|
||||
APPSRC += restconf_root.c
|
||||
APPSRC += restconf_main_$(with_restconf).c
|
||||
ifeq ($(with_restconf),native)
|
||||
APPSRC += restconf_evhtp.c # HTTP/1
|
||||
APPSRC += restconf_nghttp2.c # HTTP/2
|
||||
endif
|
||||
|
||||
# Fcgi-specific source including main
|
||||
ifeq ($(with_restconf),fcgi)
|
||||
|
|
|
|||
|
|
@ -53,10 +53,14 @@
|
|||
/* evhtp */
|
||||
#define EVHTP_DISABLE_REGEX
|
||||
#define EVHTP_DISABLE_EVTHR
|
||||
|
||||
#include <evhtp/evhtp.h>
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
|
|
|
|||
405
apps/restconf/restconf_evhtp.c
Normal file
405
apps/restconf/restconf_evhtp.c
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020-2021 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 *****
|
||||
|
||||
* Evhtp specific HTTP/1 code complementing restconf_main_native.c
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
/* The clixon evhtp code can be compiled with or without threading support
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <pwd.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* 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>
|
||||
#include <evhtp/sslutils.h> /* XXX inline this / use SSL directly */
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2 /* To get restconf_native.h include files right */
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#endif
|
||||
|
||||
/* restconf */
|
||||
#include "restconf_lib.h" /* generic shared with plugins */
|
||||
#include "restconf_handle.h"
|
||||
#include "restconf_api.h" /* generic not shared with plugins */
|
||||
#include "restconf_err.h"
|
||||
#include "restconf_root.h"
|
||||
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
|
||||
#include "restconf_evhtp.h" /* evhtp http/1 */
|
||||
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
static char*
|
||||
evhtp_method2str(enum htp_method m)
|
||||
{
|
||||
switch (m){
|
||||
case htp_method_GET:
|
||||
return "GET";
|
||||
break;
|
||||
case htp_method_HEAD:
|
||||
return "HEAD";
|
||||
break;
|
||||
case htp_method_POST:
|
||||
return "POST";
|
||||
break;
|
||||
case htp_method_PUT:
|
||||
return "PUT";
|
||||
break;
|
||||
case htp_method_DELETE:
|
||||
return "DELETE";
|
||||
break;
|
||||
case htp_method_OPTIONS:
|
||||
return "OPTIONS";
|
||||
break;
|
||||
case htp_method_PATCH:
|
||||
return "PATCH";
|
||||
break;
|
||||
#ifdef NOTUSED
|
||||
case htp_method_MKCOL:
|
||||
return "MKCOL";
|
||||
break;
|
||||
case htp_method_COPY:
|
||||
return "COPY";
|
||||
break;
|
||||
case htp_method_MOVE:
|
||||
return "MOVE";
|
||||
break;
|
||||
case htp_method_OPTIONS:
|
||||
return "OPTIONS";
|
||||
break;
|
||||
case htp_method_PROPFIND:
|
||||
return "PROPFIND";
|
||||
break;
|
||||
case htp_method_PROPPATCH:
|
||||
return "PROPPATCH";
|
||||
break;
|
||||
case htp_method_LOCK:
|
||||
return "LOCK";
|
||||
break;
|
||||
case htp_method_UNLOCK:
|
||||
return "UNLOCK";
|
||||
break;
|
||||
case htp_method_TRACE:
|
||||
return "TRACE";
|
||||
break;
|
||||
case htp_method_CONNECT:
|
||||
return "CONNECT";
|
||||
break;
|
||||
#endif /* NOTUSED */
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
evhtp_print_header(evhtp_header_t *header,
|
||||
void *arg)
|
||||
{
|
||||
clicon_debug(1, "%s %s %s", __FUNCTION__, header->key, header->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
evhtp_query_iterator(evhtp_header_t *hdr,
|
||||
void *arg)
|
||||
{
|
||||
cvec *qvec = (cvec *)arg;
|
||||
char *key;
|
||||
char *val;
|
||||
char *valu = NULL; /* unescaped value */
|
||||
cg_var *cv;
|
||||
|
||||
key = hdr->key;
|
||||
val = hdr->val;
|
||||
if (uri_percent_decode(val, &valu) < 0)
|
||||
return -1;
|
||||
if ((cv = cvec_add(qvec, CGV_STRING)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_add");
|
||||
return -1;
|
||||
}
|
||||
cv_name_set(cv, key);
|
||||
cv_string_set(cv, valu);
|
||||
if (valu)
|
||||
free(valu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Translate http header by capitalizing, prepend w HTTP_ and - -> _
|
||||
* Example: Host -> HTTP_HOST
|
||||
*/
|
||||
static int
|
||||
evhtp_convert_fcgi(evhtp_header_t *hdr,
|
||||
void *arg)
|
||||
{
|
||||
clicon_handle h = (clicon_handle)arg;
|
||||
|
||||
return restconf_convert_hdr(h, hdr->key, hdr->val);
|
||||
}
|
||||
|
||||
/*! Map from evhtp information to "fcgi" type parameters used in clixon code
|
||||
*
|
||||
* While all these params come via one call in fcgi, the information must be taken from
|
||||
* several different places in evhtp
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Evhtp request struct
|
||||
* @param[out] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
|
||||
* @retval 1 OK continue
|
||||
* @retval 0 Fail, dont continue
|
||||
* @retval -1 Error
|
||||
* The following parameters are set:
|
||||
* QUERY_STRING
|
||||
* REQUEST_METHOD
|
||||
* REQUEST_URI
|
||||
* HTTPS
|
||||
* HTTP_HOST
|
||||
* HTTP_ACCEPT
|
||||
* HTTP_CONTENT_TYPE
|
||||
* @note there may be more used by an application plugin
|
||||
*/
|
||||
static int
|
||||
evhtp_params_set(clicon_handle h,
|
||||
evhtp_request_t *req,
|
||||
cvec *qvec)
|
||||
{
|
||||
int retval = -1;
|
||||
htp_method meth;
|
||||
evhtp_uri_t *uri;
|
||||
evhtp_path_t *path;
|
||||
evhtp_ssl_t *ssl = NULL;
|
||||
char *subject = NULL;
|
||||
cvec *cvv = NULL;
|
||||
char *cn;
|
||||
cxobj *xerr = NULL;
|
||||
int pretty;
|
||||
|
||||
if ((uri = req->uri) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "No uri");
|
||||
goto done;
|
||||
}
|
||||
if ((path = uri->path) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "No path");
|
||||
goto done;
|
||||
}
|
||||
meth = evhtp_request_get_method(req);
|
||||
|
||||
/* QUERY_STRING in fcgi but go direct to the info instead of putting it in a string?
|
||||
* This is different from all else: Ie one could have re-created a string here but
|
||||
* that would mean double parsing,...
|
||||
*/
|
||||
if (qvec && uri->query)
|
||||
if (evhtp_kvs_for_each(uri->query, evhtp_query_iterator, qvec) < 0){
|
||||
clicon_err(OE_CFG, errno, "evhtp_kvs_for_each");
|
||||
goto done;
|
||||
}
|
||||
if (restconf_param_set(h, "REQUEST_METHOD", evhtp_method2str(meth)) < 0)
|
||||
goto done;
|
||||
if (restconf_param_set(h, "REQUEST_URI", path->full) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s proto:%d", __FUNCTION__, req->proto);
|
||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||
/* XXX: Any two http numbers seem accepted by evhtp, like 1.99, 99.3 as http/1.1*/
|
||||
if (req->proto != EVHTP_PROTO_10 &&
|
||||
req->proto != EVHTP_PROTO_11){
|
||||
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid HTTP version number") < 0)
|
||||
goto done;
|
||||
/* Select json as default since content-type header may not be accessible yet */
|
||||
if (api_return_err0(h, req, xerr, pretty, YANG_DATA_JSON, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
clicon_debug(1, "%s conn->ssl:%d", __FUNCTION__, req->conn->ssl?1:0);
|
||||
if ((ssl = req->conn->ssl) != NULL){
|
||||
if (restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
|
||||
goto done;
|
||||
/* SSL subject fields, eg CN (Common Name) , can add more here? */
|
||||
if ((subject = (char*)htp_sslutil_subject_tostr(req->conn->ssl)) != NULL){
|
||||
if (uri_str2cvec(subject, '/', '=', 1, &cvv) < 0)
|
||||
goto done;
|
||||
if ((cn = cvec_find_str(cvv, "CN")) != NULL){
|
||||
if (restconf_param_set(h, "SSL_CN", cn) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Translate all http headers by capitalizing, prepend w HTTP_ and - -> _
|
||||
* Example: Host -> HTTP_HOST
|
||||
*/
|
||||
if (evhtp_headers_for_each(req->headers_in, evhtp_convert_fcgi, h) < 0)
|
||||
goto done;
|
||||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
if (subject)
|
||||
free(subject);
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (cvv)
|
||||
cvec_free(cvv);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Callback for each incoming http request for path /
|
||||
*
|
||||
* This are all messages except /.well-known, Registered with evhtp_set_cb
|
||||
*
|
||||
* @param[in] req evhtp http request structure defining the incoming message
|
||||
* @param[in] arg cx_evhtp handle clixon specific fields
|
||||
* @retval void
|
||||
* Discussion: problematic if fatal error -1 is returneod from clixon routines
|
||||
* without actually terminating. Consider:
|
||||
* 1) sending some error? and/or
|
||||
* 2) terminating the process?
|
||||
*/
|
||||
void
|
||||
restconf_path_root(evhtp_request_t *req,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h;
|
||||
int ret;
|
||||
cvec *qvec = NULL;
|
||||
|
||||
clicon_debug(1, "------------");
|
||||
if ((h = (clicon_handle)arg) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
|
||||
goto done;
|
||||
}
|
||||
/* input debug */
|
||||
if (clicon_debug_get())
|
||||
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
|
||||
|
||||
/* get accepted connection */
|
||||
/* Query vector, ie the ?a=x&b=y stuff */
|
||||
if ((qvec = cvec_new(0)) ==NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
/* set fcgi-like paramaters (ignore query vector) */
|
||||
if ((ret = evhtp_params_set(h, req, qvec)) < 0)
|
||||
goto done;
|
||||
if (ret == 1){
|
||||
/* call generic function */
|
||||
if (api_root_restconf(h, req, qvec) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Clear (fcgi) paramaters from this request */
|
||||
if (restconf_param_del_all(h) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
/* Catch all on fatal error. This does not terminate the process but closes request stream */
|
||||
if (retval < 0){
|
||||
evhtp_send_reply(req, EVHTP_RES_ERROR);
|
||||
}
|
||||
if (qvec)
|
||||
cvec_free(qvec);
|
||||
return; /* void */
|
||||
}
|
||||
|
||||
/*! /.well-known callback
|
||||
*
|
||||
* @param[in] req evhtp http request structure defining the incoming message
|
||||
* @param[in] arg cx_evhtp handle clixon specific fields
|
||||
* @retval void
|
||||
*/
|
||||
void
|
||||
restconf_path_wellknown(evhtp_request_t *req,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h;
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "------------");
|
||||
if ((h = (clicon_handle)arg) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
|
||||
goto done;
|
||||
}
|
||||
/* input debug */
|
||||
if (clicon_debug_get())
|
||||
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
|
||||
/* get accepted connection */
|
||||
|
||||
/* set fcgi-like paramaters (ignore query vector) */
|
||||
if ((ret = evhtp_params_set(h, req, NULL)) < 0)
|
||||
goto done;
|
||||
if (ret == 1){
|
||||
/* call generic function */
|
||||
if (api_well_known(h, req) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Clear (fcgi) paramaters from this request */
|
||||
if (restconf_param_del_all(h) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
/* Catch all on fatal error. This does not terminate the process but closes request stream */
|
||||
if (retval < 0){
|
||||
evhtp_send_reply(req, EVHTP_RES_ERROR);
|
||||
}
|
||||
return; /* void */
|
||||
}
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
||||
47
apps/restconf/restconf_evhtp.h
Normal file
47
apps/restconf/restconf_evhtp.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020-2021 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 *****
|
||||
*
|
||||
* Virtual clixon restconf API functions.
|
||||
*/
|
||||
|
||||
#ifndef _RESTCONF_EVHTP_H_
|
||||
#define _RESTCONF_EVHTP_H_
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
void restconf_path_root(evhtp_request_t *req, void *arg);
|
||||
void restconf_path_wellknown(evhtp_request_t *req, void *arg);
|
||||
|
||||
#endif /* _RESTCONF_EVHTP_H_ */
|
||||
|
|
@ -275,6 +275,43 @@ restconf_content_type(clicon_handle h)
|
|||
return m;
|
||||
}
|
||||
|
||||
/*! Translate http header by capitalizing, prepend w HTTP_ and - -> _
|
||||
* Example: Host -> HTTP_HOST
|
||||
*/
|
||||
int
|
||||
restconf_convert_hdr(clicon_handle h,
|
||||
char *name,
|
||||
char *val)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
int i;
|
||||
char c;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* convert key name */
|
||||
cprintf(cb, "HTTP_");
|
||||
for (i=0; i<strlen(name); i++){
|
||||
c = name[i] & 0xff;
|
||||
if (islower(c))
|
||||
cprintf(cb, "%c", toupper(c));
|
||||
else if (c == '-')
|
||||
cprintf(cb, "_");
|
||||
else
|
||||
cprintf(cb, "%c", c);
|
||||
}
|
||||
if (restconf_param_set(h, cbuf_get(cb), val) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Parse a cookie string and return value of cookie attribute
|
||||
* @param[in] cookiestr cookie string according to rfc6265 (modified)
|
||||
* @param[in] attribute cookie attribute
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ const char *restconf_media_int2str(restconf_media media);
|
|||
int restconf_str2proto(char *str);
|
||||
const char *restconf_proto2str(int proto);
|
||||
restconf_media restconf_content_type(clicon_handle h);
|
||||
int restconf_convert_hdr(clicon_handle h, char *name, char *val);
|
||||
int get_user_cookie(char *cookiestr, char *attribute, char **val);
|
||||
int restconf_terminate(clicon_handle h);
|
||||
int restconf_insert_attributes(cxobj *xdata, cvec *qvec);
|
||||
|
|
@ -96,6 +97,7 @@ int restconf_authentication_cb(clicon_handle h, void *req, int pretty, restcon
|
|||
int restconf_config_init(clicon_handle h, cxobj *xrestconf);
|
||||
int restconf_socket_init(const char *netns0, const char *addrstr, const char *addrtype, uint16_t port, int backlog, int flags, int *ss);
|
||||
int restconf_socket_extract(clicon_handle h, cxobj *xs, cvec *nsc, char **namespace, char **address, char **addrtype, uint16_t *port, uint16_t *ssl);
|
||||
int restconf_convert_hdr(clicon_handle h, char *name, char *val);
|
||||
|
||||
#endif /* _RESTCONF_LIB_H_ */
|
||||
|
||||
|
|
|
|||
|
|
@ -173,6 +173,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
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define RESTCONF_OPTS "hD:f:E:l:p:y:a:u:rW:R:o:"
|
||||
|
|
@ -332,363 +338,19 @@ print_cb(const char *str, size_t len, void *cb)
|
|||
|
||||
/* Clixon error category log callback
|
||||
* @param[in] handle Application-specific handle
|
||||
* @param[in] suberr Application-specific handle
|
||||
* @param[out] cb Read log/error string into this buffer
|
||||
*/
|
||||
static int
|
||||
openssl_cat_log_cb(void *handle,
|
||||
cbuf *cb)
|
||||
clixon_openssl_log_cb(void *handle,
|
||||
int suberr,
|
||||
cbuf *cb)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
ERR_print_errors_cb(print_cb, cb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
static char*
|
||||
evhtp_method2str(enum htp_method m)
|
||||
{
|
||||
switch (m){
|
||||
case htp_method_GET:
|
||||
return "GET";
|
||||
break;
|
||||
case htp_method_HEAD:
|
||||
return "HEAD";
|
||||
break;
|
||||
case htp_method_POST:
|
||||
return "POST";
|
||||
break;
|
||||
case htp_method_PUT:
|
||||
return "PUT";
|
||||
break;
|
||||
case htp_method_DELETE:
|
||||
return "DELETE";
|
||||
break;
|
||||
case htp_method_OPTIONS:
|
||||
return "OPTIONS";
|
||||
break;
|
||||
case htp_method_PATCH:
|
||||
return "PATCH";
|
||||
break;
|
||||
#ifdef NOTUSED
|
||||
case htp_method_MKCOL:
|
||||
return "MKCOL";
|
||||
break;
|
||||
case htp_method_COPY:
|
||||
return "COPY";
|
||||
break;
|
||||
case htp_method_MOVE:
|
||||
return "MOVE";
|
||||
break;
|
||||
case htp_method_OPTIONS:
|
||||
return "OPTIONS";
|
||||
break;
|
||||
case htp_method_PROPFIND:
|
||||
return "PROPFIND";
|
||||
break;
|
||||
case htp_method_PROPPATCH:
|
||||
return "PROPPATCH";
|
||||
break;
|
||||
case htp_method_LOCK:
|
||||
return "LOCK";
|
||||
break;
|
||||
case htp_method_UNLOCK:
|
||||
return "UNLOCK";
|
||||
break;
|
||||
case htp_method_TRACE:
|
||||
return "TRACE";
|
||||
break;
|
||||
case htp_method_CONNECT:
|
||||
return "CONNECT";
|
||||
break;
|
||||
#endif /* NOTUSED */
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
evhtp_print_header(evhtp_header_t *header,
|
||||
void *arg)
|
||||
{
|
||||
clicon_debug(1, "%s %s %s", __FUNCTION__, header->key, header->val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
evhtp_query_iterator(evhtp_header_t *hdr,
|
||||
void *arg)
|
||||
{
|
||||
cvec *qvec = (cvec *)arg;
|
||||
char *key;
|
||||
char *val;
|
||||
char *valu = NULL; /* unescaped value */
|
||||
cg_var *cv;
|
||||
|
||||
key = hdr->key;
|
||||
val = hdr->val;
|
||||
if (uri_percent_decode(val, &valu) < 0)
|
||||
return -1;
|
||||
if ((cv = cvec_add(qvec, CGV_STRING)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_add");
|
||||
return -1;
|
||||
}
|
||||
cv_name_set(cv, key);
|
||||
cv_string_set(cv, valu);
|
||||
if (valu)
|
||||
free(valu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Translate http header by capitalizing, prepend w HTTP_ and - -> _
|
||||
* Example: Host -> HTTP_HOST
|
||||
*/
|
||||
static int
|
||||
evhtp_convert_fcgi(evhtp_header_t *hdr,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h = (clicon_handle)arg;
|
||||
cbuf *cb = NULL;
|
||||
int i;
|
||||
char c;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* convert key name */
|
||||
cprintf(cb, "HTTP_");
|
||||
for (i=0; i<strlen(hdr->key); i++){
|
||||
c = hdr->key[i] & 0xff;
|
||||
if (islower(c))
|
||||
cprintf(cb, "%c", toupper(c));
|
||||
else if (c == '-')
|
||||
cprintf(cb, "_");
|
||||
else
|
||||
cprintf(cb, "%c", c);
|
||||
}
|
||||
if (restconf_param_set(h, cbuf_get(cb), hdr->val) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Map from evhtp information to "fcgi" type parameters used in clixon code
|
||||
*
|
||||
* While all these params come via one call in fcgi, the information must be taken from
|
||||
* several different places in evhtp
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Evhtp request struct
|
||||
* @param[out] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
|
||||
* @retval 1 OK continue
|
||||
* @retval 0 Fail, dont continue
|
||||
* @retval -1 Error
|
||||
* The following parameters are set:
|
||||
* QUERY_STRING
|
||||
* REQUEST_METHOD
|
||||
* REQUEST_URI
|
||||
* HTTPS
|
||||
* HTTP_HOST
|
||||
* HTTP_ACCEPT
|
||||
* HTTP_CONTENT_TYPE
|
||||
* @note there may be more used by an application plugin
|
||||
*/
|
||||
static int
|
||||
evhtp_params_set(clicon_handle h,
|
||||
evhtp_request_t *req,
|
||||
cvec *qvec)
|
||||
{
|
||||
int retval = -1;
|
||||
htp_method meth;
|
||||
evhtp_uri_t *uri;
|
||||
evhtp_path_t *path;
|
||||
evhtp_ssl_t *ssl = NULL;
|
||||
char *subject = NULL;
|
||||
cvec *cvv = NULL;
|
||||
char *cn;
|
||||
cxobj *xerr = NULL;
|
||||
int pretty;
|
||||
|
||||
if ((uri = req->uri) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "No uri");
|
||||
goto done;
|
||||
}
|
||||
if ((path = uri->path) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "No path");
|
||||
goto done;
|
||||
}
|
||||
meth = evhtp_request_get_method(req);
|
||||
|
||||
/* QUERY_STRING in fcgi but go direct to the info instead of putting it in a string?
|
||||
* This is different from all else: Ie one could have re-created a string here but
|
||||
* that would mean double parsing,...
|
||||
*/
|
||||
if (qvec && uri->query)
|
||||
if (evhtp_kvs_for_each(uri->query, evhtp_query_iterator, qvec) < 0){
|
||||
clicon_err(OE_CFG, errno, "evhtp_kvs_for_each");
|
||||
goto done;
|
||||
}
|
||||
if (restconf_param_set(h, "REQUEST_METHOD", evhtp_method2str(meth)) < 0)
|
||||
goto done;
|
||||
if (restconf_param_set(h, "REQUEST_URI", path->full) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s proto:%d", __FUNCTION__, req->proto);
|
||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||
/* XXX: Any two http numbers seem accepted by evhtp, like 1.99, 99.3 as http/1.1*/
|
||||
if (req->proto != EVHTP_PROTO_10 &&
|
||||
req->proto != EVHTP_PROTO_11){
|
||||
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid HTTP version number") < 0)
|
||||
goto done;
|
||||
/* Select json as default since content-type header may not be accessible yet */
|
||||
if (api_return_err0(h, req, xerr, pretty, YANG_DATA_JSON, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
clicon_debug(1, "%s conn->ssl:%d", __FUNCTION__, req->conn->ssl?1:0);
|
||||
if ((ssl = req->conn->ssl) != NULL){
|
||||
if (restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
|
||||
goto done;
|
||||
/* SSL subject fields, eg CN (Common Name) , can add more here? */
|
||||
if ((subject = (char*)htp_sslutil_subject_tostr(req->conn->ssl)) != NULL){
|
||||
if (uri_str2cvec(subject, '/', '=', 1, &cvv) < 0)
|
||||
goto done;
|
||||
if ((cn = cvec_find_str(cvv, "CN")) != NULL){
|
||||
if (restconf_param_set(h, "SSL_CN", cn) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Translate all http headers by capitalizing, prepend w HTTP_ and - -> _
|
||||
* Example: Host -> HTTP_HOST
|
||||
*/
|
||||
if (evhtp_headers_for_each(req->headers_in, evhtp_convert_fcgi, h) < 0)
|
||||
goto done;
|
||||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
if (subject)
|
||||
free(subject);
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (cvv)
|
||||
cvec_free(cvv);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Callback for each incoming http request for path /
|
||||
*
|
||||
* This are all messages except /.well-known, Registered with evhtp_set_cb
|
||||
*
|
||||
* @param[in] req evhtp http request structure defining the incoming message
|
||||
* @param[in] arg cx_evhtp handle clixon specific fields
|
||||
* @retval void
|
||||
* Discussion: problematic if fatal error -1 is returneod from clixon routines
|
||||
* without actually terminating. Consider:
|
||||
* 1) sending some error? and/or
|
||||
* 2) terminating the process?
|
||||
*/
|
||||
static void
|
||||
restconf_path_root(evhtp_request_t *req,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h;
|
||||
int ret;
|
||||
cvec *qvec = NULL;
|
||||
|
||||
clicon_debug(1, "------------");
|
||||
if ((h = (clicon_handle)arg) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
|
||||
goto done;
|
||||
}
|
||||
/* input debug */
|
||||
if (clicon_debug_get())
|
||||
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
|
||||
|
||||
/* get accepted connection */
|
||||
/* Query vector, ie the ?a=x&b=y stuff */
|
||||
if ((qvec = cvec_new(0)) ==NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
/* set fcgi-like paramaters (ignore query vector) */
|
||||
if ((ret = evhtp_params_set(h, req, qvec)) < 0)
|
||||
goto done;
|
||||
if (ret == 1){
|
||||
/* call generic function */
|
||||
if (api_root_restconf(h, req, qvec) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Clear (fcgi) paramaters from this request */
|
||||
if (restconf_param_del_all(h) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
/* Catch all on fatal error. This does not terminate the process but closes request stream */
|
||||
if (retval < 0){
|
||||
evhtp_send_reply(req, EVHTP_RES_ERROR);
|
||||
}
|
||||
if (qvec)
|
||||
cvec_free(qvec);
|
||||
return; /* void */
|
||||
}
|
||||
|
||||
/*! /.well-known callback
|
||||
*
|
||||
* @param[in] req evhtp http request structure defining the incoming message
|
||||
* @param[in] arg cx_evhtp handle clixon specific fields
|
||||
* @retval void
|
||||
*/
|
||||
static void
|
||||
restconf_path_wellknown(evhtp_request_t *req,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h;
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "------------");
|
||||
if ((h = (clicon_handle)arg) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
|
||||
goto done;
|
||||
}
|
||||
/* input debug */
|
||||
if (clicon_debug_get())
|
||||
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
|
||||
/* get accepted connection */
|
||||
|
||||
/* set fcgi-like paramaters (ignore query vector) */
|
||||
if ((ret = evhtp_params_set(h, req, NULL)) < 0)
|
||||
goto done;
|
||||
if (ret == 1){
|
||||
/* call generic function */
|
||||
if (api_well_known(h, req) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Clear (fcgi) paramaters from this request */
|
||||
if (restconf_param_del_all(h) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
/* Catch all on fatal error. This does not terminate the process but closes request stream */
|
||||
if (retval < 0){
|
||||
evhtp_send_reply(req, EVHTP_RES_ERROR);
|
||||
}
|
||||
return; /* void */
|
||||
}
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
||||
/*
|
||||
* see restconf_config ->cv_evhtp_init(x2) -> cx_evhtp_socket ->
|
||||
* evhtp_ssl_init:4757
|
||||
|
|
@ -951,7 +613,7 @@ close_ssl_socket(restconf_conn_h *rc,
|
|||
#ifdef HAVE_LIBEVHTP
|
||||
evhtp_connection_t *evconn;
|
||||
|
||||
evconn = (evhtp_connection_t *)rc->rc_arg;
|
||||
evconn = rc->rc_evconn;
|
||||
clicon_debug(1, "%s evconn-free (%p) 1", __FUNCTION__, evconn);
|
||||
evhtp_connection_free(evconn); /* evhtp */
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
|
@ -1037,7 +699,7 @@ restconf_connection(int s,
|
|||
int retval = -1;
|
||||
restconf_conn_h *rc = NULL;
|
||||
ssize_t n;
|
||||
char buf[BUFSIZ]; /* from stdio.h, typically 8K */
|
||||
char buf[BUFSIZ]; /* from stdio.h, typically 8K XXX: reduce for test */
|
||||
int readmore = 1;
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
clicon_handle h;
|
||||
|
|
@ -1083,12 +745,15 @@ restconf_connection(int s,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
switch (rc->rc_proto){
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
case HTTP_10:
|
||||
case HTTP_11:
|
||||
h = rc->rc_h;
|
||||
/* parse incoming packet using evhtp
|
||||
* signature:
|
||||
*/
|
||||
evconn = (evhtp_connection_t*)rc->rc_arg;
|
||||
evconn = rc->rc_evconn;
|
||||
if (connection_parse_nobev(buf, n, evconn) < 0){
|
||||
clicon_debug(1, "%s connection_parse error", __FUNCTION__);
|
||||
/* XXX To get more nuanced evhtp error check
|
||||
|
|
@ -1155,6 +820,15 @@ restconf_connection(int s,
|
|||
goto done;
|
||||
}
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
case HTTP_2:
|
||||
if (http2_recv(rc, (unsigned char *)buf, n) < 0)
|
||||
goto done;
|
||||
break;
|
||||
#endif /* HAVE_LIBNGHTTP2 */
|
||||
default:
|
||||
break;
|
||||
} /* switch rc_proto */
|
||||
} /* while readmore */
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -1282,7 +956,7 @@ ssl_alpn_check(clicon_handle h,
|
|||
{
|
||||
evhtp_connection_t *evconn;
|
||||
|
||||
if ((evconn = (evhtp_connection_t *)rc->rc_arg) != NULL)
|
||||
if ((evconn = rc->rc_evconn) != NULL)
|
||||
evhtp_connection_free(evconn); /* evhtp */
|
||||
}
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
|
@ -1537,21 +1211,29 @@ restconf_accept_client(int fd,
|
|||
#ifdef HAVE_LIBEVHTP
|
||||
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(rh->rh_evhtp, rc->rc_s)) == NULL){
|
||||
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_arg = evconn; /* Generic to specific */
|
||||
rc->rc_evconn = evconn; /* Generic to specific */
|
||||
evconn->arg = rc; /* Specific to generic */
|
||||
evconn->ssl = rc->rc_ssl; /* evhtp */
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
case HTTP_2:
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
case HTTP_2:{
|
||||
if (http2_session_init(rc) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
#endif /* HAVE_LIBNGHTTP2 */
|
||||
default:
|
||||
break;
|
||||
} /* switch proto */
|
||||
|
|
@ -1587,10 +1269,14 @@ restconf_native_terminate(clicon_handle h)
|
|||
if (rh->rh_ctx)
|
||||
SSL_CTX_free(rh->rh_ctx);
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
if (rh->rh_evhtp){
|
||||
if (rh->rh_evhtp->evbase)
|
||||
event_base_free(rh->rh_evhtp->evbase);
|
||||
evhtp_free(rh->rh_evhtp);
|
||||
{
|
||||
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 */
|
||||
|
||||
|
|
@ -1836,7 +1522,7 @@ restconf_openssl_init(clicon_handle h,
|
|||
clicon_err(OE_UNIX, errno, "evhtp_new");
|
||||
goto done;
|
||||
}
|
||||
rh->rh_evhtp = evhtp;
|
||||
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;
|
||||
|
|
@ -2140,9 +1826,19 @@ main(int argc,
|
|||
*/
|
||||
if (clixon_err_cat_reg(OE_SSL, /* category */
|
||||
h, /* handle (can be NULL) */
|
||||
openssl_cat_log_cb /* log fn */
|
||||
clixon_openssl_log_cb /* log fn */
|
||||
) < 0)
|
||||
goto done;
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
/*
|
||||
* Register error category and error/log callbacks for openssl special error handling
|
||||
*/
|
||||
if (clixon_err_cat_reg(OE_NGHTTP2, /* category */
|
||||
h, /* handle (can be NULL) */
|
||||
clixon_nghttp2_log_cb /* log fn */
|
||||
) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
clicon_debug_init(dbg, NULL);
|
||||
clicon_log(LOG_NOTICE, "%s native %u Started", __PROGRAM__, getpid());
|
||||
if (set_signal(SIGTERM, restconf_sig_term, NULL) < 0){
|
||||
|
|
|
|||
|
|
@ -59,19 +59,36 @@ extern "C" {
|
|||
/*
|
||||
* Types
|
||||
*/
|
||||
/* http/2 session stream struct
|
||||
*/
|
||||
typedef struct {
|
||||
qelem_t sd_qelem; /* List header */
|
||||
int32_t sd_stream_id;
|
||||
int sd_fd;
|
||||
} restconf_stream_data;
|
||||
|
||||
/* Restconf connection handle
|
||||
* Per connection request
|
||||
*/
|
||||
typedef struct {
|
||||
// qelem_t rs_qelem; /* List header */
|
||||
cvec *rc_outp_hdrs; /* List of output headers */
|
||||
cbuf *rc_outp_buf; /* Output buffer */
|
||||
size_t rc_bufferevent_output_offset; /* Kludge to drain libevent output buffer */
|
||||
cvec *rc_outp_hdrs; /* List of output headers */
|
||||
cbuf *rc_outp_buf; /* Output buffer */
|
||||
size_t rc_bufferevent_output_offset; /* Kludge to drain libevent output buffer */
|
||||
restconf_http_proto rc_proto; /* HTTP protocol: http/1 or http/2 */
|
||||
int rc_s; /* Connection socket */
|
||||
clicon_handle rc_h; /* Clixon handle */
|
||||
SSL *rc_ssl; /* Structure for SSL connection */
|
||||
void *rc_arg; /* Specific connection pointer, eg evhtp conn struct */
|
||||
int rc_s; /* Connection socket */
|
||||
clicon_handle rc_h; /* Clixon handle */
|
||||
SSL *rc_ssl; /* Structure for SSL connection */
|
||||
restconf_stream_data *rc_streams; /* List of http/2 session streams */
|
||||
/* Decision to keep lib-specific data here, otherwise new struct necessary
|
||||
* drawback is specific includes need to go everywhere */
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
evhtp_connection_t *rc_evconn;
|
||||
#endif
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
|
||||
nghttp2_session *rc_ngsession;
|
||||
#endif
|
||||
} restconf_conn_h;
|
||||
|
||||
/* Restconf request handle
|
||||
|
|
@ -94,9 +111,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
SSL_CTX *rh_ctx; /* SSL context */
|
||||
restconf_socket *rh_sockets; /* List of restconf server (ready for accept) sockets */
|
||||
#ifdef HAVE_LIBEVHTP
|
||||
evhtp_t *rh_evhtp; /* Evhtp struct */
|
||||
#endif
|
||||
void *rh_arg; /* Packet specific handle (eg evhtp) */
|
||||
} restconf_native_handle;
|
||||
|
||||
/*
|
||||
|
|
|
|||
727
apps/restconf/restconf_nghttp2.c
Normal file
727
apps/restconf/restconf_nghttp2.c
Normal file
|
|
@ -0,0 +1,727 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020-2021 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 *****
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#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>
|
||||
#include <syslog.h>
|
||||
#include <pwd.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509v3.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
#ifdef HAVE_LIBEVHTP /* To get restconf_native.h include files right */
|
||||
/* evhtp */
|
||||
#include <event2/buffer.h> /* evbuffer */
|
||||
#define EVHTP_DISABLE_REGEX
|
||||
#define EVHTP_DISABLE_EVTHR
|
||||
#include <evhtp/evhtp.h>
|
||||
#endif /* HAVE_LIBEVHTP */
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#endif
|
||||
|
||||
/* restconf */
|
||||
#include "restconf_lib.h" /* generic shared with plugins */
|
||||
#include "restconf_handle.h"
|
||||
#include "restconf_api.h" /* generic not shared with plugins */
|
||||
#include "restconf_err.h"
|
||||
#include "restconf_root.h"
|
||||
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
|
||||
#include "restconf_nghttp2.h" /* Restconf-openssl mode specific headers*/
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2 /* XXX MOVE */
|
||||
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
static restconf_stream_data *
|
||||
restconf_stream_data_new(restconf_conn_h *rc,
|
||||
int32_t stream_id)
|
||||
{
|
||||
restconf_stream_data *sd;
|
||||
|
||||
sd = malloc(sizeof(restconf_stream_data));
|
||||
memset(sd, 0, sizeof(restconf_stream_data));
|
||||
sd->sd_stream_id = stream_id;
|
||||
sd->sd_fd = -1;
|
||||
INSQ(sd, rc->rc_streams);
|
||||
return sd;
|
||||
}
|
||||
|
||||
#ifdef NOTUSED
|
||||
static void
|
||||
delete_http2_stream_data(restconf_stream_data *sd)
|
||||
{
|
||||
if (sd->fd != -1) {
|
||||
close(sd->fd);
|
||||
}
|
||||
free(sd->request_path);
|
||||
free(sd);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NOTUSED
|
||||
static int
|
||||
send_client_connection_header(nghttp2_session *session)
|
||||
{
|
||||
int retval = -1;
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
int rv;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||
rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, ARRLEN(iv));
|
||||
if (rv != 0) {
|
||||
clicon_err(OE_XML, 0, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
#endif /* NOTUSED */
|
||||
|
||||
/* Clixon error category specialized log callback for nghttp2
|
||||
* @param[in] handle Application-specific handle
|
||||
* @param[in] suberr Application-specific handle
|
||||
* @param[out] cb Read log/error string into this buffer
|
||||
*/
|
||||
int
|
||||
clixon_nghttp2_log_cb(void *handle,
|
||||
int suberr,
|
||||
cbuf *cb)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
cprintf(cb, "Fatal error: %s", nghttp2_strerror(suberr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef NOTUSED
|
||||
static void
|
||||
nghttp2_print_header(const uint8_t *name,
|
||||
size_t namelen,
|
||||
const uint8_t *value,
|
||||
size_t valuelen)
|
||||
{
|
||||
clicon_debug(1, "%s %s", name, value);
|
||||
}
|
||||
|
||||
/* Print HTTP headers to |f|. Please note that this function does not
|
||||
take into account that header name and value are sequence of
|
||||
octets, therefore they may contain non-printable characters. */
|
||||
static void
|
||||
nghttp2_print_headers(nghttp2_nv *nva,
|
||||
size_t nvlen)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nvlen; ++i)
|
||||
nghttp2_print_header(nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen);
|
||||
}
|
||||
#endif /* NOTUSED */
|
||||
|
||||
/*! Transmit the |data|, |length| bytes, to the network.
|
||||
* This callback is required if the application uses
|
||||
* `nghttp2_session_send()` to send data to the remote endpoint. If
|
||||
* the application uses solely `nghttp2_session_mem_send()` instead,
|
||||
* this callback function is unnecessary.
|
||||
*/
|
||||
static ssize_t
|
||||
send_callback(nghttp2_session *session,
|
||||
const uint8_t *data,
|
||||
size_t length,
|
||||
int flags,
|
||||
void *user_data)
|
||||
{
|
||||
restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "%s %zu:", __FUNCTION__, length);
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<length; i++)
|
||||
fprintf(stderr, "%02x", data[i]&255);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
/* encrypt & send message */
|
||||
if ((ret = SSL_write(rc->rc_ssl, data, length)) < 0)
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! Invoked when |session| wants to receive data from the remote peer.
|
||||
*/
|
||||
static ssize_t
|
||||
recv_callback(nghttp2_session *session,
|
||||
uint8_t *buf,
|
||||
size_t length,
|
||||
int flags,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Callback for each incoming http request for path /
|
||||
*
|
||||
* This are all messages except /.well-known, Registered with evhtp_set_cb
|
||||
*
|
||||
* @param[in] req evhtp http request structure defining the incoming message
|
||||
* @param[in] arg cx_evhtp handle clixon specific fields
|
||||
* @retval void
|
||||
* Discussion: problematic if fatal error -1 is returneod from clixon routines
|
||||
* without actually terminating. Consider:
|
||||
* 1) sending some error? and/or
|
||||
* 2) terminating the process?
|
||||
*/
|
||||
static int
|
||||
restconf_nghttp2_root(restconf_conn_h *rc)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h;
|
||||
// int ret;
|
||||
cvec *qvec = NULL;
|
||||
|
||||
clicon_debug(1, "------------");
|
||||
if ((h = rc->rc_h) == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* get accepted connection */
|
||||
/* Query vector, ie the ?a=x&b=y stuff */
|
||||
if ((qvec = cvec_new(0)) ==NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
/* call generic function */
|
||||
if (api_root_restconf(h, rc, qvec) < 0)
|
||||
goto done;
|
||||
// /* Clear (fcgi) paramaters from this request */
|
||||
// if (restconf_param_del_all(h) < 0)
|
||||
// goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
/* Catch all on fatal error. This does not terminate the process but closes request stream */
|
||||
// if (retval < 0){
|
||||
// evhtp_send_reply(req, EVHTP_RES_ERROR);
|
||||
// }
|
||||
if (qvec)
|
||||
cvec_free(qvec);
|
||||
return retval; /* void */
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
int retval = -1;
|
||||
restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
restconf_stream_data *sd;
|
||||
char *path;
|
||||
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, frame->hd.stream_id);
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
case NGHTTP2_HEADERS:
|
||||
/* Check that the client request has finished */
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
/* For DATA and HEADERS frame, this callback may be called after
|
||||
on_stream_close_callback. Check that stream still alive. */
|
||||
if ((sd = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) == NULL)
|
||||
return 0;
|
||||
if ((path = restconf_uripath(rc->rc_h)) == NULL)
|
||||
goto ok;
|
||||
if (strcmp(path, "/" RESTCONF_API) == 0){
|
||||
if (restconf_nghttp2_root(rc) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(path, RESTCONF_WELL_KNOWN) == 0){
|
||||
}
|
||||
else
|
||||
; /* ignore */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
on_invalid_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
int lib_error_code,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
on_data_chunk_recv_callback(nghttp2_session *session,
|
||||
uint8_t flags,
|
||||
int32_t stream_id,
|
||||
const uint8_t *data,
|
||||
size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, stream_id);
|
||||
// if (sd->sd_session == session &&
|
||||
// sd->sd_stream_id == stream_id)
|
||||
// fwrite(data, 1, len, stdout); /* This is where data is printed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
before_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
on_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
on_frame_not_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
int lib_error_code,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
on_stream_close_callback(nghttp2_session *session,
|
||||
int32_t stream_id,
|
||||
nghttp2_error_code error_code,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
//session_data *sd = (session_data*)user_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Reception of header block in HEADERS or PUSH_PROMISE is started.
|
||||
*/
|
||||
static int
|
||||
on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
restconf_stream_data *sd;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (frame->hd.type == NGHTTP2_HEADERS &&
|
||||
frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
sd = restconf_stream_data_new(rc, frame->hd.stream_id);
|
||||
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, sd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef XXX
|
||||
/*! Translate http header by capitalizing, prepend w HTTP_ and - -> _
|
||||
* Example: Host -> HTTP_HOST
|
||||
*/
|
||||
static int
|
||||
evhtp_convert_fcgi(evhtp_header_t *hdr,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h = (clicon_handle)arg;
|
||||
cbuf *cb = NULL;
|
||||
int i;
|
||||
char c;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* convert key name */
|
||||
cprintf(cb, "HTTP_");
|
||||
for (i=0; i<strlen(hdr->key); i++){
|
||||
c = hdr->key[i] & 0xff;
|
||||
if (islower(c))
|
||||
cprintf(cb, "%c", toupper(c));
|
||||
else if (c == '-')
|
||||
cprintf(cb, "_");
|
||||
else
|
||||
cprintf(cb, "%c", c);
|
||||
}
|
||||
if (restconf_param_set(h, cbuf_get(cb), hdr->val) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! Map from nghttp2 headers to "fcgi" type parameters used in clixon code
|
||||
* Both |name| and |value| are guaranteed to be NULL-terminated.
|
||||
*/
|
||||
static int
|
||||
nghttp2_hdr2clixon(clicon_handle h,
|
||||
char *name,
|
||||
char *value)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (strcmp(name, ":path") == 0){
|
||||
/* XXX "/restconf" Is PATH really REQUEST_URI? */
|
||||
if (restconf_param_set(h, "REQUEST_URI", value) < 0) /* XXX string? */
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(name, ":method") == 0){
|
||||
if (restconf_param_set(h, "REQUEST_METHOD", value) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(name, ":scheme") == 0){
|
||||
if (strcmp(value, "https") == 0 &&
|
||||
restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(name, ":authority") == 0){
|
||||
if (restconf_param_set(h, "HTTP_HOST", value) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (restconf_convert_hdr(h, name, value) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Header name/value pair is received
|
||||
* Both |name| and |value| are guaranteed to be NULL-terminated.
|
||||
* If the application uses `nghttp2_session_mem_recv()`, it can return
|
||||
* :enum:`NGHTTP2_ERR_PAUSE` to make `nghttp2_session_mem_recv()`
|
||||
* return without processing further input bytes.
|
||||
*/
|
||||
static int
|
||||
on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
const uint8_t *name,
|
||||
size_t namelen,
|
||||
const uint8_t *value,
|
||||
size_t valuelen,
|
||||
uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
int retval = -1;
|
||||
restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
restconf_stream_data *sd;
|
||||
|
||||
clicon_debug(1, "%s %d:", __FUNCTION__, frame->hd.stream_id);
|
||||
switch (frame->hd.type){
|
||||
case NGHTTP2_HEADERS:
|
||||
assert (frame->headers.cat == NGHTTP2_HCAT_REQUEST);
|
||||
clicon_debug(1, "%s %s %s", __FUNCTION__, name, value);
|
||||
if ((sd = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) == NULL)
|
||||
break;
|
||||
if (nghttp2_hdr2clixon(rc->rc_h, (char*)name, (char*)value) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static ssize_t
|
||||
select_padding_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
size_t max_payloadlen,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static ssize_t
|
||||
data_source_read_length_callback(nghttp2_session *session,
|
||||
uint8_t frame_type,
|
||||
int32_t stream_id,
|
||||
int32_t session_remote_window_size,
|
||||
int32_t stream_remote_window_size,
|
||||
uint32_t remote_max_frame_size,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Invoked when a frame header is received.
|
||||
* Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will
|
||||
* also be called when frame header of CONTINUATION frame is received.
|
||||
*/
|
||||
static int
|
||||
on_begin_frame_callback(nghttp2_session *session,
|
||||
const nghttp2_frame_hd *hd,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s type:%d", __FUNCTION__, hd->type);
|
||||
|
||||
if (hd->type == NGHTTP2_CONTINUATION)
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
send_data_callback(nghttp2_session *session,
|
||||
nghttp2_frame *frame,
|
||||
const uint8_t *framehd, size_t length,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static ssize_t
|
||||
pack_extension_callback(nghttp2_session *session,
|
||||
uint8_t *buf, size_t len,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
unpack_extension_callback(nghttp2_session *session,
|
||||
void **payload,
|
||||
const nghttp2_frame_hd *hd,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
on_extension_chunk_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame_hd *hd,
|
||||
const uint8_t *data,
|
||||
size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
error_callback(nghttp2_session *session,
|
||||
const char *msg,
|
||||
size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
error_callback2(nghttp2_session *session,
|
||||
int lib_error_code,
|
||||
const char *msg,
|
||||
size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX see session_recv
|
||||
*/
|
||||
int
|
||||
http2_recv(restconf_conn_h *rc,
|
||||
const unsigned char *buf,
|
||||
size_t n)
|
||||
{
|
||||
int retval = -1;
|
||||
nghttp2_error ngerr;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (rc->rc_ngsession == NULL){
|
||||
clicon_err(OE_RESTCONF, EINVAL, "No nghttp2 session");
|
||||
goto done;
|
||||
}
|
||||
if ((ngerr = nghttp2_session_mem_recv(rc->rc_ngsession, buf, n)) < 0){
|
||||
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_mem_recv");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Initialize callbacks
|
||||
*/
|
||||
int
|
||||
http2_session_init(restconf_conn_h *rc)
|
||||
{
|
||||
nghttp2_session_callbacks *callbacks = NULL;
|
||||
nghttp2_session *session = NULL;
|
||||
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(callbacks, on_invalid_frame_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, on_data_chunk_recv_callback);
|
||||
nghttp2_session_callbacks_set_before_frame_send_callback(callbacks, before_frame_send_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, on_frame_send_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_not_send_callback(callbacks, on_frame_not_send_callback);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, on_begin_headers_callback);
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback);
|
||||
nghttp2_session_callbacks_set_select_padding_callback(callbacks, select_padding_callback);
|
||||
nghttp2_session_callbacks_set_data_source_read_length_callback(callbacks, data_source_read_length_callback);
|
||||
nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks, on_begin_frame_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_callback);
|
||||
nghttp2_session_callbacks_set_pack_extension_callback(callbacks, pack_extension_callback);
|
||||
nghttp2_session_callbacks_set_unpack_extension_callback(callbacks, unpack_extension_callback);
|
||||
nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(callbacks, on_extension_chunk_recv_callback);
|
||||
nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
|
||||
nghttp2_session_callbacks_set_error_callback2(callbacks, error_callback2);
|
||||
|
||||
/* Register callbacks with nghttp2 */
|
||||
nghttp2_session_server_new(&session, callbacks, rc);
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
rc->rc_ngsession = session;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBNGHTTP2 */
|
||||
48
apps/restconf/restconf_nghttp2.h
Normal file
48
apps/restconf/restconf_nghttp2.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020-2021 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 *****
|
||||
*
|
||||
* Virtual clixon restconf API functions.
|
||||
*/
|
||||
|
||||
#ifndef _RESTCONF_NGHTTP2_H_
|
||||
#define _RESTCONF_NGHTTP2_H_
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int clixon_nghttp2_log_cb(void *handle, int suberr, cbuf *cb);
|
||||
int http2_recv(restconf_conn_h *rc, const unsigned char *buf, size_t n);
|
||||
int http2_session_init(restconf_conn_h *rc);
|
||||
|
||||
#endif /* _RESTCONF_NGHTTP2_H_ */
|
||||
|
|
@ -70,19 +70,23 @@ enum clicon_err{
|
|||
OE_SYSLOG, /* syslog error */
|
||||
OE_ROUTING, /* routing daemon error (eg quagga) */
|
||||
OE_XML, /* xml parsing etc */
|
||||
OE_SSL, /* Openssl errors, see eg ssl_get_error */
|
||||
|
||||
OE_RESTCONF, /* RESTCONF errors */
|
||||
OE_PLUGIN, /* plugin loading, etc */
|
||||
OE_YANG , /* Yang error */
|
||||
OE_FATAL, /* Fatal error */
|
||||
OE_UNDEF,
|
||||
/*-- From here error extensions using clixon_err_cat_reg, XXX register dynamically? --*/
|
||||
OE_SSL, /* Openssl errors, see eg ssl_get_error */
|
||||
OE_NGHTTP2, /* nghttp2 errors, see HAVE_LIBNGHTTP2 */
|
||||
};
|
||||
|
||||
/* Clixon error category log callback
|
||||
* @param[in] handle Application-specific handle
|
||||
* @param[in] suberr Application-specific handle
|
||||
* @param[out] cb Read log/error string into this buffer
|
||||
*/
|
||||
typedef int (clixon_cat_log_cb)(void *handle, cbuf *cb);
|
||||
typedef int (clixon_cat_log_cb)(void *handle, int suberr, cbuf *cb);
|
||||
|
||||
/*
|
||||
* Variables
|
||||
|
|
|
|||
|
|
@ -116,12 +116,14 @@ static struct errvec EV[] = {
|
|||
{"Syslog error", OE_SYSLOG},
|
||||
{"Routing demon error", OE_ROUTING},
|
||||
{"XML error", OE_XML},
|
||||
{"OpenSSL error", OE_SSL},
|
||||
{"RESTCONF error", OE_RESTCONF},
|
||||
{"Plugins", OE_PLUGIN},
|
||||
{"Yang error", OE_YANG},
|
||||
{"FATAL", OE_FATAL},
|
||||
{"Undefined", OE_UNDEF},
|
||||
/* From here error extensions using clixon_err_cat_reg */
|
||||
{"OpenSSL error", OE_SSL},
|
||||
{"Nghttp2 error", OE_NGHTTP2},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
|
|
@ -224,7 +226,7 @@ clicon_err_fn(const char *fn,
|
|||
va_end(args);
|
||||
strncpy(clicon_err_reason, msg, ERR_STRLEN-1);
|
||||
|
||||
/* Check category callbacks */
|
||||
/* Check category callbacks as defined in clixon_err_cat_reg */
|
||||
if ((cec = find_category(category)) != NULL &&
|
||||
cec->cec_logfn){
|
||||
cbuf *cb = NULL;
|
||||
|
|
@ -232,7 +234,7 @@ clicon_err_fn(const char *fn,
|
|||
fprintf(stderr, "cbuf_new: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
|
||||
goto done;
|
||||
}
|
||||
if (cec->cec_logfn(cec->cec_handle, cb) < 0)
|
||||
if (cec->cec_logfn(cec->cec_handle, suberr, cb) < 0)
|
||||
goto done;
|
||||
/* Here we could take care of specific errno, like application-defined errors */
|
||||
clicon_log(LOG_ERR, "%s: %d: %s: %s: %s",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue