nat-helper utility

This commit is contained in:
Martin Piatka
2021-08-16 13:46:12 +02:00
parent 63325461b0
commit d958df8be8
11 changed files with 972 additions and 0 deletions

38
nat-helper/CMakeLists.txt Normal file
View File

@@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 3.14)
project(nat-helper
LANGUAGES CXX
VERSION 0.0.1
)
set(SOURCES
main.cpp
nat-helper.cpp
client.cpp
message.cpp
room.cpp
)
add_executable(nat-helper main.cpp ${SOURCES})
set_property(TARGET nat-helper PROPERTY CXX_STANDARD 17)
if (NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
include(CheckPIESupported)
check_pie_supported()
set_target_properties(nat-helper PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
target_compile_definitions(nat-helper PRIVATE FORTIFY_SOURCE=2)
target_compile_options(nat-helper PRIVATE -fstack-protector-strong)
target_compile_options(nat-helper PRIVATE -Wall -Wextra -pedantic)
target_link_options(nat-helper PRIVATE "SHELL:-z relro")
target_link_options(nat-helper PRIVATE "SHELL:-z now")
endif()
target_link_libraries(nat-helper PRIVATE
pthread
)

42
nat-helper/README.md Normal file
View File

@@ -0,0 +1,42 @@
# Hole punching coordinator for UltraGrid
This utility serves as a meeting point for UltraGrid clients, that need to
connect to each other, but don't have publicly routable IP addresses.
Building
---------
mkdir build && cd build
cmake ..
make
Usage
---------
./nat-helper [-h/--help] [-p/--port <port>]
If no port is specified, 12558 is used.
Protocol description
---------
Clients connect to the nat-helper server, identify themselves with a name, and
join a room. Once two clients enter the same room, nat-helper forwards name,
sdp description string, and all candidate address pairs between the two
clients.
All communication is done via messages that have the following structure:
<HEADER><MSG_BODY>
`HEADER`: 5B string containing length of MSG_BODY, null-termination optional
`MSG_BODY`: content of message, length determined by header, max 2048B
After establishing connection to the nat-helper server, following messages are
sent and received in that order:
1. Client sends its name
2. Client sends room name to join
3. Client sends its sdp description
4. Client receives the name of the other client in the room
5. Client receives the sdp description of the other client
After that the client sends and receives sdp candidate pairs as they are
discovered.

140
nat-helper/client.cpp Normal file
View File

@@ -0,0 +1,140 @@
/**
* @file client.cpp
* @author Martin Piatka <piatka@cesnet.cz>
*/
/*
* Copyright (c) 2021 CESNET, z. s. p. o.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is 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.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
*/
#include "client.hpp"
Client::Client(asio::ip::tcp::socket&& socket) : socket(std::move(socket)) { }
void Client::readDescription(std::function<void(Client&, bool)> onComplete){
using namespace std::placeholders;
inMsg.async_readMsg(socket,
std::bind(&Client::readNameComplete,
shared_from_this(), std::ref(socket), onComplete, _1));
}
void Client::readNameComplete(asio::ip::tcp::socket& socket,
std::function<void(Client&, bool)> onComplete,
bool success)
{
if(!success){
onComplete(*this, false);
return;
}
clientName = std::string(inMsg.getStr());
using namespace std::placeholders;
inMsg.async_readMsg(socket,
std::bind(&Client::readRoomComplete,
shared_from_this(), std::ref(socket), onComplete, _1));
}
void Client::readRoomComplete(asio::ip::tcp::socket& socket,
std::function<void(Client&, bool)> onComplete,
bool success)
{
if(!success){
onComplete(*this, false);
return;
}
roomName = std::string(inMsg.getStr());
using namespace std::placeholders;
inMsg.async_readMsg(socket,
std::bind(&Client::readDescComplete, shared_from_this(), onComplete, _1));
}
void Client::readDescComplete(
std::function<void(Client&, bool)> onComplete,
bool success)
{
if(!success){
onComplete(*this, false);
return;
}
sdpDesc = std::string(inMsg.getStr());
onComplete(*this, true);
}
void Client::readCandidate(std::function<void(Client&, bool)> onComplete){
using namespace std::placeholders;
inMsg.async_readMsg(socket,
std::bind(&Client::readCandidateComplete,
shared_from_this(), onComplete, _1));
}
void Client::readCandidateComplete(
std::function<void(Client&, bool)> onComplete,
bool success)
{
if(!success){
onComplete(*this, false);
return;
}
candidates.emplace_back(inMsg.getStr());
onComplete(*this, true);
}
bool Client::isSendCallbackPending() const{
return !sendQueue.empty() || outMsg.isSendingNow();
}
void Client::sendMsg(std::string_view msg){
if(isSendCallbackPending()){
sendQueue.emplace(msg);
return;
}
outMsg.setStr(msg);
using namespace std::placeholders;
outMsg.async_sendMsg(socket, std::bind(&Client::onMsgSent, shared_from_this(), _1));
}
void Client::onMsgSent(bool success){
if(!success){
//TODO
}
if(sendQueue.empty())
return;
using namespace std::placeholders;
outMsg.setStr(sendQueue.front());
sendQueue.pop();
outMsg.async_sendMsg(socket, std::bind(&Client::onMsgSent, shared_from_this(), _1));
}

97
nat-helper/client.hpp Normal file
View File

@@ -0,0 +1,97 @@
/**
* @file client.hpp
* @author Martin Piatka <piatka@cesnet.cz>
*/
/*
* Copyright (c) 2021 CESNET, z. s. p. o.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is 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.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
*/
#ifndef UG_HOLE_PUNCH_CLIENT_HPP
#define UG_HOLE_PUNCH_CLIENT_HPP
#include <string>
#include <vector>
#include <queue>
#include <functional>
#include <memory>
#include <asio.hpp>
#include "message.hpp"
class Client : public std::enable_shared_from_this<Client>{
public:
Client(asio::ip::tcp::socket&& socket);
Client(const Client&) = delete;
Client(Client&&) = delete;
Client& operator=(const Client&) = delete;
Client& operator=(Client&&) = delete;
void readDescription(std::function<void(Client&, bool)> onComplete);
std::string getClientName() { return clientName; }
std::string getRoomName() { return roomName; }
std::string getSdpDesc() { return sdpDesc; }
void sendMsg(std::string_view msg);
void readCandidate(std::function<void(Client&, bool)> onComplete);
const std::vector<std::string>& getCandidates() { return candidates; }
bool isSendCallbackPending() const;
private:
void readNameComplete(asio::ip::tcp::socket& socket,
std::function<void(Client&, bool)> onComplete, bool success);
void readRoomComplete(asio::ip::tcp::socket& socket,
std::function<void(Client&, bool)> onComplete, bool success);
void readDescComplete(
std::function<void(Client&, bool)> onComplete, bool success);
void readCandidateComplete(
std::function<void(Client&, bool)> onComplete, bool success);
void onMsgSent(bool success);
std::string clientName;
std::string roomName;
std::string sdpDesc;
std::vector<std::string> candidates;
std::queue<std::string> sendQueue;
Message inMsg;
Message outMsg;
asio::ip::tcp::socket socket;
};
#endif

90
nat-helper/main.cpp Normal file
View File

@@ -0,0 +1,90 @@
/**
* @file main.cpp
* @author Martin Piatka <piatka@cesnet.cz>
*/
/*
* Copyright (c) 2021 CESNET, z. s. p. o.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is 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.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
*/
#include <iostream>
#include <charconv>
#include "nat-helper.hpp"
namespace {
void printUsage(std::string_view name){
std::cout << "Usage: "
<< name << " [-h/--help] [-p/--port <port>]\n";
}
}
int main(int argc, char **argv){
int port = 12558;
for(int i = 1; i < argc; i++){
std::string_view arg(argv[i]);
if(arg == "-h" || arg == "--help"){
printUsage(argv[0]);
return 0;
} else if(arg == "-p" || arg == "--port"){
if(i + 1 >= argc){
std::cerr << "Expected port number\n";
printUsage(argv[0]);
return 1;
}
std::string_view portStr(argv[++i]);
auto res = std::from_chars(portStr.data(), portStr.data() + portStr.size(), port);
if(res.ec != std::errc() || res.ptr == portStr.data()){
std::cerr << "Failed to parse port number\n";
printUsage(argv[0]);
return 1;
}
} else {
std::cerr << "Unknown argument " << arg << std::endl;
printUsage(argv[0]);
return 1;
}
}
NatHelper server(port);
server.run();
for(std::string in; std::getline(std::cin, in);){
if(in == "exit"){
break;
}
}
server.stop();
return 0;
}

120
nat-helper/message.cpp Normal file
View File

@@ -0,0 +1,120 @@
/**
* @file message.cpp
* @author Martin Piatka <piatka@cesnet.cz>
*/
/*
* Copyright (c) 2021 CESNET, z. s. p. o.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is 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.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
*/
#include <charconv>
#include <cassert>
#include <cstring>
#include "message.hpp"
std::string_view Message::getStr() const{
return std::string_view(data + headerSize, size);
}
void Message::async_readMsg(asio::ip::tcp::socket& socket,
std::function<void(bool)> onComplete)
{
using namespace std::placeholders;
asio::async_read(socket,
asio::buffer(data, headerSize),
std::bind(&Message::async_readBody,
this, std::ref(socket), onComplete, _1, _2));
}
void Message::setStr(std::string_view msg){
assert(!sendingNow);
size = std::min(bodySize, msg.size());
memset(data, ' ', headerSize);
std::to_chars(data, data + headerSize, size);
memcpy(data + headerSize, msg.data(), size);
}
void Message::async_sendMsg(asio::ip::tcp::socket& socket,
std::function<void(bool)> onComplete)
{
using namespace std::placeholders;
sendingNow = true;
asio::async_write(socket,
asio::buffer(data, headerSize + size),
std::bind(&Message::async_sendComplete,
this, onComplete, _1, _2));
}
void Message::async_readBody(asio::ip::tcp::socket& socket,
std::function<void(bool)> onComplete,
const std::error_code& ec, [[maybe_unused]] size_t readLen)
{
if(ec){
onComplete(false);
return;
}
assert(readLen == headerSize);
size_t expectedSize = 0;
auto res = std::from_chars(data, data + headerSize, expectedSize);
if(res.ec != std::errc()){
onComplete(false);
}
using namespace std::placeholders;
asio::async_read(socket,
asio::buffer(data + headerSize, expectedSize),
std::bind(&Message::async_readBodyComplete,
this, onComplete, _1, _2));
}
void Message::async_readBodyComplete(std::function<void(bool)> onComplete,
const std::error_code& ec, size_t readLen)
{
if(ec){
onComplete(false);
return;
}
size = readLen;
onComplete(true);
}
void Message::async_sendComplete(std::function<void(bool)> onComplete,
const std::error_code& ec, [[maybe_unused]] size_t sentLen)
{
sendingNow = false;
onComplete(!ec);
}

82
nat-helper/message.hpp Normal file
View File

@@ -0,0 +1,82 @@
/**
* @file message.hpp
* @author Martin Piatka <piatka@cesnet.cz>
*/
/*
* Copyright (c) 2021 CESNET, z. s. p. o.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is 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.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
*/
#ifndef UG_HOLE_PUNCH_MESSAGE_HPP
#define UG_HOLE_PUNCH_MESSAGE_HPP
#include <string_view>
#include <functional>
#include <asio.hpp>
class Message{
public:
Message() = default;
void async_readMsg(asio::ip::tcp::socket& socket,
std::function<void(bool)> onComplete);
std::string_view getStr() const;
void setStr(std::string_view msg);
void async_sendMsg(asio::ip::tcp::socket& socket,
std::function<void(bool)> onComplete);
size_t getSize() const { return size; }
bool empty() const { return size == 0; }
bool isSendingNow() const { return sendingNow; }
void clear();
private:
void async_readBody(asio::ip::tcp::socket& socket,
std::function<void(bool)> onComplete,
const std::error_code& ec, size_t readLen);
void async_readBodyComplete(std::function<void(bool)> onComplete,
const std::error_code& ec, size_t readLen);
void async_sendComplete(std::function<void(bool)> onComplete,
const std::error_code& ec, size_t sentLen);
size_t size = 0;
bool sendingNow = false;
static constexpr size_t headerSize = 5;
static constexpr size_t bodySize = 2048;
char data[headerSize + bodySize];
};
#endif

138
nat-helper/nat-helper.cpp Normal file
View File

@@ -0,0 +1,138 @@
/**
* @file nat-helper.cpp
* @author Martin Piatka <piatka@cesnet.cz>
*/
/*
* Copyright (c) 2021 CESNET, z. s. p. o.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is 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.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
*/
#include <iostream>
#include <functional>
#include "nat-helper.hpp"
NatHelper::NatHelper(int port) : port(port),
io_service(),
//work_guard(io_service.get_executor()),
acceptor(io_service),
pendingSocket(io_service)
{
}
NatHelper::NatHelper() : NatHelper(12558)
{
}
void NatHelper::worker(){
std::cout << "Running" << std::endl;
io_service.run();
std::cout << "End" << std::endl;
}
void NatHelper::run(){
using namespace std::placeholders;
asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port);
acceptor.open(endpoint.protocol());
acceptor.bind(endpoint);
acceptor.listen(asio::socket_base::max_connections);
acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(false));
acceptor.async_accept(pendingSocket,
std::bind(&NatHelper::onConnectionAccepted, this, _1));
std::cout << "Starting thread" << std::endl;
worker_thread = std::thread(&NatHelper::worker, this);
}
void NatHelper::stop(){
std::cout << "Stopping..." << std::endl;
io_service.stop();
worker_thread.join();
}
void NatHelper::onConnectionAccepted(const std::error_code& ec){
if(ec){
std::cerr << "Error accepting client connection: " << ec.message() << std::endl;
return;
}
using namespace std::placeholders;
auto client = std::make_shared<Client>(std::move(pendingSocket));
client->readDescription(
std::bind(&NatHelper::onClientDesc, this, _1, _2));
incomingClients.insert({client.get(), std::move(client)});
acceptor.async_accept(pendingSocket,
std::bind(&NatHelper::onConnectionAccepted, this, _1));
}
void NatHelper::cleanEmptyRooms(){
for(auto it = rooms.begin(); it != rooms.end(); ){
if(it->second->isEmpty()){
std::cout << "Removing empty room " << it->first << "\n";
it = rooms.erase(it);
} else {
it++;
}
}
}
void NatHelper::onClientDesc(Client& client, bool success){
if(!success){
std::cerr << "Error reading client description" << std::endl;
incomingClients.erase(&client);
return;
}
const auto& roomName = client.getRoomName();
std::cout << "Moving client " << client.getClientName()
<< " to room " << roomName
<< std::endl;
auto roomIt = rooms.find(roomName);
if(roomIt == rooms.end()){
cleanEmptyRooms();
std::cout << "Creating room " << roomName << std::endl;
roomIt = rooms.insert({roomName, std::make_shared<Room>(roomName)}).first;
}
auto clientIt = incomingClients.find(&client);
auto& room = roomIt->second;
if(room->isFull()){
std::cerr << "Room " << roomName << " is full, dropping client" << std::endl;
} else {
roomIt->second->addClient(std::move(clientIt->second));
}
incomingClients.erase(clientIt);
}

77
nat-helper/nat-helper.hpp Normal file
View File

@@ -0,0 +1,77 @@
/**
* @file nat-helper.hpp
* @author Martin Piatka <piatka@cesnet.cz>
*/
/*
* Copyright (c) 2021 CESNET, z. s. p. o.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is 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.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
*/
#ifndef UG_NAT_HELPER
#define UG_NAT_HELPER
#include <asio.hpp>
#include <thread>
#include <memory>
#include <map>
#include "client.hpp"
#include "room.hpp"
class NatHelper {
public:
NatHelper();
NatHelper(int port);
~NatHelper() = default;
void run();
void stop();
private:
void worker();
void onConnectionAccepted(const std::error_code& ec);
void onClientDesc(Client& client, bool success);
void cleanEmptyRooms();
int port;
asio::io_service io_service;
//asio::executor_work_guard<asio::io_service::executor_type> work_guard;
std::thread worker_thread;
asio::ip::tcp::acceptor acceptor;
asio::ip::tcp::socket pendingSocket;
std::map<Client *, std::shared_ptr<Client>> incomingClients;
std::map<std::string, std::shared_ptr<Room>> rooms;
};
#endif //UG_NAT_HELPER

88
nat-helper/room.cpp Normal file
View File

@@ -0,0 +1,88 @@
/**
* @file room.cpp
* @author Martin Piatka <piatka@cesnet.cz>
*/
/*
* Copyright (c) 2021 CESNET, z. s. p. o.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is 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.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
*/
#include <functional>
#include <iostream>
#include "room.hpp"
Room::Room(std::string name) : name(std::move(name)) { }
void Room::addClient(std::shared_ptr<Client>&& client){
assert(!isFull());
Client *clientPtr = client.get();
for(auto& [c, _] : clients){
c->sendMsg(clientPtr->getClientName());
c->sendMsg(clientPtr->getSdpDesc());
clientPtr->sendMsg(c->getClientName());
clientPtr->sendMsg(c->getSdpDesc());
for(const auto& candidate : c->getCandidates()){
clientPtr->sendMsg(candidate);
}
}
clients.insert({clientPtr, std::move(client)});
using namespace std::placeholders;
clientPtr->readCandidate(
std::bind(&Room::onClientCandidate, shared_from_this(), _1, _2));
}
void Room::onClientCandidate(Client& client, bool success){
if(!success){
std::cerr << "Error reading candidate, removing client "
<< client.getClientName() << std::endl;
clients.erase(&client);
return;
}
std::cout << "Client candidate recieved" << std::endl;
for(auto& [c, cu] : clients){
if(&client == c)
continue;
c->sendMsg(client.getCandidates().back());
}
using namespace std::placeholders;
client.readCandidate(
std::bind(&Room::onClientCandidate, shared_from_this(), _1, _2));
}

60
nat-helper/room.hpp Normal file
View File

@@ -0,0 +1,60 @@
/**
* @file room.hpp
* @author Martin Piatka <piatka@cesnet.cz>
*/
/*
* Copyright (c) 2021 CESNET, z. s. p. o.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is 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.
*
* 3. Neither the name of CESNET nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESSED 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 AUTHORS 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.
*/
#ifndef UG_HOLE_PUNCH_ROOM_HPP
#define UG_HOLE_PUNCH_ROOM_HPP
#include <map>
#include <memory>
#include "client.hpp"
class Room : public std::enable_shared_from_this<Room>{
public:
Room(std::string name);
void addClient(std::shared_ptr<Client>&& client);
bool isFull() const { return clients.size() >= 2; }
bool isEmpty() const { return clients.empty(); }
private:
void onClientCandidate(Client& client, bool success);
std::string name;
std::map<Client *, std::shared_ptr<Client>> clients;
};
#endif