* Restconf FCGI (eg via nginx) have changed reply message syntax slightly
* native http: new restconf_err files, generic data input, restconf_methods generalized. * test: expecteq removed.
This commit is contained in:
parent
c18c40434f
commit
6e714beea5
37 changed files with 1619 additions and 1128 deletions
|
|
@ -85,15 +85,15 @@ APPL = clixon_restconf
|
|||
APPSRC =
|
||||
APPSRC += restconf_api.c # maybe empty
|
||||
APPSRC += restconf_api_$(with_restconf).c # cant be .so since libevhtp is a.
|
||||
APPSRC += restconf_err.c
|
||||
APPSRC += restconf_root.c
|
||||
APPSRC += restconf_$(with_restconf)_main.c
|
||||
APPSRC += restconf_methods.c
|
||||
APPSRC += restconf_methods_post.c
|
||||
APPSRC += restconf_methods_get.c
|
||||
|
||||
# Fcgi-specific source including main
|
||||
ifeq ($(with_restconf),fcgi)
|
||||
APPSRC += restconf_fcgi_lib.c # Most of these should be made generic
|
||||
APPSRC += restconf_methods.c
|
||||
APPSRC += restconf_methods_post.c
|
||||
APPSRC += restconf_methods_get.c
|
||||
APPSRC += restconf_stream.c
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -41,14 +41,14 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int restconf_reply_status_code(void *req, int code);
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 3
|
||||
int restconf_reply_header_add(void *req, char *name, char *vfmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
int restconf_reply_header(void *req, char *name, char *vfmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
#else
|
||||
int restconf_reply_header_add(FCGX_Request *req, char *name, char *vfmt, ...);
|
||||
int restconf_reply_header(FCGX_Request *req, char *name, char *vfmt, ...);
|
||||
#endif
|
||||
|
||||
int restconf_reply_send(void *req, cbuf *cb);
|
||||
int restconf_reply_send(void *req, int code, cbuf *cb);
|
||||
|
||||
cbuf *restconf_get_indata(void *req);
|
||||
|
||||
#endif /* _RESTCONF_API_H_ */
|
||||
|
|
|
|||
|
|
@ -65,22 +65,6 @@
|
|||
#include "restconf_lib.h"
|
||||
#include "restconf_api.h" /* Virtual api */
|
||||
|
||||
|
||||
/*! Add HTTP header field name and value to reply, evhtp specific
|
||||
* @param[in] req Evhtp http request handle
|
||||
* @param[in] code HTTP status code
|
||||
* @see eg RFC 7230
|
||||
*/
|
||||
int
|
||||
restconf_reply_status_code(void *req0,
|
||||
int code)
|
||||
{
|
||||
evhtp_request_t *req = (evhtp_request_t *)req0;
|
||||
|
||||
req->status = code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Add HTTP header field name and value to reply, evhtp specific
|
||||
* @param[in] req Evhtp http request handle
|
||||
* @param[in] name HTTP header field name
|
||||
|
|
@ -88,10 +72,10 @@ restconf_reply_status_code(void *req0,
|
|||
* @see eg RFC 7230
|
||||
*/
|
||||
int
|
||||
restconf_reply_header_add(void *req0,
|
||||
char *name,
|
||||
char *vfmt,
|
||||
...)
|
||||
restconf_reply_header(void *req0,
|
||||
char *name,
|
||||
char *vfmt,
|
||||
...)
|
||||
|
||||
{
|
||||
evhtp_request_t *req = (evhtp_request_t *)req0;
|
||||
|
|
@ -142,13 +126,20 @@ restconf_reply_header_add(void *req0,
|
|||
*/
|
||||
int
|
||||
restconf_reply_send(void *req0,
|
||||
int code,
|
||||
cbuf *cb)
|
||||
{
|
||||
evhtp_request_t *req = (evhtp_request_t *)req0;
|
||||
int retval = -1;
|
||||
evhtp_connection_t *conn;
|
||||
struct evbuffer *eb = NULL;
|
||||
|
||||
const char *reason_phrase;
|
||||
|
||||
req->status = code;
|
||||
if ((reason_phrase = restconf_code2reason(code)) == NULL)
|
||||
reason_phrase="";
|
||||
if (restconf_reply_header(req, "Status", "%d %s", code, reason_phrase) < 0)
|
||||
goto done;
|
||||
#if 1 /* Optional? */
|
||||
if ((conn = evhtp_request_get_connection(req)) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
|
||||
|
|
@ -181,3 +172,20 @@ restconf_reply_send(void *req0,
|
|||
evhtp_safe_free(eb, evbuffer_free);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! get input data
|
||||
* @param[in] req Fastcgi request handle
|
||||
* @note Pulls up an event buffer and then copies it to a cbuf. This is not efficient.
|
||||
*/
|
||||
cbuf *
|
||||
restconf_get_indata(void *req0)
|
||||
{
|
||||
evhtp_request_t *req = (evhtp_request_t *)req0;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
return NULL;
|
||||
if (evbuffer_get_length(req->buffer_in))
|
||||
cprintf(cb, "%s", evbuffer_pullup(req->buffer_in, -1));
|
||||
return cb;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,20 +63,6 @@
|
|||
#include "restconf_lib.h"
|
||||
#include "restconf_api.h" /* Virtual api */
|
||||
|
||||
/*! Add HTTP header field name and value to reply, fcgi specific
|
||||
* @param[in] req Fastcgi request handle
|
||||
* @param[in] code HTTP status code
|
||||
* @see eg RFC 7230
|
||||
*/
|
||||
int
|
||||
restconf_reply_status_code(void *req0,
|
||||
int code)
|
||||
{
|
||||
FCGX_Request *req = (FCGX_Request *)req0;
|
||||
|
||||
FCGX_SetExitStatus(code, req->out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP headers done, if there is a message body coming next
|
||||
* @param[in] req Fastcgi request handle
|
||||
|
|
@ -102,11 +88,10 @@ restconf_reply_body_start(void *req0)
|
|||
* @see eg RFC 7230
|
||||
*/
|
||||
int
|
||||
restconf_reply_header_add(void *req0,
|
||||
char *name,
|
||||
char *vfmt,
|
||||
...)
|
||||
|
||||
restconf_reply_header(void *req0,
|
||||
char *name,
|
||||
char *vfmt,
|
||||
...)
|
||||
{
|
||||
FCGX_Request *req = (FCGX_Request *)req0;
|
||||
int retval = -1;
|
||||
|
|
@ -195,23 +180,50 @@ restconf_reply_body_add(void *req0,
|
|||
}
|
||||
|
||||
/*! Send HTTP reply with potential message body
|
||||
* @param[in] req Fastcgi request handle
|
||||
* @param[in] cb Body as a cbuf, send if
|
||||
* @param[in] req Fastcgi request handle
|
||||
* @param[in] code Status code
|
||||
* @param[in] cb Body as a cbuf if non-NULL
|
||||
*
|
||||
* Prerequisites: status code set, headers given, body if wanted set
|
||||
*/
|
||||
int
|
||||
restconf_reply_send(void *req0,
|
||||
int code,
|
||||
cbuf *cb)
|
||||
{
|
||||
FCGX_Request *req = (FCGX_Request *)req0;
|
||||
int retval = -1;
|
||||
const char *reason_phrase;
|
||||
|
||||
FCGX_SetExitStatus(code, req->out);
|
||||
if ((reason_phrase = restconf_code2reason(code)) == NULL)
|
||||
reason_phrase="";
|
||||
if (restconf_reply_header(req, "Status", "%d %s", code, reason_phrase) < 0)
|
||||
goto done;
|
||||
FCGX_FPrintF(req->out, "\r\n");
|
||||
/* Write a body if cbuf is nonzero */
|
||||
if (cb != NULL && cbuf_len(cb)){
|
||||
FCGX_FPrintF(req->out, "\r\n");
|
||||
FCGX_FPrintF(req->out, "%s", cbuf_get(cb));
|
||||
FCGX_FPrintF(req->out, "\r\n");
|
||||
}
|
||||
retval = 0;
|
||||
return retval;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
cbuf *
|
||||
restconf_get_indata(void *req0)
|
||||
{
|
||||
FCGX_Request *req = (FCGX_Request *)req0;
|
||||
int c;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
return NULL;
|
||||
while ((c = FCGX_GetChar(req->in)) != -1)
|
||||
cprintf(cb, "%c", c);
|
||||
return cb;
|
||||
}
|
||||
|
|
|
|||
495
apps/restconf/restconf_err.c
Normal file
495
apps/restconf/restconf_err.c
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
*
|
||||
***** 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 *****
|
||||
*
|
||||
* Return errors
|
||||
* @see RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
|
||||
*/
|
||||
|
||||
#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 "restconf_lib.h"
|
||||
#include "restconf_api.h"
|
||||
#include "restconf_err.h"
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
/* In the fcgi implementations some errors had body, it would be cleaner to skip them
|
||||
* None seem mandatory according to RFC 7231
|
||||
*/
|
||||
#define SKIP_BODY
|
||||
|
||||
/*
|
||||
* NOTE, fcgi seems not enough with a status code (libevhtp is) but must also have a status
|
||||
* header.
|
||||
*/
|
||||
|
||||
/*! HTTP error 400
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Generic Www handle
|
||||
*/
|
||||
int
|
||||
restconf_badrequest(clicon_handle h,
|
||||
void *req)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
||||
if (restconf_reply_send(req, 400, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
#else
|
||||
char *path;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
/* Create body */
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
path = clixon_restconf_param_get("REQUEST_URI", r->envp);
|
||||
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
|
||||
goto done;
|
||||
cprintf(cb, "The requested URL %s or data is in some way badly formed.\n", path);
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 400, cb) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 401
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Generic Www handle
|
||||
*/
|
||||
int
|
||||
restconf_unauthorized(clicon_handle h,
|
||||
void *req)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
||||
if (restconf_reply_send(req, 400, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
#else
|
||||
char *path;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
/* Create body */
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
path = clixon_restconf_param_get("REQUEST_URI", r->envp);
|
||||
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
|
||||
goto done;
|
||||
cprintf(cb, "<error-tag>access-denied</error-tag>\n");
|
||||
cprintf(cb, "The requested URL %s was unauthorized.\n", path);
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
|
||||
if (restconf_reply_send(req, 400, cb) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 403
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Generic Www handle
|
||||
*/
|
||||
int
|
||||
restconf_forbidden(clicon_handle h,
|
||||
void *req)
|
||||
{
|
||||
int retval = -1;
|
||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
||||
if (restconf_reply_send(req, 403, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
#else
|
||||
char *path;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
/* Create body */
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
path = clixon_restconf_param_get("REQUEST_URI", r->envp);
|
||||
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
|
||||
goto done;
|
||||
cprintf(cb, "The requested URL %s was forbidden.\n", path);
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 403, cb) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 404
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* XXX skip body?
|
||||
*/
|
||||
int
|
||||
restconf_notfound(clicon_handle h,
|
||||
void *req)
|
||||
{
|
||||
int retval = -1;
|
||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
||||
if (restconf_reply_send(req, 404, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
#else
|
||||
char *path;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
/* Create body */
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
path = clixon_restconf_param_get("REQUEST_URI", r->envp);
|
||||
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
|
||||
goto done;
|
||||
cprintf(cb, "The requested URL %s was not found on this server.\n", path);
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 404, cb) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 405
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] allow Which methods are allowed
|
||||
*/
|
||||
int
|
||||
restconf_method_notallowed(void *req,
|
||||
char *allow)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (restconf_reply_header(req, "Allow", "%s", allow) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 405, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 406 Not acceptable
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Generic Www handle
|
||||
*/
|
||||
int
|
||||
restconf_notacceptable(clicon_handle h,
|
||||
void *req)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
||||
if (restconf_reply_send(req, 406, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
#else
|
||||
char *path;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
/* Create body */
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
path = clixon_restconf_param_get("REQUEST_URI", r->envp);
|
||||
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
|
||||
goto done;
|
||||
cprintf(cb, "The target resource does not have a current representation that would be acceptable to the user agent.\n", path);
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 406, cb) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 409
|
||||
* @param[in] req Generic Www handle
|
||||
*/
|
||||
int
|
||||
restconf_conflict(void *req)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (restconf_reply_send(req, 409, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 409 Unsupporte dmedia
|
||||
* @param[in] req Generic Www handle
|
||||
*/
|
||||
int
|
||||
restconf_unsupported_media(void *req)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (restconf_reply_send(req, 415, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 500 Internal server error
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Generic Www handle
|
||||
*/
|
||||
int
|
||||
restconf_internal_server_error(clicon_handle h,
|
||||
void *req)
|
||||
{
|
||||
int retval = -1;
|
||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
||||
if (restconf_reply_send(req, 500, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
#else
|
||||
char *path;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
/* Create body */
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
path = clixon_restconf_param_get("REQUEST_URI", r->envp);
|
||||
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
|
||||
goto done;
|
||||
cprintf(cb, "Internal server error when accessing %s</h1>\n", path);
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)=
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 500, cb) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 501 Not implemented
|
||||
* @param[in] req Generic Www handle
|
||||
*/
|
||||
int
|
||||
restconf_notimplemented(void *req)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (restconf_reply_send(req, 501, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic restconf error function on get/head request
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www 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,
|
||||
void *req,
|
||||
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;
|
||||
|
||||
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 (restconf_reply_header(req, "Content_Type", "%s", restconf_media_int2str(media)) < 0)
|
||||
goto done;
|
||||
switch (media){
|
||||
case YANG_DATA_XML:
|
||||
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
|
||||
if (pretty){
|
||||
cprintf(cb, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n");
|
||||
if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1) < 0)
|
||||
goto done;
|
||||
cprintf(cb, " </errors>\r\n");
|
||||
}
|
||||
else {
|
||||
cprintf(cb, "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">");
|
||||
if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "</errors>\r\n");
|
||||
}
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
|
||||
if (pretty){
|
||||
cprintf(cb, "{\n\"ietf-restconf:errors\" : ");
|
||||
if (xml2json_cbuf(cb, xerr, pretty) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "\n}\r\n");
|
||||
}
|
||||
else{
|
||||
cprintf(cb, "{");
|
||||
cprintf(cb, "\"ietf-restconf:errors\":");
|
||||
if (xml2json_cbuf(cb, xerr, pretty) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "}\r\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
clicon_err(OE_YANG, EINVAL, "Invalid media type %d", media);
|
||||
goto done;
|
||||
break;
|
||||
} /* switch media */
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, code, cb) < 0)
|
||||
goto done;
|
||||
// ok:
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (cberr)
|
||||
cbuf_free(cberr);
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -31,30 +31,30 @@
|
|||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
*
|
||||
* Return errors
|
||||
* @see RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
|
||||
*/
|
||||
|
||||
#ifndef _RESTCONF_FCGI_LIB_H_
|
||||
#define _RESTCONF_FCGI_LIB_H_
|
||||
#ifndef _RESTCONF_ERR_H_
|
||||
#define _RESTCONF_ERR_H_
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int restconf_badrequest(clicon_handle h, FCGX_Request *r);
|
||||
int restconf_unauthorized(clicon_handle h, FCGX_Request *r);
|
||||
int restconf_forbidden(clicon_handle h, FCGX_Request *r);
|
||||
int restconf_notfound(clicon_handle h, FCGX_Request *r);
|
||||
int restconf_notacceptable(clicon_handle h, FCGX_Request *r);
|
||||
int restconf_conflict(FCGX_Request *r);
|
||||
int restconf_unsupported_media(FCGX_Request *r);
|
||||
int restconf_internal_server_error(clicon_handle h, FCGX_Request *r);
|
||||
int restconf_notimplemented(FCGX_Request *r);
|
||||
int restconf_test(FCGX_Request *r, int dbg);
|
||||
int clixon_restconf_params_set(clicon_handle h,
|
||||
char **envp);
|
||||
int clixon_restconf_params_clear(clicon_handle h, char **envp);
|
||||
cbuf *readdata(FCGX_Request *r);
|
||||
int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr, int pretty, restconf_media media, int code0);
|
||||
int http_location(clicon_handle h, FCGX_Request *r, cxobj *xobj);
|
||||
|
||||
#endif /* _RESTCONF_FCGI_LIB_H_ */
|
||||
int restconf_badrequest(clicon_handle h, void *req);
|
||||
int restconf_unauthorized(clicon_handle h, void *req);
|
||||
int restconf_forbidden(clicon_handle h, void *req);
|
||||
int restconf_notfound(clicon_handle h, void *req);
|
||||
int restconf_method_notallowed(void *req, char *allow);
|
||||
int restconf_notacceptable(clicon_handle h, void *req);
|
||||
int restconf_conflict(void *req);
|
||||
int restconf_unsupported_media(void *req);
|
||||
int restconf_internal_server_error(clicon_handle h, void *req);
|
||||
int restconf_notimplemented(void *req);
|
||||
|
||||
int api_return_err(clicon_handle h, void *req, cxobj *xerr, int pretty, restconf_media media, int code0);
|
||||
|
||||
|
||||
#endif /* _RESTCONF_ERR_H_ */
|
||||
|
|
@ -178,6 +178,7 @@ query_iterator(evhtp_header_t *hdr,
|
|||
* several different places in evhtp
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Evhtp request struct
|
||||
* @param[out] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* The following parameters are set:
|
||||
|
|
@ -211,7 +212,10 @@ evhtp_params_set(clicon_handle h,
|
|||
}
|
||||
meth = evhtp_request_get_method(req);
|
||||
|
||||
/* QUERY_STRING */
|
||||
/* QUERY_STRING in fcgi but go direct to the info instead of putting it in a string?
|
||||
* This is different from all else: Ie one could have re-created a string here but
|
||||
* that would mean double parsing,...
|
||||
*/
|
||||
if (qvec && uri->query)
|
||||
if (evhtp_kvs_for_each(uri->query, query_iterator, qvec) < 0){
|
||||
clicon_err(OE_CFG, errno, "evhtp_kvs_for_each");
|
||||
|
|
@ -349,70 +353,29 @@ static void
|
|||
cx_path_restconf(evhtp_request_t *req,
|
||||
void *arg)
|
||||
{
|
||||
evhtp_connection_t *conn;
|
||||
clicon_handle h = arg;
|
||||
struct evbuffer *b = NULL;
|
||||
cvec *qvec = NULL;
|
||||
size_t len = 0;
|
||||
cbuf *cblen = NULL;
|
||||
clicon_handle h = arg;
|
||||
cvec *qvec = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (req == NULL){
|
||||
errno = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
/* input debug */
|
||||
if (clicon_debug_get())
|
||||
evhtp_headers_for_each(req->headers_in, print_header, h);
|
||||
|
||||
if ((cblen = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* get accepted connection */
|
||||
/* Query vector, ie the ?a=x&b=y stuff */
|
||||
if ((qvec = cvec_new(0)) ==NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
/* get accepted connection */
|
||||
if ((conn = evhtp_request_get_connection(req)) == NULL){
|
||||
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
|
||||
goto done;
|
||||
}
|
||||
/* Get all parameters from this request (resembling fcgi) */
|
||||
/* set fcgi-like paramaters (ignore query vector) */
|
||||
if (evhtp_params_set(h, req, qvec) < 0)
|
||||
goto done;
|
||||
|
||||
/* 1. create body */
|
||||
if ((b = evbuffer_new()) == NULL){
|
||||
clicon_err(OE_DAEMON, errno, "evbuffer_new");
|
||||
/* call generic function */
|
||||
if (api_root_restconf(h, req, qvec) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cblen, "%lu", len);
|
||||
|
||||
/* 2. add headers (can mix with body) */
|
||||
evhtp_headers_add_header(req->headers_out, evhtp_header_new("Cache-Control", "no-cache", 0, 0));
|
||||
evhtp_headers_add_header(req->headers_out, evhtp_header_new("Content-Type", "application/xrd+xml", 0, 0));
|
||||
evhtp_headers_add_header(req->headers_out, evhtp_header_new("Content-Length", cbuf_get(cblen), 0, 0));
|
||||
|
||||
/* Optional? */
|
||||
htp_sslutil_add_xheaders(req->headers_out, conn->ssl, HTP_SSLUTILS_XHDR_ALL);
|
||||
|
||||
/* 3. send reply */
|
||||
evhtp_send_reply_start(req, EVHTP_RES_OK);
|
||||
evhtp_send_reply_body(req, b);
|
||||
evhtp_send_reply_end(req);
|
||||
|
||||
/* Clear (fcgi)paramaters */
|
||||
/* Clear (fcgi) paramaters from this request */
|
||||
if (evhtp_params_clear(h) < 0)
|
||||
goto done;
|
||||
done:
|
||||
if (qvec)
|
||||
cvec_free(qvec);
|
||||
if (cblen)
|
||||
cbuf_free(cblen);
|
||||
if (b)
|
||||
evhtp_safe_free(b, evbuffer_free);
|
||||
return; /* void */
|
||||
}
|
||||
|
||||
|
|
@ -756,7 +719,6 @@ main(int argc,
|
|||
if (clicon_options_main(h) < 0)
|
||||
goto done;
|
||||
|
||||
|
||||
event_base_loop(evbase, 0);
|
||||
|
||||
evhtp_unbind_socket(htp);
|
||||
|
|
|
|||
|
|
@ -1,480 +0,0 @@
|
|||
/*
|
||||
*
|
||||
***** 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"
|
||||
|
||||
/*! HTTP error 400
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_badrequest(clicon_handle h,
|
||||
FCGX_Request *req)
|
||||
{
|
||||
char *path;
|
||||
|
||||
path = clixon_restconf_param_get(h, "REQUEST_URI");
|
||||
FCGX_SetExitStatus(400, req->out);
|
||||
FCGX_FPrintF(req->out, "Status: 400 Bad Request\r\n"); /* 400 bad request */
|
||||
FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(req->out, "<h1>Clixon Bad request/h1>\n");
|
||||
FCGX_FPrintF(req->out, "The requested URL %s or data is in some way badly formed.\n",
|
||||
path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP error 401
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_unauthorized(clicon_handle h,
|
||||
FCGX_Request *req)
|
||||
{
|
||||
char *path;
|
||||
|
||||
path = clixon_restconf_param_get(h, "REQUEST_URI");
|
||||
FCGX_SetExitStatus(401, req->out);
|
||||
FCGX_FPrintF(req->out, "Status: 401 Unauthorized\r\n"); /* 401 unauthorized */
|
||||
FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(req->out, "<error-tag>access-denied</error-tag>\n");
|
||||
FCGX_FPrintF(req->out, "The requested URL %s was unauthorized.\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP error 403
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_forbidden(clicon_handle h,
|
||||
FCGX_Request *req)
|
||||
{
|
||||
char *path;
|
||||
|
||||
path = clixon_restconf_param_get(h, "REQUEST_URI");
|
||||
FCGX_SetExitStatus(403, req->out);
|
||||
FCGX_FPrintF(req->out, "Status: 403 Forbidden\r\n"); /* 403 forbidden */
|
||||
FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(req->out, "<h1>Forbidden</h1>\n");
|
||||
FCGX_FPrintF(req->out, "The requested URL %s was forbidden.\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP error 404
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_notfound(clicon_handle h,
|
||||
FCGX_Request *req)
|
||||
{
|
||||
char *path;
|
||||
|
||||
path = clixon_restconf_param_get(h, "REQUEST_URI");
|
||||
FCGX_SetExitStatus(404, req->out);
|
||||
FCGX_FPrintF(req->out, "Status: 404 Not Found\r\n"); /* 404 not found */
|
||||
FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(req->out, "<h1>Not Found</h1>\n");
|
||||
FCGX_FPrintF(req->out, "Not Found\n");
|
||||
FCGX_FPrintF(req->out, "The requested URL %s was not found on this server.\n",
|
||||
path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP error 406 Not acceptable
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_notacceptable(clicon_handle h,
|
||||
FCGX_Request *req)
|
||||
{
|
||||
char *path;
|
||||
|
||||
path = clixon_restconf_param_get(h, "REQUEST_URI");
|
||||
FCGX_SetExitStatus(406, req->out);
|
||||
FCGX_FPrintF(req->out, "Status: 406 Not Acceptable\r\n"); /* 406 not acceptible */
|
||||
|
||||
FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(req->out, "<h1>Not Acceptable</h1>\n");
|
||||
FCGX_FPrintF(req->out, "Not Acceptable\n");
|
||||
FCGX_FPrintF(req->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] req Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_conflict(FCGX_Request *req)
|
||||
{
|
||||
FCGX_SetExitStatus(409, req->out);
|
||||
FCGX_FPrintF(req->out, "Status: 409 Conflict\r\n"); /* 409 Conflict */
|
||||
FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(req->out, "<h1>Data resource already exists</h1>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP error 409
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_unsupported_media(FCGX_Request *req)
|
||||
{
|
||||
FCGX_SetExitStatus(415, req->out);
|
||||
FCGX_FPrintF(req->out, "Status: 415 Unsupported Media Type\r\n");
|
||||
FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(req->out, "<h1>Unsupported Media Type</h1>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP error 500
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_internal_server_error(clicon_handle h,
|
||||
FCGX_Request *req)
|
||||
{
|
||||
char *path;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
path = clixon_restconf_param_get(h, "REQUEST_URI");
|
||||
FCGX_FPrintF(req->out, "Status: 500 Internal Server Error\r\n"); /* 500 internal server error */
|
||||
FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(req->out, "<h1>Internal server error when accessing %s</h1>\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP error 501
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_notimplemented(FCGX_Request *req)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
FCGX_FPrintF(req->out, "Status: 501 Not Implemented\r\n");
|
||||
FCGX_FPrintF(req->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(req->out, "<h1>Not Implemented/h1>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Print all FCGI headers
|
||||
* @param[in] req Fastcgi request handle
|
||||
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
|
||||
*/
|
||||
int
|
||||
restconf_test(FCGX_Request *req,
|
||||
int dbg)
|
||||
{
|
||||
char **environ = req->envp;
|
||||
int i;
|
||||
|
||||
clicon_debug(1, "All environment vars:");
|
||||
for (i = 0; environ[i] != NULL; i++){
|
||||
clicon_debug(1, "%s", environ[i]);
|
||||
}
|
||||
clicon_debug(1, "End environment vars");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Convert FCGI parameters to clixon runtime data
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] envp Fastcgi request handle parameter array on the format "<param>=<value>"
|
||||
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
|
||||
*/
|
||||
int
|
||||
clixon_restconf_params_set(clicon_handle h,
|
||||
char **envp)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
char *param = NULL;
|
||||
char *val = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
for (i = 0; envp[i] != NULL; i++){ /* on the form <param>=<value> */
|
||||
if (clixon_strsplit(envp[i], '=', ¶m, &val) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s param:%s val:%s", __FUNCTION__, param, val);
|
||||
if (clixon_restconf_param_set(h, param, val) < 0)
|
||||
goto done;
|
||||
if (param){
|
||||
free(param);
|
||||
param = NULL;
|
||||
}
|
||||
if (val){
|
||||
free(val);
|
||||
val = NULL;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Clear all FCGI parameters in an environment
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] envp Fastcgi request handle parameter array on the format "<param>=<value>"
|
||||
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
|
||||
*/
|
||||
int
|
||||
clixon_restconf_params_clear(clicon_handle h,
|
||||
char **envp)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
char *param = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
for (i = 0; envp[i] != NULL; i++){ /* on the form <param>=<value> */
|
||||
if (clixon_strsplit(envp[i], '=', ¶m, NULL) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s param:%s", __FUNCTION__, param);
|
||||
if (clixon_restconf_param_del(h, param) < 0)
|
||||
goto done;
|
||||
if (param){
|
||||
free(param);
|
||||
param = NULL;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param[in] req Fastcgi request handle
|
||||
*/
|
||||
cbuf *
|
||||
readdata(FCGX_Request *req)
|
||||
{
|
||||
int c;
|
||||
cbuf *cb;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
return NULL;
|
||||
while ((c = FCGX_GetChar(req->in)) != -1)
|
||||
cprintf(cb, "%c", c);
|
||||
return cb;
|
||||
}
|
||||
|
||||
/*! Return restconf error on get/head request
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req 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 *req,
|
||||
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, req->out); /* Created */
|
||||
FCGX_FPrintF(req->out, "Status: %d %s\r\n", code, reason_phrase);
|
||||
FCGX_FPrintF(req->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(req->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n", cbuf_get(cb));
|
||||
FCGX_FPrintF(req->out, "%s", cbuf_get(cb));
|
||||
FCGX_FPrintF(req->out, " </errors>\r\n");
|
||||
}
|
||||
else {
|
||||
FCGX_FPrintF(req->out, "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">", cbuf_get(cb));
|
||||
FCGX_FPrintF(req->out, "%s", cbuf_get(cb));
|
||||
FCGX_FPrintF(req->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(req->out, "{\n");
|
||||
FCGX_FPrintF(req->out, " \"ietf-restconf:errors\" : %s\n",
|
||||
cbuf_get(cb));
|
||||
FCGX_FPrintF(req->out, "}\r\n");
|
||||
}
|
||||
else{
|
||||
FCGX_FPrintF(req->out, "{");
|
||||
FCGX_FPrintF(req->out, "\"ietf-restconf:errors\":");
|
||||
FCGX_FPrintF(req->out, "%s", cbuf_get(cb));
|
||||
FCGX_FPrintF(req->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] req 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(clicon_handle h,
|
||||
FCGX_Request *req,
|
||||
cxobj *xobj)
|
||||
{
|
||||
int retval = -1;
|
||||
char *https;
|
||||
char *host;
|
||||
char *request_uri;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
https = clixon_restconf_param_get(h, "HTTPS");
|
||||
host = clixon_restconf_param_get(h, "HTTP_HOST");
|
||||
request_uri = clixon_restconf_param_get(h, "REQUEST_URI");
|
||||
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(req->out, "Location: http%s://%s%s%s\r\n",
|
||||
https?"s":"",
|
||||
host,
|
||||
request_uri,
|
||||
cbuf_get(cb));
|
||||
}
|
||||
else
|
||||
FCGX_FPrintF(req->out, "Location: http%s://%s%s\r\n",
|
||||
https?"s":"",
|
||||
host,
|
||||
request_uri);
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -79,9 +79,8 @@
|
|||
/* restconf */
|
||||
#include "restconf_lib.h" /* generic shared with plugins */
|
||||
#include "restconf_api.h" /* generic not shared with plugins */
|
||||
|
||||
#include "restconf_err.h"
|
||||
#include "restconf_root.h" /* generic not shared with plugins */
|
||||
#include "restconf_fcgi_lib.h" /* fcgi specific */
|
||||
#include "restconf_methods.h" /* fcgi specific */
|
||||
#include "restconf_methods_get.h"
|
||||
#include "restconf_methods_post.h"
|
||||
|
|
@ -90,6 +89,90 @@
|
|||
/* Command line options to be passed to getopt(3) */
|
||||
#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:"
|
||||
|
||||
/*! Convert FCGI parameters to clixon runtime data
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] envp Fastcgi request handle parameter array on the format "<param>=<value>"
|
||||
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
|
||||
*/
|
||||
static int
|
||||
fcgi_params_set(clicon_handle h,
|
||||
char **envp)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
char *param = NULL;
|
||||
char *val = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
for (i = 0; envp[i] != NULL; i++){ /* on the form <param>=<value> */
|
||||
if (clixon_strsplit(envp[i], '=', ¶m, &val) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s param:%s val:%s", __FUNCTION__, param, val);
|
||||
if (clixon_restconf_param_set(h, param, val) < 0)
|
||||
goto done;
|
||||
if (param){
|
||||
free(param);
|
||||
param = NULL;
|
||||
}
|
||||
if (val){
|
||||
free(val);
|
||||
val = NULL;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Clear all FCGI parameters in an environment
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] envp Fastcgi request handle parameter array on the format "<param>=<value>"
|
||||
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
|
||||
*/
|
||||
static int
|
||||
fcgi_params_clear(clicon_handle h,
|
||||
char **envp)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
char *param = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
for (i = 0; envp[i] != NULL; i++){ /* on the form <param>=<value> */
|
||||
if (clixon_strsplit(envp[i], '=', ¶m, NULL) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s param:%s", __FUNCTION__, param);
|
||||
if (clixon_restconf_param_del(h, param) < 0)
|
||||
goto done;
|
||||
if (param){
|
||||
free(param);
|
||||
param = NULL;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Print all FCGI headers
|
||||
* @param[in] req Fastcgi request handle
|
||||
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
|
||||
*/
|
||||
static int
|
||||
fcgi_print_headers(FCGX_Request *req)
|
||||
{
|
||||
char **environ = req->envp;
|
||||
int i;
|
||||
|
||||
clicon_debug(1, "All environment vars:");
|
||||
for (i = 0; environ[i] != NULL; i++)
|
||||
clicon_debug(1, "%s", environ[i]);
|
||||
clicon_debug(1, "End environment vars");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Generic REST method, GET, PUT, DELETE, etc
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
|
|
@ -97,7 +180,6 @@
|
|||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] dvec Stream input daat
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_in Input media
|
||||
* @param[in] media_out Output media
|
||||
|
|
@ -176,46 +258,6 @@ api_operations(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Determine the root of the RESTCONF API
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @note Hardcoded to "/restconf"
|
||||
* Return see RFC8040 3.1 and RFC7320
|
||||
* In line with the best practices defined by [RFC7320], RESTCONF
|
||||
* enables deployments to specify where the RESTCONF API is located.
|
||||
*/
|
||||
#if 0
|
||||
static int
|
||||
api_well_known(clicon_handle h,
|
||||
FCGX_Request *req)
|
||||
{
|
||||
char *request_method;
|
||||
FCGX_Request *body;
|
||||
|
||||
/* call generic function */
|
||||
if (api_well_known(h, req) < 0)
|
||||
goto done;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (req == NULL){
|
||||
errno = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
request_method = clixon_restconf_param_get(h, "REQUEST_METHOD");
|
||||
if (strcmp(request_method, "GET") !=0 )
|
||||
return restconf_method_notallowed(req, "GET");
|
||||
restconf_reply_status_code(req, 200); /* OK */
|
||||
restconf_reply_header_add(req, "Cache-Control", "no-cache");
|
||||
restconf_reply_header_add(req, "Content-Type", "application/xrd+xml");
|
||||
body = restconf_reply_body_start(req);
|
||||
|
||||
restconf_reply_body_add(body, NULL, "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>\n");
|
||||
restconf_reply_body_add(body, NULL, " <Link rel='restconf' href='/restconf'/>\n");
|
||||
restconf_reply_body_add(body, NULL, "</XRD>\r\n");
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/*! Retrieve the Top-Level API Resource
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
|
|
@ -224,10 +266,10 @@ api_well_known(clicon_handle h,
|
|||
* XXX doesnt check method
|
||||
*/
|
||||
static int
|
||||
api_root(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
int pretty,
|
||||
restconf_media media_out)
|
||||
api_fcgi_root(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
int pretty,
|
||||
restconf_media media_out)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -337,25 +379,23 @@ api_yang_library_version(clicon_handle h,
|
|||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
static int
|
||||
api_restconf(clicon_handle h,
|
||||
FCGX_Request *req)
|
||||
api_fcgi_restconf(clicon_handle h,
|
||||
FCGX_Request *req)
|
||||
{
|
||||
int retval = -1;
|
||||
char *path;
|
||||
char *query = NULL;
|
||||
char *method;
|
||||
char *api_resource;
|
||||
char **pvec = NULL;
|
||||
int pn;
|
||||
cvec *qvec = NULL;
|
||||
cvec *dvec = NULL;
|
||||
cvec *pcvec = NULL; /* for rest api */
|
||||
cbuf *cb = NULL;
|
||||
char *data;
|
||||
char *indata = NULL;
|
||||
int authenticated = 0;
|
||||
char *media_str = NULL;
|
||||
restconf_media media_out = YANG_DATA_JSON;
|
||||
int pretty;
|
||||
cbuf *cbret = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
|
|
@ -398,30 +438,28 @@ api_restconf(clicon_handle h,
|
|||
retval = restconf_notfound(h, req);
|
||||
goto done;
|
||||
}
|
||||
restconf_test(req, 1);
|
||||
fcgi_print_headers(req);
|
||||
|
||||
if (pn == 2){
|
||||
retval = api_root(h, req, pretty, media_out);
|
||||
retval = api_fcgi_root(h, req, pretty, media_out);
|
||||
goto done;
|
||||
}
|
||||
if ((method = pvec[2]) == NULL){
|
||||
if ((api_resource = pvec[2]) == NULL){
|
||||
retval = restconf_notfound(h, req);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "%s: method=%s", __FUNCTION__, method);
|
||||
clicon_debug(1, "%s: api_resource=%s", __FUNCTION__, api_resource);
|
||||
if (query != NULL && strlen(query))
|
||||
if (str2cvec(query, '&', '=', &qvec) < 0)
|
||||
goto done;
|
||||
if (str2cvec(path, '/', '=', &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
||||
goto done;
|
||||
/* data */
|
||||
if ((cb = readdata(req)) == NULL)
|
||||
/* indata */
|
||||
if ((cb = restconf_get_indata(req)) == NULL)
|
||||
goto done;
|
||||
data = cbuf_get(cb);
|
||||
clicon_debug(1, "%s DATA=%s", __FUNCTION__, data);
|
||||
indata = cbuf_get(cb);
|
||||
clicon_debug(1, "%s DATA=%s", __FUNCTION__, indata);
|
||||
|
||||
if (str2cvec(data, '&', '=', &dvec) < 0)
|
||||
goto done;
|
||||
/* If present, check credentials. See "plugin_credentials" in plugin
|
||||
* See RFC 8040 section 2.5
|
||||
*/
|
||||
|
|
@ -429,7 +467,7 @@ api_restconf(clicon_handle h,
|
|||
goto done;
|
||||
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||
|
||||
/* If set but no user, we set a dummy user */
|
||||
/* If set but no user, set a dummy user */
|
||||
if (authenticated){
|
||||
if (clicon_username_get(h) == NULL)
|
||||
clicon_username_set(h, "none");
|
||||
|
|
@ -445,22 +483,20 @@ api_restconf(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||
if (strcmp(method, "yang-library-version")==0){
|
||||
if (strcmp(api_resource, "yang-library-version")==0){
|
||||
if (api_yang_library_version(h, req, pretty, media_out) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(method, "data") == 0){ /* restconf, skip /api/data */
|
||||
if (api_data(h, req, path, pcvec, 2, qvec, data,
|
||||
else if (strcmp(api_resource, "data") == 0){ /* restconf, skip /api/data */
|
||||
if (api_data(h, req, path, pcvec, 2, qvec, indata,
|
||||
pretty, media_out) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(method, "operations") == 0){ /* rpc */
|
||||
if (api_operations(h, req, path, pcvec, 2, qvec, data,
|
||||
else if (strcmp(api_resource, "operations") == 0){ /* rpc */
|
||||
if (api_operations(h, req, path, pcvec, 2, qvec, indata,
|
||||
pretty, media_out) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(method, "test") == 0)
|
||||
restconf_test(req, 0);
|
||||
else
|
||||
restconf_notfound(h, req);
|
||||
ok:
|
||||
|
|
@ -469,16 +505,12 @@ api_restconf(clicon_handle h,
|
|||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (pvec)
|
||||
free(pvec);
|
||||
if (dvec)
|
||||
cvec_free(dvec);
|
||||
if (qvec)
|
||||
cvec_free(qvec);
|
||||
if (pcvec)
|
||||
cvec_free(pcvec);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
|
|
@ -834,12 +866,16 @@ main(int argc,
|
|||
/* Translate from FCGI parameter form to Clixon runtime data
|
||||
* XXX: potential name collision?
|
||||
*/
|
||||
if (clixon_restconf_params_set(h, req->envp) < 0)
|
||||
if (fcgi_params_set(h, req->envp) < 0)
|
||||
goto done;
|
||||
/* Debug_ print headers */
|
||||
if (clicon_debug_get())
|
||||
fcgi_print_headers(req);
|
||||
|
||||
if ((path = clixon_restconf_param_get(h, "REQUEST_URI")) != NULL){
|
||||
clicon_debug(1, "path: %s", path);
|
||||
if (strncmp(path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0)
|
||||
api_restconf(h, req); /* This is the function */
|
||||
api_fcgi_restconf(h, req); /* This is the function */
|
||||
else if (strncmp(path+1, stream_path, strlen(stream_path)) == 0) {
|
||||
api_stream(h, req, stream_path, &finish);
|
||||
}
|
||||
|
|
@ -853,7 +889,7 @@ main(int argc,
|
|||
}
|
||||
else
|
||||
clicon_debug(1, "NULL URI");
|
||||
if (clixon_restconf_params_clear(h, req->envp) < 0)
|
||||
if (fcgi_params_clear(h, req->envp) < 0)
|
||||
goto done;
|
||||
if (finish)
|
||||
FCGX_Finish_r(req);
|
||||
|
|
|
|||
|
|
@ -478,7 +478,6 @@ restconf_uripath(clicon_handle h)
|
|||
return path;
|
||||
}
|
||||
|
||||
|
||||
/*! Drop privileges from root to user (or already at user)
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] user Drop to this level
|
||||
|
|
@ -540,15 +539,3 @@ restconf_drop_privileges(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! HTTP error 405
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] allow Which methods are allowed
|
||||
*/
|
||||
int
|
||||
restconf_method_notallowed(void *req,
|
||||
char *allow)
|
||||
{
|
||||
restconf_reply_status_code(req, 405);
|
||||
restconf_reply_header_add(req, "Allow", "%s", allow);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,5 @@ int clixon_restconf_param_set(clicon_handle h, char *param, char *val);
|
|||
int clixon_restconf_param_del(clicon_handle h, char *param);
|
||||
char *restconf_uripath(clicon_handle h);
|
||||
int restconf_drop_privileges(clicon_handle h, char *user);
|
||||
int restconf_method_notallowed(void *req, char *allow);
|
||||
|
||||
#endif /* _RESTCONF_LIB_H_ */
|
||||
|
|
|
|||
|
|
@ -119,16 +119,16 @@ Mapping netconf error-tag -> status code
|
|||
/* 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"
|
||||
#include "restconf_api.h"
|
||||
#include "restconf_err.h"
|
||||
#include "restconf_methods.h"
|
||||
|
||||
/*! REST OPTIONS method
|
||||
* According to restconf
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] req Generic Www handle
|
||||
*
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
|
|
@ -139,14 +139,20 @@ Mapping netconf error-tag -> status code
|
|||
*/
|
||||
int
|
||||
api_data_options(clicon_handle h,
|
||||
FCGX_Request *r)
|
||||
void *req)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE\r\n");
|
||||
FCGX_FPrintF(r->out, "Accept-Patch: application/yang-data+xml,application/yang-data+json\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
return 0;
|
||||
if (restconf_reply_header(req, "Allow", "OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Accept-Patch", "application/yang-data+xml,application/yang-data+json") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 200, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Check matching keys
|
||||
|
|
@ -226,7 +232,7 @@ match_list_keys(yang_stmt *y,
|
|||
*/
|
||||
static int
|
||||
api_data_write(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *api_path0,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
|
|
@ -285,7 +291,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -300,7 +306,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
||||
|
|
@ -311,7 +317,7 @@ api_data_write(clicon_handle h,
|
|||
#endif
|
||||
if (xml_child_nr(xret) == 0){ /* Object does not exist */
|
||||
if (plain_patch){ /* If the target resource instance does not exist, the server MUST NOT create it. */
|
||||
restconf_badrequest(h, r);
|
||||
restconf_badrequest(h, req);
|
||||
goto ok;
|
||||
}
|
||||
else
|
||||
|
|
@ -340,7 +346,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -357,7 +363,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -400,7 +406,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -409,7 +415,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -422,7 +428,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -431,13 +437,13 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
restconf_unsupported_media(r);
|
||||
restconf_unsupported_media(req);
|
||||
goto ok;
|
||||
break;
|
||||
} /* switch media_in */
|
||||
|
|
@ -452,7 +458,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -471,7 +477,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -520,7 +526,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -545,7 +551,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -569,7 +575,7 @@ api_data_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -610,7 +616,7 @@ api_data_write(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -631,7 +637,7 @@ api_data_write(clicon_handle h,
|
|||
/* log errors from discard, but ignore */
|
||||
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
|
||||
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -659,14 +665,13 @@ api_data_write(clicon_handle h,
|
|||
}
|
||||
/* Check if it was created, or if we tried again and replaced it */
|
||||
if (op == OP_CREATE){
|
||||
FCGX_SetExitStatus(201, r->out); /* Created */
|
||||
FCGX_FPrintF(r->out, "Status: 201 Created\r\n");
|
||||
if (restconf_reply_send(req, 201, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
FCGX_SetExitStatus(204, r->out); /* Replaced */
|
||||
FCGX_FPrintF(r->out, "Status: 204 No Content\r\n");
|
||||
if (restconf_reply_send(req, 204, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -693,14 +698,14 @@ api_data_write(clicon_handle h,
|
|||
} /* api_data_write */
|
||||
|
||||
/*! Generic REST PUT method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
|
||||
* @note restconf PUT is mapped to edit-config replace.
|
||||
|
|
@ -729,7 +734,7 @@ api_data_write(clicon_handle h,
|
|||
*/
|
||||
int
|
||||
api_data_put(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *api_path0,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
|
|
@ -741,19 +746,19 @@ api_data_put(clicon_handle h,
|
|||
restconf_media media_in;
|
||||
|
||||
media_in = restconf_content_type(h);
|
||||
return api_data_write(h, r, api_path0, pcvec, pi, qvec, data, pretty,
|
||||
return api_data_write(h, req, api_path0, pcvec, pi, qvec, data, pretty,
|
||||
media_in, media_out, 0);
|
||||
}
|
||||
|
||||
/*! Generic REST PATCH method for plain patch
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
* Netconf: <edit-config> (nc:operation="merge")
|
||||
* See RFC8040 Sec 4.6.1
|
||||
|
|
@ -764,7 +769,7 @@ api_data_put(clicon_handle h,
|
|||
*/
|
||||
int
|
||||
api_data_patch(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *api_path0,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
|
|
@ -780,26 +785,26 @@ api_data_patch(clicon_handle h,
|
|||
switch (media_in){
|
||||
case YANG_DATA_XML:
|
||||
case YANG_DATA_JSON: /* plain patch */
|
||||
ret = api_data_write(h, r, api_path0, pcvec, pi, qvec, data, pretty,
|
||||
ret = api_data_write(h, req, api_path0, pcvec, pi, qvec, data, pretty,
|
||||
media_in, media_out, 1);
|
||||
break;
|
||||
case YANG_PATCH_XML:
|
||||
case YANG_PATCH_JSON: /* RFC 8072 patch */
|
||||
ret = restconf_notimplemented(r);
|
||||
ret = restconf_notimplemented(req);
|
||||
break;
|
||||
default:
|
||||
ret = restconf_unsupported_media(r);
|
||||
ret = restconf_unsupported_media(req);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! Generic REST DELETE method translated to edit-config
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
* See RFC 8040 Sec 4.7
|
||||
* Example:
|
||||
|
|
@ -808,7 +813,7 @@ api_data_patch(clicon_handle h,
|
|||
*/
|
||||
int
|
||||
api_data_delete(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *api_path,
|
||||
int pi,
|
||||
int pretty,
|
||||
|
|
@ -850,7 +855,7 @@ api_data_delete(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -879,7 +884,7 @@ api_data_delete(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -901,7 +906,7 @@ api_data_delete(clicon_handle h,
|
|||
/* log errors from discard, but ignore */
|
||||
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
|
||||
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -927,10 +932,8 @@ api_data_delete(clicon_handle h,
|
|||
clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
FCGX_SetExitStatus(204, r->out);
|
||||
FCGX_FPrintF(r->out, "Status: 204 No Content\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (restconf_reply_send(req, 204, NULL) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -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,18 +42,18 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int api_data_options(clicon_handle h, FCGX_Request *r);
|
||||
int api_data_put(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
int api_data_options(clicon_handle h, void *req);
|
||||
int api_data_put(clicon_handle h, void *req, char *api_path,
|
||||
cvec *pcvec, int pi,
|
||||
cvec *qvec, char *data,
|
||||
int pretty, restconf_media media_out);
|
||||
|
||||
int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
int api_data_patch(clicon_handle h, void *req, char *api_path,
|
||||
cvec *pcvec, int pi,
|
||||
cvec *qvec, char *data, int pretty,
|
||||
restconf_media media_out);
|
||||
|
||||
int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi,
|
||||
int api_data_delete(clicon_handle h, void *req, char *api_path, int pi,
|
||||
int pretty, restconf_media media_out);
|
||||
|
||||
#endif /* _RESTCONF_METHODS_H_ */
|
||||
|
|
|
|||
|
|
@ -57,21 +57,20 @@
|
|||
/* 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"
|
||||
#include "restconf_api.h"
|
||||
#include "restconf_err.h"
|
||||
#include "restconf_methods_get.h"
|
||||
|
||||
/*! Generic GET (both HEAD and GET)
|
||||
* According to restconf
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
* @param[in] head If 1 is HEAD, otherwise GET
|
||||
* @code
|
||||
|
|
@ -93,7 +92,7 @@
|
|||
*/
|
||||
static int
|
||||
api_data_get2(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *api_path,
|
||||
cvec *pcvec, /* XXX remove? */
|
||||
int pi,
|
||||
|
|
@ -148,7 +147,7 @@ api_data_get2(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -165,7 +164,7 @@ api_data_get2(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -187,7 +186,7 @@ api_data_get2(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -213,7 +212,7 @@ api_data_get2(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -226,7 +225,7 @@ api_data_get2(clicon_handle h,
|
|||
#endif
|
||||
/* Check if error return */
|
||||
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -234,9 +233,13 @@ api_data_get2(clicon_handle h,
|
|||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if (head){
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
/* Same headers as the GET, but no body */
|
||||
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 200, NULL) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (xpath==NULL || strcmp(xpath,"/")==0){ /* Special case: data root */
|
||||
|
|
@ -261,7 +264,7 @@ api_data_get2(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -275,7 +278,7 @@ api_data_get2(clicon_handle h,
|
|||
goto done;
|
||||
/* override invalid-value default 400 with 404 */
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 404) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 404) < 0)
|
||||
goto done;
|
||||
}
|
||||
goto ok;
|
||||
|
|
@ -309,12 +312,12 @@ api_data_get2(clicon_handle h,
|
|||
}
|
||||
}
|
||||
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 200, cbx) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -337,13 +340,13 @@ api_data_get2(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! REST HEAD method
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
*
|
||||
* The HEAD method is sent by the client to retrieve just the header fields
|
||||
|
|
@ -353,7 +356,7 @@ api_data_get2(clicon_handle h,
|
|||
*/
|
||||
int
|
||||
api_data_head(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *api_path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
|
|
@ -361,18 +364,18 @@ api_data_head(clicon_handle h,
|
|||
int pretty,
|
||||
restconf_media media_out)
|
||||
{
|
||||
return api_data_get2(h, r, api_path, pcvec, pi, qvec, pretty, media_out, 1);
|
||||
return api_data_get2(h, req, api_path, pcvec, pi, qvec, pretty, media_out, 1);
|
||||
}
|
||||
|
||||
/*! REST GET method
|
||||
* According to restconf
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
|
|
@ -392,7 +395,7 @@ api_data_head(clicon_handle h,
|
|||
*/
|
||||
int
|
||||
api_data_get(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *api_path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
|
|
@ -400,12 +403,12 @@ api_data_get(clicon_handle h,
|
|||
int pretty,
|
||||
restconf_media media_out)
|
||||
{
|
||||
return api_data_get2(h, r, api_path, pcvec, pi, qvec, pretty, media_out, 0);
|
||||
return api_data_get2(h, req, api_path, pcvec, pi, qvec, pretty, media_out, 0);
|
||||
}
|
||||
|
||||
/*! GET restconf/operations resource
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
|
|
@ -430,7 +433,7 @@ api_data_get(clicon_handle h,
|
|||
*/
|
||||
int
|
||||
api_operations_get(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *path,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
|
|
@ -505,11 +508,13 @@ api_operations_get(clicon_handle h,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
|
||||
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 200, cbx) < 0)
|
||||
goto done;
|
||||
// ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
@ -40,11 +41,11 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int api_data_head(clicon_handle h, FCGX_Request *r, char *api_path, cvec *pcvec, int pi,
|
||||
int api_data_head(clicon_handle h, void *req, char *api_path, cvec *pcvec, int pi,
|
||||
cvec *qvec, int pretty, restconf_media media_out);
|
||||
int api_data_get(clicon_handle h, FCGX_Request *r, char *api_path, cvec *pcvec, int pi,
|
||||
int api_data_get(clicon_handle h, void *req, char *api_path, cvec *pcvec, int pi,
|
||||
cvec *qvec, int pretty, restconf_media media_out);
|
||||
int api_operations_get(clicon_handle h, FCGX_Request *r,
|
||||
int api_operations_get(clicon_handle h, void *req,
|
||||
char *api_path, int pi, cvec *qvec, char *data,
|
||||
int pretty, restconf_media media_out);
|
||||
|
||||
|
|
|
|||
|
|
@ -59,21 +59,67 @@
|
|||
/* 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"
|
||||
#include "restconf_api.h"
|
||||
#include "restconf_err.h"
|
||||
#include "restconf_methods_post.h"
|
||||
|
||||
/*! Print location header from
|
||||
* @param[in] req Generic Www 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
|
||||
*/
|
||||
static int
|
||||
http_location_header(clicon_handle h,
|
||||
void *req,
|
||||
cxobj *xobj)
|
||||
{
|
||||
int retval = -1;
|
||||
char *https;
|
||||
char *host;
|
||||
char *request_uri;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
https = clixon_restconf_param_get(h, "HTTPS");
|
||||
host = clixon_restconf_param_get(h, "HTTP_HOST");
|
||||
request_uri = clixon_restconf_param_get(h, "REQUEST_URI");
|
||||
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;
|
||||
if (restconf_reply_header(req, "Location", "http%s://%s%s%s",
|
||||
https?"s":"",
|
||||
host,
|
||||
request_uri,
|
||||
cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (restconf_reply_header(req, "Location", "http%s://%s%s",
|
||||
https?"s":"",
|
||||
host,
|
||||
request_uri) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic REST POST method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
* restconf POST is mapped to edit-config create.
|
||||
* @see RFC8040 Sec 4.4.1
|
||||
|
|
@ -99,7 +145,7 @@
|
|||
*/
|
||||
int
|
||||
api_data_post(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *api_path,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
|
|
@ -153,7 +199,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -168,7 +214,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -198,7 +244,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -207,7 +253,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -220,7 +266,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -229,13 +275,13 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
restconf_unsupported_media(r);
|
||||
restconf_unsupported_media(req);
|
||||
goto ok;
|
||||
break;
|
||||
} /* switch media_in */
|
||||
|
|
@ -251,7 +297,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -284,7 +330,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
||||
|
|
@ -298,7 +344,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -332,7 +378,7 @@ api_data_post(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -354,7 +400,7 @@ api_data_post(clicon_handle h,
|
|||
/* log errors from discard, but ignore */
|
||||
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
|
||||
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) /* Use original xe */
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0) /* Use original xe */
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -376,14 +422,13 @@ api_data_post(clicon_handle h,
|
|||
goto done;
|
||||
/* If copy-config failed, log and ignore (already committed) */
|
||||
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
|
||||
|
||||
clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
FCGX_SetExitStatus(201, r->out);
|
||||
FCGX_FPrintF(r->out, "Status: 201 Created\r\n");
|
||||
http_location(h, r, xdata);
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (http_location_header(h, req, xdata) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 201, NULL) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -404,8 +449,8 @@ api_data_post(clicon_handle h,
|
|||
} /* api_data_post */
|
||||
|
||||
/*! Handle input data to api_operations_post
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] yspec Yang top-level specification
|
||||
* @param[in] yrpc Yang rpc spec
|
||||
|
|
@ -426,7 +471,7 @@ api_data_post(clicon_handle h,
|
|||
*/
|
||||
static int
|
||||
api_operations_post_input(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *data,
|
||||
yang_stmt *yspec,
|
||||
yang_stmt *yrpc,
|
||||
|
|
@ -462,17 +507,16 @@ api_operations_post_input(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (ret == 0){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_debug(1, "%s F", __FUNCTION__);
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -487,7 +531,7 @@ api_operations_post_input(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -496,17 +540,16 @@ api_operations_post_input(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
restconf_unsupported_media(r);
|
||||
restconf_unsupported_media(req);
|
||||
goto fail;
|
||||
break;
|
||||
} /* switch media_in */
|
||||
clicon_debug(1, "%s F", __FUNCTION__);
|
||||
xml_name_set(xdata, "data");
|
||||
/* Here xdata is:
|
||||
* <data><input xmlns="urn:example:clixon">...</input></data>
|
||||
|
|
@ -531,7 +574,7 @@ api_operations_post_input(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -560,8 +603,8 @@ api_operations_post_input(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Handle output data to api_operations_post
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] xret XML reply messages from backend/handler
|
||||
* @param[in] yspec Yang top-level specification
|
||||
* @param[in] youtput Yang rpc output specification
|
||||
|
|
@ -575,7 +618,7 @@ api_operations_post_input(clicon_handle h,
|
|||
*/
|
||||
static int
|
||||
api_operations_post_output(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
cxobj *xret,
|
||||
yang_stmt *yspec,
|
||||
yang_stmt *youtput,
|
||||
|
|
@ -605,7 +648,7 @@ api_operations_post_output(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -638,7 +681,7 @@ api_operations_post_output(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -659,9 +702,8 @@ api_operations_post_output(clicon_handle h,
|
|||
strcmp(xml_name(xok),"ok")==0);
|
||||
if (isempty) {
|
||||
/* Internal error - invalid output from rpc handler */
|
||||
FCGX_SetExitStatus(204, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Status: 204 No Content\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (restconf_reply_send(req, 204, NULL) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
/* Clear namespace of parameters */
|
||||
|
|
@ -687,12 +729,12 @@ api_operations_post_output(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! REST operation POST method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
* See RFC 8040 Sec 3.6 / 4.4.2
|
||||
* @note We map post to edit-config create.
|
||||
|
|
@ -717,7 +759,7 @@ api_operations_post_output(clicon_handle h,
|
|||
*/
|
||||
int
|
||||
api_operations_post(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
void *req,
|
||||
char *api_path,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
|
|
@ -766,7 +808,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -785,7 +827,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -796,7 +838,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -821,7 +863,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -834,7 +876,7 @@ api_operations_post(clicon_handle h,
|
|||
namespace = xml_find_type_value(xbot, NULL, "xmlns", CX_ATTR);
|
||||
clicon_debug(1, "%s : 4. Parse input data: %s", __FUNCTION__, data);
|
||||
if (data && strlen(data)){
|
||||
if ((ret = api_operations_post_input(h, r, data, yspec, yrpc, xbot,
|
||||
if ((ret = api_operations_post_input(h, req, data, yspec, yrpc, xbot,
|
||||
pretty, media_out)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
|
|
@ -854,7 +896,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto ok;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -866,7 +908,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto ok;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -884,14 +926,14 @@ api_operations_post(clicon_handle h,
|
|||
/* Look for local (client-side) restconf plugins.
|
||||
* -1:Error, 0:OK local, 1:OK backend
|
||||
*/
|
||||
if ((ret = rpc_callback_call(h, xbot, cbret, r)) < 0)
|
||||
if ((ret = rpc_callback_call(h, xbot, cbret, req)) < 0)
|
||||
goto done;
|
||||
if (ret > 0){ /* Handled locally */
|
||||
if (clixon_xml_parse_string(cbuf_get(cbret), YB_NONE, NULL, &xret, NULL) < 0)
|
||||
goto done;
|
||||
/* Local error: return it and quit */
|
||||
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -900,7 +942,7 @@ api_operations_post(clicon_handle h,
|
|||
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -913,16 +955,14 @@ api_operations_post(clicon_handle h,
|
|||
clicon_log_xml(LOG_DEBUG, xret, "%s Receive reply:", __FUNCTION__);
|
||||
#endif
|
||||
youtput = yang_find(yrpc, Y_OUTPUT, NULL);
|
||||
if ((ret = api_operations_post_output(h, r, xret, yspec, youtput, namespace,
|
||||
if ((ret = api_operations_post_output(h, req, xret, yspec, youtput, namespace,
|
||||
pretty, media_out, &xoutput)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto ok;
|
||||
/* xoutput should now look: <output xmlns="uri"><x>0</x></output> */
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
|
||||
goto done;
|
||||
cbuf_reset(cbret);
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
|
|
@ -938,8 +978,8 @@ api_operations_post(clicon_handle h,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
FCGX_FPrintF(r->out, "%s", cbuf_get(cbret));
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
if (restconf_reply_send(req, 200, cbret) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
@ -34,22 +35,19 @@
|
|||
* Restconf method implementation for post: operation(rpc) and data
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _RESTCONF_METHODS_POST_H_
|
||||
#define _RESTCONF_METHODS_POST_H_
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
int pi,
|
||||
cvec *qvec, char *data,
|
||||
int api_data_post(clicon_handle h, void *req, char *api_path,
|
||||
int pi, cvec *qvec, char *data,
|
||||
int pretty,
|
||||
restconf_media media_out);
|
||||
|
||||
int api_operations_post(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
int api_operations_post(clicon_handle h, void *req, char *api_path,
|
||||
int pi, cvec *qvec, char *data,
|
||||
int pretty, restconf_media media_out);
|
||||
|
||||
|
||||
#endif /* _RESTCONF_METHODS_POST_H_ */
|
||||
|
|
|
|||
|
|
@ -63,13 +63,13 @@
|
|||
/* restconf */
|
||||
#include "restconf_lib.h"
|
||||
#include "restconf_api.h"
|
||||
#include "restconf_err.h"
|
||||
#include "restconf_root.h"
|
||||
|
||||
|
||||
/*! Determine the root of the RESTCONF API
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||
* @param[in] cb Body buffer
|
||||
* @see RFC8040 3.1 and RFC7320
|
||||
* In line with the best practices defined by [RFC7320], RESTCONF
|
||||
* enables deployments to specify where the RESTCONF API is located.
|
||||
|
|
@ -92,9 +92,10 @@ api_well_known(clicon_handle h,
|
|||
restconf_method_notallowed(req, "GET");
|
||||
goto ok;
|
||||
}
|
||||
restconf_reply_status_code(req, 200); /* OK */
|
||||
restconf_reply_header_add(req, "Cache-Control", "no-cache");
|
||||
restconf_reply_header_add(req, "Content-Type", "application/xrd+xml");
|
||||
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Content-Type", "application/xrd+xml") < 0)
|
||||
goto done;
|
||||
/* Create body */
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
|
|
@ -105,8 +106,9 @@ api_well_known(clicon_handle h,
|
|||
cprintf(cb, "</XRD>\r\n");
|
||||
|
||||
/* Must be after body */
|
||||
restconf_reply_header_add(req, "Content-Length", "%d", cbuf_len(cb));
|
||||
if (restconf_reply_send(req, cb) < 0)
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 200, cb) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -116,3 +118,461 @@ api_well_known(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Retrieve the Top-Level API Resource
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @note Only returns null for operations and data,...
|
||||
* See RFC8040 3.3
|
||||
* XXX doesnt check method
|
||||
*/
|
||||
static int
|
||||
api_root(clicon_handle h,
|
||||
void *req,
|
||||
char *request_method,
|
||||
int pretty,
|
||||
restconf_media media_out)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
cxobj *xt = NULL;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (strcmp(request_method, "GET") != 0){
|
||||
restconf_method_notallowed(req, "GET");
|
||||
goto ok;
|
||||
}
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
}
|
||||
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
|
||||
goto done;
|
||||
|
||||
if (clixon_xml_parse_string("<restconf xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><data/>"
|
||||
"<operations/><yang-library-version>2016-06-21</yang-library-version></restconf>",
|
||||
YB_MODULE, yspec, &xt, NULL) < 0)
|
||||
goto done;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (xml2json_cbuf(cb, xt, pretty) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 200, cb) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
* See https://tools.ietf.org/html/rfc7895
|
||||
*/
|
||||
static int
|
||||
api_yang_library_version(clicon_handle h,
|
||||
void *req,
|
||||
int pretty,
|
||||
restconf_media media_out)
|
||||
|
||||
{
|
||||
#if 1
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
#else
|
||||
int retval = -1;
|
||||
cxobj *xt = NULL;
|
||||
cbuf *cb = NULL;
|
||||
char *ietf_yang_library_revision = "2016-06-21"; /* XXX */
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (clixon_xml_parse_va(YB_NONE, NULL, &xt, NULL,
|
||||
"<yang-library-version>%s</yang-library-version>",
|
||||
ietf_yang_library_revision) < 0)
|
||||
goto done;
|
||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
goto done;
|
||||
}
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (xml2json_cbuf(cb, xt, pretty) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
clicon_debug(1, "%s cb%s", __FUNCTION__, cbuf_get(cb));
|
||||
FCGX_FPrintF(r->out, "%s\n", cb?cbuf_get(cb):"");
|
||||
FCGX_FPrintF(r->out, "\n\n");
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
return retval;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*! Generic REST method, GET, PUT, DELETE, etc
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_in Input media
|
||||
* @param[in] media_out Output media
|
||||
*/
|
||||
static int
|
||||
api_data(clicon_handle h,
|
||||
void *req,
|
||||
char *api_path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
restconf_media media_out)
|
||||
{
|
||||
#if 1
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
return 0;
|
||||
#else
|
||||
int retval = -1;
|
||||
char *request_method;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
request_method = clixon_restconf_param_get(h, "REQUEST_METHOD");
|
||||
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||
if (strcmp(request_method, "OPTIONS")==0)
|
||||
retval = api_data_options(h, req);
|
||||
else if (strcmp(request_method, "HEAD")==0)
|
||||
retval = api_data_head(h, req, api_path, pcvec, pi, qvec, pretty, media_out);
|
||||
else if (strcmp(request_method, "GET")==0)
|
||||
retval = api_data_get(h, req, api_path, pcvec, pi, qvec, pretty, media_out);
|
||||
else if (strcmp(request_method, "POST")==0)
|
||||
retval = api_data_post(h, req, api_path, pi, qvec, data, pretty, media_out);
|
||||
else if (strcmp(request_method, "PUT")==0)
|
||||
retval = api_data_put(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out);
|
||||
else if (strcmp(request_method, "PATCH")==0)
|
||||
retval = api_data_patch(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out);
|
||||
else if (strcmp(request_method, "DELETE")==0)
|
||||
retval = api_data_delete(h, req, api_path, pi, pretty, media_out);
|
||||
else
|
||||
retval = restconf_notfound(h, req);
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*! Move back to restconf_methods_get
|
||||
*/
|
||||
static int
|
||||
api_operations_get(clicon_handle h,
|
||||
void *req,
|
||||
char *path,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *ymod; /* yang module */
|
||||
yang_stmt *yc;
|
||||
char *namespace;
|
||||
cbuf *cb = NULL;
|
||||
cxobj *xt = NULL;
|
||||
int i;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
cprintf(cb, "<operations>");
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (pretty)
|
||||
cprintf(cb, "{\"operations\": {\n");
|
||||
else
|
||||
cprintf(cb, "{\"operations\":{");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ymod = NULL;
|
||||
i = 0;
|
||||
while ((ymod = yn_each(yspec, ymod)) != NULL) {
|
||||
namespace = yang_find_mynamespace(ymod);
|
||||
yc = NULL;
|
||||
while ((yc = yn_each(ymod, yc)) != NULL) {
|
||||
if (yang_keyword_get(yc) != Y_RPC)
|
||||
continue;
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
cprintf(cb, "<%s xmlns=\"%s\"/>", yang_argument_get(yc), namespace);
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (i++){
|
||||
cprintf(cb, ",");
|
||||
if (pretty)
|
||||
cprintf(cb, "\n\t");
|
||||
}
|
||||
if (pretty)
|
||||
cprintf(cb, "\"%s:%s\": [null]", yang_argument_get(ymod), yang_argument_get(yc));
|
||||
else
|
||||
cprintf(cb, "\"%s:%s\":[null]", yang_argument_get(ymod), yang_argument_get(yc));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
cprintf(cb, "</operations>");
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (pretty)
|
||||
cprintf(cb, "}\n}");
|
||||
else
|
||||
cprintf(cb, "}}");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
|
||||
goto done;
|
||||
if (restconf_reply_send(req, 200, cb) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Operations REST method, POST
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||
* @param[in] request_method eg GET,...
|
||||
* @param[in] path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] media_out Output media
|
||||
*/
|
||||
static int
|
||||
api_operations(clicon_handle h,
|
||||
void *req,
|
||||
char *request_method,
|
||||
char *path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (strcmp(request_method, "GET")==0)
|
||||
retval = api_operations_get(h, req, path, pi, qvec, data, pretty, media_out);
|
||||
#ifdef NYI
|
||||
else if (strcmp(request_method, "POST")==0)
|
||||
retval = api_operations_post(h, req, path, pi, qvec, data,
|
||||
pretty, media_out);
|
||||
#endif
|
||||
else
|
||||
retval = restconf_notfound(h, req);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Process a /restconf root input, this is the root of the restconf processing
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||
* @param[in] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
|
||||
*/
|
||||
int
|
||||
api_root_restconf(clicon_handle h,
|
||||
void *req,
|
||||
cvec *qvec)
|
||||
{
|
||||
int retval = -1;
|
||||
char *request_method = NULL; /* GET,.. */
|
||||
char *api_resource = NULL; /* RFC8040 3.3: eg data/operations */
|
||||
char *path;
|
||||
char **pvec = NULL;
|
||||
cvec *pcvec = NULL; /* for rest api */
|
||||
int pn;
|
||||
int pretty;
|
||||
cbuf *cb = NULL;
|
||||
char *media_str = NULL;
|
||||
restconf_media media_out = YANG_DATA_JSON;
|
||||
char *indata = NULL;
|
||||
int authenticated = 0;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (req == NULL){
|
||||
errno = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
request_method = clixon_restconf_param_get(h, "REQUEST_METHOD");
|
||||
path = restconf_uripath(h);
|
||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||
/* Get media for output (proactive negotiation) RFC7231 by using
|
||||
* Accept:. This is for methods that have output, such as GET,
|
||||
* operation POST, etc
|
||||
* If accept is * default is yang-json
|
||||
*/
|
||||
if ((media_str = clixon_restconf_param_get(h, "HTTP_ACCEPT")) == NULL){
|
||||
// retval = restconf_unsupported_media(r);
|
||||
// goto done;
|
||||
}
|
||||
else if ((int)(media_out = restconf_media_str2int(media_str)) == -1){
|
||||
if (strcmp(media_str, "*/*") == 0) /* catch-all */
|
||||
media_out = YANG_DATA_JSON;
|
||||
else{
|
||||
retval = restconf_unsupported_media(req);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
clicon_debug(1, "%s ACCEPT: %s %s", __FUNCTION__, media_str, restconf_media_int2str(media_out));
|
||||
|
||||
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||
goto done;
|
||||
/* Sanity check of path. Should be /restconf/ */
|
||||
if (pn < 2){
|
||||
restconf_notfound(h, req);
|
||||
goto ok;
|
||||
}
|
||||
if (strlen(pvec[0]) != 0){
|
||||
retval = restconf_notfound(h, req);
|
||||
goto done;
|
||||
}
|
||||
if (strcmp(pvec[1], RESTCONF_API)){
|
||||
retval = restconf_notfound(h, req);
|
||||
goto done;
|
||||
}
|
||||
if (pn == 2){
|
||||
retval = api_root(h, req, request_method, pretty, media_out);
|
||||
goto done;
|
||||
}
|
||||
if ((api_resource = pvec[2]) == NULL){
|
||||
retval = restconf_notfound(h, req);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "%s: api_resource=%s", __FUNCTION__, api_resource);
|
||||
if (str2cvec(path, '/', '=', &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
||||
goto done;
|
||||
/* data */
|
||||
if ((cb = restconf_get_indata(req)) == NULL) /* XXX NYI ACTUALLY not always needed, do this later? */
|
||||
goto done;
|
||||
indata = cbuf_get(cb);
|
||||
clicon_debug(1, "%s DATA=%s", __FUNCTION__, indata);
|
||||
|
||||
/* If present, check credentials. See "plugin_credentials" in plugin
|
||||
* See RFC 8040 section 2.5
|
||||
*/
|
||||
if ((authenticated = clixon_plugin_auth_all(h, req)) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||
|
||||
/* If set but no user, set a dummy user */
|
||||
if (authenticated){
|
||||
if (clicon_username_get(h) == NULL)
|
||||
clicon_username_set(h, "none");
|
||||
}
|
||||
else{
|
||||
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
goto ok;
|
||||
}
|
||||
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||
if (strcmp(api_resource, "yang-library-version")==0){
|
||||
if (api_yang_library_version(h, req, pretty, media_out) < 0) /* XXX NYI */
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(api_resource, "data") == 0){ /* restconf, skip /api/data */ /* XXX NYI */
|
||||
if (api_data(h, req, path, pcvec, 2, qvec, indata,
|
||||
pretty, media_out) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(api_resource, "operations") == 0){ /* rpc */
|
||||
if (api_operations(h, req, request_method, path, pcvec, 2, qvec, indata,
|
||||
pretty, media_out) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
restconf_notfound(h, req);
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (pcvec)
|
||||
cvec_free(pcvec);
|
||||
if (pvec)
|
||||
free(pvec);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,5 +53,6 @@
|
|||
* Prototypes
|
||||
*/
|
||||
int api_well_known(clicon_handle h, void *req);
|
||||
int api_root_restconf(clicon_handle h, void *req, cvec *qvec);
|
||||
|
||||
#endif /* _RESTCONF_ROOT_H_ */
|
||||
|
|
|
|||
|
|
@ -86,7 +86,8 @@
|
|||
#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_api.h"
|
||||
#include "restconf_err.h"
|
||||
#include "restconf_stream.h"
|
||||
|
||||
/*
|
||||
|
|
@ -377,7 +378,6 @@ api_stream(clicon_handle h,
|
|||
path = restconf_uripath(h);
|
||||
query = clixon_restconf_param_get(h, "QUERY_STRING");
|
||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||
restconf_test(r, 1);
|
||||
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||
goto done;
|
||||
/* Sanity check of path. Should be /stream/<name> */
|
||||
|
|
@ -404,7 +404,7 @@ api_stream(clicon_handle h,
|
|||
if (str2cvec(path, '/', '=', &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
||||
goto done;
|
||||
/* data */
|
||||
if ((cb = readdata(r)) == NULL)
|
||||
if ((cb = restconf_get_indata(r)) == NULL)
|
||||
goto done;
|
||||
data = cbuf_get(cb);
|
||||
clicon_debug(1, "%s DATA=%s", __FUNCTION__, data);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue