libhttp compile

This commit is contained in:
Olof hagsand 2020-05-28 17:19:15 +02:00
parent ebaedfd482
commit 8c1372d26a
18 changed files with 1176 additions and 565 deletions

1
.gitignore vendored
View file

@ -35,6 +35,7 @@ apps/cli/test.c
apps/dbctrl/clixon_dbctrl apps/dbctrl/clixon_dbctrl
apps/netconf/clixon_netconf apps/netconf/clixon_netconf
apps/restconf/clixon_restconf apps/restconf/clixon_restconf
apps/restconf/clixon_restconf_libhttp
apps/xmldb/clixon_xmldb apps/xmldb/clixon_xmldb
etc/clixonrc etc/clixonrc

View file

@ -64,7 +64,7 @@ CLIXON_LIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR)
# even though it may exist in $(libdir). But the new version may not have been installed yet. # even though it may exist in $(libdir). But the new version may not have been installed yet.
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB) -lpthread LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB) #-lpthread
CPPFLAGS = @CPPFLAGS@ -fPIC CPPFLAGS = @CPPFLAGS@ -fPIC
INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@ INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@

View file

@ -272,7 +272,8 @@ usage(clicon_handle h,
/* /*
*/ */
int int
main(int argc, char **argv) main(int argc,
char **argv)
{ {
int retval = -1; int retval = -1;
int c; int c;

View file

@ -55,6 +55,7 @@ HOST_VENDOR = @host_vendor@
# XXX why is not wwwdir under prefix? # XXX why is not wwwdir under prefix?
wwwdir = @wwwdir@ wwwdir = @wwwdir@
wwwuser = @wwwuser@ wwwuser = @wwwuser@
with_libhttp = @with_libhttp@
SH_SUFFIX = @SH_SUFFIX@ SH_SUFFIX = @SH_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@ CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
@ -67,40 +68,50 @@ CLIXON_LIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR)
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB)
ifeq ($(with_libhttp),yes)
LIBS += -ldl -lpthread
endif
CPPFLAGS = @CPPFLAGS@ -fPIC CPPFLAGS = @CPPFLAGS@ -fPIC
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@ INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
# Name of application # Applications
APPL = clixon_restconf APPLS = clixon_restconf # fcgi / nginx
ifeq ($(with_libhttp),yes)
APPLS += clixon_restconf_libhttp
endif
# Not accessible from plugin # Common source - not accessible from plugin - independent of fcgi
APPSRC = restconf_main.c APPSRC = restconf_lib.c
APPSRC += restconf_methods.c
APPSRC += restconf_methods_post.c
APPSRC += restconf_methods_get.c
APPSRC += restconf_stream.c
APPOBJ = $(APPSRC:.c=.o) APPOBJ = $(APPSRC:.c=.o)
# Accessible from plugin # Fcgi-specific source including main
LIBSRC = restconf_lib.c APPFCGI = restconf_fcgi_lib.c
LIBOBJ = $(LIBSRC:.c=.o) APPFCGI += restconf_fcgi_main.c
APPFCGI += restconf_methods.c # These should be moved ^
APPFCGI += restconf_methods_post.c
APPFCGI += restconf_methods_get.c
APPFCGI += restconf_stream.c
APPFCGIOBJ = $(APPFCGI:.c=.o)
# Name of lib ifeq ($(with_libhttp),yes)
MYNAME = clixon_restconf # Libhttp-specific source including main
MYLIBLINK = lib$(MYNAME)$(SH_SUFFIX) APPLIBH = restconf_libhttp_main.c
MYLIB = $(MYLIBLINK).$(CLIXON_MAJOR).$(CLIXON_MINOR) APPLIBHOBJ = $(APPLIBH:.c=.o)
MYLIBSO = $(MYLIBLINK).$(CLIXON_MAJOR) endif
all: $(MYLIB) $(APPL) all: $(APPLS)
# Dependency of clixon library # Dependency of clixon library
$(top_srcdir)/lib/src/$(CLIXON_LIB): $(top_srcdir)/lib/src/$(CLIXON_LIB):
(cd $(top_srcdir)/lib/src && $(MAKE) $(MFLAGS) $(CLIXON_LIB)) (cd $(top_srcdir)/lib/src && $(MAKE) $(MFLAGS) $(CLIXON_LIB))
clean: clean:
rm -f $(LIBOBJ) *.core $(APPL) $(APPOBJ) $(MYLIB) $(MYLIBSO) $(MYLIBLINK) rm -f *.core $(APPLS) $(APPOBJ) $(APPFCGIOBJ)
ifeq ($(with_libhttp),yes)
rm -f $(APPLIBHOBJ)
endif
distclean: clean distclean: clean
rm -f Makefile *~ .depend rm -f Makefile *~ .depend
@ -110,52 +121,38 @@ distclean: clean
# Also create a libexec/ directory for writeable/temporary files. # Also create a libexec/ directory for writeable/temporary files.
# Put config file in etc/ # Put config file in etc/
# Also a rule for letting www-dir be owned by www-data, which only works for sudo # Also a rule for letting www-dir be owned by www-data, which only works for sudo
install: install-lib $(APPL) install: $(APPLS)
ifeq ($(shell whoami),root) ifeq ($(shell whoami),root)
install -d -m 0755 -o $(wwwuser) -g $(wwwuser) $(DESTDIR)$(wwwdir) install -d -m 0755 -o $(wwwuser) -g $(wwwuser) $(DESTDIR)$(wwwdir)
else else
install -d -m 0755 $(DESTDIR)$(wwwdir) install -d -m 0755 $(DESTDIR)$(wwwdir)
endif endif
install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(wwwdir) install -m 0755 $(INSTALLFLAGS) $(APPLS) $(DESTDIR)$(wwwdir)
install-lib: $(MYLIB) install-include:
install -d -m 0755 $(DESTDIR)$(libdir)
install -m 0644 $(INSTALLFLAGS) $(MYLIB) $(DESTDIR)$(libdir)
ln -sf $(MYLIB) $(DESTDIR)$(libdir)/$(MYLIBSO) # -l:libclixon_restconf.so.2
ln -sf $(MYLIBSO) $(DESTDIR)$(libdir)/$(MYLIBLINK) # -l:libclixon_restconf.so
install-include: clixon_restconf.h
install -d -m 0755 $(DESTDIR)$(includedir)/clixon
install -m 0644 $^ $(DESTDIR)$(includedir)/clixon
uninstall: uninstall:
rm -f $(DESTDIR)$(wwwdir)/$(APPL) rm -f $(DESTDIR)$(wwwdir)/$(APPLS)
rm -f $(DESTDIR)$(libdir)/$(MYLIBLINK)*
.SUFFIXES: .SUFFIXES:
.SUFFIXES: .c .o .SUFFIXES: .c .o
.c.o: .c.o:
$(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" $(CPPFLAGS) $(CFLAGS) -c $< $(CC) $(INCLUDES) -D__PROGRAM__=\"clixon_restconf\" $(CPPFLAGS) $(CFLAGS) -c $<
$(APPL) : $(APPOBJ) $(MYLIBLINK) $(LIBDEPS) clixon_restconf : $(APPOBJ) $(APPFCGIOBJ)
$(CC) $(LDFLAGS) $(APPOBJ) -L. $(MYLIB) $(LIBS) -o $@ $(CC) $(LDFLAGS) $(APPOBJ) $(APPFCGIOBJ) -L. $(LIBS) -o $@
$(MYLIB) : $(LIBOBJ) $(LIBDEPS) ifeq ($(with_libhttp),yes)
ifeq ($(HOST_VENDOR),apple) clixon_restconf_libhttp : $(APPOBJ) $(APPLIBHOBJ) $(LIBDEPS)
$(CC) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ $(LIBOBJ) $(LIBS) $(CC) $(LDFLAGS) $(APPOBJ) $(APPLIBHOBJ) -L. $(LIBS) -o $@
else
$(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(LIBOBJ) $(LIBS) -Wl,-soname=$(MYLIBSO)
endif endif
# link-name is needed for application linking, eg for clixon_cli and clixon_config
$(MYLIBLINK) : $(MYLIB)
TAGS: TAGS:
find . -name '*.[chyl]' -print | etags - find . -name '*.[chyl]' -print | etags -
depend: depend:
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(LIBSRC) $(APPSRC) > .depend $(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(APPFCGI) $(APPSRC) > .depend
#include .depend #include .depend

View file

@ -0,0 +1,490 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2020 Olof Hagsand
Copyright (C) 2020 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 *****
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
* @note The response payload for errors uses text_html. RFC7231 is vague
* on the response payload (and its media). Maybe it should be omitted
* altogether?
*/
#include <stdlib.h>
#include <string.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>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include <fcgiapp.h> /* Need to be after clixon_xml-h due to attribute format */
#include "restconf_lib.h"
#include "restconf_fcgi_lib.h"
/*! Return media_in from Content-Type, -1 if not found or unrecognized
* @note media-type syntax does not support parameters
* @see RFC7231 Sec 3.1.1.1 for media-type syntax type:
* media-type = type "/" subtype *( OWS ";" OWS parameter )
* type = token
* subtype = token
*
*/
restconf_media
restconf_content_type(FCGX_Request *r)
{
char *str;
restconf_media m;
if ((str = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp)) == NULL)
return -1;
if ((int)(m = restconf_media_str2int(str)) == -1)
return -1;
return m;
}
/*! HTTP error 400
* @param[in] r Fastcgi request handle
*/
int
restconf_badrequest(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(400, r->out);
FCGX_FPrintF(r->out, "Status: 400 Bad Request\r\n"); /* 400 bad request */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Clixon Bad request/h1>\n");
FCGX_FPrintF(r->out, "The requested URL %s or data is in some way badly formed.\n",
path);
return 0;
}
/*! HTTP error 401
* @param[in] r Fastcgi request handle
*/
int
restconf_unauthorized(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(401, r->out);
FCGX_FPrintF(r->out, "Status: 401 Unauthorized\r\n"); /* 401 unauthorized */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<error-tag>access-denied</error-tag>\n");
FCGX_FPrintF(r->out, "The requested URL %s was unauthorized.\n", path);
return 0;
}
/*! HTTP error 403
* @param[in] r Fastcgi request handle
*/
int
restconf_forbidden(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(403, r->out);
FCGX_FPrintF(r->out, "Status: 403 Forbidden\r\n"); /* 403 forbidden */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Forbidden</h1>\n");
FCGX_FPrintF(r->out, "The requested URL %s was forbidden.\n", path);
return 0;
}
/*! HTTP error 404
* @param[in] r Fastcgi request handle
*/
int
restconf_notfound(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(404, r->out);
FCGX_FPrintF(r->out, "Status: 404 Not Found\r\n"); /* 404 not found */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Not Found</h1>\n");
FCGX_FPrintF(r->out, "Not Found\n");
FCGX_FPrintF(r->out, "The requested URL %s was not found on this server.\n",
path);
return 0;
}
/*! HTTP error 406 Not acceptable
* @param[in] r Fastcgi request handle
*/
int
restconf_notacceptable(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(406, r->out);
FCGX_FPrintF(r->out, "Status: 406 Not Acceptable\r\n"); /* 406 not acceptible */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Not Acceptable</h1>\n");
FCGX_FPrintF(r->out, "Not Acceptable\n");
FCGX_FPrintF(r->out, "The target resource does not have a current representation that would be acceptable to the user agent.\n",
path);
return 0;
}
/*! HTTP error 409
* @param[in] r Fastcgi request handle
*/
int
restconf_conflict(FCGX_Request *r)
{
FCGX_SetExitStatus(409, r->out);
FCGX_FPrintF(r->out, "Status: 409 Conflict\r\n"); /* 409 Conflict */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Data resource already exists</h1>\n");
return 0;
}
/*! HTTP error 409
* @param[in] r Fastcgi request handle
*/
int
restconf_unsupported_media(FCGX_Request *r)
{
FCGX_SetExitStatus(415, r->out);
FCGX_FPrintF(r->out, "Status: 415 Unsupported Media Type\r\n");
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Unsupported Media Type</h1>\n");
return 0;
}
/*! HTTP error 500
* @param[in] r Fastcgi request handle
*/
int
restconf_internal_server_error(FCGX_Request *r)
{
char *path;
clicon_debug(1, "%s", __FUNCTION__);
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_FPrintF(r->out, "Status: 500 Internal Server Error\r\n"); /* 500 internal server error */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Internal server error when accessing %s</h1>\n", path);
return 0;
}
/*! HTTP error 501
* @param[in] r Fastcgi request handle
*/
int
restconf_notimplemented(FCGX_Request *r)
{
clicon_debug(1, "%s", __FUNCTION__);
FCGX_FPrintF(r->out, "Status: 501 Not Implemented\r\n");
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Not Implemented/h1>\n");
return 0;
}
/*!
* @param[in] r Fastcgi request handle
*/
static int
printparam(FCGX_Request *r,
char *e,
int dbgp)
{
char *p = FCGX_GetParam(e, r->envp);
if (dbgp)
clicon_debug(1, "%s = '%s'", e, p?p:"");
else
FCGX_FPrintF(r->out, "%s = '%s'\n", e, p?p:"");
return 0;
}
/*! Print all FCGI headers
* @param[in] r Fastcgi request handle
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
*/
int
restconf_test(FCGX_Request *r,
int dbg)
{
printparam(r, "QUERY_STRING", dbg);
printparam(r, "REQUEST_METHOD", dbg);
printparam(r, "CONTENT_TYPE", dbg);
printparam(r, "CONTENT_LENGTH", dbg);
printparam(r, "SCRIPT_FILENAME", dbg);
printparam(r, "SCRIPT_NAME", dbg);
printparam(r, "REQUEST_URI", dbg);
printparam(r, "DOCUMENT_URI", dbg);
printparam(r, "DOCUMENT_ROOT", dbg);
printparam(r, "SERVER_PROTOCOL", dbg);
printparam(r, "GATEWAY_INTERFACE", dbg);
printparam(r, "SERVER_SOFTWARE", dbg);
printparam(r, "REMOTE_ADDR", dbg);
printparam(r, "REMOTE_PORT", dbg);
printparam(r, "SERVER_ADDR", dbg);
printparam(r, "SERVER_PORT", dbg);
printparam(r, "SERVER_NAME", dbg);
printparam(r, "HTTP_COOKIE", dbg);
printparam(r, "HTTPS", dbg);
printparam(r, "HTTP_HOST", dbg);
printparam(r, "HTTP_ACCEPT", dbg);
printparam(r, "HTTP_CONTENT_TYPE", dbg);
printparam(r, "HTTP_AUTHORIZATION", dbg);
#if 0 /* For debug */
clicon_debug(1, "All environment vars:");
{
extern char **environ;
int i;
for (i = 0; environ[i] != NULL; i++){
clicon_debug(1, "%s", environ[i]);
}
}
clicon_debug(1, "End environment vars:");
#endif
return 0;
}
/*!
* @param[in] r Fastcgi request handle
*/
cbuf *
readdata(FCGX_Request *r)
{
int c;
cbuf *cb;
if ((cb = cbuf_new()) == NULL)
return NULL;
while ((c = FCGX_GetChar(r->in)) != -1)
cprintf(cb, "%c", c);
return cb;
}
/*! Return restconf error on get/head request
* @param[in] h Clixon handle
* @param[in] r Fastcgi request handle
* @param[in] xerr XML error message from backend
* @param[in] pretty Set to 1 for pretty-printed xml/json output
* @param[in] media Output media
* @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping
* otherwise use this code
*/
int
api_return_err(clicon_handle h,
FCGX_Request *r,
cxobj *xerr,
int pretty,
restconf_media media,
int code0)
{
int retval = -1;
cbuf *cb = NULL;
cbuf *cberr = NULL;
cxobj *xtag;
char *tagstr;
int code;
cxobj *xerr2 = NULL;
const char *reason_phrase;
clicon_debug(1, "%s", __FUNCTION__);
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* A well-formed error message when entering here should look like:
* <rpc-error>...<error-tag>invalid-value</error-tag>
* Check this is so, otherwise generate an internal error.
*/
if (strcmp(xml_name(xerr), "rpc-error") != 0 ||
(xtag = xpath_first(xerr, NULL, "error-tag")) == NULL){
if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cberr, "Internal error, system returned invalid error message: ");
if (netconf_err2cb(xerr, cberr) < 0)
goto done;
if (netconf_operation_failed_xml(&xerr2, "application",
cbuf_get(cberr)) < 0)
goto done;
if ((xerr = xpath_first(xerr2, NULL, "//rpc-error")) == NULL){
clicon_err(OE_XML, 0, "Internal error, shouldnt happen");
goto done;
}
}
if (xml_name_set(xerr, "error") < 0)
goto done;
tagstr = xml_body(xtag);
if (code0 != 0)
code = code0;
else{
if ((code = restconf_err2code(tagstr)) < 0)
code = 500; /* internal server error */
}
if ((reason_phrase = restconf_code2reason(code)) == NULL)
reason_phrase="";
FCGX_SetExitStatus(code, r->out); /* Created */
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
FCGX_FPrintF(r->out, "Content-Type: %s\r\n\r\n", restconf_media_int2str(media));
switch (media){
case YANG_DATA_XML:
if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1) < 0)
goto done;
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
if (pretty){
FCGX_FPrintF(r->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n", cbuf_get(cb));
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
FCGX_FPrintF(r->out, " </errors>\r\n");
}
else {
FCGX_FPrintF(r->out, "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">", cbuf_get(cb));
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
FCGX_FPrintF(r->out, "</errors>\r\n");
}
break;
case YANG_DATA_JSON:
if (xml2json_cbuf(cb, xerr, pretty) < 0)
goto done;
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
if (pretty){
FCGX_FPrintF(r->out, "{\n");
FCGX_FPrintF(r->out, " \"ietf-restconf:errors\" : %s\n",
cbuf_get(cb));
FCGX_FPrintF(r->out, "}\r\n");
}
else{
FCGX_FPrintF(r->out, "{");
FCGX_FPrintF(r->out, "\"ietf-restconf:errors\":");
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
FCGX_FPrintF(r->out, "}\r\n");
}
break;
default:
clicon_err(OE_YANG, EINVAL, "Invalid media type %d", media);
goto done;
break;
} /* switch media */
// ok:
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (cb)
cbuf_free(cb);
if (cberr)
cbuf_free(cberr);
return retval;
}
/*! Print location header from FCGI environment
* @param[in] r Fastcgi request handle
* @param[in] xobj If set (eg POST) add to api-path
* $https on if connection operates in SSL mode, or an empty string otherwise
* @note ports are ignored
*/
int
http_location(FCGX_Request *r,
cxobj *xobj)
{
int retval = -1;
char *https;
char *host;
char *request_uri;
cbuf *cb = NULL;
https = FCGX_GetParam("HTTPS", r->envp);
host = FCGX_GetParam("HTTP_HOST", r->envp);
request_uri = FCGX_GetParam("REQUEST_URI", r->envp);
if (xobj != NULL){
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
}
if (xml2api_path_1(xobj, cb) < 0)
goto done;
FCGX_FPrintF(r->out, "Location: http%s://%s%s%s\r\n",
https?"s":"",
host,
request_uri,
cbuf_get(cb));
}
else
FCGX_FPrintF(r->out, "Location: http%s://%s%s\r\n",
https?"s":"",
host,
request_uri);
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Extract uri-encoded uri-path from fastcgi parameters
* Use REQUEST_URI parameter and strip ?args
* REQUEST_URI have args and is encoded
* eg /interface=eth%2f0%2f0?insert=first
* DOCUMENT_URI dont have args and is not encoded
* eg /interface=eth/0/0
* causes problems with eg /interface=eth%2f0%2f0
*/
char *
restconf_uripath(FCGX_Request *r)
{
char *path;
char *q;
path = FCGX_GetParam("REQUEST_URI", r->envp);
if ((q = index(path, '?')) != NULL)
*q = '\0';
return path;
}

View file

@ -3,6 +3,7 @@
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON. This file is part of CLIXON.
@ -30,43 +31,30 @@
the terms of any one of the Apache License version 2 or the GPL. the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
* The exported interface to plugins. External apps (eg frontend restconf plugins)
* should only include this file (not the restconf_*.h)
*/ */
#ifndef _CLIXON_RESTCONF_H_ #ifndef _RESTCONF_FCGI_LIB_H_
#define _CLIXON_RESTCONF_H_ #define _RESTCONF_FCGI_LIB_H_
/* /*
* Types (also in restconf_lib.h) * Prototypes
*/ */
enum restconf_media{ restconf_media restconf_content_type(FCGX_Request *r);
YANG_DATA_JSON, /* "application/yang-data+json" */ int restconf_badrequest(FCGX_Request *r);
YANG_DATA_XML, /* "application/yang-data+xml" */ int restconf_unauthorized(FCGX_Request *r);
YANG_PATCH_JSON, /* "application/yang-patch+json" */ int restconf_forbidden(FCGX_Request *r);
YANG_PATCH_XML /* "application/yang-patch+xml" */ int restconf_notfound(FCGX_Request *r);
}; int restconf_notacceptable(FCGX_Request *r);
typedef enum restconf_media restconf_media; int restconf_conflict(FCGX_Request *r);
int restconf_unsupported_media(FCGX_Request *r);
/* int restconf_internal_server_error(FCGX_Request *r);
* Prototypes (also in restconf_lib.h) int restconf_notimplemented(FCGX_Request *r);
*/
int restconf_err2code(char *tag);
const char *restconf_code2reason(int code);
int badrequest(FCGX_Request *r);
int unauthorized(FCGX_Request *r);
int forbidden(FCGX_Request *r);
int notfound(FCGX_Request *r);
int conflict(FCGX_Request *r);
int internal_server_error(FCGX_Request *r);
int notimplemented(FCGX_Request *r);
int restconf_test(FCGX_Request *r, int dbg); int restconf_test(FCGX_Request *r, int dbg);
cbuf *readdata(FCGX_Request *r); cbuf *readdata(FCGX_Request *r);
int get_user_cookie(char *cookiestr, char *attribute, char **val);
int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr, int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr,
int pretty, restconf_media media, int code); int pretty, enum restconf_media media, int code);
int http_location(FCGX_Request *r, cxobj *xobj);
char *restconf_uripath(FCGX_Request *r);
#endif /* _RESTCONF_FCGI_LIB_H_ */
#endif /* _CLIXON_RESTCONF_H_ */

View file

@ -78,6 +78,7 @@
/* restconf */ /* restconf */
#include "restconf_lib.h" #include "restconf_lib.h"
#include "restconf_fcgi_lib.h"
#include "restconf_methods.h" #include "restconf_methods.h"
#include "restconf_methods_get.h" #include "restconf_methods_get.h"
#include "restconf_methods_post.h" #include "restconf_methods_post.h"
@ -86,12 +87,6 @@
/* Command line options to be passed to getopt(3) */ /* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:" #define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:"
/* RESTCONF enables deployments to specify where the RESTCONF API is
located. The client discovers this by getting the "/.well-known/host-meta"
resource
*/
#define RESTCONF_WELL_KNOWN "/.well-known/host-meta"
/*! Generic REST method, GET, PUT, DELETE, etc /*! Generic REST method, GET, PUT, DELETE, etc
* @param[in] h CLIXON handle * @param[in] h CLIXON handle
* @param[in] r Fastcgi request handle * @param[in] r Fastcgi request handle
@ -492,44 +487,6 @@ restconf_sig_term(int arg)
exit(-1); exit(-1);
} }
/*! Callback for yang extensions ietf-restconf:yang-data
* @see ietf-restconf.yang
* @param[in] h Clixon handle
* @param[in] yext Yang node of extension
* @param[in] ys Yang node of (unknown) statement belonging to extension
* @retval 0 OK, all callbacks executed OK
* @retval -1 Error in one callback
*/
static int
restconf_main_extension_cb(clicon_handle h,
yang_stmt *yext,
yang_stmt *ys)
{
int retval = -1;
char *extname;
char *modname;
yang_stmt *ymod;
yang_stmt *yc;
yang_stmt *yn = NULL;
ymod = ys_module(yext);
modname = yang_argument_get(ymod);
extname = yang_argument_get(yext);
if (strcmp(modname, "ietf-restconf") != 0 || strcmp(extname, "yang-data") != 0)
goto ok;
clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname);
if ((yc = yang_find(ys, 0, NULL)) == NULL)
goto ok;
if ((yn = ys_dup(yc)) == NULL)
goto done;
if (yn_insert(yang_parent_get(ys), yn) < 0)
goto done;
ok:
retval = 0;
done:
return retval;
}
static void static void
restconf_sig_child(int arg) restconf_sig_child(int arg)
{ {
@ -567,7 +524,7 @@ usage(clicon_handle h,
exit(0); exit(0);
} }
/*! Main routine for fastcgi API /*! Main routine for fastcgi restconf
*/ */
int int
main(int argc, main(int argc,
@ -603,6 +560,7 @@ main(int argc,
goto done; goto done;
_CLICON_HANDLE = h; /* for termination handling */ _CLICON_HANDLE = h; /* for termination handling */
while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1) while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1)
switch (c) { switch (c) {
case 'h': case 'h':

View file

@ -3,6 +3,7 @@
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2020 Olof Hagsand Copyright (C) 2009-2020 Olof Hagsand
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON. This file is part of CLIXON.
@ -58,8 +59,6 @@
/* clicon */ /* clicon */
#include <clixon/clixon.h> #include <clixon/clixon.h>
#include <fcgiapp.h> /* Need to be after clixon_xml-h due to attribute format */
#include "restconf_lib.h" #include "restconf_lib.h"
/* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code /* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code
@ -177,251 +176,6 @@ restconf_media_int2str(restconf_media media)
return clicon_int2str(http_media_map, media); return clicon_int2str(http_media_map, media);
} }
/*! Return media_in from Content-Type, -1 if not found or unrecognized
* @note media-type syntax does not support parameters
* @see RFC7231 Sec 3.1.1.1 for media-type syntax type:
* media-type = type "/" subtype *( OWS ";" OWS parameter )
* type = token
* subtype = token
*
*/
restconf_media
restconf_content_type(FCGX_Request *r)
{
char *str;
restconf_media m;
if ((str = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp)) == NULL)
return -1;
if ((int)(m = restconf_media_str2int(str)) == -1)
return -1;
return m;
}
/*! HTTP error 400
* @param[in] r Fastcgi request handle
*/
int
restconf_badrequest(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(400, r->out);
FCGX_FPrintF(r->out, "Status: 400 Bad Request\r\n"); /* 400 bad request */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Clixon Bad request/h1>\n");
FCGX_FPrintF(r->out, "The requested URL %s or data is in some way badly formed.\n",
path);
return 0;
}
/*! HTTP error 401
* @param[in] r Fastcgi request handle
*/
int
restconf_unauthorized(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(401, r->out);
FCGX_FPrintF(r->out, "Status: 401 Unauthorized\r\n"); /* 401 unauthorized */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<error-tag>access-denied</error-tag>\n");
FCGX_FPrintF(r->out, "The requested URL %s was unauthorized.\n", path);
return 0;
}
/*! HTTP error 403
* @param[in] r Fastcgi request handle
*/
int
restconf_forbidden(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(403, r->out);
FCGX_FPrintF(r->out, "Status: 403 Forbidden\r\n"); /* 403 forbidden */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Forbidden</h1>\n");
FCGX_FPrintF(r->out, "The requested URL %s was forbidden.\n", path);
return 0;
}
/*! HTTP error 404
* @param[in] r Fastcgi request handle
*/
int
restconf_notfound(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(404, r->out);
FCGX_FPrintF(r->out, "Status: 404 Not Found\r\n"); /* 404 not found */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Not Found</h1>\n");
FCGX_FPrintF(r->out, "Not Found\n");
FCGX_FPrintF(r->out, "The requested URL %s was not found on this server.\n",
path);
return 0;
}
/*! HTTP error 406 Not acceptable
* @param[in] r Fastcgi request handle
*/
int
restconf_notacceptable(FCGX_Request *r)
{
char *path;
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_SetExitStatus(406, r->out);
FCGX_FPrintF(r->out, "Status: 406 Not Acceptable\r\n"); /* 406 not acceptible */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Not Acceptable</h1>\n");
FCGX_FPrintF(r->out, "Not Acceptable\n");
FCGX_FPrintF(r->out, "The target resource does not have a current representation that would be acceptable to the user agent.\n",
path);
return 0;
}
/*! HTTP error 409
* @param[in] r Fastcgi request handle
*/
int
restconf_conflict(FCGX_Request *r)
{
FCGX_SetExitStatus(409, r->out);
FCGX_FPrintF(r->out, "Status: 409 Conflict\r\n"); /* 409 Conflict */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Data resource already exists</h1>\n");
return 0;
}
/*! HTTP error 409
* @param[in] r Fastcgi request handle
*/
int
restconf_unsupported_media(FCGX_Request *r)
{
FCGX_SetExitStatus(415, r->out);
FCGX_FPrintF(r->out, "Status: 415 Unsupported Media Type\r\n");
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Unsupported Media Type</h1>\n");
return 0;
}
/*! HTTP error 500
* @param[in] r Fastcgi request handle
*/
int
restconf_internal_server_error(FCGX_Request *r)
{
char *path;
clicon_debug(1, "%s", __FUNCTION__);
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_FPrintF(r->out, "Status: 500 Internal Server Error\r\n"); /* 500 internal server error */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Internal server error when accessing %s</h1>\n", path);
return 0;
}
/*! HTTP error 501
* @param[in] r Fastcgi request handle
*/
int
restconf_notimplemented(FCGX_Request *r)
{
clicon_debug(1, "%s", __FUNCTION__);
FCGX_FPrintF(r->out, "Status: 501 Not Implemented\r\n");
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Not Implemented/h1>\n");
return 0;
}
/*!
* @param[in] r Fastcgi request handle
*/
static int
printparam(FCGX_Request *r,
char *e,
int dbgp)
{
char *p = FCGX_GetParam(e, r->envp);
if (dbgp)
clicon_debug(1, "%s = '%s'", e, p?p:"");
else
FCGX_FPrintF(r->out, "%s = '%s'\n", e, p?p:"");
return 0;
}
/*! Print all FCGI headers
* @param[in] r Fastcgi request handle
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
*/
int
restconf_test(FCGX_Request *r,
int dbg)
{
printparam(r, "QUERY_STRING", dbg);
printparam(r, "REQUEST_METHOD", dbg);
printparam(r, "CONTENT_TYPE", dbg);
printparam(r, "CONTENT_LENGTH", dbg);
printparam(r, "SCRIPT_FILENAME", dbg);
printparam(r, "SCRIPT_NAME", dbg);
printparam(r, "REQUEST_URI", dbg);
printparam(r, "DOCUMENT_URI", dbg);
printparam(r, "DOCUMENT_ROOT", dbg);
printparam(r, "SERVER_PROTOCOL", dbg);
printparam(r, "GATEWAY_INTERFACE", dbg);
printparam(r, "SERVER_SOFTWARE", dbg);
printparam(r, "REMOTE_ADDR", dbg);
printparam(r, "REMOTE_PORT", dbg);
printparam(r, "SERVER_ADDR", dbg);
printparam(r, "SERVER_PORT", dbg);
printparam(r, "SERVER_NAME", dbg);
printparam(r, "HTTP_COOKIE", dbg);
printparam(r, "HTTPS", dbg);
printparam(r, "HTTP_HOST", dbg);
printparam(r, "HTTP_ACCEPT", dbg);
printparam(r, "HTTP_CONTENT_TYPE", dbg);
printparam(r, "HTTP_AUTHORIZATION", dbg);
#if 0 /* For debug */
clicon_debug(1, "All environment vars:");
{
extern char **environ;
int i;
for (i = 0; environ[i] != NULL; i++){
clicon_debug(1, "%s", environ[i]);
}
}
clicon_debug(1, "End environment vars:");
#endif
return 0;
}
/*!
* @param[in] r Fastcgi request handle
*/
cbuf *
readdata(FCGX_Request *r)
{
int c;
cbuf *cb;
if ((cb = cbuf_new()) == NULL)
return NULL;
while ((c = FCGX_GetChar(r->in)) != -1)
cprintf(cb, "%c", c);
return cb;
}
/*! Parse a cookie string and return value of cookie attribute /*! Parse a cookie string and return value of cookie attribute
* @param[in] cookiestr cookie string according to rfc6265 (modified) * @param[in] cookiestr cookie string according to rfc6265 (modified)
* @param[in] attribute cookie attribute * @param[in] attribute cookie attribute
@ -449,165 +203,6 @@ get_user_cookie(char *cookiestr,
return retval; return retval;
} }
/*! Return restconf error on get/head request
* @param[in] h Clixon handle
* @param[in] r Fastcgi request handle
* @param[in] xerr XML error message from backend
* @param[in] pretty Set to 1 for pretty-printed xml/json output
* @param[in] media Output media
* @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping
* otherwise use this code
*/
int
api_return_err(clicon_handle h,
FCGX_Request *r,
cxobj *xerr,
int pretty,
restconf_media media,
int code0)
{
int retval = -1;
cbuf *cb = NULL;
cbuf *cberr = NULL;
cxobj *xtag;
char *tagstr;
int code;
cxobj *xerr2 = NULL;
const char *reason_phrase;
clicon_debug(1, "%s", __FUNCTION__);
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* A well-formed error message when entering here should look like:
* <rpc-error>...<error-tag>invalid-value</error-tag>
* Check this is so, otherwise generate an internal error.
*/
if (strcmp(xml_name(xerr), "rpc-error") != 0 ||
(xtag = xpath_first(xerr, NULL, "error-tag")) == NULL){
if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cberr, "Internal error, system returned invalid error message: ");
if (netconf_err2cb(xerr, cberr) < 0)
goto done;
if (netconf_operation_failed_xml(&xerr2, "application",
cbuf_get(cberr)) < 0)
goto done;
if ((xerr = xpath_first(xerr2, NULL, "//rpc-error")) == NULL){
clicon_err(OE_XML, 0, "Internal error, shouldnt happen");
goto done;
}
}
if (xml_name_set(xerr, "error") < 0)
goto done;
tagstr = xml_body(xtag);
if (code0 != 0)
code = code0;
else{
if ((code = restconf_err2code(tagstr)) < 0)
code = 500; /* internal server error */
}
if ((reason_phrase = restconf_code2reason(code)) == NULL)
reason_phrase="";
FCGX_SetExitStatus(code, r->out); /* Created */
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
FCGX_FPrintF(r->out, "Content-Type: %s\r\n\r\n", restconf_media_int2str(media));
switch (media){
case YANG_DATA_XML:
if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1) < 0)
goto done;
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
if (pretty){
FCGX_FPrintF(r->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n", cbuf_get(cb));
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
FCGX_FPrintF(r->out, " </errors>\r\n");
}
else {
FCGX_FPrintF(r->out, "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">", cbuf_get(cb));
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
FCGX_FPrintF(r->out, "</errors>\r\n");
}
break;
case YANG_DATA_JSON:
if (xml2json_cbuf(cb, xerr, pretty) < 0)
goto done;
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
if (pretty){
FCGX_FPrintF(r->out, "{\n");
FCGX_FPrintF(r->out, " \"ietf-restconf:errors\" : %s\n",
cbuf_get(cb));
FCGX_FPrintF(r->out, "}\r\n");
}
else{
FCGX_FPrintF(r->out, "{");
FCGX_FPrintF(r->out, "\"ietf-restconf:errors\":");
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
FCGX_FPrintF(r->out, "}\r\n");
}
break;
default:
clicon_err(OE_YANG, EINVAL, "Invalid media type %d", media);
goto done;
break;
} /* switch media */
// ok:
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (cb)
cbuf_free(cb);
if (cberr)
cbuf_free(cberr);
return retval;
}
/*! Print location header from FCGI environment
* @param[in] r Fastcgi request handle
* @param[in] xobj If set (eg POST) add to api-path
* $https on if connection operates in SSL mode, or an empty string otherwise
* @note ports are ignored
*/
int
http_location(FCGX_Request *r,
cxobj *xobj)
{
int retval = -1;
char *https;
char *host;
char *request_uri;
cbuf *cb = NULL;
https = FCGX_GetParam("HTTPS", r->envp);
host = FCGX_GetParam("HTTP_HOST", r->envp);
request_uri = FCGX_GetParam("REQUEST_URI", r->envp);
if (xobj != NULL){
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
}
if (xml2api_path_1(xobj, cb) < 0)
goto done;
FCGX_FPrintF(r->out, "Location: http%s://%s%s%s\r\n",
https?"s":"",
host,
request_uri,
cbuf_get(cb));
}
else
FCGX_FPrintF(r->out, "Location: http%s://%s%s\r\n",
https?"s":"",
host,
request_uri);
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Clean and close all state of restconf process (but dont exit). /*! Clean and close all state of restconf process (but dont exit).
* Cannot use h after this * Cannot use h after this
* @param[in] h Clixon handle * @param[in] h Clixon handle
@ -757,23 +352,40 @@ restconf_insert_attributes(cxobj *xdata,
return retval; return retval;
} }
/*! Extract uri-encoded uri-path from fastcgi parameters /*! Callback for yang extensions ietf-restconf:yang-data
* Use REQUEST_URI parameter and strip ?args * @see ietf-restconf.yang
* REQUEST_URI have args and is encoded * @param[in] h Clixon handle
* eg /interface=eth%2f0%2f0?insert=first * @param[in] yext Yang node of extension
* DOCUMENT_URI dont have args and is not encoded * @param[in] ys Yang node of (unknown) statement belonging to extension
* eg /interface=eth/0/0 * @retval 0 OK, all callbacks executed OK
* causes problems with eg /interface=eth%2f0%2f0 * @retval -1 Error in one callback
*/ */
char * int
restconf_uripath(FCGX_Request *r) restconf_main_extension_cb(clicon_handle h,
yang_stmt *yext,
yang_stmt *ys)
{ {
char *path; int retval = -1;
char *q; char *extname;
char *modname;
path = FCGX_GetParam("REQUEST_URI", r->envp); yang_stmt *ymod;
if ((q = index(path, '?')) != NULL) yang_stmt *yc;
*q = '\0'; yang_stmt *yn = NULL;
return path;
ymod = ys_module(yext);
modname = yang_argument_get(ymod);
extname = yang_argument_get(yext);
if (strcmp(modname, "ietf-restconf") != 0 || strcmp(extname, "yang-data") != 0)
goto ok;
clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname);
if ((yc = yang_find(ys, 0, NULL)) == NULL)
goto ok;
if ((yn = ys_dup(yc)) == NULL)
goto done;
if (yn_insert(yang_parent_get(ys), yn) < 0)
goto done;
ok:
retval = 0;
done:
return retval;
} }

View file

@ -3,6 +3,7 @@
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON. This file is part of CLIXON.
@ -41,8 +42,15 @@
*/ */
#define RESTCONF_API "restconf" #define RESTCONF_API "restconf"
/* RESTCONF enables deployments to specify where the RESTCONF API is
located. The client discovers this by getting the "/.well-known/host-meta"
resource
*/
#define RESTCONF_WELL_KNOWN "/.well-known/host-meta"
/* /*
* Types * Variables
*/ */
/*! RESTCONF media types /*! RESTCONF media types
@ -58,32 +66,15 @@ enum restconf_media{
typedef enum restconf_media restconf_media; typedef enum restconf_media restconf_media;
/* /*
* Prototypes (also in clixon_restconf.h) * Prototypes
*/ */
int restconf_err2code(char *tag); int restconf_err2code(char *tag);
const char *restconf_code2reason(int code); const char *restconf_code2reason(int code);
const restconf_media restconf_media_str2int(char *media); const restconf_media restconf_media_str2int(char *media);
const char *restconf_media_int2str(restconf_media media); const char *restconf_media_int2str(restconf_media media);
restconf_media restconf_content_type(FCGX_Request *r);
int restconf_badrequest(FCGX_Request *r);
int restconf_unauthorized(FCGX_Request *r);
int restconf_forbidden(FCGX_Request *r);
int restconf_notfound(FCGX_Request *r);
int restconf_notacceptable(FCGX_Request *r);
int restconf_conflict(FCGX_Request *r);
int restconf_unsupported_media(FCGX_Request *r);
int restconf_internal_server_error(FCGX_Request *r);
int restconf_notimplemented(FCGX_Request *r);
int restconf_test(FCGX_Request *r, int dbg);
cbuf *readdata(FCGX_Request *r);
int get_user_cookie(char *cookiestr, char *attribute, char **val); int get_user_cookie(char *cookiestr, char *attribute, char **val);
int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr,
int pretty, enum restconf_media media, int code);
int http_location(FCGX_Request *r, cxobj *xobj);
int restconf_terminate(clicon_handle h); int restconf_terminate(clicon_handle h);
int restconf_insert_attributes(cxobj *xdata, cvec *qvec); int restconf_insert_attributes(cxobj *xdata, cvec *qvec);
char *restconf_uripath(FCGX_Request *r); int restconf_main_extension_cb(clicon_handle h, yang_stmt *yext, yang_stmt *ys);
#endif /* _RESTCONF_LIB_H_ */ #endif /* _RESTCONF_LIB_H_ */

