diff --git a/.gitmodules b/.gitmodules
index 85252af..d5dfc46 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,9 @@
[submodule "lib/better-enums"]
path = lib/better-enums
url = https://github.com/aantron/better-enums
+[submodule "lib/gtest"]
+ path = lib/gtest
+ url = https://github.com/google/googletest.git
+[submodule "lib/rapidcheck"]
+ path = lib/rapidcheck
+ url = https://github.com/emil-e/rapidcheck.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9db37fe..1d329e4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,8 +2,14 @@ cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
project(keepupnpup CXX)
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)
+include(CTest)
+
+find_package(Boost 1.55.0 REQUIRED)
+
if (USE_SYSTEM_MINIUPNP)
find_package(Miniupnpc 2.0 REQUIRED)
else()
@@ -17,35 +23,56 @@ else()
set(UPNPC_BUILD_SHARED OFF CACHE BOOL "" FORCE)
endif()
set(UPNPC_BUILD_TESTS OFF CACHE BOOL "" FORCE)
- set(CXX_STANDARD_REQUIRED ON)
add_subdirectory(lib/miniupnp/miniupnpc)
endif()
add_executable(${PROJECT_NAME}
src/main.cpp
+)
+set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_EXTENSIONS OFF)
+set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 14)
+add_library(${PROJECT_NAME}_implem STATIC
src/upnp.cpp
src/upnpexceptions.cpp
+ src/find_redirections.cpp
)
+set_property(TARGET ${PROJECT_NAME}_implem PROPERTY CXX_EXTENSIONS OFF)
+set_property(TARGET ${PROJECT_NAME}_implem PROPERTY CXX_STANDARD 14)
-target_include_directories(${PROJECT_NAME}
- PRIVATE lib/better-enums
+target_include_directories(${PROJECT_NAME}_implem
+ PUBLIC lib/better-enums
+)
+target_include_directories(${PROJECT_NAME}_implem SYSTEM
+ PUBLIC ${Boost_INCLUDE_DIRS}
)
if (USE_SYSTEM_MINIUPNP)
- target_include_directories(${PROJECT_NAME} SYSTEM
+ target_include_directories(${PROJECT_NAME}_implem SYSTEM
PRIVATE ${MINIUPNP_INCLUDE_DIR}
)
- target_link_libraries(${PROJECT_NAME}
+ target_link_libraries(${PROJECT_NAME}_implem
PRIVATE ${MINIUPNP_LIBRARY}
)
else()
- target_include_directories(${PROJECT_NAME}
+ target_include_directories(${PROJECT_NAME}_implem
PRIVATE lib/miniupnp/miniupnpc
)
- target_link_libraries(${PROJECT_NAME}
- libminiupnpc-${upnp_lib_type}
+ target_link_libraries(${PROJECT_NAME}_implem
+ PRIVATE libminiupnpc-${upnp_lib_type}
)
endif()
-set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 14)
+target_link_libraries(${PROJECT_NAME}
+ PRIVATE ${PROJECT_NAME}_implem
+)
+
+if (BUILD_TESTING)
+ set(BUILD_GTEST ON CACHE BOOL "" FORCE)
+ set(BUILD_GMOCK OFF CACHE BOOL "" FORCE)
+ set(RC_ENABLE_GTEST ON CACHE BOOL "" FORCE)
+
+ add_subdirectory(lib/gtest)
+ add_subdirectory(lib/rapidcheck)
+ add_subdirectory(test/unit)
+endif()
diff --git a/lib/gtest b/lib/gtest
new file mode 160000
index 0000000..48ee8e9
--- /dev/null
+++ b/lib/gtest
@@ -0,0 +1 @@
+Subproject commit 48ee8e98abc950abd8541e15550b18f8f6cfb3a9
diff --git a/lib/rapidcheck b/lib/rapidcheck
new file mode 160000
index 0000000..f5d3afa
--- /dev/null
+++ b/lib/rapidcheck
@@ -0,0 +1 @@
+Subproject commit f5d3afa4f387ecf147faf98d96710a6edfa420f1
diff --git a/src/find_redirections.cpp b/src/find_redirections.cpp
new file mode 100644
index 0000000..f07bd57
--- /dev/null
+++ b/src/find_redirections.cpp
@@ -0,0 +1,49 @@
+/* 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 "find_redirections.hpp"
+#include
+
+namespace kuu {
+ auto redirections_for_localaddr (
+ const RedirectionsList& parRedirs,
+ const std::string& parAddr,
+ uint16_t parPort,
+ Protocol parProto
+ ) {
+ return parRedirs | boost::adaptors::filtered([=](const Redirection& parRedir) {
+ return parRedir.internal_port == parPort and
+ parRedir.protocol == parProto and
+ parRedir.internal_client == parAddr
+ ;
+ });
+ }
+
+ auto redirections_not_for_localaddr (
+ const RedirectionsList& parRedirs,
+ const std::string& parAddr,
+ uint16_t parPort,
+ Protocol parProto
+ ) {
+ return parRedirs | boost::adaptors::filtered([=](const Redirection& parRedir) {
+ return parRedir.internal_port == parPort and
+ parRedir.protocol == parProto and
+ parRedir.internal_client != parAddr
+ ;
+ });
+ }
+} //namespace kuu
diff --git a/src/find_redirections.hpp b/src/find_redirections.hpp
new file mode 100644
index 0000000..5886b35
--- /dev/null
+++ b/src/find_redirections.hpp
@@ -0,0 +1,41 @@
+/* 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 {
+ using RedirectionsList = std::vector;
+
+ auto redirections_for_localaddr (
+ const RedirectionsList& parRedirs,
+ const std::string& parAddr,
+ uint16_t parPort,
+ Protocol parProto
+ );
+
+ auto redirections_not_for_localaddr (
+ const RedirectionsList& parRedirs,
+ const std::string& parAddr,
+ uint16_t parPort,
+ Protocol parProto
+ );
+} //namespace kuu
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
new file mode 100644
index 0000000..d1ce180
--- /dev/null
+++ b/test/unit/CMakeLists.txt
@@ -0,0 +1,23 @@
+project(test_kuu CXX)
+
+add_executable(${PROJECT_NAME}
+ test_upnp.cpp
+)
+set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_EXTENSIONS OFF)
+set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 14)
+
+add_test(
+ NAME kuu
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND ${PROJECT_NAME}
+)
+
+target_link_libraries(${PROJECT_NAME}
+ PRIVATE keepupnpup_implem
+ PRIVATE gtest
+ PRIVATE gtest_main
+ PRIVATE rapidcheck_gtest
+)
+target_include_directories(${PROJECT_NAME}
+ PRIVATE ${CMAKE_SOURCE_DIR}/src
+)
diff --git a/test/unit/test_upnp.cpp b/test/unit/test_upnp.cpp
new file mode 100644
index 0000000..f493512
--- /dev/null
+++ b/test/unit/test_upnp.cpp
@@ -0,0 +1,101 @@
+/* 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 "gtest/gtest.h"
+#include "rapidcheck/gtest.h"
+#include "redirection.hpp"
+#include "find_redirections.hpp"
+#include
+#include
+#include
+#include
+
+namespace {
+ std::string make_ip(uint8_t parA, uint8_t parB, uint8_t parC, uint8_t parD) {
+ std::ostringstream oss;
+ oss <<
+ static_cast(parA) << '.' <<
+ static_cast(parB) << '.' <<
+ static_cast(parC) << '.' <<
+ static_cast(parD)
+ ;
+ return oss.str();
+ }
+
+ rc::Gen generate_lan_ip() {
+ using namespace rc;
+
+ auto ip_any = gen::arbitrary();
+ auto ip_pos = gen::positive();
+
+ std::vector> gens;
+ gens.push_back(gen::apply([](uint8_t c, uint8_t d) { return make_ip(192, 168, c, d); }, ip_any, ip_pos));
+ gens.push_back(gen::apply([](uint8_t b, uint8_t c, uint8_t d) { return make_ip(10, b, c, d); }, ip_any, ip_any, ip_pos));
+ gens.push_back(gen::apply([](uint8_t b, uint8_t c, uint8_t d) { return make_ip(172, b, c, d); }, gen::inRange(16, 31), ip_any, ip_pos));
+
+ return gen::join(gen::elementOf(gens));
+ }
+
+ rc::Gen generate_external_ip() {
+ using namespace rc;
+ auto ip_a = gen::suchThat(gen::inRange(1, 254), [](uint8_t v) { return 192 != v and 10 != v and 172 != v; });
+ auto ip_b = gen::arbitrary();
+ auto ip_c = gen::arbitrary();
+ auto ip_d = gen::positive();
+
+ return gen::apply(&make_ip, ip_a, ip_b, ip_c, ip_d);
+ }
+
+ rc::Gen generate_description() {
+ using namespace rc;
+ return gen::nonEmpty(gen::resize(80, gen::container(gen::inRange('a', 'z'))));
+ }
+} //unnamed namespace
+
+namespace rc {
+ template <>
+ struct Arbitrary {
+ static Gen arbitrary() {
+ using kuu::Redirection;
+
+ return gen::build(
+ gen::set(&Redirection::internal_client, generate_lan_ip()),
+ gen::set(&Redirection::remote_host, generate_external_ip()),
+ gen::set(&Redirection::desc, generate_description()),
+ gen::set(&Redirection::protocol, gen::element(kuu::Protocol::TCP, kuu::Protocol::UDP)),
+ gen::set(&Redirection::duration, gen::just(0)),
+ gen::set(&Redirection::internal_port),
+ gen::set(&Redirection::external_port),
+ gen::set(&Redirection::enabled, gen::just(true))
+ );
+ }
+ };
+} //namespace rc
+
+RC_GTEST_PROP(kuu, upnp, (const std::vector& parRedirections)) {
+ //for (auto& red : parRedirections) {
+ // std::cout << "internal_client: " << red.internal_client << '\n';
+ // std::cout << "remote_host: " << red.remote_host << '\n';
+ // std::cout << "desc: \"" << red.desc << "\" (" << red.desc.size() << ")\n";
+ // std::cout << "protocol: " << red.protocol << '\n';
+ // std::cout << "duration: " << red.duration << '\n';
+ // std::cout << "internal_port: " << red.internal_port << '\n';
+ // std::cout << "external_port: " << red.external_port << '\n';
+ // std::cout << "enabled: " << red.enabled << '\n';
+ // std::cout << "---------------------------------------------------------\n";
+ //}
+}