#include <iostream>
|
|
#include <numeric>
|
|
|
|
#include "nginx-ssl-util.hpp"
|
|
#include "nginx-util.hpp"
|
|
|
|
static auto constexpr file_comment_auto_created =
|
|
std::string_view{"# This file is re-created when Nginx starts.\n"};
|
|
|
|
// 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*
|
|
{
|
|
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");
|
|
|
|
for (const auto* ip : loopback_status.filter("ipv4-address", "", "address")) {
|
|
ips.emplace_back(_pst_get_string(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 */
|
|
}
|
|
|
|
try {
|
|
auto lan_status = ubus::call("network.interface.lan", "status");
|
|
|
|
for (const auto* ip : lan_status.filter("ipv4-address", "", "address")) {
|
|
ips.emplace_back(_pst_get_string(ip));
|
|
}
|
|
|
|
for (const auto* ip : lan_status.filter("ipv6-address", "", "address")) {
|
|
ips.emplace_back(std::string{"["} + _pst_get_string(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 */
|
|
}
|
|
#else
|
|
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);
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
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 error: checking UCI file /etc/config/nginx\n";
|
|
ex = std::current_exception();
|
|
}
|
|
});
|
|
|
|
try {
|
|
create_lan_listen();
|
|
}
|
|
catch (...) {
|
|
std::cerr << "init_lan error: cannot create listen files of local IPs.\n";
|
|
ex = std::current_exception();
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
thrd.join();
|
|
if (ex) {
|
|
std::rethrow_exception(ex);
|
|
}
|
|
}
|
|
|
|
void get_env()
|
|
{
|
|
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
|
|
{
|
|
// TODO(pst): use std::span when available:
|
|
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", ""},
|
|
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();
|
|
}
|
|
|
|
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]});
|
|
}
|
|
|
|
// 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 == 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 (";
|
|
#ifdef VERSION
|
|
std::cerr << "version " << VERSION << " ";
|
|
#endif
|
|
std::cerr << "with libuci, ";
|
|
#ifndef NO_UBUS
|
|
std::cerr << "libubus, ";
|
|
#endif
|
|
std::cerr << "libopenssl, ";
|
|
#ifndef NO_PCRE
|
|
std::cerr << "PCRE, ";
|
|
#endif
|
|
std::cerr << "pthread and libstdcpp)." << 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 << " * " << *argv << " " << e.what() << "\n";
|
|
}
|
|
|
|
catch (...) {
|
|
std::cerr << " * * " << *argv;
|
|
perror(" main error");
|
|
}
|
|
|
|
return 1;
|
|
}
|