View file

@ -0,0 +1,471 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020 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 *****
*/
/* XXX temp constant should go away, */
#undef _LIBHTTP_NYI
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <fcntl.h>
#include <time.h>
#include <limits.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <libgen.h>
#include <sys/stat.h> /* chmod */
/* libhttp */
#include <libhttp/libhttp.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
/* restconf */
#include "restconf_lib.h"
#if 0 /* These are all dependent on FCGX */
#include "restconf_methods.h"
#include "restconf_methods_get.h"
#include "restconf_methods_post.h"
#include "restconf_stream.h"
#endif
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:"
/* Need global variable to for signal handler XXX */
static clicon_handle _CLICON_HANDLE = NULL;
/*! Signall terminates process
*/
static void
restconf_sig_term(int arg)
{
static int i=0;
if (i++ == 0)
clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
__PROGRAM__, __FUNCTION__, getpid(), arg);
else
exit(-1);
if (_CLICON_HANDLE){
#ifdef _LIBHTTP_NYI
stream_child_freeall(_CLICON_HANDLE);
#endif
restconf_terminate(_CLICON_HANDLE);
}
clicon_exit_set(); /* checked in event_loop() */
exit(-1);
}
static void
restconf_sig_child(int arg)
{
int status;
int pid;
if ((pid = waitpid(-1, &status, 0)) != -1 && WIFEXITED(status))
#ifdef _LIBHTTP_NYI
stream_child_free(_CLICON_HANDLE, pid);
#else
;
#endif
}
/*! Usage help routine
* @param[in] argv0 command line
* @param[in] h Clicon handle
*/
static void
usage(clicon_handle h,
char *argv0)
{
fprintf(stderr, "usage:%s [options]\n"
"where options are\n"
"\t-h \t\t Help\n"
"\t-D <level>\t Debug level\n"
"\t-f <file>\t Configuration file (mandatory)\n"
"\t-l <s|f<file>> \t Log on (s)yslog, (f)ile (syslog is default)\n"
"\t-p <dir>\t Yang directory path (see CLICON_YANG_DIR)\n"
"\t-d <dir>\t Specify restconf plugin directory dir (default: %s)\n"
"\t-y <file>\t Load yang spec file (override yang main module)\n"
"\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
"\t-u <path|addr>\t Internal socket domain path or IP addr (see -a)\n"
"\t-o \"<option>=<value>\" Give configuration option overriding config file (see clixon-config.yang)\n",
argv0,
clicon_restconf_dir(h)
);
exit(0);
}
#if 0
/*! This function will be called by the server on every new request.
*/
static int
begin_request_handler(struct httplib_connection *conn)
{
const struct httplib_request_info *request_info = httplib_get_request_info(conn);
char content[100];
/* Prepare the message we're going to send */
int content_length = snprintf(content, sizeof(content),
"Hello from clixon-civerweb! Remote port: %d",
request_info->remote_port);
/* Send HTTP reply to the client */
httplib_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: %d\r\n" // Always set Content-Length
"\r\n"
"%s",
content_length, content);
/* Returning non-zero tells the server that our function has replied to
* the client, and should not send client any more data.
*/
return 1;
}
#endif
LIBHTTP_API struct lh_ctx_t * httplib_start( const struct lh_clb_t *callbacks, void *user_data, const struct lh_opt_t *options );
static int
my_begin_request(struct lh_ctx_t *ctx,
struct lh_con_t *conn)
{
const struct lh_rqi_t *request_info = httplib_get_request_info(conn);
char content[100];
// Prepare the message we're going to send
int content_length = snprintf(content, sizeof(content),
"Hello from Clixon! Remote port: %d",
request_info->remote_port);
fprintf(stderr, "%s\n", __FUNCTION__);
httplib_printf(ctx, conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: %d\r\n" // Always set Content-Length
"\r\n"
"%s",
content_length, content);
return 0;
}
/*! Main routine for libhttp restconf
*/
int
main(int argc,
char **argv)
{
int retval = -1;
// int sock;
char *argv0 = argv[0];
int c;
// char *sockpath;
// char *path;
clicon_handle h;
char *dir;
int logdst = CLICON_LOG_SYSLOG;
yang_stmt *yspec = NULL;
int finish = 0;
int start = 1;
char *str;
clixon_plugin *cp = NULL;
uint32_t id = 0;
cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen;
size_t cligen_bufthreshold;
#ifdef _LIBHTTP_NYI
char *stream_path;
#endif
struct lh_ctx_t *ctx = NULL;
/* See XX_httplib_free_config_options, XX_httplib_init_options */
const struct lh_opt_t options[] = {{"listening_ports", "8080"}, {NULL, NULL}};
const struct lh_clb_t callbacks = {my_begin_request,NULL};
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
/* Create handle */
if ((h = clicon_handle_init()) == NULL)
goto done;
_CLICON_HANDLE = h; /* for termination handling */
while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1)
switch (c) {
case 'h':
usage(h, argv[0]);
break;
case 'D' : /* debug */
if (sscanf(optarg, "%d", &debug) != 1)
usage(h, argv[0]);
break;
case 'f': /* override config file */
if (!strlen(optarg))
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
break;
case 'l': /* Log destination: s|e|o */
if ((logdst = clicon_log_opt(optarg[0])) < 0)
usage(h, argv[0]);
if (logdst == CLICON_LOG_FILE &&
strlen(optarg)>1 &&
clicon_log_file(optarg+1) < 0)
goto done;
break;
} /* switch getopt */
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
clicon_debug_init(debug, NULL);
clicon_log(LOG_NOTICE, "%s: %u Started", __PROGRAM__, getpid());
if (set_signal(SIGTERM, restconf_sig_term, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
if (set_signal(SIGINT, restconf_sig_term, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
if (set_signal(SIGCHLD, restconf_sig_child, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
/* Find and read configfile */
if (clicon_options_main(h) < 0)
goto done;
#ifdef _LIBHTTP_NYI
stream_path = clicon_option_str(h, "CLICON_STREAM_PATH");
#endif
/* Now rest of options, some overwrite option file */
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1)
switch (c) {
case 'h' : /* help */
case 'D' : /* debug */
case 'f': /* config file */
case 'l': /* log */
break; /* see above */
case 'p' : /* yang dir path */
if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
goto done;
break;
case 'd': /* Plugin directory */
if (!strlen(optarg))
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_RESTCONF_DIR", optarg);
break;
case 'y' : /* Load yang spec file (override yang main module) */
clicon_option_str_set(h, "CLICON_YANG_MAIN_FILE", optarg);
break;
case 'a': /* internal backend socket address family */
clicon_option_str_set(h, "CLICON_SOCK_FAMILY", optarg);
break;
case 'u': /* internal backend socket unix domain path or ip host */
if (!strlen(optarg))
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
usage(h, argv0);
*val++ = '\0';
if (clicon_option_add(h, optarg, val) < 0)
goto done;
break;
}
default:
usage(h, argv[0]);
break;
}
argc -= optind;
argv += optind;
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);
/* Init cligen buffers */
cligen_buflen = clicon_option_int(h, "CLICON_CLI_BUF_START");
cligen_bufthreshold = clicon_option_int(h, "CLICON_CLI_BUF_THRESHOLD");
cbuf_alloc_set(cligen_buflen, cligen_bufthreshold);
/* Add (hardcoded) netconf features in case ietf-netconf loaded here
* Otherwise it is loaded in netconf_module_load below
*/
if (netconf_module_features(h) < 0)
goto done;
/* Create top-level yang spec and store as option */
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
/* Treat unknown XML as anydata */
if (clicon_option_bool(h, "CLICON_YANG_UNKNOWN_ANYDATA") == 1)
xml_bind_yang_unknown_anydata(1);
/* Load restconf plugins before yangs are loaded (eg extension callbacks) */
if ((dir = clicon_restconf_dir(h)) != NULL)
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0)
return -1;
/* Create a pseudo-plugin to create extension callback to set the ietf-routing
* yang-data extension for api-root top-level restconf function.
*/
if (clixon_pseudo_plugin(h, "pseudo restconf", &cp) < 0)
goto done;
cp->cp_api.ca_extension = restconf_main_extension_cb;
/* Load Yang modules
* 1. Load a yang module as a specific absolute filename */
if ((str = clicon_yang_main_file(h)) != NULL){
if (yang_spec_parse_file(h, str, yspec) < 0)
goto done;
}
/* 2. Load a (single) main module */
if ((str = clicon_yang_module_main(h)) != NULL){
if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
yspec) < 0)
goto done;
}
/* 3. Load all modules in a directory */
if ((str = clicon_yang_main_dir(h)) != NULL){
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
/* Load clixon lib yang module */
if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
/* Load yang restconf module */
if (yang_spec_parse_module(h, "ietf-restconf", NULL, yspec)< 0)
goto done;
/* Add netconf yang spec, used as internal protocol */
if (netconf_module_load(h) < 0)
goto done;
/* Add system modules */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
goto done;
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
/* Here all modules are loaded
* Compute and set canonical namespace context
*/
if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
/* Dump configuration options on debug */
if (debug)
clicon_option_dump(h, debug);
/* Call start function in all plugins before we go interactive
*/
if (clixon_plugin_start_all(h) < 0)
goto done;
/* Find and read configfile */
if (clicon_options_main(h) < 0)
goto done;
/* Start the web server. */
if ((ctx = httplib_start(&callbacks, /* callbacks */
NULL, /* user-data */
options /* options */
)) == NULL)
goto done;
while (finish == 0) {
finish = 1; /* If zero, dont finish request, initiate new */
clicon_debug(1, "------------");
if (start == 0){
/* Send hello request to backend to get session-id back
* This is done once at the beginning of the session and then this is
* used by the client, even though new TCP sessions are created for
* each message sent to the backend.
*/
if (clicon_hello_req(h, &id) < 0)
goto done;
clicon_session_id_set(h, id);
start++;
}
break; /* XXX */
}
pthread_exit(NULL);
/* Use options to select the port and document root among other things.
* Use callbacks to add your own hooks.
*/
retval = 0;
done:
if (ctx)
httplib_stop(ctx); /* to stop the server. */
#ifdef _LIBHTTP_NYI
stream_child_freeall(h);
#endif
restconf_terminate(h);
return retval;
}

View file

@ -122,6 +122,7 @@ Mapping netconf error-tag -> status code
#include <fcgiapp.h> /* Need to be after clixon_xml-h due to attribute format */ #include <fcgiapp.h> /* Need to be after clixon_xml-h due to attribute format */
#include "restconf_lib.h" #include "restconf_lib.h"
#include "restconf_fcgi_lib.h"
#include "restconf_methods.h" #include "restconf_methods.h"
/*! REST OPTIONS method /*! REST OPTIONS method

View file

@ -60,6 +60,7 @@
#include <fcgiapp.h> /* Need to be after clixon_xml-h due to attribute format */ #include <fcgiapp.h> /* Need to be after clixon_xml-h due to attribute format */
#include "restconf_lib.h" #include "restconf_lib.h"
#include "restconf_fcgi_lib.h"
#include "restconf_methods_get.h" #include "restconf_methods_get.h"
/*! Generic GET (both HEAD and GET) /*! Generic GET (both HEAD and GET)

View file

@ -62,6 +62,7 @@
#include <fcgiapp.h> /* Need to be after clixon_xml.h due to attribute format */ #include <fcgiapp.h> /* Need to be after clixon_xml.h due to attribute format */
#include "restconf_lib.h" #include "restconf_lib.h"
#include "restconf_fcgi_lib.h"
#include "restconf_methods_post.h" #include "restconf_methods_post.h"
/*! Generic REST POST method /*! Generic REST POST method

View file

@ -3,6 +3,7 @@
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON. This file is part of CLIXON.
@ -85,6 +86,7 @@
#include <fcgiapp.h> /* Need to be after clixon_xml.h due to attribute format */ #include <fcgiapp.h> /* Need to be after clixon_xml.h due to attribute format */
#include "restconf_lib.h" #include "restconf_lib.h"
#include "restconf_fcgi_lib.h"
#include "restconf_stream.h" #include "restconf_stream.h"
/* /*

View file

@ -3,6 +3,7 @@
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON. This file is part of CLIXON.

82
configure vendored
View file

@ -634,6 +634,7 @@ CPP
wwwuser wwwuser
wwwdir wwwdir
enable_optyangs enable_optyangs
with_libhttp
with_restconf with_restconf
SH_SUFFIX SH_SUFFIX
CLIXON_DEFAULT_CONFIG CLIXON_DEFAULT_CONFIG
@ -715,6 +716,7 @@ enable_optyangs
enable_publish enable_publish
with_restconf with_restconf
with_wwwuser with_wwwuser
with_libhttp
with_configfile with_configfile
with_libxml2 with_libxml2
with_yang_installdir with_yang_installdir
@ -1366,6 +1368,8 @@ Optional Packages:
--with-cligen=dir Use CLIGEN here --with-cligen=dir Use CLIGEN here
--without-restconf disable support for restconf --without-restconf disable support for restconf
--with-wwwuser=<user> Set www user different from www-data --with-wwwuser=<user> Set www user different from www-data
--with-libhttp Use libhttp as restconf server
--with-configfile=FILE set default path to config file --with-configfile=FILE set default path to config file
--with-libxml2 use gnome/libxml2 regex engine --with-libxml2 use gnome/libxml2 regex engine
--with-yang-installdir=DIR Install Clixon yang files here (default: ${prefix}/share/clixon) --with-yang-installdir=DIR Install Clixon yang files here (default: ${prefix}/share/clixon)
@ -3296,6 +3300,7 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
# If yes, compile apps/restconf # If yes, compile apps/restconf
# If yes, include libhttp server (https://www.libhttp.org/)
wwwdir=/www-data wwwdir=/www-data
@ -4739,6 +4744,83 @@ cat >>confdefs.h <<_ACEOF
_ACEOF _ACEOF
# This is for libhttp server (https://www.libhttp.org/) to be used as restconf client
# Out-of-the box it comes as static lib .a
# Check whether --with-libhttp was given.
if test "${with_libhttp+set}" = set; then :
withval=$with_libhttp;
else
with_libhttp=no
fi
echo "lib_http: ${with_libhttp}"
if test "x${with_libhttp}" == xyes; then
for ac_header in libhttp/libhttp.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "libhttp/libhttp.h" "ac_cv_header_libhttp_libhttp_h" "$ac_includes_default"
if test "x$ac_cv_header_libhttp_libhttp_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBHTTP_LIBHTTP_H 1
_ACEOF
else
as_fn_error $? "libhttp header missing. See https://www.libhttp.org" "$LINENO" 5
fi
done
# In github repo default install this is a static lib
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for httplib_start in -lhttp" >&5
$as_echo_n "checking for httplib_start in -lhttp... " >&6; }
if ${ac_cv_lib_http_httplib_start+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lhttp -lpthread -ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char httplib_start ();
int
main ()
{
return httplib_start ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_http_httplib_start=yes
else
ac_cv_lib_http_httplib_start=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_http_httplib_start" >&5
$as_echo "$ac_cv_lib_http_httplib_start" >&6; }
if test "x$ac_cv_lib_http_httplib_start" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBHTTP 1
_ACEOF
LIBS="-lhttp $LIBS"
else
as_fn_error $? "libhttplib missing" "$LINENO" 5
fi
fi
# Set default config file location # Set default config file location
CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml

View file

@ -94,6 +94,7 @@ AC_SUBST(CLIXON_DEFAULT_CONFIG)
AC_SUBST(LIBS) AC_SUBST(LIBS)
AC_SUBST(SH_SUFFIX) AC_SUBST(SH_SUFFIX)
AC_SUBST(with_restconf) # If yes, compile apps/restconf AC_SUBST(with_restconf) # If yes, compile apps/restconf
AC_SUBST(with_libhttp) # If yes, include libhttp server (https://www.libhttp.org/)
AC_SUBST(enable_optyangs) AC_SUBST(enable_optyangs)
AC_SUBST(wwwdir,/www-data) AC_SUBST(wwwdir,/www-data)
AC_SUBST(wwwuser,www-data) AC_SUBST(wwwuser,www-data)
@ -208,6 +209,20 @@ fi
AC_MSG_RESULT(www user is $wwwuser) AC_MSG_RESULT(www user is $wwwuser)
AC_DEFINE_UNQUOTED(WWWUSER, "$wwwuser", [WWW user for restconf daemon]) AC_DEFINE_UNQUOTED(WWWUSER, "$wwwuser", [WWW user for restconf daemon])
# This is for libhttp server (https://www.libhttp.org/) to be used as restconf client
# Out-of-the box it comes as static lib .a
AC_ARG_WITH([libhttp],
[AS_HELP_STRING([--with-libhttp Use libhttp as restconf server])],
,
[with_libhttp=no])
echo "lib_http: ${with_libhttp}"
if test "x${with_libhttp}" == xyes; then
AC_CHECK_HEADERS(libhttp/libhttp.h,, AC_MSG_ERROR([libhttp header missing. See https://www.libhttp.org]))
# In github repo default install this is a static lib
AC_CHECK_LIB(http, httplib_start,, AC_MSG_ERROR([libhttplib missing]),[-lpthread -ldl])
fi
# Set default config file location # Set default config file location
CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml
AC_ARG_WITH([configfile], AC_ARG_WITH([configfile],

View file

@ -46,7 +46,6 @@
/* clicon */ /* clicon */
#include <clixon/clixon.h> #include <clixon/clixon.h>
#include <clixon/clixon_restconf.h>
static const char Base64[] = static const char Base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";