nginx-util: add tests, clean up and fix issues 11284/head
authorPeter Stadler <peter.stadler@student.uibk.ac.at>
Tue, 4 Feb 2020 12:10:00 +0000 (13:10 +0100)
committerPeter Stadler <peter.stadler@student.uibk.ac.at>
Sun, 9 Feb 2020 12:02:54 +0000 (13:02 +0100)
Add tests for nginx-ssl-util and nginx-ssl-util-nopcre using (fake)chroot.
Clean the code up making nginx-ssl-util a header file.
Both changes are for better (future) code quality only.

There are minor functional improvements:
* fix compiler error of gcc7 by using std=c++17
* fix error if there is no lan/loopback interface
* notice instead of error message if there is no default server
* add ipv6-prefix-assignment.*.local-address.address for LAN
* add CONFLICTS in Makefile for choosing the right version
* add cast to release of unique_ptr to avoid warning
* add version message to help message

Signed-off-by: Peter Stadler <peter.stadler@student.uibk.ac.at>
net/nginx-util/Makefile
net/nginx-util/src/CMakeLists.txt
net/nginx-util/src/nginx-ssl-util.cpp [deleted file]
net/nginx-util/src/nginx-ssl-util.hpp [new file with mode: 0644]
net/nginx-util/src/nginx-util.cpp
net/nginx-util/src/test-nginx-util-root.sh [new file with mode: 0644]
net/nginx-util/src/test-nginx-util.sh [new file with mode: 0755]
net/nginx-util/src/test-px5g.sh
net/nginx-util/src/ubus-cxx.hpp

index 2224336bcf613efe7d00ae84cc302ee935d02d29..46c98a1d67c9eb17be7b7b832cfde278bf2e8647 100644 (file)
@@ -1,13 +1,17 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=nginx-util
-PKG_VERSION:=1.2
+PKG_VERSION:=1.3
 PKG_RELEASE:=1
+PKG_MAINTAINER:=Peter Stadler <peter.stadler@student.uibk.ac.at>
 
 include $(INCLUDE_DIR)/package.mk
 include $(INCLUDE_DIR)/cmake.mk
 
-define Package/nginx-util
+CMAKE_OPTIONS+= -DUBUS=y
+CMAKE_OPTIONS+= -DVERSION=$(PKG_VERSION)
+
+define Package/nginx-util/default
   SECTION:=net
   CATEGORY:=Network
   SUBMENU:=Web Servers/Proxies
@@ -15,23 +19,29 @@ define Package/nginx-util
   DEPENDS:=+libstdcpp +libubus +libubox +libpthread
 endef
 
+define Package/nginx-util
+  $(Package/nginx-util/default)
+  CONFLICTS:=nginx-ssl-util
+endef
+
 define Package/nginx-ssl-util/default
-  $(Package/nginx-util)
+  $(Package/nginx-util/default)
   TITLE+= and manager of its SSL certificates
   DEPENDS+= +libopenssl
   CONFLICTS:=nginx-util
-  PROVIDES:=nginx-ssl-util
 endef
 
 define Package/nginx-ssl-util
   $(Package/nginx-ssl-util/default)
   TITLE+= (using PCRE)
   DEPENDS+= +libpcre
+  CONFLICTS+= nginx-ssl-util-nopcre
 endef
 
 define Package/nginx-ssl-util-nopcre
   $(Package/nginx-ssl-util/default)
   TITLE+= (using <regex>)
+  CONFLICTS+= nginx-ssl-util
 endef
 
 define Package/nginx-util/description
index df7440d3831c105b269ed4d603565fd7e831b262..af1d67ea1ef1fcbf074e988cadc3933a7a254f6f 100644 (file)
@@ -1,38 +1,59 @@
 cmake_minimum_required(VERSION 2.6)
 
 PROJECT(nginx-util CXX)
+SET(CMAKE_CXX_STANDARD 17)
 
 INCLUDE(CheckFunctionExists)
 
+IF(UBUS)
 FIND_PATH(ubus_include_dir libubus.h)
 FIND_LIBRARY(ubox NAMES ubox)
 FIND_LIBRARY(ubus NAMES ubus)
 INCLUDE_DIRECTORIES(${ubus_include_dir})
+ENDIF()
 
-ADD_DEFINITIONS(-Os -Wall -Werror -Wextra --std=c++2a -g3)
+ADD_DEFINITIONS(-Os -Wall -Werror -Wextra -g3)
 ADD_DEFINITIONS(-Wno-unused-parameter -Wmissing-declarations)
 
 SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
 
-ADD_EXECUTABLE(px5g px5g.cpp)
-TARGET_LINK_LIBRARIES(px5g ssl crypto)
+
+IF(UBUS)
+
+ADD_COMPILE_DEFINITIONS(VERSION=${VERSION})
 
 ADD_EXECUTABLE(nginx-util nginx-util.cpp)
+TARGET_COMPILE_DEFINITIONS(nginx-util PUBLIC -DNO_SSL)
 TARGET_LINK_LIBRARIES(nginx-util ${ubox} ${ubus} pthread)
+INSTALL(TARGETS nginx-util RUNTIME DESTINATION bin)
 
-ADD_EXECUTABLE(nginx-ssl-util nginx-ssl-util.cpp)
+ADD_EXECUTABLE(nginx-ssl-util nginx-util.cpp)
 TARGET_LINK_LIBRARIES(nginx-ssl-util ${ubox} ${ubus} pthread ssl crypto pcre)
+INSTALL(TARGETS nginx-ssl-util RUNTIME DESTINATION bin)
 
-ADD_EXECUTABLE(nginx-ssl-util-nopcre nginx-ssl-util.cpp)
+ADD_EXECUTABLE(nginx-ssl-util-nopcre nginx-util.cpp)
 TARGET_COMPILE_DEFINITIONS(nginx-ssl-util-nopcre PUBLIC -DNO_PCRE)
 TARGET_LINK_LIBRARIES(nginx-ssl-util-nopcre ${ubox} ${ubus} pthread ssl crypto)
+INSTALL(TARGETS nginx-ssl-util-nopcre RUNTIME DESTINATION bin)
+
+ELSE()
+
+CONFIGURE_FILE(test-px5g.sh test-px5g.sh COPYONLY)
+CONFIGURE_FILE(test-nginx-util.sh test-nginx-util.sh COPYONLY)
+CONFIGURE_FILE(test-nginx-util-root.sh test-nginx-util-root.sh COPYONLY)
 
-ADD_EXECUTABLE(nginx-ssl-util-noubus nginx-ssl-util.cpp)
+ADD_EXECUTABLE(px5g px5g.cpp)
+TARGET_LINK_LIBRARIES(px5g ssl crypto)
+INSTALL(TARGETS px5g RUNTIME DESTINATION bin)
+
+ADD_EXECUTABLE(nginx-ssl-util-noubus nginx-util.cpp)
 TARGET_COMPILE_DEFINITIONS(nginx-ssl-util-noubus PUBLIC -DNO_UBUS)
 TARGET_LINK_LIBRARIES(nginx-ssl-util-noubus pthread ssl crypto pcre)
+INSTALL(TARGETS nginx-ssl-util-noubus RUNTIME DESTINATION bin)
 
-INSTALL(TARGETS px5g RUNTIME DESTINATION bin)
-INSTALL(TARGETS nginx-util RUNTIME DESTINATION bin)
-INSTALL(TARGETS nginx-ssl-util RUNTIME DESTINATION bin)
-INSTALL(TARGETS nginx-ssl-util-nopcre RUNTIME DESTINATION bin)
-# INSTALL(TARGETS nginx-ssl-util-noubus RUNTIME DESTINATION bin)
+ADD_EXECUTABLE(nginx-ssl-util-nopcre-noubus nginx-util.cpp)
+TARGET_COMPILE_DEFINITIONS(nginx-ssl-util-nopcre-noubus PUBLIC -DNO_PCRE -DNO_UBUS)
+TARGET_LINK_LIBRARIES(nginx-ssl-util-nopcre-noubus pthread ssl crypto)
+INSTALL(TARGETS nginx-ssl-util-nopcre-noubus RUNTIME DESTINATION bin)
+
+ENDIF()
diff --git a/net/nginx-util/src/nginx-ssl-util.cpp b/net/nginx-util/src/nginx-ssl-util.cpp
deleted file mode 100644 (file)
index 7b268cb..0000000
+++ /dev/null
@@ -1,660 +0,0 @@
-#include <thread>
-
-#ifdef NO_PCRE
-#include <regex>
-namespace rgx = std;
-#else
-#include "regex-pcre.hpp"
-#endif
-
-#include "nginx-util.hpp"
-#include "px5g-openssl.hpp"
-
-
-#ifndef NO_UBUS
-static constexpr auto UBUS_TIMEOUT = 1000;
-#endif
-
-// once a year:
-static constexpr auto CRON_INTERVAL = std::string_view{"3 3 12 12 *"};
-
-static constexpr auto LAN_SSL_LISTEN =
-    std::string_view{"/var/lib/nginx/lan_ssl.listen"};
-
-static constexpr auto LAN_SSL_LISTEN_DEFAULT =
-    std::string_view{"/var/lib/nginx/lan_ssl.listen.default"};
-
-static constexpr auto ADD_SSL_FCT = std::string_view{"add_ssl"};
-
-static constexpr auto SSL_SESSION_CACHE_ARG =
-    [](const std::string_view & /*name*/) -> std::string
-    { return "shared:SSL:32k"; };
-
-static constexpr auto SSL_SESSION_TIMEOUT_ARG = std::string_view{"64m"};
-
-
-using _Line =
-    std::array< std::string (*)(const std::string &, const std::string &), 2 >;
-
-class Line {
-
-private:
-
-    _Line _line;
-
-public:
-
-    explicit Line(const _Line & line) noexcept : _line{line} {}
-
-    template<const _Line & ...xn>
-    static auto build() noexcept -> Line
-    {
-        return Line{_Line{
-            [](const std::string & p, const std::string & b) -> std::string
-            { return (... + xn[0](p, b)); },
-            [](const std::string & p, const std::string & b) -> std::string
-            { return (... + xn[1](p, b)); }
-        }};
-    }
-
-
-    [[nodiscard]] auto STR(const std::string & param, const std::string & begin)
-        const -> std::string
-    { return _line[0](param, begin); }
-
-
-    [[nodiscard]] auto RGX() const -> rgx::regex
-    { return rgx::regex{_line[1]("", "")}; }
-
-};
-
-
-auto get_if_missed(const std::string & conf, const Line & LINE,
-                   const std::string & val,
-                   const std::string & indent="\n    ", bool compare=true)
-    -> std::string;
-
-
-auto delete_if(const std::string & conf, const rgx::regex & rgx,
-               const std::string & val="", bool compare=false)
-    -> std::string;
-
-
-void add_ssl_directives_to(const std::string & name, bool isdefault);
-
-
-void create_ssl_certificate(const std::string & crtpath,
-                            const std::string & keypath,
-                            int days=792);
-
-
-void use_cron_to_recreate_certificate(const std::string & name);
-
-
-void add_ssl_if_needed(const std::string & name);
-
-
-void del_ssl_directives_from(const std::string & name, bool isdefault);
-
-
-void del_ssl(const std::string & name);
-
-
-constexpr auto _begin = _Line{
-    [](const std::string & /*param*/, const std::string & begin) -> std::string
-    { return begin; },
-
-    [](const std::string & /*param*/, const std::string & /*begin*/)
-        -> std::string
-    { return R"([{;](?:\s*#[^\n]*(?=\n))*(\s*))"; }
-};
-
-
-constexpr auto _space = _Line{
-    [](const std::string & /*param*/, const std::string & /*begin*/)
-        -> std::string
-    { return std::string{" "}; },
-
-    [](const std::string & /*param*/, const std::string & /*begin*/)
-        -> std::string
-    { return R"(\s+)"; }
-};
-
-
-constexpr auto _newline = _Line{
-    [](const std::string & /*param*/, const std::string & /*begin*/)
-        -> std::string
-    { return std::string{"\n"}; },
-
-    [](const std::string & /*param*/, const std::string & /*begin*/)
-        -> std::string
-    { return std::string{"\n"}; }
-};
-
-
-constexpr auto _end = _Line{
-    [](const std::string & /*param*/, const std::string & /*begin*/)
-        -> std::string
-    { return std::string{";"}; },
-
-    [](const std::string & /*param*/, const std::string & /*begin*/)
-        -> std::string
-    { return std::string{R"(\s*;(?:[\t ]*#[^\n]*)?)"}; }
-};
-
-
-template<char clim='\0'>
-constexpr auto _capture = _Line{
-    [](const std::string & param, const std::string & /*begin*/) -> std::string
-    { return '\'' + param + '\''; },
-
-    [](const std::string & /*param*/, const std::string & /*begin*/)
-        -> std::string
-    {
-        const auto lim = clim=='\0' ? std::string{"\\s"} : std::string{clim};
-        return std::string{R"(((?:(?:"[^"]*")|(?:[^'")"} +
-            lim + "][^" + lim + "]*)|(?:'[^']*'))+)";
-    }
-};
-
-
-template<const std::string_view & strptr, char clim='\0'>
-constexpr auto _escape = _Line{
-    [](const std::string &  /*param*/, const std::string & /*begin*/)
-        -> std::string
-    {
-        return clim=='\0' ?
-            std::string{strptr.data()} :
-            clim + std::string{strptr.data()} + clim;
-    },
-
-    [](const std::string & /*param*/, const std::string & /*begin*/)
-        -> std::string
-    {
-        std::string ret{};
-        for (char c : strptr) {
-                switch(c) {
-                    case '^': ret += '\\'; [[fallthrough]];
-                    case '_': [[fallthrough]];
-                    case '-': ret += c;
-                    break;
-                    default:
-                        if ((isalpha(c)!=0) || (isdigit(c)!=0)) { ret += c; }
-                        else { ret += std::string{"["}+c+"]"; }
-                }
-        }
-        return "(?:"+ret+"|'"+ret+"'"+"|\""+ret+"\""+")";
-    }
-};
-
-
-constexpr std::string_view _server_name = "server_name";
-
-constexpr std::string_view _include = "include";
-
-constexpr std::string_view _ssl_certificate = "ssl_certificate";
-
-constexpr std::string_view _ssl_certificate_key = "ssl_certificate_key";
-
-constexpr std::string_view _ssl_session_cache = "ssl_session_cache";
-
-constexpr std::string_view _ssl_session_timeout = "ssl_session_timeout";
-
-
-// For a compile time regex lib, this must be fixed, use one of these options:
-// * Hand craft or macro concat them (loosing more or less flexibility).
-// * Use Macro concatenation of __VA_ARGS__ with the help of:
-//   https://p99.gforge.inria.fr/p99-html/group__preprocessor__for.html
-// * Use constexpr---not available for strings or char * for now---look at lib.
-
-static const auto CRON_CMD = Line::build
-    < _space, _escape<NGINX_UTIL>, _space, _escape<ADD_SSL_FCT,'\''>, _space,
-        _capture<>, _newline >();
-
-static const auto NGX_SERVER_NAME =
-    Line::build<_begin, _escape<_server_name>, _space, _capture<';'>, _end>();
-
-static const auto NGX_INCLUDE_LAN_LISTEN = Line::build
-    <_begin, _escape<_include>, _space, _escape<LAN_LISTEN,'\''>, _end>();
-
-static const auto NGX_INCLUDE_LAN_LISTEN_DEFAULT = Line::build
-    < _begin, _escape<_include>, _space,
-        _escape<LAN_LISTEN_DEFAULT, '\''>, _end >();
-
-static const auto NGX_INCLUDE_LAN_SSL_LISTEN = Line::build
-    <_begin, _escape<_include>, _space, _escape<LAN_SSL_LISTEN, '\''>, _end>();
-
-static const auto NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT = Line::build
-    < _begin, _escape<_include>, _space,
-        _escape<LAN_SSL_LISTEN_DEFAULT, '\''>, _end >();
-
-static const auto NGX_SSL_CRT = Line::build
-    <_begin, _escape<_ssl_certificate>, _space, _capture<';'>, _end>();
-
-static const auto NGX_SSL_KEY = Line::build
-    <_begin, _escape<_ssl_certificate_key>, _space, _capture<';'>, _end>();
-
-static const auto NGX_SSL_SESSION_CACHE = Line::build
-    <_begin, _escape<_ssl_session_cache>, _space, _capture<';'>, _end>();
-
-static const auto NGX_SSL_SESSION_TIMEOUT = Line::build
-    <_begin, _escape<_ssl_session_timeout>, _space, _capture<';'>, _end>();
-
-
-auto get_if_missed(const std::string & conf, const Line & LINE,
-                   const std::string & val,
-                   const std::string & indent, bool compare)
-    -> std::string
-{
-    if (!compare || val.empty()) {
-        return rgx::regex_search(conf, LINE.RGX()) ? "" : LINE.STR(val, indent);
-    }
-
-    rgx::smatch match; // assuming last capture has the value!
-
-    for (auto pos = conf.begin();
-         rgx::regex_search(pos, conf.end(), match, LINE.RGX());
-         pos += match.position(0) + match.length(0))
-    {
-        const std::string value = match.str(match.size() - 1);
-
-        if (value==val || value=="'"+val+"'" || value=='"'+val+'"') {
-            return "";
-        }
-    }
-
-    return LINE.STR(val, indent);
-}
-
-
-auto delete_if(const std::string & conf, const rgx::regex & rgx,
-               const std::string & val, const bool compare)
-    -> std::string
-{
-    std::string ret{};
-    auto pos = conf.begin();
-
-    for (rgx::smatch match;
-         rgx::regex_search(pos, conf.end(), match, rgx);
-         pos += match.position(0) + match.length(0))
-    {
-        const std::string value = match.str(match.size() - 1);
-        auto len = match.position(1);
-        if (compare && value!=val && value!="'"+val+"'" && value!='"'+val+'"') {
-            len = match.position(0) + match.length(0);
-        }
-        ret.append(pos, pos + len);
-    }
-
-    ret.append(pos, conf.end());
-    return ret;
-}
-
-
-void add_ssl_directives_to(const std::string & name, const bool isdefault)
-{
-    const std::string prefix = std::string{CONF_DIR} + name;
-
-    std::string conf = read_file(prefix+".conf");
-
-    const std::string & const_conf = conf; // iteration needs const string.
-    rgx::smatch match; // captures str(1)=indentation spaces, str(2)=server name
-    for (auto pos = const_conf.begin();
-        rgx::regex_search(pos, const_conf.end(), match, NGX_SERVER_NAME.RGX());
-        pos += match.position(0) + match.length(0))
-    {
-        if (match.str(2).find(name) == std::string::npos) { continue; }
-
-        const std::string indent = match.str(1);
-
-        std::string adds = isdefault ?
-            get_if_missed(conf, NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT,"",indent) :
-            get_if_missed(conf, NGX_INCLUDE_LAN_SSL_LISTEN, "", indent);
-
-        adds += get_if_missed(conf, NGX_SSL_CRT, prefix+".crt", indent);
-
-        adds += get_if_missed(conf, NGX_SSL_KEY, prefix+".key", indent);
-
-        adds += get_if_missed(conf, NGX_SSL_SESSION_CACHE,
-                              SSL_SESSION_CACHE_ARG(name), indent, false);
-
-        adds += get_if_missed(conf, NGX_SSL_SESSION_TIMEOUT,
-                        std::string{SSL_SESSION_TIMEOUT_ARG}, indent, false);
-
-        if (adds.length() > 0) {
-            pos += match.position(0) + match.length(0);
-
-            conf = std::string(const_conf.begin(), pos) + adds +
-                    std::string(pos, const_conf.end());
-
-            conf = isdefault ?
-                delete_if(conf, NGX_INCLUDE_LAN_LISTEN_DEFAULT.RGX()) :
-                delete_if(conf, NGX_INCLUDE_LAN_LISTEN.RGX());
-
-            write_file(prefix+".conf", conf);
-
-            std::cerr<<"Added SSL directives to "<<prefix<<".conf: ";
-            std::cerr<<adds<<std::endl;
-        }
-
-        return;
-    }
-
-    auto errmsg = std::string{"add_ssl_directives_to error: "};
-    errmsg += "cannot add SSL directives to " + name + ".conf, missing: ";
-    errmsg += NGX_SERVER_NAME.STR(name, "\n    ") + "\n";
-    throw std::runtime_error(errmsg.c_str());
-}
-
-
-template<typename T>
-inline auto num2hex(T bytes) -> std::array<char, 2*sizeof(bytes)+1>
-{
-    constexpr auto n = 2*sizeof(bytes);
-    std::array<char, n+1> str{};
-
-    for (size_t i=0; i<n; ++i) {
-        static const std::array<char, 17> hex{"0123456789ABCDEF"};
-        static constexpr auto get = 0x0fU;
-        str.at(i) = hex.at(bytes & get);
-
-        static constexpr auto move = 4U;
-        bytes >>= move;
-    }
-
-    str[n] = '\0';
-    return str;
-}
-
-
-template<typename T>
-inline auto get_nonce(const T salt=0) -> T
-{
-    T nonce = 0;
-
-    std::ifstream urandom{"/dev/urandom"};
-
-    static constexpr auto move = 6U;
-
-    constexpr size_t steps = (sizeof(nonce)*8 - 1)/move + 1;
-
-    for (size_t i=0; i<steps; ++i) {
-        if (!urandom.good()) { throw std::runtime_error("get_nonce error"); }
-        nonce = (nonce << move) + static_cast<unsigned>(urandom.get());
-    }
-
-    nonce ^= salt;
-
-    return nonce;
-}
-
-
-void create_ssl_certificate(const std::string & crtpath,
-                            const std::string & keypath,
-                            const int days)
-{
-    size_t nonce = 0;
-
-    try { nonce = get_nonce(nonce); }
-
-    catch (...) { // the address of a variable should be random enough:
-        auto addr = &crtpath;
-        auto addrptr = static_cast<const size_t *>(
-                        static_cast<const void *>(&addr) );
-        nonce += *addrptr;
-    }
-
-    auto noncestr = num2hex(nonce);
-
-    const auto tmpcrtpath = crtpath + ".new-" + noncestr.data();
-    const auto tmpkeypath = keypath + ".new-" + noncestr.data();
-
-    try {
-        auto pkey = gen_eckey(NID_secp384r1);
-
-        write_key(pkey, tmpkeypath);
-
-        std::string subject {"/C=ZZ/ST=Somewhere/L=None/CN=OpenWrt/O=OpenWrt"};
-        subject += noncestr.data();
-
-        selfsigned(pkey, days, subject, tmpcrtpath);
-
-        static constexpr auto to_seconds = 24*60*60;
-        static constexpr auto leeway = 42;
-        if (!checkend(tmpcrtpath, days*to_seconds - leeway)) {
-            throw std::runtime_error("bug: created certificate is not valid!!");
-        }
-
-    } catch (...) {
-        std::cerr<<"create_ssl_certificate error: ";
-        std::cerr<<"cannot create selfsigned certificate, ";
-        std::cerr<<"removing temporary files ..."<<std::endl;
-
-        if (remove(tmpcrtpath.c_str())!=0) {
-            auto errmsg = "\t cannot remove "+tmpcrtpath;
-            perror(errmsg.c_str());
-        }
-
-        if (remove(tmpkeypath.c_str())!=0) {
-            auto errmsg = "\t cannot remove "+tmpkeypath;
-            perror(errmsg.c_str());
-        }
-
-        throw;
-    }
-
-    if ( rename(tmpcrtpath.c_str(), crtpath.c_str())!=0 ||
-         rename(tmpkeypath.c_str(), keypath.c_str())!=0 )
-    {
-        auto errmsg = std::string{"create_ssl_certificate warning: "};
-        errmsg += "cannot move "+tmpcrtpath+" to "+crtpath;
-        errmsg += " or "+tmpkeypath+" to "+keypath+", continuing ... ";
-        perror(errmsg.c_str());
-    }
-
-}
-
-
-void use_cron_to_recreate_certificate(const std::string & name)
-{
-    static const char * filename = "/etc/crontabs/root";
-
-    std::string conf{};
-    try { conf = read_file(filename); }
-    catch (const std::ifstream::failure &) { /* is ok if not found, create. */ }
-
-    const std::string add = get_if_missed(conf, CRON_CMD, name);
-
-    if (add.length() > 0) {
-#ifndef NO_UBUS
-        auto service = ubus::call("service","list",UBUS_TIMEOUT).filter("cron");
-
-        if (!service) {
-            std::string errmsg{"use_cron_to_recreate_certificate error: "};
-            errmsg += "Cron unavailable to re-create the ssl certificate for ";
-            errmsg += name + "\n";
-            throw std::runtime_error(errmsg.c_str());
-        } // else active with or without instances:
-#endif
-
-        write_file(filename, std::string{CRON_INTERVAL}+add, std::ios::app);
-
-#ifndef NO_UBUS
-        call("/etc/init.d/cron", "reload");
-#endif
-
-        std::cerr<<"Rebuild the ssl certificate for '";
-        std::cerr<<name<<"' annually with cron."<<std::endl;
-    }
-}
-
-
-void add_ssl_if_needed(const std::string & name)
-{
-    try { add_ssl_directives_to(name, name==LAN_NAME); }
-    catch (...) {
-        std::cerr<<"add_ssl_if_needed error: ";
-        std::cerr<<"cannot add SSL directives to "<<name<<".conf"<<std::endl;
-        throw;
-    }
-
-    const auto crtpath = std::string{CONF_DIR} + name + ".crt";
-    const auto keypath = std::string{CONF_DIR} + name + ".key";
-    constexpr auto remaining_seconds = (365 + 32)*24*60*60;
-    constexpr auto validity_days = 3*(365 + 31);
-
-    bool is_valid = true;
-
-    if (access(keypath.c_str(), R_OK) != 0 ||
-        access(crtpath.c_str(), R_OK) != 0)
-    { is_valid = false; }
-
-    else {
-        try {
-            if (!checkend(crtpath, remaining_seconds)) {
-                is_valid = false;
-            }
-        }
-        catch (...) { // something went wrong, maybe it is in DER format:
-            try {
-                if (!checkend(crtpath, remaining_seconds, false)) {
-                    is_valid = false;
-                }
-            }
-            catch (...) { // it has neither DER nor PEM format, rebuild.
-                is_valid = false;
-            }
-        }
-    }
-
-    if (!is_valid) { create_ssl_certificate(crtpath, keypath, validity_days); }
-
-    try { use_cron_to_recreate_certificate(name); }
-    catch (...) {
-        std::cerr<<"add_ssl_if_needed warning: ";
-        std::cerr<<"cannot use cron to rebuild certificate for "<<name<<"\n";
-    }
-}
-
-
-void del_ssl_directives_from(const std::string & name, const bool isdefault)
-{
-    const std::string prefix = std::string{CONF_DIR} + name;
-
-    std::string conf = read_file(prefix+".conf");
-
-    const std::string & const_conf = conf; // iteration needs const string.
-    rgx::smatch match; // captures str(1)=indentation spaces, str(2)=server name
-    for (auto pos = const_conf.begin();
-        rgx::regex_search(pos, const_conf.end(), match, NGX_SERVER_NAME.RGX());
-        pos += match.position(0) + match.length(0))
-    {
-        if (match.str(2).find(name) == std::string::npos) { continue; }
-
-        const std::string indent = match.str(1);
-
-        std::string adds = isdefault ?
-            get_if_missed(conf, NGX_INCLUDE_LAN_LISTEN_DEFAULT,"",indent) :
-            get_if_missed(conf, NGX_INCLUDE_LAN_LISTEN, "", indent);
-
-        if (adds.length() > 0) {
-            pos += match.position(1);
-
-            conf = std::string(const_conf.begin(), pos) + adds
-                    + std::string(pos, const_conf.end());
-
-            conf = isdefault ?
-                delete_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT.RGX())
-                : delete_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN.RGX());
-
-            const auto crtpath = prefix+".crt";
-            conf = delete_if(conf, NGX_SSL_CRT.RGX(), crtpath, true);
-
-            const auto keypath = prefix+".key";
-            conf = delete_if(conf, NGX_SSL_KEY.RGX(), keypath, true);
-
-            conf = delete_if(conf, NGX_SSL_SESSION_CACHE.RGX());
-
-            conf = delete_if(conf, NGX_SSL_SESSION_TIMEOUT.RGX());
-
-            write_file(prefix+".conf", conf);
-
-            std::cerr<<"Deleted SSL directives from "<<prefix<<".conf\n";
-        }
-
-        return;
-    }
-
-    auto errmsg = std::string{"del_ssl_directives_from error: "};
-    errmsg += "cannot delete SSL directives from " + name + ".conf, missing: ";
-    errmsg += NGX_SERVER_NAME.STR(name, "\n    ") + "\n";
-    throw std::runtime_error(errmsg.c_str());
-}
-
-
-void del_ssl(const std::string & name)
-{
-    static const char * filename = "/etc/crontabs/root";
-
-    try {
-        const auto const_conf = read_file(filename);
-
-        bool changed = false;
-        auto conf = std::string{};
-
-        size_t prev = 0;
-        size_t curr = 0;
-        while ((curr=const_conf.find('\n', prev)) != std::string::npos) {
-
-            auto line = const_conf.substr(prev, curr-prev+1);
-
-            if (line==delete_if(line,CRON_CMD.RGX(),std::string{name},true)) {
-                conf += line;
-            } else { changed = true; }
-
-            prev = curr + 1;
-        }
-
-        if (changed) {
-            write_file(filename, conf);
-
-            std::cerr<<"Do not rebuild the ssl certificate for '";
-            std::cerr<<name<<"' annually with cron anymore."<<std::endl;
-
-#ifndef NO_UBUS
-            if (ubus::call("service", "list", UBUS_TIMEOUT).filter("cron"))
-            { call("/etc/init.d/cron", "reload"); }
-#endif
-        }
-
-    } catch (...) {
-        std::cerr<<"del_ssl warning: ";
-        std::cerr<<"cannot delete cron job for "<<name<<" in "<<filename<<"\n";
-    }
-
-    try { del_ssl_directives_from(name, name==LAN_NAME); }
-    catch (...) {
-        std::cerr<<"del_ssl error: ";
-        std::cerr<<"cannot delete SSL directives from "<<name<<".conf\n";
-        throw;
-    }
-
-    const auto crtpath = std::string{CONF_DIR} + name + ".crt";
-
-    if (remove(crtpath.c_str())!=0) {
-        auto errmsg = "del_ssl warning: cannot remove "+crtpath;
-        perror(errmsg.c_str());
-    }
-
-    const auto keypath = std::string{CONF_DIR} + name + ".key";
-
-    if (remove(keypath.c_str())!=0) {
-        auto errmsg = "del_ssl warning: cannot remove "+keypath;
-        perror(errmsg.c_str());
-    }
-}
-
-
-// reuse main(...) and common functions:
-#define NGINX_OPENSSL
-#include "nginx-util.cpp"
diff --git a/net/nginx-util/src/nginx-ssl-util.hpp b/net/nginx-util/src/nginx-ssl-util.hpp
new file mode 100644 (file)
index 0000000..0fa7009
--- /dev/null
@@ -0,0 +1,656 @@
+#ifndef __NGINX_SSL_UTIL_HPP
+#define __NGINX_SSL_UTIL_HPP
+
+#include <thread>
+
+#ifdef NO_PCRE
+#include <regex>
+namespace rgx = std;
+#else
+#include "regex-pcre.hpp"
+#endif
+
+#include "nginx-util.hpp"
+#include "px5g-openssl.hpp"
+
+
+#ifndef NO_UBUS
+static constexpr auto UBUS_TIMEOUT = 1000;
+#endif
+
+// once a year:
+static constexpr auto CRON_INTERVAL = std::string_view{"3 3 12 12 *"};
+
+static constexpr auto LAN_SSL_LISTEN =
+    std::string_view{"/var/lib/nginx/lan_ssl.listen"};
+
+static constexpr auto LAN_SSL_LISTEN_DEFAULT =
+    std::string_view{"/var/lib/nginx/lan_ssl.listen.default"};
+
+static constexpr auto ADD_SSL_FCT = std::string_view{"add_ssl"};
+
+static constexpr auto SSL_SESSION_CACHE_ARG =
+    [](const std::string_view & /*name*/) -> std::string
+    { return "shared:SSL:32k"; };
+
+static constexpr auto SSL_SESSION_TIMEOUT_ARG = std::string_view{"64m"};
+
+
+using _Line =
+    std::array< std::string (*)(const std::string &, const std::string &), 2 >;
+
+class Line {
+
+private:
+
+    _Line _line;
+
+public:
+
+    explicit Line(const _Line & line) noexcept : _line{line} {}
+
+    template<const _Line & ...xn>
+    static auto build() noexcept -> Line
+    {
+        return Line{_Line{
+            [](const std::string & p, const std::string & b) -> std::string
+            { return (... + xn[0](p, b)); },
+            [](const std::string & p, const std::string & b) -> std::string
+            { return (... + xn[1](p, b)); }
+        }};
+    }
+
+
+    [[nodiscard]] auto STR(const std::string & param, const std::string & begin)
+        const -> std::string
+    { return _line[0](param, begin); }
+
+
+    [[nodiscard]] auto RGX() const -> rgx::regex
+    { return rgx::regex{_line[1]("", "")}; }
+
+};
+
+
+auto get_if_missed(const std::string & conf, const Line & LINE,
+                   const std::string & val,
+                   const std::string & indent="\n    ", bool compare=true)
+    -> std::string;
+
+
+auto delete_if(const std::string & conf, const rgx::regex & rgx,
+               const std::string & val="", bool compare=false)
+    -> std::string;
+
+
+void add_ssl_directives_to(const std::string & name, bool isdefault);
+
+
+void create_ssl_certificate(const std::string & crtpath,
+                            const std::string & keypath,
+                            int days=792);
+
+
+void use_cron_to_recreate_certificate(const std::string & name);
+
+
+void add_ssl_if_needed(const std::string & name);
+
+
+void del_ssl_directives_from(const std::string & name, bool isdefault);
+
+
+void del_ssl(const std::string & name);
+
+
+constexpr auto _begin = _Line{
+    [](const std::string & /*param*/, const std::string & begin) -> std::string
+    { return begin; },
+
+    [](const std::string & /*param*/, const std::string & /*begin*/)
+        -> std::string
+    { return R"([{;](?:\s*#[^\n]*(?=\n))*(\s*))"; }
+};
+
+
+constexpr auto _space = _Line{
+    [](const std::string & /*param*/, const std::string & /*begin*/)
+        -> std::string
+    { return std::string{" "}; },
+
+    [](const std::string & /*param*/, const std::string & /*begin*/)
+        -> std::string
+    { return R"(\s+)"; }
+};
+
+
+constexpr auto _newline = _Line{
+    [](const std::string & /*param*/, const std::string & /*begin*/)
+        -> std::string
+    { return std::string{"\n"}; },
+
+    [](const std::string & /*param*/, const std::string & /*begin*/)
+        -> std::string
+    { return std::string{"\n"}; }
+};
+
+
+constexpr auto _end = _Line{
+    [](const std::string & /*param*/, const std::string & /*begin*/)
+        -> std::string
+    { return std::string{";"}; },
+
+    [](const std::string & /*param*/, const std::string & /*begin*/)
+        -> std::string
+    { return std::string{R"(\s*;(?:[\t ]*#[^\n]*)?)"}; }
+};
+
+
+template<char clim='\0'>
+constexpr auto _capture = _Line{
+    [](const std::string & param, const std::string & /*begin*/) -> std::string
+    { return '\'' + param + '\''; },
+
+    [](const std::string & /*param*/, const std::string & /*begin*/)
+        -> std::string
+    {
+        const auto lim = clim=='\0' ? std::string{"\\s"} : std::string{clim};
+        return std::string{R"(((?:(?:"[^"]*")|(?:[^'")"} +
+            lim + "][^" + lim + "]*)|(?:'[^']*'))+)";
+    }
+};
+
+
+template<const std::string_view & strptr, char clim='\0'>
+constexpr auto _escape = _Line{
+    [](const std::string &  /*param*/, const std::string & /*begin*/)
+        -> std::string
+    {
+        return clim=='\0' ?
+            std::string{strptr.data()} :
+            clim + std::string{strptr.data()} + clim;
+    },
+
+    [](const std::string & /*param*/, const std::string & /*begin*/)
+        -> std::string
+    {
+        std::string ret{};
+        for (char c : strptr) {
+                switch(c) {
+                    case '^': ret += '\\'; [[fallthrough]];
+                    case '_': [[fallthrough]];
+                    case '-': ret += c;
+                    break;
+                    default:
+                        if ((isalpha(c)!=0) || (isdigit(c)!=0)) { ret += c; }
+                        else { ret += std::string{"["}+c+"]"; }
+                }
+        }
+        return "(?:"+ret+"|'"+ret+"'"+"|\""+ret+"\""+")";
+    }
+};
+
+
+constexpr std::string_view _server_name = "server_name";
+
+constexpr std::string_view _include = "include";
+
+constexpr std::string_view _ssl_certificate = "ssl_certificate";
+
+constexpr std::string_view _ssl_certificate_key = "ssl_certificate_key";
+
+constexpr std::string_view _ssl_session_cache = "ssl_session_cache";
+
+constexpr std::string_view _ssl_session_timeout = "ssl_session_timeout";
+
+
+// For a compile time regex lib, this must be fixed, use one of these options:
+// * Hand craft or macro concat them (loosing more or less flexibility).
+// * Use Macro concatenation of __VA_ARGS__ with the help of:
+//   https://p99.gforge.inria.fr/p99-html/group__preprocessor__for.html
+// * Use constexpr---not available for strings or char * for now---look at lib.
+
+static const auto CRON_CMD = Line::build
+    < _space, _escape<NGINX_UTIL>, _space, _escape<ADD_SSL_FCT,'\''>, _space,
+        _capture<>, _newline >();
+
+static const auto NGX_SERVER_NAME =
+    Line::build<_begin, _escape<_server_name>, _space, _capture<';'>, _end>();
+
+static const auto NGX_INCLUDE_LAN_LISTEN = Line::build
+    <_begin, _escape<_include>, _space, _escape<LAN_LISTEN,'\''>, _end>();
+
+static const auto NGX_INCLUDE_LAN_LISTEN_DEFAULT = Line::build
+    < _begin, _escape<_include>, _space,
+        _escape<LAN_LISTEN_DEFAULT, '\''>, _end >();
+
+static const auto NGX_INCLUDE_LAN_SSL_LISTEN = Line::build
+    <_begin, _escape<_include>, _space, _escape<LAN_SSL_LISTEN, '\''>, _end>();
+
+static const auto NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT = Line::build
+    < _begin, _escape<_include>, _space,
+        _escape<LAN_SSL_LISTEN_DEFAULT, '\''>, _end >();
+
+static const auto NGX_SSL_CRT = Line::build
+    <_begin, _escape<_ssl_certificate>, _space, _capture<';'>, _end>();
+
+static const auto NGX_SSL_KEY = Line::build
+    <_begin, _escape<_ssl_certificate_key>, _space, _capture<';'>, _end>();
+
+static const auto NGX_SSL_SESSION_CACHE = Line::build
+    <_begin, _escape<_ssl_session_cache>, _space, _capture<';'>, _end>();
+
+static const auto NGX_SSL_SESSION_TIMEOUT = Line::build
+    <_begin, _escape<_ssl_session_timeout>, _space, _capture<';'>, _end>();
+
+
+auto get_if_missed(const std::string & conf, const Line & LINE,
+                   const std::string & val,
+                   const std::string & indent, bool compare)
+    -> std::string
+{
+    if (!compare || val.empty()) {
+        return rgx::regex_search(conf, LINE.RGX()) ? "" : LINE.STR(val, indent);
+    }
+
+    rgx::smatch match; // assuming last capture has the value!
+
+    for (auto pos = conf.begin();
+         rgx::regex_search(pos, conf.end(), match, LINE.RGX());
+         pos += match.position(0) + match.length(0))
+    {
+        const std::string value = match.str(match.size() - 1);
+
+        if (value==val || value=="'"+val+"'" || value=='"'+val+'"') {
+            return "";
+        }
+    }
+
+    return LINE.STR(val, indent);
+}
+
+
+auto delete_if(const std::string & conf, const rgx::regex & rgx,
+               const std::string & val, const bool compare)
+    -> std::string
+{
+    std::string ret{};
+    auto pos = conf.begin();
+
+    for (rgx::smatch match;
+         rgx::regex_search(pos, conf.end(), match, rgx);
+         pos += match.position(0) + match.length(0))
+    {
+        const std::string value = match.str(match.size() - 1);
+        auto len = match.position(1);
+        if (compare && value!=val && value!="'"+val+"'" && value!='"'+val+'"') {
+            len = match.position(0) + match.length(0);
+        }
+        ret.append(pos, pos + len);
+    }
+
+    ret.append(pos, conf.end());
+    return ret;
+}
+
+
+void add_ssl_directives_to(const std::string & name, const bool isdefault)
+{
+    const std::string prefix = std::string{CONF_DIR} + name;
+
+    std::string conf = read_file(prefix+".conf");
+
+    const std::string & const_conf = conf; // iteration needs const string.
+    rgx::smatch match; // captures str(1)=indentation spaces, str(2)=server name
+    for (auto pos = const_conf.begin();
+        rgx::regex_search(pos, const_conf.end(), match, NGX_SERVER_NAME.RGX());
+        pos += match.position(0) + match.length(0))
+    {
+        if (match.str(2).find(name) == std::string::npos) { continue; }
+
+        const std::string indent = match.str(1);
+
+        std::string adds = isdefault ?
+            get_if_missed(conf, NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT,"",indent) :
+            get_if_missed(conf, NGX_INCLUDE_LAN_SSL_LISTEN, "", indent);
+
+        adds += get_if_missed(conf, NGX_SSL_CRT, prefix+".crt", indent);
+
+        adds += get_if_missed(conf, NGX_SSL_KEY, prefix+".key", indent);
+
+        adds += get_if_missed(conf, NGX_SSL_SESSION_CACHE,
+                              SSL_SESSION_CACHE_ARG(name), indent, false);
+
+        adds += get_if_missed(conf, NGX_SSL_SESSION_TIMEOUT,
+                        std::string{SSL_SESSION_TIMEOUT_ARG}, indent, false);
+
+        if (adds.length() > 0) {
+            pos += match.position(0) + match.length(0);
+
+            conf = std::string(const_conf.begin(), pos) + adds +
+                    std::string(pos, const_conf.end());
+
+            conf = isdefault ?
+                delete_if(conf, NGX_INCLUDE_LAN_LISTEN_DEFAULT.RGX()) :
+                delete_if(conf, NGX_INCLUDE_LAN_LISTEN.RGX());
+
+            write_file(prefix+".conf", conf);
+
+            std::cerr<<"Added SSL directives to "<<prefix<<".conf: ";
+            std::cerr<<adds<<std::endl;
+        }
+
+        return;
+    }
+
+    auto errmsg = std::string{"add_ssl_directives_to error: "};
+    errmsg += "cannot add SSL directives to " + name + ".conf, missing: ";
+    errmsg += NGX_SERVER_NAME.STR(name, "\n    ") + "\n";
+    throw std::runtime_error(errmsg.c_str());
+}
+
+
+template<typename T>
+inline auto num2hex(T bytes) -> std::array<char, 2*sizeof(bytes)+1>
+{
+    constexpr auto n = 2*sizeof(bytes);
+    std::array<char, n+1> str{};
+
+    for (size_t i=0; i<n; ++i) {
+        static const std::array<char, 17> hex{"0123456789ABCDEF"};
+        static constexpr auto get = 0x0fU;
+        str.at(i) = hex.at(bytes & get);
+
+        static constexpr auto move = 4U;
+        bytes >>= move;
+    }
+
+    str[n] = '\0';
+    return str;
+}
+
+
+template<typename T>
+inline auto get_nonce(const T salt=0) -> T
+{
+    T nonce = 0;
+
+    std::ifstream urandom{"/dev/urandom"};
+
+    static constexpr auto move = 6U;
+
+    constexpr size_t steps = (sizeof(nonce)*8 - 1)/move + 1;
+
+    for (size_t i=0; i<steps; ++i) {
+        if (!urandom.good()) { throw std::runtime_error("get_nonce error"); }
+        nonce = (nonce << move) + static_cast<unsigned>(urandom.get());
+    }
+
+    nonce ^= salt;
+
+    return nonce;
+}
+
+
+void create_ssl_certificate(const std::string & crtpath,
+                            const std::string & keypath,
+                            const int days)
+{
+    size_t nonce = 0;
+
+    try { nonce = get_nonce(nonce); }
+
+    catch (...) { // the address of a variable should be random enough:
+        auto addr = &crtpath;
+        auto addrptr = static_cast<const size_t *>(
+                        static_cast<const void *>(&addr) );
+        nonce += *addrptr;
+    }
+
+    auto noncestr = num2hex(nonce);
+
+    const auto tmpcrtpath = crtpath + ".new-" + noncestr.data();
+    const auto tmpkeypath = keypath + ".new-" + noncestr.data();
+
+    try {
+        auto pkey = gen_eckey(NID_secp384r1);
+
+        write_key(pkey, tmpkeypath);
+
+        std::string subject {"/C=ZZ/ST=Somewhere/L=None/CN=OpenWrt/O=OpenWrt"};
+        subject += noncestr.data();
+
+        selfsigned(pkey, days, subject, tmpcrtpath);
+
+        static constexpr auto to_seconds = 24*60*60;
+        static constexpr auto leeway = 42;
+        if (!checkend(tmpcrtpath, days*to_seconds - leeway)) {
+            throw std::runtime_error("bug: created certificate is not valid!!");
+        }
+
+    } catch (...) {
+        std::cerr<<"create_ssl_certificate error: ";
+        std::cerr<<"cannot create selfsigned certificate, ";
+        std::cerr<<"removing temporary files ..."<<std::endl;
+
+        if (remove(tmpcrtpath.c_str())!=0) {
+            auto errmsg = "\t cannot remove "+tmpcrtpath;
+            perror(errmsg.c_str());
+        }
+
+        if (remove(tmpkeypath.c_str())!=0) {
+            auto errmsg = "\t cannot remove "+tmpkeypath;
+            perror(errmsg.c_str());
+        }
+
+        throw;
+    }
+
+    if ( rename(tmpcrtpath.c_str(), crtpath.c_str())!=0 ||
+         rename(tmpkeypath.c_str(), keypath.c_str())!=0 )
+    {
+        auto errmsg = std::string{"create_ssl_certificate warning: "};
+        errmsg += "cannot move "+tmpcrtpath+" to "+crtpath;
+        errmsg += " or "+tmpkeypath+" to "+keypath+", continuing ... ";
+        perror(errmsg.c_str());
+    }
+
+}
+
+
+void use_cron_to_recreate_certificate(const std::string & name)
+{
+    static const char * filename = "/etc/crontabs/root";
+
+    std::string conf{};
+    try { conf = read_file(filename); }
+    catch (const std::ifstream::failure &) { /* is ok if not found, create. */ }
+
+    const std::string add = get_if_missed(conf, CRON_CMD, name);
+
+    if (add.length() > 0) {
+#ifndef NO_UBUS
+        auto service = ubus::call("service","list",UBUS_TIMEOUT).filter("cron");
+
+        if (!service) {
+            std::string errmsg{"use_cron_to_recreate_certificate error: "};
+            errmsg += "Cron unavailable to re-create the ssl certificate for ";
+            errmsg += name + "\n";
+            throw std::runtime_error(errmsg.c_str());
+        } // else active with or without instances:
+#endif
+
+        write_file(filename, std::string{CRON_INTERVAL}+add, std::ios::app);
+
+#ifndef NO_UBUS
+        call("/etc/init.d/cron", "reload");
+#endif
+
+        std::cerr<<"Rebuild the ssl certificate for '";
+        std::cerr<<name<<"' annually with cron."<<std::endl;
+    }
+}
+
+
+void add_ssl_if_needed(const std::string & name)
+{
+    add_ssl_directives_to(name, name==LAN_NAME); // let it throw.
+
+    const auto crtpath = std::string{CONF_DIR} + name + ".crt";
+    const auto keypath = std::string{CONF_DIR} + name + ".key";
+    constexpr auto remaining_seconds = (365 + 32)*24*60*60;
+    constexpr auto validity_days = 3*(365 + 31);
+
+    bool is_valid = true;
+
+    if (access(keypath.c_str(), R_OK) != 0 ||
+        access(crtpath.c_str(), R_OK) != 0)
+    { is_valid = false; }
+
+    else {
+        try {
+            if (!checkend(crtpath, remaining_seconds)) {
+                is_valid = false;
+            }
+        }
+        catch (...) { // something went wrong, maybe it is in DER format:
+            try {
+                if (!checkend(crtpath, remaining_seconds, false)) {
+                    is_valid = false;
+                }
+            }
+            catch (...) { // it has neither DER nor PEM format, rebuild.
+                is_valid = false;
+            }
+        }
+    }
+
+    if (!is_valid) { create_ssl_certificate(crtpath, keypath, validity_days); }
+
+    try { use_cron_to_recreate_certificate(name); }
+    catch (...) {
+        std::cerr<<"add_ssl_if_needed warning: ";
+        std::cerr<<"cannot use cron to rebuild certificate for "<<name<<"\n";
+    }
+}
+
+
+void del_ssl_directives_from(const std::string & name, const bool isdefault)
+{
+    const std::string prefix = std::string{CONF_DIR} + name;
+
+    std::string conf = read_file(prefix+".conf");
+
+    const std::string & const_conf = conf; // iteration needs const string.
+    rgx::smatch match; // captures str(1)=indentation spaces, str(2)=server name
+    for (auto pos = const_conf.begin();
+        rgx::regex_search(pos, const_conf.end(), match, NGX_SERVER_NAME.RGX());
+        pos += match.position(0) + match.length(0))
+    {
+        if (match.str(2).find(name) == std::string::npos) { continue; }
+
+        const std::string indent = match.str(1);
+
+        std::string adds = isdefault ?
+            get_if_missed(conf, NGX_INCLUDE_LAN_LISTEN_DEFAULT,"",indent) :
+            get_if_missed(conf, NGX_INCLUDE_LAN_LISTEN, "", indent);
+
+        if (adds.length() > 0) {
+            pos += match.position(1);
+
+            conf = std::string(const_conf.begin(), pos) + adds
+                    + std::string(pos, const_conf.end());
+
+            conf = isdefault ?
+                delete_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT.RGX())
+                : delete_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN.RGX());
+
+            const auto crtpath = prefix+".crt";
+            conf = delete_if(conf, NGX_SSL_CRT.RGX(), crtpath, true);
+
+            const auto keypath = prefix+".key";
+            conf = delete_if(conf, NGX_SSL_KEY.RGX(), keypath, true);
+
+            conf = delete_if(conf, NGX_SSL_SESSION_CACHE.RGX());
+
+            conf = delete_if(conf, NGX_SSL_SESSION_TIMEOUT.RGX());
+
+            write_file(prefix+".conf", conf);
+
+            std::cerr<<"Deleted SSL directives from "<<prefix<<".conf\n";
+        }
+
+        return;
+    }
+
+    auto errmsg = std::string{"del_ssl_directives_from error: "};
+    errmsg += "cannot delete SSL directives from " + name + ".conf, missing: ";
+    errmsg += NGX_SERVER_NAME.STR(name, "\n    ") + "\n";
+    throw std::runtime_error(errmsg.c_str());
+}
+
+
+void del_ssl(const std::string & name)
+{
+    static const char * filename = "/etc/crontabs/root";
+
+    try {
+        const auto const_conf = read_file(filename);
+
+        bool changed = false;
+        auto conf = std::string{};
+
+        size_t prev = 0;
+        size_t curr = 0;
+        while ((curr=const_conf.find('\n', prev)) != std::string::npos) {
+
+            auto line = const_conf.substr(prev, curr-prev+1);
+
+            if (line==delete_if(line,CRON_CMD.RGX(),std::string{name},true)) {
+                conf += line;
+            } else { changed = true; }
+
+            prev = curr + 1;
+        }
+
+        if (changed) {
+            write_file(filename, conf);
+
+            std::cerr<<"Do not rebuild the ssl certificate for '";
+            std::cerr<<name<<"' annually with cron anymore."<<std::endl;
+
+#ifndef NO_UBUS
+            if (ubus::call("service", "list", UBUS_TIMEOUT).filter("cron"))
+            { call("/etc/init.d/cron", "reload"); }
+#endif
+        }
+
+    } catch (...) {
+        std::cerr<<"del_ssl warning: ";
+        std::cerr<<"cannot delete cron job for "<<name<<" in "<<filename<<"\n";
+    }
+
+    try { del_ssl_directives_from(name, name==LAN_NAME); }
+    catch (...) {
+        std::cerr<<"del_ssl error: ";
+        std::cerr<<"cannot delete SSL directives from "<<name<<".conf\n";
+        throw;
+    }
+
+    const auto crtpath = std::string{CONF_DIR} + name + ".crt";
+
+    if (remove(crtpath.c_str())!=0) {
+        auto errmsg = "del_ssl warning: cannot remove "+crtpath;
+        perror(errmsg.c_str());
+    }
+
+    const auto keypath = std::string{CONF_DIR} + name + ".key";
+
+    if (remove(keypath.c_str())!=0) {
+        auto errmsg = "del_ssl warning: cannot remove "+keypath;
+        perror(errmsg.c_str());
+    }
+}
+
+
+#endif
index acce1e7231c7107239f68e86448e8235dd4363db..02aa7013ac21d8e9044a18ade93c9f6745759657 100644 (file)
@@ -1,9 +1,9 @@
-// This file is included in nginx-ssl-util.cpp, which defines NGINX_OPENSSL.
-#ifndef __NGINX_UTIL_C
-#define __NGINX_UTIL_C
-
 #include "nginx-util.hpp"
 
+#ifndef NO_SSL
+#include "nginx-ssl-util.hpp"
+#endif
+
 
 void create_lan_listen()
 {
@@ -13,9 +13,8 @@ void create_lan_listen()
     std::string ssl_listen = listen;
     std::string ssl_listen_default = listen;
 
-#ifndef NO_UBUS
     auto add_listen = [&listen, &listen_default
-#ifdef NGINX_OPENSSL
+#ifndef NO_SSL
                        ,&ssl_listen, &ssl_listen_default
 #endif
                       ]
@@ -26,36 +25,49 @@ void create_lan_listen()
         const std::string val = pre + ip + suf;
         listen += "\tlisten " + val + ":80;\n";
         listen_default += "\tlisten " + val + ":80 default_server;\n";
-#ifdef NGINX_OPENSSL
+#ifndef NO_SSL
         ssl_listen += "\tlisten " + val + ":443 ssl;\n";
         ssl_listen_default += "\tlisten " + val + ":443 ssl default_server;\n";
 #endif
     };
 
-    auto loopback_status = ubus::call("network.interface.loopback", "status");
+#ifndef NO_UBUS
+    try {
+        auto loopback_status=ubus::call("network.interface.loopback", "status");
 
-    for (auto ip : loopback_status.filter("ipv4-address", "", "address")) {
-        add_listen("",  static_cast<const char *>(blobmsg_data(ip)), "");
-    }
+        for (auto ip : loopback_status.filter("ipv4-address", "", "address")) {
+            add_listen("",  static_cast<const char *>(blobmsg_data(ip)), "");
+        }
 
-    for (auto ip : loopback_status.filter("ipv6-address", "", "address")) {
-        add_listen("[", static_cast<const char *>(blobmsg_data(ip)), "]");
-    }
+        for (auto ip : loopback_status.filter("ipv6-address", "", "address")) {
+            add_listen("[", static_cast<const char *>(blobmsg_data(ip)), "]");
+        }
+    } catch (const std::runtime_error &) { /* do nothing about it */ }
 
-    auto lan_status = ubus::call("network.interface.lan", "status");
+    try {
+        auto lan_status = ubus::call("network.interface.lan", "status");
 
-    for (auto ip : lan_status.filter("ipv4-address", "", "address")) {
-        add_listen("",  static_cast<const char *>(blobmsg_data(ip)), "");
-    }
+        for (auto ip : lan_status.filter("ipv4-address", "", "address")) {
+            add_listen("",  static_cast<const char *>(blobmsg_data(ip)), "");
+        }
 
-    for (auto ip : lan_status.filter("ipv6-address", "", "address")) {
-        add_listen("[", static_cast<const char *>(blobmsg_data(ip)), "]");
-    }
+        for (auto ip : lan_status.filter("ipv6-address", "", "address")) {
+            add_listen("[", static_cast<const char *>(blobmsg_data(ip)), "]");
+        }
+
+        for (auto ip : lan_status.filter("ipv6-prefix-assignment", "", 
+            "local-address", "address"))
+        {
+            add_listen("[", static_cast<const char *>(blobmsg_data(ip)), "]");
+        }
+    } catch (const std::runtime_error &) { /* do nothing about it */ }
+#else
+    add_listen("", "127.0.0.1", "");
 #endif
 
     write_file(LAN_LISTEN, listen);
     write_file(LAN_LISTEN_DEFAULT, listen_default);
-#ifdef NGINX_OPENSSL
+#ifndef NO_SSL
     write_file(LAN_SSL_LISTEN, ssl_listen);
     write_file(LAN_SSL_LISTEN_DEFAULT, ssl_listen_default);
 #endif
@@ -66,23 +78,23 @@ void init_lan()
 {
     std::exception_ptr ex;
 
-#ifdef NGINX_OPENSSL
-    auto thrd = std::thread([&ex]{
-       try { add_ssl_if_needed(std::string{LAN_NAME}); }
+#ifndef NO_SSL
+    auto thrd = std::thread([]{ //&ex
+        try { add_ssl_if_needed(std::string{LAN_NAME}); }
         catch (...) {
-            std::cerr<<"init_lan error: cannot add SSL for "<<LAN_NAME<<std::endl;
-            ex = std::current_exception();
+            std::cerr<<"init_lan notice: no server named "<<LAN_NAME<<std::endl;
+            // not: ex = std::current_exception();
         }
     });
 #endif
 
     try { create_lan_listen(); }
     catch (...) {
-        std::cerr<<"init_lan error: cannot create LAN listen directives"<<std::endl;
+        std::cerr<<"init_lan error: cannot create LAN listen files"<<std::endl;
         ex = std::current_exception();
     }
 
-#ifdef NGINX_OPENSSL
+#ifndef NO_SSL
     thrd.join();
 #endif
 
@@ -96,7 +108,7 @@ void get_env()
     std::cout<<"CONF_DIR="<<"'"<<CONF_DIR<<"'"<<std::endl;
     std::cout<<"LAN_NAME="<<"'"<<LAN_NAME<<"'"<<std::endl;
     std::cout<<"LAN_LISTEN="<<"'"<<LAN_LISTEN<<"'"<<std::endl;
-#ifdef NGINX_OPENSSL
+#ifndef NO_SSL
     std::cout<<"LAN_SSL_LISTEN="<<"'"<<LAN_SSL_LISTEN<<"'"<<std::endl;
     std::cout<<"SSL_SESSION_CACHE_ARG="<<"'"<<SSL_SESSION_CACHE_ARG(LAN_NAME)<<
         "'"<<std::endl;
@@ -114,7 +126,7 @@ auto main(int argc, char * argv[]) -> int
     auto cmds = std::array{
         std::array<std::string_view, 2>{"init_lan", ""},
         std::array<std::string_view, 2>{"get_env", ""},
-#ifdef NGINX_OPENSSL
+#ifndef NO_SSL
         std::array<std::string_view, 2>{ADD_SSL_FCT, " server_name" },
         std::array<std::string_view, 2>{"del_ssl", " server_name" },
 #endif
@@ -126,7 +138,7 @@ auto main(int argc, char * argv[]) -> int
 
         else if (argc==2 && args[1]==cmds[1][0]) { get_env(); }
 
-#ifdef NGINX_OPENSSL
+#ifndef NO_SSL
         else if (argc==3 && args[1]==cmds[2][0])
         { add_ssl_if_needed(std::string{args[2]});}
 
@@ -138,13 +150,30 @@ auto main(int argc, char * argv[]) -> int
 #endif
 
         else {
+            std::cerr<<"Tool for creating Nginx configuration files (";
+#ifdef VERSION
+            std::cerr<<"version "<<VERSION<<" ";
+#endif
+            std::cerr<<"with ";
+#ifndef NO_UBUS
+            std::cerr<<"ubus, ";
+#endif
+#ifndef NO_SSL
+            std::cerr<<"libopenssl, ";
+#ifdef NO_PCRE
+            std::cerr<<"std::regex, ";
+#else
+            std::cerr<<"PCRE, ";
+#endif
+#endif
+            std::cerr<<"pthread and libstdcpp)."<<std::endl;
+
             auto usage = std::string{"usage: "} + *argv + " [";
             for (auto cmd : cmds) {
                 usage += std::string{cmd[0]};
                 usage += std::string{cmd[1]} + "|";
             }
             usage[usage.size()-1] = ']';
-
             std::cerr<<usage<<std::endl;
 
             throw std::runtime_error("main error: argument not recognized");
@@ -161,5 +190,3 @@ auto main(int argc, char * argv[]) -> int
     return 1;
 
 }
-
-#endif
diff --git a/net/nginx-util/src/test-nginx-util-root.sh b/net/nginx-util/src/test-nginx-util-root.sh
new file mode 100644 (file)
index 0000000..a6d7875
--- /dev/null
@@ -0,0 +1,251 @@
+#!/bin/bash
+
+PRINT_PASSED=2
+
+NGINX_UTIL="/usr/bin/nginx-util"
+
+__esc_newlines() {
+    echo "${1}" | sed -E 's/$/\\n/' | tr -d '\n' | sed -E 's/\\n$/\n/'
+}
+
+__esc_sed_rhs() {
+    __esc_newlines "${1}" |  sed -E 's/[&/\]/\\&/g'
+}
+
+_sed_rhs() {
+    __esc_sed_rhs "$(echo "${1}" | sed -E "s/[$]/$(__esc_sed_rhs "${2}")/g")"
+}
+
+__esc_regex() {
+    __esc_newlines "${1}" | sed -E 's/[^^_a-zA-Z0-9-]/[&]/g; s/\^/\\^/g'
+}
+
+_regex() {
+    __esc_regex "${1}" | sed -E -e 's/^(\[\s])*/^\\s*/' \
+        -e 's/(\[\s])+\[[*]]/(\\s.*)?/g' \
+        -e 's/(\[\s])+/\\s+/g' \
+        -e 's/(\[\s])*\[[;]]/\\s*;/g' \
+        -e "s/\[['\"]]/['\"]?/g" \
+        -e "s/\[[$]]/$(__esc_sed_rhs "$(__esc_regex "${2}")")/g"
+}
+
+_echo_sed() {
+    echo "" | sed -E "c${1}"
+}
+
+setpoint_add_ssl() {
+    local indent="\n$1"
+    local name="$2"
+    local default=""
+    [ "${name}" == "${LAN_NAME}" ] && default=".default"
+    local prefix="${CONF_DIR}${name}"
+
+    local CONF="$(grep -vE "$(_regex "${NGX_INCLUDE}" \
+        "${LAN_LISTEN}${default}")" "${prefix}.sans" 2>/dev/null)"
+    local ADDS=""
+    echo "${CONF}" \
+        | grep -qE "$(_regex "${NGX_INCLUDE}" "${LAN_SSL_LISTEN}${default}")" \
+    || ADDS="${ADDS}${indent}$(_sed_rhs "${NGX_INCLUDE}" \
+        "${LAN_SSL_LISTEN}${default}")"
+    echo "${CONF}" | grep -qE "$(_regex "${NGX_SSL_CRT}" "${prefix}")" \
+    || ADDS="${ADDS}${indent}$(_sed_rhs "${NGX_SSL_CRT}" "${prefix}")"
+    echo "${CONF}" | grep -qE "$(_regex "${NGX_SSL_KEY}" "${prefix}")" \
+    || ADDS="${ADDS}${indent}$(_sed_rhs "${NGX_SSL_KEY}" "${prefix}")"
+    echo "${CONF}" | grep -qE "^\s*ssl_session_cache\s" \
+    || ADDS="${ADDS}${indent}$(_sed_rhs "${NGX_SSL_SESSION_CACHE}" "${name}")"
+    echo "${CONF}" | grep -qE "^\s*ssl_session_timeout\s" \
+    || ADDS="${ADDS}${indent}$(_sed_rhs "${NGX_SSL_SESSION_TIMEOUT}" "")"
+
+    if [ -n "${ADDS}" ]
+    then
+        ADDS="$(echo "${ADDS}" | sed -E 's/^\\n//')"
+        echo "${CONF}" | grep -qE "$(_regex "${NGX_SERVER_NAME}" "${name}")" \
+        && echo "${CONF}" \
+            | sed -E "/$(_regex "${NGX_SERVER_NAME}" "${name}")/a\\${ADDS}" \
+            > "${prefix}.with" \
+        && _echo_sed "Added directives to ${prefix}.with:\n${ADDS}" \
+        && return 0 \
+        || _echo_sed "Cannot add directives to ${prefix}.sans, missing:\
+            \n$(_sed_rhs "${NGX_SERVER_NAME}" "${name}")\n${ADDS}"
+        return 1
+    fi
+    return 0
+}
+
+# ----------------------------------------------------------------------------
+
+
+function test() {
+    eval "$1 2>/dev/null >/dev/null "
+    if [ $? -eq $2 ]
+    then
+        [ "${PRINT_PASSED}" -gt 0 ] \
+        && printf "%-72s%-1s\n" "$1" "2>/dev/null >/dev/null (-> $2?) passed."
+    else
+        printf "%-72s%-1s\n" "$1" "2>/dev/null >/dev/null (-> $2?) failed!!!"
+        [ "${PRINT_PASSED}" -gt 1 ] && exit 1
+    fi
+}
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting ${NGINX_UTIL} get_env ...\n"
+
+eval $("${NGINX_UTIL}" get_env)
+test '[ -n "${NGINX_CONF}" ]' 0
+test '[ -n "${CONF_DIR}" ]' 0
+test '[ -n "${LAN_NAME}" ]' 0
+test '[ -n "${LAN_LISTEN}" ]' 0
+test '[ -n "${LAN_SSL_LISTEN}" ]' 0
+test '[ -n "${SSL_SESSION_CACHE_ARG}" ]' 0
+test '[ -n "${SSL_SESSION_TIMEOUT_ARG}" ]' 0
+test '[ -n "${ADD_SSL_FCT}" ]' 0
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nPrepare files in ${CONF_DIR} ...\n"
+
+mkdir -p ${CONF_DIR}
+
+cd ${CONF_DIR}
+
+NGX_INCLUDE="include '\$';"
+NGX_SERVER_NAME="server_name * '\$' *;"
+NGX_SSL_CRT="ssl_certificate '\$.crt';"
+NGX_SSL_KEY="ssl_certificate_key '\$.key';"
+NGX_SSL_SESSION_CACHE="ssl_session_cache '$(echo "${SSL_SESSION_CACHE_ARG}" \
+    | sed -E "s/$(__esc_regex ${LAN_NAME})/\$/")';"
+NGX_SSL_SESSION_TIMEOUT="ssl_session_timeout '${SSL_SESSION_TIMEOUT_ARG}';"
+
+cat > ${LAN_NAME}.sans <<EOF
+# default_server for the LAN addresses getting the IPs by:
+# ifstatus lan | jsonfilter -e '@["ipv4-address","ipv6-address"].*.address'
+server {
+    include '${LAN_LISTEN}.default';
+    server_name ${LAN_NAME};
+    include conf.d/*.locations;
+}
+EOF
+CONFS="${CONFS} ${LAN_NAME}:0"
+
+cat > minimal.sans <<EOF
+server {
+    server_name minimal;
+}
+EOF
+CONFS="${CONFS} minimal:0"
+
+cat > normal.sans <<EOF
+server {
+    include '${LAN_LISTEN}';
+    server_name normal;
+}
+EOF
+CONFS="${CONFS} normal:0"
+
+cat > more_server.sans <<EOF
+server {
+    # include '${LAN_LISTEN}';
+    server_name normal;
+}
+server {
+    include '${LAN_LISTEN}';
+    server_name more_server;
+}
+EOF
+CONFS="${CONFS} more_server:0"
+
+cat > more_names.sans <<EOF
+server {
+    include '${LAN_LISTEN}';
+    server_name example.com more_names example.org;
+}
+EOF
+CONFS="${CONFS} more_names:0"
+
+cat > different_name.sans <<EOF
+server {
+    include '${LAN_LISTEN}';
+    server_name minimal;
+}
+EOF
+CONFS="${CONFS} different_name:1"
+
+cat > comments.sans <<EOF
+server { # comment1
+    # comment2
+    include '${LAN_LISTEN}';
+    server_name comments;
+    # comment3
+} # comment4
+EOF
+CONFS="${CONFS} comments:0"
+
+cat > name_comment.sans <<EOF
+server {
+    include '${LAN_LISTEN}';
+    server_name name_comment; # comment
+}
+EOF
+CONFS="${CONFS} name_comment:0"
+
+cat > tab.sans <<EOF
+server {
+       include '${LAN_LISTEN}';
+       server_name tab;
+}
+EOF
+CONFS="${CONFS} tab:0"
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting ${NGINX_UTIL} init_lan ...\n"
+
+mkdir -p "$(dirname "${LAN_LISTEN}")"
+
+cp ${LAN_NAME}.sans ${LAN_NAME}.conf
+
+test '${NGINX_UTIL} init_lan' 0
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nSetup files in ${CONF_DIR} ...\n"
+
+for conf in ${CONFS}
+do test 'setpoint_add_ssl "    " '${conf%:*} ${conf#*:}
+done
+
+test 'setpoint_add_ssl "\t" tab' 0 # fixes wrong indentation.
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting ${NGINX_UTIL} add_ssl ...\n"
+
+cp different_name.sans different_name.with
+
+test '[ "${ADD_SSL_FCT}" == "add_ssl" ] ' 0
+
+for conf in ${CONFS}; do
+    name=${conf%:*}
+    cp ${name}.sans ${name}.conf
+    test '${NGINX_UTIL} add_ssl '${name} ${conf#*:}
+    (test '[ "$(cat '${name}'.conf)" == "$(cat '${name}'.with)" ]' 0 >/dev/null)
+    [ "$?" -gt 0 ] && {
+        echo "created ${name}.conf:"; cat "${name}.conf"
+        echo "differs from setpoint:"; cat "${name}.with"
+        exit 1
+    }
+done
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting ${NGINX_UTIL} del_ssl ...\n"
+
+sed -i "/server {/a\\    include '${LAN_LISTEN}';" minimal.sans
+
+for conf in ${CONFS}; do
+    name=${conf%:*}
+    cp ${name}.with ${name}.conf
+    test '${NGINX_UTIL} del_ssl '${name} ${conf#*:}
+    (test '[ "$(cat '${name}'.conf)" == "$(cat '${name}'.sans)" ]' 0 >/dev/null)
+    [ "$?" -gt 0 ] && {
+        echo "created ${name}.conf:"; cat "${name}.conf"
+        echo "differs from setpoint:"; cat "${name}.sans"
+        exit 1
+    }
+done
+
diff --git a/net/nginx-util/src/test-nginx-util.sh b/net/nginx-util/src/test-nginx-util.sh
new file mode 100755 (executable)
index 0000000..840f6f2
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+printf "Initializing tests ...\n"
+
+fakechroot=""
+
+[ -x /usr/bin/fakechroot ] && fakechroot="/usr/bin/fakechroot" \
+|| [ "$(id -u)" -eq 0 ] || { \
+    printf "Error: Testing needs fakechroot or whoami=root for chroot."
+    return 1
+}
+
+TMPROOT=$(mktemp -d /tmp/test-nginx-util-XXXXXX)
+
+ln -s /bin ${TMPROOT}/bin
+
+mkdir -p ${TMPROOT}/usr/bin/
+
+cp ./test-nginx-util-root.sh ${TMPROOT}/usr/bin/
+
+
+printf "\n\n******* Testing nginx-ssl-util-noubus *******\n"
+
+cp ./nginx-ssl-util-noubus ${TMPROOT}/usr/bin/nginx-util
+
+${fakechroot} /bin/chroot ${TMPROOT} /bin/sh -c /usr/bin/test-nginx-util-root.sh
+
+echo $?
+
+
+printf "\n\n******* Testing nginx-ssl-util-nopcre-noubus *******\n"
+
+cp ./nginx-ssl-util-nopcre-noubus ${TMPROOT}/usr/bin/nginx-util
+
+${fakechroot} /bin/chroot ${TMPROOT} /bin/sh -c /usr/bin/test-nginx-util-root.sh
+
+
+rm -r ${TMPROOT}
index 9bae051c6b230ecea4854f35cdbecd528ed72490..944464c2a0c0a44b6bf278bc1b526a7fac20e6eb 100755 (executable)
@@ -2,6 +2,7 @@
 
 PRINT_PASSED=2
 
+printf "Initializing tests ...\n"
 
 OPENSSL_PEM="$(mktemp)"
 OPENSSL_DER="$(mktemp)"
@@ -18,14 +19,14 @@ openssl req -x509 -nodes -days 1 -keyout /dev/null 2>/dev/null \
 
 
 function test() {
-    MSG="$1 >/dev/null \t (-> $2?) \t"
     eval "$1 >/dev/null "
     if [ $? -eq $2 ]
     then
-        [ "$PRINT_PASSED" -gt 0 ] && printf "$MSG passed.\n"
+        [ "${PRINT_PASSED}" -gt 0 ] \
+        && printf "%-72s%-1s\n" "$1" ">/dev/null (-> $2?) passed."
     else
-        printf "$MSG failed!!!\n"
-        [ "$PRINT_PASSED" -gt 1 ] && exit 1
+        printf "%-72s%-1s\n" "$1" ">/dev/null (-> $2?) failed!!!"
+        [ "${PRINT_PASSED}" -gt 1 ] && exit 1
     fi
 }
 
index 83a1ee1dfc253abb5a15f45dd424bbc864ebd663..b1a62c6ce0b326158ff899eb40783bb63e50acb6 100644 (file)
@@ -208,7 +208,7 @@ public:
     auto operator++() -> iterator &;
 
 
-    inline ~iterator() { if (cur.get()==this) { cur.release(); } }
+    inline ~iterator() { if (cur.get()==this) { static_cast<void>(cur.release()); } }
 
 };