From 8c1372d26a666f4d921d070bc3c59ef26d24db0a Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 28 May 2020 17:19:15 +0200 Subject: [PATCH] libhttp compile --- .gitignore | 1 + apps/cli/Makefile.in | 2 +- apps/cli/cli_main.c | 3 +- apps/restconf/Makefile.in | 83 ++- apps/restconf/restconf_fcgi_lib.c | 490 ++++++++++++++++++ ...{clixon_restconf.h => restconf_fcgi_lib.h} | 50 +- .../{restconf_main.c => restconf_fcgi_main.c} | 48 +- apps/restconf/restconf_lib.c | 458 ++-------------- apps/restconf/restconf_lib.h | 31 +- apps/restconf/restconf_libhttp_main.c | 471 +++++++++++++++++ apps/restconf/restconf_methods.c | 1 + apps/restconf/restconf_methods_get.c | 1 + apps/restconf/restconf_methods_post.c | 1 + apps/restconf/restconf_stream.c | 2 + apps/restconf/restconf_stream.h | 1 + configure | 82 +++ configure.ac | 15 + example/main/example_restconf.c | 1 - 18 files changed, 1176 insertions(+), 565 deletions(-) create mode 100644 apps/restconf/restconf_fcgi_lib.c rename apps/restconf/{clixon_restconf.h => restconf_fcgi_lib.h} (59%) rename apps/restconf/{restconf_main.c => restconf_fcgi_main.c} (94%) create mode 100644 apps/restconf/restconf_libhttp_main.c diff --git a/.gitignore b/.gitignore index 6826005a..04d0e8bd 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/apps/cli/Makefile.in b/apps/cli/Makefile.in index 5d078fa1..edeb00c7 100644 --- a/apps/cli/Makefile.in +++ b/apps/cli/Makefile.in @@ -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@ diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index b0f597e6..622a6023 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -272,7 +272,8 @@ usage(clicon_handle h, /* */ int -main(int argc, char **argv) +main(int argc, + char **argv) { int retval = -1; int c; diff --git a/apps/restconf/Makefile.in b/apps/restconf/Makefile.in index 28a49e9c..45a7f62d 100644 --- a/apps/restconf/Makefile.in +++ b/apps/restconf/Makefile.in @@ -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 diff --git a/apps/restconf/restconf_fcgi_lib.c b/apps/restconf/restconf_fcgi_lib.c new file mode 100644 index 00000000..63815d7f --- /dev/null +++ b/apps/restconf/restconf_fcgi_lib.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ +#include + +#include /* 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, "

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, "access-denied\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, "

Forbidden

\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, "

Not Found

\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, "

Not Acceptable

\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, "

Data resource already exists

\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, "

Unsupported Media Type

\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, "

Internal server error when accessing %s

\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, "

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: + * ...invalid-value + * 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, " \n", cbuf_get(cb)); + FCGX_FPrintF(r->out, "%s", cbuf_get(cb)); + FCGX_FPrintF(r->out, " \r\n"); + } + else { + FCGX_FPrintF(r->out, "", cbuf_get(cb)); + FCGX_FPrintF(r->out, "%s", cbuf_get(cb)); + FCGX_FPrintF(r->out, "\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; +} + diff --git a/apps/restconf/clixon_restconf.h b/apps/restconf/restconf_fcgi_lib.h similarity index 59% rename from apps/restconf/clixon_restconf.h rename to apps/restconf/restconf_fcgi_lib.h index 4a1b7b04..9d9ce829 100644 --- a/apps/restconf/clixon_restconf.h +++ b/apps/restconf/restconf_fcgi_lib.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. @@ -30,43 +31,30 @@ the terms of any one of the Apache License version 2 or the GPL. ***** 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_ */ diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_fcgi_main.c similarity index 94% rename from apps/restconf/restconf_main.c rename to apps/restconf/restconf_fcgi_main.c index 8a9bb9da..118ad915 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_fcgi_main.c @@ -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': diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 79bd12da..adf30863 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -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 -#include /* Need to be after clixon_xml-h due to attribute format */ - #include "restconf_lib.h" /* See RFC 8040 Section 7: Mapping from NETCONF 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, "

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, "access-denied\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, "

Forbidden

\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, "

Not Found

\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, "

Not Acceptable

\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, "

Data resource already exists

\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, "

Unsupported Media Type

\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, "

Internal server error when accessing %s

\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, "

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: - * ...invalid-value - * 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, " \n", cbuf_get(cb)); - FCGX_FPrintF(r->out, "%s", cbuf_get(cb)); - FCGX_FPrintF(r->out, " \r\n"); - } - else { - FCGX_FPrintF(r->out, "", cbuf_get(cb)); - FCGX_FPrintF(r->out, "%s", cbuf_get(cb)); - FCGX_FPrintF(r->out, "\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; - - path = FCGX_GetParam("REQUEST_URI", r->envp); - if ((q = index(path, '?')) != NULL) - *q = '\0'; - return path; + 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; } - diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h index 6c4a2613..f1ac15e3 100644 --- a/apps/restconf/restconf_lib.h +++ b/apps/restconf/restconf_lib.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. @@ -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_ */ diff --git a/apps/restconf/restconf_libhttp_main.c b/apps/restconf/restconf_libhttp_main.c new file mode 100644 index 00000000..2f4d8dfe --- /dev/null +++ b/apps/restconf/restconf_libhttp_main.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* chmod */ + +/* libhttp */ +#include + +/* cligen */ +#include + +/* clicon */ +#include + +/* 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 \t Debug level\n" + "\t-f \t Configuration file (mandatory)\n" + "\t-l > \t Log on (s)yslog, (f)ile (syslog is default)\n" + "\t-p \t Yang directory path (see CLICON_YANG_DIR)\n" + "\t-d \t Specify restconf plugin directory dir (default: %s)\n" + "\t-y \t Load yang spec file (override yang main module)\n" + "\t-a UNIX|IPv4|IPv6 Internal backend socket family\n" + "\t-u \t Internal socket domain path or IP addr (see -a)\n" + "\t-o \"