diff --git a/net/nginx-util/Makefile b/net/nginx-util/Makefile index 41903a58b..54eb8bf87 100644 --- a/net/nginx-util/Makefile +++ b/net/nginx-util/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=nginx-util -PKG_VERSION:=1.4 -PKG_RELEASE:=3 +PKG_VERSION:=1.5 +PKG_RELEASE:=1 PKG_MAINTAINER:=Peter Stadler include $(INCLUDE_DIR)/package.mk @@ -17,7 +17,7 @@ define Package/nginx-ssl-util/default CATEGORY:=Network SUBMENU:=Web Servers/Proxies TITLE:=Nginx configurator including SSL - DEPENDS:=+libstdcpp +libubus +libubox +libpthread +libopenssl + DEPENDS:=+libstdcpp +libuci +libubus +libubox +libpthread +libopenssl # TODO: remove after a transition period (together with below and pkg nginx): # It actually removes nginx-util (replacing it by a dummy pkg) to avoid # conflicts with nginx-ssl-util* @@ -58,19 +58,63 @@ Package/nginx-ssl-util-nopcre/description = \ It uses the standard regex library of C++. +define Package/nginx-ssl-util/install/default + $(INSTALL_DIR) $(1)/etc/nginx/conf.d/ + + $(INSTALL_CONF) ./files/uci.conf.template $(1)/etc/nginx/ + $(LN) /var/lib/nginx/uci.conf $(1)/etc/nginx/uci.conf + + $(INSTALL_CONF) ./files/restrict_locally $(1)/etc/nginx/ + + $(INSTALL_DIR) $(1)/etc/config/ + $(INSTALL_CONF) ./files/nginx.config $(1)/etc/config/nginx + +ifneq ($(CONFIG_IPV6),y) # the used IPv6 directives have `::` in them: + $(SED) "/::/d" $(1)/etc/nginx/restrict_locally + $(SED) "/::/d" $(1)/etc/config/nginx +endif +endef + + define Package/nginx-ssl-util/install + $(call Package/nginx-ssl-util/install/default, $(1)) $(INSTALL_DIR) $(1)/usr/bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/nginx-ssl-util $(1)/usr/bin/nginx-util endef define Package/nginx-ssl-util-nopcre/install + $(call Package/nginx-ssl-util/install/default, $(1)) $(INSTALL_DIR) $(1)/usr/bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/nginx-ssl-util-nopcre \ $(1)/usr/bin/nginx-util endef +define Package/nginx-ssl-util/prerm +#!/bin/sh + +[ -n "$${IPKG_INSTROOT}" ] && exit 0 +[ "$${PKG_UPGRADE}" = "1" ] && exit 0 +case "$$(/sbin/uci get "nginx.global.uci_enable" 2>/dev/null)" in + 1|on|true|yes|enabled) ;; + *) exit 0;; +esac + +eval "$$(/usr/bin/nginx-util get_env)" && +[ "$$(/sbin/uci get "nginx.$${LAN_NAME}.$${MANAGE_SSL}" 2>/dev/null)" = \ + "self-signed" ] && +cd "/etc/nginx" && +rm -f "$$(/sbin/uci get "nginx.$${LAN_NAME}.ssl_certificate")" \ + "$$(/sbin/uci get "nginx.$${LAN_NAME}.ssl_certificate_key")" + +exit 0 +endef + + +Package/nginx-ssl-util-nopcre/prerm = $(Package/nginx-ssl-util/prerm) + + $(eval $(call BuildPackage,nginx-ssl-util)) $(eval $(call BuildPackage,nginx-ssl-util-nopcre)) diff --git a/net/nginx-util/files/README.sh b/net/nginx-util/files/README.sh new file mode 100755 index 000000000..7ca172964 --- /dev/null +++ b/net/nginx-util/files/README.sh @@ -0,0 +1,475 @@ +#!/bin/sh +# This is a template copy it by: ./README.sh | xclip -selection c +# to https://openwrt.org/docs/guide-user/services/webserver/nginx#configuration + + +NGINX_UTIL="/usr/bin/nginx-util" + +EXAMPLE_COM="example.com" + +MSG=" +/* Created by the following bash script that includes the source of some files: + * https://github.com/openwrt/packages/net/nginx-util/files/README.sh + */" + +eval $("${NGINX_UTIL}" get_env) + +code() { + local file + [ $# -gt 1 ] && file="$2" || file="$(basename "$1")" + printf "\n%s" "$1" "$(cat "${file}")"; +} + +ifConfEcho() { + sed -nE "s/^\s*$1=\s*(\S*)\s*\\\\$/\n$2 \"\1\";/p" ../../nginx/Makefile; +} + +cat <service nginx reload + +For OpenWrt we use a special initial configuration, which is explained in the +section [[#openwrt_s_defaults|🡓OpenWrt’s Defaults]]. +So, we can make a site available at a specific URL in the **LAN** by creating a +''.locations'' file in the directory ''${CONF_DIR}''. +Such a file consists just of some +[[https://nginx.org/en/docs/http/ngx_http_core_module.html#location| +location blocks]]. +Under the latter link, you can find also the official documentation for all +available directives of the HTTP core of Nginx. +Look for //location// in the Context list. + +The following example provides a simple template, see at the end for +different [[#locations_for_apps|🡓Locations for Apps]]((look for +[[https://github.com/search?utf8=%E2%9C%93&q=repo%3Aopenwrt%2Fpackages ++extension%3Alocations&type=Code&ref=advsearch&l=&l=| +other packages using a .locations file]], too.)): + + +location /ex/am/ple { + access_log off; # default: not logging accesses. + # access_log /proc/self/fd/1 openwrt; # use logd (init forwards stdout). + # error_log stderr; # default: logging to logd (init forwards stderr). + error_log /dev/null; # disable error logging after config file is read. + # (state path of a file for access_log/error_log to the file instead.) + index index.html; +} +# location /eg/static { … } + + +All location blocks in all ''.locations'' files must use different URLs, +since they are all included in the ''${LAN_NAME}'' server that is part of the +[[#openwrt_s_defaults|🡓OpenWrt’s Defaults]].(( +We reserve the ''location /'' for making LuCI available under the root URL, +e.g. [[https://192.168.1.1/|192.168.1.1/]]. +All other sites shouldn’t use the root ''location /'' without suffix.)) +We should use the root URL for other sites than LuCI only on **other** domain +names, e.g., we could make a site available at https://${EXAMPLE_COM}/. +In order to do that, we create [[#new_server_parts|🡓New Server Parts]] for all +domain names. +We can also activate SSL thereby, see +[[#ssl_server_parts|🡓SSL Server Parts]]. +We use such server parts also for publishing sites to the internet (WAN) +instead of making them available just locally (in the LAN). + +Via ''${CONF_DIR}*.conf'' files we can add directives to the //http// part of +the configuration. +If you would change the configuration ''$(basename "${UCI_CONF}").template'' +instead, it is not updated to new package's versions anymore. +Although it is not recommended, you can also disable the whole UCI config and +create your own ''${NGINX_CONF}''; then invoke: + +uci set nginx.global.uci_enable=false + + + +==== New Server Parts ====${MSG} + + +For making the router reachable from the WAN at a registered domain name, +it is not enough letting the +[[docs:guide-user:firewall:firewall_configuration|🡒firewall]] accept requests +(typically on ports 80 and 443) and giving the name server the internet IP +address of the router (maybe updated automatically by a +[[docs:guide-user:services:ddns:client|🡒DDNS Client]]). + +We also need to set up virtual hosting for this domain name by creating an +appropriate server section in ''/etc/config/nginx'' +(or in a ''${CONF_DIR}*.conf'' file, which cannot be changed using UCI). +All such parts are included in the main configuration of OpenWrt +([[#openwrt_s_defaults|🡓OpenWrt’s Defaults]]). + +In the server part, we state the domain as +[[https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name| +server_name]]. +The link points to the same document as for the location blocks in the +[[#basic|🡑Basic Configuration]]: the official documentation for all available +directives of the HTTP core of Nginx. +This time look for //server// in the Context list, too. +The server part should also contain similar location blocks as +++before.| +We can re-include a ''.locations'' file that is included in the server part for +the LAN by default. +Then the site is reachable under the same path at both domains, e.g. by +https://192.168.1.1/ex/am/ple as well as by https://${EXAMPLE_COM}/ex/am/ple. +++ + +We can add directives to a server in the UCI configuration by invoking +''uci [set|add_list] nginx.${EXAMPLE_COM//./_}.key=value''. +If the //key// is not starting with //uci_//, it becomes a ''key value;'' +++directive.| +Although the UCI config does not support nesting like Nginx, we can add a whole +block as //value//. +++ + +We cannot use dots in a //key// name other than in the //value//. +In the following example we replace the dot in //${EXAMPLE_COM}// by an +underscore for the UCI name of the server, but not for Nginx's //server_name//: + + +uci add nginx server && +uci rename nginx.@server[-1]=${EXAMPLE_COM//./_} && +uci add_list nginx.${EXAMPLE_COM//./_}.listen='80' && +uci add_list nginx.${EXAMPLE_COM//./_}.listen='[::]:80' && +uci set nginx.${EXAMPLE_COM//./_}.server_name='${EXAMPLE_COM}' && +uci add_list nginx.${EXAMPLE_COM//./_}.include=\ +'$(basename ${CONF_DIR})/${EXAMPLE_COM}.locations' +# uci add_list nginx.${EXAMPLE_COM//./_}.location='/ { … }' \ +# root location for this server. + + +We can disable respective re-enable this server again by: + + +uci set nginx.${EXAMPLE_COM//./_}=disable # respective: \ +uci set nginx.${EXAMPLE_COM//./_}=server + + +These changes are made in the RAM (and can be used until a reboot), we can save +them permanently by: + +uci commit nginx + +For creating a similar ''${CONF_DIR}${EXAMPLE_COM}.conf'', we can adopt the +following: + + +server { + listen 80; + listen [::]:80; + server_name ${EXAMPLE_COM}; + include '$(basename ${CONF_DIR})/${EXAMPLE_COM}.locations'; + # location / { … } # root location for this server. +} + + +[[#openwrt_s_defaults|🡓OpenWrt’s Defaults]] include the UCI server +''config server '_redirect2ssl' ''. +It acts as //default_server// for HTTP and redirects requests for inexistent +URLs to HTTPS. +For making another domain name accessible to all addresses, the corresponding +server part should listen on port //80// and contain the FQDN as +//server_name//, cf. the official documentation on +[[https://nginx.org/en/docs/http/request_processing.html|request_processing]]. + +Furthermore, there is a UCI server named ''${LAN_NAME}''. +It is the //default_server// for HTTPS and allows connections from LAN only. +It includes the file ''$(dirname "${CONF_DIR}")/restrict_locally'' with +appropriate //allow/deny// directives, cf. the official documentation on +[[https://nginx.org/en/docs/http/ngx_http_access_module.html|limiting access]]. + + + +==== SSL Server Parts ====${MSG} + + +For enabling HTTPS for a domain we need a SSL certificate as well as its key and +add them by the directives //ssl_certificate// respective +//ssl_certificate_key// to the server part of the domain +([[https://nginx.org/en/docs/http/configuring_https_servers.html#sni|TLS SNI]] +is supported by default). +The rest of the configuration is similar as for general +[[#new_server_parts|🡑New Server Parts]]. +We only have to adjust the listen directives by adding the //ssl// parameter and +changing the port from //80// to //443//. + +The official documentation of the SSL module contains an +[[https://nginx.org/en/docs/http/ngx_http_ssl_module.html#example| +example]] with some optimizations. +We can extend an existing UCI server section similarly, e.g., for the above +''config server '${EXAMPLE_COM//./_}' '' we invoke: + + +# Instead of 'del_list' the listen* entries, we could use '443 ssl' beforehand. +uci del_list nginx.${EXAMPLE_COM//./_}.listen='80' && +uci del_list nginx.${EXAMPLE_COM//./_}.listen='[::]:80' && +uci add_list nginx.${EXAMPLE_COM//./_}.listen='443 ssl' && +uci add_list nginx.${EXAMPLE_COM//./_}.listen='[::]:443 ssl' && +uci set nginx.${EXAMPLE_COM//./_}.ssl_certificate=\ +'${CONF_DIR}${EXAMPLE_COM}.crt' && +uci set nginx.${EXAMPLE_COM//./_}.ssl_certificate_key=\ +'${CONF_DIR}${EXAMPLE_COM}.key' && +uci set nginx.${EXAMPLE_COM//./_}.ssl_session_cache=\ +'${SSL_SESSION_CACHE_ARG}' && +uci set nginx.${EXAMPLE_COM//./_}.ssl_session_timeout=\ +'${SSL_SESSION_TIMEOUT_ARG}' && +uci commit nginx + + +For making the server in ''${CONF_DIR}${EXAMPLE_COM}.conf'' available +via SSL, we can make similar changes there. + +The following command creates a **self-signed** SSL certificate and changes the +corresponding configuration: + +$(basename "${NGINX_UTIL}") ${ADD_SSL_FCT} ${EXAMPLE_COM} + + - If a ''$(basename "${CONF_DIR}")/${EXAMPLE_COM}.conf'' file exists, it\ + adds //ssl_*// directives and changes the //listen// directives there.\ + Else it does that similarly to the example above for a ++selected UCI\ + server.| Hereby it searches the UCI config first for a server with the\ + given name and then for a server whose //server_name// contains the name.\ + For //${EXAMPLE_COM}// it is the latter as a UCI key cannot have dots.++ + - It checks if there is a certificate with key for '${EXAMPLE_COM}' that is\ + valid for at least 13 months or tries to create a self-signed one. + - When cron is activated, it installs a cron job for renewing the self-signed\ + certificate every year if needed, too. We can activate cron by: \ + service cron enable && service cron start + +This can be undone by invoking: + +$(basename "${NGINX_UTIL}") del_ssl ${EXAMPLE_COM} + +For using an SSL certificate and key that are managed otherwise, there is: + +$(basename "${NGINX_UTIL}") add_ssl ${EXAMPLE_COM} "\$MANAGER" \ +"/absolute/path/to/crt" "/absolute/path/to/key" + +It only adds //ssl_*// directives and changes the //listen// directives in +the appropriate configuration, but does not create or change the certificate +or its key. This can be reverted by: + +$(basename "${NGINX_UTIL}") del_ssl ${EXAMPLE_COM} "\$MANAGER" + +For example [[https://github.com/ndilieto/uacme|uacme]] or +[[https://github.com/Neilpang/acme.sh|acme.sh]] can be used for creating an SSL +certificate signed by Let’s Encrypt and changing the config +++accordingly.| +They call ''$(basename "${NGINX_UTIL}") add_ssl \$FQDN acme \$CRT \$KEY'' +internally.++ +We can install them by: + + +opkg update && opkg install uacme #or: acme #and for LuCI: luci-app-acme + + +[[#openwrt_s_defaults|🡓OpenWrt’s Defaults]] include a UCI server for the LAN: +''config server '${LAN_NAME}' ''. +It has //ssl_*// directives prepared for a self-signed((Let’s Encrypt (and other +CAs) cannot sign certificates of a **local** server.)) +SSL certificate, which is created on the first start of Nginx. +The server listens on all addresses, is the //default_server// for HTTPS and +allows connections from LAN only (by including the file ''restrict_locally'' +with //allow/deny// directives, cf. the official documentation on +[[https://nginx.org/en/docs/http/ngx_http_access_module.html|limiting access]]). + +For making another domain name accessible to all addresses, the corresponding +SSL server part should listen on port //443// and contain the FQDN as +//server_name//, cf. the official documentation on +[[https://nginx.org/en/docs/http/request_processing.html|request_processing]]. + +Furthermore, there is also a UCI server named ''_redirect2ssl'', which listens +on all addresses, acts as //default_server// for HTTP and redirects requests for +inexistent URLs to HTTPS. + + + +==== OpenWrt’s Defaults ====${MSG} + + +Since Nginx is compiled with these presets, we can pretend that the main +configuration will always contain the following directives +(though we can overwrite them): + +$(ifConfEcho --pid-path pid)\ +$(ifConfEcho --lock-path lock_file)\ +$(ifConfEcho --error-log-path error_log)\ +$(false && ifConfEcho --http-log-path access_log)\ +$(ifConfEcho --http-proxy-temp-path proxy_temp_path)\ +$(ifConfEcho --http-client-body-temp-path client_body_temp_path)\ +$(ifConfEcho --http-fastcgi-temp-path fastcgi_temp_path)\ + + +When starting or reloading the Nginx service, the ''/etc/init.d/nginx'' script +sets also the following directives +(so we cannot change them in the used configuration file): + + +daemon off; # procd expects services to run in the foreground + + +Then, the init sript creates the main configuration +''$(basename "${UCI_CONF}")'' dynamically from the template: + +$(code "${UCI_CONF}.template") + +So, the access log is turned off by default and we can look at the error log +by ''logread'', as init.d script forwards stderr and stdout to the +[[docs:guide-user:base-system:log.essentials|🡒runtime log]]. +We can set the //error_log// and //access_log// to files, where the log +messages are forwarded to instead (after the configuration is read). +And for redirecting the access log of a //server// or //location// to the logd, +too, we insert the following directive in the corresponding block: + + access_log /proc/self/fd/1 openwrt; + +If we setup a server through UCI, we can use the options //error_log// and/or +//access_log// also with the special path +++'logd'.| +When initializing the Nginx service, this special path is replaced by //stderr// +respective ///proc/self/fd/1// (which are forwarded to the runtime log). +++ + +For creating the configuration from the template shown above, Nginx’s init +script replaces the comment ''#UCI_HTTP_CONFIG'' by all UCI servers. +For each server section in the the UCI configuration, it basically copies all +options into a Nginx //server { … }// part, in detail: + * Options starting with ''uci_'' are skipped. Currently there is only\ + the ''option ${MANAGE_SSL}=…'' in ++usage.| It is set to\ + //'self-signed'// when invoking\ + ''$(basename ${NGINX_UTIL}) ${ADD_SSL_FCT} \$NAME''.\ + Then the corresponding certificate is re-newed if it is about to expire.\ + All those certificates are checked on the initialization of the Nginx service\ + and if Cron is available, it is deployed for checking them annually, too.++ + * All other lists or options of the form ''key='value' '' are written\ + one-to-one as ''key value;'' directives to the configuration file.\ + Just the path //logd// has a special meaning for the logging directives\ + (described in the previous paragraph). + +The init.d script of Nginx uses the //$(basename ${NGINX_UTIL})// for creating +the configuration file +++in RAM.| +The main configuration ''${UCI_CONF}'' is a symbolic link to this place +(it is a dead link if the Nginx service is not running). +++ + +We could use a custom configuration created at ''${NGINX_CONF}'' instead of the +dynamic configuration, too.(( +For using a custom configuration at ''${NGINX_CONF}'', we execute +uci set nginx.global.uci_enable='false' +Then the rest of the UCI config is ignored and //init.d// will not create the +main configuration dynamically from the template anymore. +Invoking ''$(basename ${NGINX_UTIL}) [${ADD_SSL_FCT}|del_ssl] \$FQDN'' +will still try to change a server in ''$(basename "${CONF_DIR}")/\$FQDN.conf'' +(this is less reliable than for a UCI config as it uses regular expressions, not +a complete parser for the Nginx configuration).)) +This is not encouraged since you cannot setup servers using UCI anymore. +Rather, we can put custom configuration parts to ''.conf'' files in the +''${CONF_DIR}'' directory. +The main configuration pulls in all ''$(basename "${CONF_DIR}")/*.conf'' files +into the //http {…}// block behind the created UCI servers. + +The initial UCI config is enabled and contains two server section: + +$(code "/etc/config/nginx" "nginx.config") + +While the LAN server is the //default_server// for HTTPS, the server +redirecting requests for an inexistent ''server_name'' from HTTP to HTTPS acts +as //default_server// if there is ++no other|; +it uses an invalid name for that, more in the official documentation on +[[https://nginx.org/en/docs/http/request_processing.html|request_processing]] +++. + +The LAN server pulls in all ''.locations'' files from the directory +''${CONF_DIR}''. +We can install the location parts of different sites there (see +[[#basic|🡑Basic Configuration]]) and re-include them into other servers. +This is needed especially for making them available to the WAN +([[#new_server_parts|🡑New Server Parts]]). +The LAN server listens for all addresses on port //443// and restricts the +access to local addresses by including: +$(code "$(dirname "${CONF_DIR}")/restrict_locally") + +When starting or reloading the Nginx service, the init.d looks which UCI servers +have set ''option ${MANAGE_SSL} 'self-signed' '', e.g. the LAN server. +For all those servers it checks if there is a certificate that is still valid +for 13 months or (re-)creates a self-signed one. +If there is any such server, it installs also a cron job that checks the +corresponding certificates once a year. +The option ''${MANAGE_SSL}'' is set to //'self-signed'// respectively removed +from a UCI server named ''${EXAMPLE_COM//./_}'' by the following +(see [[#ssl_server_parts|🡑SSL Server Parts]], too): + + +$(basename ${NGINX_UTIL}) ${ADD_SSL_FCT} ${EXAMPLE_COM//./_} \ +# respectively: \ +$(basename ${NGINX_UTIL}) del_ssl ${EXAMPLE_COM//./_} + + + +EOF diff --git a/net/nginx-util/files/nginx.config b/net/nginx-util/files/nginx.config new file mode 100644 index 000000000..4f07ae1d4 --- /dev/null +++ b/net/nginx-util/files/nginx.config @@ -0,0 +1,22 @@ + +config main global + option uci_enable 'true' + +config server '_lan' + list listen '443 ssl default_server' + list listen '[::]:443 ssl default_server' + option server_name '_lan' + list include 'restrict_locally' + list include 'conf.d/*.locations' + option uci_manage_ssl 'self-signed' + option ssl_certificate '/etc/nginx/conf.d/_lan.crt' + option ssl_certificate_key '/etc/nginx/conf.d/_lan.key' + option ssl_session_cache 'shared:SSL:32k' + option ssl_session_timeout '64m' + option access_log 'off; # logd openwrt' + +config server '_redirect2ssl' + list listen '80' + list listen '[::]:80' + option server_name '_redirect2ssl' + option return '302 https://$host$request_uri' diff --git a/net/nginx-util/files/restrict_locally b/net/nginx-util/files/restrict_locally new file mode 100644 index 000000000..0b791cdfd --- /dev/null +++ b/net/nginx-util/files/restrict_locally @@ -0,0 +1,10 @@ + allow ::1; + allow fc00::/7; + allow fec0::/10; + allow fe80::/10; + allow 127.0.0.0/8; + allow 10.0.0.0/8; + allow 172.16.0.0/12; + allow 192.168.0.0/16; + allow 169.254.0.0/16; + deny all; diff --git a/net/nginx-util/files/uci.conf.template b/net/nginx-util/files/uci.conf.template new file mode 100644 index 000000000..1c611d9ad --- /dev/null +++ b/net/nginx-util/files/uci.conf.template @@ -0,0 +1,32 @@ +# Consider using UCI or creating files in /etc/nginx/conf.d/ for configuration. +# Parsing UCI configuration is skipped if uci set nginx.global.uci_enable=false +# For details see: https://openwrt.org/docs/guide-user/services/webserver/nginx + +worker_processes auto; + +user root; + +events {} + +http { + access_log off; + log_format openwrt + '$request_method $scheme://$host$request_uri => $status' + ' (${body_bytes_sent}B in ${request_time}s) <- $http_referer'; + + include mime.types; + default_type application/octet-stream; + sendfile on; + + client_max_body_size 128M; + large_client_header_buffers 2 1k; + + gzip on; + gzip_vary on; + gzip_proxied any; + + root /www; + + #UCI_HTTP_CONFIG + include conf.d/*.conf; +} diff --git a/net/nginx-util/src/.clang-format b/net/nginx-util/src/.clang-format new file mode 100644 index 000000000..3c1ba7372 --- /dev/null +++ b/net/nginx-util/src/.clang-format @@ -0,0 +1,171 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +# AllowShortEnumsOnASingleLine: true +AllowShortLambdasOnASingleLine: All +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Always +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: false +# BitFieldColonSpacing: After +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: MultiLine + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: true + BeforeElse: true + # BeforeLambdaBody: true + # BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: None +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + - Regex: '.*' + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +... + diff --git a/net/nginx-util/src/.clang-tidy b/net/nginx-util/src/.clang-tidy new file mode 100644 index 000000000..c34ab6ada --- /dev/null +++ b/net/nginx-util/src/.clang-tidy @@ -0,0 +1,407 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*,*,-fuchsia-*,-misc-definitions-in-headers,-llvm-header-guard,-*-qualified-auto,-llvm-include-order' +WarningsAsErrors: '' +HeaderFilterRegex: '.*' +AnalyzeTemporaryDtors: false +FormatStyle: file +CheckOptions: + - key: abseil-string-find-startswith.AbseilStringsMatchHeader + value: 'absl/strings/match.h' + - key: abseil-string-find-startswith.IncludeStyle + value: llvm + - key: abseil-string-find-startswith.StringLikeClasses + value: '::std::basic_string' + - key: bugprone-argument-comment.CommentBoolLiterals + value: '0' + - key: bugprone-argument-comment.CommentCharacterLiterals + value: '0' + - key: bugprone-argument-comment.CommentFloatLiterals + value: '0' + - key: bugprone-argument-comment.CommentIntegerLiterals + value: '0' + - key: bugprone-argument-comment.CommentNullPtrs + value: '0' + - key: bugprone-argument-comment.CommentStringLiterals + value: '0' + - key: bugprone-argument-comment.CommentUserDefinedLiterals + value: '0' + - key: bugprone-argument-comment.IgnoreSingleArgument + value: '0' + - key: bugprone-argument-comment.StrictMode + value: '0' + - key: bugprone-assert-side-effect.AssertMacros + value: assert + - key: bugprone-assert-side-effect.CheckFunctionCalls + value: '0' + - key: bugprone-dangling-handle.HandleClasses + value: 'std::basic_string_view;std::experimental::basic_string_view' + - key: bugprone-dynamic-static-initializers.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: bugprone-exception-escape.FunctionsThatShouldNotThrow + value: '' + - key: bugprone-exception-escape.IgnoredExceptions + value: '' + - key: bugprone-misplaced-widening-cast.CheckImplicitCasts + value: '0' + - key: bugprone-not-null-terminated-result.WantToUseSafeFunctions + value: '1' + - key: bugprone-signed-char-misuse.CharTypdefsToIgnore + value: '' + - key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant + value: '1' + - key: bugprone-sizeof-expression.WarnOnSizeOfConstant + value: '1' + - key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression + value: '0' + - key: bugprone-sizeof-expression.WarnOnSizeOfThis + value: '1' + - key: bugprone-string-constructor.LargeLengthThreshold + value: '8388608' + - key: bugprone-string-constructor.WarnOnLargeLength + value: '1' + - key: bugprone-suspicious-enum-usage.StrictMode + value: '0' + - key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens + value: '5' + - key: bugprone-suspicious-missing-comma.RatioThreshold + value: '0.200000' + - key: bugprone-suspicious-missing-comma.SizeThreshold + value: '5' + - key: bugprone-suspicious-string-compare.StringCompareLikeFunctions + value: '' + - key: bugprone-suspicious-string-compare.WarnOnImplicitComparison + value: '1' + - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison + value: '0' + - key: bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit + value: '16' + - key: bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField + value: '1' + - key: bugprone-unused-return-value.CheckedFunctions + value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty' + - key: cert-dcl16-c.IgnoreMacros + value: '1' + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-dcl59-cpp.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: cert-err09-cpp.CheckThrowTemporaries + value: '1' + - key: cert-err61-cpp.CheckThrowTemporaries + value: '1' + - key: cert-msc32-c.DisallowedSeedTypes + value: 'time_t,std::time_t' + - key: cert-msc51-cpp.DisallowedSeedTypes + value: 'time_t,std::time_t' + - key: cert-oop11-cpp.IncludeStyle + value: llvm + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: cppcoreguidelines-avoid-magic-numbers.IgnoredFloatingPointValues + value: '1.0;100.0;' + - key: cppcoreguidelines-avoid-magic-numbers.IgnoredIntegerValues + value: '1;2;3;4;' + - key: cppcoreguidelines-explicit-virtual-functions.AllowOverrideAndFinal + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.FinalSpelling + value: final + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-explicit-virtual-functions.OverrideSpelling + value: override + - key: cppcoreguidelines-macro-usage.AllowedRegexp + value: '^DEBUG_*' + - key: cppcoreguidelines-macro-usage.CheckCapsOnly + value: '0' + - key: cppcoreguidelines-macro-usage.IgnoreCommandLineMacros + value: '1' + - key: cppcoreguidelines-no-malloc.Allocations + value: '::malloc;::calloc' + - key: cppcoreguidelines-no-malloc.Deallocations + value: '::free' + - key: cppcoreguidelines-no-malloc.Reallocations + value: '::realloc' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: cppcoreguidelines-owning-memory.LegacyResourceConsumers + value: '::free;::realloc;::freopen;::fclose' + - key: cppcoreguidelines-owning-memory.LegacyResourceProducers + value: '::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile' + - key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader + value: '' + - key: cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle + value: '0' + - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays + value: '0' + - key: cppcoreguidelines-pro-type-member-init.UseAssignment + value: '0' + - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions + value: '0' + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: '0' + - key: google-build-namespaces.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: google-global-names-in-headers.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.BranchThreshold + value: '4294967295' + - key: google-readability-function-size.LineThreshold + value: '4294967295' + - key: google-readability-function-size.NestingThreshold + value: '4294967295' + - key: google-readability-function-size.ParameterThreshold + value: '4294967295' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-function-size.VariableThreshold + value: '4294967295' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: google-runtime-int.SignedTypePrefix + value: int + - key: google-runtime-int.TypeSuffix + value: '' + - key: google-runtime-int.UnsignedTypePrefix + value: uint + - key: google-runtime-references.WhiteListTypes + value: '' + - key: hicpp-braces-around-statements.ShortStatementLines + value: '0' + - key: hicpp-function-size.BranchThreshold + value: '4294967295' + - key: hicpp-function-size.LineThreshold + value: '4294967295' + - key: hicpp-function-size.NestingThreshold + value: '4294967295' + - key: hicpp-function-size.ParameterThreshold + value: '4294967295' + - key: hicpp-function-size.StatementThreshold + value: '800' + - key: hicpp-function-size.VariableThreshold + value: '4294967295' + - key: hicpp-member-init.IgnoreArrays + value: '0' + - key: hicpp-member-init.UseAssignment + value: '0' + - key: hicpp-move-const-arg.CheckTriviallyCopyableMove + value: '1' + - key: hicpp-multiway-paths-covered.WarnOnMissingElse + value: '0' + - key: hicpp-named-parameter.IgnoreFailedSplit + value: '0' + - key: hicpp-no-malloc.Allocations + value: '::malloc;::calloc' + - key: hicpp-no-malloc.Deallocations + value: '::free' + - key: hicpp-no-malloc.Reallocations + value: '::realloc' + - key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals + value: '0' + - key: hicpp-special-member-functions.AllowMissingMoveFunctions + value: '0' + - key: hicpp-special-member-functions.AllowSoleDefaultDtor + value: '0' + - key: hicpp-uppercase-literal-suffix.IgnoreMacros + value: '1' + - key: hicpp-uppercase-literal-suffix.NewSuffixes + value: '' + - key: hicpp-use-auto.MinTypeNameLength + value: '5' + - key: hicpp-use-auto.RemoveStars + value: '0' + - key: hicpp-use-emplace.ContainersWithPushBack + value: '::std::vector;::std::list;::std::deque' + - key: hicpp-use-emplace.SmartPointers + value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' + - key: hicpp-use-emplace.TupleMakeFunctions + value: '::std::make_pair;::std::make_tuple' + - key: hicpp-use-emplace.TupleTypes + value: '::std::pair;::std::tuple' + - key: hicpp-use-equals-default.IgnoreMacros + value: '1' + - key: hicpp-use-equals-delete.IgnoreMacros + value: '1' + - key: hicpp-use-noexcept.ReplacementString + value: '' + - key: hicpp-use-noexcept.UseNoexceptFalse + value: '1' + - key: hicpp-use-nullptr.NullMacros + value: '' + - key: hicpp-use-override.AllowOverrideAndFinal + value: '0' + - key: hicpp-use-override.FinalSpelling + value: final + - key: hicpp-use-override.IgnoreDestructors + value: '0' + - key: hicpp-use-override.OverrideSpelling + value: override + - key: llvm-namespace-comment.ShortNamespaceLines + value: '1' + - key: llvm-namespace-comment.SpacesBeforeComments + value: '1' + - key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries + value: '1' + - key: misc-unused-parameters.StrictMode + value: '0' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-make-shared.IgnoreMacros + value: '1' + - key: modernize-make-shared.IncludeStyle + value: '0' + - key: modernize-make-shared.MakeSmartPtrFunction + value: 'std::make_shared' + - key: modernize-make-shared.MakeSmartPtrFunctionHeader + value: memory + - key: modernize-make-unique.IgnoreMacros + value: '1' + - key: modernize-make-unique.IncludeStyle + value: '0' + - key: modernize-make-unique.MakeSmartPtrFunction + value: 'std::make_unique' + - key: modernize-make-unique.MakeSmartPtrFunctionHeader + value: memory + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-pass-by-value.ValuesOnly + value: '0' + - key: modernize-raw-string-literal.ReplaceShorterLiterals + value: '0' + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-replace-random-shuffle.IncludeStyle + value: llvm + - key: modernize-use-auto.MinTypeNameLength + value: '5' + - key: modernize-use-auto.RemoveStars + value: '0' + - key: modernize-use-default-member-init.IgnoreMacros + value: '1' + - key: modernize-use-default-member-init.UseAssignment + value: '0' + - key: modernize-use-emplace.ContainersWithPushBack + value: '::std::vector;::std::list;::std::deque' + - key: modernize-use-emplace.SmartPointers + value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' + - key: modernize-use-emplace.TupleMakeFunctions + value: '::std::make_pair;::std::make_tuple' + - key: modernize-use-emplace.TupleTypes + value: '::std::pair;::std::tuple' + - key: modernize-use-equals-default.IgnoreMacros + value: '1' + - key: modernize-use-equals-delete.IgnoreMacros + value: '1' + - key: modernize-use-nodiscard.ReplacementString + value: '[[nodiscard]]' + - key: modernize-use-noexcept.ReplacementString + value: '' + - key: modernize-use-noexcept.UseNoexceptFalse + value: '1' + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + - key: modernize-use-override.AllowOverrideAndFinal + value: '0' + - key: modernize-use-override.FinalSpelling + value: final + - key: modernize-use-override.IgnoreDestructors + value: '0' + - key: modernize-use-override.OverrideSpelling + value: override + - key: modernize-use-transparent-functors.SafeMode + value: '0' + - key: modernize-use-using.IgnoreMacros + value: '1' + - key: objc-forbidden-subclassing.ForbiddenSuperClassNames + value: 'ABNewPersonViewController;ABPeoplePickerNavigationController;ABPersonViewController;ABUnknownPersonViewController;NSHashTable;NSMapTable;NSPointerArray;NSPointerFunctions;NSTimer;UIActionSheet;UIAlertView;UIImagePickerController;UITextInputMode;UIWebView' + - key: openmp-exception-escape.IgnoredExceptions + value: '' + - key: performance-faster-string-find.StringLikeClasses + value: 'std::basic_string' + - key: performance-for-range-copy.AllowedTypes + value: '' + - key: performance-for-range-copy.WarnOnAllAutoCopies + value: '0' + - key: performance-inefficient-string-concatenation.StrictMode + value: '0' + - key: performance-inefficient-vector-operation.EnableProto + value: '0' + - key: performance-inefficient-vector-operation.VectorLikeClasses + value: '::std::vector' + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: '1' + - key: performance-move-constructor-init.IncludeStyle + value: llvm + - key: performance-no-automatic-move.AllowedTypes + value: '' + - key: performance-type-promotion-in-math-fn.IncludeStyle + value: llvm + - key: performance-unnecessary-copy-initialization.AllowedTypes + value: '' + - key: performance-unnecessary-value-param.AllowedTypes + value: '' + - key: performance-unnecessary-value-param.IncludeStyle + value: llvm + - key: portability-simd-intrinsics.Std + value: '' + - key: portability-simd-intrinsics.Suggest + value: '0' + - key: readability-braces-around-statements.ShortStatementLines + value: '0' + - key: readability-else-after-return.WarnOnUnfixable + value: '1' + - key: readability-function-size.BranchThreshold + value: '4294967295' + - key: readability-function-size.LineThreshold + value: '4294967295' + - key: readability-function-size.NestingThreshold + value: '4294967295' + - key: readability-function-size.ParameterThreshold + value: '4294967295' + - key: readability-function-size.StatementThreshold + value: '800' + - key: readability-function-size.VariableThreshold + value: '4294967295' + - key: readability-identifier-naming.IgnoreFailedSplit + value: '0' + - key: readability-implicit-bool-conversion.AllowIntegerConditions + value: '0' + - key: readability-implicit-bool-conversion.AllowPointerConditions + value: '0' + - key: readability-inconsistent-declaration-parameter-name.IgnoreMacros + value: '1' + - key: readability-inconsistent-declaration-parameter-name.Strict + value: '0' + - key: readability-magic-numbers.IgnoredFloatingPointValues + value: '1.0;100.0;' + - key: readability-magic-numbers.IgnoredIntegerValues + value: '1;2;3;4;' + - key: readability-redundant-member-init.IgnoreBaseInCopyConstructors + value: '0' + - key: readability-redundant-smartptr-get.IgnoreMacros + value: '1' + - key: readability-redundant-string-init.StringNames + value: '::std::basic_string' + - key: readability-simplify-boolean-expr.ChainedConditionalAssignment + value: '0' + - key: readability-simplify-boolean-expr.ChainedConditionalReturn + value: '0' + - key: readability-simplify-subscript-expr.Types + value: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array' + - key: readability-static-accessed-through-instance.NameSpecifierNestingThreshold + value: '3' + - key: readability-uppercase-literal-suffix.IgnoreMacros + value: '1' + - key: readability-uppercase-literal-suffix.NewSuffixes + value: '' + - key: zircon-temporary-objects.Names + value: '' +... + diff --git a/net/nginx-util/src/CMakeLists.txt b/net/nginx-util/src/CMakeLists.txt index 284853620..2adff1c71 100644 --- a/net/nginx-util/src/CMakeLists.txt +++ b/net/nginx-util/src/CMakeLists.txt @@ -10,6 +10,13 @@ ADD_DEFINITIONS(-Wno-unused-parameter -Wmissing-declarations -Wshadow) SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +FIND_PATH(uci_include_dir uci.h) +FIND_LIBRARY(uci NAMES uci) +INCLUDE_DIRECTORIES(${uci_include_dir}) + +FIND_PATH(ubox_include_dir libubox/blobmsg.h) +FIND_LIBRARY(ubox NAMES ubox) +INCLUDE_DIRECTORIES(${ubox_include_dir}) IF(UBUS) @@ -19,17 +26,13 @@ FIND_PATH(ubus_include_dir libubus.h) FIND_LIBRARY(ubus NAMES ubus) INCLUDE_DIRECTORIES(${ubus_include_dir}) -FIND_PATH(ubox_include_dir libubox/blobmsg.h) -FIND_LIBRARY(ubox NAMES ubox) -INCLUDE_DIRECTORIES(${ubox_include_dir}) - ADD_EXECUTABLE(nginx-ssl-util nginx-util.cpp) -TARGET_LINK_LIBRARIES(nginx-ssl-util ${ubox} ${ubus} pthread ssl crypto pcre) +TARGET_LINK_LIBRARIES(nginx-ssl-util ${uci} ${ubox} ${ubus} pthread ssl crypto pcre) INSTALL(TARGETS nginx-ssl-util RUNTIME DESTINATION bin) 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) +TARGET_LINK_LIBRARIES(nginx-ssl-util-nopcre ${uci} ${ubox} ${ubus} pthread ssl crypto) INSTALL(TARGETS nginx-ssl-util-nopcre RUNTIME DESTINATION bin) ELSE() @@ -39,6 +42,8 @@ ADD_COMPILE_DEFINITIONS(VERSION=0) 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) +CONFIGURE_FILE(../files/nginx.config config-nginx-ssl COPYONLY) +CONFIGURE_FILE(../files/uci.conf.template uci.conf.template COPYONLY) ADD_EXECUTABLE(px5g px5g.cpp) TARGET_LINK_LIBRARIES(px5g ssl crypto) @@ -46,12 +51,12 @@ 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) +TARGET_LINK_LIBRARIES(nginx-ssl-util-noubus ${uci} ${ubox} pthread ssl crypto pcre) 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) +TARGET_LINK_LIBRARIES(nginx-ssl-util-nopcre-noubus ${uci} ${ubox} pthread ssl crypto) INSTALL(TARGETS nginx-ssl-util-nopcre-noubus RUNTIME DESTINATION bin) ENDIF() diff --git a/net/nginx-util/src/LICENSE b/net/nginx-util/src/LICENSE new file mode 100644 index 000000000..bddd69079 --- /dev/null +++ b/net/nginx-util/src/LICENSE @@ -0,0 +1,23 @@ +/* Copyright 2020 Peter Stadler + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/net/nginx-util/src/nginx-ssl-util.hpp b/net/nginx-util/src/nginx-ssl-util.hpp index 895f9569f..4f12925db 100644 --- a/net/nginx-util/src/nginx-ssl-util.hpp +++ b/net/nginx-util/src/nginx-ssl-util.hpp @@ -11,7 +11,6 @@ namespace rgx = std; #include "nginx-util.hpp" #include "px5g-openssl.hpp" - #ifndef NO_UBUS static constexpr auto UBUS_TIMEOUT = 1000; #endif @@ -19,178 +18,175 @@ static constexpr auto UBUS_TIMEOUT = 1000; // 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 = std::string_view{"/var/lib/nginx/lan_ssl.listen"}; -static constexpr auto LAN_SSL_LISTEN_DEFAULT = +static constexpr auto LAN_SSL_LISTEN_DEFAULT = // TODO(pst) deprecate 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_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 >; +using _Line = std::array; class Line { - -private: - + private: _Line _line; -public: + public: + explicit Line(const _Line& line) noexcept : _line{line} {} - explicit Line(const _Line & line) noexcept : _line{line} {} - - template + template 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)); } - }}; + 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 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]("", "")}; } - + { + 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 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 replace_if(const std::string& conf, + const rgx::regex& rgx, + const std::string& val, + const std::string& insert) -> std::string; - -auto delete_if(const std::string & conf, const rgx::regex & rgx, - const std::string & val="", bool compare=false) +auto replace_listen(const std::string& conf, const std::array& ngx_port) -> std::string; +auto check_ssl_certificate(const std::string& crtpath, const std::string& keypath) -> bool; -void add_ssl_directives_to(const std::string & name, bool isdefault); - +auto contains(const std::string& sentence, const std::string& word) -> bool; -void create_ssl_certificate(const std::string & crtpath, - const std::string & keypath, - int days=792); +auto get_uci_section_for_name(const std::string& name) -> uci::section; +void add_ssl_if_needed(const std::string& name); -void use_cron_to_recreate_certificate(const std::string & name); +void add_ssl_if_needed(const std::string& name, + std::string_view manage, + std::string_view crt, + std::string_view key); +void install_cron_job(const Line& CRON_LINE, const std::string& name = ""); -void add_ssl_if_needed(const std::string & name); +void remove_cron_job(const Line& CRON_LINE, const std::string& name = ""); +auto del_ssl_legacy(const std::string& name) -> bool; -void del_ssl_directives_from(const std::string & name, bool isdefault); +void del_ssl(const std::string& name); +void del_ssl(const std::string& name, std::string_view manage); -void del_ssl(const std::string & name); +auto check_ssl(const uci::package& pkg, bool is_enabled) -> bool; +inline void check_ssl(const uci::package& pkg) +{ + if (!check_ssl(pkg, is_enabled(pkg))) { +#ifndef NO_UBUS + if (ubus::call("service", "list", UBUS_TIMEOUT).filter("nginx")) { + call("/etc/init.d/nginx", "reload"); + std::cerr << "Reload Nginx.\n"; + } +#endif + } +} 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 begin; }, - [](const std::string & /*param*/, const std::string & /*begin*/) - -> std::string - { return R"([{;](?:\s*#[^\n]*(?=\n))*(\s*))"; } -}; + [](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{" "}; }, -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+)"; } -}; - + [](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"}; + }, - [](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)"}; + } // capture it as _end captures it, too. }; +constexpr auto _end = + _Line{[](const std::string& /*param*/, const std::string & /*begin*/) -> std::string { + return std::string{";"}; + }, -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]*)?))"}; + }}; - [](const std::string & /*param*/, const std::string & /*begin*/) - -> std::string - { return std::string{R"(\s*;(?:[\t ]*#[^\n]*)?)"}; } -}; - - -template +template static 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 + "]*)|(?:'[^']*'))+)"; - } -}; + [](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 -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; +template +static 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 - { + [](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+"]"; } - } + 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+"\""+")"; - } -}; + return "(?:" + ret + "|'" + ret + "'" + "|\"" + ret + "\"" + ")"; + }}; +constexpr std::string_view _check_ssl = "check_ssl"; constexpr std::string_view _server_name = "server_name"; +constexpr std::string_view _listen = "listen"; + constexpr std::string_view _include = "include"; constexpr std::string_view _ssl_certificate = "ssl_certificate"; @@ -201,65 +197,82 @@ 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, _space, _escape, _space, - _capture<>, _newline>(); +static const auto CRON_CHECK = + Line::build<_space, _escape, _space, _escape<_check_ssl, '\''>, _newline>(); + +static const auto CRON_CMD = Line::build<_space, + _escape, + _space, + _escape, + _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, _end>(); +static const auto NGX_INCLUDE_LAN_LISTEN = + Line::build<_begin, _escape<_include>, _space, _escape, _end>(); -static const auto NGX_INCLUDE_LAN_LISTEN_DEFAULT = Line::build - <_begin, _escape<_include>, _space, - _escape, _end>(); +static const auto NGX_INCLUDE_LAN_LISTEN_DEFAULT = + Line::build<_begin, _escape<_include>, _space, _escape, _end>(); -static const auto NGX_INCLUDE_LAN_SSL_LISTEN = Line::build - <_begin, _escape<_include>, _space, _escape, _end>(); +static const auto NGX_INCLUDE_LAN_SSL_LISTEN = + Line::build<_begin, _escape<_include>, _space, _escape, _end>(); -static const auto NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT = Line::build - <_begin, _escape<_include>, _space, - _escape, _end>(); +static const auto NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT = + Line::build<_begin, _escape<_include>, _space, _escape, _end>(); -static const auto NGX_SSL_CRT = Line::build - <_begin, _escape<_ssl_certificate>, _space, _capture<';'>, _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_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_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>(); +static const auto NGX_SSL_SESSION_TIMEOUT = + Line::build<_begin, _escape<_ssl_session_timeout>, _space, _capture<';'>, _end>(); +static const auto NGX_LISTEN = Line::build<_begin, _escape<_listen>, _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 +static const auto NGX_PORT_80 = std::array{ + R"(^\s*([^:]*:|\[[^\]]*\]:)?80(\s|$|;))", + "$01443 ssl$2", +}; + +static const auto NGX_PORT_443 = std::array{ + R"(^\s*([^:]*:|\[[^\]]*\]:)?443(\s.*)?\sssl(\s|$|;))", + "$0180$2$3", +}; + +// ------------------------- implementation: ---------------------------------- + +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! + rgx::smatch match; // assuming last capture has the value! - for (auto pos = conf.begin(); - rgx::regex_search(pos, conf.end(), match, LINE.RGX()); + 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); + const std::string value = match.str(match.size() - 2); - if (value==val || value=="'"+val+"'" || value=='"'+val+'"') { + if (value == val || value == "'" + val + "'" || value == '"' + val + '"') { return ""; } } @@ -267,75 +280,99 @@ auto get_if_missed(const std::string & conf, const Line & LINE, return LINE.STR(val, indent); } +auto replace_if(const std::string& conf, + const rgx::regex& rgx, + const std::string& val, + const std::string& insert) -> std::string +{ + std::string ret{}; + auto pos = conf.begin(); -auto delete_if(const std::string & conf, const rgx::regex & rgx, - const std::string & val, const bool compare) + auto skip = 0; + for (rgx::smatch match; rgx::regex_search(pos, conf.end(), match, rgx); + pos += match.position(match.size() - 1)) + { + auto i = match.size() - 2; + const std::string value = match.str(i); + + bool compare = !val.empty(); + if (compare && value != val && value != "'" + val + "'" && value != '"' + val + '"') { + ret.append(pos + skip, pos + match.position(i) + match.length(i)); + skip = 0; + } + else { + ret.append(pos + skip, pos + match.position(match.size() > 2 ? 1 : 0)); + ret += insert; + skip = 1; + } + } + + ret.append(pos + skip, conf.end()); + return ret; +} + +auto replace_listen(const std::string& conf, const std::array& ngx_port) -> 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)) + for (rgx::smatch match; rgx::regex_search(pos, conf.end(), match, NGX_LISTEN.RGX()); + pos += match.position(match.size() - 1)) { - 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); + auto i = match.size() - 2; + ret.append(pos, pos + match.position(i)); + ret += rgx::regex_replace(match.str(i), rgx::regex{ngx_port[0]}, ngx_port[1]); } ret.append(pos, conf.end()); return ret; } - -void add_ssl_directives_to(const std::string & name, const bool isdefault) +inline void add_ssl_directives_to(const std::string& name) { const std::string prefix = std::string{CONF_DIR} + name; - std::string conf = read_file(prefix+".conf"); + const std::string const_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 + 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)) + 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; } + if (!contains(match.str(2), name)) { + continue; + } // else: 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); + auto adds = std::string{}; - adds += get_if_missed(conf, NGX_SSL_CRT, prefix+".crt", indent); + adds += get_if_missed(const_conf, NGX_SSL_CRT, prefix + ".crt", indent); - adds += get_if_missed(conf, NGX_SSL_KEY, prefix+".key", indent); + adds += get_if_missed(const_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(const_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); + adds += get_if_missed(const_conf, NGX_SSL_SESSION_TIMEOUT, + std::string{SSL_SESSION_TIMEOUT_ARG}, indent, false); - if (adds.length() > 0) { - pos += match.position(0) + match.length(0); + pos += match.position(0) + match.length(0); + std::string conf = + std::string(const_conf.begin(), pos) + adds + std::string(pos, const_conf.end()); - conf = std::string(const_conf.begin(), pos) + adds + - std::string(pos, const_conf.end()); + conf = replace_if(conf, NGX_INCLUDE_LAN_LISTEN_DEFAULT.RGX(), "", + NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT.STR("", indent)); - conf = isdefault ? - delete_if(conf, NGX_INCLUDE_LAN_LISTEN_DEFAULT.RGX()) : - delete_if(conf, NGX_INCLUDE_LAN_LISTEN.RGX()); + conf = replace_if(conf, NGX_INCLUDE_LAN_LISTEN.RGX(), "", + NGX_INCLUDE_LAN_SSL_LISTEN.STR("", indent)); - write_file(prefix+".conf", conf); + conf = replace_listen(conf, NGX_PORT_80); - std::cerr<<"Added SSL directives to "< -inline auto num2hex(T bytes) -> std::array +template +inline auto num2hex(T bytes) -> std::array { - constexpr auto n = 2*sizeof(bytes); - std::array str{}; + constexpr auto n = 2 * sizeof(bytes); + std::array str{}; - for (size_t i=0; i hex{"0123456789ABCDEF"}; static constexpr auto get = 0x0fU; str.at(i) = hex.at(bytes & get); @@ -367,9 +403,8 @@ inline auto num2hex(T bytes) -> std::array return str; } - -template -inline auto get_nonce(const T salt=0) -> T +template +inline auto get_nonce(const T salt = 0) -> T { T nonce = 0; @@ -377,10 +412,12 @@ inline auto get_nonce(const T salt=0) -> T static constexpr auto move = 6U; - constexpr size_t steps = (sizeof(nonce)*8 - 1)/move + 1; + constexpr size_t steps = (sizeof(nonce) * 8 - 1) / move + 1; - for (size_t i=0; i(urandom.get()); } @@ -389,20 +426,19 @@ inline auto get_nonce(const T salt=0) -> T return nonce; } - -void create_ssl_certificate(const std::string & crtpath, - const std::string & keypath, - const int days) +inline void create_ssl_certificate(const std::string& crtpath, + const std::string& keypath, + const int days = 792) { size_t nonce = 0; - try { nonce = get_nonce(nonce); } + try { + nonce = get_nonce(nonce); + } - catch (...) { // the address of a variable should be random enough: - auto addr = &crtpath; - auto addrptr = static_cast( - static_cast(&addr) ); - nonce += *addrptr; + catch (...) { // the address of a variable should be random enough: + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) sic: + nonce += reinterpret_cast(&crtpath); } auto noncestr = num2hex(nonce); @@ -415,167 +451,434 @@ void create_ssl_certificate(const std::string & crtpath, write_key(pkey, tmpkeypath); - std::string subject {"/C=ZZ/ST=Somewhere/L=None/CN=OpenWrt/O=OpenWrt"}; + 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 to_seconds = 24 * 60 * 60; static constexpr auto leeway = 42; - if (!checkend(tmpcrtpath, days*to_seconds - leeway)) { + 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; - } catch (...) { - std::cerr<<"create_ssl_certificate error: "; - std::cerr<<"cannot create selfsigned certificate, "; - std::cerr<<"removing temporary files ..."< bool +{ + { // paths are relative to dir: + auto dir = std::string_view{"/etc/nginx"}; + auto crt_rel = crtpath[0] != '/'; + auto key_rel = keypath[0] != '/'; + if ((crt_rel || key_rel) && (chdir(dir.data()) != 0)) { + auto errmsg = std::string{"check_ssl_certificate error: entering "}; + errmsg += dir; + perror(errmsg.c_str()); + errmsg += " (need to change directory since the given "; + errmsg += crt_rel ? "ssl_certificate '" + crtpath : std::string{}; + errmsg += crt_rel && key_rel ? "' and " : ""; + errmsg += key_rel ? "ssl_certificate_key '" + keypath : std::string{}; + errmsg += crt_rel && key_rel ? "' are" : "' is a"; + errmsg += " relative path"; + errmsg += crt_rel && key_rel ? "s)" : ")"; + throw std::runtime_error(errmsg); + } + } + + 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); + } + + return is_valid; } +auto contains(const std::string& sentence, const std::string& word) -> bool +{ + auto pos = sentence.find(word); + if (pos == std::string::npos) { + return false; + } + if (pos != 0 && (isgraph(sentence[pos - 1]) != 0)) { + return false; + } + if (isgraph(sentence[pos + word.size()]) != 0) { + return false; + } + // else: + return true; +} -void use_cron_to_recreate_certificate(const std::string & name) +auto get_uci_section_for_name(const std::string& name) -> uci::section { - static const char * filename = "/etc/crontabs/root"; + auto pkg = uci::package{"nginx"}; // let it throw. + + auto uci_enabled = is_enabled(pkg); + + if (uci_enabled) { + for (auto sec : pkg) { + if (sec.name() == name) { + return sec; + } + } + // try interpreting 'name' as FQDN: + for (auto sec : pkg) { + for (auto opt : sec) { + if (opt.name() == "server_name") { + for (auto itm : opt) { + if (contains(itm.name(), name)) { + return sec; + } + } + } + } + } + } + + auto errmsg = std::string{"lookup error: neither there is a file named '"}; + errmsg += std::string{CONF_DIR} + name + ".conf' nor the UCI config has "; + if (uci_enabled) { + errmsg += "a nginx server with section name or 'server_name': " + name; + } + else { + errmsg += "been enabled by:\n\tuci set nginx.global.uci_enable=true"; + } + throw std::runtime_error(errmsg); +} + +inline auto add_ssl_to_config(const std::string& name, + const std::string_view manage = "self-signed", + const std::string_view crt = "", + const std::string_view key = "") +{ + auto sec = get_uci_section_for_name(name); // let it throw. + auto secname = sec.name(); + + struct { + std::string crt; + std::string key; + } ret; + + std::cerr << "Adding SSL directives to UCI server: nginx." << secname << "\n"; + + std::cerr << "\t" << MANAGE_SSL << "='" << manage << "'\n"; + sec.set(MANAGE_SSL.data(), manage.data()); + + if (!crt.empty() && !key.empty()) { + sec.set("ssl_certificate", crt.data()); + std::cerr << "\tssl_certificate='" << crt << "'\n"; + sec.set("ssl_certificate_key", key.data()); + std::cerr << "\tssl_certificate_key='" << key << "'\n"; + } + + auto cache = false; + auto timeout = false; + for (auto opt : sec) { + if (opt.name() == "ssl_session_cache") { + cache = true; + continue; + } // else: + + if (opt.name() == "ssl_session_timeout") { + timeout = true; + continue; + } + + // else: + for (auto itm : opt) { + if (opt.name() == "ssl_certificate_key") { + ret.key = itm.name(); + } + + else if (opt.name() == "ssl_certificate") { + ret.crt = itm.name(); + } + + else if (opt.name() == "listen") { + auto val = regex_replace(itm.name(), rgx::regex{NGX_PORT_80[0]}, NGX_PORT_80[1]); + if (val != itm.name()) { + std::cerr << "\t" << opt.name() << "='" << val << "' (replacing)\n"; + itm.rename(val.c_str()); + } + } + } + } + + if (ret.crt.empty()) { + ret.crt = std::string{CONF_DIR} + name + ".crt"; + std::cerr << "\tssl_certificate='" << ret.crt << "'\n"; + sec.set("ssl_certificate", ret.crt.c_str()); + } + + if (ret.key.empty()) { + ret.key = std::string{CONF_DIR} + name + ".key"; + std::cerr << "\tssl_certificate_key='" << ret.key << "'\n"; + sec.set("ssl_certificate_key", ret.key.c_str()); + } + + if (!cache) { + std::cerr << "\tssl_session_cache='" << SSL_SESSION_CACHE_ARG(name) << "'\n"; + sec.set("ssl_session_cache", SSL_SESSION_CACHE_ARG(name).data()); + } + + if (!timeout) { + std::cerr << "\tssl_session_timeout='" << SSL_SESSION_TIMEOUT_ARG << "'\n"; + sec.set("ssl_session_timeout", SSL_SESSION_TIMEOUT_ARG.data()); + } + + sec.commit(); + + return ret; +} + +void install_cron_job(const Line& CRON_LINE, 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. */ } + 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); + const std::string add = get_if_missed(conf, CRON_LINE, 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"; + if (!ubus::call("service", "list", UBUS_TIMEOUT).filter("cron")) { + std::string errmsg{"install_cron_job error: "}; + errmsg += "Cron unavailable to re-create the ssl certificate"; + errmsg += (name.empty() ? std::string{"s\n"} : " for '" + name + "'\n"); throw std::runtime_error(errmsg); - } // else active with or without instances: + } // else active with or without instances: #endif - write_file(filename, std::string{CRON_INTERVAL}+add, std::ios::app); + const auto* pre = (conf.length() == 0 || conf.back() == '\n' ? "" : "\n"); + write_file(filename, pre + 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< " + crt.data() + " ("; + errmsg += std::to_string(errno) + "): " + std::strerror(errno); + throw std::runtime_error(errmsg); + } + + auto keypath = std::string{CONF_DIR} + name + ".key"; + if (keypath != key && /* then */ symlink(key.data(), keypath.c_str()) != 0) { + auto errmsg = std::string{"add_ssl_if_needed error: cannot link "}; + errmsg += "ssl_certificate_key " + keypath + " -> " + key.data() + " ("; + errmsg += std::to_string(errno) + "): " + std::strerror(errno); + throw std::runtime_error(errmsg); + } -void add_ssl_if_needed(const std::string & name) + add_ssl_directives_to(name); // let it throw. +} + +void remove_cron_job(const Line& CRON_LINE, const std::string& name) { - add_ssl_directives_to(name, name==LAN_NAME); // let it throw. + static const char* filename = "/etc/crontabs/root"; - 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); + const auto const_conf = read_file(filename); - bool is_valid = true; + bool changed = false; + auto conf = std::string{}; - if (access(keypath.c_str(), R_OK) != 0 || - access(crtpath.c_str(), R_OK) != 0) - { is_valid = false; } + 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); - else { - try { - if (!checkend(crtpath, remaining_seconds)) { - is_valid = false; - } + if (line == replace_if(line, CRON_LINE.RGX(), name, "")) { + conf += line; } - 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; - } + else { + changed = true; } + + prev = curr + 1; } - if (!is_valid) { create_ssl_certificate(crtpath, keypath, validity_days); } + if (changed) { + write_file(filename, conf); - try { use_cron_to_recreate_certificate(name); } - catch (...) { - std::cerr<<"add_ssl_if_needed warning: "; - std::cerr<<"cannot use cron to rebuild certificate for "< 0) { - pos += match.position(1); + std::string conf = const_conf; - conf = std::string(const_conf.begin(), pos) + adds - + std::string(pos, const_conf.end()); + conf = replace_listen(conf, NGX_PORT_443); - conf = isdefault ? - delete_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT.RGX()) - : delete_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN.RGX()); + conf = replace_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT.RGX(), "", + NGX_INCLUDE_LAN_LISTEN_DEFAULT.STR("", indent)); - const auto crtpath = prefix+".crt"; - conf = delete_if(conf, NGX_SSL_CRT.RGX(), crtpath, true); + conf = replace_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN.RGX(), "", + NGX_INCLUDE_LAN_LISTEN.STR("", indent)); - const auto keypath = prefix+".key"; - conf = delete_if(conf, NGX_SSL_KEY.RGX(), keypath, true); + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) prefix: + conf = replace_if(conf, NGX_SSL_CRT.RGX(), prefix + ".crt", ""); - conf = delete_if(conf, NGX_SSL_SESSION_CACHE.RGX()); + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) prefix: + conf = replace_if(conf, NGX_SSL_KEY.RGX(), prefix + ".key", ""); - conf = delete_if(conf, NGX_SSL_SESSION_TIMEOUT.RGX()); + conf = replace_if(conf, NGX_SSL_SESSION_CACHE.RGX(), "", ""); - write_file(prefix+".conf", conf); + conf = replace_if(conf, NGX_SSL_SESSION_TIMEOUT.RGX(), "", ""); - std::cerr<<"Deleted SSL directives from "< bool +{ + const auto legacypath = std::string{CONF_DIR} + name + ".conf"; + + if (access(legacypath.c_str(), R_OK) != 0) { + return false; } - try { del_ssl_directives_from(name, name==LAN_NAME); } + try { + remove_cron_job(CRON_CMD, name); + } + catch (...) { + std::cerr << "del_ssl warning: cannot remove cron job rebuilding "; + std::cerr << "the self-signed SSL certificate for " << name << "\n"; + } + + try { + del_ssl_directives_from(name); + } catch (...) { - std::cerr<<"del_ssl error: "; - std::cerr<<"cannot delete SSL directives from "< bool +{ + auto are_valid = true; + auto is_enabled_and_at_least_one_has_manage_ssl = false; + + if (is_enabled) { + for (auto sec : pkg) { + if (sec.anonymous() || sec.type() != "server") { + continue; + } // else: + + const auto legacypath = std::string{CONF_DIR} + sec.name() + ".conf"; + if (access(legacypath.c_str(), R_OK) == 0) { + continue; + } // else: + + auto keypath = std::string{}; + auto crtpath = std::string{}; + auto self_signed = false; + + for (auto opt : sec) { + for (auto itm : opt) { + if (opt.name() == "ssl_certificate_key") { + keypath = itm.name(); + } + + else if (opt.name() == "ssl_certificate") { + crtpath = itm.name(); + } + + else if (opt.name() == MANAGE_SSL) { + if (itm.name() == "self-signed") { + self_signed = true; + } + + // else if (itm.name()=="???") { /* manage other */ } + + else { + continue; + } // no supported manage_ssl string. + + is_enabled_and_at_least_one_has_manage_ssl = true; + } + } + } + + if (self_signed && !crtpath.empty() && !keypath.empty()) { + try { + if (!check_ssl_certificate(crtpath, keypath)) { + are_valid = false; + } + } + catch (...) { + std::cerr << "check_ssl warning: cannot build certificate '"; + std::cerr << crtpath << "' or key '" << keypath << "'.\n"; + } + } + } + } + + auto suffix = std::string_view{" the cron job checking the managed SSL certificates.\n"}; + + if (is_enabled_and_at_least_one_has_manage_ssl) { + try { + install_cron_job(CRON_CHECK); + } + catch (...) { + std::cerr << "check_ssl warning: cannot install" << suffix; + } + } + + else if (access("/etc/crontabs/root", R_OK) == 0) { + try { + remove_cron_job(CRON_CHECK); + } + catch (...) { + std::cerr << "check_ssl warning: cannot remove" << suffix; + } + } // else: do nothing + + return are_valid; +} #endif diff --git a/net/nginx-util/src/nginx-util.cpp b/net/nginx-util/src/nginx-util.cpp index b0a724da6..3b4ad8c60 100644 --- a/net/nginx-util/src/nginx-util.cpp +++ b/net/nginx-util/src/nginx-util.cpp @@ -1,194 +1,378 @@ #include +#include -#include "nginx-util.hpp" - -#ifndef NO_SSL #include "nginx-ssl-util.hpp" -#endif +#include "nginx-util.hpp" +static auto constexpr file_comment_auto_created = + std::string_view{"# This file is re-created when Nginx starts.\n"}; -void create_lan_listen() +// TODO(pst) replace it with blobmsg_get_string if upstream takes const: +#ifndef NO_UBUS +static inline auto _pst_get_string(const blob_attr* attr) -> char* { - std::string listen = "# This file is re-created if Nginx starts or" - " a LAN address changes.\n"; - std::string listen_default = listen; - std::string ssl_listen = listen; - std::string ssl_listen_default = listen; - - auto add_listen = [&listen, &listen_default -#ifndef NO_SSL - ,&ssl_listen, &ssl_listen_default -#endif - ] - (const std::string &pre, const std::string &ip, const std::string &suf) - -> void - { - if (ip.empty()) { return; } - const std::string val = pre + ip + suf; - listen += "\tlisten " + val + ":80;\n"; - listen_default += "\tlisten " + val + ":80 default_server;\n"; -#ifndef NO_SSL - ssl_listen += "\tlisten " + val + ":443 ssl;\n"; - ssl_listen_default += "\tlisten " + val + ":443 ssl default_server;\n"; + return static_cast(blobmsg_data(attr)); +} #endif - }; + +void create_lan_listen() // create empty files for compatibility: +{ + // TODO(pst): replace by dummies after transitioning nginx config to UCI: + std::vector ips; #ifndef NO_UBUS try { - auto loopback_status=ubus::call("network.interface.loopback", "status"); + auto loopback_status = ubus::call("network.interface.loopback", "status"); - for (auto ip : loopback_status.filter("ipv4-address", "", "address")) { - add_listen("", static_cast(blobmsg_data(ip)), ""); + for (const auto* ip : loopback_status.filter("ipv4-address", "", "address")) { + ips.emplace_back(_pst_get_string(ip)); } - for (auto ip : loopback_status.filter("ipv6-address", "", "address")) { - add_listen("[", static_cast(blobmsg_data(ip)), "]"); + for (const auto* ip : loopback_status.filter("ipv6-address", "", "address")) { + ips.emplace_back(std::string{"["} + _pst_get_string(ip) + "]"); } - } catch (const std::runtime_error &) { /* do nothing about it */ } + } + catch (const std::runtime_error&) { /* do nothing about it */ + } try { auto lan_status = ubus::call("network.interface.lan", "status"); - for (auto ip : lan_status.filter("ipv4-address", "", "address")) { - add_listen("", static_cast(blobmsg_data(ip)), ""); + for (const auto* ip : lan_status.filter("ipv4-address", "", "address")) { + ips.emplace_back(_pst_get_string(ip)); } - for (auto ip : lan_status.filter("ipv6-address", "", "address")) { - add_listen("[", static_cast(blobmsg_data(ip)), "]"); + for (const auto* ip : lan_status.filter("ipv6-address", "", "address")) { + ips.emplace_back(std::string{"["} + _pst_get_string(ip) + "]"); } - for (auto ip : lan_status.filter("ipv6-prefix-assignment", "", - "local-address", "address")) - { - add_listen("[", static_cast(blobmsg_data(ip)), "]"); + for (const auto* ip : + lan_status.filter("ipv6-prefix-assignment", "", "local-address", "address")) { + ips.emplace_back(std::string{"["} + _pst_get_string(ip) + "]"); } - } catch (const std::runtime_error &) { /* do nothing about it */ } + } + catch (const std::runtime_error&) { /* do nothing about it */ + } #else - add_listen("", "127.0.0.1", ""); + ips.emplace_back("127.0.0.1"); #endif + std::string listen = std::string{file_comment_auto_created}; + std::string listen_default = std::string{file_comment_auto_created}; + for (const auto& ip : ips) { + listen += "\tlisten " + ip + ":80;\n"; + listen_default += "\tlisten " + ip + ":80 default_server;\n"; + } write_file(LAN_LISTEN, listen); write_file(LAN_LISTEN_DEFAULT, listen_default); -#ifndef NO_SSL + + std::string ssl_listen = std::string{file_comment_auto_created}; + std::string ssl_listen_default = std::string{file_comment_auto_created}; + for (const auto& ip : ips) { + ssl_listen += "\tlisten " + ip + ":443 ssl;\n"; + ssl_listen_default += "\tlisten " + ip + ":443 ssl default_server;\n"; + } write_file(LAN_SSL_LISTEN, ssl_listen); write_file(LAN_SSL_LISTEN_DEFAULT, ssl_listen_default); -#endif } +inline auto change_if_starts_with(const std::string_view& subject, + const std::string_view& prefix, + const std::string_view& substitute, + const std::string_view& seperator = " \t\n;") -> std::string +{ + auto view = subject; + view = view.substr(view.find_first_not_of(seperator)); + if (view.rfind(prefix, 0) == 0) { + if (view.size() == prefix.size()) { + return std::string{substitute}; + } + view = view.substr(prefix.size()); + if (seperator.find(view[0]) != std::string::npos) { + auto ret = std::string{substitute}; + ret += view; + return ret; + } + } + return std::string{subject}; +} + +inline auto create_server_conf(const uci::section& sec, const std::string& indent = "") + -> std::string +{ + auto secname = sec.name(); + + auto legacypath = std::string{CONF_DIR} + secname + ".conf"; + if (access(legacypath.c_str(), R_OK) == 0) { + auto message = std::string{"skipped UCI server 'nginx."} + secname; + message += "' as it could conflict with: " + legacypath + "\n"; + + // TODO(pst) std::cerr<<"create_server_conf notice: "< bool +{ + for (auto sec : pkg) { + if (sec.type() != std::string_view{"main"}) { + continue; + } + if (sec.name() != std::string_view{"global"}) { + continue; + } + for (auto opt : sec) { + if (opt.name() != "uci_enable") { + continue; + } + for (auto itm : opt) { + if (itm) { + return true; + } + } + } + } + return false; +} + +/* + * ___________main_thread________________|______________thread_1________________ + * create_lan_listen() or do nothing | config = uci::package("nginx") + * if config_enabled (set in thread_1): | config_enabled = is_enabled(config) + * then init_uci(config) | check_ssl(config, config_enabled) + */ void init_lan() { std::exception_ptr ex; - -#ifndef NO_SSL - auto thrd = std::thread([]{ //&ex - try { add_ssl_if_needed(std::string{LAN_NAME}); } + std::unique_ptr config; + bool config_enabled = false; + std::mutex configuring; + + configuring.lock(); + auto thrd = std::thread([&config, &config_enabled, &configuring, &ex] { + try { + config = std::make_unique("nginx"); + config_enabled = is_enabled(*config); + configuring.unlock(); + check_ssl(*config, config_enabled); + } catch (...) { - std::cerr<<"init_lan notice: no server named "< int +auto main(int argc, char* argv[]) -> int { // TODO(pst): use std::span when available: - auto args = std::basic_string_view{argv, static_cast(argc)}; + auto args = std::basic_string_view{argv, static_cast(argc)}; auto cmds = std::array{ std::array{"init_lan", ""}, std::array{"get_env", ""}, -#ifndef NO_SSL - std::array{ADD_SSL_FCT, " server_name" }, - std::array{"del_ssl", " server_name" }, -#endif + std::array{ + ADD_SSL_FCT, "server_name [manager /path/to/ssl_certificate /path/to/ssl_key]"}, + std::array{"del_ssl", "server_name [manager]"}, + std::array{"check_ssl", ""}, }; try { + if (argc == 2 && args[1] == cmds[0][0]) { + init_lan(); + } - if (argc==2 && args[1]==cmds[0][0]) { init_lan(); } + else if (argc == 2 && args[1] == cmds[1][0]) { + get_env(); + } - else if (argc==2 && args[1]==cmds[1][0]) { get_env(); } + else if (argc == 3 && args[1] == cmds[2][0]) { + add_ssl_if_needed(std::string{args[2]}); + } -#ifndef NO_SSL - else if (argc==3 && args[1]==cmds[2][0]) - { add_ssl_if_needed(std::string{args[2]});} + // NOLINTNEXTLINE(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers): 6 + else if (argc == 6 && args[1] == cmds[2][0]) { + // NOLINTNEXTLINE(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers): 5 + add_ssl_if_needed(std::string{args[2]}, args[3], args[4], args[5]); + } - else if (argc==3 && args[1]==cmds[3][0]) - { del_ssl(std::string{args[2]}); } + else if (argc == 3 && args[1] == cmds[3][0]) { + del_ssl(std::string{args[2]}); + } - else if (argc==2 && args[1]==cmds[3][0]) - { del_ssl(std::string{LAN_NAME}); } -#endif + else if (argc == 4 && args[1] == cmds[3][0]) { + del_ssl(std::string{args[2]}, args[3]); + } + + else if (argc == 2 && args[1] == cmds[3][0]) // TODO(pst) deprecate + { + try { + auto name = std::string{LAN_NAME}; + if (del_ssl_legacy(name)) { + auto crtpath = std::string{CONF_DIR} + name + ".crt"; + remove(crtpath.c_str()); + auto keypath = std::string{CONF_DIR} + name + ".key"; + remove(keypath.c_str()); + } + } + catch (...) { /* do nothing. */ + } + } + + else if (argc == 2 && args[1] == cmds[4][0]) { + check_ssl(uci::package{"nginx"}); + } else { - std::cerr<<"Tool for creating Nginx configuration files ("; + std::cerr << "Tool for creating Nginx configuration files ("; #ifdef VERSION - std::cerr<<"version "< #include #include -#include +// #include +#include #include +#include #include #ifndef NO_UBUS #include "ubus-cxx.hpp" #endif +#include "uci-cxx.hpp" static constexpr auto NGINX_UTIL = std::string_view{"/usr/bin/nginx-util"}; +static constexpr auto VAR_UCI_CONF = std::string_view{"/var/lib/nginx/uci.conf"}; + +static constexpr auto UCI_CONF = std::string_view{"/etc/nginx/uci.conf"}; + static constexpr auto NGINX_CONF = std::string_view{"/etc/nginx/nginx.conf"}; static constexpr auto CONF_DIR = std::string_view{"/etc/nginx/conf.d/"}; static constexpr auto LAN_NAME = std::string_view{"_lan"}; -static constexpr auto LAN_LISTEN =std::string_view{"/var/lib/nginx/lan.listen"}; +static auto constexpr MANAGE_SSL = std::string_view{"uci_manage_ssl"}; -static constexpr auto LAN_LISTEN_DEFAULT = - std::string_view{"/var/lib/nginx/lan.listen.default"}; +static constexpr auto LAN_LISTEN = std::string_view{"/var/lib/nginx/lan.listen"}; +static constexpr auto LAN_LISTEN_DEFAULT = // TODO(pst) deprecate + std::string_view{"/var/lib/nginx/lan.listen.default"}; // mode: optional ios::binary and/or ios::app (default ios::trunc) -void write_file(const std::string_view & name, const std::string & str, - std::ios_base::openmode flag=std::ios::trunc); - +void write_file(const std::string_view& name, + const std::string& str, + std::ios_base::openmode flag = std::ios::trunc); // mode: optional ios::binary (internally ios::ate|ios::in) -auto read_file(const std::string_view & name, - std::ios_base::openmode mode=std::ios::in) -> std::string; - +auto read_file(const std::string_view& name, std::ios_base::openmode mode = std::ios::in) + -> std::string; // all S must be convertible to const char[] -template -auto call(const std::string & program, S... args) -> pid_t; - +template +auto call(const std::string& program, S... args) -> pid_t; void create_lan_listen(); +void init_uci(const uci::package& pkg); -void init_lan(); +auto is_enabled(const uci::package& pkg) -> bool; +void init_lan(); void get_env(); - - // --------------------- partial implementation: ------------------------------ - -void write_file(const std::string_view & name, const std::string & str, +void write_file(const std::string_view& name, + const std::string& str, const std::ios_base::openmode flag) { auto tmp = std::string{name}; - if ( (flag & std::ios::ate) == 0 && (flag & std::ios::app) == 0 ) { + if ((flag & std::ios::ate) == 0 && (flag & std::ios::app) == 0) { tmp += ".tmp-XXXXXX"; auto fd = mkstemp(&tmp[0]); - if (fd==-1 || close(fd)!=0) - { throw std::runtime_error("write_file error: cannot access " + tmp); } + if (fd == -1 || close(fd) != 0) { + throw std::runtime_error("write_file error: cannot access " + tmp); + } } try { std::ofstream file(tmp.data(), flag); if (!file.good()) { - throw std::ofstream::failure - ("write_file error: cannot open " + std::string{tmp}); + throw std::ofstream::failure("write_file error: cannot open " + std::string{tmp}); } - file< std::string +auto read_file(const std::string_view& name, const std::ios_base::openmode mode) -> std::string { - std::ifstream file(name.data(), mode|std::ios::ate); + std::ifstream file(name.data(), mode | std::ios::ate); if (!file.good()) { - throw std::ifstream::failure( - "read_file error: cannot open " + std::string{name}); + throw std::ifstream::failure("read_file error: cannot open " + std::string{name}); } std::string ret{}; @@ -108,27 +113,25 @@ auto read_file(const std::string_view & name, ret.reserve(size); file.seekg(0); - ret.assign((std::istreambuf_iterator(file)), - std::istreambuf_iterator()); + ret.assign((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); return ret; } - -template -auto call(const char * program, S... args) -> pid_t +template +auto call(const char* program, S... args) -> pid_t { pid_t pid = fork(); - if (pid==0) { //child: - std::array argv = - { strdup(program), strdup(args)..., nullptr }; + if (pid == 0) { // child: + std::array argv = {strdup(program), strdup(args)..., nullptr}; - execv(program, argv.data()); // argv cannot be const char * const[]! + execv(program, argv.data()); // argv cannot be const char * const[]! _exit(EXIT_FAILURE); // exec never returns. - } else if (pid>0) { //parent: + } + else if (pid > 0) { // parent: return pid; } @@ -137,5 +140,4 @@ auto call(const char * program, S... args) -> pid_t throw std::runtime_error(errmsg); } - #endif diff --git a/net/nginx-util/src/px5g-openssl.hpp b/net/nginx-util/src/px5g-openssl.hpp index 1b70dc5b0..7c79bad97 100644 --- a/net/nginx-util/src/px5g-openssl.hpp +++ b/net/nginx-util/src/px5g-openssl.hpp @@ -3,15 +3,14 @@ // #define OPENSSL_API_COMPAT 0x10102000L #include -#include #include #include #include #include +#include +#include #include #include -#include - static constexpr auto rsa_min_modulus_bits = 512; @@ -19,67 +18,51 @@ using EVP_PKEY_ptr = std::unique_ptr; using X509_NAME_ptr = std::unique_ptr; - -auto checkend(const std::string & crtpath, - time_t seconds=0, bool use_pem=true) -> bool; - +auto checkend(const std::string& crtpath, time_t seconds = 0, bool use_pem = true) -> bool; auto gen_eckey(int curve) -> EVP_PKEY_ptr; +auto gen_rsakey(int keysize, BN_ULONG exponent = RSA_F4) -> EVP_PKEY_ptr; -auto gen_rsakey(int keysize, BN_ULONG exponent=RSA_F4) -> EVP_PKEY_ptr; - - -void write_key(const EVP_PKEY_ptr & pkey, - const std::string & keypath="", bool use_pem=true); - - -auto subject2name(const std::string & subject) -> X509_NAME_ptr; - - -void selfsigned(const EVP_PKEY_ptr & pkey, int days, - const std::string & subject="", const std::string & crtpath="", - bool use_pem=true); +void write_key(const EVP_PKEY_ptr& pkey, const std::string& keypath = "", bool use_pem = true); +auto subject2name(const std::string& subject) -> X509_NAME_ptr; +void selfsigned(const EVP_PKEY_ptr& pkey, + int days, + const std::string& subject = "", + const std::string& crtpath = "", + bool use_pem = true); // ------------------------- implementation: ---------------------------------- - -inline auto print_error(const char * str, const size_t /*len*/, void * errmsg) - -> int +inline auto print_error(const char* str, const size_t /*len*/, void* errmsg) -> int { - *static_cast(errmsg) += str; + *static_cast(errmsg) += str; return 0; } - // wrapper for clang-tidy: -inline auto _BIO_new_fp(FILE * stream, const bool use_pem, - const bool close=false) -> BIO * +inline auto _BIO_new_fp(FILE* stream, const bool use_pem, const bool close = false) -> BIO* { - return BIO_new_fp( stream, //NOLINTNEXTLINE(hicpp-signed-bitwise) macros: - (use_pem ? BIO_FP_TEXT : 0) | (close ? BIO_CLOSE : BIO_NOCLOSE) ); + return BIO_new_fp(stream, // NOLINTNEXTLINE(hicpp-signed-bitwise) macros: + (use_pem ? BIO_FP_TEXT : 0) | (close ? BIO_CLOSE : BIO_NOCLOSE)); } - -auto checkend(const std::string & crtpath, - const time_t seconds, const bool use_pem) -> bool +auto checkend(const std::string& crtpath, const time_t seconds, const bool use_pem) -> bool { - BIO * bio = crtpath.empty() ? - _BIO_new_fp(stdin, use_pem) : - BIO_new_file(crtpath.c_str(), (use_pem ? "r" : "rb")); + BIO* bio = crtpath.empty() ? _BIO_new_fp(stdin, use_pem) + : BIO_new_file(crtpath.c_str(), (use_pem ? "r" : "rb")); - X509 * x509 = nullptr; + X509* x509 = nullptr; if (bio != nullptr) { - x509 = use_pem ? - PEM_read_bio_X509_AUX(bio, nullptr, nullptr, nullptr) : - d2i_X509_bio(bio, nullptr); + x509 = use_pem ? PEM_read_bio_X509_AUX(bio, nullptr, nullptr, nullptr) + : d2i_X509_bio(bio, nullptr); BIO_free(bio); } - if (x509==nullptr) { + if (x509 == nullptr) { std::string errmsg{"checkend error: unable to load certificate\n"}; ERR_print_errors_cb(print_error, &errmsg); throw std::runtime_error(errmsg); @@ -93,10 +76,9 @@ auto checkend(const std::string & crtpath, return (cmp >= 0); } - auto gen_eckey(const int curve) -> EVP_PKEY_ptr { - EC_GROUP * group = curve!=0 ? EC_GROUP_new_by_curve_name(curve) : nullptr; + EC_GROUP* group = curve != 0 ? EC_GROUP_new_by_curve_name(curve) : nullptr; if (group == nullptr) { std::string errmsg{"gen_eckey error: cannot build group for curve id "}; @@ -109,12 +91,10 @@ auto gen_eckey(const int curve) -> EVP_PKEY_ptr EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED); - auto eckey = EC_KEY_new(); + auto* eckey = EC_KEY_new(); if (eckey != nullptr) { - if ( (EC_KEY_set_group(eckey, group) == 0) || - (EC_KEY_generate_key(eckey) == 0) ) - { + if ((EC_KEY_set_group(eckey, group) == 0) || (EC_KEY_generate_key(eckey) == 0)) { EC_KEY_free(eckey); eckey = nullptr; } @@ -132,7 +112,7 @@ auto gen_eckey(const int curve) -> EVP_PKEY_ptr EVP_PKEY_ptr pkey{EVP_PKEY_new(), ::EVP_PKEY_free}; // EVP_PKEY_assign_EC_KEY is a macro casting eckey to char *: - //NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) if (!EVP_PKEY_assign_EC_KEY(pkey.get(), eckey)) { EC_KEY_free(eckey); std::string errmsg{"gen_eckey error: cannot assign EC key to EVP\n"}; @@ -143,16 +123,15 @@ auto gen_eckey(const int curve) -> EVP_PKEY_ptr return pkey; } - auto gen_rsakey(const int keysize, const BN_ULONG exponent) -> EVP_PKEY_ptr { - if (keysizeOPENSSL_RSA_MAX_MODULUS_BITS) { + if (keysize < rsa_min_modulus_bits || keysize > OPENSSL_RSA_MAX_MODULUS_BITS) { std::string errmsg{"gen_rsakey error: RSA keysize ("}; errmsg += std::to_string(keysize) + ") out of range [512.."; errmsg += std::to_string(OPENSSL_RSA_MAX_MODULUS_BITS) + "]"; throw std::runtime_error(errmsg); } - auto bignum = BN_new(); + auto* bignum = BN_new(); if (bignum == nullptr) { std::string errmsg{"gen_rsakey error: cannot get big number struct\n"}; @@ -160,7 +139,7 @@ auto gen_rsakey(const int keysize, const BN_ULONG exponent) -> EVP_PKEY_ptr throw std::runtime_error(errmsg); } - auto rsa = RSA_new(); + auto* rsa = RSA_new(); if (rsa != nullptr) { if ((BN_set_word(bignum, exponent) == 0) || @@ -184,7 +163,7 @@ auto gen_rsakey(const int keysize, const BN_ULONG exponent) -> EVP_PKEY_ptr EVP_PKEY_ptr pkey{EVP_PKEY_new(), ::EVP_PKEY_free}; // EVP_PKEY_assign_RSA is a macro casting rsa to char *: - //NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) if (!EVP_PKEY_assign_RSA(pkey.get(), rsa)) { RSA_free(rsa); std::string errmsg{"gen_rsakey error: cannot assign RSA key to EVP\n"}; @@ -195,32 +174,36 @@ auto gen_rsakey(const int keysize, const BN_ULONG exponent) -> EVP_PKEY_ptr return pkey; } - -void write_key(const EVP_PKEY_ptr & pkey, - const std::string & keypath, const bool use_pem) +void write_key(const EVP_PKEY_ptr& pkey, const std::string& keypath, const bool use_pem) { - BIO * bio = nullptr; + BIO* bio = nullptr; - if (keypath.empty()) { bio = _BIO_new_fp(stdout, use_pem); } + if (keypath.empty()) { + bio = _BIO_new_fp(stdout, use_pem); + } - else { // BIO_new_file(keypath.c_str(), (use_pem ? "w" : "wb") ); + else { // BIO_new_file(keypath.c_str(), (use_pem ? "w" : "wb") ); static constexpr auto mask = 0600; // auto fd = open(keypath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mask); // creat has no cloexec, alt. triggers cppcoreguidelines-pro-type-vararg - //NOLINTNEXTLINE(android-cloexec-creat) - auto fd = creat(keypath.c_str(), mask); // the same without va_args. + // NOLINTNEXTLINE(android-cloexec-creat) + auto fd = creat(keypath.c_str(), mask); // the same without va_args. if (fd >= 0) { - auto fp = fdopen(fd, (use_pem ? "w" : "wb") ); + auto* fp = fdopen(fd, (use_pem ? "w" : "wb")); if (fp != nullptr) { bio = _BIO_new_fp(fp, use_pem, true); - if (bio == nullptr) { fclose(fp); } // (fp owns fd) + if (bio == nullptr) { + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) fp owns fd: + fclose(fp); + } + } + else { + close(fd); } - else { close(fd); } } - } if (bio == nullptr) { @@ -233,30 +216,27 @@ void write_key(const EVP_PKEY_ptr & pkey, int len = 0; - auto key = pkey.get(); - switch (EVP_PKEY_base_id(key)) { // use same format as px5g: + auto* key = pkey.get(); + switch (EVP_PKEY_base_id(key)) { // use same format as px5g: case EVP_PKEY_EC: - len = use_pem ? - PEM_write_bio_ECPrivateKey(bio, EVP_PKEY_get0_EC_KEY(key), - nullptr, nullptr, 0, nullptr, nullptr) : - i2d_ECPrivateKey_bio(bio, EVP_PKEY_get0_EC_KEY(key)); + len = use_pem ? PEM_write_bio_ECPrivateKey(bio, EVP_PKEY_get0_EC_KEY(key), nullptr, + nullptr, 0, nullptr, nullptr) + : i2d_ECPrivateKey_bio(bio, EVP_PKEY_get0_EC_KEY(key)); break; case EVP_PKEY_RSA: - len = use_pem ? - PEM_write_bio_RSAPrivateKey(bio, EVP_PKEY_get0_RSA(key), - nullptr, nullptr, 0, nullptr, nullptr) : - i2d_RSAPrivateKey_bio(bio, EVP_PKEY_get0_RSA(key)); + len = use_pem ? PEM_write_bio_RSAPrivateKey(bio, EVP_PKEY_get0_RSA(key), nullptr, + nullptr, 0, nullptr, nullptr) + : i2d_RSAPrivateKey_bio(bio, EVP_PKEY_get0_RSA(key)); break; default: - len = use_pem ? - PEM_write_bio_PrivateKey(bio, key, - nullptr, nullptr, 0, nullptr, nullptr) : - i2d_PrivateKey_bio(bio, key); + len = use_pem + ? PEM_write_bio_PrivateKey(bio, key, nullptr, nullptr, 0, nullptr, nullptr) + : i2d_PrivateKey_bio(bio, key); } BIO_free_all(bio); - if (len==0) { + if (len == 0) { std::string errmsg{"write_key error: cannot write EVP pkey to "}; errmsg += keypath.empty() ? "stdout" : keypath; errmsg += "\n"; @@ -265,10 +245,9 @@ void write_key(const EVP_PKEY_ptr & pkey, } } - -auto subject2name(const std::string & subject) -> X509_NAME_ptr +auto subject2name(const std::string& subject) -> X509_NAME_ptr { - if (!subject.empty() && subject[0]!='/') { + if (!subject.empty() && subject[0] != '/') { throw std::runtime_error("subject2name errror: not starting with /"); } @@ -280,56 +259,63 @@ auto subject2name(const std::string & subject) -> X509_NAME_ptr throw std::runtime_error(errmsg); } - if (subject.empty()) { return name; } + if (subject.empty()) { + return name; + } - size_t prev = 1; + int prev = 1; std::string type{}; char chr = '='; - for (size_t i=0; subject[i] != 0; ) { + for (int i = 0; subject[i] != 0;) { ++i; - if (subject[i]=='\\' && subject[++i]=='\0') { + if (subject[i] == '\\' && subject[++i] == '\0') { throw std::runtime_error("subject2name errror: escape at the end"); } - if (subject[i]!=chr && subject[i]!='\0') { continue; } + if (subject[i] != chr && subject[i] != '\0') { + continue; + } if (chr == '=') { - type = subject.substr(prev, i-prev); + type = subject.substr(prev, i - prev); chr = '/'; - } else { + } + else { auto nid = OBJ_txt2nid(type.c_str()); if (nid == NID_undef) { // skip unknown entries (silently?). - } else { - auto val = // X509_NAME_add_entry_by_NID wants it unsigned: - //NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - reinterpret_cast(&subject[prev]); + } + else { + const auto* val = // X509_NAME_add_entry_by_NID wants it unsigned: + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(&subject[prev]); - auto len = i - prev; + int len = i - prev; - if ( X509_NAME_add_entry_by_NID(name.get(), nid, - MBSTRING_ASC, //NOLINT(hicpp-signed-bitwise) is macro - val, len, -1, 0) - == 0 ) + if (X509_NAME_add_entry_by_NID( + name.get(), nid, + MBSTRING_ASC, // NOLINT(hicpp-signed-bitwise) is macro + val, len, -1, 0) == 0) { std::string errmsg{"subject2name error: cannot add "}; - errmsg += "/" + type +"="+ subject.substr(prev, len) +"\n"; + errmsg += "/" + type + "=" + subject.substr(prev, len) + "\n"; ERR_print_errors_cb(print_error, &errmsg); throw std::runtime_error(errmsg); } } chr = '='; } - prev = i+1; + prev = i + 1; } return name; } - -void selfsigned(const EVP_PKEY_ptr & pkey, const int days, - const std::string & subject, const std::string & crtpath, +void selfsigned(const EVP_PKEY_ptr& pkey, + const int days, + const std::string& subject, + const std::string& crtpath, const bool use_pem) { - auto x509 = X509_new(); + auto* x509 = X509_new(); if (x509 == nullptr) { std::string errmsg{"selfsigned error: cannot create X509 structure\n"}; @@ -337,8 +323,7 @@ void selfsigned(const EVP_PKEY_ptr & pkey, const int days, throw std::runtime_error(errmsg); } - auto freeX509_and_throw = [&x509](const std::string & what) - { + auto freeX509_and_throw = [&x509](const std::string& what) { X509_free(x509); std::string errmsg{"selfsigned error: cannot set "}; errmsg += what + " in X509 certificate\n"; @@ -346,19 +331,25 @@ void selfsigned(const EVP_PKEY_ptr & pkey, const int days, throw std::runtime_error(errmsg); }; - if (X509_set_version(x509, 2) == 0) { freeX509_and_throw("version"); } + if (X509_set_version(x509, 2) == 0) { + freeX509_and_throw("version"); + } - if (X509_set_pubkey(x509, pkey.get()) == 0) { freeX509_and_throw("pubkey");} + if (X509_set_pubkey(x509, pkey.get()) == 0) { + freeX509_and_throw("pubkey"); + } if ((X509_gmtime_adj(X509_getm_notBefore(x509), 0) == nullptr) || - (X509_time_adj_ex(X509_getm_notAfter(x509), days,0,nullptr) == nullptr)) + (X509_time_adj_ex(X509_getm_notAfter(x509), days, 0, nullptr) == nullptr)) { freeX509_and_throw("times"); } X509_NAME_ptr name{nullptr, ::X509_NAME_free}; - try { name = subject2name(subject); } + try { + name = subject2name(subject); + } catch (...) { X509_free(x509); throw; @@ -372,9 +363,11 @@ void selfsigned(const EVP_PKEY_ptr & pkey, const int days, freeX509_and_throw("issuer"); } - auto bignum = BN_new(); + auto* bignum = BN_new(); - if (bignum == nullptr) { freeX509_and_throw("serial (creating big number struct)"); } + if (bignum == nullptr) { + freeX509_and_throw("serial (creating big number struct)"); + } static const auto BITS = 159; if (BN_rand(bignum, BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY) == 0) { @@ -393,22 +386,19 @@ void selfsigned(const EVP_PKEY_ptr & pkey, const int days, freeX509_and_throw("signing digest"); } - BIO * bio = crtpath.empty() ? - _BIO_new_fp(stdout, use_pem) : - BIO_new_file(crtpath.c_str(), (use_pem ? "w" : "wb")); + BIO* bio = crtpath.empty() ? _BIO_new_fp(stdout, use_pem) + : BIO_new_file(crtpath.c_str(), (use_pem ? "w" : "wb")); int len = 0; if (bio != nullptr) { - len = use_pem ? - PEM_write_bio_X509(bio, x509) : - i2d_X509_bio(bio, x509); + len = use_pem ? PEM_write_bio_X509(bio, x509) : i2d_X509_bio(bio, x509); BIO_free_all(bio); } X509_free(x509); - if (len==0) { + if (len == 0) { std::string errmsg{"selfsigned error: cannot write certificate to "}; errmsg += crtpath.empty() ? "stdout" : crtpath; errmsg += "\n"; @@ -417,5 +407,4 @@ void selfsigned(const EVP_PKEY_ptr & pkey, const int days, } } - #endif diff --git a/net/nginx-util/src/px5g.cpp b/net/nginx-util/src/px5g.cpp index e2a073d35..4edfb3b8e 100644 --- a/net/nginx-util/src/px5g.cpp +++ b/net/nginx-util/src/px5g.cpp @@ -1,66 +1,53 @@ -#include "px5g-openssl.hpp" +#include #include #include +#include #include #include -#include - - -class argv_view { // TODO(pst): use std::span when available. - -private: - - std::basic_string_view data; - -public: - - argv_view(const argv_view &) = delete; - - - argv_view(argv_view &&) = delete; - +#include "px5g-openssl.hpp" - auto operator=(const argv_view &) -> argv_view & = delete; +class argv_view { // TODO(pst): use std::span when available. + private: + std::basic_string_view data; - auto operator=(argv_view &&) -> argv_view & = delete; + public: + argv_view(const argv_view&) = delete; + argv_view(argv_view&&) = delete; - argv_view(const char ** argv, int argc) : - data{argv, static_cast(argc)} {} + auto operator=(const argv_view&) -> argv_view& = delete; + auto operator=(argv_view &&) -> argv_view& = delete; - inline auto operator[] (size_t pos) const -> std::string_view - { return std::string_view{data[pos]}; } + argv_view(const char** argv, int argc) : data{argv, static_cast(argc)} {} + inline auto operator[](size_t pos) const -> std::string_view + { + return std::string_view{data[pos]}; + } [[nodiscard]] inline constexpr auto size() const noexcept -> size_t - { return data.size(); } - + { + return data.size(); + } ~argv_view() = default; - }; - static const auto default_validity = 30; +auto checkend(const argv_view& argv) -> int; -auto checkend(const argv_view & argv) -> int; - - -void eckey(const argv_view & argv); - - -void rsakey(const argv_view & argv); +void eckey(const argv_view& argv); +void rsakey(const argv_view& argv); -void selfsigned(const argv_view & argv); +void selfsigned(const argv_view& argv); - -inline auto parse_int(const std::string_view & arg) -> int +inline auto parse_int(const std::string_view& arg) -> int { - size_t pos; + size_t pos = 0; int ret = stoi(std::string{arg}, &pos); if (pos < arg.size()) { throw std::runtime_error("number has trailing char"); @@ -68,28 +55,35 @@ inline auto parse_int(const std::string_view & arg) -> int return ret; } - -inline auto parse_curve(const std::string_view & name) -> int +inline auto parse_curve(const std::string_view& name) -> int { - if (name=="P-384") { return NID_secp384r1; } - if (name=="P-521") { return NID_secp521r1; } - if (name=="P-256" || name=="secp256r1") { return NID_X9_62_prime256v1; } - if (name=="secp192r1") { return NID_X9_62_prime192v1; } + if (name == "P-384") { + return NID_secp384r1; + } + if (name == "P-521") { + return NID_secp521r1; + } + if (name == "P-256" || name == "secp256r1") { + return NID_X9_62_prime256v1; + } + if (name == "secp192r1") { + return NID_X9_62_prime192v1; + } return OBJ_sn2nid(name.data()); // not: if (curve == 0) { curve = EC_curve_nist2nid(name.c_str()); } } - -auto checkend(const argv_view & argv) -> int +auto checkend(const argv_view& argv) -> int { bool use_pem = true; std::string crtpath{}; time_t seconds = 0; - for (size_t i=2; i= argv.size()) { @@ -98,24 +92,26 @@ auto checkend(const argv_view & argv) -> int if (!crtpath.empty()) { if (argv[i] == crtpath) { - std::cerr<<"checkend warning: repeated same -in file\n"; - } else { - throw std::runtime_error - ("checkend error: more than one -in file"); + std::cerr << "checkend warning: repeated same -in file\n"; + } + else { + throw std::runtime_error("checkend error: more than one -in file"); } } crtpath = argv[i]; } - else if (argv[i][0]=='-') { - std::cerr<<"checkend warning: skipping option "< int seconds = static_cast(num); - if (num!=static_cast(seconds)) { + if (num != static_cast(seconds)) { auto errmsg = std::string{"checkend error: time too big "}; errmsg += argv[i]; throw std::runtime_error(errmsg); @@ -132,23 +128,23 @@ auto checkend(const argv_view & argv) -> int } bool valid = checkend(crtpath, seconds, use_pem); - std::cout<<"Certificate will"<<(valid ? " not " : " ")<<"expire"<= argv.size()) { @@ -156,25 +152,25 @@ void eckey(const argv_view & argv) } if (!keypath.empty()) { - if (argv[i]==keypath) { - std::cerr<<"eckey warning: repeated same -out file\n"; - } else { - throw std::runtime_error - ("eckey error: more than one -out file"); + if (argv[i] == keypath) { + std::cerr << "eckey warning: repeated same -out file\n"; + } + else { + throw std::runtime_error("eckey error: more than one -out file"); } } keypath = argv[i]; } - else if (argv[i][0]=='-') { - std::cerr<<"eckey warning: skipping option "<= argv.size()) { @@ -206,29 +203,31 @@ void rsakey(const argv_view & argv) } if (!keypath.empty()) { - if (argv[i]==keypath) { - std::cerr<<"rsakey warning: repeated -out file"<= argv.size()) { - throw std::runtime_error - ("selfsigned error: -newkey misses algorithm option"); + throw std::runtime_error("selfsigned error: -newkey misses algorithm option"); } static constexpr auto rsa_prefix = std::string_view{"rsa:"}; - if (argv[i]=="ec") { + if (argv[i] == "ec") { use_rsa = false; - } else if (argv[i].rfind(rsa_prefix, 0) == 0) { + } + else if (argv[i].rfind(rsa_prefix, 0) == 0) { use_rsa = true; try { keysize = parse_int(argv[i].substr(rsa_prefix.size())); - } catch (...) { + } + catch (...) { std::string errmsg{"selfsigned error: invalid keysize "}; errmsg += argv[i].substr(4); std::throw_with_nested(std::runtime_error(errmsg)); } - } else { + } + else { throw std::runtime_error("selfsigned error: invalid algorithm"); } } - else if (argv[i]=="-pkeyopt") { + else if (argv[i] == "-pkeyopt") { ++i; if (i >= argv.size()) { - throw std::runtime_error - ("selfsigned error: -pkeyopt misses value"); + throw std::runtime_error("selfsigned error: -pkeyopt misses value"); } - static constexpr auto curve_prefix = - std::string_view{"ec_paramgen_curve:"}; + static constexpr auto curve_prefix = std::string_view{"ec_paramgen_curve:"}; if (argv[i].rfind(curve_prefix, 0) != 0) { throw std::runtime_error("selfsigned error: -pkeyopt invalid"); @@ -312,60 +312,57 @@ void selfsigned(const argv_view & argv) curve = parse_curve(argv[i].substr(curve_prefix.size())); } - else if (argv[i]=="-keyout") { + else if (argv[i] == "-keyout") { ++i; if (i >= argv.size()) { - throw std::runtime_error - ("selfsigned error: -keyout misses path"); + throw std::runtime_error("selfsigned error: -keyout misses path"); } if (!keypath.empty()) { - if (argv[i]==keypath) { - std::cerr<<"selfsigned warning: repeated -keyout file\n"; - } else { - throw std::runtime_error - ("selfsigned error: more than one -keyout file"); + if (argv[i] == keypath) { + std::cerr << "selfsigned warning: repeated -keyout file\n"; + } + else { + throw std::runtime_error("selfsigned error: more than one -keyout file"); } } keypath = argv[i]; } - else if (argv[i]=="-out") { + else if (argv[i] == "-out") { ++i; if (i >= argv.size()) { - throw std::runtime_error - ("selfsigned error: -out misses filename"); + throw std::runtime_error("selfsigned error: -out misses filename"); } if (!crtpath.empty()) { - if (argv[i]==crtpath) { - std::cerr<<"selfsigned warning: repeated same -out file\n"; - } else { - throw std::runtime_error - ("selfsigned error: more than one -out file"); + if (argv[i] == crtpath) { + std::cerr << "selfsigned warning: repeated same -out file\n"; + } + else { + throw std::runtime_error("selfsigned error: more than one -out file"); } } crtpath = argv[i]; } - else if (argv[i]=="-subj") { + else if (argv[i] == "-subj") { ++i; if (i >= argv.size()) { - throw std::runtime_error - ("selfsigned error: -subj misses value"); + throw std::runtime_error("selfsigned error: -subj misses value"); } if (!subject.empty()) { - if (argv[i]==subject) { - std::cerr<<"selfsigned warning: repeated same -subj\n"; - } else { - throw std::runtime_error - ("selfsigned error: more than one -subj value"); + if (argv[i] == subject) { + std::cerr << "selfsigned warning: repeated same -subj\n"; + } + else { + throw std::runtime_error("selfsigned error: more than one -subj value"); } } @@ -373,7 +370,7 @@ void selfsigned(const argv_view & argv) } else { - std::cerr<<"selfsigned warning: skipping option "< int +auto main(int argc, const char** argv) -> int { auto args = argv_view{argv, argc}; auto cmds = std::array{ std::array{"checkend", - " [-der] [-in certificate_path] [seconds_remaining]" - }, - std::array{"eckey", - " [-der] [-out key_path] [curve_name]" - }, - std::array{"rsakey", - " [-der] [-out key_path] [-3] [key_size]" - }, - std::array{"selfsigned", + " [-der] [-in certificate_path] [seconds_remaining]"}, + std::array{"eckey", " [-der] [-out key_path] [curve_name]"}, + std::array{"rsakey", " [-der] [-out key_path] [-3] [key_size]"}, + std::array{ + "selfsigned", " [-der] [-keyout key_path] [-out certificate_path]" " [-newkey ec|rsa:key_size] [-pkeyopt ec_paramgen_curve:name]" - " [-days validity] [-subj /C=.../ST=.../L=.../O=.../CN=.../... ]" - }, + " [-days validity] [-subj /C=.../ST=.../L=.../O=.../CN=.../... ]"}, }; try { - if (argc < 2) { throw std::runtime_error("error: no argument"); } + if (argc < 2) { + throw std::runtime_error("error: no argument"); + } - if (args[1]==cmds[0][0]) {return checkend(args);} + if (args[1] == cmds[0][0]) { + return checkend(args); + } - if (args[1]==cmds[1][0]) { eckey(args); } + if (args[1] == cmds[1][0]) { + eckey(args); + } - else if (args[1]==cmds[2][0]) { rsakey(args); } + else if (args[1] == cmds[2][0]) { + rsakey(args); + } - else if (args[1]==cmds[3][0]) { selfsigned(args); } + else if (args[1] == cmds[3][0]) { + selfsigned(args); + } - else { throw std::runtime_error("error: argument not recognized"); } + else { + throw std::runtime_error("error: argument not recognized"); + } } - catch (const std::exception & e) { - - auto usage = std::string{"usage: \n"} ; - for (auto cmd : cmds) { - usage += std::string{4, ' '} + *argv +" "+ cmd[0] + cmd[1] +"\n"; - } + catch (const std::exception& e) { + auto usage = std::accumulate( + cmds.begin(), cmds.end(), std::string{"usage: \n"}, + [=](const auto& use, const auto& cmd) { + return use + std::string{4, ' '} + *argv + " " + cmd[0] + cmd[1] + "\n"; + }); - std::cerr< void - { - std::cerr< void { + std::cerr << std::string(depth, '\t') << outer.what() << std::endl; + try { + std::rethrow_if_nested(outer); + } + catch (const std::exception& inner) { + self(self, inner, depth + 1); + } }; print_nested(print_nested, e); @@ -443,7 +450,7 @@ auto main(int argc, const char ** argv) -> int } catch (...) { - std::cerr<<*argv<<" unknown error."< #include +#include #include #include #include - namespace rgx { /* partially implement the std::regex interface using PCRE for performance * (=> pass "match" as non-const reference) */ - namespace regex_constants { - enum error_type - { - _enum_error_collate, - _enum_error_ctype, - _enum_error_escape, - _enum_error_backref, - _enum_error_brack, - _enum_error_paren, - _enum_error_brace, - _enum_error_badbrace, - _enum_error_range, - _enum_error_space, - _enum_error_badrepeat, - _enum_error_complexity, - _enum_error_stack, - _enum_error_last - }; - static const error_type error_collate(_enum_error_collate); - static const error_type error_ctype(_enum_error_ctype); - static const error_type error_escape(_enum_error_escape); - static const error_type error_backref(_enum_error_backref); - static const error_type error_brack(_enum_error_brack); - static const error_type error_paren(_enum_error_paren); - static const error_type error_brace(_enum_error_brace); - static const error_type error_badbrace(_enum_error_badbrace); - static const error_type error_range(_enum_error_range); - static const error_type error_space(_enum_error_space); - static const error_type error_badrepeat(_enum_error_badrepeat); - static const error_type error_complexity(_enum_error_complexity); - static const error_type error_stack(_enum_error_stack); -} // namespace regex_constants - - +enum error_type { + _enum_error_collate, + _enum_error_ctype, + _enum_error_escape, + _enum_error_backref, + _enum_error_brack, + _enum_error_paren, + _enum_error_brace, + _enum_error_badbrace, + _enum_error_range, + _enum_error_space, + _enum_error_badrepeat, + _enum_error_complexity, + _enum_error_stack, + _enum_error_last +}; +static const error_type error_collate(_enum_error_collate); +static const error_type error_ctype(_enum_error_ctype); +static const error_type error_escape(_enum_error_escape); +static const error_type error_backref(_enum_error_backref); +static const error_type error_brack(_enum_error_brack); +static const error_type error_paren(_enum_error_paren); +static const error_type error_brace(_enum_error_brace); +static const error_type error_badbrace(_enum_error_badbrace); +static const error_type error_range(_enum_error_range); +static const error_type error_space(_enum_error_space); +static const error_type error_badrepeat(_enum_error_badrepeat); +static const error_type error_complexity(_enum_error_complexity); +static const error_type error_stack(_enum_error_stack); +} // namespace regex_constants class regex_error : public std::runtime_error { - -private: - + private: regex_constants::error_type errcode; + public: + explicit regex_error(regex_constants::error_type code, const char* what = "regex error") + : runtime_error(what), errcode(code) + {} -public: - - explicit regex_error(regex_constants::error_type code, - const char * what="regex error") - : runtime_error(what), errcode(code) - { } - - - [[nodiscard]] auto code() const -> regex_constants::error_type - { return errcode; } - + [[nodiscard]] auto virtual code() const -> regex_constants::error_type; }; - +[[nodiscard]] auto regex_error::code() const -> regex_constants::error_type +{ + return errcode; +} class regex { - -private: - + private: int errcode = 0; - const char * errptr = nullptr; + const char* errptr = nullptr; int erroffset = 0; - pcre * const re = nullptr; + pcre* const re = nullptr; - static const std::array errcode_pcre2regex; + static const std::array errcode_pcre2regex; static const auto BASE = 10; - -public: - + public: inline regex() = default; + inline regex(const regex&) = delete; - inline regex(const regex &) = delete; - - - inline regex(regex &&) = default; - - - inline auto operator=(const regex &) -> regex & = delete; + inline regex(regex&&) = default; + inline auto operator=(const regex&) -> regex& = delete; - inline auto operator=(regex &&) -> regex & = delete; + inline auto operator=(regex &&) -> regex& = delete; + explicit regex(const std::string& str) : regex(str.c_str()) {} - explicit regex(const std::string & str) - : re{ pcre_compile2(str.c_str(), 0, &errcode, &errptr, &erroffset,nullptr) } + explicit regex(const char* const str) + : re{pcre_compile2(str, 0, &errcode, &errptr, &erroffset, nullptr)} { - if (re==nullptr) { + if (re == nullptr) { std::string what = std::string("regex error: ") + errptr + '\n'; - what += " '" + str + "'\n"; + what += " '" + std::string{str} + "'\n"; what += " " + std::string(erroffset, ' ') + '^'; throw regex_error(errcode_pcre2regex.at(errcode), what.c_str()); } } + ~regex() + { + if (re != nullptr) { + pcre_free(re); + } + } - ~regex() { if (re != nullptr) { pcre_free(re); } } - - - inline auto operator()() const -> const pcre * { return re; } - + inline auto operator()() const -> const pcre* + { + return re; + } }; - - class smatch { - friend auto regex_search(std::string::const_iterator begin, std::string::const_iterator end, - smatch & match, //NOLINT(google-runtime-references) - const regex & rgx); // match std::regex interface. - - -private: + smatch& match, // NOLINT(google-runtime-references) + const regex& rgx); // match std::regex interface. + private: std::string::const_iterator begin; std::string::const_iterator end; - std::vector vec{}; + std::vector vec{}; int n = 0; - -public: - - [[nodiscard]] inline auto position(int i=0) const { - return (i<0 || i>=n) ? std::string::npos : vec[2*i]; + public: + [[nodiscard]] inline auto position(int i = 0) const + { + return (i < 0 || i >= n) ? std::string::npos : vec[2 * i]; } - - [[nodiscard]] inline auto length(int i=0) const { - return (i<0 || i>=n) ? 0 : vec[2*i+1] - vec[2*i]; + [[nodiscard]] inline auto length(int i = 0) const + { + return (i < 0 || i >= n) ? 0 : vec[2 * i + 1] - vec[2 * i]; } - - [[nodiscard]] auto str(int i=0) const -> std::string { // should we throw? - if (i<0 || i>=n) { return ""; } - int x = vec[2*i]; - if (x<0) { return ""; } - int y = vec[2*i+1]; + [[nodiscard]] auto str(int i = 0) const -> std::string + { // should we throw? + if (i < 0 || i >= n) { + return ""; + } + int x = vec[2 * i]; + if (x < 0) { + return ""; + } + int y = vec[2 * i + 1]; return std::string{begin + x, begin + y}; } + [[nodiscard]] auto format(const std::string& fmt) const; - [[nodiscard]] auto format(const std::string & fmt) const; - - - [[nodiscard]] auto size() const -> int { return n; } - - - [[nodiscard]] inline auto empty() const { return n<0; } - + [[nodiscard]] auto size() const -> int + { + return n; + } - [[nodiscard]] inline auto ready() const { return !vec.empty(); } + [[nodiscard]] inline auto empty() const + { + return n < 0; + } + [[nodiscard]] inline auto ready() const + { + return !vec.empty(); + } }; +inline auto regex_search(const std::string& subj, const regex& rgx); -inline auto regex_search(const std::string & subj, const regex & rgx); - - -auto regex_replace(const std::string & subj, - const regex & rgx, - const std::string & insert); - - -inline auto regex_search(const std::string & subj, - smatch & match, //NOLINT(google-runtime-references) - const regex & rgx); // match std::regex interface. +auto regex_replace(const std::string& subj, const regex& rgx, const std::string& insert); +inline auto regex_search(const std::string& subj, + smatch& match, // NOLINT(google-runtime-references) + const regex& rgx); // match std::regex interface. auto regex_search(std::string::const_iterator begin, std::string::const_iterator end, - smatch & match, //NOLINT(google-runtime-references) - const regex & rgx); // match std::regex interface. - - + smatch& match, // NOLINT(google-runtime-references) + const regex& rgx); // match std::regex interface. // ------------------------- implementation: ---------------------------------- - -inline auto regex_search(const std::string & subj, const regex & rgx) +inline auto regex_search(const std::string& subj, const regex& rgx) { - if (rgx()==nullptr) { + if (rgx() == nullptr) { throw std::runtime_error("regex_search error: no regex given"); } - int n = pcre_exec(rgx(), nullptr, subj.c_str(), subj.length(), - 0, 0, nullptr, 0); - return n>=0; + int n = + pcre_exec(rgx(), nullptr, subj.c_str(), static_cast(subj.length()), 0, 0, nullptr, 0); + return n >= 0; } - auto regex_search(const std::string::const_iterator begin, const std::string::const_iterator end, - smatch & match, - const regex & rgx) + smatch& match, + const regex& rgx) { - if (rgx()==nullptr) { + if (rgx() == nullptr) { throw std::runtime_error("regex_search error: no regex given"); } int sz = 0; pcre_fullinfo(rgx(), nullptr, PCRE_INFO_CAPTURECOUNT, &sz); - sz = 3*(sz + 1); + sz = 3 * (sz + 1); match.vec.reserve(sz); - const char * subj = &*begin; - size_t len = &*end - subj; + const char* subj = &*begin; + int len = static_cast(&*end - subj); match.begin = begin; match.end = end; match.n = pcre_exec(rgx(), nullptr, subj, len, 0, 0, &match.vec[0], sz); - if (match.n<0) { return false; } - if (match.n==0) { match.n = sz/3; } + if (match.n < 0) { + return false; + } + if (match.n == 0) { + match.n = sz / 3; + } return true; } - -inline auto regex_search(const std::string & subj, smatch & match, - const regex & rgx) +inline auto regex_search(const std::string& subj, smatch& match, const regex& rgx) { return regex_search(subj.begin(), subj.end(), match, rgx); } - -auto smatch::format(const std::string & fmt) const { +auto smatch::format(const std::string& fmt) const +{ std::string ret{}; size_t index = 0; - size_t pos; - while ((pos=fmt.find('$', index)) != std::string::npos) { - ret.append(fmt, index, pos-index); + size_t pos = 0; + while ((pos = fmt.find('$', index)) != std::string::npos) { + ret.append(fmt, index, pos - index); index = pos + 1; char chr = fmt[index++]; - switch(chr) { - - case '&': // match + switch (chr) { + case '&': // match ret += str(0); break; - case '`': // prefix - ret.append(begin, begin+vec[0]); + case '`': // prefix + ret.append(begin, begin + vec[0]); break; - case '\'': // suffix - ret.append(begin+vec[1], end); + case '\'': // suffix + ret.append(begin + vec[1], end); break; default: - if (isdigit(chr) != 0) { // one or two digits => submatch: + if (isdigit(chr) != 0) { // one or two digits => submatch: int num = chr - '0'; chr = fmt[index]; - if (isdigit(chr) != 0) { // second digit: + if (isdigit(chr) != 0) { // second digit: ++index; static const auto base = 10; - num = num*base + chr - '0'; + num = num * base + chr - '0'; } ret += str(num); break; - } //else: + } // else: ret += '$'; [[fallthrough]]; - case '$': // escaped + case '$': // escaped ret += chr; } } @@ -300,20 +281,16 @@ auto smatch::format(const std::string & fmt) const { return ret; } - -auto regex_replace(const std::string & subj, - const regex & rgx, - const std::string & insert) +auto regex_replace(const std::string& subj, const regex& rgx, const std::string& insert) { - if (rgx()==nullptr) { + if (rgx() == nullptr) { throw std::runtime_error("regex_replace error: no regex given"); } std::string ret{}; auto pos = subj.begin(); - for (smatch match; - regex_search(pos, subj.end(), match, rgx); + for (smatch match; regex_search(pos, subj.end(), match, rgx); pos += match.position(0) + match.length(0)) { ret.append(pos, pos + match.position(0)); @@ -324,11 +301,8 @@ auto regex_replace(const std::string & subj, return ret; } - - // ------------ There is only the translation table below : ------------------- - const std::array regex::errcode_pcre2regex = { // 0 no error regex_constants::error_type::_enum_error_last, @@ -444,7 +418,8 @@ const std::array regex::errcode_pcre2regex = { regex_constants::error_backref, // 56 inconsistent NEWLINE options regex_constants::error_escape, - // 57 \g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number + // 57 \g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain + // number regex_constants::error_backref, // 58 a numbered reference must not be zero regex_constants::error_backref, @@ -501,11 +476,8 @@ const std::array regex::errcode_pcre2regex = { // 84 group name must start with a non-digit regex_constants::error_backref, // 85 parentheses are too deeply nested (stack check) - regex_constants::error_stack -}; - - -} // namespace rgx + regex_constants::error_stack}; +} // namespace rgx #endif diff --git a/net/nginx-util/src/test-nginx-util-root.sh b/net/nginx-util/src/test-nginx-util-root.sh index 0ce8c9428..eb8ca56f1 100644 --- a/net/nginx-util/src/test-nginx-util-root.sh +++ b/net/nginx-util/src/test-nginx-util-root.sh @@ -4,6 +4,57 @@ PRINT_PASSED=2 NGINX_UTIL="/usr/bin/nginx-util" +ORIG=".original-test-nginx-util-root" + +mkdir -p /tmp/.uci/ + +uci commit nginx || { printf "Error invoking: uci commit\n Exit."; exit 2; } + + +pst_exit() { + printf "\nExit: Recovering original settings ... " + + uci revert nginx + + cd "/etc/config/" && rm "nginx" && mv "nginx.${ORIG}" "nginx" || + printf "\n%s: not moved %s to %s\n" "/etc/config/" "nginx${ORIG}" "nginx" + + cd "/etc/crontabs/" && rm "root" && mv "root${ORIG}" "root" || + printf "\n%s: not moved %s to %s\n" "/etc/crontabs/" "root${ORIG}" "root" + + cd "$(dirname "${CONF_DIR}")" && rm -r "${CONF_DIR}" && + mv "$(basename "${CONF_DIR}")${ORIG}" "$(basename "${CONF_DIR}")" || + printf "\n%s: not moved %s to %s\n" "$(dirname "${CONF_DIR}")" \ + "$(basename "${CONF_DIR}")${ORIG}" "$(basename "${CONF_DIR}")" + + printf "done.\n" + + exit "$1" +} + + +mkdir -p "/etc/config/" && touch "/etc/config/nginx" + +cd "/etc/config/" && [ ! -e "nginx${ORIG}" ] && cp "nginx" "nginx.${ORIG}" || { + printf "\n%s: not copied %s to %s\n" "/etc/config/" "nginx" "nginx${ORIG}" + pst_exit 3 +} + +uci set nginx.global.uci_enable=1 + + +mkdir -p "/etc/crontabs/" && touch "/etc/crontabs/root" + +cd "/etc/crontabs/" && [ ! -e "root${ORIG}" ] && mv "root" "root${ORIG}" || { + printf "\n%s: not moved %s to %s\n" "/etc/crontabs/" "root${ORIG}" "root" + pst_exit 4 +} + +touch "/etc/crontabs/root" + + +# ---------------------------------------------------------------------------- + __esc_newlines() { echo "${1}" | sed -E 's/$/\\n/' | tr -d '\n' | sed -E 's/\\n$/\n/' } @@ -33,6 +84,32 @@ _echo_sed() { echo "" | sed -E "c${1}" } + +fileauto="# This file is re-created when Nginx starts." + +setpoint_init_lan() { + echo "${fileauto}" + + sed -n -E '/^\s*#UCI_HTTP_CONFIG\s*$/q;p' "${UCI_CONF}.template" + + local rhs="\t}\n\n\tserver { #see uci show 'nginx.\1'" + uci -n export nginx \ + | sed -E -e "s/'//g" \ + -e '/^\s*package\s+nginx\s*$/d' \ + -e '/^\s*config\s+main\s/d' \ + -e "s/^\s*config\s+server\s+(.*)$/$rhs/g" \ + -e 's/^\s*list\s/\t\t/g' \ + -e 's/^\s*option\s/\t\t/g' \ + -e 's/^\s*uci_listen_locally\s+/\t\tlisten 127.0.0.1:/g' \ + -e '/^\s*uci_/d' \ + -e '/^$/d' -e "s/[^'\n]$/&;/g" \ + | sed "1,2d" + printf "\t}\n\n" + + sed -E '1,/^\s*#UCI_HTTP_CONFIG\s*$/ d' "${UCI_CONF}.template" +} + + setpoint_add_ssl() { local indent="\n$1" local name="$2" @@ -40,13 +117,13 @@ setpoint_add_ssl() { [ "${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}")" + local CONF + CONF="$(sed -E \ + -e "s/$(_regex "${NGX_INCLUDE}" "${LAN_LISTEN}${default}")/$1$(\ + _sed_rhs "${NGX_INCLUDE}" "${LAN_SSL_LISTEN}${default}")/g" \ + -e "s/^(\s*listen\s+)([^:]*:|\[[^]]*\]:)?80(\s|$|;)/\1\2443 ssl\3/g" \ + "${prefix}.sans" 2>/dev/null)" 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}")" \ @@ -82,6 +159,18 @@ test_setpoint() { } +test_existence() { + if [ "$2" -eq "0" ] + then + [ ! -f "$1" ] && echo "$1 missing!" && + [ "${PRINT_PASSED}" -gt 1 ] && pst_exit 1 + else + [ -f "$1" ] && echo "$1 existing!" && + [ "${PRINT_PASSED}" -gt 1 ] && pst_exit 1 + fi +} + + test() { eval "$1 2>/dev/null >/dev/null" if [ "$?" -eq "$2" ] @@ -90,14 +179,19 @@ test() { && 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 + [ "${PRINT_PASSED}" -gt 0 ] && printf "\n### Snip:\n" && eval "$1" + [ "${PRINT_PASSED}" -gt 0 ] && printf "### Snap.\n" + [ "${PRINT_PASSED}" -gt 1 ] && pst_exit 1 fi } + [ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s get_env ...\n" "${NGINX_UTIL}" + eval $("${NGINX_UTIL}" get_env) +test '[ -n "${UCI_CONF}" ]' 0 test '[ -n "${NGINX_CONF}" ]' 0 test '[ -n "${CONF_DIR}" ]' 0 test '[ -n "${LAN_NAME}" ]' 0 @@ -106,13 +200,26 @@ 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 +test '[ -n "${MANAGE_SSL}" ]' 0 + +mkdir -p "$(dirname "${LAN_LISTEN}")" + +mkdir -p "${CONF_DIR}" + +cd "$(dirname "${CONF_DIR}")" && [ ! -e "$(basename "${CONF_DIR}")${ORIG}" ] && +mv "$(basename "${CONF_DIR}")" "$(basename "${CONF_DIR}")${ORIG}" || +{ + printf "\n%s: not moved %s to %s\n" "$(dirname "${CONF_DIR}")" \ + "$(basename "${CONF_DIR}")" "$(basename "${CONF_DIR}")${ORIG}" + pst_exit 3 +} [ "$PRINT_PASSED" -gt 0 ] && printf "\nPrepare files in %s ...\n" "${CONF_DIR}" mkdir -p "${CONF_DIR}" -cd "${CONF_DIR}" || exit 2 +cd "${CONF_DIR}" || pst_exit 2 NGX_INCLUDE="include '\$';" NGX_SERVER_NAME="server_name * '\$' *;" @@ -140,6 +247,24 @@ server { EOF CONFS="${CONFS} minimal:0" +cat > listens.sans < normal.sans < acme.sans < more_server.sans < more_names.sans <"/etc/crontabs/root" +test '"${NGINX_UTIL}" add_ssl _lan' 0 +echo "postfix" >>"/etc/crontabs/root" +test_setpoint "/etc/crontabs/root" "prefix +3 3 12 12 * ${NGINX_UTIL} 'check_ssl' +postfix" + +test '"${NGINX_UTIL}" del_ssl _lan' 0 +test_setpoint "/etc/crontabs/root" "prefix +3 3 12 12 * ${NGINX_UTIL} 'check_ssl' +postfix" + +test '"${NGINX_UTIL}" check_ssl' 0 +test_setpoint "/etc/crontabs/root" "prefix +postfix" + +test '"${NGINX_UTIL}" add_ssl _lan' 0 +test_setpoint "/etc/crontabs/root" "prefix +postfix +3 3 12 12 * ${NGINX_UTIL} 'check_ssl'" + +rm -f "/etc/crontabs/root" + + +[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\t(legacy) ... \n' + +echo -n "prefix" >"/etc/crontabs/root" +cp "minimal.sans" "minimal.conf" + +test '"${NGINX_UTIL}" add_ssl minimal' 0 +echo "postfix" >>"/etc/crontabs/root" +test_setpoint "/etc/crontabs/root" "prefix +3 3 12 12 * ${NGINX_UTIL} 'add_ssl' 'minimal' +postfix" + +test '"${NGINX_UTIL}" del_ssl minimal' 0 +test_setpoint "/etc/crontabs/root" "prefix +postfix" + +rm -f "/etc/crontabs/root" + + + [ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s init_lan ...\n" "${NGINX_UTIL}" -mkdir -p "$(dirname "${LAN_LISTEN}")" -cp "${LAN_NAME}.sans" "${LAN_NAME}.conf" +rm -f "${LAN_NAME}.conf" "_redirect2ssl.conf" "${UCI_ADDED}.conf" +rm -f "$(readlink "${UCI_CONF}")" test '"${NGINX_UTIL}" init_lan' 0 +test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)" +test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'" -[ "$PRINT_PASSED" -gt 0 ] && printf "\nSetup files in %s ...\n" "${CONF_DIR}" +[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\twith temporary UCI config ... \n' -for conf in ${CONFS} -do test 'setpoint_add_ssl " " '"${conf%:*}" "${conf#*:}" -done +UCI_ADDED="$(uci add nginx server)" && +uci set nginx.@server[-1].server_name='temp' && +uci add_list nginx.@server[-1].listen='81 default_server' && +uci add_list nginx.@server[-1].listen='80' && +echo "UCI: nginx.${UCI_ADDED} added." + +rm -f "${LAN_NAME}.conf" "_redirect2ssl.conf" "${UCI_ADDED}.conf" +rm -f "$(readlink "${UCI_CONF}")" + +test '"${NGINX_UTIL}" init_lan' 0 +test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)" +test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'" + + +[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\t(legacy) ... \n' + +cp "${LAN_NAME}.sans" "${LAN_NAME}.conf" +touch "_redirect2ssl.conf" "${UCI_ADDED}.conf" +rm -f "$(readlink "${UCI_CONF}")" +test '"${NGINX_UTIL}" init_lan' 0 + +skipped() { + printf "\t# skipped UCI server 'nginx.%s'" "$1" + printf " as it could conflict with: %s%s.conf\n\n" "${CONF_DIR}" "$1" +} +rhs="$(skipped "$LAN_NAME" && skipped _redirect2ssl && skipped "${UCI_ADDED}")" +sed -E -e "s/^\t#UCI_HTTP_CONFIG$/$(__esc_sed_rhs "$rhs")\n/" \ + -e 's/\\n/\n/g' -e "1i${fileauto}" "${UCI_CONF}.template" >"uci.setpoint" + +test_setpoint "${UCI_CONF}" "$(cat "uci.setpoint")" +test_setpoint "/etc/crontabs/root" "" -test 'setpoint_add_ssl "\t" tab' 0 # fixes wrong indentation. [ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s add_ssl ...\n" "${NGINX_UTIL}" -cp different_name.sans different_name.with test '[ "${ADD_SSL_FCT}" = "add_ssl" ] ' 0 +rm -f "${LAN_NAME}.conf" "_redirect2ssl.conf" "${UCI_ADDED}.conf" +rm -f "$(readlink "${UCI_CONF}")" +test 'uci set nginx._lan.uci_manage_ssl="self-signed"' 0 +"${NGINX_UTIL}" del_ssl "${LAN_NAME}" 2>/dev/null +test_setpoint "/etc/crontabs/root" "" +test_existence "${LAN_NAME}.crt" 1 +test_existence "${LAN_NAME}.key" 1 +test '"${NGINX_UTIL}" add_ssl '"${UCI_ADDED}"' acme \ + '"${CONF_DIR}${UCI_ADDED}.crt"' '"${CONF_DIR}${UCI_ADDED}.key"' ' 0 +test_setpoint "/etc/crontabs/root" "" +test_existence "${UCI_ADDED}.crt" 1 +test_existence "${UCI_ADDED}.key" 1 +test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 0 +test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'" +test_existence "${LAN_NAME}.crt" 0 +test_existence "${LAN_NAME}.key" 0 +test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 0 +test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'" +test '"${NGINX_UTIL}" add_ssl inexistent' 1 +test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'" +test '"${NGINX_UTIL}" init_lan' 0 +test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)" +test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'" +test_existence "${UCI_ADDED}.crt" 1 +test_existence "${UCI_ADDED}.key" 1 +test_existence "${LAN_NAME}.crt" 0 +test_existence "${LAN_NAME}.key" 0 + + +[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\t(legacy) ... \n' + +cp different_name.sans different_name.with + +cp "/etc/crontabs/root" "cron.setpoint" for conf in ${CONFS}; do name="${conf%:*}" + [ "${name}" = "acme" ] && continue + [ "${name}" = "different_name" ] || + echo "3 3 12 12 * ${NGINX_UTIL} 'add_ssl' '${name}'" >>"cron.setpoint" cp "${name}.sans" "${name}.conf" test '"${NGINX_UTIL}" add_ssl '"${name}" "${conf#*:}" test_setpoint "${name}.conf" "$(cat "${name}.with")" + test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")" + [ "${name}" = "different_name" ] || test_existence "${name}.crt" 0 + [ "${name}" = "different_name" ] || test_existence "${name}.key" 0 done +cp acme.sans acme.conf +test '"${NGINX_UTIL}" add_ssl acme acme /path/to/crt /path/to/key' 0 +test_setpoint "acme.conf" "$(cat "acme.with")" +test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")" +test_existence "acme.crt" 1 +test_existence "acme.key" 1 + + + [ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s del_ssl ...\n" "${NGINX_UTIL}" -sed -i "/server {/a\\ include '${LAN_LISTEN}';" minimal.sans + +sed -E -e 's/443 ssl/80/' -e '/[^2]ssl/d' "/etc/config/nginx" >"config.setpoint" + +cp "/etc/crontabs/root" "cron.setpoint" +rm -f "${LAN_NAME}.conf" "_redirect2ssl.conf" "${UCI_ADDED}.conf" +test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 0 +test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")" +test_existence "${LAN_NAME}.crt" 1 +test_existence "${LAN_NAME}.key" 1 +test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 1 +test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")" + +rm -f "$(readlink "${UCI_CONF}")" +sed -E "/$(__esc_regex "'check_ssl'")/d" "/etc/crontabs/root" >"cron.setpoint" +test '"${NGINX_UTIL}" init_lan' 0 +test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)" +test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")" + +touch "${UCI_ADDED}.crt" "${UCI_ADDED}.key" +test '"${NGINX_UTIL}" del_ssl "'${UCI_ADDED}'" acme' 0 +test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")" +test_existence "${UCI_ADDED}.crt" 0 +test_existence "${UCI_ADDED}.key" 0 + +test '"${NGINX_UTIL}" del_ssl inexistent' 1 +test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")" + +test_setpoint "/etc/config/nginx" "$(cat "config.setpoint")" +test '"${NGINX_UTIL}" add_ssl "'${UCI_ADDED}'" acme \ + '"${CONF_DIR}${UCI_ADDED}.crt"' '"${CONF_DIR}${UCI_ADDED}.key"' ' 0 +test '"${NGINX_UTIL}" add_ssl "'$(uci get "nginx.${UCI_ADDED}.server_name")'"' 0 +test '"${NGINX_UTIL}" del_ssl "'$(uci get "nginx.${UCI_ADDED}.server_name")'"' 0 +rm -f "$(readlink "${UCI_CONF}")" +sed -E "/$(__esc_regex "'check_ssl'")/d" "/etc/crontabs/root" >"cron.setpoint" +test '"${NGINX_UTIL}" init_lan' 0 +test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)" +test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")" +test_existence "${UCI_ADDED}.crt" 1 +test_existence "${UCI_ADDED}.key" 1 + + +[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\t(legacy) ... \n' for conf in ${CONFS}; do name="${conf%:*}" + [ "${name}" = "acme" ] && continue + sed -E "/$(__esc_regex "'${name}'")/d" "/etc/crontabs/root" >"cron.setpoint" + touch "${name}.crt" "${name}.key" cp "${name}.with" "${name}.conf" test '"${NGINX_UTIL}" del_ssl '"${name}" "${conf#*:}" test_setpoint "${name}.conf" "$(cat "${name}.sans")" + test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")" + [ "${name}" = "different_name" ] && rm "${name}.crt" "${name}.key" + test_existence "${name}.crt" 1 + test_existence "${name}.key" 1 done +test_setpoint "/etc/crontabs/root" "" + +test '"${NGINX_UTIL}" del_ssl acme acme' 0 +test_existence "acme.crt" 1 +test_existence "acme.key" 1 + +cp acme.with acme.conf +touch acme.crt acme.key +echo "3 3 12 12 * ${NGINX_UTIL} 'add_ssl' 'acme'" >>"/etc/crontabs/root" +test '"${NGINX_UTIL}" del_ssl acme acme' 0 +test_setpoint "acme.conf" "$(cat "acme.sans")" +test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'add_ssl' 'acme'" +test_existence "acme.crt" 0 +test_existence "acme.key" 0 +"${NGINX_UTIL}" del_ssl acme 2>/dev/null +test_setpoint "/etc/crontabs/root" "" +test_existence "acme.crt" 1 +test_existence "acme.key" 1 + + +[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting without UCI ... \n" + +rm -f "$(readlink "${UCI_CONF}")" + +test 'uci set nginx.global.uci_enable=0' 0 + +test '"${NGINX_UTIL}" init_lan' 0 + +test '[ -e "$(readlink '"${UCI_CONF}"')" ]' 1 + +cp "${LAN_NAME}.sans" "${LAN_NAME}.conf" +test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 0 +test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 0 +test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 0 +test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 0 + +test 'rm "${LAN_NAME}.conf"' 0 +test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 1 +test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 1 + + + +pst_exit 0 diff --git a/net/nginx-util/src/test-nginx-util.sh b/net/nginx-util/src/test-nginx-util.sh index 787906350..6f8d07259 100755 --- a/net/nginx-util/src/test-nginx-util.sh +++ b/net/nginx-util/src/test-nginx-util.sh @@ -14,8 +14,17 @@ TMPROOT="$(mktemp -d "/tmp/test-nginx-util-XXXXXX")" ln -s /bin "${TMPROOT}/bin" -mkdir -p "${TMPROOT}/usr/bin/" +mkdir -p "${TMPROOT}/etc/crontabs/" + +mkdir -p "${TMPROOT}/etc/config/" +cp "./config-nginx-ssl" "${TMPROOT}/etc/config/nginx" +mkdir -p "${TMPROOT}/etc/nginx/" +cp "./uci.conf.template" "${TMPROOT}/etc/nginx/uci.conf.template" +ln -s "${TMPROOT}/var/lib/nginx/uci.conf" "${TMPROOT}/etc/nginx/uci.conf" + +mkdir -p "${TMPROOT}/usr/bin/" +cp "/usr/local/bin/uci" "${TMPROOT}/usr/bin/" cp "./test-nginx-util-root.sh" "${TMPROOT}/usr/bin/" diff --git a/net/nginx-util/src/ubus-cxx.cpp b/net/nginx-util/src/ubus-cxx.cpp index 7f65813ca..5fa29c7d1 100644 --- a/net/nginx-util/src/ubus-cxx.cpp +++ b/net/nginx-util/src/ubus-cxx.cpp @@ -2,130 +2,118 @@ #include "ubus-cxx.hpp" - inline void example_for_checking_if_there_is_a_key() { if (ubus::call("service", "list").filter("cron")) { - std::cout<<"Cron is active (with or without instances) "<(t); - std::cout<<"["<(t); + std::cout << "[" << blobmsg_get_string(x) << "] "; } - for (auto t : lan_status.filter("ipv4-address", "").filter("address")) { - //NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) - auto x = const_cast(t); - std::cout<(t); + std::cout << blobmsg_get_string(x) << " "; } - std::cout< int - { return blobmsg_add_string(buf, "config", "nginx"); }; - for (auto t : ubus::call("uci", "get", set_arg).filter("values")) { - //NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) - auto x = const_cast(t); - std::cout< int { return blobmsg_add_string(buf, "config", "nginx"); }; + for (const auto* t : ubus::call("uci", "get", set_arg).filter("values")) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + auto* x = const_cast(t); + std::cout << blobmsg_get_string(x) << "\n"; } } - inline void example_for_exploring() { ubus::strings keys{"ipv4-address", "", ""}; - for (auto t : ubus::call("network.interface.lan", "status").filter(keys)) { - //NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) - auto x = const_cast(t); - std::cout<(t); + std::cout << blobmsg_name(x) << ": "; switch (blob_id(x)) { - case BLOBMSG_TYPE_UNSPEC: std::cout<<"[unspecified]"; break; - case BLOBMSG_TYPE_ARRAY: std::cout<<"[array]"; break; - case BLOBMSG_TYPE_TABLE: std::cout<<"[table]"; break; - case BLOBMSG_TYPE_STRING: std::cout< void - { +{ // output like from the original ubus call: + const auto explore = [](auto message) -> void { auto end = message.end(); - auto explore_internal = - [&end](auto & explore_ref, auto it, size_t depth=1) -> void - { - std::cout< void { + std::cout << std::endl; bool first = true; - for (; it!=end; ++it) { - //NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) - auto attr = const_cast(*it); - if (first) { first = false; } - else { std::cout<<",\n"; } - std::cout<(*it); + if (first) { + first = false; + } + else { + std::cout << ",\n"; + } + std::cout << std::string(depth, '\t'); std::string name = blobmsg_name(attr); - if (!name.empty()) { std::cout<<"\""< int { - +auto main() -> int +{ try { example_for_checking_if_there_is_a_key(); @@ -140,9 +128,13 @@ auto main() -> int { return 0; } - catch (const std::exception & e) { std::cerr< #include +#include #include #include #include @@ -13,7 +13,6 @@ #include #endif - namespace ubus { static constexpr int call_timeout = 500; @@ -22,12 +21,13 @@ using msg_ptr = std::shared_ptr; using strings = std::vector; +inline auto concat(strings dest) +{ + return dest; +} -inline auto concat(strings dest) { return dest; } - - -template -inline auto concat(strings dest, strings src, Strings ...more) +template +inline auto concat(strings dest, strings src, Strings... more) { dest.reserve(dest.size() + src.size()); dest.insert(std::end(dest), std::make_move_iterator(std::begin(src)), @@ -35,182 +35,162 @@ inline auto concat(strings dest, strings src, Strings ...more) return concat(std::move(dest), std::move(more)...); } - -template -inline auto concat(strings dest, S src, Strings ...more) +template +inline auto concat(strings dest, S src, Strings... more) { dest.emplace_back(std::move(src)); return concat(std::move(dest), std::move(more)...); } - - class iterator { - -private: - - const strings & keys; + private: + const strings& keys; const size_t n = 0; size_t i = 0; - const blob_attr * pos = nullptr; + const blob_attr* pos = nullptr; std::unique_ptr cur{}; - iterator * parent = nullptr; + iterator* parent = nullptr; size_t rem = 0; - [[nodiscard]] inline auto matches() const -> bool { - return (keys[i].empty() || blobmsg_name(cur->pos)==keys[i]); + return (keys[i].empty() || blobmsg_name(cur->pos) == keys[i]); } - - explicit iterator(iterator * par) - : keys{par->keys}, n{par->n}, pos{par->pos}, cur{this}, parent{par} + explicit iterator(iterator* par) + : keys{par->keys}, n{par->n}, pos{par->pos}, cur{this}, parent{par} { - if (pos!=nullptr) { + if (pos != nullptr) { rem = blobmsg_data_len(pos); - pos = static_cast(blobmsg_data(pos)); + pos = static_cast(blobmsg_data(pos)); } } - -public: - - explicit iterator(const blob_attr * msg, const strings & filter={""}) - : keys{filter}, n{keys.size()-1}, pos{msg}, cur{this} + public: + explicit iterator(const blob_attr* msg, const strings& key_filter = {""}) + : keys{key_filter}, n{keys.size() - 1}, pos{msg}, cur{this} { - if (pos!=nullptr) { + if (pos != nullptr) { rem = blobmsg_data_len(pos); - pos = static_cast(blobmsg_data(pos)); + pos = static_cast(blobmsg_data(pos)); - if (rem==0) { pos = nullptr; } - else if (i!=n || !matches()) { ++*this; } + if (rem == 0) { + pos = nullptr; + } + else if (i != n || !matches()) { + ++*this; + } } } + inline iterator(iterator&&) noexcept = default; - inline iterator(iterator &&) noexcept = default; - - - inline iterator(const iterator &) = delete; - + inline iterator(const iterator&) = delete; - inline auto operator=(const iterator &) -> iterator & = delete; + inline auto operator=(const iterator&) -> iterator& = delete; + inline auto operator=(iterator &&) -> iterator& = delete; - inline auto operator=(iterator &&) -> iterator & = delete; - - - inline auto operator*() { return cur->pos; } - - - inline auto operator!=(const iterator & rhs) - { return (cur->rem!=rhs.cur->rem || cur->pos!=rhs.cur->pos); } - + inline auto operator*() + { + return cur->pos; + } - auto operator++() -> iterator &; + inline auto operator!=(const iterator& rhs) + { + return (cur->rem != rhs.cur->rem || cur->pos != rhs.cur->pos); + } + auto operator++() -> iterator&; inline ~iterator() - { if (cur.get()==this) { static_cast(cur.release()); } } - + { + if (cur.get() == this) { + static_cast(cur.release()); + } + } }; - - class message { - -private: - - const msg_ptr msg{}; // initialized by callback. + private: + const msg_ptr msg{}; // initialized by callback. const strings keys{}; + public: + inline explicit message(msg_ptr message_ptr, strings key_filter = {""}) + : msg{std::move(message_ptr)}, keys{std::move(key_filter)} + {} -public: - - inline explicit message(msg_ptr message_ptr, strings filter={""}) - : msg{std::move(message_ptr)}, keys{std::move(filter)} {} - + inline message(message&&) = default; - inline message(message &&) = default; + inline message(const message&) = delete; + inline auto operator=(message &&) -> message& = delete; - inline message(const message &) = delete; - - - inline auto operator=(message &&) -> message & = delete; - - - inline auto operator=(const message &) -> message & = delete; - + inline auto operator=(const message&) -> message& = delete; [[nodiscard]] inline auto begin() const -> iterator - { return iterator{msg.get(), keys}; } - + { + return iterator{msg.get(), keys}; + } [[nodiscard]] inline auto end() const -> iterator - { return iterator{nullptr, keys}; } - - - inline explicit operator bool() const { return begin()!=end(); } + { + return iterator{nullptr, keys}; + } + inline explicit operator bool() const + { + return begin() != end(); + } - template - auto filter(Strings ...filter) + template + auto filter(Strings... key_filter) { strings both{}; - if (keys.size()!=1 || !keys[0].empty()) { both = keys; } - both = concat(std::move(both), std::move(filter)...); + if (keys.size() != 1 || !keys[0].empty()) { + both = keys; + } + both = concat(std::move(both), std::move(key_filter)...); return std::move(message{msg, std::move(both)}); } - inline ~message() = default; - }; - - class lock_shared_resources { - -private: - + private: static std::mutex inuse; + public: + inline lock_shared_resources() + { + inuse.lock(); + } -public: - - - inline lock_shared_resources() { inuse.lock(); } - - - inline lock_shared_resources(lock_shared_resources &&) noexcept = default; - - - inline lock_shared_resources(const lock_shared_resources &) = delete; - - - inline auto operator=(const lock_shared_resources &) -> auto & = delete; + inline lock_shared_resources(lock_shared_resources&&) noexcept = default; + inline lock_shared_resources(const lock_shared_resources&) = delete; - inline auto operator=(lock_shared_resources &&) -> auto && = delete; + inline auto operator=(const lock_shared_resources&) -> auto& = delete; + inline auto operator=(lock_shared_resources &&) -> auto&& = delete; - //NOLINTNEXTLINE(readability-convert-member-functions-to-static) - inline auto get_context() -> ubus_context * // is member to enforce inuse. + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + inline auto get_context() -> ubus_context* // is member to enforce inuse. { - static auto ubus_freeing = [] (ubus_context * ctx) { ubus_free(ctx); }; - static std::unique_ptr - lazy_ctx{ubus_connect(nullptr), ubus_freeing}; + static auto ubus_freeing = [](ubus_context* ctx) { ubus_free(ctx); }; + static std::unique_ptr lazy_ctx{ubus_connect(nullptr), + ubus_freeing}; - if (!lazy_ctx) { // it could be available on a later call: + if (!lazy_ctx) { // it could be available on a later call: lazy_ctx.reset(ubus_connect(nullptr)); @@ -222,82 +202,79 @@ public: return lazy_ctx.get(); } - - //NOLINTNEXTLINE(readability-convert-member-functions-to-static) - inline auto get_blob_buf() -> blob_buf * // is member to enforce inuse. + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + inline auto get_blob_buf() -> blob_buf* // is member to enforce inuse. { static blob_buf buf; - static auto blob_buf_freeing = [] (blob_buf * b) { blob_buf_free(b); }; + static auto blob_buf_freeing = [](blob_buf* b) { blob_buf_free(b); }; static std::unique_ptr - created_to_free_on_the_end_of_life{&buf, blob_buf_freeing}; + created_to_free_on_the_end_of_life{&buf, blob_buf_freeing}; blob_buf_init(&buf, 0); return &buf; } - - inline ~lock_shared_resources() { inuse.unlock(); } - + inline ~lock_shared_resources() + { + inuse.unlock(); + } }; +template +auto call(const char* path, const char* method, F set_arguments, int timeout = call_timeout) + -> message; -template -auto call(const char * path, const char * method, F set_arguments, - int timeout=call_timeout) -> message; - - -inline auto call(const char * path, const char * method, - int timeout=call_timeout) -> message -{ return call(path, method, [](blob_buf * /*buf*/) { return 0; }, timeout); } - - -inline auto call(const char * path, int timeout=call_timeout) -> message -{ return call(path, "", timeout); } - - +inline auto call(const char* path, const char* method, int timeout = call_timeout) -> message +{ + return call( + path, method, [](blob_buf* /*buf*/) { return 0; }, timeout); +} +inline auto call(const char* path, int timeout = call_timeout) -> message +{ + return call(path, "", timeout); +} // ------------------------- implementation: ---------------------------------- - std::mutex lock_shared_resources::inuse; - -inline auto iterator::operator++() -> iterator & +inline auto iterator::operator++() -> iterator& { - for(;;) { - #ifndef NDEBUG - std::cout<')<<" look for "<pos)<') << " look for " << keys[i] << " at "; + std::cout << blobmsg_name(cur->pos) << std::endl; +#endif auto id = blob_id(cur->pos); - if ( (id==BLOBMSG_TYPE_TABLE || id==BLOBMSG_TYPE_ARRAY) - && ipos)>0 ) - { //immmerge: + if ((id == BLOBMSG_TYPE_TABLE || id == BLOBMSG_TYPE_ARRAY) && i < n && matches() && + blobmsg_data_len(cur->pos) > 0) + { // immmerge: ++i; - auto tmp = cur.release(); + auto* tmp = cur.release(); - struct new_iterator : public iterator // use private constructor: - { explicit new_iterator(iterator * par) : iterator{par} {} }; + struct new_iterator : public iterator // use private constructor: + { + explicit new_iterator(iterator* par) : iterator{par} {} + }; cur = std::make_unique(tmp); - - } else { + } + else { while (true) { cur->rem -= blob_pad_len(cur->pos); cur->pos = blob_next(cur->pos); auto len = blob_pad_len(cur->pos); - if (cur->rem>0 && len<=cur->rem && len>=sizeof(blob_attr)) - { break; } + if (cur->rem > 0 && len <= cur->rem && len >= sizeof(blob_attr)) { + break; + } - //emerge: - auto tmp = cur->parent; + // emerge: + auto* tmp = cur->parent; if (tmp == nullptr) { cur->pos = nullptr; @@ -309,67 +286,69 @@ inline auto iterator::operator++() -> iterator & --i; } } - if (i==n && matches()) { return *cur; } + if (i == n && matches()) { + return *cur; + } } } - -template -inline auto call(const char * path, const char * method, F set_arguments, - int timeout) -> message +template +inline auto call(const char* path, const char* method, F set_arguments, int timeout) -> message { - auto shared = lock_shared_resources{}; - auto ctx = shared.get_context(); + auto* ctx = shared.get_context(); - uint32_t id; + uint32_t id = 0; int err = ubus_lookup_id(ctx, path, &id); - if (err==0) { // call + if (err == 0) { // call ubus_request request{}; - auto buf = shared.get_blob_buf(); + auto* buf = shared.get_blob_buf(); err = set_arguments(buf); - if (err==0) { + if (err == 0) { err = ubus_invoke_async(ctx, id, method, buf->head, &request); } - if (err==0) { - + if (err == 0) { msg_ptr message_ptr; /* Cannot capture message_ptr, the lambda would be another type. - * Pass a location where to save the message as priv pointer when - * invoking and get it back here: - */ + * Pass a location where to save the message as priv pointer when + * invoking and get it back here: + */ request.priv = &message_ptr; - request.data_cb = - [](ubus_request * req, int /*type*/, blob_attr * msg) - { - if (req==nullptr || msg==nullptr) { return; } + request.data_cb = [](ubus_request* req, int /*type*/, blob_attr* msg) { + if (req == nullptr || msg == nullptr) { + return; + } - auto saved = static_cast(req->priv); - if (saved==nullptr || *saved) { return; } + auto* saved = static_cast(req->priv); + if (saved == nullptr || *saved) { + return; + } saved->reset(blob_memdup(msg), free); - if (!*saved) { throw std::bad_alloc(); } + if (!*saved) { + throw std::bad_alloc(); + } }; err = ubus_complete_request(ctx, &request, timeout); - if (err==0) { return message{message_ptr}; } + if (err == 0) { + return message{message_ptr}; + } } } std::string errmsg = "ubus::call error: cannot invoke"; - errmsg += " (" + std::to_string(err) + ") " + path + " " + method; + errmsg += " (" + std::to_string(err) + ") " + path + " " + method; throw std::runtime_error(errmsg); } - -} // namespace ubus - +} // namespace ubus #endif diff --git a/net/nginx-util/src/uci-cxx.cpp b/net/nginx-util/src/uci-cxx.cpp new file mode 100644 index 000000000..c8805482b --- /dev/null +++ b/net/nginx-util/src/uci-cxx.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include + +#include "uci-cxx.hpp" + +auto main() -> int +{ + uci::element p = uci::package{"nginx"}; + std::cout << "package " << p.name() << "\n\n"; + for (auto s : p) { + std::cout << "config " << s.type() << " '" << s.name() << "'\n"; + for (auto o : s) { + for (auto i : o) { + std::cout << "\t" << o.type() << " " << o.name() << " '" << i.name() << "'\n"; + } + } + std::cout << "\n"; + } +} diff --git a/net/nginx-util/src/uci-cxx.hpp b/net/nginx-util/src/uci-cxx.hpp new file mode 100644 index 000000000..4598890d9 --- /dev/null +++ b/net/nginx-util/src/uci-cxx.hpp @@ -0,0 +1,474 @@ +#ifndef _UCI_CXX_HPP +#define _UCI_CXX_HPP + +#include +#include +#include +#include +#include +#include + +namespace uci { + +template +class iterator { // like uci_foreach_element_safe. + + private: + const uci_ptr& _ptr; + + uci_element* _it = nullptr; + + uci_element* _next = nullptr; + + // wrapper for clang-tidy + inline auto _list_to_element(const uci_list* cur) -> uci_element* + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast) + return list_to_element(cur); // macro casting container=pointer-offset. + } + + public: + inline explicit iterator(const uci_ptr& ptr, const uci_list* cur) + : _ptr{ptr}, _it{_list_to_element(cur)} + { + _next = _list_to_element(_it->list.next); + } + + inline iterator(iterator&&) noexcept = default; + + inline iterator(const iterator&) = delete; + + inline auto operator=(const iterator&) -> iterator& = delete; + + inline auto operator=(iterator &&) -> iterator& = delete; + + auto operator*() -> T + { + return T{_ptr, _it}; + } + + inline auto operator!=(const iterator& rhs) -> bool + { + return (&_it->list != &rhs._it->list); + } + + inline auto operator++() -> iterator& + { + _it = _next; + _next = _list_to_element(_next->list.next); + return *this; + } + + inline ~iterator() = default; +}; + +class locked_context { + private: + static std::mutex inuse; + + public: + inline locked_context() + { + inuse.lock(); + } + + inline locked_context(locked_context&&) noexcept = default; + + inline locked_context(const locked_context&) = delete; + + inline auto operator=(const locked_context&) -> locked_context& = delete; + + inline auto operator=(locked_context &&) -> locked_context& = delete; + + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + inline auto get() -> uci_context* // is member to enforce inuse + { + static auto free_ctx = [](uci_context* ctx) { uci_free_context(ctx); }; + static std::unique_ptr lazy_ctx{uci_alloc_context(), + free_ctx}; + + if (!lazy_ctx) { // it could be available on a later call: + lazy_ctx.reset(uci_alloc_context()); + if (!lazy_ctx) { + throw std::runtime_error("uci error: cannot allocate context"); + } + } + + return lazy_ctx.get(); + } + + inline ~locked_context() + { + inuse.unlock(); + } +}; + +template +class element { + private: + uci_list* _begin = nullptr; + + uci_list* _end = nullptr; + + uci_ptr _ptr{}; + + protected: + [[nodiscard]] inline auto ptr() -> uci_ptr& + { + return _ptr; + } + + [[nodiscard]] inline auto ptr() const -> const uci_ptr& + { + return _ptr; + } + + void init_begin_end(uci_list* begin, uci_list* end) + { + _begin = begin; + _end = end; + } + + inline explicit element(const uci_ptr& pre, uci_element* last) : _ptr{pre} + { + _ptr.last = last; + } + + inline explicit element() = default; + + public: + inline element(element&&) noexcept = default; + + inline element(const element&) = delete; + + inline auto operator=(const element&) -> element& = delete; + + inline auto operator=(element &&) -> element& = delete; + + auto operator[](std::string_view key) const -> T; + + [[nodiscard]] inline auto name() const -> std::string + { + return _ptr.last->name; + } + + void rename(const char* value) const; + + void commit() const; + + [[nodiscard]] inline auto begin() const -> iterator + { + return iterator{_ptr, _begin}; + } + + [[nodiscard]] inline auto end() const -> iterator + { + return iterator{_ptr, _end}; + } + + inline ~element() = default; +}; + +class section; + +class option; + +class item; + +class package : public element
{ + public: + inline package(const uci_ptr& pre, uci_element* last) : element{pre, last} + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast) + ptr().p = uci_to_package(ptr().last); // macro casting pointer-offset. + ptr().package = ptr().last->name; + + auto* end = &ptr().p->sections; + auto* begin = end->next; + init_begin_end(begin, end); + } + + explicit package(const char* name); + + auto set(const char* key, const char* type) const -> section; +}; + +class section : public element