libhttp compile
This commit is contained in:
parent
ebaedfd482
commit
8c1372d26a
18 changed files with 1176 additions and 565 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -35,6 +35,7 @@ apps/cli/test.c
|
|||
apps/dbctrl/clixon_dbctrl
|
||||
apps/netconf/clixon_netconf
|
||||
apps/restconf/clixon_restconf
|
||||
apps/restconf/clixon_restconf_libhttp
|
||||
apps/xmldb/clixon_xmldb
|
||||
|
||||
etc/clixonrc
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
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
|
||||
INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
|
||||
|
|
|
|||
|
|
@ -272,7 +272,8 @@ usage(clicon_handle h,
|
|||
/*
|
||||
*/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
main(int argc,
|
||||
char **argv)
|
||||
{
|
||||
int retval = -1;
|
||||
int c;
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ HOST_VENDOR = @host_vendor@
|
|||
# XXX why is not wwwdir under prefix?
|
||||
wwwdir = @wwwdir@
|
||||
wwwuser = @wwwuser@
|
||||
with_libhttp = @with_libhttp@
|
||||
|
||||
SH_SUFFIX = @SH_SUFFIX@
|
||||
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)
|
||||
|
||||
LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB)
|
||||
ifeq ($(with_libhttp),yes)
|
||||
LIBS += -ldl -lpthread
|
||||
endif
|
||||
|
||||
CPPFLAGS = @CPPFLAGS@ -fPIC
|
||||
|
||||
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
|
||||
|
||||
# Name of application
|
||||
APPL = clixon_restconf
|
||||
# Applications
|
||||
APPLS = clixon_restconf # fcgi / nginx
|
||||
ifeq ($(with_libhttp),yes)
|
||||
APPLS += clixon_restconf_libhttp
|
||||
endif
|
||||
|
||||
# Not accessible from plugin
|
||||
APPSRC = restconf_main.c
|
||||
APPSRC += restconf_methods.c
|
||||
APPSRC += restconf_methods_post.c
|
||||
APPSRC += restconf_methods_get.c
|
||||
APPSRC += restconf_stream.c
|
||||
# Common source - not accessible from plugin - independent of fcgi
|
||||
APPSRC = restconf_lib.c
|
||||
APPOBJ = $(APPSRC:.c=.o)
|
||||
|
||||
# Accessible from plugin
|
||||
LIBSRC = restconf_lib.c
|
||||
LIBOBJ = $(LIBSRC:.c=.o)
|
||||
# Fcgi-specific source including main
|
||||
APPFCGI = restconf_fcgi_lib.c
|
||||
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
|
||||
MYNAME = clixon_restconf
|
||||
MYLIBLINK = lib$(MYNAME)$(SH_SUFFIX)
|
||||
MYLIB = $(MYLIBLINK).$(CLIXON_MAJOR).$(CLIXON_MINOR)
|
||||
MYLIBSO = $(MYLIBLINK).$(CLIXON_MAJOR)
|
||||
ifeq ($(with_libhttp),yes)
|
||||
# Libhttp-specific source including main
|
||||
APPLIBH = restconf_libhttp_main.c
|
||||
APPLIBHOBJ = $(APPLIBH:.c=.o)
|
||||
endif
|
||||
|
||||
all: $(MYLIB) $(APPL)
|
||||
all: $(APPLS)
|
||||
|
||||
# Dependency of clixon library
|
||||
$(top_srcdir)/lib/src/$(CLIXON_LIB):
|
||||
(cd $(top_srcdir)/lib/src && $(MAKE) $(MFLAGS) $(CLIXON_LIB))
|
||||
|
||||
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
|
||||
rm -f Makefile *~ .depend
|
||||
|
|
@ -110,52 +121,38 @@ distclean: clean
|
|||
# Also create a libexec/ directory for writeable/temporary files.
|
||||
# Put config file in etc/
|
||||
# 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)
|
||||
install -d -m 0755 -o $(wwwuser) -g $(wwwuser) $(DESTDIR)$(wwwdir)
|
||||
else
|
||||
install -d -m 0755 $(DESTDIR)$(wwwdir)
|
||||
endif
|
||||
install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(wwwdir)
|
||||
install -m 0755 $(INSTALLFLAGS) $(APPLS) $(DESTDIR)$(wwwdir)
|
||||
|
||||
install-lib: $(MYLIB)
|
||||
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
|
||||
install-include:
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(wwwdir)/$(APPL)
|
||||
rm -f $(DESTDIR)$(libdir)/$(MYLIBLINK)*
|
||||
rm -f $(DESTDIR)$(wwwdir)/$(APPLS)
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .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)
|
||||
$(CC) $(LDFLAGS) $(APPOBJ) -L. $(MYLIB) $(LIBS) -o $@
|
||||
clixon_restconf : $(APPOBJ) $(APPFCGIOBJ)
|
||||
$(CC) $(LDFLAGS) $(APPOBJ) $(APPFCGIOBJ) -L. $(LIBS) -o $@
|
||||
|
||||
$(MYLIB) : $(LIBOBJ) $(LIBDEPS)
|
||||
ifeq ($(HOST_VENDOR),apple)
|
||||
$(CC) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ $(LIBOBJ) $(LIBS)
|
||||
else
|
||||
$(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(LIBOBJ) $(LIBS) -Wl,-soname=$(MYLIBSO)
|
||||
ifeq ($(with_libhttp),yes)
|
||||
clixon_restconf_libhttp : $(APPOBJ) $(APPLIBHOBJ) $(LIBDEPS)
|
||||
$(CC) $(LDFLAGS) $(APPOBJ) $(APPLIBHOBJ) -L. $(LIBS) -o $@
|
||||
endif
|
||||
|
||||
# link-name is needed for application linking, eg for clixon_cli and clixon_config
|
||||
$(MYLIBLINK) : $(MYLIB)
|
||||
|
||||
TAGS:
|
||||
find . -name '*.[chyl]' -print | etags -
|
||||
|
||||
depend:
|
||||
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(LIBSRC) $(APPSRC) > .depend
|
||||
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(APPFCGI) $(APPSRC) > .depend
|
||||
|
||||
#include .depend
|
||||
|
||||
|
|
|
|||
490
apps/restconf/restconf_fcgi_lib.c
Normal file
490
apps/restconf/restconf_fcgi_lib.c
Normal 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;
|
||||
}
|
||||
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
***** 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.
|
||||
|
||||
|
|
@ -31,42 +32,29 @@
|
|||
|
||||
***** 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_
|
||||
#define _CLIXON_RESTCONF_H_
|
||||
#ifndef _RESTCONF_FCGI_LIB_H_
|
||||
#define _RESTCONF_FCGI_LIB_H_
|
||||
|
||||
/*
|
||||
* Types (also in restconf_lib.h)
|
||||
* Prototypes
|
||||
*/
|
||||
enum restconf_media{
|
||||
YANG_DATA_JSON, /* "application/yang-data+json" */
|
||||
YANG_DATA_XML, /* "application/yang-data+xml" */
|
||||
YANG_PATCH_JSON, /* "application/yang-patch+json" */
|
||||
YANG_PATCH_XML /* "application/yang-patch+xml" */
|
||||
};
|
||||
typedef enum restconf_media restconf_media;
|
||||
|
||||
/*
|
||||
* Prototypes (also in restconf_lib.h)
|
||||
*/
|
||||
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);
|
||||
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 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 /* _CLIXON_RESTCONF_H_ */
|
||||
#endif /* _RESTCONF_FCGI_LIB_H_ */
|
||||
|
|
@ -78,6 +78,7 @@
|
|||
|
||||
/* restconf */
|
||||
#include "restconf_lib.h"
|
||||
#include "restconf_fcgi_lib.h"
|
||||
#include "restconf_methods.h"
|
||||
#include "restconf_methods_get.h"
|
||||
#include "restconf_methods_post.h"
|
||||
|
|
@ -86,12 +87,6 @@
|
|||
/* Command line options to be passed to getopt(3) */
|
||||
#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
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
|
|
@ -492,44 +487,6 @@ restconf_sig_term(int arg)
|
|||
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
|
||||
restconf_sig_child(int arg)
|
||||
{
|
||||
|
|
@ -567,7 +524,7 @@ usage(clicon_handle h,
|
|||
exit(0);
|
||||
}
|
||||
|
||||
/*! Main routine for fastcgi API
|
||||
/*! Main routine for fastcgi restconf
|
||||
*/
|
||||
int
|
||||
main(int argc,
|
||||
|
|
@ -603,6 +560,7 @@ main(int argc,
|
|||
goto done;
|
||||
|
||||
_CLICON_HANDLE = h; /* for termination handling */
|
||||
|
||||
while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1)
|
||||
switch (c) {
|
||||
case 'h':
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
***** 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.
|
||||
|
||||
|
|
@ -58,8 +59,6 @@
|
|||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
#include <fcgiapp.h> /* Need to be after clixon_xml-h due to attribute format */
|
||||
|
||||
#include "restconf_lib.h"
|
||||
|
||||
/* 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 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
|
||||
* @param[in] cookiestr cookie string according to rfc6265 (modified)
|
||||
* @param[in] attribute cookie attribute
|
||||
|
|
@ -449,165 +203,6 @@ get_user_cookie(char *cookiestr,
|
|||
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).
|
||||
* Cannot use h after this
|
||||
* @param[in] h Clixon handle
|
||||
|
|
@ -757,23 +352,40 @@ restconf_insert_attributes(cxobj *xdata,
|
|||
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
|
||||
/*! 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
|
||||
*/
|
||||
char *
|
||||
restconf_uripath(FCGX_Request *r)
|
||||
int
|
||||
restconf_main_extension_cb(clicon_handle h,
|
||||
yang_stmt *yext,
|
||||
yang_stmt *ys)
|
||||
{
|
||||
char *path;
|
||||
char *q;
|
||||
int retval = -1;
|
||||
char *extname;
|
||||
char *modname;
|
||||
yang_stmt *ymod;
|
||||
yang_stmt *yc;
|
||||
yang_stmt *yn = NULL;
|
||||
|
||||
path = FCGX_GetParam("REQUEST_URI", r->envp);
|
||||
if ((q = index(path, '?')) != NULL)
|
||||
*q = '\0';
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
***** 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.
|
||||
|
||||
|
|
@ -41,8 +42,15 @@
|
|||
*/
|
||||
#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
|
||||
|
|
@ -58,32 +66,15 @@ enum restconf_media{
|
|||
typedef enum restconf_media restconf_media;
|
||||
|
||||
/*
|
||||
* Prototypes (also in clixon_restconf.h)
|
||||
* Prototypes
|
||||
*/
|
||||
int restconf_err2code(char *tag);
|
||||
const char *restconf_code2reason(int code);
|
||||
|
||||
const restconf_media restconf_media_str2int(char *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 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_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_ */
|
||||
|
|
|
|||
471
apps/restconf/restconf_libhttp_main.c
Normal file
471
apps/restconf/restconf_libhttp_main.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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 "restconf_lib.h"
|
||||
#include "restconf_fcgi_lib.h"
|
||||
#include "restconf_methods.h"
|
||||
|
||||
/*! REST OPTIONS method
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
#include <fcgiapp.h> /* Need to be after clixon_xml-h due to attribute format */
|
||||
|
||||
#include "restconf_lib.h"
|
||||
#include "restconf_fcgi_lib.h"
|
||||
#include "restconf_methods_get.h"
|
||||
|
||||
/*! Generic GET (both HEAD and GET)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@
|
|||
#include <fcgiapp.h> /* Need to be after clixon_xml.h due to attribute format */
|
||||
|
||||
#include "restconf_lib.h"
|
||||
#include "restconf_fcgi_lib.h"
|
||||
#include "restconf_methods_post.h"
|
||||
|
||||
/*! Generic REST POST method
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
***** 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.
|
||||
|
||||
|
|
@ -85,6 +86,7 @@
|
|||
#include <fcgiapp.h> /* Need to be after clixon_xml.h due to attribute format */
|
||||
|
||||
#include "restconf_lib.h"
|
||||
#include "restconf_fcgi_lib.h"
|
||||
#include "restconf_stream.h"
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
***** 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.
|
||||
|
||||
|
|
|
|||
82
configure
vendored
82
configure
vendored
|
|
@ -634,6 +634,7 @@ CPP
|
|||
wwwuser
|
||||
wwwdir
|
||||
enable_optyangs
|
||||
with_libhttp
|
||||
with_restconf
|
||||
SH_SUFFIX
|
||||
CLIXON_DEFAULT_CONFIG
|
||||
|
|
@ -715,6 +716,7 @@ enable_optyangs
|
|||
enable_publish
|
||||
with_restconf
|
||||
with_wwwuser
|
||||
with_libhttp
|
||||
with_configfile
|
||||
with_libxml2
|
||||
with_yang_installdir
|
||||
|
|
@ -1366,6 +1368,8 @@ Optional Packages:
|
|||
--with-cligen=dir Use CLIGEN here
|
||||
--without-restconf disable support for restconf
|
||||
--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-libxml2 use gnome/libxml2 regex engine
|
||||
--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, include libhttp server (https://www.libhttp.org/)
|
||||
|
||||
wwwdir=/www-data
|
||||
|
||||
|
|
@ -4739,6 +4744,83 @@ cat >>confdefs.h <<_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
|
||||
CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml
|
||||
|
||||
|
|
|
|||
15
configure.ac
15
configure.ac
|
|
@ -94,6 +94,7 @@ AC_SUBST(CLIXON_DEFAULT_CONFIG)
|
|||
AC_SUBST(LIBS)
|
||||
AC_SUBST(SH_SUFFIX)
|
||||
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(wwwdir,/www-data)
|
||||
AC_SUBST(wwwuser,www-data)
|
||||
|
|
@ -208,6 +209,20 @@ fi
|
|||
AC_MSG_RESULT(www user is $wwwuser)
|
||||
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
|
||||
CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml
|
||||
AC_ARG_WITH([configfile],
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@
|
|||
|
||||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
#include <clixon/clixon_restconf.h>
|
||||
|
||||
static const char Base64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue