123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840 |
- /**
- * Copyright (c) 2019-2020 Paul-Louis Ageneau
- *
- * 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 "icetransport.hpp"
- #include "configuration.hpp"
- #include "internals.hpp"
- #include "transport.hpp"
- #include <iostream>
- #include <random>
- #include <sstream>
- #ifdef _WIN32
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #else
- #include <arpa/inet.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <sys/socket.h>
- #endif
- #include <sys/types.h>
- using namespace std::chrono_literals;
- using std::chrono::system_clock;
- namespace rtc::impl {
- #if !USE_NICE // libjuice
- const int MAX_TURN_SERVERS_COUNT = 2;
- IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
- state_callback stateChangeCallback,
- gathering_state_callback gatheringStateChangeCallback)
- : Transport(nullptr, std::move(stateChangeCallback)), mRole(Description::Role::ActPass),
- mMid("0"), mGatheringState(GatheringState::New),
- mCandidateCallback(std::move(candidateCallback)),
- mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
- mAgent(nullptr, nullptr) {
- PLOG_DEBUG << "Initializing ICE transport (libjuice)";
- juice_log_level_t level;
- auto logger = plog::get();
- switch (logger ? logger->getMaxSeverity() : plog::none) {
- case plog::none:
- level = JUICE_LOG_LEVEL_NONE;
- break;
- case plog::fatal:
- level = JUICE_LOG_LEVEL_VERBOSE;
- break;
- case plog::error:
- level = JUICE_LOG_LEVEL_ERROR;
- break;
- case plog::warning:
- level = JUICE_LOG_LEVEL_WARN;
- break;
- case plog::info:
- case plog::debug: // juice debug is output as verbose
- level = JUICE_LOG_LEVEL_INFO;
- break;
- default:
- level = JUICE_LOG_LEVEL_VERBOSE;
- break;
- }
- juice_set_log_handler(IceTransport::LogCallback);
- juice_set_log_level(level);
- juice_config_t jconfig = {};
- jconfig.cb_state_changed = IceTransport::StateChangeCallback;
- jconfig.cb_candidate = IceTransport::CandidateCallback;
- jconfig.cb_gathering_done = IceTransport::GatheringDoneCallback;
- jconfig.cb_recv = IceTransport::RecvCallback;
- jconfig.user_ptr = this;
- if (config.enableIceTcp) {
- PLOG_WARNING << "ICE-TCP is not supported with libjuice";
- }
- if (config.enableIceUdpMux) {
- PLOG_DEBUG << "Enabling ICE UDP mux";
- jconfig.concurrency_mode = JUICE_CONCURRENCY_MODE_MUX;
- } else {
- jconfig.concurrency_mode = JUICE_CONCURRENCY_MODE_POLL;
- }
- // Randomize servers order
- std::vector<IceServer> servers = config.iceServers;
- auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
- std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
- // Pick a STUN server
- for (auto &server : servers) {
- if (!server.hostname.empty() && server.type == IceServer::Type::Stun) {
- if (server.port == 0)
- server.port = 3478; // STUN UDP port
- PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.port << "\"";
- jconfig.stun_server_host = server.hostname.c_str();
- jconfig.stun_server_port = server.port;
- break;
- }
- }
- juice_turn_server_t turn_servers[MAX_TURN_SERVERS_COUNT];
- std::memset(turn_servers, 0, sizeof(turn_servers));
- // Add TURN servers
- int k = 0;
- for (auto &server : servers) {
- if (!server.hostname.empty() && server.type == IceServer::Type::Turn) {
- if (server.port == 0)
- server.port = 3478; // TURN UDP port
- PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port << "\"";
- turn_servers[k].host = server.hostname.c_str();
- turn_servers[k].username = server.username.c_str();
- turn_servers[k].password = server.password.c_str();
- turn_servers[k].port = server.port;
- if (++k >= MAX_TURN_SERVERS_COUNT)
- break;
- }
- }
- jconfig.turn_servers = k > 0 ? turn_servers : nullptr;
- jconfig.turn_servers_count = k;
- // Bind address
- if (config.bindAddress) {
- jconfig.bind_address = config.bindAddress->c_str();
- }
- // Port range
- if (config.portRangeBegin > 1024 ||
- (config.portRangeEnd != 0 && config.portRangeEnd != 65535)) {
- jconfig.local_port_range_begin = config.portRangeBegin;
- jconfig.local_port_range_end = config.portRangeEnd;
- }
- // Create agent
- mAgent = decltype(mAgent)(juice_create(&jconfig), juice_destroy);
- if (!mAgent)
- throw std::runtime_error("Failed to create the ICE agent");
- }
- IceTransport::~IceTransport() {
- stop();
- mAgent.reset();
- }
- bool IceTransport::stop() { return Transport::stop(); }
- Description::Role IceTransport::role() const { return mRole; }
- Description IceTransport::getLocalDescription(Description::Type type) const {
- char sdp[JUICE_MAX_SDP_STRING_LEN];
- if (juice_get_local_description(mAgent.get(), sdp, JUICE_MAX_SDP_STRING_LEN) < 0)
- throw std::runtime_error("Failed to generate local SDP");
- // RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
- // setup:actpass.
- // See https://tools.ietf.org/html/rfc5763#section-5
- Description desc(string(sdp), type,
- type == Description::Type::Offer ? Description::Role::ActPass : mRole);
- desc.addIceOption("trickle");
- return desc;
- }
- void IceTransport::setRemoteDescription(const Description &description) {
- if (mRole == Description::Role::ActPass)
- mRole = description.role() == Description::Role::Active ? Description::Role::Passive
- : Description::Role::Active;
- if (mRole == description.role())
- throw std::logic_error("Incompatible roles with remote description");
- mMid = description.bundleMid();
- if (juice_set_remote_description(mAgent.get(),
- description.generateApplicationSdp("\r\n").c_str()) < 0)
- throw std::runtime_error("Failed to parse ICE settings from remote SDP");
- }
- bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
- // Don't try to pass unresolved candidates for more safety
- if (!candidate.isResolved())
- return false;
- return juice_add_remote_candidate(mAgent.get(), string(candidate).c_str()) >= 0;
- }
- void IceTransport::gatherLocalCandidates(string mid) {
- mMid = std::move(mid);
- // Change state now as candidates calls can be synchronous
- changeGatheringState(GatheringState::InProgress);
- if (juice_gather_candidates(mAgent.get()) < 0) {
- throw std::runtime_error("Failed to gather local ICE candidates");
- }
- }
- optional<string> IceTransport::getLocalAddress() const {
- char str[JUICE_MAX_ADDRESS_STRING_LEN];
- if (juice_get_selected_addresses(mAgent.get(), str, JUICE_MAX_ADDRESS_STRING_LEN, NULL, 0) ==
- 0) {
- return std::make_optional(string(str));
- }
- return nullopt;
- }
- optional<string> IceTransport::getRemoteAddress() const {
- char str[JUICE_MAX_ADDRESS_STRING_LEN];
- if (juice_get_selected_addresses(mAgent.get(), NULL, 0, str, JUICE_MAX_ADDRESS_STRING_LEN) ==
- 0) {
- return std::make_optional(string(str));
- }
- return nullopt;
- }
- bool IceTransport::getSelectedCandidatePair(Candidate *local, Candidate *remote) {
- char sdpLocal[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
- char sdpRemote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN];
- if (juice_get_selected_candidates(mAgent.get(), sdpLocal, JUICE_MAX_CANDIDATE_SDP_STRING_LEN,
- sdpRemote, JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0) {
- if (local) {
- *local = Candidate(sdpLocal, mMid);
- local->resolve(Candidate::ResolveMode::Simple);
- }
- if (remote) {
- *remote = Candidate(sdpRemote, mMid);
- remote->resolve(Candidate::ResolveMode::Simple);
- }
- return true;
- }
- return false;
- }
- bool IceTransport::send(message_ptr message) {
- auto s = state();
- if (!message || (s != State::Connected && s != State::Completed))
- return false;
- PLOG_VERBOSE << "Send size=" << message->size();
- return outgoing(message);
- }
- bool IceTransport::outgoing(message_ptr message) {
- // Explicit Congestion Notification takes the least-significant 2 bits of the DS field
- int ds = int(message->dscp << 2);
- return juice_send_diffserv(mAgent.get(), reinterpret_cast<const char *>(message->data()),
- message->size(), ds) >= 0;
- }
- void IceTransport::changeGatheringState(GatheringState state) {
- if (mGatheringState.exchange(state) != state)
- mGatheringStateChangeCallback(mGatheringState);
- }
- void IceTransport::processStateChange(unsigned int state) {
- switch (state) {
- case JUICE_STATE_DISCONNECTED:
- changeState(State::Disconnected);
- break;
- case JUICE_STATE_CONNECTING:
- changeState(State::Connecting);
- break;
- case JUICE_STATE_CONNECTED:
- changeState(State::Connected);
- break;
- case JUICE_STATE_COMPLETED:
- changeState(State::Completed);
- break;
- case JUICE_STATE_FAILED:
- changeState(State::Failed);
- break;
- };
- }
- void IceTransport::processCandidate(const string &candidate) {
- mCandidateCallback(Candidate(candidate, mMid));
- }
- void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); }
- void IceTransport::StateChangeCallback(juice_agent_t *, juice_state_t state, void *user_ptr) {
- auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
- try {
- iceTransport->processStateChange(static_cast<unsigned int>(state));
- } catch (const std::exception &e) {
- PLOG_WARNING << e.what();
- }
- }
- void IceTransport::CandidateCallback(juice_agent_t *, const char *sdp, void *user_ptr) {
- auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
- try {
- iceTransport->processCandidate(sdp);
- } catch (const std::exception &e) {
- PLOG_WARNING << e.what();
- }
- }
- void IceTransport::GatheringDoneCallback(juice_agent_t *, void *user_ptr) {
- auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
- try {
- iceTransport->processGatheringDone();
- } catch (const std::exception &e) {
- PLOG_WARNING << e.what();
- }
- }
- void IceTransport::RecvCallback(juice_agent_t *, const char *data, size_t size, void *user_ptr) {
- auto iceTransport = static_cast<rtc::impl::IceTransport *>(user_ptr);
- try {
- PLOG_VERBOSE << "Incoming size=" << size;
- auto b = reinterpret_cast<const byte *>(data);
- iceTransport->incoming(make_message(b, b + size));
- } catch (const std::exception &e) {
- PLOG_WARNING << e.what();
- }
- }
- void IceTransport::LogCallback(juice_log_level_t level, const char *message) {
- plog::Severity severity;
- switch (level) {
- case JUICE_LOG_LEVEL_FATAL:
- severity = plog::fatal;
- break;
- case JUICE_LOG_LEVEL_ERROR:
- severity = plog::error;
- break;
- case JUICE_LOG_LEVEL_WARN:
- severity = plog::warning;
- break;
- case JUICE_LOG_LEVEL_INFO:
- severity = plog::info;
- break;
- default:
- severity = plog::verbose; // libjuice debug as verbose
- break;
- }
- PLOG(severity) << "juice: " << message;
- }
- #else // USE_NICE == 1
- IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
- state_callback stateChangeCallback,
- gathering_state_callback gatheringStateChangeCallback)
- : Transport(nullptr, std::move(stateChangeCallback)), mRole(Description::Role::ActPass),
- mMid("0"), mGatheringState(GatheringState::New),
- mCandidateCallback(std::move(candidateCallback)),
- mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
- mNiceAgent(nullptr, nullptr), mMainLoop(nullptr, nullptr), mOutgoingDscp(0) {
- PLOG_DEBUG << "Initializing ICE transport (libnice)";
- g_log_set_handler("libnice", G_LOG_LEVEL_MASK, LogCallback, this);
- IF_PLOG(plog::verbose) {
- nice_debug_enable(false); // do not output STUN debug messages
- }
- mMainLoop = decltype(mMainLoop)(g_main_loop_new(nullptr, FALSE), g_main_loop_unref);
- if (!mMainLoop)
- std::runtime_error("Failed to create the main loop");
- // RFC 8445: The nomination process that was referred to as "aggressive nomination" in RFC 5245
- // has been deprecated in this specification.
- // libnice defaults to aggressive nomation therefore we change to regular nomination.
- // See https://gitlab.freedesktop.org/libnice/libnice/-/merge_requests/125
- NiceAgentOption flags = NICE_AGENT_OPTION_REGULAR_NOMINATION;
- // Create agent
- mNiceAgent = decltype(mNiceAgent)(
- nice_agent_new_full(
- g_main_loop_get_context(mMainLoop.get()),
- NICE_COMPATIBILITY_RFC5245, // RFC 5245 was obsoleted by RFC 8445 but this should be OK
- flags),
- g_object_unref);
- if (!mNiceAgent)
- throw std::runtime_error("Failed to create the nice agent");
- mMainLoopThread = std::thread(g_main_loop_run, mMainLoop.get());
- mStreamId = nice_agent_add_stream(mNiceAgent.get(), 1);
- if (!mStreamId)
- throw std::runtime_error("Failed to add a stream");
- g_object_set(G_OBJECT(mNiceAgent.get()), "controlling-mode", TRUE, nullptr); // decided later
- g_object_set(G_OBJECT(mNiceAgent.get()), "ice-udp", TRUE, nullptr);
- g_object_set(G_OBJECT(mNiceAgent.get()), "ice-tcp", config.enableIceTcp ? TRUE : FALSE,
- nullptr);
- // RFC 8445: Agents MUST NOT use an RTO value smaller than 500 ms.
- g_object_set(G_OBJECT(mNiceAgent.get()), "stun-initial-timeout", 500, nullptr);
- g_object_set(G_OBJECT(mNiceAgent.get()), "stun-max-retransmissions", 3, nullptr);
- // RFC 8445: ICE agents SHOULD use a default Ta value, 50 ms, but MAY use another value based on
- // the characteristics of the associated data.
- g_object_set(G_OBJECT(mNiceAgent.get()), "stun-pacing-timer", 25, nullptr);
- g_object_set(G_OBJECT(mNiceAgent.get()), "upnp", FALSE, nullptr);
- g_object_set(G_OBJECT(mNiceAgent.get()), "upnp-timeout", 200, nullptr);
- // Proxy
- if (config.proxyServer.has_value()) {
- ProxyServer proxyServer = config.proxyServer.value();
- g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-type", proxyServer.type, nullptr);
- g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-ip", proxyServer.hostname.c_str(), nullptr);
- g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-port", proxyServer.port, nullptr);
- g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-username", proxyServer.username.c_str(),
- nullptr);
- g_object_set(G_OBJECT(mNiceAgent.get()), "proxy-password", proxyServer.password.c_str(),
- nullptr);
- }
- if (config.enableIceUdpMux) {
- PLOG_WARNING << "ICE UDP mux is not available with libnice";
- }
- // Randomize order
- std::vector<IceServer> servers = config.iceServers;
- auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
- std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
- // Add one STUN server
- bool success = false;
- for (auto &server : servers) {
- if (server.hostname.empty())
- continue;
- if (server.type != IceServer::Type::Stun)
- continue;
- if (server.port == 0)
- server.port = 3478; // STUN UDP port
- struct addrinfo hints = {};
- hints.ai_family = AF_INET; // IPv4
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_protocol = IPPROTO_UDP;
- hints.ai_flags = AI_ADDRCONFIG;
- struct addrinfo *result = nullptr;
- if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints,
- &result) != 0) {
- PLOG_WARNING << "Unable to resolve STUN server address: " << server.hostname << ':'
- << server.port;
- continue;
- }
- for (auto p = result; p; p = p->ai_next) {
- if (p->ai_family == AF_INET) {
- char nodebuffer[MAX_NUMERICNODE_LEN];
- char servbuffer[MAX_NUMERICSERV_LEN];
- if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
- servbuffer, MAX_NUMERICSERV_LEN,
- NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
- PLOG_INFO << "Using STUN server \"" << server.hostname << ":" << server.port
- << "\"";
- g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server", nodebuffer, nullptr);
- g_object_set(G_OBJECT(mNiceAgent.get()), "stun-server-port",
- std::stoul(servbuffer), nullptr);
- success = true;
- break;
- }
- }
- }
- freeaddrinfo(result);
- if (success)
- break;
- }
- // Add TURN servers
- for (auto &server : servers) {
- if (server.hostname.empty())
- continue;
- if (server.type != IceServer::Type::Turn)
- continue;
- if (server.port == 0)
- server.port = server.relayType == IceServer::RelayType::TurnTls ? 5349 : 3478;
- struct addrinfo hints = {};
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype =
- server.relayType == IceServer::RelayType::TurnUdp ? SOCK_DGRAM : SOCK_STREAM;
- hints.ai_protocol =
- server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP;
- hints.ai_flags = AI_ADDRCONFIG;
- struct addrinfo *result = nullptr;
- if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints,
- &result) != 0) {
- PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':'
- << server.port;
- continue;
- }
- for (auto p = result; p; p = p->ai_next) {
- if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
- char nodebuffer[MAX_NUMERICNODE_LEN];
- char servbuffer[MAX_NUMERICSERV_LEN];
- if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
- servbuffer, MAX_NUMERICSERV_LEN,
- NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
- PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port
- << "\"";
- NiceRelayType niceRelayType;
- switch (server.relayType) {
- case IceServer::RelayType::TurnTcp:
- niceRelayType = NICE_RELAY_TYPE_TURN_TCP;
- break;
- case IceServer::RelayType::TurnTls:
- niceRelayType = NICE_RELAY_TYPE_TURN_TLS;
- break;
- default:
- niceRelayType = NICE_RELAY_TYPE_TURN_UDP;
- break;
- }
- nice_agent_set_relay_info(mNiceAgent.get(), mStreamId, 1, nodebuffer,
- std::stoul(servbuffer), server.username.c_str(),
- server.password.c_str(), niceRelayType);
- }
- }
- }
- freeaddrinfo(result);
- }
- g_signal_connect(G_OBJECT(mNiceAgent.get()), "component-state-changed",
- G_CALLBACK(StateChangeCallback), this);
- g_signal_connect(G_OBJECT(mNiceAgent.get()), "new-candidate-full",
- G_CALLBACK(CandidateCallback), this);
- g_signal_connect(G_OBJECT(mNiceAgent.get()), "candidate-gathering-done",
- G_CALLBACK(GatheringDoneCallback), this);
- nice_agent_set_stream_name(mNiceAgent.get(), mStreamId, "application");
- nice_agent_set_port_range(mNiceAgent.get(), mStreamId, 1, config.portRangeBegin,
- config.portRangeEnd);
- nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(mMainLoop.get()),
- RecvCallback, this);
- }
- IceTransport::~IceTransport() { stop(); }
- bool IceTransport::stop() {
- if (mTimeoutId) {
- g_source_remove(mTimeoutId);
- mTimeoutId = 0;
- }
- if (!Transport::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;
- }
- Description::Role IceTransport::role() const { return mRole; }
- Description IceTransport::getLocalDescription(Description::Type type) const {
- // RFC 8445: The initiating agent that started the ICE processing MUST take the controlling
- // role, and the other MUST take the controlled role.
- g_object_set(G_OBJECT(mNiceAgent.get()), "controlling-mode",
- type == Description::Type::Offer ? TRUE : FALSE, nullptr);
- unique_ptr<gchar[], void (*)(void *)> sdp(nice_agent_generate_local_sdp(mNiceAgent.get()),
- g_free);
- // RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
- // setup:actpass.
- // See https://tools.ietf.org/html/rfc5763#section-5
- Description desc(string(sdp.get()), type,
- type == Description::Type::Offer ? Description::Role::ActPass : mRole);
- desc.addIceOption("trickle");
- return desc;
- }
- void IceTransport::setRemoteDescription(const Description &description) {
- if (mRole == Description::Role::ActPass)
- mRole = description.role() == Description::Role::Active ? Description::Role::Passive
- : Description::Role::Active;
- if (mRole == description.role())
- throw std::logic_error("Incompatible roles with remote description");
- mMid = description.bundleMid();
- mTrickleTimeout = !description.ended() ? 30s : 0s;
- // Warning: libnice expects "\n" as end of line
- if (nice_agent_parse_remote_sdp(mNiceAgent.get(),
- description.generateApplicationSdp("\n").c_str()) < 0)
- throw std::runtime_error("Failed to parse ICE settings from remote SDP");
- }
- bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
- // Don't try to pass unresolved candidates to libnice for more safety
- if (!candidate.isResolved())
- return false;
- // Warning: the candidate string must start with "a=candidate:" and it must not end with a
- // newline or whitespace, else libnice will reject it.
- string sdp(candidate);
- NiceCandidate *cand =
- nice_agent_parse_remote_candidate_sdp(mNiceAgent.get(), mStreamId, sdp.c_str());
- if (!cand) {
- PLOG_WARNING << "Rejected ICE candidate: " << sdp;
- return false;
- }
- GSList *list = g_slist_append(nullptr, cand);
- int ret = nice_agent_set_remote_candidates(mNiceAgent.get(), mStreamId, 1, list);
- g_slist_free_full(list, reinterpret_cast<GDestroyNotify>(nice_candidate_free));
- return ret > 0;
- }
- void IceTransport::gatherLocalCandidates(string mid) {
- mMid = std::move(mid);
- // Change state now as candidates calls can be synchronous
- changeGatheringState(GatheringState::InProgress);
- if (!nice_agent_gather_candidates(mNiceAgent.get(), mStreamId)) {
- throw std::runtime_error("Failed to gather local ICE candidates");
- }
- }
- optional<string> IceTransport::getLocalAddress() const {
- NiceCandidate *local = nullptr;
- NiceCandidate *remote = nullptr;
- if (nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &local, &remote)) {
- return std::make_optional(AddressToString(local->addr));
- }
- return nullopt;
- }
- optional<string> IceTransport::getRemoteAddress() const {
- NiceCandidate *local = nullptr;
- NiceCandidate *remote = nullptr;
- if (nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &local, &remote)) {
- return std::make_optional(AddressToString(remote->addr));
- }
- return nullopt;
- }
- bool IceTransport::send(message_ptr message) {
- auto s = state();
- if (!message || (s != State::Connected && s != State::Completed))
- return false;
- PLOG_VERBOSE << "Send size=" << message->size();
- return outgoing(message);
- }
- bool IceTransport::outgoing(message_ptr message) {
- std::lock_guard lock(mOutgoingMutex);
- if (mOutgoingDscp != message->dscp) {
- mOutgoingDscp = message->dscp;
- // Explicit Congestion Notification takes the least-significant 2 bits of the DS field
- int ds = int(message->dscp << 2);
- nice_agent_set_stream_tos(mNiceAgent.get(), mStreamId, ds); // ToS is the legacy name for DS
- }
- return nice_agent_send(mNiceAgent.get(), mStreamId, 1, message->size(),
- reinterpret_cast<const char *>(message->data())) >= 0;
- }
- void IceTransport::changeGatheringState(GatheringState state) {
- if (mGatheringState.exchange(state) != state)
- mGatheringStateChangeCallback(mGatheringState);
- }
- void IceTransport::processTimeout() {
- PLOG_WARNING << "ICE timeout";
- mTimeoutId = 0;
- changeState(State::Failed);
- }
- void IceTransport::processCandidate(const string &candidate) {
- mCandidateCallback(Candidate(candidate, mMid));
- }
- void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); }
- void IceTransport::processStateChange(unsigned int state) {
- if (state == NICE_COMPONENT_STATE_FAILED && mTrickleTimeout.count() > 0) {
- if (mTimeoutId)
- g_source_remove(mTimeoutId);
- mTimeoutId = g_timeout_add(mTrickleTimeout.count() /* ms */, TimeoutCallback, this);
- return;
- }
- if (state == NICE_COMPONENT_STATE_CONNECTED && mTimeoutId) {
- g_source_remove(mTimeoutId);
- mTimeoutId = 0;
- }
- switch (state) {
- case NICE_COMPONENT_STATE_DISCONNECTED:
- changeState(State::Disconnected);
- break;
- case NICE_COMPONENT_STATE_CONNECTING:
- changeState(State::Connecting);
- break;
- case NICE_COMPONENT_STATE_CONNECTED:
- changeState(State::Connected);
- break;
- case NICE_COMPONENT_STATE_READY:
- changeState(State::Completed);
- break;
- case NICE_COMPONENT_STATE_FAILED:
- changeState(State::Failed);
- break;
- };
- }
- string IceTransport::AddressToString(const NiceAddress &addr) {
- char buffer[NICE_ADDRESS_STRING_LEN];
- nice_address_to_string(&addr, buffer);
- unsigned int port = nice_address_get_port(&addr);
- std::ostringstream ss;
- ss << buffer << ":" << port;
- return ss.str();
- }
- void IceTransport::CandidateCallback(NiceAgent *agent, NiceCandidate *candidate,
- gpointer userData) {
- auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
- gchar *cand = nice_agent_generate_local_candidate_sdp(agent, candidate);
- try {
- iceTransport->processCandidate(cand);
- } catch (const std::exception &e) {
- PLOG_WARNING << e.what();
- }
- g_free(cand);
- }
- void IceTransport::GatheringDoneCallback(NiceAgent * /*agent*/, guint /*streamId*/,
- gpointer userData) {
- auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
- try {
- iceTransport->processGatheringDone();
- } catch (const std::exception &e) {
- PLOG_WARNING << e.what();
- }
- }
- void IceTransport::StateChangeCallback(NiceAgent * /*agent*/, guint /*streamId*/,
- guint /*componentId*/, guint state, gpointer userData) {
- auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
- try {
- iceTransport->processStateChange(state);
- } catch (const std::exception &e) {
- PLOG_WARNING << e.what();
- }
- }
- void IceTransport::RecvCallback(NiceAgent * /*agent*/, guint /*streamId*/, guint /*componentId*/,
- guint len, gchar *buf, gpointer userData) {
- auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
- try {
- PLOG_VERBOSE << "Incoming size=" << len;
- auto b = reinterpret_cast<byte *>(buf);
- iceTransport->incoming(make_message(b, b + len));
- } catch (const std::exception &e) {
- PLOG_WARNING << e.what();
- }
- }
- gboolean IceTransport::TimeoutCallback(gpointer userData) {
- auto iceTransport = static_cast<rtc::impl::IceTransport *>(userData);
- try {
- iceTransport->processTimeout();
- } catch (const std::exception &e) {
- PLOG_WARNING << e.what();
- }
- return FALSE;
- }
- void IceTransport::LogCallback(const gchar * /*logDomain*/, GLogLevelFlags logLevel,
- const gchar *message, gpointer /*userData*/) {
- plog::Severity severity;
- unsigned int flags = logLevel & G_LOG_LEVEL_MASK;
- if (flags & G_LOG_LEVEL_ERROR)
- severity = plog::fatal;
- else if (flags & G_LOG_LEVEL_CRITICAL)
- severity = plog::error;
- else if (flags & G_LOG_LEVEL_WARNING)
- severity = plog::warning;
- else if (flags & G_LOG_LEVEL_MESSAGE)
- severity = plog::info;
- else if (flags & G_LOG_LEVEL_INFO)
- severity = plog::info;
- else
- severity = plog::verbose; // libnice debug as verbose
- PLOG(severity) << "nice: " << message;
- }
- bool IceTransport::getSelectedCandidatePair(Candidate *local, Candidate *remote) {
- NiceCandidate *niceLocal, *niceRemote;
- if (!nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &niceLocal, &niceRemote))
- return false;
- gchar *sdpLocal = nice_agent_generate_local_candidate_sdp(mNiceAgent.get(), niceLocal);
- if (local)
- *local = Candidate(sdpLocal, mMid);
- g_free(sdpLocal);
- gchar *sdpRemote = nice_agent_generate_local_candidate_sdp(mNiceAgent.get(), niceRemote);
- if (remote)
- *remote = Candidate(sdpRemote, mMid);
- g_free(sdpRemote);
- if (local)
- local->resolve(Candidate::ResolveMode::Simple);
- if (remote)
- remote->resolve(Candidate::ResolveMode::Simple);
- return true;
- }
- #endif
- } // namespace rtc::impl
|