* HTTP/1 native parser as part of the RESTCONF client

* Fixed memory error in opendir/readdir in clicon_file_dirent
* Remove MAXPATH in parsers
* New string-del function
This commit is contained in:
Olof hagsand 2022-01-26 13:48:20 +01:00
parent 0ed34b4fab
commit dadf4a778a
53 changed files with 1061 additions and 1273 deletions

View file

@ -36,6 +36,12 @@
## 5.6.0 ## 5.6.0
Expected: March 2022 Expected: March 2022
### New features
* HTTP/1 native parser as part of the RESTCONF client
* Replaced libevhtp/libevent2 with internal http1 parser
* Replace configure option `--disable-evhtp` with `--disable-http1` for disabling HTTP/1 whihc is on by default
### API changes on existing protocol/config features ### API changes on existing protocol/config features
Users may have to change how they access the system Users may have to change how they access the system

View file

@ -45,7 +45,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>

View file

@ -44,7 +44,6 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <inttypes.h> #include <inttypes.h>
#include <dirent.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <assert.h> #include <assert.h>

View file

@ -46,7 +46,6 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <inttypes.h> #include <inttypes.h>
#include <dirent.h>
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
#include <sys/types.h> #include <sys/types.h>

View file

@ -49,7 +49,6 @@
#include <errno.h> #include <errno.h>
#define __USE_GNU /* For RTLD_DEFAULT */ #define __USE_GNU /* For RTLD_DEFAULT */
#include <dlfcn.h> #include <dlfcn.h>
#include <dirent.h>
#include <libgen.h> #include <libgen.h>
#include <grp.h> #include <grp.h>
#include <signal.h> #include <signal.h>

View file

@ -100,10 +100,10 @@ APPSRC += restconf_methods_post.c
APPSRC += restconf_methods_get.c APPSRC += restconf_methods_get.c
APPSRC += restconf_methods_patch.c APPSRC += restconf_methods_patch.c
APPSRC += restconf_root.c APPSRC += restconf_root.c
APPSRC += clixon_http1.c
APPSRC += restconf_main_$(with_restconf).c APPSRC += restconf_main_$(with_restconf).c
ifeq ($(with_restconf),native) ifeq ($(with_restconf),native)
APPSRC += restconf_native.c APPSRC += restconf_native.c
APPSRC += restconf_evhtp.c # HTTP/1
APPSRC += restconf_nghttp2.c # HTTP/2 APPSRC += restconf_nghttp2.c # HTTP/2
endif endif
@ -113,7 +113,10 @@ ifeq ($(with_restconf),fcgi)
APPSRC += restconf_stream_$(with_restconf).c APPSRC += restconf_stream_$(with_restconf).c
endif endif
APPOBJ = $(APPSRC:.c=.o) # internal http/1 parser
YACCOBJS = lex.clixon_http1_parse.o clixon_http1_parse.tab.o
APPOBJ = $(APPSRC:.c=.o) $(YACCOBJS)
# Accessible from plugin # Accessible from plugin
# XXX actually this does not work properly, there are functions in lib # XXX actually this does not work properly, there are functions in lib
@ -125,6 +128,8 @@ LIBSRC += restconf_api_$(with_restconf).c
LIBOBJ = $(LIBSRC:.c=.o) LIBOBJ = $(LIBSRC:.c=.o)
# This lib is very small but used for clixon restconf applications to access clixon restconf lib # This lib is very small but used for clixon restconf applications to access clixon restconf lib
# functions. Mostly for future use # functions. Mostly for future use
MYNAME = clixon_restconf MYNAME = clixon_restconf
@ -147,6 +152,7 @@ $(top_srcdir)/lib/src/$(CLIXON_LIB):
clean: clean:
rm -f $(LIBOBJ) *.core $(APPL) $(APPOBJ) *.o $(MYLIBDYNAMIC) $(MYLIBSTATIC) $(MYLIBSO) $(MYLIBLINK) # extra .o to clean residue if with_restconf changes rm -f $(LIBOBJ) *.core $(APPL) $(APPOBJ) *.o $(MYLIBDYNAMIC) $(MYLIBSTATIC) $(MYLIBSO) $(MYLIBLINK) # extra .o to clean residue if with_restconf changes
rm -f *.gcda *.gcno *.gcov # coverage rm -f *.gcda *.gcno *.gcov # coverage
rm -f clixon_http1_parse.tab.[ch] clixon_http1_parse.[co] lex.clixon_http1_parse.c
distclean: clean distclean: clean
rm -f Makefile *~ .depend rm -f Makefile *~ .depend
@ -191,6 +197,19 @@ uninstall:
.c.o: .c.o:
$(CC) $(INCLUDES) -D__PROGRAM__=\"clixon_restconf\" $(CPPFLAGS) $(CFLAGS) -c $< $(CC) $(INCLUDES) -D__PROGRAM__=\"clixon_restconf\" $(CPPFLAGS) $(CFLAGS) -c $<
# http1 parser
lex.clixon_http1_parse.c : clixon_http1_parse.l clixon_http1_parse.tab.h
$(LEX) -Pclixon_http1_parse clixon_http1_parse.l # -d is debug
clixon_http1_parse.tab.h: clixon_http1_parse.y
$(YACC) -l -d -b clixon_http1_parse -p clixon_http1_parse clixon_http1_parse.y # -t is debug
# extra rule to avoid parallell yaccs
clixon_http1_parse.tab.c: clixon_http1_parse.tab.h
lex.clixon_http1_parse.o : lex.clixon_http1_parse.c clixon_http1_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
ifeq ($(LINKAGE),dynamic) ifeq ($(LINKAGE),dynamic)
$(APPL): $(MYLIBDYNAMIC) $(APPL): $(MYLIBDYNAMIC)
else else

View file

@ -6,19 +6,10 @@
* [Nchan Streams](#nchan) * [Nchan Streams](#nchan)
* [Debugging](#debugging) * [Debugging](#debugging)
There are two installation instructions: for libevhtp and nginx. There are two installation instructions: for native and nginx.
## Native ## Native
Download, build and install libevhtp from source. Prereqs: libevent and ssl
```
sudo git clone https://github.com/clicon/clixon-libevhtp.git
cd clixon-libevhtp
./configure --libdir=/usr/lib
make
sudo make install
```
Configure clixon with native restconf: Configure clixon with native restconf:
``` ```
./configure --with-restconf=native ./configure --with-restconf=native

View file

@ -0,0 +1,248 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2020-2022 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 *****
* HTTP/1.1 parser according to RFC 7230
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <inttypes.h>
#include <syslog.h>
#include <errno.h>
#include <openssl/ssl.h>
#ifdef HAVE_LIBNGHTTP2
#include <nghttp2/nghttp2.h>
#endif
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include <clixon/clixon.h>
#include "restconf_lib.h"
#include "restconf_native.h"
#include "clixon_http1_parse.h"
/* Size of xml read buffer */
#define BUFLEN 1024
static int
_http1_parse(clicon_handle h,
restconf_conn *rc,
char *str,
const char *filename)
{
int retval = -1;
clixon_http1_yacc hy = {0,};
char *ptr;
size_t sz;
clicon_debug(2, "%s", __FUNCTION__);
if (strlen(str) == 0)
goto ok;
hy.hy_parse_string = str;
hy.hy_name = filename;
hy.hy_h = h;
hy.hy_rc = rc;
hy.hy_linenum = 1;
if (http1_scan_init(&hy) < 0)
goto done;
if (http1_parse_init(&hy) < 0)
goto done;
ptr = clixon_http1_parsetext;
if (clixon_http1_parseparse(&hy) != 0) { /* yacc returns 1 on error */
if (filename)
clicon_log(LOG_NOTICE, "HTTP1 error: on line %d in %s", hy.hy_linenum, filename);
else
clicon_log(LOG_NOTICE, "HTTP1 error: on line %d", hy.hy_linenum);
if (clicon_errno == 0)
clicon_err(OE_RESTCONF, 0, "HTTP1 parser error with no error code (should not happen)");
goto done;
}
if (0){
sz = (clixon_http1_parsetext - ptr) + strlen(clixon_http1_parsetext);
fprintf(stderr,"%s %p diff:%ld %ld\n", __FUNCTION__,
clixon_http1_parsetext,
sz,
strlen(ptr)
);
}
http1_parse_exit(&hy);
http1_scan_exit(&hy);
ok:
retval = 0;
done:
return retval;
}
/*! Read an XML definition from file and parse it into a parse-tree, advanced API
*
* @param[in] fd A file descriptor containing the XML file (as ASCII characters)
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] yspec Yang specification (only if bind is TOP or CONFIG)
* @param[in,out] xt Pointer to XML parse tree. If empty, create.
* @param[out] xerr Pointer to XML error tree, if retval is 0
* @retval 1 Parse OK and all yang assignment made
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
* @retval -1 Error with clicon_err called. Includes parse error
*
* @code
* cxobj *xt = NULL;
* cxobj *xerr = NULL;
* FILE *f;
* if ((f = fopen(filename, "r")) == NULL)
* err;
* if ((ret = clixon_xml_parse_file(f, YB_MODULE, yspec, &xt, &xerr)) < 0)
* err;
* xml_free(xt);
* @endcode
* @see clixon_xml_parse_string
* @see clixon_json_parse_file
* @note, If xt empty, a top-level symbol will be added so that <tree../> will be: <top><tree.../></tree></top>
* @note May block on file I/O
*/
int
clixon_http1_parse_file(clicon_handle h,
restconf_conn *rc,
FILE *f,
const char *filename)
{
int retval = -1;
int ret;
char ch;
char *buf = NULL;
char *ptr;
int buflen = BUFLEN; /* start size */
int len = 0;
int oldbuflen;
clicon_debug(1, "%s %s", __FUNCTION__, filename);
if (f == NULL){
clicon_err(OE_RESTCONF, EINVAL, "f is NULL");
goto done;
}
if ((buf = malloc(buflen)) == NULL){
clicon_err(OE_XML, errno, "malloc");
goto done;
}
memset(buf, 0, buflen);
ptr = buf;
while (1){
if ((ret = fread(&ch, 1, 1, f)) < 0){
clicon_err(OE_XML, errno, "read");
break;
}
if (ret != 0){
buf[len++] = ch;
}
if (ret == 0) { /* buffer read */
if (_http1_parse(h, rc, ptr, filename) < 0)
goto done;
break;
}
if (len >= buflen-1){ /* Space: one for the null character */
oldbuflen = buflen;
buflen *= 2;
if ((buf = realloc(buf, buflen)) == NULL){
clicon_err(OE_XML, errno, "realloc");
goto done;
}
memset(buf+oldbuflen, 0, buflen-oldbuflen);
ptr = buf;
}
} /* while */
retval = 0;
done:
if (buf)
free(buf);
return retval;
}
int
clixon_http1_parse_string(clicon_handle h,
restconf_conn *rc,
char *str)
{
return _http1_parse(h, rc, str, "http1-parse");
}
/*! Convert buffer to null-terminated string
* I dont know how to do this without copying, OR
* input flex with a non-null terminated string
*/
int
clixon_http1_parse_buf(clicon_handle h,
restconf_conn *rc,
char *buf,
size_t n)
{
char *str = NULL;
if ((str = malloc(n+1)) == NULL){
clicon_err(OE_RESTCONF, errno, "malloc");
return -1;
}
memcpy(str, buf, n);
str[n] = '\0';
return _http1_parse(h, rc, str, "http1-parse");
}
/*!
* @param[in] h Clixon handle
* @param[in] rc Clixon request connect pointer
*/
int
restconf_http1_path_root(clicon_handle h,
restconf_conn *rc)
{
int retval = -1;
restconf_stream_data *sd;
clicon_debug(1, "------------");
if ((sd = restconf_stream_find(rc, 0)) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "No stream_data");
goto done;
}
retval = 0;
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
return retval;
}

View file

@ -2,7 +2,6 @@
* *
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate) Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON. This file is part of CLIXON.
@ -31,17 +30,18 @@
the terms of any one of the Apache License version 2 or the GPL. the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
*
* Virtual clixon restconf API functions.
*/
#ifndef _RESTCONF_EVHTP_H_ * HTTP/1.1 parser according to RFC 7230
#define _RESTCONF_EVHTP_H_ */
#ifndef _CLIXON_HTTP1_H_
#define _CLIXON_HTTP1_H_
/* /*
* Prototypes * Prototypes
*/ */
void restconf_path_root(evhtp_request_t *req, void *arg); int clixon_http1_parse_file(clicon_handle h, restconf_conn *rc, FILE *f, const char *filename);
void restconf_path_wellknown(evhtp_request_t *req, void *arg); int clixon_http1_parse_string(clicon_handle h, restconf_conn *rc, char *str);
int clixon_http1_parse_buf(clicon_handle h, restconf_conn *rc, char *buf, size_t n);
int restconf_http1_path_root(clicon_handle h, restconf_conn *rc);
#endif /* _RESTCONF_EVHTP_H_ */ #endif /* _CLIXON_HTTP1_H_ */

View file

@ -0,0 +1,71 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2020-2022 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 *****
* HTTP/1.1 parser according to RFC 7230
*/
#ifndef _CLIXON_HTTP1_PARSE_H_
#define _CLIXON_HTTP1_PARSE_H_
/*
* Types
*/
struct clixon_http1_yacc {
const char *hy_name; /* Name of syntax (for error string) */
clicon_handle hy_h; /* Clixon handle */
restconf_conn *hy_rc; /* Connection handle */
int hy_linenum; /* Number of \n in parsed buffer */
char *hy_parse_string; /* original (copy of) parse string */
void *hy_lexbuf; /* internal parse buffer from lex */
void *hy_top;
};
typedef struct clixon_http1_yacc clixon_http1_yacc;
/*
* Variables
*/
extern char *clixon_http1_parsetext;
/*
* Prototypes
*/
int http1_scan_init(clixon_http1_yacc *);
int http1_scan_exit(clixon_http1_yacc *);
int http1_parse_init(clixon_http1_yacc *);
int http1_parse_exit(clixon_http1_yacc *);
int clixon_http1_parselex(void *);
int clixon_http1_parseparse(void *);
void clixon_http1_parseerror(void *, char*);
#endif /* _CLIXON_HTTP1_PARSE_H_ */

View file

@ -0,0 +1,164 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2020-2022 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 *****
*
* HTTP/1.1 parser according to RFC 7230
*
The following core rules are included by reference, as defined in
[RFC5234], Appendix B.1: ALPHA (letters), CR (carriage return), CRLF
(CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote),
HEXDIG (hexadecimal 0-9/A-F/a-f), HTAB (horizontal tab), LF (line
feed), OCTET (any 8-bit sequence of data), SP (space), and VCHAR (any
visible [USASCII] character).
*/
%{
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <openssl/ssl.h>
#ifdef HAVE_LIBNGHTTP2
#include <nghttp2/nghttp2.h>
#endif
#include "clixon_http1_parse.tab.h" /* generated */
#include <cligen/cligen.h>
#include <clixon/clixon.h>
#include "restconf_lib.h"
#include "restconf_native.h"
#include "clixon_http1_parse.h"
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
#define YY_DECL int clixon_http1_parselex(void *_hy)
/* Dont use input function (use user-buffer) */
#define YY_NO_INPUT
/* typecast macro */
#define _HY ((clixon_http1_yacc *)_hy)
#undef clixon_api_path_parsewrap
int
clixon_http1_parsewrap(void)
{
return 1;
}
%}
tchar [!#$%&'*+\-\.^_`|~0-9A-Za-z]
token {tchar}{tchar}*
pchar [A-Za-z0-9\-\._~!$&'()*+,;=:@]|%[0-9a-fA-F][0-9a-fA-F]
query [A-Za-z0-9\-\._~!$&'()*+,;=:@?/]|%[0-9a-fA-F][0-9a-fA-F]
%x REQLINE
%x REQTARG
%x REQUERY
%x REQHTTP
%x FLDNAME
%x FLDVALUE
%%
<REQLINE,REQTARG,REQUERY,REQHTTP,FLDNAME,FLDVALUE><<EOF>> { return X_EOF; }
<REQLINE>[ ] { BEGIN(REQTARG); return SP; }
<REQLINE>{token} { clixon_http1_parselval.string = yytext;
return TOKEN; }
<REQLINE>. { clixon_http1_parseerror(_HY, "LEXICAL ERROR\n"); return -1; }
<REQTARG>\? { BEGIN(REQUERY); return QMARK; }
<REQTARG>\/ { return SLASH; }
<REQTARG>[ ] { return SP; }
<REQTARG>HTTP { BEGIN(REQHTTP); return HTTP; }
<REQUERY>{pchar}+ { clixon_http1_parselval.string = yytext;
return PCHARS; }
<REQTARG>. { clixon_http1_parseerror(_HY, "LEXICAL ERROR\n"); return -1; }
<REQUERY>\/ { return SLASH; }
<REQUERY>[ ] { return SP; }
<REQUERY>HTTP { BEGIN(REQHTTP); return HTTP; }
<REQUERY>{query}+ { clixon_http1_parselval.string = yytext;
return QUERY; }
<REQUERY>. { clixon_http1_parseerror(_HY, "LEXICAL ERROR\n"); return -1; }
<REQHTTP>\r\n { BEGIN(FLDNAME); return CRLF; _HY->hy_linenum++; }
<REQHTTP>\/ { return SLASH; }
<REQHTTP>\. { return DOT; }
<REQHTTP>[0-9] { clixon_http1_parselval.intval = atoi(yytext);
return DIGIT; }
<REQHTTP>. { clixon_http1_parseerror(_HY, "LEXICAL ERROR\n"); return -1; }
<FLDNAME>: { BEGIN(FLDVALUE); return COLON; }
<FLDNAME>\r\n { return CRLF; _HY->hy_linenum++; }
<FLDNAME>[ \t\n]+ { return RWS; }
<FLDNAME>{token} { clixon_http1_parselval.string = yytext;
return TOKEN; }
<FLDNAME>. { clixon_http1_parseerror(_HY, "LEXICAL ERROR\n"); return -1; }
<FLDVALUE>\r\n { BEGIN(FLDNAME); return CRLF; _HY->hy_linenum++; }
<FLDVALUE>[ \t\n]+ { return RWS; }
<FLDVALUE>. { clixon_http1_parselval.string = yytext;
return VCHAR; }
%%
/*! Initialize scanner.
*/
int
http1_scan_init(clixon_http1_yacc *hy)
{
BEGIN(REQLINE);
hy->hy_lexbuf = yy_scan_string(hy->hy_parse_string);
#if 1 /* XXX: just to use unput to avoid warning */
if (0)
yyunput(0, "");
#endif
return 0;
}
/*
* free buffers
* Even within Flex version 2.5 (this is assumed), freeing buffers is different.
*/
int
http1_scan_exit(clixon_http1_yacc *hy)
{
yy_delete_buffer(hy->hy_lexbuf);
clixon_http1_parselex_destroy(); /* modern */
return 0;
}

View file

@ -0,0 +1,289 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2020-2022 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 *****
* HTTP/1.1 parser according to RFC 7230 Appendix B
*/
%start http_message
/* Must be here to define YYSTYPE */
%union {
char *string;
int intval;
}
%token SP
%token CRLF
%token RWS
%token SLASH
%token QMARK
%token DOT
%token HTTP
%token COLON
%token X_EOF
%token <string> PCHARS
%token <string> QUERY
%token <string> TOKEN
%token <string> VCHAR
%token <intval> DIGIT
%type <string> absolute_paths
%type <string> absolute_path
%lex-param {void *_hy} /* Add this argument to parse() and lex() function */
%parse-param {void *_hy}
%{
/* Here starts user C-code */
/* typecast macro */
#define _HY ((clixon_http1_yacc *)_hy)
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_http1_parsetext, _HY->hy_linenum); YYERROR;}
/* add _yy to error parameters */
#define YY_(msgid) msgid
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <openssl/ssl.h>
#ifdef HAVE_LIBNGHTTP2
#include <nghttp2/nghttp2.h>
#endif
#include <cligen/cligen.h>
#include <clixon/clixon.h>
#include "restconf_lib.h"
#include "restconf_handle.h"
#include "restconf_native.h"
#include "clixon_http1_parse.h"
/* Best debugging is to enable PARSE_DEBUG below and add -d to the LEX compile statement in the Makefile
* And then run the testcase with -D 1
* Disable it to stop any calls to clicon_debug. Having it on by default would mean very large debug outputs.
*/
#if 0
#define _PARSE_DEBUG(s) clicon_debug(1,(s))
#define _PARSE_DEBUG1(s, s1) clicon_debug(1,(s), (s1))
#else
#define _PARSE_DEBUG(s)
#define _PARSE_DEBUG1(s, s1)
#endif
/*
also called from yacc generated code *
*/
void
clixon_http1_parseerror(void *_hy,
char *s)
{
clicon_err(OE_RESTCONF, 0, "%s on line %d: %s at or before: '%s'",
_HY->hy_name,
_HY->hy_linenum ,
s,
clixon_http1_parsetext);
return;
}
int
http1_parse_init(clixon_http1_yacc *hy)
{
return 0;
}
int
http1_parse_exit(clixon_http1_yacc *hy)
{
return 0;
}
static int
http1_parse_query(clixon_http1_yacc *hy,
char *query)
{
int retval = -1;
restconf_stream_data *sd = NULL;
if ((sd = restconf_stream_find(hy->hy_rc, 0)) == NULL)
goto ok;
if (uri_str2cvec(query, '&', '=', 1, &sd->sd_qvec) < 0)
goto done;
ok:
retval = 0;
done:
return retval;
}
%}
%%
/* start-line *( header-field CRLF ) CRLF [ message-body ]
* start-line = request-line / status-line (only request-line here, ignore status-line)
*/
http_message : request_line header_fields CRLF
{ _HY->hy_top=NULL; _PARSE_DEBUG("http-message -> request-line header-fields ACCEPT"); YYACCEPT; }
;
/* request-line = method SP request-target SP HTTP-version CRLF */
request_line : method SP request_target SP HTTP_version CRLF
{
_PARSE_DEBUG("request-line -> method request-target HTTP_version CRLF");
}
;
/*
The request methods defined by this specification can be found in
Section 4 of [RFC7231], along with information regarding the HTTP
http://www.iana.org/assignments/http-methods/http-methods.xhtml
*/
method : TOKEN
{
if (restconf_param_set(_HY->hy_h, "REQUEST_METHOD", $1) < 0)
YYABORT;
_PARSE_DEBUG("method -> TOKEN");
}
;
/* request-target = origin-form / absolute-form / authority-form / asterisk-form *
* origin-form = absolute-path [ "?" query ] */
request_target : absolute_paths
{
if (restconf_param_set(_HY->hy_h, "REQUEST_URI", $1) < 0)
YYABORT;
_PARSE_DEBUG("request-target -> absolute-paths");
}
| absolute_paths QMARK QUERY
{
if (restconf_param_set(_HY->hy_h, "REQUEST_URI", $1) < 0)
YYABORT;
if (http1_parse_query(_HY->hy_h, $3) < 0)
YYABORT;
_PARSE_DEBUG("request-target -> absolute-paths ? query");
}
;
/* query = <query, see [RFC3986], Section 3.4>
* query = *( pchar / "/" / "?" )
*/
/*
query : query query1 { _PARSE_DEBUG("query -> query1"); }
| { _PARSE_DEBUG("query -> "); }
;
query1 : PCHARS { _PARSE_DEBUG("query1 -> PCHARS"); }
| SLASH { _PARSE_DEBUG("query1 -> /"); }
| QMARK { _PARSE_DEBUG("query1 -> ?"); }
;
*/
/* absolute-path = 1*( "/" segment ) */
absolute_paths : absolute_paths absolute_path
{
if (($$ = clixon_string_del_join($1, "/", $2)) == NULL) YYABORT;
_PARSE_DEBUG("absolute-paths -> absolute-paths absolute -path");
}
| absolute_path
{ $$ = strdup($1);
_PARSE_DEBUG("absolute-paths -> absolute -path");
}
;
/* segment = <segment, see [RFC3986], Section 3.3>
* segment = *pchar
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / }"_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
absolute_path : SLASH PCHARS
{ $$=$2; _PARSE_DEBUG("absolute-path -> PCHARS"); }
;
HTTP_version : HTTP SLASH DIGIT DOT DIGIT
{ _PARSE_DEBUG("HTTP-version -> HTTP / DIGIT . DIGIT"); }
;
/*------------------------------------------ hdr fields
*( header-field CRLF ) */
header_fields : header_fields header_field CRLF
{ _PARSE_DEBUG("header-fields -> header-fields header-field CRLF"); }
| { _PARSE_DEBUG("header-fields -> "); }
;
/* header-field = field-name ":" OWS field-value OWS */
header_field : field_name COLON ows field_values ows
{ _PARSE_DEBUG("header-field -> field-name : field-values"); }
;
/* field-name = token */
field_name : TOKEN { _PARSE_DEBUG("field-name -> TOKEN"); }
;
/* field-value = *( field-content / obs-fold ) */
field_values : field_values field_content
{ _PARSE_DEBUG("field-values -> field-values field-content"); }
| { _PARSE_DEBUG("field-values -> "); }
;
/* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] */
field_content : field_vchars { _PARSE_DEBUG("field-content -> field-vchars"); }
;
/* field-vchar = VCHAR / obs-text */
field_vchars : field_vchars RWS VCHAR
{ _PARSE_DEBUG("field-vchars -> field-vchars VCHAR"); }
| VCHAR { _PARSE_DEBUG("field-vchars -> VCHAR"); }
;
/* The OWS rule is used where zero or more linear whitespace octets
OWS = *( SP / HTAB )
; optional whitespace
RWS = 1*( SP / HTAB )
; required whitespace
*/
ows : RWS
|
;
%%

View file

@ -50,13 +50,6 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#ifdef HAVE_LIBEVHTP
/* evhtp */
#define EVHTP_DISABLE_REGEX
#define EVHTP_DISABLE_EVTHR
#include <evhtp/evhtp.h>
#endif /* HAVE_LIBEVHTP */
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#endif #endif
@ -71,8 +64,8 @@
#include "restconf_api.h" /* Virtual api */ #include "restconf_api.h" /* Virtual api */
#include "restconf_native.h" #include "restconf_native.h"
/*! Add HTTP header field name and value to reply, evhtp specific /*! Add HTTP header field name and value to reply
* @param[in] req Evhtp http request handle * @param[in] req request handle
* @param[in] name HTTP header field name * @param[in] name HTTP header field name
* @param[in] vfmt HTTP header field value format string w variable parameter * @param[in] vfmt HTTP header field value format string w variable parameter
* @see eg RFC 7230 * @see eg RFC 7230

View file

@ -1,681 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020-2022 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 *****
* Evhtp specific HTTP/1 code complementing restconf_main_native.c
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
/* The clixon evhtp code can be compiled with or without threading support
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <pwd.h>
#include <ctype.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/resource.h>
#include <openssl/ssl.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#ifdef HAVE_LIBEVHTP
#include <event2/buffer.h> /* evbuffer */
#define EVHTP_DISABLE_REGEX
#define EVHTP_DISABLE_EVTHR
#include <evhtp/evhtp.h>
#endif /* HAVE_LIBEVHTP */
#ifdef HAVE_LIBNGHTTP2 /* To get restconf_native.h include files right */
#include <nghttp2/nghttp2.h>
#endif
/* restconf */
#include "restconf_lib.h" /* generic shared with plugins */
#include "restconf_handle.h"
#include "restconf_api.h" /* generic not shared with plugins */
#include "restconf_err.h"
#include "restconf_root.h"
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
#ifdef HAVE_LIBEVHTP
#include "restconf_evhtp.h" /* evhtp http/1 */
#endif
#ifdef HAVE_LIBEVHTP
static char*
evhtp_method2str(enum htp_method m)
{
switch (m){
case htp_method_GET:
return "GET";
break;
case htp_method_HEAD:
return "HEAD";
break;
case htp_method_POST:
return "POST";
break;
case htp_method_PUT:
return "PUT";
break;
case htp_method_DELETE:
return "DELETE";
break;
case htp_method_OPTIONS:
return "OPTIONS";
break;
case htp_method_PATCH:
return "PATCH";
break;
#ifdef NOTUSED
case htp_method_MKCOL:
return "MKCOL";
break;
case htp_method_COPY:
return "COPY";
break;
case htp_method_MOVE:
return "MOVE";
break;
case htp_method_OPTIONS:
return "OPTIONS";
break;
case htp_method_PROPFIND:
return "PROPFIND";
break;
case htp_method_PROPPATCH:
return "PROPPATCH";
break;
case htp_method_LOCK:
return "LOCK";
break;
case htp_method_UNLOCK:
return "UNLOCK";
break;
case htp_method_TRACE:
return "TRACE";
break;
case htp_method_CONNECT:
return "CONNECT";
break;
#endif /* NOTUSED */
default:
return "UNKNOWN";
break;
}
}
static int
evhtp_print_header(evhtp_header_t *header,
void *arg)
{
clicon_debug(1, "%s %s %s", __FUNCTION__, header->key, header->val);
return 0;
}
static int
evhtp_query_iterator(evhtp_header_t *hdr,
void *arg)
{
cvec *qvec = (cvec *)arg;
char *key;
char *val;
char *valu = NULL; /* unescaped value */
cg_var *cv;
key = hdr->key;
val = hdr->val;
if (uri_percent_decode(val, &valu) < 0)
return -1;
if ((cv = cvec_add(qvec, CGV_STRING)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_add");
return -1;
}
cv_name_set(cv, key);
cv_string_set(cv, valu);
if (valu)
free(valu);
return 0;
}
/*! Translate http header by capitalizing, prepend w HTTP_ and - -> _
* Example: Host -> HTTP_HOST
*/
static int
evhtp_convert_fcgi(evhtp_header_t *hdr,
void *arg)
{
clicon_handle h = (clicon_handle)arg;
return restconf_convert_hdr(h, hdr->key, hdr->val);
}
/*! convert parameters from evhtp to fcgi-like parameters used by clixon
*
* While all these params come via one call in fcgi, the information must be taken from
* 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 1 OK continue
* @retval 0 Fail, dont continue
* @retval -1 Error
* The following parameters are set:
* QUERY_STRING
* REQUEST_METHOD
* REQUEST_URI
* HTTPS
* HTTP_HOST
* HTTP_ACCEPT
* HTTP_CONTENT_TYPE
* @note there may be more used by an application plugin
*/
static int
convert_evhtp_params2clixon(clicon_handle h,
evhtp_request_t *req,
cvec *qvec)
{
int retval = -1;
htp_method meth;
evhtp_uri_t *uri;
evhtp_path_t *path;
evhtp_ssl_t *ssl = NULL;
char *subject = NULL;
cvec *cvv = NULL;
char *cn;
cxobj *xerr = NULL;
int pretty;
if ((uri = req->uri) == NULL){
clicon_err(OE_DAEMON, EFAULT, "No uri");
goto done;
}
if ((path = uri->path) == NULL){
clicon_err(OE_DAEMON, EFAULT, "No path");
goto done;
}
meth = evhtp_request_get_method(req);
/* 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, evhtp_query_iterator, qvec) < 0){
clicon_err(OE_CFG, errno, "evhtp_kvs_for_each");
goto done;
}
if (restconf_param_set(h, "REQUEST_METHOD", evhtp_method2str(meth)) < 0)
goto done;
if (restconf_param_set(h, "REQUEST_URI", path->full) < 0)
goto done;
clicon_debug(1, "%s proto:%d", __FUNCTION__, req->proto);
pretty = restconf_pretty_get(h);
/* XXX: Any two http numbers seem accepted by evhtp, like 1.99, 99.3 as http/1.1*/
if (req->proto != EVHTP_PROTO_10 &&
req->proto != EVHTP_PROTO_11){
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid HTTP version number") < 0)
goto done;
/* Select json as default since content-type header may not be accessible yet */
if (api_return_err0(h, req, xerr, pretty, YANG_DATA_JSON, 0) < 0)
goto done;
goto fail;
}
clicon_debug(1, "%s conn->ssl:%d", __FUNCTION__, req->conn->ssl?1:0);
/* Slightly awkward way of taking cert subject and CN and add it to restconf parameters
* instead of accessing it directly */
if ((ssl = req->conn->ssl) != NULL){
if (restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
goto done;
/* SSL subject fields, eg CN (Common Name) , can add more here? */
if (ssl_x509_name_oneline(req->conn->ssl, &subject) < 0)
goto done;
if (subject != NULL) {
if (uri_str2cvec(subject, '/', '=', 1, &cvv) < 0)
goto done;
if ((cn = cvec_find_str(cvv, "CN")) != NULL){
if (restconf_param_set(h, "SSL_CN", cn) < 0) /* Can be used by callback */
goto done;
}
}
}
/* Translate all http headers by capitalizing, prepend w HTTP_ and - -> _
* Example: Host -> HTTP_HOST
*/
if (evhtp_headers_for_each(req->headers_in, evhtp_convert_fcgi, h) < 0)
goto done;
retval = 1;
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (subject)
free(subject);
if (xerr)
xml_free(xerr);
if (cvv)
cvec_free(cvv);
return retval;
fail:
retval = 0;
goto done;
}
/*! We got -1 back from lower layers, create a 500 Internal Server error
* Catch all on fatal error. This does not terminate the process but closes request
* stream
*/
static int
evhtp_internal_error(evhtp_request_t *req)
{
if (strlen(clicon_err_reason) &&
req->buffer_out){
evbuffer_add_printf(req->buffer_out, "%s", clicon_err_reason);
evhtp_send_reply(req, EVHTP_RES_500);
}
clicon_err_reset();
return 0;
}
/*! Send reply
* @see htp__create_reply_
*/
static int
native_send_reply(restconf_conn *rc,
restconf_stream_data *sd,
evhtp_request_t *req)
{
int retval = -1;
cg_var *cv;
int minor;
int major;
switch (req->proto) {
case EVHTP_PROTO_10:
if (req->flags & EVHTP_REQ_FLAG_KEEPALIVE) {
/* protocol is HTTP/1.0 and clients wants to keep established */
if (restconf_reply_header(sd, "Connection", "keep-alive") < 0)
goto done;
}
major = htparser_get_major(req->conn->parser); /* XXX Look origin */
minor = htparser_get_minor(req->conn->parser);
break;
case EVHTP_PROTO_11:
if (!(req->flags & EVHTP_REQ_FLAG_KEEPALIVE)) {
/* protocol is HTTP/1.1 but client wanted to close */
if (restconf_reply_header(sd, "Connection", "keep-alive") < 0)
goto done;
}
major = htparser_get_major(req->conn->parser);
minor = htparser_get_minor(req->conn->parser);
break;
default:
/* this sometimes happens when a response is made but paused before
* the method has been parsed */
major = 1;
minor = 0;
break;
}
cprintf(sd->sd_outp_buf, "HTTP/%u.%u %u %s\r\n",
major,
minor,
sd->sd_code,
restconf_code2reason(sd->sd_code));
/* Loop over headers */
cv = NULL;
while ((cv = cvec_each(sd->sd_outp_hdrs, cv)) != NULL)
cprintf(sd->sd_outp_buf, "%s: %s\r\n", cv_name_get(cv), cv_string_get(cv));
cprintf(sd->sd_outp_buf, "\r\n");
// cvec_reset(rc->rc_outp_hdrs); /* Is now done in restconf_connection but can be done here */
retval = 0;
done:
return retval;
}
/*!
*/
static int
restconf_evhtp_reply(restconf_conn *rc,
restconf_stream_data *sd,
evhtp_request_t *req)
{
int retval = -1;
req->status = sd->sd_code;
req->flags |= EVHTP_REQ_FLAG_FINISHED; /* Signal to evhtp to read next request */
/* If body, add a content-length header
* A server MUST NOT send a Content-Length header field in any response
* with a status code of 1xx (Informational) or 204 (No Content). A
* server MUST NOT send a Content-Length header field in any 2xx
* (Successful) response to a CONNECT request (Section 4.3.6 of
* [RFC7231]).
*/
if (sd->sd_code != 204 && sd->sd_code > 199)
if (restconf_reply_header(sd, "Content-Length", "%zu", sd->sd_body_len) < 0)
goto done;
/* Create reply and write headers */
if (native_send_reply(rc, sd, req) < 0)
goto done;
/* Write a body */
if (sd->sd_body){
cbuf_append_str(sd->sd_outp_buf, cbuf_get(sd->sd_body));
}
retval = 0;
done:
return retval;
}
#ifdef HAVE_LIBNGHTTP2
/*! Check http/1 UPGRADE to http/2
* If upgrade headers are encountered AND http/2 is configured, then
* - add upgrade headers or signal error
* - set http2 flag get settings to and signal to upper layer to do the actual transition.
* @retval -1 Error
* @retval 0 Yes, upgrade dont proceed with request
* @retval 1 No upgrade, proceed with request
* @note currently upgrade header is checked always if nghttp2 is configured but may be a
* runtime config option
*/
static int
evhtp_upgrade_http2(clicon_handle h,
restconf_stream_data *sd)
{
int retval = -1;
char *str;
char *settings;
cxobj *xerr = NULL;
if ((str = restconf_param_get(h, "HTTP_UPGRADE")) != NULL &&
clicon_option_bool(h, "CLICON_RESTCONF_HTTP2_PLAIN") == 1){
/* Only accept "h2c" */
if (strcmp(str, "h2c") != 0){
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid upgrade token") < 0)
goto done;
if (api_return_err0(h, sd, xerr, 1, YANG_DATA_JSON, 0) < 0)
goto done;
if (xerr)
xml_free(xerr);
}
else {
if (restconf_reply_header(sd, "Connection", "Upgrade") < 0)
goto done;
if (restconf_reply_header(sd, "Upgrade", "h2c") < 0)
goto done;
if (restconf_reply_send(sd, 101, NULL, 0) < 0) /* Swithcing protocols */
goto done;
/* Signal http/2 upgrade to http/2 to upper restconf_connection handling */
sd->sd_upgrade2 = 1;
if ((settings = restconf_param_get(h, "HTTP_HTTP2_Settings")) != NULL &&
(sd->sd_settings2 = (uint8_t*)strdup(settings)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
retval = 0; /* Yes, upgrade or error */
}
else
retval = 1; /* No upgrade, proceed with request */
done:
return retval;
}
#endif /* HAVE_LIBNGHTTP2 */
/*! Callback for each incoming http request for path /
*
* This are all messages except /.well-known, Registered with evhtp_set_cb
*
* @param[in] req evhtp http request structure defining the incoming message
* @param[in] arg cx_evhtp handle clixon specific fields
* @retval void
* Discussion: problematic if fatal error -1 is returneod from clixon routines
* without actually terminating. Consider:
* 1) sending some error? and/or
* 2) terminating the process?
*/
void
restconf_path_root(evhtp_request_t *req,
void *arg)
{
int retval = -1;
clicon_handle h;
int ret;
evhtp_connection_t *evconn;
restconf_conn *rc;
restconf_stream_data *sd;
size_t len;
unsigned char *buf;
int keep_params = 0; /* set to 1 if dont delete params */
clicon_debug(1, "------------");
if ((h = (clicon_handle)arg) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
evhtp_internal_error(req);
goto done;
}
/* evhtp connect struct */
if ((evconn = evhtp_request_get_connection(req)) == NULL){
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
evhtp_internal_error(req);
goto done;
}
/* get clixon request connect pointer from generic evhtp application pointer */
rc = evconn->arg;
if ((sd = restconf_stream_find(rc, 0)) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "No stream_data");
evhtp_internal_error(req);
goto done;
}
sd->sd_req = req;
sd->sd_proto = (req->proto == EVHTP_PROTO_10)?HTTP_10:HTTP_11;
/* input debug */
if (clicon_debug_get())
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
/* Query vector, ie the ?a=x&b=y stuff */
if (sd->sd_qvec)
cvec_free(sd->sd_qvec);
if ((sd->sd_qvec = cvec_new(0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
evhtp_internal_error(req);
goto done;
}
/* Get indata
*/
if ((len = evbuffer_get_length(req->buffer_in)) > 0){
if ((buf = evbuffer_pullup(req->buffer_in, len)) == NULL){
clicon_err(OE_CFG, errno, "evbuffer_pullup");
goto done;
}
cbuf_reset(sd->sd_indata);
/* Note the pullup may not be null-terminated */
cbuf_append_buf(sd->sd_indata, buf, len);
}
/* Convert parameters from evhtp to fcgi-like parameters used by clixon
* ret = 0 means an error has already been sent
*/
if ((ret = convert_evhtp_params2clixon(h, req, sd->sd_qvec)) < 0){
evhtp_internal_error(req);
goto done;
}
/* Check sanity of session, eg ssl client cert validation, may set rc_exit */
if (restconf_connection_sanity(h, rc, sd) < 0)
goto done;
if (rc->rc_exit == 0){
#ifdef HAVE_LIBNGHTTP2
if (ret == 1){
if ((ret = evhtp_upgrade_http2(h, sd)) < 0){
evhtp_internal_error(req);
goto done;
}
if (ret == 0)
keep_params = 1;
}
#endif
if (ret == 1){
/* call generic function */
if (api_root_restconf(h, sd, sd->sd_qvec) < 0){
evhtp_internal_error(req);
goto done;
}
}
}
/* Clear input request parameters from this request */
if (!keep_params && restconf_param_del_all(h) < 0){
evhtp_internal_error(req);
goto done;
}
/* All parameters for sending a reply are here
*/
if (sd->sd_code){
if (restconf_evhtp_reply(rc, sd, req) < 0){
evhtp_internal_error(req);
goto done;
}
}
retval = 0;
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
return; /* void */
}
/*! /.well-known callback
*
* @param[in] req evhtp http request structure defining the incoming message
* @param[in] arg cx_evhtp handle clixon specific fields
* @retval void
*/
void
restconf_path_wellknown(evhtp_request_t *req,
void *arg)
{
int retval = -1;
clicon_handle h;
int ret;
evhtp_connection_t *evconn;
restconf_conn *rc;
restconf_stream_data *sd;
int keep_params = 0; /* set to 1 if dont delete params */
clicon_debug(1, "------------");
if ((h = (clicon_handle)arg) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
evhtp_internal_error(req);
goto done;
}
/* evhtp connect struct */
if ((evconn = evhtp_request_get_connection(req)) == NULL){
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
evhtp_internal_error(req);
goto done;
}
/* get clixon request connect pointer from generic evhtp application pointer */
rc = evconn->arg;
if ((sd = restconf_stream_find(rc, 0)) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "No stream_data");
evhtp_internal_error(req);
goto done;
}
sd->sd_req = req;
sd->sd_proto = (req->proto == EVHTP_PROTO_10)?HTTP_10:HTTP_11;
/* input debug */
if (clicon_debug_get())
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
/* Query vector, ie the ?a=x&b=y stuff */
if ((sd->sd_qvec = cvec_new(0)) ==NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
evhtp_internal_error(req);
goto done;
}
/* Convert parameters from evhtp to fcgi-like parameters used by clixon
* ret = 0 means an error has already been sent
*/
if ((ret = convert_evhtp_params2clixon(h, req, sd->sd_qvec)) < 0){
evhtp_internal_error(req);
goto done;
}
/* Check sanity of session, eg ssl client cert validation, may set rc_exit */
if (restconf_connection_sanity(h, rc, sd) < 0)
goto done;
if (rc->rc_exit == 0){
#ifdef HAVE_LIBNGHTTP2
if (ret == 1){
if ((ret = evhtp_upgrade_http2(h, sd)) < 0){
evhtp_internal_error(req);
goto done;
}
if (ret == 0)
keep_params = 1;
}
#endif
if (ret == 1){
/* call generic function */
if (api_well_known(h, sd) < 0){
evhtp_internal_error(req);
goto done;
}
}
}
/* Clear input request parameters from this request */
if (!keep_params && restconf_param_del_all(h) < 0){
evhtp_internal_error(req);
goto done;
}
/* All parameters for sending a reply are here
*/
if (sd->sd_code){
if (restconf_evhtp_reply(rc, sd, req) < 0){
evhtp_internal_error(req);
goto done;
}
}
retval = 0;
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
return; /* void */
}
#endif /* HAVE_LIBEVHTP */

View file

@ -44,7 +44,6 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <inttypes.h> #include <inttypes.h>
#include <dirent.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>

View file

@ -49,18 +49,6 @@
* +--------------------+ * +--------------------+
* Note restconf_request may need to be extended eg backpointer to rs? * Note restconf_request may need to be extended eg backpointer to rs?
* *
* +--------------------+ +--------------------+
* | evhtp_connection | -----> | evhtp_t (core) |
* +--------------------+ +--------------------+
* |request ^
* v | conn
* +--------------------+ +--------------------+
* | evhtp_request | --> uri | evhtp_uri |
* +--------------------+ +--------------------+
* | (created by parser) | | |
* v v v v
* headers/buffers/method/... authority path query
*
* Buffer handling: * Buffer handling:
* c * c
* | * |
@ -121,12 +109,6 @@
#include "clixon_config.h" /* generated by config & autoconf */ #include "clixon_config.h" /* generated by config & autoconf */
#endif #endif
/* The clixon evhtp code can be compiled with or without threading support
* The choice is set at libevhtp compile time by cmake. Eg:
* cmake -DEVHTP_DISABLE_EVTHR=ON # Disable threads.
* Default in testing is disabled threads.
*/
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
@ -151,16 +133,6 @@
/* clicon */ /* clicon */
#include <clixon/clixon.h> #include <clixon/clixon.h>
#ifdef HAVE_LIBEVHTP
/* evhtp */
#include <event2/buffer.h> /* evbuffer */
#define EVHTP_DISABLE_REGEX
#define EVHTP_DISABLE_EVTHR
#include <evhtp/evhtp.h>
#endif /* HAVE_LIBEVHTP */
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
/* nghttp2 */ /* nghttp2 */
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
@ -173,12 +145,12 @@
#include "restconf_err.h" #include "restconf_err.h"
#include "restconf_root.h" #include "restconf_root.h"
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/ #include "restconf_native.h" /* Restconf-openssl mode specific headers*/
#ifdef HAVE_LIBEVHTP
#include "restconf_evhtp.h" /* http/1 */
#endif
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
#include "restconf_nghttp2.h" /* http/2 */ #include "restconf_nghttp2.h" /* http/2 */
#endif #endif
#ifdef HAVE_HTTP1
#include "clixon_http1.h"
#endif
/* Command line options to be passed to getopt(3) */ /* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:E:l:p:y:a:u:rW:R:o:" #define RESTCONF_OPTS "hD:f:E:l:p:y:a:u:rW:R:o:"
@ -352,9 +324,7 @@ clixon_openssl_log_cb(void *handle,
return 0; return 0;
} }
/* /*! Init openSSL
* see restconf_config ->cv_evhtp_init(x2) -> cx_evhtp_socket ->
* evhtp_ssl_init:4757
*/ */
static int static int
init_openssl(void) init_openssl(void)
@ -425,7 +395,7 @@ restconf_verify_certs(int preverify_ok,
* - 0 (preferity_ok) the session terminates here in SSL negotiation, an http client * - 0 (preferity_ok) the session terminates here in SSL negotiation, an http client
* will get a low level error (not http reply) * will get a low level error (not http reply)
* - 1 Check if the cert is valid using SSL_get_verify_result(rc->rc_ssl) * - 1 Check if the cert is valid using SSL_get_verify_result(rc->rc_ssl)
* @see restconf_evhtp_sanity and restconf_nghttp2_sanity where this is done for http/1 and http/2 * @see restconf_nghttp2_sanity where this is done for http/1 and http/2
*/ */
return 1; return 1;
} }
@ -479,7 +449,7 @@ alpn_select_proto_cb(SSL *ssl,
inp++; inp++;
if (clicon_debug_get()) /* debug print the protoocol */ if (clicon_debug_get()) /* debug print the protoocol */
alpn_proto_dump(__FUNCTION__, (const char*)inp, len); alpn_proto_dump(__FUNCTION__, (const char*)inp, len);
#ifdef HAVE_LIBEVHTP #ifdef HAVE_HTTP1
if (pref < 10 && len == 8 && strncmp((char*)inp, "http/1.1", len) == 0){ if (pref < 10 && len == 8 && strncmp((char*)inp, "http/1.1", len) == 0){
*outlen = len; *outlen = len;
*out = inp; *out = inp;
@ -503,8 +473,6 @@ alpn_select_proto_cb(SSL *ssl,
} }
/* /*
* see restconf_config ->cv_evhtp_init(x2) -> cx_evhtp_socket ->
* evhtp_ssl_init:4794
*/ */
static SSL_CTX * static SSL_CTX *
restconf_ssl_context_create(clicon_handle h) restconf_ssl_context_create(clicon_handle h)
@ -535,8 +503,6 @@ restconf_ssl_context_create(clicon_handle h)
} }
/* /*
* see restconf_config ->cv_evhtp_init(x2) -> cx_evhtp_socket ->
* evhtp_ssl_init: 4886
* @param[in] ctx SSL context * @param[in] ctx SSL context
* @param[in] server_cert_path Server cert * @param[in] server_cert_path Server cert
* @param[in] server_key_path Server private key * @param[in] server_key_path Server private key
@ -595,7 +561,7 @@ restconf_ssl_context_configure(clixon_handle h,
return retval; return retval;
} }
/*! Utility function to close restconf server ssl/evhtp socket. /*! Utility function to close restconf server ssl socket.
* There are many variants to closing, one could probably make this more generic * There are many variants to closing, one could probably make this more generic
* and always use this function, but it is difficult. * and always use this function, but it is difficult.
*/ */
@ -618,10 +584,6 @@ Note that in this case SSL_ERROR_ZERO_RETURN does not necessarily indicate that
} }
SSL_free(rc->rc_ssl); SSL_free(rc->rc_ssl);
rc->rc_ssl = NULL; rc->rc_ssl = NULL;
#ifdef HAVE_LIBEVHTP
if (rc->rc_evconn)
rc->rc_evconn->ssl = NULL;
#endif
} }
if (close(rc->rc_s) < 0){ if (close(rc->rc_s) < 0){
clicon_err(OE_UNIX, errno, "close"); clicon_err(OE_UNIX, errno, "close");
@ -675,7 +637,67 @@ send_badrequest(clicon_handle h,
return retval; return retval;
} }
/*! New data connection after accept, receive and reply on data sockte #if 0
#define IFILE "/var/tmp/clixon-mirror/ifile"
#define FMTDIR "/var/tmp/clixon-mirror/"
static FILE *myf = NULL;
static int
mirror_pkt(const char *buf,
ssize_t n)
{
int retval = -1;
if (fwrite(buf, 1, n, myf) != n){
perror("fopen");
goto done;
}
retval = 0;
done:
return retval;
}
static int
mirror_new(void)
{
int retval = -1;
static uint64_t u64 = 0;
cbuf *cb = cbuf_new();
FILE *ifile;
if ((ifile = fopen(IFILE, "r+")) == NULL){
perror("fopen r+ ifile");
}
else {
if (fscanf(ifile, "%" PRIu64, &u64) < 0){
perror("fscanf ifile");
goto done;
}
fclose(ifile);
}
if (myf != NULL)
fclose(myf);
cprintf(cb, FMTDIR "%" PRIu64 ".dump", u64);
if ((myf = fopen(cbuf_get(cb), "w")) == NULL){
perror("fopen");
goto done;
}
cbuf_free(cb);
u64++;
if ((ifile = fopen(IFILE, "w")) == NULL){
perror("fopen w+ ifile");
goto done;
}
fprintf(ifile, "%" PRIu64, u64);
fclose(ifile);
retval = 0;
done:
return retval;
}
#endif
/*! New data connection after accept, receive and reply on data socket
* *
* @param[in] s Socket where message arrived. read from this. * @param[in] s Socket where message arrived. read from this.
* @param[in] arg Client entry (from). * @param[in] arg Client entry (from).
@ -683,10 +705,10 @@ send_badrequest(clicon_handle h,
* @retval -1 Error Terminates backend and is never called). Instead errors are * @retval -1 Error Terminates backend and is never called). Instead errors are
* propagated back to client. * propagated back to client.
* @see restconf_accept_client where this callback is registered * @see restconf_accept_client where this callback is registered
* @note read buffer is limited. More data can be read in two ways: either evhtp returns a buffer * @note read buffer is limited. More data can be read in two ways: returns a buffer
* with 100 Continue, in which case that is replied and the function returns and the client sends * with 100 Continue, in which case that is replied and the function returns and the client sends
* more data. * more data.
* OR evhtp returns 0 with no reply, then this is assumed to mean read more data from the socket. * OR returns 0 with no reply, then this is assumed to mean read more data from the socket.
*/ */
static int static int
restconf_connection(int s, restconf_connection(int s,
@ -701,9 +723,8 @@ restconf_connection(int s,
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
int ret; int ret;
#endif #endif
#ifdef HAVE_LIBEVHTP #ifdef HAVE_HTTP1
clicon_handle h; clicon_handle h;
evhtp_connection_t *evconn = NULL;
restconf_stream_data *sd; restconf_stream_data *sd;
#endif #endif
@ -765,7 +786,7 @@ restconf_connection(int s,
continue; continue;
} }
} }
clicon_debug(1, "%s (ssl)read:%zd", __FUNCTION__, n); clicon_debug(1, "%s read:%zd", __FUNCTION__, n);
if (n == 0){ if (n == 0){
clicon_debug(1, "%s n=0 closing socket", __FUNCTION__); clicon_debug(1, "%s n=0 closing socket", __FUNCTION__);
if (restconf_close_ssl_socket(rc, 0) < 0) if (restconf_close_ssl_socket(rc, 0) < 0)
@ -774,99 +795,43 @@ restconf_connection(int s,
rc = NULL; rc = NULL;
goto ok; goto ok;
} }
#if 0
if (mirror_pkt(buf, n) < 0)
goto done;
#endif
switch (rc->rc_proto){ switch (rc->rc_proto){
#ifdef HAVE_LIBEVHTP #ifdef HAVE_HTTP1
case HTTP_10: case HTTP_10:
case HTTP_11: case HTTP_11:
h = rc->rc_h; h = rc->rc_h;
/* parse incoming packet using evhtp if (clixon_http1_parse_buf(h, rc, buf, n) < 0){
* signature:
*/
evconn = rc->rc_evconn;
/* This is the main call to EVHTP parser */
if (connection_parse_nobev(buf, n, evconn) < 0){
clicon_debug(1, "%s connection_parse error", __FUNCTION__);
/* XXX To get more nuanced evhtp error check
* htparser_get_error(conn->parser)
*/
if (send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml", if (send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml",
"<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>protocol</error-type><error-tag>malformed-message</error-tag><error-message>The requested URL or a header is in some way badly formed</error-message></error></errors>") < 0) "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>protocol</error-type><error-tag>malformed-message</error-tag><error-message>The requested URL or a header is in some way badly formed</error-message></error></errors>") < 0)
goto done; goto done;
SSL_free(rc->rc_ssl); }
rc->rc_ssl = NULL; else{
evconn->ssl = NULL; if (restconf_http1_path_root(h, rc) < 0)
if (close(rc->rc_s) < 0){
clicon_err(OE_UNIX, errno, "close");
goto done; goto done;
} }
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
clicon_debug(1, "%s evconn-free (%p) 2", __FUNCTION__, evconn);
restconf_conn_free(rc);
goto ok;
} /* connection_parse_nobev */
clicon_debug(1, "%s connection_parse OK", __FUNCTION__); clicon_debug(1, "%s connection_parse OK", __FUNCTION__);
/* default stream */ /* default stream */
if ((sd = restconf_stream_find(rc, 0)) == NULL){ if ((sd = restconf_stream_find(rc, 0)) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "restconf stream not found"); clicon_err(OE_RESTCONF, EINVAL, "restconf stream not found");
goto done; goto done;
} }
if (evconn->bev != NULL){ if (buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf),
struct evbuffer *ev; rc->rc_s, rc->rc_ssl) < 0)
size_t buflen0; goto done;
size_t buflen1; cvec_reset(sd->sd_outp_hdrs); /* Can be done in native_send_reply */
char *buf = NULL; cbuf_reset(sd->sd_outp_buf);
if ((ev = bufferevent_get_output(evconn->bev)) != NULL){
buflen0 = evbuffer_get_length(ev);
buflen1 = buflen0 - rc->rc_bufferevent_output_offset;
if (buflen1 > 0){
buf = (char*)evbuffer_pullup(ev, -1);
/* XXX Here if -1 in api_root
* HTTP/1.1 0 UNKNOWN\r\nContent-Length: 0
* And output_buffer is NULL
*/
/* If evhtp has print an output buffer, clixon whould not have done it
* Shouldnt happen
*/
if (cbuf_len(sd->sd_outp_buf)){
clicon_debug(1, "%s Warning: evhtp printed output buffer, but clixon output buffer is non-empty %s",
__FUNCTION__, cbuf_get(sd->sd_outp_buf));
cbuf_reset(sd->sd_outp_buf);
}
if (cbuf_append_buf(sd->sd_outp_buf, buf, buflen1) < 0){
clicon_err(OE_UNIX, errno, "cbuf_append_buf");
goto done;
}
/* XXX Cant get drain to work, need to keep an offset */
evbuffer_drain(ev, -1);
rc->rc_bufferevent_output_offset += buflen1;
}
}
if (cbuf_len(sd->sd_outp_buf) == 0)
readmore = 1;
else {
if (buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf),
rc->rc_s, rc->rc_ssl) < 0)
goto done;
cvec_reset(sd->sd_outp_hdrs); /* Can be done in native_send_reply */
cbuf_reset(sd->sd_outp_buf);
}
}
else{
if (send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml",
"<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>protocol</error-type><error-tag>malformed-message</error-tag><error-message>No evhtp output</error-message></error></errors>") < 0)
goto done;
}
if (rc->rc_exit){ /* Server-initiated exit for http/2 */ if (rc->rc_exit){ /* Server-initiated exit for http/2 */
SSL_free(rc->rc_ssl); SSL_free(rc->rc_ssl);
rc->rc_ssl = NULL; rc->rc_ssl = NULL;
evconn->ssl = NULL;
if (close(rc->rc_s) < 0){ if (close(rc->rc_s) < 0){
clicon_err(OE_UNIX, errno, "close"); clicon_err(OE_UNIX, errno, "close");
goto done; goto done;
} }
clixon_event_unreg_fd(rc->rc_s, restconf_connection); clixon_event_unreg_fd(rc->rc_s, restconf_connection);
clicon_debug(1, "%s evconn-free (%p) 2", __FUNCTION__, evconn);
restconf_conn_free(rc); restconf_conn_free(rc);
goto ok; goto ok;
} }
@ -916,7 +881,7 @@ restconf_connection(int s,
} }
#endif #endif
break; break;
#endif /* HAVE_LIBEVHTP */ #endif /* HAVE_HTTP1 */
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
case HTTP_2: case HTTP_2:
if (rc->rc_exit){ /* Server-initiated exit for http/2 */ if (rc->rc_exit){ /* Server-initiated exit for http/2 */
@ -1119,10 +1084,9 @@ restconf_accept_client(int fd,
clicon_debug(1, "%s %d", __FUNCTION__, fd); clicon_debug(1, "%s %d", __FUNCTION__, fd);
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
#ifndef HAVE_LIBEVHTP #ifndef HAVE_HTTP1
proto = HTTP_2; /* If nghttp2 only let default be 2.0 */ proto = HTTP_2; /* If nghttp2 only let default be 2.0 */
#endif #endif
/* If also evhtp, keep HTTP_11 */
#endif #endif
if ((rsock = (restconf_socket *)arg) == NULL){ if ((rsock = (restconf_socket *)arg) == NULL){
clicon_err(OE_YANG, EINVAL, "rsock is NULL"); clicon_err(OE_YANG, EINVAL, "rsock is NULL");
@ -1313,28 +1277,14 @@ restconf_accept_client(int fd,
} /* if ssl */ } /* if ssl */
rc->rc_proto = proto; rc->rc_proto = proto;
switch (rc->rc_proto){ switch (rc->rc_proto){
#ifdef HAVE_LIBEVHTP #ifdef HAVE_HTTP1
case HTTP_10: case HTTP_10:
case HTTP_11:{ case HTTP_11:
evhtp_t *evhtp = (evhtp_t *)rh->rh_arg;
evhtp_connection_t *evconn;
/* Create evhtp-specific struct */
if ((evconn = evhtp_connection_new_server(evhtp, rc->rc_s)) == NULL){
clicon_err(OE_UNIX, errno, "evhtp_connection_new_server");
goto done;
}
/* Mutual pointers, from generic rc to evhtp specific and from evhtp conn to generic
*/
rc->rc_evconn = evconn; /* Generic to specific */
evconn->arg = rc; /* Specific to generic */
evconn->ssl = rc->rc_ssl; /* evhtp */
/* Create a default stream for http/1 */ /* Create a default stream for http/1 */
if (restconf_stream_data_new(rc, 0) == NULL) if (restconf_stream_data_new(rc, 0) == NULL)
goto done; goto done;
}
break; break;
#endif /* HAVE_LIBEVHTP */ #endif /* HAVE_HTTP1 */
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
case HTTP_2:{ case HTTP_2:{
if (http2_session_init(rc) < 0){ if (http2_session_init(rc) < 0){
@ -1365,6 +1315,10 @@ restconf_accept_client(int fd,
default: default:
break; break;
} /* switch proto */ } /* switch proto */
#if 0
if (mirror_new() < 0)
goto done;
#endif
if (clixon_event_reg_fd(rc->rc_s, restconf_connection, (void*)rc, "restconf client socket") < 0) if (clixon_event_reg_fd(rc->rc_s, restconf_connection, (void*)rc, "restconf client socket") < 0)
goto done; goto done;
ok: ok:
@ -1376,6 +1330,8 @@ restconf_accept_client(int fd,
return retval; return retval;
} /* restconf_accept_client */ } /* restconf_accept_client */
/*!
*/
static int static int
restconf_native_terminate(clicon_handle h) restconf_native_terminate(clicon_handle h)
{ {
@ -1396,18 +1352,6 @@ restconf_native_terminate(clicon_handle h)
} }
if (rh->rh_ctx) if (rh->rh_ctx)
SSL_CTX_free(rh->rh_ctx); SSL_CTX_free(rh->rh_ctx);
#ifdef HAVE_LIBEVHTP
{
evhtp_t *evhtp = (evhtp_t *)rh->rh_arg;
if (evhtp){
if (evhtp->evbase)
event_base_free(evhtp->evbase);
evhtp_free(evhtp);
rh->rh_arg = NULL;
}
}
#endif /* HAVE_LIBEVHTP */
free(rh); free(rh);
} }
EVP_cleanup(); EVP_cleanup();
@ -1581,10 +1525,6 @@ restconf_openssl_init(clicon_handle h,
cxobj **vec = NULL; cxobj **vec = NULL;
size_t veclen; size_t veclen;
int i; int i;
#ifdef HAVE_LIBEVHTP
evhtp_t *evhtp = NULL;
struct event_base *evbase = NULL;
#endif /* HAVE_LIBEVHTP */
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
/* flag used for sanity of certs */ /* flag used for sanity of certs */
@ -1639,28 +1579,6 @@ restconf_openssl_init(clicon_handle h,
} }
rh = restconf_native_handle_get(h); rh = restconf_native_handle_get(h);
rh->rh_ctx = ctx; rh->rh_ctx = ctx;
#ifdef HAVE_LIBEVHTP
/* evhtp stuff */ /* XXX move this to global level */
if ((evbase = event_base_new()) == NULL){
clicon_err(OE_UNIX, errno, "event_base_new");
goto done;
}
/* This is socket create a new evhtp_t instance */
if ((evhtp = evhtp_new((void*)evbase, h)) == NULL){
clicon_err(OE_UNIX, errno, "evhtp_new");
goto done;
}
rh->rh_arg = evhtp;
if (evhtp_set_cb(evhtp, "/" RESTCONF_API, restconf_path_root, h) == NULL){
clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
goto done;
}
/* Callback to be executed for all /restconf api calls */
if (evhtp_set_cb(evhtp, RESTCONF_WELL_KNOWN, restconf_path_wellknown, h) == NULL){
clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
goto done;
}
#endif /* HAVE_LIBEVHTP */
/* get the list of socket config-data */ /* get the list of socket config-data */
if (xpath_vec(xrestconf, nsc, "socket", &vec, &veclen) < 0) if (xpath_vec(xrestconf, nsc, "socket", &vec, &veclen) < 0)
goto done; goto done;

View file

@ -66,14 +66,7 @@
#include "restconf_lib.h" /* generic shared with plugins */ #include "restconf_lib.h" /* generic shared with plugins */
#include "restconf_handle.h" #include "restconf_handle.h"
#include "restconf_err.h" #include "restconf_err.h"
#ifdef HAVE_LIBEVHTP
#include <event2/buffer.h> /* evbuffer */
#define EVHTP_DISABLE_REGEX
#define EVHTP_DISABLE_EVTHR
#include <evhtp/evhtp.h>
#endif
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#endif #endif
@ -167,7 +160,7 @@ restconf_conn_new(clicon_handle h,
return rc; return rc;
} }
/*! Free clixon/cbuf resources related to an evhtp connection /*! Free clixon/cbuf resources related to a connection
* @param[in] rc restconf connection * @param[in] rc restconf connection
*/ */
int int
@ -182,10 +175,6 @@ restconf_conn_free(restconf_conn *rc)
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
if (rc->rc_ngsession) if (rc->rc_ngsession)
nghttp2_session_del(rc->rc_ngsession); nghttp2_session_del(rc->rc_ngsession);
#endif
#ifdef HAVE_LIBEVHTP
if (rc->rc_evconn)
evhtp_connection_free(rc->rc_evconn); /* evhtp */
#endif #endif
/* Free all streams */ /* Free all streams */
while ((sd = rc->rc_streams) != NULL) { while ((sd = rc->rc_streams) != NULL) {

View file

@ -79,7 +79,7 @@ typedef struct {
struct restconf_conn *sd_conn; /* Backpointer to connection this stream is part of */ struct restconf_conn *sd_conn; /* Backpointer to connection this stream is part of */
restconf_http_proto sd_proto; /* http protocol XXX not sure this is needed */ restconf_http_proto sd_proto; /* http protocol XXX not sure this is needed */
cvec *sd_qvec; /* Query parameters, ie ?a=b&c=d */ cvec *sd_qvec; /* Query parameters, ie ?a=b&c=d */
void *sd_req; /* Lib-specific request, eg evhtp_request_t * */ void *sd_req; /* Lib-specific request */
int sd_upgrade2; /* Upgrade to http/2 */ int sd_upgrade2; /* Upgrade to http/2 */
uint8_t *sd_settings2; /* Settings for upgrade to http/2 request */ uint8_t *sd_settings2; /* Settings for upgrade to http/2 request */
} restconf_stream_data; } restconf_stream_data;
@ -98,9 +98,6 @@ typedef struct restconf_conn {
int rc_exit; /* Set to close socket server-side (NYI) */ int rc_exit; /* Set to close socket server-side (NYI) */
/* Decision to keep lib-specific data here, otherwise new struct necessary /* Decision to keep lib-specific data here, otherwise new struct necessary
* drawback is specific includes need to go everywhere */ * drawback is specific includes need to go everywhere */
#ifdef HAVE_LIBEVHTP
evhtp_connection_t *rc_evconn;
#endif
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
nghttp2_session *rc_ngsession; /* XXX Not sure it is needed */ nghttp2_session *rc_ngsession; /* XXX Not sure it is needed */
#endif #endif
@ -125,7 +122,7 @@ typedef struct {
typedef struct { typedef struct {
SSL_CTX *rh_ctx; /* SSL context */ SSL_CTX *rh_ctx; /* SSL context */
restconf_socket *rh_sockets; /* List of restconf server (ready for accept) sockets */ restconf_socket *rh_sockets; /* List of restconf server (ready for accept) sockets */
void *rh_arg; /* Packet specific handle (eg evhtp) */ void *rh_arg; /* Packet specific handle */
} restconf_native_handle; } restconf_native_handle;
/* /*

View file

@ -50,12 +50,6 @@
#include "clixon_config.h" /* generated by config & autoconf */ #include "clixon_config.h" /* generated by config & autoconf */
#endif #endif
/* The clixon evhtp code can be compiled with or without threading support
* The choice is set at libevhtp compile time by cmake. Eg:
* cmake -DEVHTP_DISABLE_EVTHR=ON # Disable threads.
* Default in testing is disabled threads.
*/
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
@ -80,14 +74,6 @@
/* clicon */ /* clicon */
#include <clixon/clixon.h> #include <clixon/clixon.h>
#ifdef HAVE_LIBEVHTP /* To get restconf_native.h include files right */
/* evhtp */
#include <event2/buffer.h> /* evbuffer */
#define EVHTP_DISABLE_REGEX
#define EVHTP_DISABLE_EVTHR
#include <evhtp/evhtp.h>
#endif /* HAVE_LIBEVHTP */
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#endif #endif
@ -279,12 +265,11 @@ recv_callback(nghttp2_session *session,
/*! Callback for each incoming http request for path / /*! Callback for each incoming http request for path /
* *
* This are all messages except /.well-known, Registered with evhtp_set_cb * This are all messages except /.well-known,
* *
* @param[in] req evhtp http request structure defining the incoming message * @param[in] sd session stream struct (http/1 has a single)
* @param[in] arg cx_evhtp handle clixon specific fields
* @retval void * @retval void
* Discussion: problematic if fatal error -1 is returneod from clixon routines * Discussion: problematic if fatal error -1 is returned from clixon routines
* without actually terminating. Consider: * without actually terminating. Consider:
* 1) sending some error? and/or * 1) sending some error? and/or
* 2) terminating the process? * 2) terminating the process?
@ -338,10 +323,6 @@ restconf_nghttp2_path(restconf_stream_data *sd)
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval); clicon_debug(1, "%s %d", __FUNCTION__, retval);
/* Catch all on fatal error. This does not terminate the process but closes request stream */
// if (retval < 0){
// evhtp_send_reply(req, EVHTP_RES_ERROR);
// }
if (cvv) if (cvv)
cvec_free(cvv); cvec_free(cvv);
if (oneline) if (oneline)

145
configure vendored
View file

@ -636,8 +636,8 @@ YANG_STANDARD_DIR
YANG_INSTALLDIR YANG_INSTALLDIR
CLIXON_YANG_PATCH CLIXON_YANG_PATCH
with_libxml2 with_libxml2
HAVE_HTTP1
HAVE_LIBNGHTTP2 HAVE_LIBNGHTTP2
HAVE_LIBEVHTP
with_restconf with_restconf
LINKAGE LINKAGE
LIBSTATIC_SUFFIX LIBSTATIC_SUFFIX
@ -720,7 +720,7 @@ with_cligen
enable_yang_patch enable_yang_patch
enable_publish enable_publish
with_restconf with_restconf
enable_evhtp enable_http1
enable_nghttp2 enable_nghttp2
with_configfile with_configfile
with_libxml2 with_libxml2
@ -1370,7 +1370,7 @@ Optional Features:
--enable-yang-patch Enable YANG patch, RFC 8072, default: no --enable-yang-patch Enable YANG patch, RFC 8072, default: no
--enable-publish Enable publish of notification streams using SSE and --enable-publish Enable publish of notification streams using SSE and
curl curl
--disable-evhtp Disable evhtp for native restconf http/1, ie http/2 --disable-http1 Disable http1 for native restconf http/1, ie http/2
only only
--disable-nghttp2 Disable nghttp2 for native restconf http/2, ie --disable-nghttp2 Disable nghttp2 for native restconf http/2, ie
http/1 only http/1 only
@ -3385,10 +3385,10 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
# Set to native or fcgi -> compile apps/restconf # Set to native or fcgi -> compile apps/restconf
HAVE_LIBEVHTP=false
# consider using neutral constant such as with-http1
HAVE_LIBNGHTTP2=false HAVE_LIBNGHTTP2=false
# consider using neutral constant such as with-http2 # consider using neutral constant such as with-http2
HAVE_HTTP1=false
# Where Clixon installs its YANG specs # Where Clixon installs its YANG specs
@ -5070,7 +5070,6 @@ fi
$as_echo "#define WITH_RESTCONF_FCGI 1" >>confdefs.h $as_echo "#define WITH_RESTCONF_FCGI 1" >>confdefs.h
# For c-code that cant use strings # For c-code that cant use strings
elif test "x${with_restconf}" == xnative; then elif test "x${with_restconf}" == xnative; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL_init_ssl in -lssl" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL_init_ssl in -lssl" >&5
$as_echo_n "checking for OPENSSL_init_ssl in -lssl... " >&6; } $as_echo_n "checking for OPENSSL_init_ssl in -lssl... " >&6; }
if ${ac_cv_lib_ssl_OPENSSL_init_ssl_+:} false; then : if ${ac_cv_lib_ssl_OPENSSL_init_ssl_+:} false; then :
@ -5165,136 +5164,26 @@ else
as_fn_error $? "libcrypto missing" "$LINENO" 5 as_fn_error $? "libcrypto missing" "$LINENO" 5
fi fi
# Check if evhtp is enabled for http/1 # Check if http/1 enabled
# Check whether --enable-evhtp was given. # Check whether --enable-http1 was given.
if test "${enable_evhtp+set}" = set; then : if test "${enable_http1+set}" = set; then :
enableval=$enable_evhtp; enableval=$enable_http1;
if test "$enableval" = no; then if test "$enableval" = no; then
ac_enable_evhtp=no ac_enable_http1=no
else else
ac_enable_evhtp=yes ac_enable_http1=yes
fi fi
else else
ac_enable_evhtp=yes ac_enable_http1=yes
fi fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: checking evhtp is enabled: $ac_enable_evhtp" >&5
$as_echo "checking evhtp is enabled: $ac_enable_evhtp" >&6; }
if test "$ac_enable_evhtp" = "yes"; then
for ac_header in evhtp/evhtp.h
do :
ac_fn_c_check_header_compile "$LINENO" "evhtp/evhtp.h" "ac_cv_header_evhtp_evhtp_h" "$ac_includes_default
#define EVHTP_DISABLE_REGEX
#define EVHTP_DISABLE_EVTHR
" { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking http1 is enabled: $ac_enable_http1" >&5
if test "x$ac_cv_header_evhtp_evhtp_h" = xyes; then : $as_echo "checking http1 is enabled: $ac_enable_http1" >&6; }
cat >>confdefs.h <<_ACEOF if test "$ac_enable_http1" = "yes"; then
#define HAVE_EVHTP_EVHTP_H 1 $as_echo "#define HAVE_HTTP1 true" >>confdefs.h
_ACEOF # Must be tree/false (not 0/1) used in shells
else
as_fn_error $? "evhtp header missing. See https://github.com/clicon/libevhtp" "$LINENO" 5
fi
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for event_init in -levent" >&5
$as_echo_n "checking for event_init in -levent... " >&6; }
if ${ac_cv_lib_event_event_init+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-levent $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char event_init ();
int
main ()
{
return event_init ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_event_event_init=yes
else
ac_cv_lib_event_event_init=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_event_event_init" >&5
$as_echo "$ac_cv_lib_event_event_init" >&6; }
if test "x$ac_cv_lib_event_event_init" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBEVENT 1
_ACEOF
LIBS="-levent $LIBS"
else
as_fn_error $? "libevent missing" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for evhtp_new in -levhtp" >&5
$as_echo_n "checking for evhtp_new in -levhtp... " >&6; }
if ${ac_cv_lib_evhtp_evhtp_new+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-levhtp -levent -lssl -lcrypto $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char evhtp_new ();
int
main ()
{
return evhtp_new ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_evhtp_evhtp_new=yes
else
ac_cv_lib_evhtp_evhtp_new=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_evhtp_evhtp_new" >&5
$as_echo "$ac_cv_lib_evhtp_evhtp_new" >&6; }
if test "x$ac_cv_lib_evhtp_evhtp_new" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBEVHTP 1
_ACEOF
LIBS="-levhtp $LIBS"
else
as_fn_error $? "libevhtp missing" "$LINENO" 5
fi
HAVE_LIBEVHTP=true
fi fi
# Check if nghttp2 is enabled for http/2 # Check if nghttp2 is enabled for http/2

View file

@ -114,8 +114,8 @@ AC_SUBST(SH_SUFFIX)
AC_SUBST(LIBSTATIC_SUFFIX) AC_SUBST(LIBSTATIC_SUFFIX)
AC_SUBST(LINKAGE) AC_SUBST(LINKAGE)
AC_SUBST(with_restconf) # Set to native or fcgi -> compile apps/restconf AC_SUBST(with_restconf) # Set to native or fcgi -> compile apps/restconf
AC_SUBST(HAVE_LIBEVHTP,false) # consider using neutral constant such as with-http1
AC_SUBST(HAVE_LIBNGHTTP2,false) # consider using neutral constant such as with-http2 AC_SUBST(HAVE_LIBNGHTTP2,false) # consider using neutral constant such as with-http2
AC_SUBST(HAVE_HTTP1,false)
AC_SUBST(with_libxml2) AC_SUBST(with_libxml2)
AC_SUBST(CLIXON_YANG_PATCH) AC_SUBST(CLIXON_YANG_PATCH)
# Where Clixon installs its YANG specs # Where Clixon installs its YANG specs
@ -223,31 +223,22 @@ if test "x${with_restconf}" == xfcgi; then
AC_CHECK_LIB(fcgi, FCGX_Init,, AC_MSG_ERROR([libfcgi-dev missing])) AC_CHECK_LIB(fcgi, FCGX_Init,, AC_MSG_ERROR([libfcgi-dev missing]))
AC_DEFINE(WITH_RESTCONF_FCGI, 1, [Use fcgi restconf mode]) # For c-code that cant use strings AC_DEFINE(WITH_RESTCONF_FCGI, 1, [Use fcgi restconf mode]) # For c-code that cant use strings
elif test "x${with_restconf}" == xnative; then elif test "x${with_restconf}" == xnative; then
AC_CHECK_LIB(ssl, OPENSSL_init_ssl ,, AC_MSG_ERROR([libssl missing])) AC_CHECK_LIB(ssl, OPENSSL_init_ssl ,, AC_MSG_ERROR([libssl missing]))
AC_CHECK_LIB(crypto, CRYPTO_new_ex_data, , AC_MSG_ERROR([libcrypto missing])) AC_CHECK_LIB(crypto, CRYPTO_new_ex_data, , AC_MSG_ERROR([libcrypto missing]))
# Check if evhtp is enabled for http/1 # Check if http/1 enabled
AC_ARG_ENABLE(evhtp, AS_HELP_STRING([--disable-evhtp],[Disable evhtp for native restconf http/1, ie http/2 only]),[ AC_ARG_ENABLE(http1, AS_HELP_STRING([--disable-http1],[Disable http1 for native restconf http/1, ie http/2 only]),[
if test "$enableval" = no; then if test "$enableval" = no; then
ac_enable_evhtp=no ac_enable_http1=no
else else
ac_enable_evhtp=yes ac_enable_http1=yes
fi fi
], ],
[ ac_enable_evhtp=yes]) [ ac_enable_http1=yes])
AC_MSG_RESULT(checking evhtp is enabled: $ac_enable_evhtp)
if test "$ac_enable_evhtp" = "yes"; then AC_MSG_RESULT(checking http1 is enabled: $ac_enable_http1)
AC_CHECK_HEADERS(evhtp/evhtp.h, if test "$ac_enable_http1" = "yes"; then
[], AC_DEFINE(HAVE_HTTP1,true) # Must be tree/false (not 0/1) used in shells
AC_MSG_ERROR([evhtp header missing. See https://github.com/clicon/libevhtp]), fi
[AC_INCLUDES_DEFAULT[
#define EVHTP_DISABLE_REGEX
#define EVHTP_DISABLE_EVTHR
]])
AC_CHECK_LIB(event, event_init,, AC_MSG_ERROR([libevent missing]))
AC_CHECK_LIB(evhtp, evhtp_new,, AC_MSG_ERROR([libevhtp missing]),[-levent -lssl -lcrypto])
HAVE_LIBEVHTP=true
fi
# Check if nghttp2 is enabled for http/2 # Check if nghttp2 is enabled for http/2
AC_ARG_ENABLE(nghttp2, AS_HELP_STRING([--disable-nghttp2],[Disable nghttp2 for native restconf http/2, ie http/1 only]),[ AC_ARG_ENABLE(nghttp2, AS_HELP_STRING([--disable-nghttp2],[Disable nghttp2 for native restconf http/2, ie http/1 only]),[

View file

@ -242,6 +242,16 @@ To turn callgrind off/on:
callgrind_control -i on callgrind_control -i on
``` ```
### valgrind and gdb
```
valgrind --vgdb=yes --vgdb-error=0 clixon_cli
gdb clixon_cli
(gdb) target remote | /usr/lib/valgrind/../../bin/vgdb --pid=1311 # see output from valgrind
(gdb) cont
```
## New release ## New release
What to think about when doing a new release. What to think about when doing a new release.
* Ensure all tests run OK * Ensure all tests run OK
@ -257,6 +267,7 @@ What to think about when doing a new release.
git tag -a <version> git tag -a <version>
git push origin <version> git push origin <version>
``` ```
* Add a github "release" and copy release info from CHANGELOG
After release: After release:
* Bump minor version and add a "PRE": * Bump minor version and add a "PRE":

View file

@ -38,26 +38,12 @@ MAINTAINER Olof Hagsand <olof@hagsand.se>
# For clixon and cligen # For clixon and cligen
RUN apk add --update git make build-base gcc flex bison curl-dev RUN apk add --update git make build-base gcc flex bison curl-dev
# evhtp dependencies
RUN apk add --update libevent libevent-dev
# nghttp2 dependencies # nghttp2 dependencies
RUN apk add --update nghttp2 RUN apk add --update nghttp2
# Create a directory to hold source-code, dependencies etc # Create a directory to hold source-code, dependencies etc
RUN mkdir /clixon RUN mkdir /clixon
# clone libevhtp
WORKDIR /clixon
RUN git clone https://github.com/clicon/clixon-libevhtp.git
WORKDIR /clixon/clixon-libevhtp
RUN ./configure
RUN make
RUN mkdir /usr/local/include
RUN make install
RUN mkdir /clixon/build RUN mkdir /clixon/build
WORKDIR /clixon WORKDIR /clixon
@ -79,7 +65,7 @@ WORKDIR /clixon/clixon
COPY clixon . COPY clixon .
# Configure, build and install clixon # Configure, build and install clixon
RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=native --enable-nghttp2 --enable-evhtp RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=native --enable-nghttp2 --enable-http1
RUN make RUN make
RUN make install RUN make install
@ -109,4 +95,3 @@ EXPOSE 443/tcp
RUN adduser -D -H clicon RUN adduser -D -H clicon
COPY --from=0 /clixon/build/ /usr/local/ COPY --from=0 /clixon/build/ /usr/local/
COPY --from=0 /usr/local/lib/libevhtp.so* /usr/local/lib/

View file

@ -38,26 +38,12 @@ MAINTAINER Olof Hagsand <olof@hagsand.se>
# For clixon and cligen # For clixon and cligen
RUN apk add --update git make build-base gcc flex bison curl-dev RUN apk add --update git make build-base gcc flex bison curl-dev
# evhtp dependencies
RUN apk add --update libevent libevent-dev
# nghttp2 dependencies # nghttp2 dependencies
RUN apk add --update nghttp2 RUN apk add --update nghttp2
# Create a directory to hold source-code, dependencies etc # Create a directory to hold source-code, dependencies etc
RUN mkdir /clixon RUN mkdir /clixon
# clone libevhtp
WORKDIR /clixon
RUN git clone https://github.com/clicon/clixon-libevhtp.git
WORKDIR /clixon/clixon-libevhtp
RUN ./configure
RUN make
RUN mkdir /usr/local/include
RUN make install
RUN mkdir /clixon/build RUN mkdir /clixon/build
WORKDIR /clixon WORKDIR /clixon
@ -79,7 +65,7 @@ WORKDIR /clixon/clixon
COPY clixon . COPY clixon .
# Configure, build and install clixon # Configure, build and install clixon
RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=native --enable-nghttp2 --enable-evhtp RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=native --enable-nghttp2 --enable-http1
RUN make RUN make
RUN make install RUN make install

View file

@ -38,9 +38,6 @@ MAINTAINER Olof Hagsand <olof@hagsand.se>
# For clixon and cligen # For clixon and cligen
RUN apk add --update git make build-base gcc flex bison curl-dev RUN apk add --update git make build-base gcc flex bison curl-dev
# evhtp dependencies
RUN apk add --update libevent libevent-dev
# nghttp2 dependencies # nghttp2 dependencies
RUN apk add --update nghttp2 RUN apk add --update nghttp2
@ -62,17 +59,6 @@ RUN git clone https://github.com/openconfig/public
# Create a directory to hold source-code, dependencies etc # Create a directory to hold source-code, dependencies etc
RUN mkdir /clixon RUN mkdir /clixon
# clone libevhtp
WORKDIR /clixon
RUN git clone https://github.com/clicon/clixon-libevhtp.git
WORKDIR /clixon/clixon-libevhtp
RUN ./configure
RUN make
RUN mkdir /usr/local/include
RUN make install
RUN mkdir /clixon/build RUN mkdir /clixon/build
WORKDIR /clixon WORKDIR /clixon
@ -94,7 +80,7 @@ WORKDIR /clixon/clixon
COPY clixon . COPY clixon .
# Configure, build and install clixon # Configure, build and install clixon
RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=native --enable-nghttp2 --enable-evhtp --with-yang-standard-dir=/usr/local/share/yang/standard RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=native --enable-nghttp2 --enable-http1 --with-yang-standard-dir=/usr/local/share/yang/standard
RUN make RUN make
RUN make install RUN make install
@ -148,7 +134,6 @@ EXPOSE 443/tcp
RUN adduser -D -H clicon RUN adduser -D -H clicon
COPY --from=0 /clixon/build/ /usr/local/ COPY --from=0 /clixon/build/ /usr/local/
COPY --from=0 /usr/local/lib/libevhtp.so* /usr/local/lib/
COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/standard/ COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/standard/
COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/experimental/ COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/experimental/
COPY --from=0 /usr/local/share/openconfig/* /usr/local/share/openconfig/ COPY --from=0 /usr/local/share/openconfig/* /usr/local/share/openconfig/

View file

@ -31,7 +31,7 @@
# #
# ***** END LICENSE BLOCK ***** # ***** END LICENSE BLOCK *****
# Clixon startscript for evhtp and https # Clixon startscript for native restconf and https
# This script is copied into the container on build time and runs # This script is copied into the container on build time and runs
# _inside_ the container at start in runtime. It gets environment variables # _inside_ the container at start in runtime. It gets environment variables
# from the start.sh script. # from the start.sh script.

View file

@ -27,9 +27,6 @@
/* Define to 1 if you have the <cligen/cligen.h> header file. */ /* Define to 1 if you have the <cligen/cligen.h> header file. */
#undef HAVE_CLIGEN_CLIGEN_H #undef HAVE_CLIGEN_CLIGEN_H
/* Define to 1 if you have the <evhtp/evhtp.h> header file. */
#undef HAVE_EVHTP_EVHTP_H
/* Define to 1 if you have the `getpeereid' function. */ /* Define to 1 if you have the `getpeereid' function. */
#undef HAVE_GETPEEREID #undef HAVE_GETPEEREID
@ -54,12 +51,6 @@
/* Define to 1 if you have the `dl' library (-ldl). */ /* Define to 1 if you have the `dl' library (-ldl). */
#undef HAVE_LIBDL #undef HAVE_LIBDL
/* Define to 1 if you have the `event' library (-levent). */
#undef HAVE_LIBEVENT
/* Define to 1 if you have the `evhtp' library (-levhtp). */
#undef HAVE_LIBEVHTP
/* Define to 1 if you have the `fcgi' library (-lfcgi). */ /* Define to 1 if you have the `fcgi' library (-lfcgi). */
#undef HAVE_LIBFCGI #undef HAVE_LIBFCGI
@ -69,6 +60,8 @@
/* Define to 1 if you have the `nghttp2' library (-lnghttp2). */ /* Define to 1 if you have the `nghttp2' library (-lnghttp2). */
#undef HAVE_LIBNGHTTP2 #undef HAVE_LIBNGHTTP2
#undef HAVE_HTTP1
/* Define to 1 if you have the `socket' library (-lsocket). */ /* Define to 1 if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET #undef HAVE_LIBSOCKET

View file

@ -89,6 +89,7 @@ static inline char * strdup4(char *str)
*/ */
char **clicon_strsep(char *string, char *delim, int *nvec0); char **clicon_strsep(char *string, char *delim, int *nvec0);
char *clicon_strjoin (int argc, char **argv, char *delim); char *clicon_strjoin (int argc, char **argv, char *delim);
char *clixon_string_del_join(char *str1, char *del, char *str2);
int clixon_strsplit(char *nodeid, const int delim, char **prefix, char **id); int clixon_strsplit(char *nodeid, const int delim, char **prefix, char **id);
int uri_str2cvec(char *string, char delim1, char delim2, int decode, cvec **cvp); int uri_str2cvec(char *string, char delim1, char delim2, int decode, cvec **cvp);
int uri_percent_encode(char **encp, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); int uri_percent_encode(char **encp, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
@ -102,7 +103,8 @@ int clicon_str2int_search(const map_str2int *mstab, char *str, int upper);
int nodeid_split(char *nodeid, char **prefix, char **id); int nodeid_split(char *nodeid, char **prefix, char **id);
char *clixon_trim(char *str); char *clixon_trim(char *str);
char *clixon_trim2(char *str, char *trims); char *clixon_trim2(char *str, char *trims);
int clicon_strcmp(char *s1, char *s2); int clicon_strcmp(char *s1, char *s2);
#ifndef HAVE_STRNDUP #ifndef HAVE_STRNDUP
char *clicon_strndup (const char *, size_t); char *clicon_strndup (const char *, size_t);

View file

@ -82,8 +82,6 @@
/* typecast macro */ /* typecast macro */
#define _AY ((clixon_api_path_yacc *)_ay) #define _AY ((clixon_api_path_yacc *)_ay)
#define MAXBUF 4*4*64*1024
#undef clixon_api_path_parsewrap #undef clixon_api_path_parsewrap
int int
clixon_api_path_parsewrap(void) clixon_api_path_parsewrap(void)

View file

@ -172,6 +172,11 @@ clicon_files_recursive(const char *dir,
* do something with dp[i].d_name; * do something with dp[i].d_name;
* free(dp); * free(dp);
* @endcode * @endcode
* @note "ent" is an array with n fixed entries
* But this is not what is returned from the syscall, see man readdir:
* ... the use sizeof(struct dirent) to capture the size of the record including
* the size of d_name is also incorrect.
* @note May block on file I/O
*/ */
int int
clicon_file_dirent(const char *dir, clicon_file_dirent(const char *dir,
@ -180,7 +185,7 @@ clicon_file_dirent(const char *dir,
mode_t type) mode_t type)
{ {
int retval = -1; int retval = -1;
DIR *dirp; DIR *dirp = NULL;
int res; int res;
int nent; int nent;
regex_t re; regex_t re;
@ -188,11 +193,8 @@ clicon_file_dirent(const char *dir,
char filename[MAXPATHLEN]; char filename[MAXPATHLEN];
struct stat st; struct stat st;
struct dirent *dent; struct dirent *dent;
struct dirent *tmp;
struct dirent *new = NULL; struct dirent *new = NULL;
#if 0 /* revert of https://github.com/clicon/clixon/pull/238 */
int direntStructSize; int direntStructSize;
#endif
*ent = NULL; *ent = NULL;
nent = 0; nent = 0;
@ -225,25 +227,18 @@ clicon_file_dirent(const char *dir,
if ((type & st.st_mode) == 0) if ((type & st.st_mode) == 0)
continue; continue;
} }
#if 0 /* revert of https://github.com/clicon/clixon/pull/238 */
direntStructSize = offsetof(struct dirent, d_name) + strlen(dent->d_name) + 1; direntStructSize = offsetof(struct dirent, d_name) + strlen(dent->d_name) + 1;
clicon_debug(1, "%s %u %u %lu", __FUNCTION__, nent, direntStructSize, sizeof(struct dirent)); if ((new = realloc(new, (nent+1)*sizeof(struct dirent))) == NULL) {
if ((tmp = realloc(new, (nent+1)*direntStructSize)) == NULL) {
#else
if ((tmp = realloc(new, (nent+1)*sizeof(struct dirent))) == NULL) {
#endif
clicon_err(OE_UNIX, errno, "realloc"); clicon_err(OE_UNIX, errno, "realloc");
goto quit; goto quit;
} } /* realloc */
new = tmp;
#if 0 /* revert of https://github.com/clicon/clixon/pull/238 */
clicon_debug(1, "%s memcpy(%p %p %u", __FUNCTION__, &new[nent], dent, direntStructSize); clicon_debug(1, "%s memcpy(%p %p %u", __FUNCTION__, &new[nent], dent, direntStructSize);
memcpy(&new[nent], dent, direntStructSize); /* XXX Invalid write of size 8 */ /* man (3) readdir:
#else * By implication, the use sizeof(struct dirent) to capture the size of the record including
memcpy(&new[nent], dent, sizeof(*dent)); * the size of d_name is also incorrect. */
#endif memset(&new[nent], 0, sizeof(struct dirent));
memcpy(&new[nent], dent, direntStructSize);
nent++; nent++;
} /* while */ } /* while */
qsort((void *)new, nent, sizeof(*new), clicon_file_dirent_sort); qsort((void *)new, nent, sizeof(*new), clicon_file_dirent_sort);

View file

@ -79,8 +79,6 @@
/* typecast macro */ /* typecast macro */
#define _IY ((clixon_instance_id_yacc *)_iy) #define _IY ((clixon_instance_id_yacc *)_iy)
#define MAXBUF 4*4*64*1024
#undef clixon_instance_id_parsewrap #undef clixon_instance_id_parsewrap
int int
clixon_instance_id_parsewrap(void) clixon_instance_id_parsewrap(void)

View file

@ -65,8 +65,6 @@
/* typecast macro */ /* typecast macro */
#define _JY ((clixon_json_yacc *)_yy) #define _JY ((clixon_json_yacc *)_yy)
#define MAXBUF 4*4*64*1024
#undef clixon_json_parsewrap #undef clixon_json_parsewrap
int int
clixon_json_parsewrap(void) clixon_json_parsewrap(void)

View file

@ -149,6 +149,39 @@ clicon_strjoin(int argc,
return str; return str;
} }
/*! Join two string with delimiter.
* @param[in] str1 string 1 (will be freed) (optional)
* @param[in] del delimiter string (not freed) cannot be NULL (but "")
* @param[in] str2 string 2 (will be freed)
* @see clicon_strjoin
*/
char*
clixon_string_del_join(char *str1,
char *del,
char *str2)
{
char *str;
int len;
len = strlen(str2) + 1;
if (str1)
len += strlen(str1);
len += strlen(del);
if ((str = malloc(len)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
return NULL;
}
if (str1){
snprintf(str, len, "%s%s%s", str1, del, str2);
free(str1);
}
else
snprintf(str, len, "%s%s", del, str2);
free(str2);
return str;
}
/*! Split a string once into two parts: prefix and suffix /*! Split a string once into two parts: prefix and suffix
* @param[in] string * @param[in] string
* @param[in] delim * @param[in] delim
@ -564,7 +597,7 @@ xml_chardata_cbuf_append(cbuf *cb,
* @retval -1 error * @retval -1 error
* @code * @code
* cvec *cvv = NULL; * cvec *cvv = NULL;
* if (uri_str2cvec("a=b&c=d", ';', '=', 1, &cvv) < 0) * if (uri_str2cvec("a=b&c=d", '&', '=', 1, &cvv) < 0)
* err; * err;
* @endcode * @endcode
* *
@ -840,6 +873,7 @@ clicon_strcmp(char *s1,
return strcmp(s1, s2); return strcmp(s1, s2);
} }
/*! strndup() for systems without it, such as xBSD /*! strndup() for systems without it, such as xBSD
*/ */
#ifndef HAVE_STRNDUP #ifndef HAVE_STRNDUP
@ -908,4 +942,3 @@ main(int argc, char **argv)
} }
#endif /* Test program */ #endif /* Test program */

View file

@ -640,8 +640,8 @@ clixon_xml_parse_file(FILE *fp,
int oldxmlbuflen; int oldxmlbuflen;
int failed = 0; int failed = 0;
if (xt==NULL){ if (xt==NULL || fp == NULL){
clicon_err(OE_XML, EINVAL, "xt is NULL"); clicon_err(OE_XML, EINVAL, "arg is NULL");
return -1; return -1;
} }
if (yb == YB_MODULE && yspec == NULL){ if (yb == YB_MODULE && yspec == NULL){

View file

@ -69,8 +69,6 @@
/* typecast macro */ /* typecast macro */
#define _XPY ((clixon_xpath_yacc *)_yy) #define _XPY ((clixon_xpath_yacc *)_yy)
#define MAXBUF 4*4*64*1024
#undef clixon_xpath_parsewrap #undef clixon_xpath_parsewrap
int int
clixon_xpath_parsewrap(void) clixon_xpath_parsewrap(void)

View file

@ -35,7 +35,7 @@
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0 * @see https://tools.ietf.org/html/rfc6020 YANG 1.0
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1 * @see https://tools.ietf.org/html/rfc7950 YANG 1.1
* @note differences in double quoted strings: 1.1 does not allow \x, but 1.0 * @note differences in double quoted strings: 1.1 does not allow \x, but 1.0
* does (where x is not n|t|"|\). See mode DQESC * does (where x is not n|t|<double quote)|\). See mode DQESC
*/ */
%{ %{
@ -67,11 +67,6 @@
/* typecast macro */ /* typecast macro */
#define _YY ((clixon_yang_yacc *)_yy) #define _YY ((clixon_yang_yacc *)_yy)
#define MAXBUF 4*4*64*1024
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
#undef clixon_yang_parsewrap #undef clixon_yang_parsewrap
int int
clixon_yang_parsewrap(void) clixon_yang_parsewrap(void)

View file

@ -78,7 +78,6 @@
%type <string> identifier_ref_str %type <string> identifier_ref_str
%type <string> bool_str %type <string> bool_str
/* rfc 6020 keywords /* rfc 6020 keywords
See also enum rfc_6020 in clicon_yang.h. There, the constants have Y_ prefix instead of K_ See also enum rfc_6020 in clicon_yang.h. There, the constants have Y_ prefix instead of K_
* Wanted to unify these (K_ and Y_) but gave up for several reasons: * Wanted to unify these (K_ and Y_) but gave up for several reasons:
@ -154,7 +153,6 @@
%token K_YANG_VERSION %token K_YANG_VERSION
%token K_YIN_ELEMENT %token K_YIN_ELEMENT
/* Deviate keywords /* Deviate keywords
*/ */
%token D_NOT_SUPPORTED %token D_NOT_SUPPORTED
@ -192,6 +190,7 @@
#include <cligen/cligen.h> #include <cligen/cligen.h>
#include "clixon_queue.h" #include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_hash.h" #include "clixon_hash.h"
#include "clixon_handle.h" #include "clixon_handle.h"
#include "clixon_err.h" #include "clixon_err.h"
@ -351,37 +350,6 @@ ysp_add_push(clixon_yang_yacc *yy,
return ys; return ys;
} }
/*! Join two string with delimiter.
* @param[in] str1 string 1 (will be freed) (optional)
* @param[in] del delimiter string (not freed) cannot be NULL (but "")
* @param[in] str2 string 2 (will be freed)
*/
static char*
string_del_join(char *str1,
char *del,
char *str2)
{
char *str;
int len;
len = strlen(str2) + 1;
if (str1)
len += strlen(str1);
len += strlen(del);
if ((str = malloc(len)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
return NULL;
}
if (str1){
snprintf(str, len, "%s%s%s", str1, del, str2);
free(str1);
}
else
snprintf(str, len, "%s%s", del, str2);
free(str2);
return str;
}
%} %}
@ -1640,23 +1608,23 @@ deviate_replace_substmt : type_stmt { _PARSE_DEBUG("deviate-replace-subs
* *
*/ */
unknown_stmt : ustring ':' ustring optsep ';' unknown_stmt : ustring ':' ustring optsep ';'
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt"); { char *id; if ((id=clixon_string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
if (ysp_add(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt"); if (ysp_add(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt");
_PARSE_DEBUG("unknown-stmt -> ustring : ustring ;"); _PARSE_DEBUG("unknown-stmt -> ustring : ustring ;");
} }
| ustring ':' ustring sep string optsep ';' | ustring ':' ustring sep string optsep ';'
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt"); { char *id; if ((id=clixon_string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL){ _YYERROR("unknown_stmt"); } if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL){ _YYERROR("unknown_stmt"); }
_PARSE_DEBUG("unknown-stmt -> ustring : ustring sep string ;"); _PARSE_DEBUG("unknown-stmt -> ustring : ustring sep string ;");
} }
| ustring ':' ustring optsep | ustring ':' ustring optsep
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt"); { char *id; if ((id=clixon_string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
if (ysp_add_push(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt"); } if (ysp_add_push(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt"); }
'{' yang_stmts '}' '{' yang_stmts '}'
{ if (ystack_pop(_yy) < 0) _YYERROR("unknown_stmt"); { if (ystack_pop(_yy) < 0) _YYERROR("unknown_stmt");
_PARSE_DEBUG("unknown-stmt -> ustring : ustring { yang-stmts }"); } _PARSE_DEBUG("unknown-stmt -> ustring : ustring { yang-stmts }"); }
| ustring ':' ustring sep string optsep | ustring ':' ustring sep string optsep
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt"); { char *id; if ((id=clixon_string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
if (ysp_add_push(_yy, Y_UNKNOWN, id, $5) == NULL) _YYERROR("unknown_stmt"); } if (ysp_add_push(_yy, Y_UNKNOWN, id, $5) == NULL) _YYERROR("unknown_stmt"); }
'{' yang_stmts '}' '{' yang_stmts '}'
{ if (ystack_pop(_yy) < 0) _YYERROR("unknown_stmt"); { if (ystack_pop(_yy) < 0) _YYERROR("unknown_stmt");
@ -1850,10 +1818,10 @@ ustring : ustring CHARS
; ;
abs_schema_nodeid : abs_schema_nodeid '/' node_identifier abs_schema_nodeid : abs_schema_nodeid '/' node_identifier
{ if (($$=string_del_join($1, "/", $3)) == NULL) _YYERROR("abs_schema_nodeid"); { if (($$=clixon_string_del_join($1, "/", $3)) == NULL) _YYERROR("abs_schema_nodeid");
_PARSE_DEBUG("absolute-schema-nodeid -> absolute-schema-nodeid / node-identifier"); } _PARSE_DEBUG("absolute-schema-nodeid -> absolute-schema-nodeid / node-identifier"); }
| '/' node_identifier | '/' node_identifier
{ if (($$=string_del_join(NULL, "/", $2)) == NULL) _YYERROR("abs_schema_nodeid"); { if (($$=clixon_string_del_join(NULL, "/", $2)) == NULL) _YYERROR("abs_schema_nodeid");
_PARSE_DEBUG("absolute-schema-nodeid -> / node-identifier"); } _PARSE_DEBUG("absolute-schema-nodeid -> / node-identifier"); }
; ;
@ -1887,7 +1855,7 @@ desc_schema_nodeid : node_identifier
{ $$= $1; { $$= $1;
_PARSE_DEBUG("descendant-schema-nodeid -> node_identifier"); } _PARSE_DEBUG("descendant-schema-nodeid -> node_identifier"); }
| node_identifier abs_schema_nodeid | node_identifier abs_schema_nodeid
{ if (($$=string_del_join($1, "", $2)) == NULL) _YYERROR("desc_schema_nodeid"); { if (($$=clixon_string_del_join($1, "", $2)) == NULL) _YYERROR("desc_schema_nodeid");
_PARSE_DEBUG("descendant-schema-nodeid -> node_identifier abs_schema_nodeid"); } _PARSE_DEBUG("descendant-schema-nodeid -> node_identifier abs_schema_nodeid"); }
; ;
@ -1926,7 +1894,7 @@ node_identifier : IDENTIFIER
{ $$=$1; { $$=$1;
_PARSE_DEBUG("identifier-ref-arg-str -> string"); } _PARSE_DEBUG("identifier-ref-arg-str -> string"); }
| IDENTIFIER ':' IDENTIFIER | IDENTIFIER ':' IDENTIFIER
{ if (($$=string_del_join($1, ":", $3)) == NULL) _YYERROR("node_identifier"); { if (($$=clixon_string_del_join($1, ":", $3)) == NULL) _YYERROR("node_identifier");
_PARSE_DEBUG("identifier-ref-arg-str -> prefix : string"); } _PARSE_DEBUG("identifier-ref-arg-str -> prefix : string"); }
; ;

View file

@ -114,7 +114,7 @@ For example, in FreeBSD, add:
For fcgi/nginx you need to setup https in the nginx config file, independently of clixon. For fcgi/nginx you need to setup https in the nginx config file, independently of clixon.
If you use evhtp with `configure --with-restconf=evhtp`, you can prepend the tests with RCPROTO=https which will run all restconf tests with SSL https and server certs. If you use native with `configure --with-restconf=http1`, you can prepend the tests with RCPROTO=https which will run all restconf tests with SSL https and server certs.
Ensure the server keys are in order, as follows. Ensure the server keys are in order, as follows.

View file

@ -46,7 +46,7 @@ SHELL = /bin/sh
# eg : # eg :
# HOSTS += vandal.hagsand.com # i86_32 ubuntu # HOSTS += vandal.hagsand.com # i86_32 ubuntu
# ... # ...
# You can also set RESTCONF to fcgi or evhtp # You can also set RESTCONF to fcgi or native
RESTCONF=fcgi RESTCONF=fcgi
HOSTS= HOSTS=
-include site.mk -include site.mk

View file

@ -42,12 +42,6 @@ ssh -t $h "test -d src/cligen || (cd src;git clone https://github.com/clicon/cli
ssh -t $h "(cd src/cligen;git pull origin master)" ssh -t $h "(cd src/cligen;git pull origin master)"
ssh -t $h "(cd src/cligen;./configure)" ssh -t $h "(cd src/cligen;./configure)"
ssh -t $h "(cd src/cligen; /tmp/cligen-mk.sh)" ssh -t $h "(cd src/cligen; /tmp/cligen-mk.sh)"
# pull git changes and build clixon-libevhtp
ssh -t $h "test -d src || mkdir src"
ssh -t $h "test -d src/clixon-libevhtp || (cd src;git clone https://github.com/clicon/clixon-libevhtp.git)"
ssh -t $h "(cd src/clixon-libevhtp;git pull origin master)"
ssh -t $h "(cd src/clixon-libevhtp;./configure)"
ssh -t $h "(cd src/clixon-libevhtp; /tmp/cligen-mk.sh)" # re-use cligen
# pull git changes and build clixon # pull git changes and build clixon
ssh -t $h "test -d src/clixon || (cd src;git clone https://github.com/clicon/clixon.git)" ssh -t $h "test -d src/clixon || (cd src;git clone https://github.com/clicon/clixon.git)"
ssh -t $h "(cd src/clixon;git pull origin master)" ssh -t $h "(cd src/clixon;git pull origin master)"

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ***** BEGIN LICENSE BLOCK ***** # ***** BEGIN LICENSE BLOCK *****
# #
# Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC # Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC
# #
# This file is part of CLIXON # This file is part of CLIXON
# #
@ -37,11 +37,11 @@
# This is for RESTCONF. There are three options: # This is for RESTCONF. There are three options:
# --without-restconf No restconf support # --without-restconf No restconf support
# --with-restconf=fcgi FCGI interface for separate web reverse proxy like nginx # --with-restconf=fcgi FCGI interface for separate web reverse proxy like nginx
# --with-restconf=native Integration with embedded web server libevhtp # --with-restconf=native Integration with embedded web server
WITH_RESTCONF=@with_restconf@ # native, fcgi or "" WITH_RESTCONF=@with_restconf@ # native, fcgi or ""
HAVE_LIBNGHTTP2=@HAVE_LIBNGHTTP2@ HAVE_LIBNGHTTP2=@HAVE_LIBNGHTTP2@
HAVE_LIBEVHTP=@HAVE_LIBEVHTP@ HAVE_HTTP1=@HAVE_HTTP1@
# This is for libxml2 XSD regex engine # This is for libxml2 XSD regex engine
# Note this only enables the compiling of the code. In order to actually # Note this only enables the compiling of the code. In order to actually

View file

@ -20,7 +20,7 @@ Make a modification to how Clixon sends internal messages in `include/clixon_cus
Build clixon statically with the afl-clang compiler: Build clixon statically with the afl-clang compiler:
``` ```
CC=/usr/bin/afl-clang-fast LINKAGE=static ./configure --with-restconf=evhtp CC=/usr/bin/afl-clang-fast LINKAGE=static ./configure --with-restconf=native
make clean make clean
make make
sudo make install sudo make install

View file

@ -102,11 +102,11 @@ DEFAULTHELLO="<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello $DEFAULTNS><capab
# Set HTTP version 1.1 or 2 # Set HTTP version 1.1 or 2
if ${HAVE_LIBNGHTTP2}; then if ${HAVE_LIBNGHTTP2}; then
HVER=2 HVER=2
if ${HAVE_LIBEVHTP}; then if ${HAVE_HTTP1}; then
# This is if evhtp is enabled (unset proto=HTTP_2 in restconf_accept_client) # This is if http/1 is enabled (unset proto=HTTP_2 in restconf_accept_client)
CURLOPTS="${CURLOPTS} --http2" CURLOPTS="${CURLOPTS} --http2"
else else
# This is if evhtp is disabled (set proto=HTTP_2 in restconf_accept_client) # This is if http/1 is disabled (set proto=HTTP_2 in restconf_accept_client)
CURLOPTS="${CURLOPTS} --http2-prior-knowledge" CURLOPTS="${CURLOPTS} --http2-prior-knowledge"
fi fi
else else

View file

@ -150,7 +150,7 @@ sed '/Connection:/d' $foutput > $foutput2 && mv $foutput2 $foutput
# Create a file to compare with # Create a file to compare with
if ${HAVE_LIBNGHTTP2}; then if ${HAVE_LIBNGHTTP2}; then
if [ ${HAVE_LIBEVHTP} -a ${RCPROTO} = http ]; then if [ ${HAVE_HTTP1} -a ${RCPROTO} = http ]; then
# Add 101 switch protocols for http 1->2 upgrade # Add 101 switch protocols for http 1->2 upgrade
echo "HTTP/1.1 101 Switching Protocols " > $ftest echo "HTTP/1.1 101 Switching Protocols " > $ftest
echo "Upgrade: h2c " >> $ftest echo "Upgrade: h2c " >> $ftest

View file

@ -164,7 +164,7 @@ function testrun()
fi fi
#------------------------------------------------------- HTTP/1 + HTTP/2 #------------------------------------------------------- HTTP/1 + HTTP/2
if [ ${HAVE_LIBNGHTTP2} = true -a ${HAVE_LIBEVHTP} = true ]; then if [ ${HAVE_LIBNGHTTP2} = true -a ${HAVE_HTTP1} = true ]; then
if [ $proto = http ]; then # No plain http/2 if [ $proto = http ]; then # No plain http/2
HVER=1.1 HVER=1.1
@ -213,7 +213,7 @@ function testrun()
fi fi
#------------------------------------------------------- HTTP/2 ONLY #------------------------------------------------------- HTTP/2 ONLY
elif [ ${HAVE_LIBNGHTTP2} = true -a ${HAVE_LIBEVHTP} = false ]; then elif [ ${HAVE_LIBNGHTTP2} = true -a ${HAVE_HTTP1} = false ]; then
HVER=2 HVER=2
new "wait restconf" new "wait restconf"
@ -539,7 +539,7 @@ function testrun()
# Go thru all combinations of IPv4/IPv6, http/https, local/backend config # Go thru all combinations of IPv4/IPv6, http/https, local/backend config
if [ "${WITH_RESTCONF}" = "fcgi" ]; then if [ "${WITH_RESTCONF}" = "fcgi" ]; then
protos="http" protos="http"
elif ${HAVE_LIBEVHTP}; then elif ${HAVE_HTTP1}; then
protos="http" # No plain http for http/2 only protos="http" # No plain http for http/2 only
fi fi
if [ "${WITH_RESTCONF}" = "native" ]; then if [ "${WITH_RESTCONF}" = "native" ]; then

View file

@ -14,8 +14,8 @@
# Magic line must be first in script (see README.md) # Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
if ! ${HAVE_LIBEVHTP}; then if ! ${HAVE_HTTP1}; then
echo "...skipped: LIBEVHTP is false, must run with http/1 (evhtp)" echo "...skipped: Must run with http/1"
if [ "$s" = $0 ]; then exit 0; else return 0; fi if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi fi

View file

@ -23,9 +23,9 @@
# Magic line must be first in script (see README.md) # Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
# Does not work with evhtpnative http/2-only # Does not work with native http/2-only
if [ "${WITH_RESTCONF}" = "native" -a ${HAVE_LIBEVHTP} = false ]; then if [ "${WITH_RESTCONF}" = "native" -a ${HAVE_HTTP1} = false ]; then
echo "...skipped: LIBEVHTP is false, must run with http/1 (evhtp)" echo "...skipped: must run with http/1"
if [ "$s" = $0 ]; then exit 0; else return 0; fi if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi fi
@ -206,7 +206,7 @@ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPR
if [ false -a ! ${HAVE_LIBNGHTTP2} ] ; then if [ false -a ! ${HAVE_LIBNGHTTP2} ] ; then
# Look for netcat or nc for direct socket http calls # Look for netcat or nc for direct socket http calls
if [ -n "$(type netcat 2> /dev/null)" ]; then if [ -n "$(type netcat 2> /dev/null)" ]; then
netcat="netcat -w 1" # -N works on evhtp but not fcgi netcat="netcat -w 1" # -N does not work on fcgi
elif [ -n "$(type nc 2> /dev/null)" ]; then elif [ -n "$(type nc 2> /dev/null)" ]; then
netcat=nc netcat=nc
else else
@ -241,9 +241,8 @@ Host: localhost
Accept: application/yang-data+xml Accept: application/yang-data+xml
EOF EOF
)" 0 "HTTP/$HVER 405" kalle # nginx uses "method not allowed" evhtp "not allowed" )" 0 "HTTP/$HVER 405" kalle # nginx uses "method not allowed"
# XXX error from evhtp parsing, should pick up error code
new "restconf GET wrong http version raw" new "restconf GET wrong http version raw"
expectpart "$(${netcat} 127.0.0.1 80 <<EOF expectpart "$(${netcat} 127.0.0.1 80 <<EOF
GET /restconf/data/example:a=0 HTTP/a.1 GET /restconf/data/example:a=0 HTTP/a.1

View file

@ -16,8 +16,8 @@ if [ "${WITH_RESTCONF}" != "native" ]; then
fi fi
# Cant make it work in sum.sh... # Cant make it work in sum.sh...
if ! ${HAVE_LIBEVHTP}; then if ! ${HAVE_HTTP1}; then
echo "...skipped: LIBEVHTP is false, must run with http/1 (evhtp)" echo "...skipped: must run with http/1"
if [ "$s" = $0 ]; then exit 0; else return 0; fi if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi fi
@ -85,7 +85,7 @@ function testrun()
if [ ${HAVE_LIBNGHTTP2} = false -a ${HAVE_LIBEVHTP} = true ]; then # http/1 only if [ ${HAVE_LIBNGHTTP2} = false -a ${HAVE_HTTP1} = true ]; then # http/1 only
new "wait restconf" new "wait restconf"
wait_restconf wait_restconf
@ -106,7 +106,7 @@ function testrun()
echo "curl -Ssik --http2-prior-knowledge -X GET http://localhost/.well-known/host-meta" echo "curl -Ssik --http2-prior-knowledge -X GET http://localhost/.well-known/host-meta"
expectpart "$(curl -Ssik --http2-prior-knowledge -X GET http://localhost/.well-known/host-meta 2>&1)" "16 52 55" expectpart "$(curl -Ssik --http2-prior-knowledge -X GET http://localhost/.well-known/host-meta 2>&1)" "16 52 55"
elif [ ${HAVE_LIBNGHTTP2} = true -a ${HAVE_LIBEVHTP} = false ]; then # http/2 only elif [ ${HAVE_LIBNGHTTP2} = true -a ${HAVE_HTTP1} = false ]; then # http/2 only
sleep 2 # Cannot do wait restconf sleep 2 # Cannot do wait restconf
@ -141,7 +141,7 @@ function testrun()
expectpart "$(curl -Ssik --http2-prior-knowledge -X GET http://localhost/.well-known/host-meta 2>&1)" 0 "HTTP/2 405" expectpart "$(curl -Ssik --http2-prior-knowledge -X GET http://localhost/.well-known/host-meta 2>&1)" 0 "HTTP/2 405"
fi fi
elif [ ${HAVE_LIBNGHTTP2} = true -a ${HAVE_LIBEVHTP} = true ]; then # http/1 + http/2 elif [ ${HAVE_LIBNGHTTP2} = true -a ${HAVE_HTTP1} = true ]; then # http/1 + http/2
new "wait restconf" new "wait restconf"
wait_restconf wait_restconf

View file

@ -14,9 +14,9 @@
# Magic line must be first in script (see README.md) # Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
# Does not work with evhtpnative http/2-only # Does not work with native http/2-only
if [ "${WITH_RESTCONF}" = "native" -a ${HAVE_LIBEVHTP} = false ]; then if [ "${WITH_RESTCONF}" = "native" -a ${HAVE_HTTP1} = false ]; then
echo "...skipped: LIBEVHTP is false, must run with http/1 (evhtp)" echo "...skipped: Must run with http/1"
if [ "$s" = $0 ]; then exit 0; else return 0; fi if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi fi

View file

@ -25,9 +25,9 @@
# Magic line must be first in script (see README.md) # Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
# Does not work with evhtpnative http/2-only # Does not work with native http/2-only
if [ "${WITH_RESTCONF}" = "native" -a ${HAVE_LIBEVHTP} = false ]; then if [ "${WITH_RESTCONF}" = "native" -a ${HAVE_HTTP1} = false ]; then
echo "...skipped: LIBEVHTP is false, must run with http/1 (evhtp)" echo "...skipped: Must run with http/1"
if [ "$s" = $0 ]; then exit 0; else return 0; fi if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi fi

View file

@ -16,8 +16,8 @@ if [ "${WITH_RESTCONF}" != "native" ]; then
if [ "$s" = $0 ]; then exit 0; else return 0; fi # skip if [ "$s" = $0 ]; then exit 0; else return 0; fi # skip
fi fi
if ! ${HAVE_LIBEVHTP}; then if ! ${HAVE_HTTP1}; then
echo "...skipped: LIBEVHTP is false, must run with http/1 (evhtp)" echo "...skipped: HAVE_HTTP1 is false, must run with http/1"
if [ "$s" = $0 ]; then exit 0; else return 0; fi if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi fi
@ -197,7 +197,7 @@ new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
# NOTE http/1.1 # NOTE http/1.1
if ${HAVE_LIBEVHTP}; then if ${HAVE_HTTP1}; then
new "restconf http get config on default netns" new "restconf http get config on default netns"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' http://127.0.0.1/restconf/data/clixon-example:table)" 0 "HTTP/1.1 200" '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' http://127.0.0.1/restconf/data/clixon-example:table)" 0 "HTTP/1.1 200" '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>'
fi fi
@ -211,7 +211,7 @@ expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X GET -H 'Accept: applic
new "restconf https/SSL put table b" new "restconf https/SSL put table b"
expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d '<parameter xmlns="urn:example:clixon"><name>b</name><value>99</value></parameter>' https://$vaddr/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 201" expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d '<parameter xmlns="urn:example:clixon"><name>b</name><value>99</value></parameter>' https://$vaddr/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 201"
if ${HAVE_LIBEVHTP}; then if ${HAVE_HTTP1}; then
# NOTE http/1.1 # NOTE http/1.1
new "restconf http get table b on default ns" new "restconf http get table b on default ns"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' http://127.0.0.1/restconf/data/clixon-example:table/parameter=b)" 0 "HTTP/1.1 200" '<parameter xmlns="urn:example:clixon"><name>b</name><value>99</value></parameter>' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' http://127.0.0.1/restconf/data/clixon-example:table/parameter=b)" 0 "HTTP/1.1 200" '<parameter xmlns="urn:example:clixon"><name>b</name><value>99</value></parameter>'

View file

@ -215,7 +215,6 @@ case $release in
;; ;;
native) native)
$sshcmd sudo apt install -y libssl-dev $sshcmd sudo apt install -y libssl-dev
$sshcmd sudo apt install -y libevent-dev # evhtp
$sshcmd sudo apt install -y libnghttp2-dev # nghttp2 $sshcmd sudo apt install -y libnghttp2-dev # nghttp2
;; ;;
esac esac
@ -269,15 +268,6 @@ case ${with_restconf} in
. ./nginx.sh $dir $idfile $port $wwwuser . ./nginx.sh $dir $idfile $port $wwwuser
;; ;;
native) native)
$sshcmd << EOF
test -d src || mkdir src
cd src
test -d clixon-libevhtp || git clone https://github.com/clicon/clixon-libevhtp.git
cd clixon-libevhtp;
./configure --libdir=/usr/lib # otherwise in /usr/local/lib where RH dont look
make
sudo make install
EOF
;; ;;
esac esac

View file

@ -71,7 +71,7 @@ module clixon-restconf {
proxy solution. proxy solution.
That is, a reverse proxy is the HTTP front-end and the restconf daemon listens That is, a reverse proxy is the HTTP front-end and the restconf daemon listens
to a fcgi socket. to a fcgi socket.
The alternative is the internal HTTP solution using evhtp."; The alternative is the internal native HTTP solution.";
} }
feature allow-auth-none { feature allow-auth-none {
@ -183,10 +183,10 @@ module clixon-restconf {
description description
"Path to FastCGI unix socket. Should be specified in webserver "Path to FastCGI unix socket. Should be specified in webserver
Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock
Only if with-restconf=fcgi, NOT evhtp Only if with-restconf=fcgi, NOT native
This replaces CLICON_RESTCONF_PATH option in clixon-config.yang"; This replaces CLICON_RESTCONF_PATH option in clixon-config.yang";
} }
/* Second, evhtp-specific options */ /* Second, local native options */
leaf server-cert-path { leaf server-cert-path {
type string; type string;
description description