Compare commits

..

1 Commits

Author SHA1 Message Date
908a5d7dc3 Added .travis.yml 2020-05-13 12:51:00 +02:00
32 changed files with 188 additions and 1079 deletions

View File

@ -1,40 +0,0 @@
name: Build and test with GnuTLS
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build-ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install packages
run: sudo apt update && sudo apt install libgnutls28-dev nettle-dev
- name: submodules
run: git submodule update --init --recursive
- name: cmake
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
- name: make
run: (cd build; make -j2)
- name: test
run: ./build/tests
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: install packages
run: brew install gnutls nettle
- name: submodules
run: git submodule update --init --recursive
- name: cmake
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
env:
# hack to bypass EPERM issue on sendto()
CFLAGS: -DJUICE_ENABLE_ADDRS_LOCALHOST
- name: make
run: (cd build; make -j2)
- name: test
run: ./build/tests

View File

@ -1,24 +0,0 @@
name: Build and test with libnice
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build-ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install packages
run: sudo apt update && sudo apt install libgnutls28-dev libnice-dev
- name: submodules
run: git submodule update --init --recursive
- name: cmake
run: cmake -B build -DUSE_JUICE=0 -DUSE_GNUTLS=1
- name: make
run: (cd build; make -j2)
- name: test
run: ./build/tests

View File

@ -1,42 +0,0 @@
name: Build and test with OpenSSL
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build-ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install packages
run: sudo apt update && sudo apt install libssl-dev
- name: submodules
run: git submodule update --init --recursive
- name: cmake
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=0
- name: make
run: (cd build; make -j2)
- name: test
run: ./build/tests
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: install packages
run: brew reinstall openssl@1.1
- name: submodules
run: git submodule update --init --recursive
- name: cmake
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=0
env:
OPENSSL_ROOT_DIR: /usr/local/opt/openssl
OPENSSL_LIBRARIES: /usr/local/opt/openssl/lib
# hack to bypass EPERM issue on sendto()
CFLAGS: -DJUICE_ENABLE_ADDRS_LOCALHOST
- name: make
run: (cd build; make -j2)
- name: test
run: ./build/tests

21
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Build and test
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install packages
run: sudo apt update && sudo apt install libgnutls28-dev nettle-dev
- name: submodules
run: git submodule update --init --recursive
- name: cmake
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
- name: make
run: (cd build; make)
- name: test
run: ./build/tests

4
.travis.yml Normal file
View File

@ -0,0 +1,4 @@
os: osx
osx_image: xcode11.3
language: cpp
script: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1 && cd build && make && ./tests

View File

@ -1,7 +1,7 @@
cmake_minimum_required (VERSION 3.7)
project (libdatachannel
DESCRIPTION "WebRTC DataChannels Library"
VERSION 0.5.1
VERSION 0.4.9
LANGUAGES CXX)
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
@ -18,8 +18,8 @@ set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN)
if (MSVC)
add_definitions(-DNOMINMAX)
if (MSYS OR MINGW)
add_definitions(-DSCTP_STDINT_INCLUDE=<stdint.h>)
endif()
endif()
@ -71,15 +71,10 @@ set(TESTS_ANSWERER_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/p2p/answerer.cpp
)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
add_subdirectory(deps/usrsctp EXCLUDE_FROM_ALL)
if (MSYS OR MINGW)
target_compile_definitions(usrsctp PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
target_compile_definitions(usrsctp-static PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_options(usrsctp PRIVATE -Wno-error=format-truncation)
target_compile_options(usrsctp-static PRIVATE -Wno-error=format-truncation)
@ -93,11 +88,10 @@ set_target_properties(datachannel PROPERTIES
CXX_STANDARD 17)
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel PUBLIC Threads::Threads)
target_link_libraries(datachannel PRIVATE Usrsctp::UsrsctpStatic)
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
target_link_libraries(datachannel Threads::Threads Usrsctp::UsrsctpStatic)
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL ${LIBDATACHANNEL_SOURCES})
set_target_properties(datachannel-static PROPERTIES
@ -105,15 +99,14 @@ set_target_properties(datachannel-static PROPERTIES
CXX_STANDARD 17)
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(datachannel-static PUBLIC Threads::Threads)
target_link_libraries(datachannel-static PRIVATE Usrsctp::UsrsctpStatic)
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
target_link_libraries(datachannel-static Threads::Threads Usrsctp::UsrsctpStatic)
if(WIN32)
target_link_libraries(datachannel PRIVATE wsock32 ws2_32) # winsock2
target_link_libraries(datachannel-static PRIVATE wsock32 ws2_32) # winsock2
target_link_libraries(datachannel "wsock32" "ws2_32") # winsock2
target_link_libraries(datachannel-static "wsock32" "ws2_32") # winsock2
endif()
if (USE_GNUTLS)
@ -127,29 +120,29 @@ if (USE_GNUTLS)
IMPORTED_LOCATION "${GNUTLS_LIBRARIES}")
endif()
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=1)
target_link_libraries(datachannel PRIVATE GnuTLS::GnuTLS)
target_link_libraries(datachannel GnuTLS::GnuTLS)
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=1)
target_link_libraries(datachannel-static PRIVATE GnuTLS::GnuTLS)
target_link_libraries(datachannel-static GnuTLS::GnuTLS)
else()
find_package(OpenSSL REQUIRED)
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=0)
target_link_libraries(datachannel PRIVATE OpenSSL::SSL)
target_link_libraries(datachannel OpenSSL::SSL)
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=0)
target_link_libraries(datachannel-static PRIVATE OpenSSL::SSL)
target_link_libraries(datachannel-static OpenSSL::SSL)
endif()
if (USE_JUICE)
add_subdirectory(deps/libjuice EXCLUDE_FROM_ALL)
target_compile_definitions(datachannel PRIVATE USE_JUICE=1)
target_link_libraries(datachannel PRIVATE LibJuice::LibJuiceStatic)
target_link_libraries(datachannel LibJuice::LibJuiceStatic)
target_compile_definitions(datachannel-static PRIVATE USE_JUICE=1)
target_link_libraries(datachannel-static PRIVATE LibJuice::LibJuiceStatic)
target_link_libraries(datachannel-static LibJuice::LibJuiceStatic)
else()
find_package(LibNice REQUIRED)
target_compile_definitions(datachannel PRIVATE USE_JUICE=0)
target_link_libraries(datachannel PRIVATE LibNice::LibNice)
target_link_libraries(datachannel LibNice::LibNice)
target_compile_definitions(datachannel-static PRIVATE USE_JUICE=0)
target_link_libraries(datachannel-static PRIVATE LibNice::LibNice)
target_link_libraries(datachannel-static LibNice::LibNice)
endif()
add_library(LibDataChannel::LibDataChannel ALIAS datachannel)

