* Updated "evhtp" restconf mode
* No reliance on libevent or libevhtp, but on libssl >= 1.1 directly
* Moved out event handling to clixon event handling
* Moved out all ssl calls to clixon
* New code MUST use libevhtp from https://github.com/clixon/clixon-libevhtp.git
* This does NOT work: libevhtp from https://github.com/criticalstack/libevhtp.git
This commit is contained in:
parent
95a820c862
commit
c7e7598e3b
26 changed files with 1506 additions and 944 deletions
|
|
@ -77,7 +77,6 @@ endif
|
|||
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
|
||||
|
||||
LIBS = -L$(top_srcdir)/lib/src $(top_srcdir)/lib/src/$(CLIXON_LIB) @LIBS@
|
||||
#LIBS += -lpthread -levent -levent_openssl -lssl -lcrypto
|
||||
|
||||
ifeq ($(LINKAGE),dynamic)
|
||||
CPPFLAGS = @CPPFLAGS@ -fPIC
|
||||
|
|
|
|||
|
|
@ -32,29 +32,25 @@
|
|||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* Concrete functions for libevhtp of the
|
||||
* Concrete functions for openssl of the
|
||||
* Virtual clixon restconf API functions.
|
||||
* @see restconf_api.h for virtual API
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
/* evhtp */
|
||||
#include <evhtp/evhtp.h>
|
||||
#include <evhtp/sslutils.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -65,15 +61,6 @@
|
|||
#include "restconf_lib.h"
|
||||
#include "restconf_api.h" /* Virtual api */
|
||||
|
||||
/* evhtp_safe_free is a macro that may not be present in a libevhtp release
|
||||
*/
|
||||
#ifndef evhtp_safe_free
|
||||
#define evhtp_safe_free(_var, _freefn) do { \
|
||||
_freefn((_var)); \
|
||||
(_var) = NULL; \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/*! Add HTTP header field name and value to reply, evhtp specific
|
||||
* @param[in] req Evhtp http request handle
|
||||
* @param[in] name HTTP header field name
|
||||
|
|
@ -85,7 +72,6 @@ restconf_reply_header(void *req0,
|
|||
const char *name,
|
||||
const char *vfmt,
|
||||
...)
|
||||
|
||||
{
|
||||
evhtp_request_t *req = (evhtp_request_t *)req0;
|
||||
int retval = -1;
|
||||
|
|
@ -139,8 +125,9 @@ restconf_reply_send(void *req0,
|
|||
{
|
||||
evhtp_request_t *req = (evhtp_request_t *)req0;
|
||||
int retval = -1;
|
||||
struct evbuffer *eb = NULL;
|
||||
const char *reason_phrase;
|
||||
evhtp_connection_t *conn;
|
||||
struct evbuffer *eb = NULL;
|
||||
|
||||
req->status = code;
|
||||
if ((reason_phrase = restconf_code2reason(code)) == NULL)
|
||||
|
|
@ -149,17 +136,10 @@ restconf_reply_send(void *req0,
|
|||
if (restconf_reply_header(req, "Status", "%d %s", code, reason_phrase) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
#if 0 /* Optional? */
|
||||
{
|
||||
evhtp_connection_t *conn;
|
||||
|
||||
if ((conn = evhtp_request_get_connection(req)) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
|
||||
goto done;
|
||||
}
|
||||
htp_sslutil_add_xheaders(req->headers_out, conn->ssl, HTP_SSLUTILS_XHDR_ALL);
|
||||
if ((conn = evhtp_request_get_connection(req)) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If body, add a content-length header */
|
||||
if (cb != NULL && cbuf_len(cb)){
|
||||
|
|
@ -167,14 +147,13 @@ restconf_reply_send(void *req0,
|
|||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* create evbuffer* : bufferevent_write_buffer/ drain,
|
||||
ie send everything , except body */
|
||||
evhtp_send_reply_start(req, req->status);
|
||||
evhtp_send_reply(req, req->status);
|
||||
|
||||
/* Write a body if cbuf is nonzero */
|
||||
if (cb != NULL && cbuf_len(cb)){
|
||||
/* Suboptimal, copy from cbuf to evbuffer */
|
||||
if ((eb = evbuffer_new()) == NULL){
|
||||
clicon_err(OE_CFG, errno, "evbuffer_new");
|
||||
clicon_err(OE_RESTCONF, errno, "evbuffer_new");
|
||||
goto done;
|
||||
}
|
||||
if (evbuffer_add(eb, cbuf_get(cb), cbuf_len(cb)) < 0){
|
||||
|
|
@ -217,3 +196,4 @@ restconf_get_indata(void *req0)
|
|||
|
||||
return cb;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@
|
|||
* @see restconf_api.h for virtual API
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@
|
|||
* @see RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@
|
|||
* altogether?
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -52,6 +56,7 @@
|
|||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -256,8 +261,8 @@ restconf_terminate(clicon_handle h)
|
|||
xpath_optimize_exit();
|
||||
restconf_handle_exit(h);
|
||||
clixon_err_exit();
|
||||
clicon_log_exit();
|
||||
clicon_debug(1, "%s done", __FUNCTION__);
|
||||
clicon_log_exit(); /* Must be after last clicon_debug */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -585,7 +590,7 @@ restconf_authentication_cb(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Basic config init
|
||||
/*! Basic config init, set auth-type, pretty, etc
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] xrestconf XML config containing clixon-restconf top-level
|
||||
* @retval -1 Error
|
||||
|
|
@ -636,3 +641,171 @@ restconf_config_init(clicon_handle h,
|
|||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Create and bind restconf socket
|
||||
*
|
||||
* @param[in] netns0 Network namespace, special value "default" is same as NULL
|
||||
* @param[in] addr Address as string, eg "0.0.0.0", "::"
|
||||
* @param[in] addrtype One of inet:ipv4-address or inet:ipv6-address
|
||||
* @param[in] port TCP port
|
||||
* @param[in] backlog Listen backlog, queie of pending connections
|
||||
* @param[in] flags Socket flags OR:ed in with the socket(2) type parameter
|
||||
* @param[out] ss Server socket (bound for accept)
|
||||
*/
|
||||
int
|
||||
restconf_socket_init(const char *netns0,
|
||||
const char *addr,
|
||||
const char *addrtype,
|
||||
uint16_t port,
|
||||
int backlog,
|
||||
int flags,
|
||||
int *ss)
|
||||
{
|
||||
int retval = -1;
|
||||
struct sockaddr * sa;
|
||||
struct sockaddr_in6 sin6 = { 0 };
|
||||
struct sockaddr_in sin = { 0 };
|
||||
size_t sin_len;
|
||||
const char *netns;
|
||||
|
||||
clicon_debug(1, "%s %s %s %s %hu", __FUNCTION__, netns0, addrtype, addr, port);
|
||||
/* netns default -> NULL */
|
||||
if (netns0 != NULL && strcmp(netns0, "default")==0)
|
||||
netns = NULL;
|
||||
else
|
||||
netns = netns0;
|
||||
if (strcmp(addrtype, "inet:ipv6-address") == 0) {
|
||||
sin_len = sizeof(struct sockaddr_in6);
|
||||
sin6.sin6_port = htons(port);
|
||||
sin6.sin6_family = AF_INET6;
|
||||
|
||||
inet_pton(AF_INET6, addr, &sin6.sin6_addr);
|
||||
sa = (struct sockaddr *)&sin6;
|
||||
}
|
||||
else if (strcmp(addrtype, "inet:ipv4-address") == 0) {
|
||||
sin_len = sizeof(struct sockaddr_in);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(port);
|
||||
sin.sin_addr.s_addr = inet_addr(addr);
|
||||
|
||||
sa = (struct sockaddr *)&sin;
|
||||
}
|
||||
else{
|
||||
clicon_err(OE_XML, EINVAL, "Unexpected addrtype: %s", addrtype);
|
||||
return -1;
|
||||
}
|
||||
if (clixon_netns_socket(netns, sa, sin_len, backlog, flags, ss) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s ss=%d", __FUNCTION__, *ss);
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Extract socket info from backend config
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xs socket config
|
||||
* @param[in] nsc Namespace context
|
||||
* @param[out] namespace
|
||||
* @param[out] address Address as string, eg "0.0.0.0", "::"
|
||||
* @param[out] addrtype One of inet:ipv4-address or inet:ipv6-address
|
||||
* @param[out] port
|
||||
* @param[out] ssl
|
||||
*/
|
||||
int
|
||||
restconf_socket_extract(clicon_handle h,
|
||||
cxobj *xs,
|
||||
cvec *nsc,
|
||||
char **namespace,
|
||||
char **address,
|
||||
char **addrtype,
|
||||
uint16_t *port,
|
||||
uint16_t *ssl)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
char *str = NULL;
|
||||
char *reason = NULL;
|
||||
int ret;
|
||||
char *body;
|
||||
cg_var *cv = NULL;
|
||||
yang_stmt *y;
|
||||
yang_stmt *ysub = NULL;
|
||||
|
||||
if ((x = xpath_first(xs, nsc, "namespace")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "Mandatory namespace not given");
|
||||
goto done;
|
||||
}
|
||||
*namespace = xml_body(x);
|
||||
if ((x = xpath_first(xs, nsc, "address")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "Mandatory address not given");
|
||||
goto done;
|
||||
}
|
||||
/* address is a union type and needs a special investigation to see which type (ipv4 or ipv6)
|
||||
* the address is
|
||||
*/
|
||||
body = xml_body(x);
|
||||
y = xml_spec(x);
|
||||
if ((cv = cv_dup(yang_cv_get(y))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cv_dup");
|
||||
goto done;
|
||||
}
|
||||
if ((ret = cv_parse1(body, cv, &reason)) < 0){
|
||||
clicon_err(OE_XML, errno, "cv_parse1");
|
||||
goto done;
|
||||
}
|
||||
if (ret == 0){
|
||||
clicon_err(OE_XML, EFAULT, "%s", reason);
|
||||
goto done;
|
||||
}
|
||||
if ((ret = ys_cv_validate(h, cv, y, &ysub, &reason)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
clicon_err(OE_XML, EFAULT, "Validation os address: %s", reason);
|
||||
goto done;
|
||||
}
|
||||
if (ysub == NULL){
|
||||
clicon_err(OE_XML, EFAULT, "No address union type");
|
||||
goto done;
|
||||
}
|
||||
*address = body;
|
||||
/* This is YANG type name of ip-address:
|
||||
* typedef ip-address {
|
||||
* type union {
|
||||
* type inet:ipv4-address; <---
|
||||
* type inet:ipv6-address; <---
|
||||
* }
|
||||
*/
|
||||
*addrtype = yang_argument_get(ysub);
|
||||
if ((x = xpath_first(xs, nsc, "port")) != NULL &&
|
||||
(str = xml_body(x)) != NULL){
|
||||
if ((ret = parse_uint16(str, port, &reason)) < 0){
|
||||
clicon_err(OE_XML, errno, "parse_uint16");
|
||||
goto done;
|
||||
}
|
||||
if (ret == 0){
|
||||
clicon_err(OE_XML, EINVAL, "Unrecognized value of port: %s", str);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if ((x = xpath_first(xs, nsc, "ssl")) != NULL &&
|
||||
(str = xml_body(x)) != NULL){
|
||||
/* XXX use parse_bool but it is legacy static */
|
||||
if (strcmp(str, "false") == 0)
|
||||
*ssl = 0;
|
||||
else if (strcmp(str, "true") == 0)
|
||||
*ssl = 1;
|
||||
else {
|
||||
clicon_err(OE_XML, EINVAL, "Unrecognized value of ssl: %s", str);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cv)
|
||||
cv_free(cv);
|
||||
if (reason)
|
||||
free(reason);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ char *restconf_uripath(clicon_handle h);
|
|||
int restconf_drop_privileges(clicon_handle h, char *user);
|
||||
int restconf_authentication_cb(clicon_handle h, void *req, int pretty, restconf_media media_out);
|
||||
int restconf_config_init(clicon_handle h, cxobj *xrestconf);
|
||||
int restconf_socket_init(const char *netns0, const char *addr, 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);
|
||||
|
||||
#endif /* _RESTCONF_LIB_H_ */
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
90
apps/restconf/restconf_openssl.h
Normal file
90
apps/restconf/restconf_openssl.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
*
|
||||
***** 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 *****
|
||||
*
|
||||
* 1 1
|
||||
* +--------------------+ restconf_handle_get +--------------------+
|
||||
* | rh restconf_handle | <--------------------- | h clicon_handle |
|
||||
* +--------------------+ +--------------------+
|
||||
* common SSL config \ ^
|
||||
* \ | n
|
||||
* \ rh_sockets +--------------------+
|
||||
* +-----------> | rs restconf_socket |
|
||||
* +--------------------+
|
||||
* n per-socket SSL config
|
||||
* +--------------------+
|
||||
* | rr restconf_request| per-packet
|
||||
* +--------------------+
|
||||
*
|
||||
* Parse functions
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef _RESTCONF_OPENSSL_H_
|
||||
#define _RESTCONF_OPENSSL_H_
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
/* Restconf request handle
|
||||
* Per socket request
|
||||
*/
|
||||
typedef struct {
|
||||
qelem_t rs_qelem; /* List header */
|
||||
clicon_handle rs_h; /* Clixon handle */
|
||||
int rs_ss; /* Server socket (ready for accept) */
|
||||
int rs_ssl; /* 0: Not SSL socket, 1:SSL socket */
|
||||
} restconf_socket;
|
||||
|
||||
/* Restconf handle
|
||||
* Global data about ssl (not per packet/request)
|
||||
*/
|
||||
typedef struct {
|
||||
SSL_CTX *rh_ctx; /* SSL context */
|
||||
evhtp_t *rh_evhtp; /* Evhtp struct */
|
||||
restconf_socket *rh_sockets; /* List of restconf server (ready for accept) sockets */
|
||||
} restconf_handle;
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int restconf_parse(void *req, const char *buf, size_t buflen);
|
||||
|
||||
#endif /* _RESTCONF_OPENSSL_H_ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue