Browse Source

nginx-util: use UCI for server configuration

**tl;dr:** The functions `{add,del}_ssl` modify a server
section of the UCI config if there is no `.conf` file with
the same name in `/etc/nginx/conf.d/`.

Then `init_lan` creates `/var/lib/nginx/uci.conf` files by
copying the `/etc/nginx/uci.conf.template` and standard
options from the UCI config; additionally the special path
`logd` can be used in `{access,error}_log`.

The init does not change the configuration beside
re-creating self-signed certificates when needed. This is
also the only purpose of the new `check_ssl`, which is
installed as yearly cron job.

**Initialization:**

Invoking `nginx-util init_lan` parses the UCI configuration
for package `nginx`. It creates a server part in
`/var/lib/nginx/uci.conf` for each `section server '$name'`
by copying all UCI options but the following:

* `option uci_manage_ssl` is skipped. It is set to
'self-signed' by `nginx-util add_ssl $name`, removed by
`nginx-util del_ssl $name` and used by
`nginx-util check_ssl` (see below).

* `logd` as path in `error_log` or `access_log` writes them
to STDERR respective STDOUT, which are fowarded by Nginx's
init to the log daemon. Specifically:
`option error_log 'logd'` becomes `error_log stderr;` and
`option access_log 'logd openwrt'` becomes
`access_log /proc/self/fd/1 openwrt;`

Other `[option|list] key 'value'` entries just become
`key value;` directives.

The init.d calls internally also `check_ssl` for rebuilding
self-signed SSL certificates if needed (see below). And it
still sets up `/var/lib/nginx/lan{,_ssl}.listen` files as
it is doing in the current version (so they stay available).

**Defaults:**

The package installs the file `/etc/nginx/restrict_locally`
containing allow/deny directives for restricting the access
to LAN addresses by including it into a server part. The
default server '_lan' includes this file and listens on all
IPs (instead of only the local IPs as it did before; other
servers do not need to listen explicitly on the local IPs
anymore). The default server is contained together with a
server that redirects HTTP requests for inexistent URLs to
HTTPS in the UCI configuration file `/etc/config/nginx`.
Furthermore, the packages installs a
`/etc/nginx/uci.conf.template` containing the current setup
and a marker, which will be replaced by the created UCI
servers when calling `init_lan`.

**Other:**

If there is a file named `/etc/nginx/conf.d/$name.conf` the
functions `init_lan`, `add_ssl $name` and `del_ssl $name`
will use that file instead of a UCI server section (this is
similar to the current version).

Else it selects the UCI `section server $name`, or, when
there is no such section, it searches for the first one
having `option server_name '… $name …'`. For this section:

* `nginx-util add_ssl $name` will add to it:
`option uci_manage_ssl 'self-signed'`
`option ssl_certificate '/etc/nginx/conf.d/$name.crt'`
`option ssl_certificate_key '/etc/nginx/conf.d/$name.key'`
`option ssl_session_cache 'shared:SSL:32k'`
`option ssl_session_timeout '64m'`
If these options are already present, they will stay the
same; just the first option `uci_manage_ssl` will always be
changed to 'self-signed'. The command also changes all
`listen` list items to use port 443 and ssl instead of port
80 (without ssl). If they stated another port than 80
before, they are kept the same. Furthermore, it creates a
self-signed SSL certificate if necessary, i.e., if there is
no *valid* certificate and key at the locations given by
the options `ssl_certificate` and `ssl_certificate_key`.

* `nginx-util del_ssl $name` checks if `uci_manage_ssl` is
set 'self-signed' in the corresponding UCI section. Only
then it removes all of the above options regardless of the
value looking just at the key name. Then, it also changes
all `listen` list items to use port 80 (without ssl)
instead of port 443 with ssl. If stating another port than
443, they are kept the same. Furthermore, it removes the
SSL certificate and key that were indicated by
`ssl_certificate{,_key}`.

* `nginx-util check_ssl` looks through all server sections
of the UCI config for `uci_manage_ssl 'self-signed'`. On
every hit it checks if the SSL certificate-key-pair
indicated by the options `ssl_certificate{,_key}` is
expired. Then it re-creates a self-signed certificate.
If there exists at least one `section server` with
`uci_manage_ssl 'self-signed'`, it will try to install
itself as cron job. If there are no such sections, it
removes that cron job if possible.

For installing a ssl certificate and key managed by
another app, you can call:
`nginx-util add_ssl $name $manager $crtpath $keypath`
Hereby `$name` is as above, `$manager` is an arbitrary
string, and the the ssl certificate and its key are
indicated by their absolute path. If you want to remove
the directives again, then you can use:
`nginx-util del_ssl $name $manager`

Signed-off-by: Peter Stadler <peter.stadler@student.uibk.ac.at>
lilik-openwrt-22.03
Peter Stadler 4 years ago
parent
commit
f62599d27e
21 changed files with 3882 additions and 1235 deletions
  1. +47
    -3
      net/nginx-util/Makefile
  2. +475
    -0
      net/nginx-util/files/README.sh
  3. +22
    -0
      net/nginx-util/files/nginx.config
  4. +10
    -0
      net/nginx-util/files/restrict_locally
  5. +32
    -0
      net/nginx-util/files/uci.conf.template
  6. +171
    -0
      net/nginx-util/src/.clang-format
  7. +407
    -0
      net/nginx-util/src/.clang-tidy
  8. +13
    -8
      net/nginx-util/src/CMakeLists.txt
  9. +23
    -0
      net/nginx-util/src/LICENSE
  10. +793
    -328
      net/nginx-util/src/nginx-ssl-util.hpp
  11. +294
    -110
      net/nginx-util/src/nginx-util.cpp
  12. +47
    -45
      net/nginx-util/src/nginx-util.hpp
  13. +109
    -120
      net/nginx-util/src/px5g-openssl.hpp
  14. +175
    -168
      net/nginx-util/src/px5g.cpp
  15. +147
    -175
      net/nginx-util/src/regex-pcre.hpp
  16. +380
    -17
      net/nginx-util/src/test-nginx-util-root.sh
  17. +10
    -1
      net/nginx-util/src/test-nginx-util.sh
  18. +71
    -79
      net/nginx-util/src/ubus-cxx.cpp
  19. +160
    -181
      net/nginx-util/src/ubus-cxx.hpp
  20. +22
    -0
      net/nginx-util/src/uci-cxx.cpp
  21. +474
    -0
      net/nginx-util/src/uci-cxx.hpp

+ 47
- 3
net/nginx-util/Makefile View File

@ -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 <peter.stadler@student.uibk.ac.at>
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))


+ 475
- 0
net/nginx-util/files/README.sh View File

@ -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 "<file nginx %s>\n%s</file>" "$1" "$(cat "${file}")";
}
ifConfEcho() {
sed -nE "s/^\s*$1=\s*(\S*)\s*\\\\$/\n$2 \"\1\";/p" ../../nginx/Makefile;
}
cat <<EOF
===== Configuration =====${MSG}
The official Documentation contains a
[[https://docs.nginx.com/nginx/admin-guide/|Admin Guide]].
Here we will look at some often used configuration parts and how we handle them
at OpenWrt.
At different places there are references to the official
[[https://docs.nginx.com/nginx/technical-specs/|Technical Specs]]
for further reading.
**tl;dr:** When starting Nginx by ''/etc/init.d/nginx'', it creates its main
configuration dynamically based on a minimal template and the
[[docs:guide-user:base-system:uci|🡒UCI]] configuration.
The UCI ''/etc/config/nginx'' contains initially:
| ''config server '${LAN_NAME}''' | \
Default server for the LAN, which includes all ''${CONF_DIR}*.locations''. |
| ''config server '_redirect2ssl''' | \
Redirects inexistent URLs to HTTPS. |
It enables also the ''${CONF_DIR}'' directory for further configuration:
| ''${CONF_DIR}\$NAME.conf'' | \
Is included in the main configuration. \
It is prioritized over a UCI ''config server '\$NAME' ''. |
| ''${CONF_DIR}\$NAME.locations'' | \
Is include in the ''${LAN_NAME}'' server and can be re-used for others, too. |
| ''$(dirname "${CONF_DIR}")/restrict_locally'' | \
Is include in the ''${LAN_NAME}'' server and allows only accesses from LAN. |
Setup configuration (for a server ''\$NAME''):
| ''$(basename ${NGINX_UTIL}) [${ADD_SSL_FCT}|del_ssl] \$NAME'' | \
Add/remove a self-signed certificate and corresponding directives. |
| ''uci set nginx.\$NAME.access_log='logd openwrt''' | \
Writes accesses to Openwrt’s \
[[docs:guide-user:base-system:log.essentials|🡒logd]]. |
| ''uci set nginx.\$NAME.error_log='logd' '' | \
Writes errors to Openwrt’s \
[[docs:guide-user:base-system:log.essentials|🡒logd]]. |
| ''uci [set|add_list] nginx.\$NAME.key='value' '' | \
Becomes a ''key value;'' directive if the //key// does not start with //uci_//. |
| ''uci set nginx.\$NAME=[disable|server]'' |\
Disable/enable inclusion in the dynamic conf.|
| ''uci set nginx.global.uci_enable=false'' | \
Use a custom ''${NGINX_CONF}'' rather than a dynamic conf. |
==== Basic ====${MSG}
We modify the configuration by changing servers saved in the UCI configuration
at ''/etc/config/nginx'' and/or by creating different configuration files in the
''${CONF_DIR}'' directory.
These files use the file extensions ''.locations'' and ''.conf'' plus ''.crt''
and ''.key'' for SSL certificates and keys.((
We can disable a single configuration file in ''${CONF_DIR}'' by giving it
another extension, e.g., by adding ''.disabled''.))
For the new configuration to take effect, we must reload it by:
<code bash>service nginx reload</code>
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.)):
<code nginx ${CONF_DIR}example.locations>
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 { … }
</code>
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:
<code bash>uci set nginx.global.uci_enable=false</code>
==== 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//:
<code bash>
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.
</code>
We can disable respective re-enable this server again by:
<code bash>
uci set nginx.${EXAMPLE_COM//./_}=disable # respective: \
uci set nginx.${EXAMPLE_COM//./_}=server
</code>
These changes are made in the RAM (and can be used until a reboot), we can save
them permanently by:
<code bash>uci commit nginx</code>
For creating a similar ''${CONF_DIR}${EXAMPLE_COM}.conf'', we can adopt the
following:
<code nginx ${CONF_DIR}${EXAMPLE_COM}.conf>
server {
listen 80;
listen [::]:80;
server_name ${EXAMPLE_COM};
include '$(basename ${CONF_DIR})/${EXAMPLE_COM}.locations';
# location / { … } # root location for this server.
}
</code>
[[#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:
<code bash>
# 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
</code>
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:
<code bash>$(basename "${NGINX_UTIL}") ${ADD_SSL_FCT} ${EXAMPLE_COM}</code>
- 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: \
<code bash>service cron enable && service cron start</code>
This can be undone by invoking:
<code bash>$(basename "${NGINX_UTIL}") del_ssl ${EXAMPLE_COM}</code>
For using an SSL certificate and key that are managed otherwise, there is:
<code bash>$(basename "${NGINX_UTIL}") add_ssl ${EXAMPLE_COM} "\$MANAGER" \
"/absolute/path/to/crt" "/absolute/path/to/key"</code>
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:
<code bash>$(basename "${NGINX_UTIL}") del_ssl ${EXAMPLE_COM} "\$MANAGER"</code>
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:
<code bash>
opkg update && opkg install uacme #or: acme #and for LuCI: luci-app-acme
</code>
[[#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):
<code nginx>$(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)\
</code>
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):
<code nginx>
daemon off; # procd expects services to run in the foreground
</code>
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:
<code nginx> access_log /proc/self/fd/1 openwrt;</code>
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
<code bash>uci set nginx.global.uci_enable='false' </code>
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):
<code bash>
$(basename ${NGINX_UTIL}) ${ADD_SSL_FCT} ${EXAMPLE_COM//./_} \
# respectively: \
$(basename ${NGINX_UTIL}) del_ssl ${EXAMPLE_COM//./_}
</code>
EOF

+ 22
- 0
net/nginx-util/files/nginx.config View File

@ -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'

+ 10
- 0
net/nginx-util/files/restrict_locally View File

@ -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;

+ 32
- 0
net/nginx-util/files/uci.conf.template View File

@ -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;
}

+ 171
- 0
net/nginx-util/src/.clang-format View File

@ -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: '^<ext/.*\.h>'
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
...

+ 407
- 0
net/nginx-util/src/.clang-tidy View File

@ -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: ''
...

+ 13
- 8
net/nginx-util/src/CMakeLists.txt View File

@ -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()

+ 23
- 0
net/nginx-util/src/LICENSE View File

@ -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.
*/

+ 793
- 328
net/nginx-util/src/nginx-ssl-util.hpp
File diff suppressed because it is too large
View File


+ 294
- 110
net/nginx-util/src/nginx-util.cpp View File

@ -1,194 +1,378 @@
#include <iostream>
#include <numeric>
#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<char*>(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<std::string> 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<const char *>(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<const char *>(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<const char *>(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<const char *>(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<const char *>(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: "<<message;
return indent + "# " + message;
} // else:
auto conf = indent + "server { #see uci show 'nginx." + secname + "'\n";
for (auto opt : sec) {
for (auto itm : opt) {
if (opt.name().rfind("uci_", 0) == 0) {
continue;
}
// else: standard opt.name()
auto val = itm.name();
if (opt.name() == "error_log") {
val = change_if_starts_with(val, "logd", "/proc/self/fd/1");
}
else if (opt.name() == "access_log") {
val = change_if_starts_with(val, "logd", "stderr");
}
conf += indent + "\t" + opt.name() + " " + itm.name() + ";\n";
}
}
conf += indent + "}\n";
return conf;
}
void init_uci(const uci::package& pkg)
{
auto conf = std::string{file_comment_auto_created};
static const auto uci_http_config = std::string_view{"#UCI_HTTP_CONFIG\n"};
const auto tmpl = read_file(std::string{UCI_CONF} + ".template");
auto pos = tmpl.find(uci_http_config);
if (pos == std::string::npos) {
conf += tmpl;
}
else {
const auto index = tmpl.find_last_not_of(" \t", pos - 1);
const auto before = tmpl.begin() + index + 1;
const auto middle = tmpl.begin() + pos;
const auto after = middle + uci_http_config.length();
conf.append(tmpl.begin(), before);
const auto indent = std::string{before, middle};
for (auto sec : pkg) {
if (sec.type() == std::string_view{"server"}) {
conf += create_server_conf(sec, indent) + "\n";
}
}
conf.append(after, tmpl.end());
}
write_file(VAR_UCI_CONF, conf);
}
auto is_enabled(const uci::package& pkg) -> 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<uci::package> config;
bool config_enabled = false;
std::mutex configuring;
configuring.lock();
auto thrd = std::thread([&config, &config_enabled, &configuring, &ex] {
try {
config = std::make_unique<uci::package>("nginx");
config_enabled = is_enabled(*config);
configuring.unlock();
check_ssl(*config, config_enabled);
}
catch (...) {
std::cerr<<"init_lan notice: no server named "<<LAN_NAME<<std::endl;
// not: ex = std::current_exception();
std::cerr << "init_lan error: checking UCI file /etc/config/nginx\n";
ex = std::current_exception();
}
});
#endif
try { create_lan_listen(); }
try {
create_lan_listen();
}
catch (...) {
std::cerr<<"init_lan error: cannot create LAN listen files"<<std::endl;
std::cerr << "init_lan error: cannot create listen files of local IPs.\n";
ex = std::current_exception();
}
#ifndef NO_SSL
thrd.join();
#endif
configuring.lock();
if (config_enabled) {
try {
init_uci(*config);
}
catch (...) {
std::cerr << "init_lan error: cannot create " << VAR_UCI_CONF << " from ";
std::cerr << UCI_CONF << ".template using UCI file /etc/config/nginx\n";
ex = std::current_exception();
}
}
if (ex) { std::rethrow_exception(ex); }
thrd.join();
if (ex) {
std::rethrow_exception(ex);
}
}
void get_env()
{
std::cout<<"NGINX_CONF="<<"'"<<NGINX_CONF<<"'"<<std::endl;
std::cout<<"CONF_DIR="<<"'"<<CONF_DIR<<"'"<<std::endl;
std::cout<<"LAN_NAME="<<"'"<<LAN_NAME<<"'"<<std::endl;
std::cout<<"LAN_LISTEN="<<"'"<<LAN_LISTEN<<"'"<<std::endl;
#ifndef NO_SSL
std::cout<<"LAN_SSL_LISTEN="<<"'"<<LAN_SSL_LISTEN<<"'"<<std::endl;
std::cout<<"SSL_SESSION_CACHE_ARG="<<"'"<<SSL_SESSION_CACHE_ARG(LAN_NAME)<<
"'"<<std::endl;
std::cout<<"SSL_SESSION_TIMEOUT_ARG="<<"'"<<SSL_SESSION_TIMEOUT_ARG<<"'\n";
std::cout<<"ADD_SSL_FCT="<<"'"<<ADD_SSL_FCT<<"'"<<std::endl;
#endif
std::cout << "UCI_CONF="
<< "'" << UCI_CONF << "'" << std::endl;
std::cout << "NGINX_CONF="
<< "'" << NGINX_CONF << "'" << std::endl;
std::cout << "CONF_DIR="
<< "'" << CONF_DIR << "'" << std::endl;
std::cout << "LAN_NAME="
<< "'" << LAN_NAME << "'" << std::endl;
std::cout << "LAN_LISTEN="
<< "'" << LAN_LISTEN << "'" << std::endl;
std::cout << "LAN_SSL_LISTEN="
<< "'" << LAN_SSL_LISTEN << "'" << std::endl;
std::cout << "SSL_SESSION_CACHE_ARG="
<< "'" << SSL_SESSION_CACHE_ARG(LAN_NAME) << "'" << std::endl;
std::cout << "SSL_SESSION_TIMEOUT_ARG="
<< "'" << SSL_SESSION_TIMEOUT_ARG << "'\n";
std::cout << "ADD_SSL_FCT="
<< "'" << ADD_SSL_FCT << "'" << std::endl;
std::cout << "MANAGE_SSL="
<< "'" << MANAGE_SSL << "'" << std::endl;
}
auto main(int argc, char * argv[]) -> int
auto main(int argc, char* argv[]) -> int
{
// TODO(pst): use std::span when available:
auto args = std::basic_string_view<char *>{argv, static_cast<size_t>(argc)};
auto args = std::basic_string_view<char*>{argv, static_cast<size_t>(argc)};
auto cmds = std::array{
std::array<std::string_view, 2>{"init_lan", ""},
std::array<std::string_view, 2>{"get_env", ""},
#ifndef NO_SSL
std::array<std::string_view, 2>{ADD_SSL_FCT, " server_name" },
std::array<std::string_view, 2>{"del_ssl", " server_name" },
#endif
std::array<std::string_view, 2>{
ADD_SSL_FCT, "server_name [manager /path/to/ssl_certificate /path/to/ssl_key]"},
std::array<std::string_view, 2>{"del_ssl", "server_name [manager]"},
std::array<std::string_view, 2>{"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 "<<VERSION<<" ";
std::cerr << "version " << VERSION << " ";
#endif
std::cerr<<"with ";
std::cerr << "with libuci, ";
#ifndef NO_UBUS
std::cerr<<"ubus, ";
#endif
#ifndef NO_SSL
std::cerr<<"libopenssl, ";
#ifdef NO_PCRE
std::cerr<<"std::regex, ";
#else
std::cerr<<"PCRE, ";
std::cerr << "libubus, ";
#endif
std::cerr << "libopenssl, ";
#ifndef NO_PCRE
std::cerr << "PCRE, ";
#endif
std::cerr<<"pthread and libstdcpp)."<<std::endl;
std::cerr << "pthread and libstdcpp)." << std::endl;
auto usage = std::string{"usage: "} + *argv + " [";
for (auto cmd : cmds) {
usage += std::string{cmd[0]};
usage += std::string{cmd[1]} + "|";
}
usage[usage.size()-1] = ']';
std::cerr<<usage<<std::endl;
auto usage =
std::accumulate(cmds.begin(), cmds.end(), std::string{"usage: "} + *argv + " [",
[](const auto& use, const auto& cmd) {
return use + std::string{cmd[0]} + (cmd[1].empty() ? "" : " ") +
std::string{cmd[1]} + "|";
});
usage[usage.size() - 1] = ']';
std::cerr << usage << std::endl;
throw std::runtime_error("main error: argument not recognized");
}
return 0;
}
catch (const std::exception & e) { std::cerr<<e.what()<<std::endl; }
catch (const std::exception& e) {
std::cerr << " * " << *argv << " " << e.what() << "\n";
}
catch (...) { perror("main error"); }
catch (...) {
std::cerr << " * * " << *argv;
perror(" main error");
}
return 1;
}

+ 47
- 45
net/nginx-util/src/nginx-util.hpp View File

@ -9,98 +9,103 @@
#include <fstream>
#include <string>
#include <string_view>
#include <thread>
// #include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <thread>
#include <vector>
#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<typename ...S>
auto call(const std::string & program, S... args) -> pid_t;
template <typename... S>
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<<str<<std::flush;
file << str << std::flush;
file.close();
} catch(...) {
if (tmp!=name) { remove(tmp.c_str()); } //remove can fail.
}
catch (...) {
if (tmp != name) {
remove(tmp.c_str());
} // remove can fail.
throw;
}
if (rename(tmp.c_str(), name.data()) != 0) {
throw std::runtime_error
("write_file error: cannot move " + tmp + " to " + name.data());
throw std::runtime_error("write_file error: cannot move " + tmp + " to " + name.data());
}
}
auto read_file(const std::string_view & name,
const std::ios_base::openmode mode) -> 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<char>(file)),
std::istreambuf_iterator<char>());
ret.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
return ret;
}
template<typename ...S>
auto call(const char * program, S... args) -> pid_t
template <typename... S>
auto call(const char* program, S... args) -> pid_t
{
pid_t pid = fork();
if (pid==0) { //child:
std::array<char *, sizeof...(args)+2> argv =
{ strdup(program), strdup(args)..., nullptr };
if (pid == 0) { // child:
std::array<char*, sizeof...(args) + 2> 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

+ 109
- 120
net/nginx-util/src/px5g-openssl.hpp View File

@ -3,15 +3,14 @@
// #define OPENSSL_API_COMPAT 0x10102000L
#include <fcntl.h>
#include <memory>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <unistd.h>
#include <memory>
#include <stdexcept>
#include <string>
#include <unistd.h>
static constexpr auto rsa_min_modulus_bits = 512;
@ -19,67 +18,51 @@ using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
using X509_NAME_ptr = std::unique_ptr<X509_NAME, decltype(&::X509_NAME_free)>;
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<std::string *>(errmsg) += str;
*static_cast<std::string*>(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 (keysize<rsa_min_modulus_bits || keysize>OPENSSL_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<const unsigned char *>(&subject[prev]);
}
else {
const auto* val = // X509_NAME_add_entry_by_NID wants it unsigned:
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
reinterpret_cast<const unsigned char*>(&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

+ 175
- 168
net/nginx-util/src/px5g.cpp View File

@ -1,66 +1,53 @@
#include "px5g-openssl.hpp"
#include <unistd.h>
#include <array>
#include <iostream>
#include <numeric>
#include <string>
#include <string_view>
#include <unistd.h>
class argv_view { // TODO(pst): use std::span when available.
private:
std::basic_string_view<const char *> 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<const char*> 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<size_t>(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<size_t>(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(); ++i) {
if (argv[i]=="-der") {
for (size_t i = 2; i < argv.size(); ++i) {
if (argv[i] == "-der") {
use_pem = false;
} else if (argv[i]=="-in") {
}
else if (argv[i] == "-in") {
++i;
if (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 "<<argv[i]<<std::endl;
} else { // main option:
else if (argv[i][0] == '-') {
std::cerr << "checkend warning: skipping option " << argv[i] << std::endl;
}
else { // main option:
intmax_t num = 0;
try {
num = parse_int(argv[i]);
} catch (...) {
}
catch (...) {
auto errmsg = std::string{"checkend error: invalid time "};
errmsg += argv[i];
std::throw_with_nested(std::runtime_error(errmsg));
@ -123,7 +119,7 @@ auto checkend(const argv_view & argv) -> int
seconds = static_cast<time_t>(num);
if (num!=static_cast<intmax_t>(seconds)) {
if (num != static_cast<intmax_t>(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"<<std::endl;
std::cout << "Certificate will" << (valid ? " not " : " ") << "expire" << std::endl;
return (valid ? 0 : 1);
}
void eckey(const argv_view & argv)
void eckey(const argv_view& argv)
{
bool has_main_option = false;
bool use_pem = true;
std::string keypath{};
int curve = NID_X9_62_prime256v1;
for (size_t i=2; i < argv.size(); ++i) {
if (argv[i]=="-der") {
for (size_t i = 2; i < argv.size(); ++i) {
if (argv[i] == "-der") {
use_pem = false;
} else if (argv[i]=="-out") {
}
else if (argv[i] == "-out") {
++i;
if (i >= 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[i]<<std::endl;
} else { //main option:
else if (argv[i][0] == '-') {
std::cerr << "eckey warning: skipping option " << argv[i] << std::endl;
}
else { // main option:
if (has_main_option) {
throw std::runtime_error
("eckey error: more than one main option");
} //else:
throw std::runtime_error("eckey error: more than one main option");
} // else:
has_main_option = true;
curve = parse_curve(argv[i]);
@ -184,8 +180,7 @@ void eckey(const argv_view & argv)
write_key(gen_eckey(curve), keypath, use_pem);
}
void rsakey(const argv_view & argv)
void rsakey(const argv_view& argv)
{
bool has_main_option = false;
bool use_pem = true;
@ -193,12 +188,14 @@ void rsakey(const argv_view & argv)
BN_ULONG exponent = RSA_F4;
int keysize = rsa_min_modulus_bits;
for (size_t i=2; i < argv.size(); ++i) {
if (argv[i]=="-der") {
for (size_t i = 2; i < argv.size(); ++i) {
if (argv[i] == "-der") {
use_pem = false;
} else if (argv[i]=="-3") {
}
else if (argv[i] == "-3") {
exponent = 3;
} else if (argv[i]=="-out") {
}
else if (argv[i] == "-out") {
++i;
if (i >= 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"<<std::endl;
} else {
throw std::runtime_error
("rsakey error: more than one -out file");
if (argv[i] == keypath) {
std::cerr << "rsakey warning: repeated -out file" << std::endl;
}
else {
throw std::runtime_error("rsakey error: more than one -out file");
}
}
keypath = argv[i];
}
else if (argv[i][0]=='-') {
std::cerr<<"rsakey warning: skipping option "<<argv[i]<<std::endl;
} else { //main option:
else if (argv[i][0] == '-') {
std::cerr << "rsakey warning: skipping option " << argv[i] << std::endl;
}
else { // main option:
if (has_main_option) {
throw std::runtime_error("rsakey error: more than one keysize");
} //else:
} // else:
has_main_option = true;
try {
keysize = parse_int(argv[i]);
} catch (...) {
}
catch (...) {
std::string errmsg{"rsakey error: invalid keysize "};
errmsg += argv[i];
std::throw_with_nested(std::runtime_error(errmsg));
@ -239,8 +238,7 @@ void rsakey(const argv_view & argv)
write_key(gen_rsakey(keysize, exponent), keypath, use_pem);
}
void selfsigned(const argv_view & argv)
void selfsigned(const argv_view& argv)
{
bool use_pem = true;
int days = default_validity;
@ -254,56 +252,58 @@ void selfsigned(const argv_view & argv)
int curve = NID_X9_62_prime256v1;
for (size_t i=2; i < argv.size(); ++i) {
if (argv[i]=="-der") {
for (size_t i = 2; i < argv.size(); ++i) {
if (argv[i] == "-der") {
use_pem = false;
} else if (argv[i]=="-days") {
}
else if (argv[i] == "-days") {
++i;
try {
days = parse_int(argv[i]);
} catch (...) {
}
catch (...) {
std::string errmsg{"selfsigned error: not a number for -days "};
errmsg += argv[i].substr(4);
std::throw_with_nested(std::runtime_error(errmsg));
}
}
else if (argv[i]=="-newkey") {
else if (argv[i] == "-newkey") {
++i;
if (i >= 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 "<<argv[i]<<std::endl;
std::cerr << "selfsigned warning: skipping option " << argv[i] << std::endl;
}
}
@ -381,60 +378,70 @@ void selfsigned(const argv_view & argv)
selfsigned(pkey, days, subject, crtpath, use_pem);
if (!keypath.empty()) { write_key(pkey, keypath, use_pem); }
if (!keypath.empty()) {
write_key(pkey, keypath, use_pem);
}
}
auto main(int argc, const char ** argv) -> int
auto main(int argc, const char** argv) -> int
{
auto args = argv_view{argv, argc};
auto cmds = std::array{
std::array<std::string, 2>{"checkend",
" [-der] [-in certificate_path] [seconds_remaining]"
},
std::array<std::string, 2>{"eckey",
" [-der] [-out key_path] [curve_name]"
},
std::array<std::string, 2>{"rsakey",
" [-der] [-out key_path] [-3] [key_size]"
},
std::array<std::string, 2>{"selfsigned",
" [-der] [-in certificate_path] [seconds_remaining]"},
std::array<std::string, 2>{"eckey", " [-der] [-out key_path] [curve_name]"},
std::array<std::string, 2>{"rsakey", " [-der] [-out key_path] [-3] [key_size]"},
std::array<std::string, 2>{
"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<<usage<<std::flush;
std::cerr << usage << std::flush;
auto print_nested =
[](auto && self, const std::exception & outer, int depth=0) -> 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); }
auto print_nested = [](auto&& self, const std::exception& outer, int depth = 0) -> 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."<<std::endl;
std::cerr << *argv << " unknown error." << std::endl;
return 2;
}


+ 147
- 175
net/nginx-util/src/regex-pcre.hpp View File

@ -1,298 +1,279 @@
#ifndef __REGEXP_PCRE_HPP
#define __REGEXP_PCRE_HPP
#include <array>
#include <pcre.h>
#include <array>
#include <stdexcept>
#include <string>
#include <vector>
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<regex_constants::error_type,86> errcode_pcre2regex;
static const std::array<regex_constants::error_type, 86> 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 <int> vec{};
std::vector<int> 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<int>(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<int>(&*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_constants::error_type, 86> regex::errcode_pcre2regex = {
// 0 no error
regex_constants::error_type::_enum_error_last,
@ -444,7 +418,8 @@ const std::array<regex_constants::error_type, 86> 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_constants::error_type, 86> 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

+ 380
- 17
net/nginx-util/src/test-nginx-util-root.sh View File

@ -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 <<EOF
server {
listen 80;
listen 81;
listen hostname:80;
listen hostname:81;
listen [::]:80;
listen [::]:81;
listen 1.3:80;
# listen 1.3:80;
listen 1.3:81;
listen [1::3]:80;
listen [1::3]:81;
server_name listens;
}
EOF
CONFS="${CONFS} listens:0"
cat > normal.sans <<EOF
server {
include '${LAN_LISTEN}';
@ -148,6 +273,15 @@ server {
EOF
CONFS="${CONFS} normal:0"
cat > acme.sans <<EOF
server {
listen 80;
include '${LAN_LISTEN}';
server_name acme;
}
EOF
CONFS="${CONFS} acme:0"
cat > more_server.sans <<EOF
server {
# include '${LAN_LISTEN}';
@ -163,6 +297,9 @@ CONFS="${CONFS} more_server:0"
cat > more_names.sans <<EOF
server {
include '${LAN_LISTEN}';
include '${LAN_LISTEN}';
include '${LAN_LISTEN}';
not include '${LAN_LISTEN}';
server_name example.com more_names example.org;
}
EOF
@ -203,44 +340,270 @@ EOF
CONFS="${CONFS} tab:0"
[ "$PRINT_PASSED" -gt 0 ] && printf "\nSetup files in %s ...\n" "${CONF_DIR}"
for conf in ${CONFS}
do test 'setpoint_add_ssl " " '"${conf%:*}" "${conf#*:}"
done
test 'setpoint_add_ssl "\t" tab' 0 # fixes wrong indentation.
[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting Cron ... \n"
echo -n "prefix" >"/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

+ 10
- 1
net/nginx-util/src/test-nginx-util.sh View File

@ -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/"


+ 71
- 79
net/nginx-util/src/ubus-cxx.cpp View File

@ -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) "<<std::endl;
std::cout << "Cron is active (with or without instances) " << std::endl;
}
}
inline void example_for_getting_values()
{
auto lan_status = ubus::call("network.interface.lan", "status");
for (auto t : lan_status.filter("ipv6-address", "", "address")) {
//NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
auto x = const_cast<blob_attr *>(t);
std::cout<<"["<<blobmsg_get_string(x)<<"] ";
for (const auto* t : lan_status.filter("ipv6-address", "", "address")) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
auto* x = const_cast<blob_attr*>(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<blob_attr *>(t);
std::cout<<blobmsg_get_string(x)<<" ";
for (const auto* t : lan_status.filter("ipv4-address", "").filter("address")) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
auto* x = const_cast<blob_attr*>(t);
std::cout << blobmsg_get_string(x) << " ";
}
std::cout<<std::endl;
std::cout << std::endl;
}
inline void example_for_sending_message()
{
auto set_arg = [](blob_buf * buf) -> 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<blob_attr *>(t);
std::cout<<blobmsg_get_string(x)<<"\n";
auto set_arg = [](blob_buf* buf) -> 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<blob_attr*>(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<blob_attr *>(t);
std::cout<<blobmsg_name(x)<<": ";
for (const auto* t : ubus::call("network.interface.lan", "status").filter(keys)) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
auto* x = const_cast<blob_attr*>(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<<blobmsg_get_string(x); break;
case BLOBMSG_TYPE_INT64: std::cout<<blobmsg_get_u64(x); break;
case BLOBMSG_TYPE_INT32: std::cout<<blobmsg_get_u32(x); break;
case BLOBMSG_TYPE_INT16: std::cout<<blobmsg_get_u16(x); break;
case BLOBMSG_TYPE_BOOL: std::cout<<blobmsg_get_bool(x); break;
case BLOBMSG_TYPE_DOUBLE: std::cout<<blobmsg_get_double(x); break;
default: std::cout<<"[unknown]";
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 << blobmsg_get_string(x); break;
case BLOBMSG_TYPE_INT64: std::cout << blobmsg_get_u64(x); break;
case BLOBMSG_TYPE_INT32: std::cout << blobmsg_get_u32(x); break;
case BLOBMSG_TYPE_INT16: std::cout << blobmsg_get_u16(x); break;
case BLOBMSG_TYPE_BOOL: std::cout << blobmsg_get_bool(x); break;
case BLOBMSG_TYPE_DOUBLE: std::cout << blobmsg_get_double(x); break;
default: std::cout << "[unknown]";
}
std::cout<<std::endl;
std::cout << std::endl;
}
}
inline void example_for_recursive_exploring()
{ // output like from the original ubus call:
const auto explore = [](auto message) -> 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<<std::endl;
auto explore_internal = [&end](auto& explore_ref, auto it, size_t depth = 1) -> void {
std::cout << std::endl;
bool first = true;
for (; it!=end; ++it) {
//NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
auto attr = const_cast<blob_attr *>(*it);
if (first) { first = false; }
else { std::cout<<",\n"; }
std::cout<<std::string(depth, '\t');
for (; it != end; ++it) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
auto* attr = const_cast<blob_attr*>(*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<<"\""<<name<<"\": "; }
if (!name.empty()) {
std::cout << "\"" << name << "\": ";
}
switch (blob_id(attr)) {
case BLOBMSG_TYPE_UNSPEC: std::cout<<"(unspecified)"; break;
case BLOBMSG_TYPE_UNSPEC: std::cout << "(unspecified)"; break;
case BLOBMSG_TYPE_ARRAY:
std::cout<<"[";
explore_ref(explore_ref, ubus::iterator{attr}, depth+1);
std::cout<<"\n"<<std::string(depth, '\t')<<"]";
std::cout << "[";
explore_ref(explore_ref, ubus::iterator{attr}, depth + 1);
std::cout << "\n" << std::string(depth, '\t') << "]";
break;
case BLOBMSG_TYPE_TABLE:
std::cout<<"{";
explore_ref(explore_ref, ubus::iterator{attr}, depth+1);
std::cout<<"\n"<<std::string(depth, '\t')<<"}";
std::cout << "{";
explore_ref(explore_ref, ubus::iterator{attr}, depth + 1);
std::cout << "\n" << std::string(depth, '\t') << "}";
break;
case BLOBMSG_TYPE_STRING:
std::cout<<"\""<<blobmsg_get_string(attr)<<"\"";
break;
case BLOBMSG_TYPE_INT64:
std::cout<<blobmsg_get_u64(attr);
break;
case BLOBMSG_TYPE_INT32:
std::cout<<blobmsg_get_u32(attr);
break;
case BLOBMSG_TYPE_INT16:
std::cout<<blobmsg_get_u16(attr);
std::cout << "\"" << blobmsg_get_string(attr) << "\"";
break;
case BLOBMSG_TYPE_INT64: std::cout << blobmsg_get_u64(attr); break;
case BLOBMSG_TYPE_INT32: std::cout << blobmsg_get_u32(attr); break;
case BLOBMSG_TYPE_INT16: std::cout << blobmsg_get_u16(attr); break;
case BLOBMSG_TYPE_BOOL:
std::cout<<(blobmsg_get_bool(attr) ? "true" : "false");
std::cout << (blobmsg_get_bool(attr) ? "true" : "false");
break;
case BLOBMSG_TYPE_DOUBLE:
std::cout<<blobmsg_get_double(attr);
break;
default: std::cout<<"(unknown)"; break;
case BLOBMSG_TYPE_DOUBLE: std::cout << blobmsg_get_double(attr); break;
default: std::cout << "(unknown)"; break;
}
}
};
std::cout<<"{";
std::cout << "{";
explore_internal(explore_internal, message.begin());
std::cout<<"\n}"<<std::endl;
std::cout << "\n}" << std::endl;
};
explore(ubus::call("network.interface.lan", "status"));
}
auto main() -> 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<<e.what()<<std::endl; }
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
catch (...) { perror("main error"); }
catch (...) {
perror("main error");
}
return 1;
}

+ 160
- 181
net/nginx-util/src/ubus-cxx.hpp View File

@ -1,8 +1,8 @@
#ifndef _UBUS_CXX_HPP
#define _UBUS_CXX_HPP
#include <cassert>
#include <libubus.h>
#include <cassert>
#include <memory>
#include <mutex>
#include <string>
@ -13,7 +13,6 @@
#include <iostream>
#endif
namespace ubus {
static constexpr int call_timeout = 500;
@ -22,12 +21,13 @@ using msg_ptr = std::shared_ptr<const blob_attr>;
using strings = std::vector<std::string>;
inline auto concat(strings dest)
{
return dest;
}
inline auto concat(strings dest) { return dest; }
template<class ...Strings>
inline auto concat(strings dest, strings src, Strings ...more)
template <class... Strings>
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<class S, class ...Strings>
inline auto concat(strings dest, S src, Strings ...more)
template <class S, class... Strings>
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<iterator> 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<blob_attr *>(blobmsg_data(pos));
pos = static_cast<blob_attr*>(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<blob_attr *>(blobmsg_data(pos));
pos = static_cast<blob_attr*>(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<void>(cur.release()); } }
{
if (cur.get() == this) {
static_cast<void>(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<class ...Strings>
auto filter(Strings ...filter)
template <class... Strings>
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<ubus_context, decltype(ubus_freeing)>
lazy_ctx{ubus_connect(nullptr), ubus_freeing};
static auto ubus_freeing = [](ubus_context* ctx) { ubus_free(ctx); };
static std::unique_ptr<ubus_context, decltype(ubus_freeing)> 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<blob_buf, decltype(blob_buf_freeing)>
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 <class F>
auto call(const char* path, const char* method, F set_arguments, int timeout = call_timeout)
-> message;
template<class F>
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<<std::string(i,'>')<<" look for "<<keys[i]<<" at ";
std::cout<<blobmsg_name(cur->pos)<<std::endl;
#endif
for (;;) {
#ifndef NDEBUG
std::cout << std::string(i, '>') << " 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)
&& i<n
&& matches()
&& blobmsg_data_len(cur->pos)>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<new_iterator>(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<class F>
inline auto call(const char * path, const char * method, F set_arguments,
int timeout) -> message
template <class F>
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<msg_ptr *>(req->priv);
if (saved==nullptr || *saved) { return; }
auto* saved = static_cast<msg_ptr*>(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

+ 22
- 0
net/nginx-util/src/uci-cxx.cpp View File

@ -0,0 +1,22 @@
#include <iostream>
#include <mutex>
#include <string>
#include <string_view>
#include <vector>
#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";
}
}

+ 474
- 0
net/nginx-util/src/uci-cxx.hpp View File

@ -0,0 +1,474 @@
#ifndef _UCI_CXX_HPP
#define _UCI_CXX_HPP
#include <uci.h>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>
#include <string_view>
namespace uci {
template <class T>
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<uci_context, decltype(free_ctx)> 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 T>
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<T>
{
return iterator<T>{_ptr, _begin};
}
[[nodiscard]] inline auto end() const -> iterator<T>
{
return iterator<T>{_ptr, _end};
}
inline ~element() = default;
};
class section;
class option;
class item;
class package : public element<section> {
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<option> {
public:
inline section(const uci_ptr& pre, uci_element* last) : element{pre, last}
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast)
ptr().s = uci_to_section(ptr().last); // macro casting pointer-offset.
ptr().section = ptr().last->name;
auto* end = &ptr().s->options;
auto* begin = end->next;
init_begin_end(begin, end);
}
auto set(const char* key, const char* value) const -> option;
void del();
[[nodiscard]] inline auto anonymous() const -> bool
{
return ptr().s->anonymous;
}
[[nodiscard]] inline auto type() const -> std::string
{
return ptr().s->type;
}
};
class option : public element<item> {
public:
inline option(const uci_ptr& pre, uci_element* last) : element{pre, last}
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast)
ptr().o = uci_to_option(ptr().last); // macro casting pointer-offset.
ptr().option = ptr().last->name;
if (ptr().o->type == UCI_TYPE_LIST) { // use union ptr().o->v as list:
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
auto* end = &ptr().o->v.list;
auto* begin = end->next;
init_begin_end(begin, end);
}
else {
auto* begin = &ptr().last->list;
auto* end = begin->next;
init_begin_end(begin, end);
}
}
void del();
[[nodiscard]] inline auto type() const -> std::string
{
return (ptr().o->type == UCI_TYPE_LIST ? "list" : "option");
}
};
class item : public element<item> {
public:
inline item(const uci_ptr& pre, uci_element* last) : element{pre, last}
{
ptr().value = ptr().last->name;
}
[[nodiscard]] inline auto type() const -> std::string
{
return (ptr().o->type == UCI_TYPE_LIST ? "list" : "option");
}
[[nodiscard]] inline auto name() const -> std::string
{
return (ptr().last->type == UCI_TYPE_ITEM
? ptr().last->name
:
// else: use union ptr().o->v as string:
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
ptr().o->v.string);
}
inline explicit operator bool() const
{
const auto x = std::string_view{name()};
if (x == "0" || x == "off" || x == "false" || x == "no" || x == "disabled") {
return false;
}
if (x == "1" || x == "on" || x == "true" || x == "yes" || x == "enabled") {
return true;
}
auto errmsg = std::string{"uci_error: item is not bool "} + name();
throw std::runtime_error(errmsg);
}
void rename(const char* value) const;
};
// ------------------------- implementation: ----------------------------------
std::mutex locked_context::inuse{};
inline auto uci_error(uci_context* ctx, const char* prefix = nullptr) -> std::runtime_error
{
char* errmsg = nullptr;
uci_get_errorstr(ctx, &errmsg, prefix);
std::unique_ptr<char, decltype(&std::free)> auto_free{errmsg, std::free};
return std::runtime_error{errmsg};
}
template <class T>
auto element<T>::operator[](std::string_view key) const -> T
{
for (auto elmt : *this) {
if (elmt.name() == key) {
return elmt;
}
}
auto errmsg = std::string{"uci error: cannot find "}.append(key);
throw uci_error(locked_context{}.get(), errmsg.c_str());
}
template <class T>
void element<T>::rename(const char* value) const
{
if (value == name()) {
return;
}
auto ctx = locked_context{};
auto tmp_ptr = uci_ptr{_ptr};
tmp_ptr.value = value;
if (uci_rename(ctx.get(), &tmp_ptr) != 0) {
auto errmsg = std::string{"uci error: cannot rename "}.append(name());
throw uci_error(ctx.get(), errmsg.c_str());
}
}
template <class T>
void element<T>::commit() const
{
auto ctx = locked_context{};
// TODO(pst) use when possible:
// if (uci_commit(ctx.get(), &_ptr.p, true) != 0) {
// auto errmsg = std::string{"uci error: cannot commit "} + _ptr.package;
// throw uci_error(ctx.get(), errmsg.c_str());
// }
auto err = uci_save(ctx.get(), _ptr.p);
if (err == 0) {
uci_package* tmp_pkg = nullptr;
uci_context* tmp_ctx = uci_alloc_context();
err = (tmp_ctx == nullptr ? 1 : 0);
if (err == 0) {
err = uci_load(tmp_ctx, _ptr.package, &tmp_pkg);
}
if (err == 0) {
err = uci_commit(tmp_ctx, &tmp_pkg, false);
}
if (err == 0) {
err = uci_unload(tmp_ctx, tmp_pkg);
}
if (tmp_ctx != nullptr) {
uci_free_context(tmp_ctx);
}
}
if (err != 0) {
auto errmsg = std::string{"uci error: cannot commit "} + _ptr.package;
throw uci_error(ctx.get(), errmsg.c_str());
}
}
package::package(const char* name)
{
auto ctx = locked_context{};
auto* pkg = uci_lookup_package(ctx.get(), name);
if (pkg == nullptr) {
if (uci_load(ctx.get(), name, &pkg) != 0) {
auto errmsg = std::string{"uci error: cannot load package "} + name;
throw uci_error(ctx.get(), errmsg.c_str());
}
}
ptr().package = name;
ptr().p = pkg;
ptr().last = &pkg->e;
auto* end = &ptr().p->sections;
auto* begin = end->next;
init_begin_end(begin, end);
}
auto package::set(const char* key, const char* type) const -> section
{
auto ctx = locked_context{};
auto tmp_ptr = uci_ptr{ptr()};
tmp_ptr.section = key;
tmp_ptr.value = type;
if (uci_set(ctx.get(), &tmp_ptr) != 0) {
auto errmsg = std::string{"uci error: cannot set section "} + type + "'" + key +
"' in package " + name();
throw uci_error(ctx.get(), errmsg.c_str());
}
return section{ptr(), tmp_ptr.last};
}
auto section::set(const char* key, const char* value) const -> option
{
auto ctx = locked_context{};
auto tmp_ptr = uci_ptr{ptr()};
tmp_ptr.option = key;
tmp_ptr.value = value;
if (uci_set(ctx.get(), &tmp_ptr) != 0) {
auto errmsg = std::string{"uci error: cannot set option "} + key + "'" + value +
"' in package " + name();
throw uci_error(ctx.get(), errmsg.c_str());
}
return option{ptr(), tmp_ptr.last};
}
void section::del()
{
auto ctx = locked_context{};
if (uci_delete(ctx.get(), &ptr()) != 0) {
auto errmsg = std::string{"uci error: cannot delete section "} + name();
throw uci_error(ctx.get(), errmsg.c_str());
}
}
void option::del()
{
auto ctx = locked_context{};
if (uci_delete(ctx.get(), &ptr()) != 0) {
auto errmsg = std::string{"uci error: cannot delete option "} + name();
throw uci_error(ctx.get(), errmsg.c_str());
}
}
void item::rename(const char* value) const
{
if (value == name()) {
return;
}
auto ctx = locked_context{};
auto tmp_ptr = uci_ptr{ptr()};
if (tmp_ptr.last->type != UCI_TYPE_ITEM) {
tmp_ptr.value = value;
if (uci_set(ctx.get(), &tmp_ptr) != 0) {
auto errmsg = std::string{"uci error: cannot rename item "} + name();
throw uci_error(ctx.get(), errmsg.c_str());
}
return;
} // else:
tmp_ptr.value = tmp_ptr.last->name;
if (uci_del_list(ctx.get(), &tmp_ptr) != 0) {
auto errmsg = std::string{"uci error: cannot rename (del) "} + name();
throw uci_error(ctx.get(), errmsg.c_str());
}
tmp_ptr.value = value;
if (uci_add_list(ctx.get(), &tmp_ptr) != 0) {
auto errmsg = std::string{"uci error: cannot rename (add) "} + value;
throw uci_error(ctx.get(), errmsg.c_str());
}
}
} // namespace uci
#endif

Loading…
Cancel
Save