diff --git a/.gitmodules b/.gitmodules index d5dfc46..c0fbe12 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/rapidcheck"] path = lib/rapidcheck url = https://github.com/emil-e/rapidcheck.git +[submodule "lib/yaml-cpp"] + path = lib/yaml-cpp + url = https://github.com/jbeder/yaml-cpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d329e4..c0f38e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) set(CXX_STANDARD_REQUIRED ON) option (USE_SYSTEM_MINIUPNP "Try to locate miniupnpc on the system and build against that" ON) +option (USE_SYSTEM_YAMLCPP "Try to locate miniupnpc on the system and build against that" ON) include(CTest) @@ -26,6 +27,12 @@ else() add_subdirectory(lib/miniupnp/miniupnpc) endif() +if (USE_SYSTEM_YAMLCPP) + find_package(YamlCpp 0.5.1 REQUIRED) +else() + set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "" FORCE) + add_subdirectory(lib/yaml-cpp) +endif() add_executable(${PROJECT_NAME} src/main.cpp @@ -36,6 +43,7 @@ add_library(${PROJECT_NAME}_implem STATIC src/upnp.cpp src/upnpexceptions.cpp src/find_redirections.cpp + src/settings.cpp ) set_property(TARGET ${PROJECT_NAME}_implem PROPERTY CXX_EXTENSIONS OFF) set_property(TARGET ${PROJECT_NAME}_implem PROPERTY CXX_STANDARD 14) @@ -62,6 +70,21 @@ else() PRIVATE libminiupnpc-${upnp_lib_type} ) endif() +if (USE_SYSTEM_YAMLCPP) + target_include_directories(${PROJECT_NAME}_implem SYSTEM + PRIVATE ${YAMLCPP_INCLUDE_DIR} + ) + target_link_libraries(${PROJECT_NAME}_implem + PRIVATE ${YAMLCPP_LIBRARY} + ) +else() + target_include_directories(${PROJECT_NAME}_implem + PRIVATE lib/yaml-cpp/include + ) + target_link_libraries(${PROJECT_NAME}_implem + PRIVATE yaml-cpp + ) +endif() target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_implem diff --git a/keepupnpup.yml b/keepupnpup.yml new file mode 100644 index 0000000..01162ac --- /dev/null +++ b/keepupnpup.yml @@ -0,0 +1,8 @@ +redirect: + host: self + tcp: + - 6881 + - 49164 + udp: + - 49164 + desc: RoutArm redir diff --git a/lib/yaml-cpp b/lib/yaml-cpp new file mode 160000 index 0000000..519d33f --- /dev/null +++ b/lib/yaml-cpp @@ -0,0 +1 @@ +Subproject commit 519d33fea3fbcbe7e1f89f97ee0fa539cec33eb7 diff --git a/src/main.cpp b/src/main.cpp index dc33e73..54d977a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ */ #include "upnp.hpp" +#include "settings.hpp" #include #include #include @@ -30,23 +31,22 @@ namespace { return false; } + void add_bindings (kuu::UPNP& parUpnp, const std::string& parHost, const kuu::RedirectSetting::PortMappingsList& parPortMappings, const std::string& parDesc) { + const std::vector redirs; // = upnp.redirections(); + + for (auto& mapping : parPortMappings) { + //std::cout << "Adding " << mapping.port << " --> " << parHost << ":" << mapping.internal_port << " " << mapping.protocol << '\n'; + if (not has_mapping(redirs, parHost, mapping.port, mapping.protocol)) + parUpnp.add_port_mapping(mapping.port, mapping.internal_port, parHost, parDesc, 0, mapping.protocol); + } + } + void runapp() { kuu::UPNP upnp; - const auto redirs = upnp.redirections(); + kuu::RedirectSetting settings = kuu::load_redirect_settings("keepupnpup.yml", upnp.lanaddr()); - if (not has_mapping(redirs, upnp.lanaddr(), 6881, kuu::Protocol::TCP)) - upnp.add_port_mapping(6881, 6881, upnp.lanaddr(), "RoutArm redir", 0, kuu::Protocol::TCP); - if (not has_mapping(redirs, upnp.lanaddr(), 49164, kuu::Protocol::TCP)) - upnp.add_port_mapping(49164, 49164, upnp.lanaddr(), "RoutArm redir", 0, kuu::Protocol::TCP); - if (not has_mapping(redirs, upnp.lanaddr(), 49164, kuu::Protocol::UDP)) - upnp.add_port_mapping(49164, 49164, upnp.lanaddr(), "RoutArm redir", 0, kuu::Protocol::UDP); - - //for (auto& redir : redirs) { - // std::cout << redir.protocol << ", " << redir.remote_host << ":" << redir.external_port << " --> " << - // redir.internal_client << ":" << redir.internal_port << ", " << redir.duration << ", " << - // redir.enabled << R"(, ")" << redir.desc << "\"\n"; - //} + add_bindings(upnp, settings.host, settings.port_mappings, settings.desc); } } //unnamed namespace diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 0000000..b98403a --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,76 @@ +/* Copyright 2016, Michele Santullo + * This file is part of "keepupnpup". + * + * "keepupnpup" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "keepupnpup" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "keepupnpup". If not, see . + */ + +#include "settings.hpp" +#include +#include +#include + +namespace kuu { + namespace { + void load_bindings(RedirectSetting::PortMappingsList& parMappings, Protocol parProtocol, const YAML::Node& parNode) { + if (parNode) { + if (parNode.IsSequence()) { + parMappings.reserve(parMappings.size() + parNode.size()); + for (std::size_t z = 0; z < parNode.size(); ++z) { + const auto port = parNode[z].as(); + parMappings.push_back(PortMapping(port, port, parProtocol)); + } + } + else if (parNode.IsScalar()) { + parMappings.reserve(parMappings.size() + 1); + const auto port = parNode.as(); + parMappings.push_back(PortMapping(port, port, parProtocol)); + } + else { + throw std::runtime_error("Expected single port or port list"); + } + } + } + } //unnamed namespace + + RedirectSetting load_redirect_settings (const std::string& parPath, const std::string& parLanAddr) { + auto settings = YAML::LoadFile(parPath); + auto redirect_node = settings["redirect"]; + if (not redirect_node) + throw std::runtime_error("Can't find \"redirect\" key in yaml configuration"); + if (not redirect_node.IsMap()) + throw std::runtime_error("\"redirect\" key is not a map"); + + RedirectSetting redir_settings; + redir_settings.host = "self"; + if (redirect_node["host"]) { + if (not redirect_node["host"].IsScalar()) + throw std::runtime_error("\"host\" node is not a scalar"); + redir_settings.host = redirect_node["host"].as(); + } + if (redir_settings.host == "self") + redir_settings.host = parLanAddr; + + load_bindings(redir_settings.port_mappings, Protocol::TCP, redirect_node["tcp"]); + load_bindings(redir_settings.port_mappings, Protocol::UDP, redirect_node["udp"]); + + redir_settings.desc = "KeepUPNPUp redirect"; + if (redirect_node["desc"]) { + if (not redirect_node["desc"].IsScalar()) + throw std::runtime_error("\"desc\" node is not a scalar"); + redir_settings.host = redirect_node["desc"].as(); + } + + return redir_settings; + } +} //namespace kuu diff --git a/src/settings.hpp b/src/settings.hpp new file mode 100644 index 0000000..65a257b --- /dev/null +++ b/src/settings.hpp @@ -0,0 +1,47 @@ +/* Copyright 2016, Michele Santullo + * This file is part of "keepupnpup". + * + * "keepupnpup" is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * "keepupnpup" is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with "keepupnpup". If not, see . + */ + +#pragma once + +#include "redirection.hpp" +#include +#include +#include + +namespace kuu { + struct PortMapping { + PortMapping (uint16_t parPort, uint16_t parInternalPort, Protocol parProtocol) : + port(parPort), + internal_port(parInternalPort), + protocol(parProtocol) + { + } + + uint16_t port; + uint16_t internal_port; + Protocol protocol; + }; + + struct RedirectSetting { + using PortMappingsList = std::vector; + std::string host; + std::string desc; + PortMappingsList port_mappings; + }; + + RedirectSetting load_redirect_settings (const std::string& parPath, const std::string& parLanAddr); +} //namespace kuu