27
Jamfile
View File

@ -1,5 +1,3 @@
import feature : feature ;
project libdatachannel ;
path-constant CWD : . ;
@ -7,26 +5,21 @@ lib libdatachannel
: # sources
[ glob ./src/*.cpp ]
: # requirements
<cxxstd>17
<include>./include/rtc
<define>USE_GNUTLS=0
<define>USE_JUICE=1
<cxxflags>"`pkg-config --cflags openssl`"
<library>/libdatachannel//usrsctp
<library>/libdatachannel//juice
<library>/libdatachannel//plog
: # default build
<link>static
: # usage requirements
<include>./include
<library>/libdatachannel//plog
<cxxflags>-pthread
<linkflags>"`pkg-config --libs openssl`"
;
feature crypto : openssl gnutls : composite propagated ;
feature.compose <crypto>openssl
: <define>USE_GNUTLS=0 ;
feature.compose <crypto>gnutls
: <define>USE_GNUTLS=1 ;
alias plog
: # no sources
: # no build requirements
@ -64,21 +57,9 @@ actions make_libusrsctp
}
make libjuice.a : : @make_libjuice ;
rule make_libjuice ( targets * : sources * : properties * )
{
if <crypto>gnutls in $(properties)
{
MAKEOPTS on $(targets) = "USE_NETTLE=1" ;
}
else
{
MAKEOPTS on $(targets) = "USE_NETTLE=0" ;
}
}
actions make_libjuice
{
(cd $(CWD)/deps/libjuice && make $(MAKEOPTS))
(cd $(CWD)/deps/libjuice && make USE_NETTLE=0)
cp $(CWD)/deps/libjuice/libjuice.a $(<)
}

View File

@ -10,7 +10,7 @@ if (NOT TARGET LibNice::LibNice)
HINTS ${PC_LIBNICE_LIBDIR} ${PC_LIBNICE_LIBRARY_DIRS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibNice DEFAULT_MSG
find_package_handle_standard_args(Libnice DEFAULT_MSG
LIBNICE_LIBRARY LIBNICE_INCLUDE_DIR)
mark_as_advanced(LIBNICE_INCLUDE_DIR LIBNICE_LIBRARY)

2
deps/libjuice vendored

View File

@ -68,7 +68,7 @@ public:
synchronized_callback &operator=(std::function<void(P...)> func) {
std::lock_guard lock(mutex);
callback = std::move(func);
callback = func;
return *this;
}
@ -78,10 +78,7 @@ public:
callback(args...);
}
operator bool() const {
std::lock_guard lock(mutex);
return callback ? true : false;
}
operator bool() const { return callback ? true : false; }
private:
std::function<void(P...)> callback;

View File

@ -28,9 +28,9 @@
namespace rtc {
struct Message : binary {
enum Type { Binary, String, Control, Reset };
enum Type { Binary, String, Control };
Message(size_t size, Type type_ = Binary) : binary(size), type(type_) {}
Message(size_t size) : binary(size), type(Binary) {}
template <typename Iterator>
Message(Iterator begin_, Iterator end_, Type type_ = Binary)
@ -46,7 +46,7 @@ using mutable_message_ptr = std::shared_ptr<Message>;
using message_callback = std::function<void(message_ptr message)>;
constexpr auto message_size_func = [](const message_ptr &m) -> size_t {
return m->type == Message::Binary || m->type == Message::String ? m->size() : 0;
return m->type != Message::Control ? m->size() : 0;
};
template <typename Iterator>
@ -59,15 +59,6 @@ message_ptr make_message(Iterator begin, Iterator end, Message::Type type = Mess
return message;
}
inline message_ptr make_message(size_t size, Message::Type type = Message::Binary,
unsigned int stream = 0,
std::shared_ptr<Reliability> reliability = nullptr) {
auto message = std::make_shared<Message>(size, type);
message->stream = stream;
message->reliability = reliability;
return message;
}
} // namespace rtc
#endif

View File

@ -31,7 +31,6 @@
#include <atomic>
#include <functional>
#include <future>
#include <list>
#include <mutex>
#include <shared_mutex>
@ -45,9 +44,6 @@ class IceTransport;
class DtlsTransport;
class SctpTransport;
using certificate_ptr = std::shared_ptr<Certificate>;
using future_certificate_ptr = std::shared_future<certificate_ptr>;
class PeerConnection : public std::enable_shared_from_this<PeerConnection> {
public:
enum class State : int {
@ -130,7 +126,7 @@ private:
void resetCallbacks();
const Configuration mConfig;
const future_certificate_ptr mCertificate;
const std::shared_ptr<Certificate> mCertificate;
std::optional<Description> mLocalDescription, mRemoteDescription;
mutable std::recursive_mutex mLocalDescriptionMutex, mRemoteDescriptionMutex;

View File

@ -114,9 +114,6 @@ int rtcGetAvailableAmount(int dc); // total size available to receive
int rtcSetAvailableCallback(int dc, availableCallbackFunc cb);
int rtcReceiveMessage(int dc, char *buffer, int *size);
// Cleanup
void rtcCleanup();
#ifdef __cplusplus
} // extern "C"
#endif

View File

@ -60,19 +60,12 @@ bool Candidate::resolve(ResolveMode mode) {
if (mIsResolved)
return true;
PLOG_VERBOSE << "Resolving candidate (mode="
<< (mode == ResolveMode::Simple ? "simple" : "lookup")
<< "): " << mCandidate;
// See RFC 8445 for format
std::istringstream iss(mCandidate);
std::stringstream ss(mCandidate);
int component{0}, priority{0};
string foundation, transport, node, service, typ_, type;
if (iss >> foundation >> component >> transport >> priority &&
iss >> node >> service >> typ_ >> type && typ_ == "typ") {
string left;
std::getline(iss, left);
if (ss >> foundation >> component >> transport >> priority &&
ss >> node >> service >> typ_ >> type && typ_ == "typ") {
// Try to resolve the node
struct addrinfo hints = {};
@ -101,13 +94,15 @@ bool Candidate::resolve(ResolveMode mode) {
if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
servbuffer, MAX_NUMERICSERV_LEN,
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
string left;
std::getline(ss, left);
const char sp{' '};
std::ostringstream oss;
oss << foundation << sp << component << sp << transport << sp << priority;
oss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type;
oss << left;
mCandidate = oss.str();
PLOG_VERBOSE << "Resolved candidate: " << mCandidate;
ss.clear();
ss << foundation << sp << component << sp << transport << sp << priority;
ss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type;
if (!left.empty())
ss << left;
mCandidate = ss.str();
return mIsResolved = true;
}
}

View File

@ -141,9 +141,14 @@ string make_fingerprint(gnutls_x509_crt_t crt) {
return oss.str();
}
namespace {
shared_ptr<Certificate> make_certificate(const string &commonName) {
static std::unordered_map<string, shared_ptr<Certificate>> cache;
static std::mutex cacheMutex;
std::lock_guard lock(cacheMutex);
if (auto it = cache.find(commonName); it != cache.end())
return it->second;
certificate_ptr make_certificate_impl(string commonName) {
std::unique_ptr<gnutls_x509_crt_t, decltype(&delete_crt)> crt(create_crt(), delete_crt);
std::unique_ptr<gnutls_x509_privkey_t, decltype(&delete_privkey)> privkey(create_privkey(),
delete_privkey);
@ -169,11 +174,11 @@ certificate_ptr make_certificate_impl(string commonName) {
check_gnutls(gnutls_x509_crt_sign2(*crt, *crt, *privkey, GNUTLS_DIG_SHA256, 0),
"Unable to auto-sign certificate");
return std::make_shared<Certificate>(*crt, *privkey);
auto certificate = std::make_shared<Certificate>(*crt, *privkey);
cache.emplace(std::make_pair(commonName, certificate));
return certificate;
}
} // namespace
} // namespace rtc
#else
@ -231,9 +236,15 @@ string make_fingerprint(X509 *x509) {
return oss.str();
}
namespace {
certificate_ptr make_certificate_impl(string commonName) {
shared_ptr<Certificate> make_certificate(const string &commonName) {
static std::unordered_map<string, shared_ptr<Certificate>> cache;
static std::mutex cacheMutex;
std::lock_guard lock(cacheMutex);
if (auto it = cache.find(commonName); it != cache.end())
return it->second;
shared_ptr<X509> x509(X509_new(), X509_free);
shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);
@ -254,8 +265,7 @@ certificate_ptr make_certificate_impl(string commonName) {
throw std::runtime_error("Unable to generate key pair");
const size_t serialSize = 16;
auto *commonNameBytes =
reinterpret_cast<unsigned char *>(const_cast<char *>(commonName.c_str()));
const auto *commonNameBytes = reinterpret_cast<const unsigned char *>(commonName.c_str());
if (!X509_gmtime_adj(X509_get_notBefore(x509.get()), 3600 * -1) ||
!X509_gmtime_adj(X509_get_notAfter(x509.get()), 3600 * 24 * 365) ||
@ -271,54 +281,12 @@ certificate_ptr make_certificate_impl(string commonName) {
if (!X509_sign(x509.get(), pkey.get(), EVP_sha256()))
throw std::runtime_error("Unable to auto-sign certificate");
return std::make_shared<Certificate>(x509, pkey);
auto certificate = std::make_shared<Certificate>(x509, pkey);
cache.emplace(std::make_pair(commonName, certificate));
return certificate;
}
} // namespace
} // namespace rtc
#endif
// Common for GnuTLS and OpenSSL
namespace rtc {
namespace {
// Helper function roughly equivalent to std::async with policy std::launch::async
// since std::async might be unreliable on some platforms (e.g. Mingw32 on Windows)
template <class F, class... Args>
std::future<std::result_of_t<std::decay_t<F>(std::decay_t<Args>...)>> thread_call(F &&f,
Args &&... args) {
using R = std::result_of_t<std::decay_t<F>(std::decay_t<Args>...)>;
std::packaged_task<R()> task(std::bind(f, std::forward<Args>(args)...));
std::future<R> future = task.get_future();
std::thread t(std::move(task));
t.detach();
return future;
}
static std::unordered_map<string, future_certificate_ptr> CertificateCache;
static std::mutex CertificateCacheMutex;
} // namespace
future_certificate_ptr make_certificate(string commonName) {
std::lock_guard lock(CertificateCacheMutex);
if (auto it = CertificateCache.find(commonName); it != CertificateCache.end())
return it->second;
auto future = thread_call(make_certificate_impl, commonName);
auto shared = future.share();
CertificateCache.emplace(std::move(commonName), shared);
return shared;
}
void CleanupCertificateCache() {
std::lock_guard lock(CertificateCacheMutex);
CertificateCache.clear();
}
} // namespace rtc

View File

@ -21,7 +21,6 @@
#include "include.hpp"
#include <future>
#include <tuple>
#if USE_GNUTLS
@ -63,12 +62,7 @@ string make_fingerprint(gnutls_x509_crt_t crt);
string make_fingerprint(X509 *x509);
#endif
using certificate_ptr = std::shared_ptr<Certificate>;
using future_certificate_ptr = std::shared_future<certificate_ptr>;
future_certificate_ptr make_certificate(string commonName); // cached
void CleanupCertificateCache();
std::shared_ptr<Certificate> make_certificate(const string &commonName);
} // namespace rtc

View File

@ -93,20 +93,19 @@ string DataChannel::protocol() const { return mProtocol; }
Reliability DataChannel::reliability() const { return *mReliability; }
void DataChannel::close() {
mIsClosed = true;
if (mIsOpen.exchange(false))
if (auto transport = mSctpTransport.lock())
transport->close(mStream);
transport->reset(mStream);
mIsClosed = true;
mSctpTransport.reset();
resetCallbacks();
}
void DataChannel::remoteClose() {
mIsOpen = false;
if (!mIsClosed.exchange(true))
triggerClosed();
mIsOpen = false;
mSctpTransport.reset();
}
@ -140,9 +139,6 @@ std::optional<std::variant<binary, string>> DataChannel::receive() {
string(reinterpret_cast<const char *>(message->data()), message->size()));
case Message::Binary:
return std::make_optional(std::move(*message));
default:
// Ignore
break;
}
}

View File

@ -63,8 +63,9 @@ void DtlsTransport::Cleanup() {
// Nothing to do
}
DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
verifier_callback verifierCallback, state_callback stateChangeCallback)
DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certificate> certificate,
verifier_callback verifierCallback,
state_callback stateChangeCallback)
: Transport(lower), mCertificate(certificate), mState(State::Disconnected),
mVerifierCallback(std::move(verifierCallback)),
mStateChangeCallback(std::move(stateChangeCallback)) {
@ -99,7 +100,6 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
gnutls_transport_set_pull_timeout_function(mSession, TimeoutCallback);
mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
registerIncoming();
}
DtlsTransport::~DtlsTransport() {
@ -116,7 +116,9 @@ bool DtlsTransport::stop() {
PLOG_DEBUG << "Stopping DTLS recv thread";
mIncomingQueue.stop();
gnutls_bye(mSession, GNUTLS_SHUT_RDWR);
mRecvThread.join();
onRecv(nullptr);
return true;
}
@ -215,8 +217,6 @@ void DtlsTransport::runRecvLoop() {
PLOG_ERROR << "DTLS recv: " << e.what();
}
gnutls_bye(mSession, GNUTLS_SHUT_RDWR);
PLOG_INFO << "DTLS disconnected";
changeState(State::Disconnected);
recv(nullptr);
@ -410,7 +410,6 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
SSL_set_tmp_ecdh(mSsl, ecdh.get());
mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
registerIncoming();
}
DtlsTransport::~DtlsTransport() {
@ -428,6 +427,7 @@ bool DtlsTransport::stop() {
mIncomingQueue.stop();
mRecvThread.join();
SSL_shutdown(mSsl);
onRecv(nullptr);
return true;
}

View File

@ -51,7 +51,7 @@ public:
using verifier_callback = std::function<bool(const std::string &fingerprint)>;
using state_callback = std::function<void(State state)>;
DtlsTransport(std::shared_ptr<IceTransport> lower, certificate_ptr certificate,
DtlsTransport(std::shared_ptr<IceTransport> lower, std::shared_ptr<Certificate> certificate,
verifier_callback verifierCallback, state_callback stateChangeCallback);
~DtlsTransport();
@ -65,7 +65,7 @@ private:
void changeState(State state);
void runRecvLoop();
const certificate_ptr mCertificate;
const std::shared_ptr<Certificate> mCertificate;
Queue<message_ptr> mIncomingQueue;
std::atomic<State> mState;

View File

@ -103,6 +103,7 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
IceTransport::~IceTransport() { stop(); }
bool IceTransport::stop() {
onRecv(nullptr);
return Transport::stop();
}
@ -168,6 +169,15 @@ bool IceTransport::send(message_ptr message) {
return outgoing(message);
}
void IceTransport::incoming(message_ptr message) {
PLOG_VERBOSE << "Incoming size=" << message->size();
recv(message);
}
void IceTransport::incoming(const byte *data, int size) {
incoming(make_message(data, data + size));
}
bool IceTransport::outgoing(message_ptr message) {
return juice_send(mAgent.get(), reinterpret_cast<const char *>(message->data()),
message->size()) >= 0;
@ -224,9 +234,7 @@ void IceTransport::RecvCallback(juice_agent_t *agent, const char *data, size_t s
void *user_ptr) {
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
try {
PLOG_VERBOSE << "Incoming size=" << size;
auto b = reinterpret_cast<const byte *>(data);
iceTransport->incoming(make_message(b, b + size));
iceTransport->incoming(reinterpret_cast<const byte *>(data), size);
} catch (const std::exception &e) {
PLOG_WARNING << e.what();
}
@ -447,9 +455,6 @@ bool IceTransport::stop() {
return false;
PLOG_DEBUG << "Stopping ICE thread";
nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(mMainLoop.get()),
NULL, NULL);
nice_agent_remove_stream(mNiceAgent.get(), mStreamId);
g_main_loop_quit(mMainLoop.get());
mMainLoopThread.join();
return true;
@ -536,6 +541,15 @@ bool IceTransport::send(message_ptr message) {
return outgoing(message);
}
void IceTransport::incoming(message_ptr message) {
PLOG_VERBOSE << "Incoming size=" << message->size();
recv(message);
}
void IceTransport::incoming(const byte *data, int size) {
incoming(make_message(data, data + size));
}
bool IceTransport::outgoing(message_ptr message) {
return nice_agent_send(mNiceAgent.get(), mStreamId, 1, message->size(),
reinterpret_cast<const char *>(message->data())) >= 0;
@ -623,9 +637,7 @@ void IceTransport::RecvCallback(NiceAgent *agent, guint streamId, guint componen
gchar *buf, gpointer userData) {
auto iceTransport = static_cast<rtc::IceTransport *>(userData);
try {
PLOG_VERBOSE << "Incoming size=" << len;
auto b = reinterpret_cast<byte *>(buf);
iceTransport->incoming(make_message(b, b + len));
iceTransport->incoming(reinterpret_cast<byte *>(buf), len);
} catch (const std::exception &e) {
PLOG_WARNING << e.what();
}
@ -688,14 +700,14 @@ bool IceTransport::getSelectedCandidatePair(CandidateInfo *localInfo, CandidateI
const CandidateType IceTransport::NiceTypeToCandidateType(NiceCandidateType type) {
switch (type) {
case NiceCandidateType::NICE_CANDIDATE_TYPE_HOST:
return CandidateType::Host;
case NiceCandidateType::NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
return CandidateType::PeerReflexive;
case NiceCandidateType::NICE_CANDIDATE_TYPE_RELAYED:
return CandidateType::Relayed;
case NiceCandidateType::NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
return CandidateType::ServerReflexive;
default:
return CandidateType::Host;
}
}
@ -708,7 +720,7 @@ IceTransport::NiceTransportTypeToCandidateTransportType(NiceCandidateTransport t
return CandidateTransportType::TcpPassive;
case NiceCandidateTransport::NICE_CANDIDATE_TRANSPORT_TCP_SO:
return CandidateTransportType::TcpSo;
default:
case NiceCandidateTransport::NICE_CANDIDATE_TRANSPORT_UDP:
return CandidateTransportType::Udp;
}
}

View File

@ -37,7 +37,7 @@
#include <thread>
namespace rtc {
class IceTransport : public Transport {
public:
#if USE_JUICE
@ -85,6 +85,8 @@ public:
bool send(message_ptr message) override; // false if dropped
private:
void incoming(message_ptr message) override;
void incoming(const byte *data, int size);
bool outgoing(message_ptr message) override;
void changeState(State state);

View File

@ -18,7 +18,6 @@
#include "init.hpp"
#include "certificate.hpp"
#include "dtlstransport.hpp"
#include "sctptransport.hpp"
@ -75,7 +74,6 @@ Init::Init() {
}
Init::~Init() {
CleanupCertificateCache();
DtlsTransport::Cleanup();
SctpTransport::Cleanup();

View File

@ -53,8 +53,7 @@ auto weak_bind_verifier(F &&f, T *t, Args &&... _args) {
PeerConnection::PeerConnection() : PeerConnection(Configuration()) {}
PeerConnection::PeerConnection(const Configuration &config)
: mConfig(config), mCertificate(make_certificate("libdatachannel")), mState(State::New),
mGatheringState(GatheringState::New) {}
: mConfig(config), mCertificate(make_certificate("libdatachannel")), mState(State::New) {}
PeerConnection::~PeerConnection() { close(); }
@ -269,10 +268,9 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
if (auto transport = std::atomic_load(&mDtlsTransport))
return transport;
auto certificate = mCertificate.get();
auto lower = std::atomic_load(&mIceTransport);
auto transport = std::make_shared<DtlsTransport>(
lower, certificate, weak_bind_verifier(&PeerConnection::checkFingerprint, this, _1),
lower, mCertificate, weak_bind_verifier(&PeerConnection::checkFingerprint, this, _1),
[this, weak_this = weak_from_this()](DtlsTransport::State state) {
auto shared_this = weak_this.lock();
if (!shared_this)
@ -423,8 +421,8 @@ void PeerConnection::forwardMessage(message_ptr message) {
weak_ptr<DataChannel>{channel}));
mDataChannels.insert(std::make_pair(message->stream, channel));
} else {
// Invalid, close the DataChannel
sctpTransport->close(message->stream);
// Invalid, close the DataChannel by resetting the stream
sctpTransport->reset(message->stream);
return;
}
}
@ -514,11 +512,9 @@ void PeerConnection::processLocalDescription(Description description) {
if (auto remote = remoteDescription())
remoteSctpPort = remote->sctpPort();
auto certificate = mCertificate.get(); // wait for certificate if not ready
std::lock_guard lock(mLocalDescriptionMutex);
mLocalDescription.emplace(std::move(description));
mLocalDescription->setFingerprint(certificate->fingerprint());
mLocalDescription->setFingerprint(mCertificate->fingerprint());
mLocalDescription->setSctpPort(remoteSctpPort.value_or(DEFAULT_SCTP_PORT));
mLocalDescription->setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);

View File

@ -450,5 +450,3 @@ int rtcReceiveMessage(int dc, char *buffer, int *size) {
*message);
});
}
void rtcCleanup() { rtc::Cleanup(); }

View File

@ -21,7 +21,6 @@
#include <chrono>
#include <exception>
#include <iostream>
#include <thread>
#include <vector>
#ifdef USE_JUICE
@ -63,10 +62,7 @@ void SctpTransport::Init() {
usrsctp_sysctl_set_sctp_heartbeat_interval_default(10 * 1000); // ms
}
void SctpTransport::Cleanup() {
while (usrsctp_finish() != 0)
std::this_thread::sleep_for(100ms);
}
void SctpTransport::Cleanup() { usrsctp_finish(); }
SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
message_callback recvCallback, amount_callback bufferedAmountCallback,
@ -167,16 +163,13 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
throw std::runtime_error("Could not set SCTP send buffer size, errno=" +
std::to_string(errno));
registerIncoming();
connect();
}
SctpTransport::~SctpTransport() {
stop();
if (mSock)
usrsctp_close(mSock);
usrsctp_close(mSock);
usrsctp_deregister_address(this);
}
@ -194,9 +187,6 @@ bool SctpTransport::stop() {
}
void SctpTransport::connect() {
if (!mSock)
return;
PLOG_DEBUG << "SCTP connect";
changeState(State::Connecting);
@ -220,19 +210,12 @@ void SctpTransport::connect() {
}
void SctpTransport::shutdown() {
if (!mSock)
return;
PLOG_DEBUG << "SCTP shutdown";
if (usrsctp_shutdown(mSock, SHUT_RDWR) != 0 && errno != ENOTCONN) {
if (usrsctp_shutdown(mSock, SHUT_RDWR)) {
PLOG_WARNING << "SCTP shutdown failed, errno=" << errno;
}
// close() abort the connection when linger is disabled, call it now
usrsctp_close(mSock);
mSock = nullptr;
PLOG_INFO << "SCTP disconnected";
changeState(State::Disconnected);
mWrittenCondition.notify_all();
@ -254,15 +237,31 @@ bool SctpTransport::send(message_ptr message) {
return false;
}
void SctpTransport::close(unsigned int stream) {
send(make_message(0, Message::Reset, uint16_t(stream)));
}
void SctpTransport::flush() {
std::lock_guard lock(mSendMutex);
trySendQueue();
}
void SctpTransport::reset(unsigned int stream) {
PLOG_DEBUG << "SCTP resetting stream " << stream;
std::unique_lock lock(mWriteMutex);
mWritten = false;
using srs_t = struct sctp_reset_streams;
const size_t len = sizeof(srs_t) + sizeof(uint16_t);
byte buffer[len] = {};
srs_t &srs = *reinterpret_cast<srs_t *>(buffer);
srs.srs_flags = SCTP_STREAM_RESET_OUTGOING;
srs.srs_number_streams = 1;
srs.srs_stream_list[0] = uint16_t(stream);
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_RESET_STREAMS, &srs, len) == 0) {
mWrittenCondition.wait_for(lock, 1000ms,
[&]() { return mWritten || mState != State::Connected; });
} else {
PLOG_WARNING << "SCTP reset stream " << stream << " failed, errno=" << errno;
}
}
void SctpTransport::incoming(message_ptr message) {
// There could be a race condition here where we receive the remote INIT before the local one is
// sent, which would result in the connection being aborted. Therefore, we need to wait for data
@ -302,9 +301,16 @@ bool SctpTransport::trySendQueue() {
bool SctpTransport::trySendMessage(message_ptr message) {
// Requires mSendMutex to be locked
if (!mSock || mState != State::Connected)
if (mState != State::Connected)
return false;
PLOG_VERBOSE << "SCTP try send size=" << message->size();
// TODO: Implement SCTP ndata specification draft when supported everywhere
// See https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-08
const Reliability reliability = message->reliability ? *message->reliability : Reliability();
uint32_t ppid;
switch (message->type) {
case Message::String:
@ -313,24 +319,11 @@ bool SctpTransport::trySendMessage(message_ptr message) {
case Message::Binary:
ppid = !message->empty() ? PPID_BINARY : PPID_BINARY_EMPTY;
break;
case Message::Control:
default:
ppid = PPID_CONTROL;
break;
case Message::Reset:
sendReset(message->stream);
return true;
default:
// Ignore
return true;
}
PLOG_VERBOSE << "SCTP try send size=" << message->size();
// TODO: Implement SCTP ndata specification draft when supported everywhere
// See https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-08
const Reliability reliability = message->reliability ? *message->reliability : Reliability();
struct sctp_sendv_spa spa = {};
// set sndinfo
@ -395,33 +388,6 @@ void SctpTransport::updateBufferedAmount(uint16_t streamId, long delta) {
mBufferedAmountCallback(streamId, amount);
}
void SctpTransport::sendReset(uint16_t streamId) {
// Requires mSendMutex to be locked
if (!mSock || state() != State::Connected)
return;
PLOG_DEBUG << "SCTP resetting stream " << streamId;
using srs_t = struct sctp_reset_streams;
const size_t len = sizeof(srs_t) + sizeof(uint16_t);
byte buffer[len] = {};
srs_t &srs = *reinterpret_cast<srs_t *>(buffer);
srs.srs_flags = SCTP_STREAM_RESET_OUTGOING;
srs.srs_number_streams = 1;
srs.srs_stream_list[0] = streamId;
mWritten = false;
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_RESET_STREAMS, &srs, len) == 0) {
std::unique_lock lock(mWriteMutex); // locking before setsockopt might deadlock usrsctp...
mWrittenCondition.wait_for(lock, 1000ms,
[&]() { return mWritten || mState != State::Connected; });
} else if (errno == EINVAL) {
PLOG_VERBOSE << "SCTP stream " << streamId << " already reset";
} else {
PLOG_WARNING << "SCTP reset stream " << streamId << " failed, errno=" << errno;
}
}
bool SctpTransport::safeFlush() {
try {
flush();
@ -598,7 +564,7 @@ void SctpTransport::processNotification(const union sctp_notification *notify, s
if (flags & SCTP_STREAM_RESET_OUTGOING_SSN) {
for (int i = 0; i < count; ++i) {
uint16_t streamId = reset_event.strreset_stream_list[i];
close(streamId);
reset(streamId);
}
}
if (flags & SCTP_STREAM_RESET_INCOMING_SSN) {
@ -627,17 +593,15 @@ size_t SctpTransport::bytesSent() { return mBytesSent; }
size_t SctpTransport::bytesReceived() { return mBytesReceived; }
std::optional<milliseconds> SctpTransport::rtt() {
if (!mSock || state() != State::Connected)
return nullopt;
std::optional<std::chrono::milliseconds> SctpTransport::rtt() {
struct sctp_status status = {};
socklen_t len = sizeof(status);
if (usrsctp_getsockopt(mSock, IPPROTO_SCTP, SCTP_STATUS, &status, &len)) {
if (usrsctp_getsockopt(this->mSock, IPPROTO_SCTP, SCTP_STATUS, &status, &len)) {
PLOG_WARNING << "Could not read SCTP_STATUS";
return nullopt;
return std::nullopt;
}
return milliseconds(status.sstat_primary.spinfo_srtt);
return std::chrono::milliseconds(status.sstat_primary.spinfo_srtt);
}
int SctpTransport::RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data,

View File

@ -51,8 +51,8 @@ public:
bool stop() override;
bool send(message_ptr message) override; // false if buffered
void close(unsigned int stream);
void flush();
void reset(unsigned int stream);
// Stats
void clearStats();
@ -81,7 +81,6 @@ private:
bool trySendQueue();
bool trySendMessage(message_ptr message);
void updateBufferedAmount(uint16_t streamId, long delta);
void sendReset(uint16_t streamId);
bool safeFlush();
int handleRecv(struct socket *sock, union sctp_sockstore addr, const byte *data, size_t len,
@ -100,9 +99,9 @@ private:
std::map<uint16_t, size_t> mBufferedAmount;
amount_callback mBufferedAmountCallback;
std::mutex mWriteMutex;
std::condition_variable mWrittenCondition;
std::atomic<bool> mWritten = false; // written outside lock
std::recursive_mutex mWriteMutex;
std::condition_variable_any mWrittenCondition;
bool mWritten = false;
bool mWrittenOnce = false;
state_callback mStateChangeCallback;

View File

@ -32,30 +32,26 @@ using namespace std::placeholders;
class Transport {
public:
Transport(std::shared_ptr<Transport> lower = nullptr) : mLower(std::move(lower)) {}
virtual ~Transport() {
stop();
if (mLower)
mLower->onRecv(nullptr); // doing it on stop could cause a deadlock
}
virtual bool stop() {
return !mShutdown.exchange(true);
}
void registerIncoming() {
Transport(std::shared_ptr<Transport> lower = nullptr) : mLower(std::move(lower)) {
if (mLower)
mLower->onRecv(std::bind(&Transport::incoming, this, _1));
}
virtual ~Transport() { stop(); }
virtual bool stop() {
if (mLower)
mLower->onRecv(nullptr);
return !mShutdown.exchange(true);
}
virtual bool send(message_ptr message) = 0;
void onRecv(message_callback callback) { mRecvCallback = std::move(callback); }
virtual bool send(message_ptr message) { return outgoing(message); }
protected:
void recv(message_ptr message) { mRecvCallback(message); }
virtual void incoming(message_ptr message) { recv(message); }
virtual void incoming(message_ptr message) = 0;
virtual bool outgoing(message_ptr message) {
if (mLower)
return mLower->send(message);

View File

@ -23,12 +23,9 @@
#include <cstdlib>
#include <cstring>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // for sleep
#endif
using namespace std;
typedef struct {
rtcState state;
@ -199,10 +196,6 @@ int test_capi_main() {
deletePeer(peer2);
sleep(1);
// You may call rtcCleanup() when finished to free static resources
rtcCleanup();
sleep(1);
printf("Success\n");
return 0;

View File

@ -140,9 +140,5 @@ void test_connectivity() {
pc2->close();
this_thread::sleep_for(1s);
// You may call rtc::Cleanup() when finished to free static resources
rtc::Cleanup();
this_thread::sleep_for(1s);
cout << "Success" << endl;
}

View File

@ -1,12 +0,0 @@
cmake_minimum_required(VERSION 3.5.1)
project(offerer C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_FLAGS "-Wall -g -O2")
add_executable(offerer offerer.c)
target_link_libraries(offerer datachannel)
add_executable(answerer answerer.c)
target_link_libraries(answerer datachannel)

View File

@ -1,326 +0,0 @@
/**
* Copyright (c) 2020 Paul-Louis Ageneau
* Copyright (c) 2020 Stevedan Ogochukwu Omodolor
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <rtc/rtc.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for sleep
#include <ctype.h>
typedef struct {
rtcState state;
rtcGatheringState gatheringState;
int pc;
int dc;
bool connected;
} Peer;
Peer *peer = NULL;
static void dataChannelCallback(int dc, void *ptr);
static void descriptionCallback(const char *sdp, const char *type, void *ptr);
static void candidateCallback(const char *cand, const char *mid, void *ptr);
static void stateChangeCallback(rtcState state, void *ptr);
static void gatheringStateCallback(rtcGatheringState state, void *ptr);
static void closedCallback(void *ptr);
static void messageCallback(const char *message, int size, void *ptr);
static void deletePeer(Peer *peer);
int all_space(const char *str);
char* state_print(rtcState state);
char* rtcGatheringState_print(rtcState state);
int main(int argc, char **argv) {
rtcInitLogger(RTC_LOG_DEBUG);
// Create peer
rtcConfiguration config;
memset(&config, 0, sizeof(config));
Peer *peer = (Peer *)malloc(sizeof(Peer));
if (!peer) {
printf("Error allocating memory for peer\n");
deletePeer(peer);
}
memset(peer, 0, sizeof(Peer));
printf("Peer created\n");
// Create peer connection
peer->pc = rtcCreatePeerConnection(&config);
rtcSetUserPointer(peer->pc, peer);
rtcSetLocalDescriptionCallback(peer->pc, descriptionCallback);
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
rtcSetUserPointer(peer->dc, NULL);
rtcSetDataChannelCallback(peer->pc, dataChannelCallback);
bool exit = false;
while (!exit) {
printf("\n");
printf("***************************************************************************************\n");
// << endl
printf("* 0: Exit /"
" 1: Enter remote description /"
" 2: Enter remote candidate /"
" 3: Send message /"
" 4: Print Connection Info *\n"
"[Command]: ");
int command = -1;
int c;
// int check_scan
if (scanf("%d", &command)) {
}else {
break;
}
while ((c = getchar()) != '\n' && c != EOF) { }
fflush(stdin);
switch (command) {
case 0: {
exit = true;
break;
}
case 1: {
// Parse Description
printf("[Description]: ");
char *line = NULL;
size_t len = 0;
size_t read = 0;
char *sdp = (char*) malloc(sizeof(char));
while ((read = getline(&line, &len, stdin)) != -1 && !all_space(line)) {
sdp = (char*) realloc (sdp,(strlen(sdp)+1) +strlen(line)+1);
strcat(sdp, line);
}
printf("%s\n",sdp);
rtcSetRemoteDescription(peer->pc, sdp, "offer");
free(sdp);
free(line);
break;
}
case 2: {
// Parse Candidate
printf("[Candidate]: ");
char* candidate = NULL;
size_t candidate_size = 0;
if(getline(&candidate, &candidate_size, stdin)) {
rtcAddRemoteCandidate(peer->pc, candidate, "0");
free(candidate);
}else {
printf("Error reading line\n");
break;
}
break;
}
case 3: {
// Send Message
if(!peer->connected) {
printf("** Channel is not Open **");
break;
}
printf("[Message]: ");
char* message = NULL;
size_t message_size = 0;
if(getline(&message, &message_size, stdin)) {
rtcSendMessage(peer->dc, message, -1);
free(message);
}else {
printf("Error reading line\n");
break;
}
break;
}
case 4: {
// Connection Info
if(!peer->connected) {
printf("** Channel is not Open **");
break;
}
char buffer[256];
if (rtcGetLocalAddress(peer->pc, buffer, 256) >= 0)
printf("Local address 1: %s\n", buffer);
if (rtcGetRemoteAddress(peer->pc, buffer, 256) >= 0)
printf("Remote address 1: %s\n", buffer);
else
printf("Could not get Candidate Pair Info\n");
break;
}
default: {
printf("** Invalid Command **");
break;
}
}
}
deletePeer(peer);
return 0;
}
static void descriptionCallback(const char *sdp, const char *type, void *ptr) {
// Peer *peer = (Peer *)ptr;
printf("Description %s:\n%s\n", "answerer", sdp);
}
static void candidateCallback(const char *cand, const char *mid, void *ptr) {
// Peer *peer = (Peer *)ptr;
printf("Candidate %s: %s\n", "answerer", cand);
}
static void stateChangeCallback(rtcState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->state = state;
printf("State %s: %s\n", "answerer", state_print(state));
}
static void gatheringStateCallback(rtcGatheringState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->gatheringState = state;
printf("Gathering state %s: %s\n", "answerer", rtcGatheringState_print(state));
}
static void closedCallback(void *ptr) {
Peer *peer = (Peer *)ptr;
peer->connected = false;
}
static void messageCallback(const char *message, int size, void *ptr) {
// Peer *peer = (Peer *)ptr;
if (size < 0) { // negative size indicates a null-terminated string
printf("Message %s: %s\n", "answerer", message);
} else {
printf("Message %s: [binary of size %d]\n", "answerer", size);
}
}
static void deletePeer(Peer *peer) {
if (peer) {
if (peer->dc)
rtcDeleteDataChannel(peer->dc);
if (peer->pc)
rtcDeletePeerConnection(peer->pc);
free(peer);
}
}
static void dataChannelCallback(int dc, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->dc = dc;
peer->connected = true;
rtcSetClosedCallback(dc, closedCallback);
rtcSetMessageCallback(dc, messageCallback);
char buffer[256];
if (rtcGetDataChannelLabel(dc, buffer, 256) >= 0)
printf("DataChannel %s: Received with label \"%s\"\n", "answerer", buffer);
}
int all_space(const char *str) {
while (*str) {
if (!isspace(*str++)) {
return 0;
}
}
return 1;
}
char* state_print(rtcState state) {
char *str = NULL;
switch (state) {
case RTC_NEW:
str = "RTC_NEW";
break;
case RTC_CONNECTING:
str = "RTC_CONNECTING";
break;
case RTC_CONNECTED:
str = "RTC_CONNECTED";
break;
case RTC_DISCONNECTED:
str = "RTC_DISCONNECTED";
break;
case RTC_FAILED:
str = "RTC_FAILED";
break;
case RTC_CLOSED:
str = "RTC_CLOSED";
break;
default:
break;
}
return str;
}
char* rtcGatheringState_print(rtcState state) {
char* str = NULL;
switch (state) {
case RTC_GATHERING_NEW:
str = "RTC_GATHERING_NEW";
break;
case RTC_GATHERING_INPROGRESS:
str = "RTC_GATHERING_INPROGRESS";
break;
case RTC_GATHERING_COMPLETE:
str = "RTC_GATHERING_COMPLETE";
break;
default:
break;
}
return str;
}

View File

@ -1,334 +0,0 @@
/**
* Copyright (c) 2020 Paul-Louis Ageneau
* Copyright (c) 2020 Stevedan Ogochukwu Omodolor
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <rtc/rtc.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for sleep
#include <ctype.h>
char* state_print(rtcState state);
char* rtcGatheringState_print(rtcState state);
typedef struct {
rtcState state;
rtcGatheringState gatheringState;
int pc;
int dc;
bool connected;
} Peer;
Peer *peer = NULL;
static void descriptionCallback(const char *sdp, const char *type, void *ptr);
static void candidateCallback(const char *cand, const char *mid, void *ptr);
static void stateChangeCallback(rtcState state, void *ptr);
static void gatheringStateCallback(rtcGatheringState state, void *ptr);
static void openCallback(void *ptr);
static void closedCallback(void *ptr);
static void messageCallback(const char *message, int size, void *ptr);
static void deletePeer(Peer *peer);
int all_space(const char *str);
int main(int argc, char **argv){
rtcInitLogger(RTC_LOG_DEBUG);
// Create peer
rtcConfiguration config;
memset(&config, 0, sizeof(config));
Peer *peer = (Peer *)malloc(sizeof(Peer));
if (!peer) {
printf("Error allocating memory for peer\n");
deletePeer(peer);
}
memset(peer, 0, sizeof(Peer));
printf("Peer created\n");
// Create peer connection
peer->pc = rtcCreatePeerConnection(&config);
rtcSetUserPointer(peer->pc, peer);
rtcSetLocalDescriptionCallback(peer->pc, descriptionCallback);
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
// Since this is the offere, we will create a datachannel
peer->dc = rtcCreateDataChannel(peer->pc, "test");
rtcSetOpenCallback(peer->dc, openCallback);
rtcSetClosedCallback(peer->dc, closedCallback);
rtcSetMessageCallback(peer->dc, messageCallback);
sleep(1);
bool exit = false;
while (!exit) {
printf("\n");
printf("***************************************************************************************\n");
// << endl
printf("* 0: Exit /"
" 1: Enter remote description /"
" 2: Enter remote candidate /"
" 3: Send message /"
" 4: Print Connection Info *\n"
"[Command]: ");
int command = -1;
int c;
if (scanf("%d", &command)) {
}else {
break;
}
while ((c = getchar()) != '\n' && c != EOF) { }
fflush(stdin);
switch (command) {
case 0: {
exit = true;
break;
}
case 1: {
// Parse Description
printf("[Description]: ");
char *line = NULL;
size_t len = 0;
size_t read = 0;
char *sdp = (char*) malloc(sizeof(char));
while ((read = getline(&line, &len, stdin)) != -1 && !all_space(line)) {
sdp = (char*) realloc (sdp,(strlen(sdp)+1) +strlen(line)+1);
strcat(sdp, line);
}
printf("%s\n",sdp);
rtcSetRemoteDescription(peer->pc, sdp, "answer");
free(sdp);
free(line);
break;
}
case 2: {
// Parse Candidate
printf("[Candidate]: ");
char* candidate = NULL;
size_t candidate_size = 0;
if(getline(&candidate, &candidate_size, stdin)) {
rtcAddRemoteCandidate(peer->pc, candidate, "0");
free(candidate);
}else {
printf("Error reading line\n");
break;
}
break;
}
case 3: {
// Send Message
if(!peer->connected) {
printf("** Channel is not Open **");
break;
}
printf("[Message]: ");
char* message = NULL;
size_t message_size = 0;
if(getline(&message, &message_size, stdin)) {
rtcSendMessage(peer->dc, message, -1);
free(message);
}else {
printf("Error reading line\n");
break;
}
break;
}
case 4: {
// Connection Info
if(!peer->connected) {
printf("** Channel is not Open **");
break;
}
char buffer[256];
if (rtcGetLocalAddress(peer->pc, buffer, 256) >= 0)
printf("Local address 1: %s\n", buffer);
if (rtcGetRemoteAddress(peer->pc, buffer, 256) >= 0)
printf("Remote address 1: %s\n", buffer);
else
printf("Could not get Candidate Pair Info\n");
break;
}
default: {
printf("** Invalid Command **");
break;
}
}
}
deletePeer(peer);
return 0;
}
static void descriptionCallback(const char *sdp, const char *type, void *ptr) {
// Peer *peer = (Peer *)ptr;
printf("Description %s:\n%s\n", "offerer", sdp);
}
static void candidateCallback(const char *cand, const char *mid, void *ptr) {
// Peer *peer = (Peer *)ptr;
printf("Candidate %s: %s\n", "offerer", cand);
}
static void stateChangeCallback(rtcState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->state = state;
printf("State %s: %s\n", "offerer", state_print(state));
}
static void gatheringStateCallback(rtcGatheringState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->gatheringState = state;
printf("Gathering state %s: %s\n", "offerer", rtcGatheringState_print(state));
}
static void openCallback(void *ptr) {
Peer *peer = (Peer *)ptr;
peer->connected = true;
char buffer[256];
if (rtcGetDataChannelLabel(peer->dc, buffer, 256) >= 0)
printf("DataChannel %s: Received with label \"%s\"\n","offerer", buffer);
}
static void closedCallback(void *ptr) {
Peer *peer = (Peer *)ptr;
peer->connected = false;
}
static void messageCallback(const char *message, int size, void *ptr) {
// Peer *peer = (Peer *)ptr;
if (size < 0) { // negative size indicates a null-terminated string
printf("Message %s: %s\n", "offerer", message);
} else {
printf("Message %s: [binary of size %d]\n", "offerer", size);
}
}
static void deletePeer(Peer *peer) {
if (peer) {
if (peer->dc)
rtcDeleteDataChannel(peer->dc);
if (peer->pc)
rtcDeletePeerConnection(peer->pc);
free(peer);
}
}
int all_space(const char *str) {
while (*str) {
if (!isspace(*str++)) {
return 0;
}
}
return 1;
}
char* state_print(rtcState state) {
char *str = NULL;
switch (state) {
case RTC_NEW:
str = "RTC_NEW";
break;
case RTC_CONNECTING:
str = "RTC_CONNECTING";
break;
case RTC_CONNECTED:
str = "RTC_CONNECTED";
break;
case RTC_DISCONNECTED:
str = "RTC_DISCONNECTED";
break;
case RTC_FAILED:
str = "RTC_FAILED";
break;
case RTC_CLOSED:
str = "RTC_CLOSED";
break;
default:
break;
}
return str;
}
char* rtcGatheringState_print(rtcState state) {
char* str = NULL;
switch (state) {
case RTC_GATHERING_NEW:
str = "RTC_GATHERING_NEW";
break;
case RTC_GATHERING_INPROGRESS:
str = "RTC_GATHERING_INPROGRESS";
break;
case RTC_GATHERING_COMPLETE:
str = "RTC_GATHERING_COMPLETE";
break;
default:
break;
}
return str;
}