* @brief Tests for Nether service
*/
-#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <condition_variable>
#include <string>
+#include <thread>
+#include <vector>
-#include <dpl/log/log.h>
-#include <dpl/test/test_runner.h>
-#include <sm_api.h>
-#include <sm_request.h>
+#include <scoped_installer.h>
+#include <sm_commons.h>
#include <tests_common.h>
+#include <dpl/test/safe_cleanup.h>
-#define NETHER_NNS_NAME "nether_test_network_ns"
using namespace SecurityManagerTest;
+using namespace DPL::Test;
-namespace {
+const std::string INTERNET_ACCESS_PRIVILEGE = "http://tizen.org/privilege/internet";
+const std::string NETHER_NETNS_NAME_NONE = "";
+const std::string NETHER_NETNS_NAME_TEST = "nether_test_network_ns";
+const std::string NETNS_RUN_DIR = "/var/run/netns"; // taken from iproute2
+const std::string NETHER_NETNS_SETUP_COMMAND = "/usr/bin/setup-nether-tests-nns.sh " + NETHER_NETNS_NAME_TEST;
+const std::string NETHER_NETNS_TEARDOWN_COMMAND = "/usr/bin/teardown-nether-tests-nns.sh " + NETHER_NETNS_NAME_TEST;
-const std::string NETHER_NNS_SETUP_COMMAND = "/usr/bin/setup-nether-tests-nns.sh"
- " " NETHER_NNS_NAME;
-const std::string NETHER_NNS_TEARDOWN_COMMAND = "/usr/bin/teardown-nether-tests-nns.sh"
- " " NETHER_NNS_NAME;
+const ssize_t NET_BUFFER_SIZE = 1024;
+const int UDP_MESSAGES_COUNT = 20000;
+const int TCP_MESSAGES_COUNT = 20000;
+
+const uint16_t UDP_TEST_PORT = 12000;
+const uint16_t TCP_TEST_PORT = 12000;
+
+const std::string REMOTE_TEST_SERVER_ADDRESS = "10.1.0.2";
+const std::string LOCAL_TEST_SERVER_ADDRESS = "127.0.0.1";
+const std::string DNS_TEST_ADDRESS = "www.samsung.com";
+
+const int MONITOR_TIMEOUT = 1000; // ms
+
+enum class NetherInternetAccess {
+ ACCESS_GRANTED,
+ ACCESS_DENIED
+};
-const std::string INTERNET_ACCESS_PRIVILEGE = "http://tizen.org/privilege/internet";
-class ScopedSystemRunner final {
+void runShellScriptInChildAndWait(const std::string &command)
+{
+ RUNNER_ASSERT_MSG(system(command.c_str()) != -1, "Couldn't run command: " << command);
+}
+
+
+class ScopedShellScriptRunner final {
public:
- ScopedSystemRunner(const std::string &setupCmd, const std::string &teardownCmd)
+ ScopedShellScriptRunner(const std::string &setupCmd, const std::string &teardownCmd)
: m_teardownCmd(teardownCmd)
{
- RUNNER_ASSERT_MSG(system(setupCmd.c_str()) != -1,
- "Couldn't run " << setupCmd << " command");
+ runShellScriptInChildAndWait(setupCmd);
}
- ~ScopedSystemRunner()
+ ~ScopedShellScriptRunner()
{
- RUNNER_ASSERT_MSG(system(m_teardownCmd.c_str()) != -1,
- "Couldn't run " << m_teardownCmd << " command");
+ SafeCleanup::run([this]() {
+ runShellScriptInChildAndWait(m_teardownCmd);
+ });
}
- ScopedSystemRunner(const ScopedSystemRunner &) = delete;
- ScopedSystemRunner &operator=(const ScopedSystemRunner &) = delete;
+ ScopedShellScriptRunner(const ScopedShellScriptRunner &) = delete;
+ ScopedShellScriptRunner &operator=(const ScopedShellScriptRunner &) = delete;
private:
std::string m_teardownCmd;
};
-} // namespace
-RUNNER_TEST_GROUP_INIT(NETHER)
+void createChildProcess(const std::function<int(int)> &procedure, pid_t &childPid, int &childPipeFd)
+{
+ int pipeFd[2];
+ RUNNER_ASSERT_ERRNO_MSG(pipe2(pipeFd, O_DIRECT) == 0, "pipe() failed");
+
+ pid_t pid = fork();
+ RUNNER_ASSERT_ERRNO_MSG(pid != -1, "fork() failed");
+
+ if (pid != 0) {
+ // parent code
+ TEMP_FAILURE_RETRY(close(pipeFd[1]));
+
+ childPipeFd = pipeFd[0];
+ childPid = pid;
+ } else {
+ // child code
+ TestRunnerSingleton::Instance().Terminate();
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ close(pipeFd[0]);
+
+ int retStatus = EXIT_FAILURE;
+
+ int devNullFd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY));
+ if (devNullFd == -1) {
+ goto end;
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(devNullFd, STDIN_FILENO)) == -1) {
+ goto end;
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(devNullFd, STDOUT_FILENO)) == -1) {
+ goto end;
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(devNullFd, STDERR_FILENO)) == -1) {
+ goto end;
+ }
+
+ retStatus = procedure(pipeFd[1]);
+end:
+ exit(retStatus);
+ }
+}
+
+
+int switchToNetworkNamespace(const std::string &netnsName)
+{
+ if (netnsName == NETHER_NETNS_NAME_NONE) {
+ return 0;
+ }
+
+ const std::string netnsPath { NETNS_RUN_DIR + "/" + netnsName };
+ if (netnsPath.length() >= PATH_MAX) {
+ return -1;
+ }
+
+ int netNsFd = open(netnsPath.c_str(), O_RDONLY);
+ if (netNsFd == -1) {
+ return -1;
+ }
+
+ // uses an existing network name-space (previously created by "ip netns" command)
+ if (setns(netNsFd, CLONE_NEWNET) == -1) {
+ close(netNsFd);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+class TemporaryNormalTestUser
+{
+public:
+ TemporaryNormalTestUser(const std::string &userName)
+ : m_user(userName, GUM_USERTYPE_NORMAL, false)
+ {
+ m_user.create();
+ }
+
+ uid_t getUid() const
+ {
+ return m_user.getUid();
+ }
+
+private:
+ TemporaryTestUser m_user;
+};
+
+
+class InternetLocalAppInstallHelper : public AppInstallHelper
+{
+public:
+ InternetLocalAppInstallHelper(const std::string &namePrefix, uid_t uid, NetherInternetAccess access)
+ : AppInstallHelper(namePrefix, uid)
+ {
+ setInstallType(app_install_type::SM_APP_INSTALL_LOCAL);
+ if (access == NetherInternetAccess::ACCESS_GRANTED) {
+ addPrivilege(INTERNET_ACCESS_PRIVILEGE);
+ }
+ }
+};
+
+
+class AppContext
+{
+public:
+ AppContext()
+ : m_appId{}
+ , m_appUID(-1)
+ , m_appGID(-1)
+ {
+ }
+
+ AppContext(const std::string &appId, uid_t appUID, gid_t appGID)
+ : m_appId(appId)
+ , m_appUID(appUID)
+ , m_appGID(appGID)
+ {
+ }
+
+ const std::string &appId() const
+ {
+ return m_appId;
+ }
+
+ void setAppId(const std::string &appId)
+ {
+ m_appId = appId;
+ }
+
+ uid_t getUID() const
+ {
+ return m_appUID;
+ }
+
+ void setUID(uid_t appUID)
+ {
+ m_appUID = appUID;
+ }
+
+ gid_t getGID() const
+ {
+ return m_appGID;
+ }
+
+ void setGID(gid_t appGID)
+ {
+ m_appGID = appGID;
+ }
+
+private:
+ std::string m_appId;
+ uid_t m_appUID;
+ gid_t m_appGID;
+};
+
+
+class ScopedAppContext : public AppContext
+{
+public:
+ ScopedAppContext(const std::string &appPrefix, NetherInternetAccess access)
+ : m_user(appPrefix + "_user")
+ , m_installHelper(appPrefix + "_app_normal", m_user.getUid(), access)
+ , m_scopedInstaller(m_installHelper)
+ {
+ setAppId(m_installHelper.getAppId());
+ setUID(m_installHelper.getUID());
+ setGID(m_installHelper.getGID());
+ }
+
+private:
+ TemporaryNormalTestUser m_user;
+ InternetLocalAppInstallHelper m_installHelper;
+ ScopedInstaller m_scopedInstaller;
+};
+
+
+void runProcedureInNetAppContext(
+ const std::string &appPrefix,
+ const std::function<void(void)> &procedure,
+ const NetherInternetAccess access = NetherInternetAccess::ACCESS_DENIED)
+{
+ ScopedAppContext scopedAppContext(appPrefix, access);
+
+ auto smackLabel = scopedAppContext.appId();
+ auto appUID = scopedAppContext.getUID();
+ auto appGID = scopedAppContext.getGID();
+
+ // run client procedure in app context
+ runInChildParentWait([=]() {
+ Api::setProcessLabel(smackLabel);
+ RUNNER_ASSERT_ERRNO_MSG(drop_root_privileges(appUID, appGID) == 0,
+ "drop_root_privileges() failed");
+ procedure();
+ });
+}
+
+
+class NetServer
+{
+public:
+ NetServer()
+ : m_monitorIsRunning(false)
+ , m_receivedBytes(0ull)
+ , m_serverPid(-1)
+ , m_serverPipeFd(-1)
+ {}
+
+ NetServer(const NetServer &) = delete;
+ NetServer &operator=(const NetServer &) = delete;
+
+ virtual ~NetServer()
+ {
+ SafeCleanup::run([this]() {
+ stop();
+ });
+ }
+
+ void start(const std::string &netnsName = NETHER_NETNS_NAME_NONE)
+ {
+ std::unique_lock<std::mutex> lock(m_monitorMutex);
+
+ RUNNER_ASSERT_MSG(!m_monitorIsRunning, "Monitor thread is already running");
+
+ createChildProcess([=] (int pipeFd) -> int {
+ if (netnsName != NETHER_NETNS_NAME_NONE) {
+ if (switchToNetworkNamespace(netnsName) == -1) {
+ exit(EXIT_FAILURE);
+ }
+ }
+ return serverProcedure(pipeFd);
+ }, m_serverPid, m_serverPipeFd);
+
+ waitForServer();
+
+ m_monitorThread = std::move(std::thread(&NetServer::monitorThreadProc, this));
+
+ m_monitorSyncPoint.wait(lock, [this]() {
+ return m_monitorIsRunning;
+ });
+ }
+
+ void stop()
+ {
+ stopMonitor();
+ closeServerPipe();
+ stopServerProcess();
+ }
+
+ uint64_t getReceivedBytes() const
+ {
+ RUNNER_ASSERT_MSG(!isAlive(), "Cannot read statistics. Server is still running: " << (*this));
+ return m_receivedBytes;
+ }
+
+ bool isAlive() const
+ {
+ int status;
+ return waitpid(m_serverPid, &status, WNOHANG) == 0;
+ }
+
+ virtual int serverProcedure(int) = 0;
+
+ virtual std::string getDescription() const = 0;
+
+protected:
+ void notifyManager(int pipeFd)
+ {
+ char c = 'X';
+ ssize_t ret = TEMP_FAILURE_RETRY(write(pipeFd, &c, sizeof(c)));
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+ }
+
+private:
+ void waitForServer() const
+ {
+ pollfd pfd { m_serverPipeFd, POLLIN, 0 };
+
+ int ret = TEMP_FAILURE_RETRY(poll(&pfd, 1, MONITOR_TIMEOUT));
+ RUNNER_ASSERT_ERRNO_MSG(ret >= 0, "poll() failed");
+
+ if (pfd.revents & POLLIN) {
+ char c;
+ ssize_t len = TEMP_FAILURE_RETRY(read(m_serverPipeFd, &c, sizeof(c)));
+ RUNNER_ASSERT_ERRNO_MSG(len >= 0, "Read pipe failed");
+ }
+ }
+
+ ssize_t collectStatistics()
+ {
+ char buffer[NET_BUFFER_SIZE];
+
+ ssize_t ret = TEMP_FAILURE_RETRY(read(m_serverPipeFd, buffer, sizeof(buffer)));
+ if (ret > 0) {
+ m_receivedBytes += static_cast<uint64_t>(ret);
+ }
+
+ return ret;
+ }
+
+ void closeServerPipe()
+ {
+ if (m_serverPipeFd == -1) {
+ return;
+ }
+
+ close(m_serverPipeFd);
+ m_serverPipeFd = -1;
+ }
+
+ void stopServerProcess()
+ {
+ if (m_serverPid == -1) {
+ return;
+ }
+
+ if (!isAlive()) {
+ m_serverPid = -1;
+ return;
+ }
+
+ RUNNER_ASSERT_ERRNO_MSG(kill(m_serverPid, SIGTERM) == 0, "kill() failed");
+
+ int status;
+ do {
+ pid_t ret = TEMP_FAILURE_RETRY(waitpid(m_serverPid, &status, WUNTRACED | WCONTINUED));
+ RUNNER_ASSERT_ERRNO_MSG(ret != -1, "waitpid() failed");
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+
+ m_serverPid = -1;
+ }
+
+ void stopMonitor()
+ {
+ {
+ std::lock_guard<std::mutex> lock(m_monitorMutex);
+ m_monitorIsRunning = false;
+ }
+
+ if (m_monitorThread.joinable()) {
+ m_monitorThread.join();
+ }
+ }
+
+ void monitorThreadProc()
+ {
+ std::unique_lock<std::mutex> lock(m_monitorMutex);
+ m_monitorIsRunning = true;
+ lock.unlock();
+
+ m_monitorSyncPoint.notify_one();
+
+ lock.lock();
+
+ pollfd pollFd{ m_serverPipeFd, POLLIN, 0 };
+
+ while (m_monitorIsRunning) {
+
+ lock.unlock();
+
+ int ret = TEMP_FAILURE_RETRY(poll(&pollFd, 1, MONITOR_TIMEOUT));
+ if (ret == -1) {
+ lock.lock();
+ break;
+ }
+
+ if (ret > 0 && (pollFd.revents & POLLIN)) {
+ if (collectStatistics() == -1) {
+ lock.lock();
+ break;
+ }
+ }
+
+ lock.lock();
+ }
+
+ m_monitorIsRunning = false;
+ lock.unlock();
+ }
+
+ // monitor variables
+ std::thread m_monitorThread;
+ bool m_monitorIsRunning;
+ std::mutex m_monitorMutex;
+ std::condition_variable m_monitorSyncPoint;
+
+ // server variables
+ uint64_t m_receivedBytes;
+ pid_t m_serverPid;
+ int m_serverPipeFd;
+
+ friend std::ostream &operator<<(std::ostream &os, const NetServer &);
+};
+
+
+std::ostream &operator<<(std::ostream &os, const NetServer &server)
+{
+ os << server.getDescription();
+ return os;
+}
+
+
+class UDPServer : public NetServer
+{
+public:
+ UDPServer(uint16_t port, int protocol = IPPROTO_UDP)
+ : NetServer{}
+ , m_port(port)
+ , m_protocol(protocol)
+ {}
+
+ virtual ~UDPServer() {}
+
+ virtual std::string getDescription() const override
+ {
+ return "UDPServer port: " + std::to_string(m_port);
+ }
+
+ virtual int serverProcedure(int pipeFd) override
+ {
+ FdUniquePtr pipePtr(&pipeFd);
+
+ int sockFd = socket(AF_INET, SOCK_DGRAM, m_protocol);
+ if (sockFd == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ SockUniquePtr sockPtr(&sockFd);
+
+ int optionValue = 1;
+ if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, static_cast<const void *>(&optionValue) , sizeof(int)) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_in serverAddress;
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ serverAddress.sin_family = AF_INET;
+ serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
+ serverAddress.sin_port = htons(m_port);
+
+ if (bind(sockFd, reinterpret_cast<struct sockaddr *>(&serverAddress), sizeof(serverAddress)) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_in clntAddress;
+ size_t clntAddressLength = sizeof(clntAddress);
+ char receiveBuffer[NET_BUFFER_SIZE];
+
+ notifyManager(pipeFd);
+
+ while (true) {
+ ssize_t len = TEMP_FAILURE_RETRY(recvfrom(sockFd, receiveBuffer, sizeof(receiveBuffer), 0,
+ reinterpret_cast<struct sockaddr *>(&clntAddress), static_cast<socklen_t *>(&clntAddressLength)));
+
+ if (len == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (len == 0) {
+ continue;
+ }
+
+ do {
+ ssize_t pos = 0;
+ ssize_t ret = TEMP_FAILURE_RETRY(write(pipeFd, &receiveBuffer[pos], len));
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ len -= ret;
+ pos += ret;
+ } while (len != 0);
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+private:
+ uint16_t m_port;
+ int m_protocol;
+};
+
+
+class UDPServerApp : public UDPServer
+{
+public:
+ UDPServerApp(uint16_t port, int protocol, const AppContext &appContext)
+ : UDPServer(port, protocol)
+ , m_appContext(appContext)
+ {
+ }
+
+ virtual int serverProcedure(int pipeFd) override
+ {
+ Api::setProcessLabel(m_appContext.appId());
+ RUNNER_ASSERT_ERRNO_MSG(drop_root_privileges(m_appContext.getUID(), m_appContext.getGID()) == 0,
+ "drop_root_privileges() failed");
+
+ return UDPServer::serverProcedure(pipeFd);
+ }
+
+private:
+ AppContext m_appContext;
+};
+
+class TCPServer : public NetServer
+{
+public:
+ explicit TCPServer(uint16_t port)
+ : NetServer{}
+ , m_port(port)
+ {}
+
+ virtual ~TCPServer() {}
+
+ virtual std::string getDescription() const override
+ {
+ return "TCPServer port: " + std::to_string(m_port);
+ }
+
+ virtual int serverProcedure(int pipeFd) override
+ {
+ FdUniquePtr pipePtr(&pipeFd);
+
+ int sockFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sockFd == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ SockUniquePtr sockPtr(&sockFd);
+
+ int optionValue = 1;
+ if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, static_cast<const void *>(&optionValue) , sizeof(int)) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_in serverAddress;
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ serverAddress.sin_family = AF_INET;
+ serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
+ serverAddress.sin_port = htons(m_port);
+
+ if (bind(sockFd, reinterpret_cast<struct sockaddr *>(&serverAddress), sizeof(serverAddress)) == -1) {
+ exit(EXIT_FAILURE);
+ }
-RUNNER_CHILD_TEST(nether_01_check_app_install_uninstall)
+ if (listen(sockFd, 1) == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ struct sockaddr_in clntAddress;
+ size_t clntAddressLength = sizeof(clntAddress);
+ char receiveBuffer[NET_BUFFER_SIZE];
+
+ notifyManager(pipeFd);
+
+ while (true) {
+ int acceptedSocketFd = TEMP_FAILURE_RETRY(accept(sockFd, reinterpret_cast<struct sockaddr *>(&clntAddress),
+ static_cast<socklen_t *>(&clntAddressLength)));
+ if (acceptedSocketFd == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ SockUniquePtr acceptSockPtr(&acceptedSocketFd);
+
+ while (true) {
+ ssize_t len = TEMP_FAILURE_RETRY(recv(acceptedSocketFd, receiveBuffer, sizeof(receiveBuffer), 0));
+ if (len == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ if (len == 0) {
+ break;
+ }
+
+ do {
+ ssize_t pos = 0;
+ ssize_t ret = TEMP_FAILURE_RETRY(write(pipeFd, &receiveBuffer[pos], len));
+ if (ret == -1) {
+ exit(EXIT_FAILURE);
+ }
+
+ len -= ret;
+ pos += ret;
+ } while (len != 0);
+ }
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+private:
+ uint16_t m_port;
+};
+
+
+class TCPServerApp : public TCPServer
{
- const std::string nether_app_id = "nether_test_01_app";
- const std::string nether_pkg_id = "nether_test_01_pkg";
+public:
+ TCPServerApp(int port, const AppContext &appContext)
+ : TCPServer(port)
+ , m_appContext(appContext)
+ {
+ }
- ScopedSystemRunner networkNSRunner(NETHER_NNS_SETUP_COMMAND,
- NETHER_NNS_TEARDOWN_COMMAND);
+ virtual int serverProcedure(int pipeFd) override
+ {
+ Api::setProcessLabel(m_appContext.appId());
+ RUNNER_ASSERT_ERRNO_MSG(drop_root_privileges(m_appContext.getUID(), m_appContext.getGID()) == 0,
+ "drop_root_privileges() failed");
- InstallRequest requestInst;
- requestInst.setAppId(nether_app_id);
- requestInst.setPkgId(nether_pkg_id);
- requestInst.addPrivilege(INTERNET_ACCESS_PRIVILEGE);
+ return TCPServer::serverProcedure(pipeFd);
+ }
- Api::install(requestInst);
+private:
+ AppContext m_appContext;
+};
- // TODO test code goes here
- InstallRequest requestUninst;
- requestUninst.setAppId(nether_app_id);
+class NetClient
+{
+public:
+ NetClient(const std::string &appPrefix, NetherInternetAccess access, int msgCount)
+ : m_appPrefix(appPrefix)
+ , m_access(access)
+ , m_msgCount(msgCount)
+ {
+ }
- Api::uninstall(requestUninst);
+ NetClient(const NetClient &) = delete;
+ NetClient &operator=(const NetClient &) = delete;
+
+ void start()
+ {
+ runProcedureInNetAppContext(m_appPrefix, [this]() { clientProcedure(); } , m_access);
+ }
+
+ virtual void clientProcedure() = 0;
+
+protected:
+ std::string m_appPrefix;
+ NetherInternetAccess m_access;
+ int m_msgCount;
+};
+
+class UDPClientApp : public NetClient
+{
+public:
+ UDPClientApp(const std::string &appPrefix, NetherInternetAccess access, int msgCount,
+ int protocol, const std::string &hostName, uint16_t port)
+ : NetClient(appPrefix, access, msgCount)
+ , m_protocol(protocol)
+ , m_hostName(hostName)
+ , m_port(port)
+ {
+ }
+
+private:
+ virtual void clientProcedure() override
+ {
+ int sockFd = socket(AF_INET, SOCK_DGRAM, m_protocol);
+ RUNNER_ASSERT_ERRNO_MSG(sockFd >= 0, "socket() failed");
+ SockUniquePtr sockPtr(&sockFd);
+
+ struct hostent *server = gethostbyname(m_hostName.c_str());
+ RUNNER_ASSERT_MSG(server != nullptr, "Couldn't find host " << m_hostName
+ << " h_errno = " << hstrerror(h_errno));
+
+ struct sockaddr_in serverAddress;
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ serverAddress.sin_family = AF_INET;
+ memcpy(&serverAddress.sin_addr.s_addr, server->h_addr, server->h_length);
+ serverAddress.sin_port = htons(m_port);
+
+ char sendBuffer[NET_BUFFER_SIZE];
+
+ memset(sendBuffer, 'X', sizeof(sendBuffer));
+ size_t serverAddressLength = sizeof(serverAddress);
+
+ int msgCount = m_msgCount;
+ while (msgCount-- > 0) {
+ ssize_t len = sizeof(sendBuffer);
+ ssize_t pos = 0;
+ do {
+ ssize_t ret = TEMP_FAILURE_RETRY(sendto(sockFd, &sendBuffer[pos], len, 0,
+ reinterpret_cast<struct sockaddr *>(&serverAddress), serverAddressLength));
+ RUNNER_ASSERT_ERRNO_MSG(ret >= 0, "sendto() failed");
+ len -= ret;
+ pos += ret;
+ } while (len != 0);
+ }
+ }
+
+ int m_protocol;
+ std::string m_hostName;
+ uint16_t m_port;
+};
+
+
+
+class TCPClientApp : public NetClient
+{
+public:
+ TCPClientApp(const std::string &appPrefix, NetherInternetAccess access, int msgCount,
+ const std::string &hostName, int port, bool expectConnectionError)
+ : NetClient(appPrefix, access, msgCount)
+ , m_hostName(hostName)
+ , m_port(port)
+ , m_expectConnectionError(expectConnectionError)
+ {
+ }
+
+private:
+ virtual void clientProcedure() override
+ {
+ int sockFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ RUNNER_ASSERT_ERRNO_MSG(sockFd >= 0, "socket() failed");
+ SockUniquePtr sockPtr(&sockFd);
+
+ struct hostent *server = gethostbyname(m_hostName.c_str());
+ RUNNER_ASSERT_MSG(server != nullptr, "Couldn't find host " << m_hostName
+ << " h_errno = " << hstrerror(h_errno));
+
+ struct sockaddr_in serverAddress;
+ memset(&serverAddress, 0, sizeof(serverAddress));
+ serverAddress.sin_family = AF_INET;
+ memcpy(&serverAddress.sin_addr.s_addr, server->h_addr, server->h_length);
+ serverAddress.sin_port = htons(m_port);
+ size_t serverAddressLength = sizeof(serverAddress);
+
+ int ret = TEMP_FAILURE_RETRY(connect(sockFd, reinterpret_cast<struct sockaddr *>(&serverAddress), serverAddressLength));
+ if (ret == -1 && m_expectConnectionError) {
+ return;
+ }
+
+ RUNNER_ASSERT_ERRNO_MSG(ret == 0, "Couldn't connect to " << m_hostName);
+
+ char sendBuffer[NET_BUFFER_SIZE];
+ memset(sendBuffer, 'X', sizeof(sendBuffer));
+
+ int msgCount = m_msgCount;
+ while (msgCount-- > 0) {
+ ssize_t len = sizeof(sendBuffer);
+ ssize_t pos = 0;
+ do {
+ ssize_t ret = TEMP_FAILURE_RETRY(send(sockFd, &sendBuffer[pos], len, 0));
+ RUNNER_ASSERT_ERRNO_MSG(ret >= 0, "send() failed");
+ len -= ret;
+ pos += ret;
+ } while (len != 0);
+ }
+ }
+
+ int m_protocol;
+ std::string m_hostName;
+ uint16_t m_port;
+ bool m_expectConnectionError;
+};
+
+
+RUNNER_TEST_GROUP_INIT(NETHER_REMOTE_CONNECTION)
+
+
+RUNNER_CHILD_TEST(nether_check_udp_connection_internet_access_granted)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of UDP traffic is finished.");
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPServer udpServer(UDP_TEST_PORT);
+ udpServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPClientApp udpClientApp("nether_test_01", NetherInternetAccess::ACCESS_GRANTED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, REMOTE_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpServer.isAlive(), "UDP server was not running");
+ udpServer.stop();
+ RUNNER_ASSERT_MSG(udpServer.getReceivedBytes() > 0, "UDP server didn't receive any data");
}
+
+RUNNER_CHILD_TEST(nether_check_udp_connection_internet_access_denied)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of UDP traffic is finished.");
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPServer udpServer(UDP_TEST_PORT);
+ udpServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPClientApp udpClientApp("nether_test_02", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, REMOTE_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpServer.isAlive(), "UDP server was not running");
+ udpServer.stop();
+ RUNNER_ASSERT_MSG(udpServer.getReceivedBytes() == 0, "UDP server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_connection_internet_access_granted)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of UDP traffic is finished.");
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDPLITE);
+ udpLiteServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPClientApp udpClientApp("nether_test_03", NetherInternetAccess::ACCESS_GRANTED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, REMOTE_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDPLite server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() > 0, "UDPLite server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_connection_internet_access_denied)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of UDP traffic is finished.");
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDPLITE);
+ udpLiteServer.start(NETHER_NETNS_NAME_TEST);
+
+ UDPClientApp udpClientApp("nether_test_04", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, REMOTE_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDP server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() == 0, "UDP server received some data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_connection_internet_access_granted)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ TCPServer tcpServer(TCP_TEST_PORT);
+ tcpServer.start(NETHER_NETNS_NAME_TEST);
+
+ TCPClientApp tcpClientApp("nether_test_05", NetherInternetAccess::ACCESS_GRANTED, TCP_MESSAGES_COUNT,
+ REMOTE_TEST_SERVER_ADDRESS, TCP_TEST_PORT, false);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServer.isAlive(), "TCP server was not running");
+ tcpServer.stop();
+ RUNNER_ASSERT_MSG(tcpServer.getReceivedBytes() > 0, "TCP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_connection_internet_access_denied)
+{
+ ScopedShellScriptRunner networkNSRunner(NETHER_NETNS_SETUP_COMMAND,
+ NETHER_NETNS_TEARDOWN_COMMAND);
+
+ TCPServer tcpServer(TCP_TEST_PORT);
+ tcpServer.start(NETHER_NETNS_NAME_TEST);
+
+ TCPClientApp tcpClientApp("nether_test_06", NetherInternetAccess::ACCESS_DENIED, TCP_MESSAGES_COUNT,
+ REMOTE_TEST_SERVER_ADDRESS, TCP_TEST_PORT, true);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServer.isAlive(), "TCP server was not running");
+ tcpServer.stop();
+ RUNNER_ASSERT_MSG(tcpServer.getReceivedBytes() == 0, "TCP server received some data");
+}
+
+
+RUNNER_TEST_GROUP_INIT(NETHER_LOCAL_SERVICE_CONNECTION)
+
+
+RUNNER_CHILD_TEST(nether_check_udp_local_connection_internet_access_denied)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of UDP traffic is finished.");
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDP);
+ udpLiteServer.start();
+
+ UDPClientApp udpClientApp("nether_test_07", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, LOCAL_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDP server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() > 0, "UDP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_local_connection_internet_access_denied)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of UDP traffic is finished.");
+ UDPServer udpLiteServer(UDP_TEST_PORT, IPPROTO_UDPLITE);
+ udpLiteServer.start();
+
+ UDPClientApp udpClientApp("nether_test_08", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, LOCAL_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServer.isAlive(), "UDP server was not running");
+ udpLiteServer.stop();
+ RUNNER_ASSERT_MSG(udpLiteServer.getReceivedBytes() > 0, "UDP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_local_connection_internet_access_denied)
+{
+ TCPServer tcpServer(TCP_TEST_PORT);
+ tcpServer.start();
+
+ TCPClientApp tcpClientApp("nether_test_09", NetherInternetAccess::ACCESS_DENIED, TCP_MESSAGES_COUNT,
+ LOCAL_TEST_SERVER_ADDRESS, TCP_TEST_PORT, false);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServer.isAlive(), "TCP server was not running");
+ tcpServer.stop();
+ RUNNER_ASSERT_MSG(tcpServer.getReceivedBytes() > 0, "TCP server didn't receive any data");
+}
+
+
+RUNNER_TEST_GROUP_INIT(NETHER_LOCAL_INTER_APP_CONNECTION)
+
+
+RUNNER_CHILD_TEST(nether_check_udp_local_inter_app_connection_internet_access_denied)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of UDP traffic is finished.");
+ ScopedAppContext appContext("nether_test_10a", NetherInternetAccess::ACCESS_DENIED);
+ UDPServerApp udpServerApp(UDP_TEST_PORT, IPPROTO_UDP, appContext);
+ udpServerApp.start();
+
+ UDPClientApp udpClientApp("nether_test_10b", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDP, LOCAL_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpServerApp.isAlive(), "UDP server was not running");
+ udpServerApp.stop();
+ RUNNER_ASSERT_MSG(udpServerApp.getReceivedBytes() > 0, "UDP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_udp_lite_local_inter_app_connection_internet_access_denied)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of UDP traffic is finished.");
+ ScopedAppContext appContext("nether_test_11a", NetherInternetAccess::ACCESS_DENIED);
+ UDPServerApp udpLiteServerApp(UDP_TEST_PORT, IPPROTO_UDPLITE, appContext);
+ udpLiteServerApp.start();
+
+ UDPClientApp udpClientApp("nether_test_11b", NetherInternetAccess::ACCESS_DENIED, UDP_MESSAGES_COUNT,
+ IPPROTO_UDPLITE, LOCAL_TEST_SERVER_ADDRESS, UDP_TEST_PORT);
+ udpClientApp.start();
+
+ RUNNER_ASSERT_MSG(udpLiteServerApp.isAlive(), "UDP server was not running");
+ udpLiteServerApp.stop();
+ RUNNER_ASSERT_MSG(udpLiteServerApp.getReceivedBytes() > 0, "UDP server didn't receive any data");
+}
+
+
+RUNNER_CHILD_TEST(nether_check_tcp_local_inter_app_connection_internet_access_denied)
+{
+ ScopedAppContext appContext("nether_test_12a", NetherInternetAccess::ACCESS_DENIED);
+ TCPServerApp tcpServerApp(TCP_TEST_PORT, appContext);
+ tcpServerApp.start();
+
+ TCPClientApp tcpClientApp("nether_test_12b", NetherInternetAccess::ACCESS_DENIED, TCP_MESSAGES_COUNT,
+ LOCAL_TEST_SERVER_ADDRESS, TCP_TEST_PORT, false);
+ tcpClientApp.start();
+
+ RUNNER_ASSERT_MSG(tcpServerApp.isAlive(), "TCP server was not running");
+ tcpServerApp.stop();
+ RUNNER_ASSERT_MSG(tcpServerApp.getReceivedBytes() > 0, "TCP server didn't receive any data");
+}
+
+
+RUNNER_TEST_GROUP_INIT(NETHER_LOCAL_DNS_CONNECTION)
+
+
+RUNNER_CHILD_TEST(nether_check_gethostbyname_internet_access_granted)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of DNS queries is finished.");
+ const auto getHostAddress = [](void) {
+ struct hostent *server = gethostbyname(DNS_TEST_ADDRESS.c_str());
+ RUNNER_ASSERT_MSG(server != nullptr, "Couldn't find host "
+ << DNS_TEST_ADDRESS << " h_errno = " << h_errno);
+ };
+
+ runProcedureInNetAppContext("nether_test_13", getHostAddress, NetherInternetAccess::ACCESS_GRANTED);
+}
+
+
+RUNNER_CHILD_TEST(nether_check_gethostbyname_internet_access_denied)
+{
+ RUNNER_IGNORED_MSG("Disabled until the implementation of handling of DNS queries is finished.");
+ const auto getHostAddress = [](void) {
+ struct hostent *server = gethostbyname(DNS_TEST_ADDRESS.c_str());
+ RUNNER_ASSERT_MSG(server == nullptr, "Host was found " << DNS_TEST_ADDRESS);
+ };
+
+ runProcedureInNetAppContext("nether_test_14", getHostAddress, NetherInternetAccess::ACCESS_DENIED);
+}
+
+
+void checkRawSocket(void)
+{
+ const std::vector<int> protocols {
+ IPPROTO_ICMP,
+ IPPROTO_IGMP
+ };
+
+ for (const auto protocol : protocols) {
+ int sockFd = socket(AF_INET, SOCK_RAW, protocol);
+ RUNNER_ASSERT_ERRNO_MSG(sockFd == -1, "RAW socket was successfully created for protocol " << protocol);
+ }
+};
+
+
+RUNNER_TEST_GROUP_INIT(NETHER_RAW_SOCKET)
+
+
+RUNNER_CHILD_TEST(nether_check_raw_socket_access_granted)
+{
+ runProcedureInNetAppContext("nether_test_15", checkRawSocket, NetherInternetAccess::ACCESS_GRANTED);
+}
+
+
+RUNNER_CHILD_TEST(nether_check_raw_socket_access_denied)
+{
+ runProcedureInNetAppContext("nether_test_16", checkRawSocket, NetherInternetAccess::ACCESS_DENIED);
+}
+
+
int main(int argc, char *argv[])
{
return DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv);