blob: 302afb1e6fae3b25901b50f365e8f39a782e09e7 [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
Adrien Béraudcb753622023-07-17 22:32:49 -04004 * This program is free software: you can redistribute it and/or modify
Adrien Béraud612b55b2023-05-29 10:42:04 -04005 * it under the terms of the GNU General Public License as published by
Adrien Béraudcb753622023-07-17 22:32:49 -04006 * the Free Software Foundation, either version 3 of the License, or
Adrien Béraud612b55b2023-05-29 10:42:04 -04007 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Adrien Béraudcb753622023-07-17 22:32:49 -040011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Adrien Béraud612b55b2023-05-29 10:42:04 -040012 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
Adrien Béraudcb753622023-07-17 22:32:49 -040015 * along with this program. If not, see <https://www.gnu.org/licenses/>.
Adrien Béraud612b55b2023-05-29 10:42:04 -040016 */
Adrien Béraud612b55b2023-05-29 10:42:04 -040017#include "ice_transport.h"
Adrien Béraud6de3f882023-07-06 12:56:29 -040018#include "ice_transport_factory.h"
Adrien Béraud612b55b2023-05-29 10:42:04 -040019#include "ice_socket.h"
20#include "sip_utils.h"
21#include "string_utils.h"
22#include "upnp/upnp_control.h"
23#include "transport/peer_channel.h"
24#include "tracepoint/tracepoint.h"
25
26#include <opendht/logger.h>
27#include <opendht/utils.h>
28
Adrien Béraud9e0f84f2023-07-27 16:02:21 -040029extern "C" {
Adrien Béraud612b55b2023-05-29 10:42:04 -040030#include <pjlib.h>
Adrien Béraud9e0f84f2023-07-27 16:02:21 -040031}
Adrien Béraud612b55b2023-05-29 10:42:04 -040032
33#include <map>
34#include <atomic>
35#include <queue>
36#include <mutex>
37#include <condition_variable>
38#include <thread>
39#include <utility>
40#include <tuple>
41#include <algorithm>
42#include <sstream>
43#include <chrono>
Adrien Béraud67ff0772023-07-12 11:22:29 -040044#include <memory>
Adrien Béraud612b55b2023-05-29 10:42:04 -040045#include <cerrno>
46
47#include "pj/limits.h"
48
49#define TRY(ret) \
50 do { \
51 if ((ret) != PJ_SUCCESS) \
52 throw std::runtime_error(#ret " failed"); \
53 } while (0)
54
55// Validate that the component ID is within the expected range
56#define ASSERT_COMP_ID(compId, compCount) \
57 do { \
58 if ((compId) == 0 or (compId) > (compCount)) \
59 throw std::runtime_error("Invalid component ID " + (std::to_string(compId))); \
60 } while (0)
61
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040062namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040063
64static constexpr unsigned STUN_MAX_PACKET_SIZE {8192};
65static constexpr uint16_t IPV6_HEADER_SIZE = 40; ///< Size in bytes of IPV6 packet header
66static constexpr uint16_t IPV4_HEADER_SIZE = 20; ///< Size in bytes of IPV4 packet header
67static constexpr int MAX_CANDIDATES {32};
68static constexpr int MAX_DESTRUCTION_TIMEOUT {3000};
69static constexpr int HANDLE_EVENT_DURATION {500};
70
71//==============================================================================
72
73using MutexGuard = std::lock_guard<std::mutex>;
74using MutexLock = std::unique_lock<std::mutex>;
75using namespace upnp;
76
77//==============================================================================
78
79class IceLock
80{
81 pj_grp_lock_t* lk_;
82
83public:
84 IceLock(pj_ice_strans* strans)
85 : lk_(pj_ice_strans_get_grp_lock(strans))
86 {
87 lock();
88 }
89
90 ~IceLock() { unlock(); }
91
92 void lock() { if (lk_) pj_grp_lock_acquire(lk_); }
93
94 void unlock() { if (lk_) pj_grp_lock_release(lk_); }
95};
96
97class IceTransport::Impl
98{
99public:
Adrien Béraud89933c12023-07-26 14:53:30 -0400100 Impl(std::string_view name, const std::shared_ptr<Logger>& logger);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400101 ~Impl();
102
103 void initIceInstance(const IceTransportOptions& options);
104
105 void onComplete(pj_ice_strans* ice_st, pj_ice_strans_op op, pj_status_t status);
106
107 void onReceiveData(unsigned comp_id, void* pkt, pj_size_t size);
108
109 /**
110 * Set/change transport role as initiator.
111 * Should be called before start method.
112 */
113 bool setInitiatorSession();
114
115 /**
116 * Set/change transport role as slave.
117 * Should be called before start method.
118 */
119 bool setSlaveSession();
120 bool createIceSession(pj_ice_sess_role role);
121
122 void getUFragPwd();
123
124 std::string link() const;
125
126 bool _isInitialized() const;
127 bool _isStarted() const;
128 bool _isRunning() const;
129 bool _isFailed() const;
130 bool _waitForInitialization(std::chrono::milliseconds timeout);
131
132 const pj_ice_sess_cand* getSelectedCandidate(unsigned comp_id, bool remote) const;
133 IpAddr getLocalAddress(unsigned comp_id) const;
134 IpAddr getRemoteAddress(unsigned comp_id) const;
135 static const char* getCandidateType(const pj_ice_sess_cand* cand);
136 bool isTcpEnabled() const { return config_.protocol == PJ_ICE_TP_TCP; }
137 bool addStunConfig(int af);
138 void requestUpnpMappings();
139 bool hasUpnp() const;
140 // Take a list of address pairs (local/public) and add them as
141 // reflexive candidates using STUN config.
142 void addServerReflexiveCandidates(const std::vector<std::pair<IpAddr, IpAddr>>& addrList);
143 // Generate server reflexive candidates using the published (DHT/Account) address
144 std::vector<std::pair<IpAddr, IpAddr>> setupGenericReflexiveCandidates();
145 // Generate server reflexive candidates using UPNP mappings.
146 std::vector<std::pair<IpAddr, IpAddr>> setupUpnpReflexiveCandidates();
147 void setDefaultRemoteAddress(unsigned comp_id, const IpAddr& addr);
148 IpAddr getDefaultRemoteAddress(unsigned comp_id) const;
149 bool handleEvents(unsigned max_msec);
150 int flushTimerHeapAndIoQueue();
151 int checkEventQueue(int maxEventToPoll);
152
153 std::shared_ptr<dht::log::Logger> logger_ {};
Amna81221ad2023-09-14 17:33:26 -0400154 std::shared_ptr<dhtnet::IceTransportFactory> factory {};
Adrien Béraud612b55b2023-05-29 10:42:04 -0400155
156 std::condition_variable_any iceCV_ {};
157
158 std::string sessionName_ {};
159 std::unique_ptr<pj_pool_t, decltype(&pj_pool_release)> pool_ {nullptr, pj_pool_release};
160 bool isTcp_ {false};
161 bool upnpEnabled_ {false};
162 IceTransportCompleteCb on_initdone_cb_ {};
163 IceTransportCompleteCb on_negodone_cb_ {};
164 pj_ice_strans* icest_ {nullptr};
165 unsigned streamsCount_ {0};
166 unsigned compCountPerStream_ {0};
167 unsigned compCount_ {0};
168 std::string local_ufrag_ {};
169 std::string local_pwd_ {};
170 pj_sockaddr remoteAddr_ {};
171 pj_ice_strans_cfg config_ {};
172 //std::string last_errmsg_ {};
173
174 std::atomic_bool is_stopped_ {false};
175
Adrien Béraud21d5f462023-08-27 12:06:21 -0400176 using Packet = std::vector<uint8_t>;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400177
178 struct ComponentIO
179 {
180 std::mutex mutex;
181 std::condition_variable cv;
182 std::deque<Packet> queue;
183 IceRecvCb recvCb;
184 };
185
186 // NOTE: Component IDs start from 1, while these three vectors
187 // are indexed from 0. Conversion from ID to vector index must
188 // be done properly.
189 std::vector<ComponentIO> compIO_ {};
190 std::vector<PeerChannel> peerChannels_ {};
191 std::vector<IpAddr> iceDefaultRemoteAddr_;
192
193 // ICE controlling role. True for controller agents and false for
194 // controlled agents
195 std::atomic_bool initiatorSession_ {true};
196
197 // Local/Public addresses used by the account owning the ICE instance.
198 IpAddr accountLocalAddr_ {};
199 IpAddr accountPublicAddr_ {};
200
201 // STUN and TURN servers
202 std::vector<StunServerInfo> stunServers_;
203 std::vector<TurnServerInfo> turnServers_;
204
205 /**
206 * Returns the IP of each candidate for a given component in the ICE session
207 */
208 struct LocalCandidate
209 {
210 IpAddr addr;
211 pj_ice_cand_transport transport;
212 };
213
214 std::shared_ptr<upnp::Controller> upnp_ {};
215 std::mutex upnpMutex_ {};
216 std::map<Mapping::key_t, Mapping> upnpMappings_;
217 std::mutex upnpMappingsMutex_ {};
218
219 bool onlyIPv4Private_ {true};
220
221 // IO/Timer events are handled by following thread
222 std::thread thread_ {};
223 std::atomic_bool threadTerminateFlags_ {false};
224
225 // Wait data on components
226 mutable std::mutex sendDataMutex_ {};
227 std::condition_variable waitDataCv_ = {};
228 pj_size_t lastSentLen_ {0};
229 bool destroying_ {false};
230 onShutdownCb scb {};
231
232 void cancelOperations()
233 {
234 for (auto& c : peerChannels_)
235 c.stop();
236 std::lock_guard<std::mutex> lk(sendDataMutex_);
237 destroying_ = true;
238 waitDataCv_.notify_all();
239 }
240};
241
242//==============================================================================
243
244/**
245 * Add stun/turn configuration or default host as candidates
246 */
247
248static void
Sébastien Blincf569402023-07-27 09:46:40 -0400249add_stun_server(pj_pool_t& pool, pj_ice_strans_cfg& cfg, const StunServerInfo& info, const std::shared_ptr<dht::log::Logger>& logger)
Adrien Béraud612b55b2023-05-29 10:42:04 -0400250{
251 if (cfg.stun_tp_cnt >= PJ_ICE_MAX_STUN)
252 throw std::runtime_error("Too many STUN configurations");
253
254 IpAddr ip {info.uri};
255
256 // Given URI cannot be DNS resolved or not IPv4 or IPv6?
257 // This prevents a crash into PJSIP when ip.toString() is called.
258 if (ip.getFamily() == AF_UNSPEC) {
259 /*JAMI_DBG("[ice (%s)] STUN server '%s' not used, unresolvable address",
260 (cfg.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP"),
261 info.uri.c_str());*/
262 return;
263 }
264
265 auto& stun = cfg.stun_tp[cfg.stun_tp_cnt++];
266 pj_ice_strans_stun_cfg_default(&stun);
267 pj_strdup2_with_null(&pool, &stun.server, ip.toString().c_str());
268 stun.af = ip.getFamily();
269 if (!(stun.port = ip.getPort()))
270 stun.port = PJ_STUN_PORT;
271 stun.cfg.max_pkt_size = STUN_MAX_PACKET_SIZE;
272 stun.conn_type = cfg.stun.conn_type;
Sébastien Blincf569402023-07-27 09:46:40 -0400273 if (logger)
274 logger->debug("added stun server '{}', port {}", pj_strbuf(&stun.server), stun.port);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400275}
276
277static void
Sébastien Blincf569402023-07-27 09:46:40 -0400278add_turn_server(pj_pool_t& pool, pj_ice_strans_cfg& cfg, const TurnServerInfo& info, const std::shared_ptr<dht::log::Logger>& logger)
Adrien Béraud612b55b2023-05-29 10:42:04 -0400279{
280 if (cfg.turn_tp_cnt >= PJ_ICE_MAX_TURN)
281 throw std::runtime_error("Too many TURN servers");
282
283 IpAddr ip {info.uri};
284
285 // Same comment as add_stun_server()
286 if (ip.getFamily() == AF_UNSPEC) {
Sébastien Blincf569402023-07-27 09:46:40 -0400287 if (logger)
288 logger->debug("TURN server '{}' not used, unresolvable address", info.uri);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400289 return;
290 }
291
292 auto& turn = cfg.turn_tp[cfg.turn_tp_cnt++];
293 pj_ice_strans_turn_cfg_default(&turn);
294 pj_strdup2_with_null(&pool, &turn.server, ip.toString().c_str());
295 turn.af = ip.getFamily();
296 if (!(turn.port = ip.getPort()))
297 turn.port = PJ_STUN_PORT;
298 turn.cfg.max_pkt_size = STUN_MAX_PACKET_SIZE;
299 turn.conn_type = cfg.turn.conn_type;
300
301 // Authorization (only static plain password supported yet)
302 if (not info.password.empty()) {
303 turn.auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
304 turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
305 pj_strset(&turn.auth_cred.data.static_cred.realm,
306 (char*) info.realm.c_str(),
307 info.realm.size());
308 pj_strset(&turn.auth_cred.data.static_cred.username,
309 (char*) info.username.c_str(),
310 info.username.size());
311 pj_strset(&turn.auth_cred.data.static_cred.data,
312 (char*) info.password.c_str(),
313 info.password.size());
314 }
Sébastien Blincf569402023-07-27 09:46:40 -0400315 if (logger)
316 logger->debug("added turn server '{}', port {}", pj_strbuf(&turn.server), turn.port);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400317}
318
319//==============================================================================
320
Adrien Béraud89933c12023-07-26 14:53:30 -0400321IceTransport::Impl::Impl(std::string_view name, const std::shared_ptr<Logger>& logger)
322 : logger_(logger), sessionName_(name)
Adrien Béraud612b55b2023-05-29 10:42:04 -0400323{
324 if (logger_)
Sébastien Blincf569402023-07-27 09:46:40 -0400325 logger_->debug("[ice:{}] Creating IceTransport session for \"{:s}\"", fmt::ptr(this), sessionName_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400326}
327
328IceTransport::Impl::~Impl()
329{
Adrien Béraud612b55b2023-05-29 10:42:04 -0400330 threadTerminateFlags_ = true;
331
332 if (thread_.joinable()) {
333 thread_.join();
334 }
335
336 if (icest_) {
337 pj_ice_strans* strans = nullptr;
338
339 std::swap(strans, icest_);
340
341 // must be done before ioqueue/timer destruction
342 if (logger_)
343 logger_->debug("[ice:{}] Destroying ice_strans {}", pj_ice_strans_get_user_data(strans), fmt::ptr(strans));
344
345 pj_ice_strans_stop_ice(strans);
346 pj_ice_strans_destroy(strans);
347
348 // NOTE: This last timer heap and IO queue polling is necessary to close
349 // TURN socket.
350 // Because when destroying the TURN session pjproject creates a pj_timer
351 // to postpone the TURN destruction. This timer is only called if we poll
352 // the event queue.
353
354 int ret = flushTimerHeapAndIoQueue();
355
356 if (ret < 0) {
357 if (logger_)
358 logger_->error("[ice:{}] IO queue polling failed", fmt::ptr(this));
359 } else if (ret > 0) {
360 if (logger_)
361 logger_->error("[ice:{}] Unexpected left timer in timer heap. "
362 "Please report the bug",
363 fmt::ptr(this));
364 }
365
366 if (checkEventQueue(1) > 0) {
367 if (logger_)
368 logger_->warn("[ice:{}] Unexpected left events in IO queue", fmt::ptr(this));
369 }
370
371 if (config_.stun_cfg.ioqueue)
372 pj_ioqueue_destroy(config_.stun_cfg.ioqueue);
373
374 if (config_.stun_cfg.timer_heap)
375 pj_timer_heap_destroy(config_.stun_cfg.timer_heap);
376 }
377
Adrien Béraud612b55b2023-05-29 10:42:04 -0400378 if (scb)
379 scb();
380}
381
382void
383IceTransport::Impl::initIceInstance(const IceTransportOptions& options)
384{
Amna81221ad2023-09-14 17:33:26 -0400385 factory = options.factory;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400386 isTcp_ = options.tcpEnable;
387 upnpEnabled_ = options.upnpEnable;
388 on_initdone_cb_ = options.onInitDone;
389 on_negodone_cb_ = options.onNegoDone;
390 streamsCount_ = options.streamsCount;
391 compCountPerStream_ = options.compCountPerStream;
392 compCount_ = streamsCount_ * compCountPerStream_;
393 compIO_ = std::vector<ComponentIO>(compCount_);
394 peerChannels_ = std::vector<PeerChannel>(compCount_);
395 iceDefaultRemoteAddr_.resize(compCount_);
396 initiatorSession_ = options.master;
397 accountLocalAddr_ = std::move(options.accountLocalAddr);
398 accountPublicAddr_ = std::move(options.accountPublicAddr);
399 stunServers_ = std::move(options.stunServers);
400 turnServers_ = std::move(options.turnServers);
401
402 if (logger_)
403 logger_->debug("[ice:{}] Initializing the session - comp count {} - as a {}",
404 fmt::ptr(this),
405 compCount_,
406 initiatorSession_ ? "master" : "slave");
407
408 if (upnpEnabled_)
Adrien Beraud753c8192023-08-17 17:21:24 -0400409 upnp_ = std::make_shared<upnp::Controller>(options.upnpContext);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400410
Amna81221ad2023-09-14 17:33:26 -0400411 config_ = factory->getIceCfg(); // config copy
Adrien Béraud612b55b2023-05-29 10:42:04 -0400412 if (isTcp_) {
413 config_.protocol = PJ_ICE_TP_TCP;
414 config_.stun.conn_type = PJ_STUN_TP_TCP;
415 config_.turn.conn_type = PJ_TURN_TP_TCP;
416 } else {
417 config_.protocol = PJ_ICE_TP_UDP;
418 config_.stun.conn_type = PJ_STUN_TP_UDP;
419 config_.turn.conn_type = PJ_TURN_TP_UDP;
420 }
421
422 pool_.reset(
Amna81221ad2023-09-14 17:33:26 -0400423 pj_pool_create(factory->getPoolFactory(), "IceTransport.pool", 512, 512, NULL));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400424 if (not pool_)
425 throw std::runtime_error("pj_pool_create() failed");
426
427 // Note: For server reflexive candidates, UPNP mappings will
428 // be used if available. Then, the public address learnt during
429 // the account registration process will be added only if it
430 // differs from the UPNP public address.
431 // Also note that UPNP candidates should be added first in order
432 // to have a higher priority when performing the connectivity
433 // checks.
434 // STUN configs layout:
435 // - index 0 : host IPv4
436 // - index 1 : host IPv6
437 // - index 2 : upnp/generic srflx IPv4.
438 // - index 3 : generic srflx (if upnp exists and different)
439
440 config_.stun_tp_cnt = 0;
441
Adrien Béraud370257c2023-08-15 20:53:09 -0400442 // if (logger_)
443 // logger_->debug("[ice:{}] Add host candidates", fmt::ptr(this));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400444 addStunConfig(pj_AF_INET());
445 addStunConfig(pj_AF_INET6());
446
447 std::vector<std::pair<IpAddr, IpAddr>> upnpSrflxCand;
448 if (upnp_) {
449 requestUpnpMappings();
450 upnpSrflxCand = setupUpnpReflexiveCandidates();
451 if (not upnpSrflxCand.empty()) {
452 addServerReflexiveCandidates(upnpSrflxCand);
Adrien Béraud370257c2023-08-15 20:53:09 -0400453 // if (logger_)
454 // logger_->debug("[ice:{}] Added UPNP srflx candidates:", fmt::ptr(this));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400455 }
456 }
457
458 auto genericSrflxCand = setupGenericReflexiveCandidates();
459
460 if (not genericSrflxCand.empty()) {
461 // Generic srflx candidates will be added only if different
462 // from upnp candidates.
463 if (upnpSrflxCand.empty()
464 or (upnpSrflxCand[0].second.toString() != genericSrflxCand[0].second.toString())) {
465 addServerReflexiveCandidates(genericSrflxCand);
Adrien Béraud370257c2023-08-15 20:53:09 -0400466 // if (logger_)
467 // logger_->debug("[ice:{}] Added generic srflx candidates:", fmt::ptr(this));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400468 }
469 }
470
471 if (upnpSrflxCand.empty() and genericSrflxCand.empty()) {
472 if (logger_)
473 logger_->warn("[ice:{}] No server reflexive candidates added", fmt::ptr(this));
474 }
475
476 pj_ice_strans_cb icecb;
477 pj_bzero(&icecb, sizeof(icecb));
478
479 icecb.on_rx_data = [](pj_ice_strans* ice_st,
480 unsigned comp_id,
481 void* pkt,
482 pj_size_t size,
483 const pj_sockaddr_t* /*src_addr*/,
484 unsigned /*src_addr_len*/) {
485 if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st)))
486 tr->onReceiveData(comp_id, pkt, size);
487 };
488
489 icecb.on_ice_complete = [](pj_ice_strans* ice_st, pj_ice_strans_op op, pj_status_t status) {
490 if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st)))
491 tr->onComplete(ice_st, op, status);
492 };
493
494 if (isTcp_) {
495 icecb.on_data_sent = [](pj_ice_strans* ice_st, pj_ssize_t size) {
496 if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st))) {
497 std::lock_guard lk(tr->sendDataMutex_);
498 tr->lastSentLen_ += size;
499 tr->waitDataCv_.notify_all();
500 }
501 };
502 }
503
504 icecb.on_destroy = [](pj_ice_strans* ice_st) {
505 if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st)))
506 tr->cancelOperations(); // Avoid upper layer to manage this ; Stop read operations
507 };
508
509 // Add STUN servers
510 for (auto& server : stunServers_)
Sébastien Blincf569402023-07-27 09:46:40 -0400511 add_stun_server(*pool_, config_, server, logger_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400512
513 // Add TURN servers
514 for (auto& server : turnServers_)
Sébastien Blincf569402023-07-27 09:46:40 -0400515 add_turn_server(*pool_, config_, server, logger_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400516
517 static constexpr auto IOQUEUE_MAX_HANDLES = std::min(PJ_IOQUEUE_MAX_HANDLES, 64);
518 TRY(pj_timer_heap_create(pool_.get(), 100, &config_.stun_cfg.timer_heap));
519 TRY(pj_ioqueue_create(pool_.get(), IOQUEUE_MAX_HANDLES, &config_.stun_cfg.ioqueue));
520 std::ostringstream sessionName {};
521 // We use the instance pointer as the PJNATH session name in order
522 // to easily identify the logs reported by PJNATH.
523 sessionName << this;
524 pj_status_t status = pj_ice_strans_create(sessionName.str().c_str(),
525 &config_,
526 compCount_,
527 this,
528 &icecb,
529 &icest_);
530
531 if (status != PJ_SUCCESS || icest_ == nullptr) {
532 throw std::runtime_error("pj_ice_strans_create() failed");
533 }
534
535 // Must be created after any potential failure
536 thread_ = std::thread([this] {
537 while (not threadTerminateFlags_) {
538 // NOTE: handleEvents can return false in this case
539 // but here we don't care if there is event or not.
540 handleEvents(HANDLE_EVENT_DURATION);
541 }
542 });
543}
544
545bool
546IceTransport::Impl::_isInitialized() const
547{
548 if (auto *icest = icest_) {
549 auto state = pj_ice_strans_get_state(icest);
550 return state >= PJ_ICE_STRANS_STATE_SESS_READY and state != PJ_ICE_STRANS_STATE_FAILED;
551 }
552 return false;
553}
554
555bool
556IceTransport::Impl::_isStarted() const
557{
558 if (auto *icest = icest_) {
559 auto state = pj_ice_strans_get_state(icest);
560 return state >= PJ_ICE_STRANS_STATE_NEGO and state != PJ_ICE_STRANS_STATE_FAILED;
561 }
562 return false;
563}
564
565bool
566IceTransport::Impl::_isRunning() const
567{
568 if (auto *icest = icest_) {
569 auto state = pj_ice_strans_get_state(icest);
570 return state >= PJ_ICE_STRANS_STATE_RUNNING and state != PJ_ICE_STRANS_STATE_FAILED;
571 }
572 return false;
573}
574
575bool
576IceTransport::Impl::_isFailed() const
577{
578 if (auto *icest = icest_)
579 return pj_ice_strans_get_state(icest) == PJ_ICE_STRANS_STATE_FAILED;
580 return false;
581}
582
583bool
584IceTransport::Impl::handleEvents(unsigned max_msec)
585{
586 // By tests, never seen more than two events per 500ms
587 static constexpr auto MAX_NET_EVENTS = 2;
588
589 pj_time_val max_timeout = {0, static_cast<long>(max_msec)};
590 pj_time_val timeout = {0, 0};
591 unsigned net_event_count = 0;
592
593 pj_timer_heap_poll(config_.stun_cfg.timer_heap, &timeout);
594 auto hasActiveTimer = timeout.sec != PJ_MAXINT32 || timeout.msec != PJ_MAXINT32;
595
596 // timeout limitation
597 if (hasActiveTimer)
598 pj_time_val_normalize(&timeout);
599
600 if (PJ_TIME_VAL_GT(timeout, max_timeout)) {
601 timeout = max_timeout;
602 }
603
604 do {
605 auto n_events = pj_ioqueue_poll(config_.stun_cfg.ioqueue, &timeout);
606
607 // timeout
608 if (not n_events)
609 return hasActiveTimer;
610
611 // error
612 if (n_events < 0) {
613 const auto err = pj_get_os_error();
614 // Kept as debug as some errors are "normal" in regular context
615 if (logger_)
616 logger_->debug("[ice:{}] ioqueue error {:d}: {:s}", fmt::ptr(this), err, sip_utils::sip_strerror(err));
617 std::this_thread::sleep_for(std::chrono::milliseconds(PJ_TIME_VAL_MSEC(timeout)));
618 return hasActiveTimer;
619 }
620
621 net_event_count += n_events;
622 timeout.sec = timeout.msec = 0;
623 } while (net_event_count < MAX_NET_EVENTS);
624 return hasActiveTimer;
625}
626
627int
628IceTransport::Impl::flushTimerHeapAndIoQueue()
629{
630 pj_time_val timerTimeout = {0, 0};
631 pj_time_val defaultWaitTime = {0, HANDLE_EVENT_DURATION};
632 bool hasActiveTimer = false;
633 std::chrono::milliseconds totalWaitTime {0};
Adrien Béraud9a676812023-08-21 09:02:16 -0400634 // auto const start = std::chrono::steady_clock::now();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400635 // We try to process pending events as fast as possible to
636 // speed-up the release.
637 int maxEventToProcess = 10;
638
639 do {
640 if (checkEventQueue(maxEventToProcess) < 0)
641 return -1;
642
643 pj_timer_heap_poll(config_.stun_cfg.timer_heap, &timerTimeout);
644 hasActiveTimer = !(timerTimeout.sec == PJ_MAXINT32 && timerTimeout.msec == PJ_MAXINT32);
645
646 if (hasActiveTimer) {
647 pj_time_val_normalize(&timerTimeout);
648 auto waitTime = std::chrono::milliseconds(
649 std::min(PJ_TIME_VAL_MSEC(timerTimeout), PJ_TIME_VAL_MSEC(defaultWaitTime)));
650 std::this_thread::sleep_for(waitTime);
651 totalWaitTime += waitTime;
652 }
653 } while (hasActiveTimer && totalWaitTime < std::chrono::milliseconds(MAX_DESTRUCTION_TIMEOUT));
654
Adrien Béraud9a676812023-08-21 09:02:16 -0400655 // auto duration = std::chrono::steady_clock::now() - start;
656 // if (logger_)
657 // logger_->debug("[ice:{}] Timer heap flushed after {}", fmt::ptr(this), dht::print_duration(duration));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400658
659 return static_cast<int>(pj_timer_heap_count(config_.stun_cfg.timer_heap));
660}
661
662int
663IceTransport::Impl::checkEventQueue(int maxEventToPoll)
664{
665 pj_time_val timeout = {0, 0};
666 int eventCount = 0;
667 int events = 0;
668
669 do {
670 events = pj_ioqueue_poll(config_.stun_cfg.ioqueue, &timeout);
671 if (events < 0) {
672 const auto err = pj_get_os_error();
673 if (logger_)
674 logger_->error("[ice:{}] ioqueue error {:d}: {:s}", fmt::ptr(this), err, sip_utils::sip_strerror(err));
675 return events;
676 }
677
678 eventCount += events;
679
680 } while (events > 0 && eventCount < maxEventToPoll);
681
682 return eventCount;
683}
684
685void
686IceTransport::Impl::onComplete(pj_ice_strans*, pj_ice_strans_op op, pj_status_t status)
687{
688 const char* opname = op == PJ_ICE_STRANS_OP_INIT ? "initialization"
689 : op == PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation"
690 : "unknown_op";
691
692 const bool done = status == PJ_SUCCESS;
693 if (done) {
694 if (logger_)
695 logger_->debug("[ice:{}] {:s} {:s} success",
696 fmt::ptr(this),
697 (config_.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP"),
698 opname);
699 } else {
700 if (logger_)
701 logger_->error("[ice:{}] {:s} {:s} failed: {:s}",
702 fmt::ptr(this),
703 (config_.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP"),
704 opname,
705 sip_utils::sip_strerror(status));
706 }
707
708 if (done and op == PJ_ICE_STRANS_OP_INIT) {
709 if (initiatorSession_)
710 setInitiatorSession();
711 else
712 setSlaveSession();
713 }
714
715 if (op == PJ_ICE_STRANS_OP_INIT and on_initdone_cb_)
716 on_initdone_cb_(done);
717 else if (op == PJ_ICE_STRANS_OP_NEGOTIATION) {
718 if (done) {
719 // Dump of connection pairs
720 if (logger_)
721 logger_->debug("[ice:{}] {:s} connection pairs ([comp id] local [type] <-> remote [type]):\n{:s}",
722 fmt::ptr(this),
723 (config_.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP"),
724 link());
725 }
726 if (on_negodone_cb_)
727 on_negodone_cb_(done);
728 }
729
730 iceCV_.notify_all();
731}
732
733std::string
734IceTransport::Impl::link() const
735{
736 std::ostringstream out;
737 for (unsigned strm = 1; strm <= streamsCount_ * compCountPerStream_; strm++) {
738 auto absIdx = strm;
739 auto comp = (strm + 1) / compCountPerStream_;
740 auto laddr = getLocalAddress(absIdx);
741 auto raddr = getRemoteAddress(absIdx);
742
743 if (laddr and laddr.getPort() != 0 and raddr and raddr.getPort() != 0) {
744 out << " [" << comp << "] " << laddr.toString(true, true) << " ["
745 << getCandidateType(getSelectedCandidate(absIdx, false)) << "] "
746 << " <-> " << raddr.toString(true, true) << " ["
747 << getCandidateType(getSelectedCandidate(absIdx, true)) << "] " << '\n';
748 } else {
749 out << " [" << comp << "] disabled\n";
750 }
751 }
752 return out.str();
753}
754
755bool
756IceTransport::Impl::setInitiatorSession()
757{
758 if (logger_)
759 logger_->debug("[ice:{}] as master", fmt::ptr(this));
760 initiatorSession_ = true;
761 if (_isInitialized()) {
762 auto status = pj_ice_strans_change_role(icest_, PJ_ICE_SESS_ROLE_CONTROLLING);
763 if (status != PJ_SUCCESS) {
764 if (logger_)
765 logger_->error("[ice:{}] role change failed: {:s}", fmt::ptr(this), sip_utils::sip_strerror(status));
766 return false;
767 }
768 return true;
769 }
770 return createIceSession(PJ_ICE_SESS_ROLE_CONTROLLING);
771}
772
773bool
774IceTransport::Impl::setSlaveSession()
775{
776 if (logger_)
777 logger_->debug("[ice:{}] as slave", fmt::ptr(this));
778 initiatorSession_ = false;
779 if (_isInitialized()) {
780 auto status = pj_ice_strans_change_role(icest_, PJ_ICE_SESS_ROLE_CONTROLLED);
781 if (status != PJ_SUCCESS) {
782 if (logger_)
783 logger_->error("[ice:{}] role change failed: {:s}", fmt::ptr(this), sip_utils::sip_strerror(status));
784 return false;
785 }
786 return true;
787 }
788 return createIceSession(PJ_ICE_SESS_ROLE_CONTROLLED);
789}
790
791const pj_ice_sess_cand*
792IceTransport::Impl::getSelectedCandidate(unsigned comp_id, bool remote) const
793{
794 ASSERT_COMP_ID(comp_id, compCount_);
795
796 // Return the selected candidate pair. Might not be the nominated pair if
797 // ICE has not concluded yet, but should be the nominated pair afterwards.
798 if (not _isRunning()) {
799 if (logger_)
800 logger_->error("[ice:{}] ICE transport is not running", fmt::ptr(this));
801 return nullptr;
802 }
803
804 const auto* sess = pj_ice_strans_get_valid_pair(icest_, comp_id);
805 if (sess == nullptr) {
806 if (logger_)
807 logger_->warn("[ice:{}] Component {} has no valid pair (disabled)", fmt::ptr(this), comp_id);
808 return nullptr;
809 }
810
811 if (remote)
812 return sess->rcand;
813 else
814 return sess->lcand;
815}
816
817IpAddr
818IceTransport::Impl::getLocalAddress(unsigned comp_id) const
819{
820 ASSERT_COMP_ID(comp_id, compCount_);
821
822 if (auto cand = getSelectedCandidate(comp_id, false))
823 return cand->addr;
824
825 return {};
826}
827
828IpAddr
829IceTransport::Impl::getRemoteAddress(unsigned comp_id) const
830{
831 ASSERT_COMP_ID(comp_id, compCount_);
832
833 if (auto cand = getSelectedCandidate(comp_id, true))
834 return cand->addr;
835
836 return {};
837}
838
839const char*
840IceTransport::Impl::getCandidateType(const pj_ice_sess_cand* cand)
841{
842 auto name = cand ? pj_ice_get_cand_type_name(cand->type) : nullptr;
843 return name ? name : "?";
844}
845
846void
847IceTransport::Impl::getUFragPwd()
848{
849 if (icest_) {
850 pj_str_t local_ufrag, local_pwd;
851
852 pj_ice_strans_get_ufrag_pwd(icest_, &local_ufrag, &local_pwd, nullptr, nullptr);
853 local_ufrag_.assign(local_ufrag.ptr, local_ufrag.slen);
854 local_pwd_.assign(local_pwd.ptr, local_pwd.slen);
855 }
856}
857
858bool
859IceTransport::Impl::createIceSession(pj_ice_sess_role role)
860{
861 if (not icest_) {
862 return false;
863 }
864
865 if (pj_ice_strans_init_ice(icest_, role, nullptr, nullptr) != PJ_SUCCESS) {
866 if (logger_)
867 logger_->error("[ice:{}] pj_ice_strans_init_ice() failed", fmt::ptr(this));
868 return false;
869 }
870
871 // Fetch some information on local configuration
872 getUFragPwd();
873
874 if (logger_)
Sébastien Blincf569402023-07-27 09:46:40 -0400875 logger_->debug("[ice:{}] (local) ufrag={}, pwd={}", fmt::ptr(this), local_ufrag_, local_pwd_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400876
877 return true;
878}
879
880bool
881IceTransport::Impl::addStunConfig(int af)
882{
883 if (config_.stun_tp_cnt >= PJ_ICE_MAX_STUN) {
884 if (logger_)
Sébastien Blincf569402023-07-27 09:46:40 -0400885 logger_->error("Max number of STUN configurations reached ({})", PJ_ICE_MAX_STUN);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400886 return false;
887 }
888
889 if (af != pj_AF_INET() and af != pj_AF_INET6()) {
890 if (logger_)
Sébastien Blincf569402023-07-27 09:46:40 -0400891 logger_->error("Invalid address familly ({})", af);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400892 return false;
893 }
894
895 auto& stun = config_.stun_tp[config_.stun_tp_cnt++];
896
897 pj_ice_strans_stun_cfg_default(&stun);
898 stun.cfg.max_pkt_size = STUN_MAX_PACKET_SIZE;
899 stun.af = af;
900 stun.conn_type = config_.stun.conn_type;
901
Adrien Béraud370257c2023-08-15 20:53:09 -0400902 // if (logger_)
903 // logger_->debug("[ice:{}] added host stun config for {:s} transport",
904 // fmt::ptr(this),
905 // config_.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400906
907 return true;
908}
909
910void
911IceTransport::Impl::requestUpnpMappings()
912{
913 // Must be called once !
914
915 std::lock_guard<std::mutex> lock(upnpMutex_);
916
917 if (not upnp_)
918 return;
919
920 auto transport = isTcpEnabled() ? PJ_CAND_TCP_PASSIVE : PJ_CAND_UDP;
921 auto portType = transport == PJ_CAND_UDP ? PortType::UDP : PortType::TCP;
922
923 // Request upnp mapping for each component.
924 for (unsigned id = 1; id <= compCount_; id++) {
925 // Set port number to 0 to get any available port.
926 Mapping requestedMap(portType);
927
928 // Request the mapping
929 Mapping::sharedPtr_t mapPtr = upnp_->reserveMapping(requestedMap);
930
931 // To use a mapping, it must be valid, open and has valid host address.
932 if (mapPtr and mapPtr->getMapKey() and (mapPtr->getState() == MappingState::OPEN)
933 and mapPtr->hasValidHostAddress()) {
934 std::lock_guard<std::mutex> lock(upnpMappingsMutex_);
935 auto ret = upnpMappings_.emplace(mapPtr->getMapKey(), *mapPtr);
936 if (ret.second) {
937 if (logger_)
938 logger_->debug("[ice:{}] UPNP mapping {:s} successfully allocated",
939 fmt::ptr(this),
940 mapPtr->toString(true));
941 } else {
942 if (logger_)
943 logger_->warn("[ice:{}] UPNP mapping {:s} already in the list!",
944 fmt::ptr(this),
945 mapPtr->toString());
946 }
947 } else {
948 if (logger_)
949 logger_->warn("[ice:{}] UPNP mapping request failed!", fmt::ptr(this));
950 upnp_->releaseMapping(requestedMap);
951 }
952 }
953}
954
955bool
956IceTransport::Impl::hasUpnp() const
957{
958 return upnp_ and upnpMappings_.size() == compCount_;
959}
960
961void
962IceTransport::Impl::addServerReflexiveCandidates(
963 const std::vector<std::pair<IpAddr, IpAddr>>& addrList)
964{
965 if (addrList.size() != compCount_) {
966 if (logger_)
967 logger_->warn("[ice:{}] Provided addr list size {} does not match component count {}",
968 fmt::ptr(this),
969 addrList.size(),
970 compCount_);
971 return;
972 }
973 if (compCount_ > PJ_ICE_MAX_COMP) {
974 if (logger_)
975 logger_->error("[ice:{}] Too many components", fmt::ptr(this));
976 return;
977 }
978
979 // Add config for server reflexive candidates (UPNP or from DHT).
980 if (not addStunConfig(pj_AF_INET()))
981 return;
982
983 assert(config_.stun_tp_cnt > 0 && config_.stun_tp_cnt < PJ_ICE_MAX_STUN);
984 auto& stun = config_.stun_tp[config_.stun_tp_cnt - 1];
985
986 for (unsigned id = 1; id <= compCount_; id++) {
987 auto idx = id - 1;
988 auto& localAddr = addrList[idx].first;
989 auto& publicAddr = addrList[idx].second;
990
991 if (logger_)
992 logger_->debug("[ice:{}] Add srflx reflexive candidates [{:s} : {:s}] for comp {:d}",
993 fmt::ptr(this),
994 localAddr.toString(true),
995 publicAddr.toString(true),
996 id);
997
998 pj_sockaddr_cp(&stun.cfg.user_mapping[idx].local_addr, localAddr.pjPtr());
999 pj_sockaddr_cp(&stun.cfg.user_mapping[idx].mapped_addr, publicAddr.pjPtr());
1000
1001 if (isTcpEnabled()) {
1002 if (publicAddr.getPort() == 9) {
1003 stun.cfg.user_mapping[idx].tp_type = PJ_CAND_TCP_ACTIVE;
1004 } else {
1005 stun.cfg.user_mapping[idx].tp_type = PJ_CAND_TCP_PASSIVE;
1006 }
1007 } else {
1008 stun.cfg.user_mapping[idx].tp_type = PJ_CAND_UDP;
1009 }
1010 }
1011
1012 stun.cfg.user_mapping_cnt = compCount_;
1013}
1014
1015std::vector<std::pair<IpAddr, IpAddr>>
1016IceTransport::Impl::setupGenericReflexiveCandidates()
1017{
1018 if (not accountLocalAddr_) {
1019 if (logger_)
1020 logger_->warn("[ice:{}] Missing local address, generic srflx candidates wont be generated!",
1021 fmt::ptr(this));
1022 return {};
1023 }
1024
1025 if (not accountPublicAddr_) {
1026 if (logger_)
1027 logger_->warn("[ice:{}] Missing public address, generic srflx candidates wont be generated!",
1028 fmt::ptr(this));
1029 return {};
1030 }
1031
1032 std::vector<std::pair<IpAddr, IpAddr>> addrList;
1033 auto isTcp = isTcpEnabled();
1034
1035 addrList.reserve(compCount_);
1036 for (unsigned id = 1; id <= compCount_; id++) {
1037 // For TCP, the type is set to active, because most likely the incoming
1038 // connection will be blocked by the NAT.
1039 // For UDP use random port number.
1040 uint16_t port = isTcp ? 9
1041 : upnp::Controller::generateRandomPort(isTcp ? PortType::TCP
1042 : PortType::UDP);
1043
1044 accountLocalAddr_.setPort(port);
1045 accountPublicAddr_.setPort(port);
1046 addrList.emplace_back(accountLocalAddr_, accountPublicAddr_);
1047 }
1048
1049 return addrList;
1050}
1051
1052std::vector<std::pair<IpAddr, IpAddr>>
1053IceTransport::Impl::setupUpnpReflexiveCandidates()
1054{
1055 // Add UPNP server reflexive candidates if available.
1056 if (not hasUpnp())
1057 return {};
1058
1059 std::lock_guard<std::mutex> lock(upnpMappingsMutex_);
1060
1061 if (upnpMappings_.size() < (size_t)compCount_) {
1062 if (logger_)
1063 logger_->warn("[ice:{}] Not enough mappings {:d}. Expected {:d}",
1064 fmt::ptr(this),
1065 upnpMappings_.size(),
1066 compCount_);
1067 return {};
1068 }
1069
1070 std::vector<std::pair<IpAddr, IpAddr>> addrList;
1071
1072 addrList.reserve(upnpMappings_.size());
1073 for (auto const& [_, map] : upnpMappings_) {
1074 assert(map.getMapKey());
1075 IpAddr localAddr {map.getInternalAddress()};
1076 localAddr.setPort(map.getInternalPort());
1077 IpAddr publicAddr {map.getExternalAddress()};
1078 publicAddr.setPort(map.getExternalPort());
1079 addrList.emplace_back(localAddr, publicAddr);
1080 }
1081
1082 return addrList;
1083}
1084
1085void
1086IceTransport::Impl::setDefaultRemoteAddress(unsigned compId, const IpAddr& addr)
1087{
1088 ASSERT_COMP_ID(compId, compCount_);
1089
1090 iceDefaultRemoteAddr_[compId - 1] = addr;
1091 // The port does not matter. Set it 0 to avoid confusion.
1092 iceDefaultRemoteAddr_[compId - 1].setPort(0);
1093}
1094
1095IpAddr
1096IceTransport::Impl::getDefaultRemoteAddress(unsigned compId) const
1097{
1098 ASSERT_COMP_ID(compId, compCount_);
1099 return iceDefaultRemoteAddr_[compId - 1];
1100}
1101
1102void
1103IceTransport::Impl::onReceiveData(unsigned comp_id, void* pkt, pj_size_t size)
1104{
1105 ASSERT_COMP_ID(comp_id, compCount_);
1106
1107 jami_tracepoint_if_enabled(ice_transport_recv,
1108 reinterpret_cast<uint64_t>(this),
1109 comp_id,
1110 size,
1111 getRemoteAddress(comp_id).toString().c_str());
1112 if (size == 0)
1113 return;
1114
1115 {
1116 auto& io = compIO_[comp_id - 1];
1117 std::lock_guard<std::mutex> lk(io.mutex);
1118
1119 if (io.recvCb) {
1120 io.recvCb((uint8_t*) pkt, size);
1121 return;
1122 }
1123 }
1124
1125 std::error_code ec;
1126 auto err = peerChannels_.at(comp_id - 1).write((const char*) pkt, size, ec);
1127 if (err < 0) {
1128 if (logger_)
1129 logger_->error("[ice:{}] rx: channel is closed", fmt::ptr(this));
1130 }
1131}
1132
1133bool
1134IceTransport::Impl::_waitForInitialization(std::chrono::milliseconds timeout)
1135{
1136 IceLock lk(icest_);
1137
1138 if (not iceCV_.wait_for(lk, timeout, [this] {
1139 return threadTerminateFlags_ or _isInitialized() or _isFailed();
1140 })) {
1141 if (logger_)
1142 logger_->warn("[ice:{}] waitForInitialization: timeout", fmt::ptr(this));
1143 return false;
1144 }
1145
1146 return _isInitialized();
1147}
1148
1149//==============================================================================
1150
Adrien Béraud89933c12023-07-26 14:53:30 -04001151IceTransport::IceTransport(std::string_view name, const std::shared_ptr<dht::log::Logger>& logger)
1152 : pimpl_ {std::make_unique<Impl>(name, logger)}
Adrien Béraud612b55b2023-05-29 10:42:04 -04001153{}
1154
1155IceTransport::~IceTransport()
1156{
1157 cancelOperations();
1158}
1159
1160const std::shared_ptr<dht::log::Logger>&
1161IceTransport::logger() const
1162{
1163 return pimpl_->logger_;
1164}
1165
1166void
1167IceTransport::initIceInstance(const IceTransportOptions& options)
1168{
1169 pimpl_->initIceInstance(options);
1170 jami_tracepoint(ice_transport_context, reinterpret_cast<uint64_t>(this));
1171}
1172
1173bool
1174IceTransport::isInitialized() const
1175{
1176 IceLock lk(pimpl_->icest_);
1177 return pimpl_->_isInitialized();
1178}
1179
1180bool
1181IceTransport::isStarted() const
1182{
1183 IceLock lk(pimpl_->icest_);
1184 return pimpl_->_isStarted();
1185}
1186
1187bool
1188IceTransport::isRunning() const
1189{
1190 if (!pimpl_->icest_)
1191 return false;
1192 IceLock lk(pimpl_->icest_);
1193 return pimpl_->_isRunning();
1194}
1195
1196bool
1197IceTransport::isFailed() const
1198{
1199 return pimpl_->_isFailed();
1200}
1201
1202unsigned
1203IceTransport::getComponentCount() const
1204{
1205 return pimpl_->compCount_;
1206}
1207
1208bool
1209IceTransport::setSlaveSession()
1210{
1211 return pimpl_->setSlaveSession();
1212}
1213bool
1214IceTransport::setInitiatorSession()
1215{
1216 return pimpl_->setInitiatorSession();
1217}
1218
1219bool
1220IceTransport::isInitiator() const
1221{
1222 if (isInitialized()) {
1223 return pj_ice_strans_get_role(pimpl_->icest_) == PJ_ICE_SESS_ROLE_CONTROLLING;
1224 }
1225 return pimpl_->initiatorSession_;
1226}
1227
1228bool
1229IceTransport::startIce(const Attribute& rem_attrs, std::vector<IceCandidate>&& rem_candidates)
1230{
1231 if (not isInitialized()) {
1232 if (pimpl_->logger_)
1233 pimpl_->logger_->error("[ice:{}] not initialized transport", fmt::ptr(pimpl_.get()));
1234 pimpl_->is_stopped_ = true;
1235 return false;
1236 }
1237
1238 // pj_ice_strans_start_ice crashes if remote candidates array is empty
1239 if (rem_candidates.empty()) {
1240 if (pimpl_->logger_)
1241 pimpl_->logger_->error("[ice:{}] start failed: no remote candidates", fmt::ptr(pimpl_.get()));
1242 pimpl_->is_stopped_ = true;
1243 return false;
1244 }
1245
1246 auto comp_cnt = std::max(1u, getComponentCount());
1247 if (rem_candidates.size() / comp_cnt > PJ_ICE_ST_MAX_CAND - 1) {
1248 std::vector<IceCandidate> rcands;
1249 rcands.reserve(PJ_ICE_ST_MAX_CAND - 1);
1250 if (pimpl_->logger_)
1251 pimpl_->logger_->warn("[ice:{}] too much candidates detected, trim list.", fmt::ptr(pimpl_.get()));
1252 // Just trim some candidates. To avoid to only take host candidates, iterate
1253 // through the whole list and select some host, some turn and peer reflexives
1254 // It should give at least enough infos to negotiate.
1255 auto maxHosts = 8;
1256 auto maxRelays = PJ_ICE_MAX_TURN;
1257 for (auto& c : rem_candidates) {
1258 if (c.type == PJ_ICE_CAND_TYPE_HOST) {
1259 if (maxHosts == 0)
1260 continue;
1261 maxHosts -= 1;
1262 } else if (c.type == PJ_ICE_CAND_TYPE_RELAYED) {
1263 if (maxRelays == 0)
1264 continue;
1265 maxRelays -= 1;
1266 }
1267 if (rcands.size() == PJ_ICE_ST_MAX_CAND - 1)
1268 break;
1269 rcands.emplace_back(std::move(c));
1270 }
1271 rem_candidates = std::move(rcands);
1272 }
1273
1274 pj_str_t ufrag, pwd;
1275 if (pimpl_->logger_)
1276 pimpl_->logger_->debug("[ice:{}] negotiation starting ({:d} remote candidates)",
1277 fmt::ptr(pimpl_),
1278 rem_candidates.size());
1279
1280 auto status = pj_ice_strans_start_ice(pimpl_->icest_,
1281 pj_strset(&ufrag,
1282 (char*) rem_attrs.ufrag.c_str(),
1283 rem_attrs.ufrag.size()),
1284 pj_strset(&pwd,
1285 (char*) rem_attrs.pwd.c_str(),
1286 rem_attrs.pwd.size()),
1287 rem_candidates.size(),
1288 rem_candidates.data());
1289 if (status != PJ_SUCCESS) {
1290 if (pimpl_->logger_)
1291 pimpl_->logger_->error("[ice:{}] start failed: {:s}", fmt::ptr(pimpl_.get()), sip_utils::sip_strerror(status));
1292 pimpl_->is_stopped_ = true;
1293 return false;
1294 }
1295
1296 return true;
1297}
1298
1299bool
1300IceTransport::startIce(const SDP& sdp)
1301{
1302 if (pimpl_->streamsCount_ != 1) {
1303 if (pimpl_->logger_)
Adrien Béraud49b07192023-06-13 20:03:33 -04001304 pimpl_->logger_->error(FMT_STRING("Expected exactly one stream per SDP (found {:d} streams)"), pimpl_->streamsCount_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001305 return false;
1306 }
1307
1308 if (not isInitialized()) {
1309 if (pimpl_->logger_)
1310 pimpl_->logger_->error(FMT_STRING("[ice:{}] not initialized transport"), fmt::ptr(pimpl_));
1311 pimpl_->is_stopped_ = true;
1312 return false;
1313 }
1314
1315 for (unsigned id = 1; id <= getComponentCount(); id++) {
1316 auto candVec = getLocalCandidates(id);
1317 for (auto const& cand : candVec) {
1318 if (pimpl_->logger_)
1319 pimpl_->logger_->debug("[ice:{}] Using local candidate {:s} for comp {:d}",
1320 fmt::ptr(pimpl_), cand, id);
1321 }
1322 }
1323
1324 if (pimpl_->logger_)
1325 pimpl_->logger_->debug("[ice:{}] negotiation starting ({:u} remote candidates)",
1326 fmt::ptr(pimpl_), sdp.candidates.size());
1327 pj_str_t ufrag, pwd;
1328
1329 std::vector<IceCandidate> rem_candidates;
1330 rem_candidates.reserve(sdp.candidates.size());
1331 IceCandidate cand;
1332 for (const auto& line : sdp.candidates) {
1333 if (parseIceAttributeLine(0, line, cand))
1334 rem_candidates.emplace_back(cand);
1335 }
1336
1337 auto status = pj_ice_strans_start_ice(pimpl_->icest_,
1338 pj_strset(&ufrag,
1339 (char*) sdp.ufrag.c_str(),
1340 sdp.ufrag.size()),
1341 pj_strset(&pwd, (char*) sdp.pwd.c_str(), sdp.pwd.size()),
1342 rem_candidates.size(),
1343 rem_candidates.data());
1344 if (status != PJ_SUCCESS) {
1345 if (pimpl_->logger_)
1346 pimpl_->logger_->error("[ice:{}] start failed: {:s}", fmt::ptr(pimpl_), sip_utils::sip_strerror(status));
1347 pimpl_->is_stopped_ = true;
1348 return false;
1349 }
1350
1351 return true;
1352}
1353
1354void
1355IceTransport::cancelOperations()
1356{
1357 pimpl_->cancelOperations();
1358}
1359
1360IpAddr
1361IceTransport::getLocalAddress(unsigned comp_id) const
1362{
1363 return pimpl_->getLocalAddress(comp_id);
1364}
1365
1366IpAddr
1367IceTransport::getRemoteAddress(unsigned comp_id) const
1368{
1369 // Return the default remote address if set.
1370 // Note that the default remote addresses are the addresses
1371 // set in the 'c=' and 'a=rtcp' lines of the received SDP.
1372 // See pj_ice_strans_sendto2() for more details.
1373 if (auto defaultAddr = pimpl_->getDefaultRemoteAddress(comp_id)) {
1374 return defaultAddr;
1375 }
1376
1377 return pimpl_->getRemoteAddress(comp_id);
1378}
1379
1380const IceTransport::Attribute
1381IceTransport::getLocalAttributes() const
1382{
1383 return {pimpl_->local_ufrag_, pimpl_->local_pwd_};
1384}
1385
1386std::vector<std::string>
1387IceTransport::getLocalCandidates(unsigned comp_id) const
1388{
1389 ASSERT_COMP_ID(comp_id, getComponentCount());
1390 std::vector<std::string> res;
1391 pj_ice_sess_cand cand[MAX_CANDIDATES];
1392 unsigned cand_cnt = PJ_ARRAY_SIZE(cand);
1393
1394 if (!isInitialized()) {
1395 return res;
1396 }
1397
1398 if (pj_ice_strans_enum_cands(pimpl_->icest_, comp_id, &cand_cnt, cand) != PJ_SUCCESS) {
1399 if (pimpl_->logger_)
1400 pimpl_->logger_->error("[ice:{}] pj_ice_strans_enum_cands() failed", fmt::ptr(pimpl_));
1401 return res;
1402 }
1403
1404 res.reserve(cand_cnt);
1405 for (unsigned i = 0; i < cand_cnt; ++i) {
1406 /** Section 4.5, RFC 6544 (https://tools.ietf.org/html/rfc6544)
1407 * candidate-attribute = "candidate" ":" foundation SP component-id
1408 * SP "TCP" SP priority SP connection-address SP port SP cand-type [SP
1409 * rel-addr] [SP rel-port] SP tcp-type-ext
1410 * *(SP extension-att-name SP
1411 * extension-att-value)
1412 *
1413 * tcp-type-ext = "tcptype" SP tcp-type
1414 * tcp-type = "active" / "passive" / "so"
1415 */
1416 char ipaddr[PJ_INET6_ADDRSTRLEN];
1417 std::string tcp_type;
1418 if (cand[i].transport != PJ_CAND_UDP) {
1419 tcp_type += " tcptype";
1420 switch (cand[i].transport) {
1421 case PJ_CAND_TCP_ACTIVE:
1422 tcp_type += " active";
1423 break;
1424 case PJ_CAND_TCP_PASSIVE:
1425 tcp_type += " passive";
1426 break;
1427 case PJ_CAND_TCP_SO:
1428 default:
1429 tcp_type += " so";
1430 break;
1431 }
1432 }
1433 res.emplace_back(
1434 fmt::format("{} {} {} {} {} {} typ {}{}",
1435 sip_utils::as_view(cand[i].foundation),
1436 cand[i].comp_id,
1437 (cand[i].transport == PJ_CAND_UDP ? "UDP" : "TCP"),
1438 cand[i].prio,
1439 pj_sockaddr_print(&cand[i].addr, ipaddr, sizeof(ipaddr), 0),
1440 pj_sockaddr_get_port(&cand[i].addr),
1441 pj_ice_get_cand_type_name(cand[i].type),
1442 tcp_type));
1443 }
1444
1445 return res;
1446}
1447std::vector<std::string>
1448IceTransport::getLocalCandidates(unsigned streamIdx, unsigned compId) const
1449{
1450 ASSERT_COMP_ID(compId, getComponentCount());
1451
1452 std::vector<std::string> res;
1453 pj_ice_sess_cand cand[MAX_CANDIDATES];
1454 unsigned cand_cnt = MAX_CANDIDATES;
1455
1456 if (not isInitialized()) {
1457 return res;
1458 }
1459
1460 // In the implementation, the component IDs are enumerated globally
1461 // (per SDP: 1, 2, 3, 4, ...). This is simpler because we create
1462 // only one pj_ice_strans instance. However, the component IDs are
1463 // enumerated per stream in the generated SDP (1, 2, 1, 2, ...) in
1464 // order to be compliant with the spec.
1465
1466 auto globalCompId = streamIdx * 2 + compId;
1467 if (pj_ice_strans_enum_cands(pimpl_->icest_, globalCompId, &cand_cnt, cand) != PJ_SUCCESS) {
1468 if (pimpl_->logger_)
1469 pimpl_->logger_->error("[ice:{}] pj_ice_strans_enum_cands() failed", fmt::ptr(pimpl_));
1470 return res;
1471 }
1472
1473 res.reserve(cand_cnt);
1474 // Build ICE attributes according to RFC 6544, section 4.5.
1475 for (unsigned i = 0; i < cand_cnt; ++i) {
1476 char ipaddr[PJ_INET6_ADDRSTRLEN];
1477 std::string tcp_type;
1478 if (cand[i].transport != PJ_CAND_UDP) {
1479 tcp_type += " tcptype";
1480 switch (cand[i].transport) {
1481 case PJ_CAND_TCP_ACTIVE:
1482 tcp_type += " active";
1483 break;
1484 case PJ_CAND_TCP_PASSIVE:
1485 tcp_type += " passive";
1486 break;
1487 case PJ_CAND_TCP_SO:
1488 default:
1489 tcp_type += " so";
1490 break;
1491 }
1492 }
1493 res.emplace_back(
1494 fmt::format("{} {} {} {} {} {} typ {}{}",
1495 sip_utils::as_view(cand[i].foundation),
1496 compId,
1497 (cand[i].transport == PJ_CAND_UDP ? "UDP" : "TCP"),
1498 cand[i].prio,
1499 pj_sockaddr_print(&cand[i].addr, ipaddr, sizeof(ipaddr), 0),
1500 pj_sockaddr_get_port(&cand[i].addr),
1501 pj_ice_get_cand_type_name(cand[i].type),
1502 tcp_type));
1503 }
1504
1505 return res;
1506}
1507
1508bool
1509IceTransport::parseIceAttributeLine(unsigned streamIdx,
1510 const std::string& line,
1511 IceCandidate& cand) const
1512{
1513 // Silently ignore empty lines
1514 if (line.empty())
1515 return false;
1516
1517 if (streamIdx >= pimpl_->streamsCount_) {
1518 throw std::runtime_error(fmt::format("Stream index {:d} is invalid!", streamIdx));
1519 }
1520
1521 int af, cnt;
1522 char foundation[32], transport[12], ipaddr[80], type[32], tcp_type[32];
1523 pj_str_t tmpaddr;
1524 unsigned comp_id, prio, port;
1525 pj_status_t status;
1526 pj_bool_t is_tcp = PJ_FALSE;
1527
1528 // Parse ICE attribute line according to RFC-6544 section 4.5.
1529 // TODO/WARNING: There is no fail-safe in case of malformed attributes.
1530 cnt = sscanf(line.c_str(),
1531 "%31s %u %11s %u %79s %u typ %31s tcptype %31s\n",
1532 foundation,
1533 &comp_id,
1534 transport,
1535 &prio,
1536 ipaddr,
1537 &port,
1538 type,
1539 tcp_type);
1540 if (cnt != 7 && cnt != 8) {
1541 if (pimpl_->logger_)
1542 pimpl_->logger_->error("[ice:{}] Invalid ICE candidate line: {:s}", fmt::ptr(pimpl_), line);
1543 return false;
1544 }
1545
1546 if (strcmp(transport, "TCP") == 0) {
1547 is_tcp = PJ_TRUE;
1548 }
1549
1550 pj_bzero(&cand, sizeof(IceCandidate));
1551
1552 if (strcmp(type, "host") == 0)
1553 cand.type = PJ_ICE_CAND_TYPE_HOST;
1554 else if (strcmp(type, "srflx") == 0)
1555 cand.type = PJ_ICE_CAND_TYPE_SRFLX;
1556 else if (strcmp(type, "prflx") == 0)
1557 cand.type = PJ_ICE_CAND_TYPE_PRFLX;
1558 else if (strcmp(type, "relay") == 0)
1559 cand.type = PJ_ICE_CAND_TYPE_RELAYED;
1560 else {
1561 if (pimpl_->logger_)
1562 pimpl_->logger_->warn("[ice:{}] invalid remote candidate type '{:s}'", fmt::ptr(pimpl_), type);
1563 return false;
1564 }
1565
1566 if (is_tcp) {
1567 if (strcmp(tcp_type, "active") == 0)
1568 cand.transport = PJ_CAND_TCP_ACTIVE;
1569 else if (strcmp(tcp_type, "passive") == 0)
1570 cand.transport = PJ_CAND_TCP_PASSIVE;
1571 else if (strcmp(tcp_type, "so") == 0)
1572 cand.transport = PJ_CAND_TCP_SO;
1573 else {
1574 if (pimpl_->logger_)
1575 pimpl_->logger_->warn("[ice:{}] invalid transport type type '{:s}'", fmt::ptr(pimpl_), tcp_type);
1576 return false;
1577 }
1578 } else {
1579 cand.transport = PJ_CAND_UDP;
1580 }
1581
1582 // If the component Id is enumerated relative to media, convert
1583 // it to absolute enumeration.
1584 if (comp_id <= pimpl_->compCountPerStream_) {
1585 comp_id += pimpl_->compCountPerStream_ * streamIdx;
1586 }
1587 cand.comp_id = (pj_uint8_t) comp_id;
1588
1589 cand.prio = prio;
1590
1591 if (strchr(ipaddr, ':'))
1592 af = pj_AF_INET6();
1593 else {
1594 af = pj_AF_INET();
1595 pimpl_->onlyIPv4Private_ &= IpAddr(ipaddr).isPrivate();
1596 }
1597
1598 tmpaddr = pj_str(ipaddr);
1599 pj_sockaddr_init(af, &cand.addr, NULL, 0);
1600 status = pj_sockaddr_set_str_addr(af, &cand.addr, &tmpaddr);
1601 if (status != PJ_SUCCESS) {
1602 if (pimpl_->logger_)
1603 pimpl_->logger_->warn("[ice:{}] invalid IP address '{:s}'", fmt::ptr(pimpl_), ipaddr);
1604 return false;
1605 }
1606
1607 pj_sockaddr_set_port(&cand.addr, (pj_uint16_t) port);
1608 pj_strdup2(pimpl_->pool_.get(), &cand.foundation, foundation);
1609
1610 return true;
1611}
1612
1613ssize_t
1614IceTransport::recv(unsigned compId, unsigned char* buf, size_t len, std::error_code& ec)
1615{
1616 ASSERT_COMP_ID(compId, getComponentCount());
1617 auto& io = pimpl_->compIO_[compId - 1];
1618 std::lock_guard<std::mutex> lk(io.mutex);
1619
1620 if (io.queue.empty()) {
1621 ec = std::make_error_code(std::errc::resource_unavailable_try_again);
1622 return -1;
1623 }
1624
1625 auto& packet = io.queue.front();
Adrien Béraud21d5f462023-08-27 12:06:21 -04001626 const auto count = std::min(len, packet.size());
1627 std::copy_n(packet.begin(), count, buf);
1628 if (count == packet.size()) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001629 io.queue.pop_front();
1630 } else {
Adrien Béraud21d5f462023-08-27 12:06:21 -04001631 packet.erase(packet.begin(), packet.begin() + count);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001632 }
1633
1634 ec.clear();
1635 return count;
1636}
1637
1638ssize_t
1639IceTransport::recvfrom(unsigned compId, char* buf, size_t len, std::error_code& ec)
1640{
1641 ASSERT_COMP_ID(compId, getComponentCount());
1642 return pimpl_->peerChannels_.at(compId - 1).read(buf, len, ec);
1643}
1644
1645void
1646IceTransport::setOnRecv(unsigned compId, IceRecvCb cb)
1647{
1648 ASSERT_COMP_ID(compId, getComponentCount());
1649
1650 auto& io = pimpl_->compIO_[compId - 1];
1651 std::lock_guard<std::mutex> lk(io.mutex);
1652 io.recvCb = std::move(cb);
1653
1654 if (io.recvCb) {
1655 // Flush existing queue using the callback
1656 for (const auto& packet : io.queue)
Adrien Béraud21d5f462023-08-27 12:06:21 -04001657 io.recvCb((uint8_t*) packet.data(), packet.size());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001658 io.queue.clear();
1659 }
1660}
1661
1662void
1663IceTransport::setOnShutdown(onShutdownCb&& cb)
1664{
Adrien Béraudef3e9fd2023-10-01 21:15:15 -04001665 pimpl_->scb = std::move(cb);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001666}
1667
1668ssize_t
1669IceTransport::send(unsigned compId, const unsigned char* buf, size_t len)
1670{
1671 ASSERT_COMP_ID(compId, getComponentCount());
1672
1673 auto remote = getRemoteAddress(compId);
1674
1675 if (!remote) {
1676 if (pimpl_->logger_)
1677 pimpl_->logger_->error("[ice:{}] can't find remote address for component {:d}", fmt::ptr(pimpl_), compId);
1678 errno = EINVAL;
1679 return -1;
1680 }
1681
1682 std::unique_lock dlk(pimpl_->sendDataMutex_, std::defer_lock);
1683 if (isTCPEnabled())
1684 dlk.lock();
1685
1686 jami_tracepoint(ice_transport_send,
1687 reinterpret_cast<uint64_t>(this),
1688 compId,
1689 len,
1690 remote.toString().c_str());
1691
1692 auto status = pj_ice_strans_sendto2(pimpl_->icest_,
1693 compId,
1694 buf,
1695 len,
1696 remote.pjPtr(),
1697 remote.getLength());
1698
1699 jami_tracepoint(ice_transport_send_status, status);
1700
1701 if (status == PJ_EPENDING && isTCPEnabled()) {
1702 // NOTE; because we are in TCP, the sent size will count the header (2
1703 // bytes length).
1704 pimpl_->waitDataCv_.wait(dlk, [&] {
1705 return pimpl_->lastSentLen_ >= static_cast<pj_size_t>(len) or pimpl_->destroying_;
1706 });
1707 pimpl_->lastSentLen_ = 0;
1708 } else if (status != PJ_SUCCESS && status != PJ_EPENDING) {
1709 if (status == PJ_EBUSY) {
1710 errno = EAGAIN;
1711 } else {
1712 if (pimpl_->logger_)
1713 pimpl_->logger_->error("[ice:{}] ice send failed: {:s}", fmt::ptr(pimpl_), sip_utils::sip_strerror(status));
1714 errno = EIO;
1715 }
1716 return -1;
1717 }
1718
1719 return len;
1720}
1721
1722bool
1723IceTransport::waitForInitialization(std::chrono::milliseconds timeout)
1724{
1725 return pimpl_->_waitForInitialization(timeout);
1726}
1727
1728ssize_t
1729IceTransport::waitForData(unsigned compId, std::chrono::milliseconds timeout, std::error_code& ec)
1730{
1731 ASSERT_COMP_ID(compId, getComponentCount());
1732 return pimpl_->peerChannels_.at(compId - 1).wait(timeout, ec);
1733}
1734
1735bool
1736IceTransport::isTCPEnabled()
1737{
1738 return pimpl_->isTcpEnabled();
1739}
1740
1741ICESDP
1742IceTransport::parseIceCandidates(std::string_view sdp_msg)
1743{
1744 if (pimpl_->streamsCount_ != 1) {
1745 if (pimpl_->logger_)
Sébastien Blincf569402023-07-27 09:46:40 -04001746 pimpl_->logger_->error("Expected exactly one stream per SDP (found {} streams)", pimpl_->streamsCount_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001747 return {};
1748 }
1749
1750 ICESDP res;
1751 int nr = 0;
Adrien Béraud1ae60aa2023-07-07 09:55:09 -04001752 for (std::string_view line; dhtnet::getline(sdp_msg, line); nr++) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001753 if (nr == 0) {
1754 res.rem_ufrag = line;
1755 } else if (nr == 1) {
1756 res.rem_pwd = line;
1757 } else {
1758 IceCandidate cand;
1759 if (parseIceAttributeLine(0, std::string(line), cand)) {
1760 if (pimpl_->logger_)
1761 pimpl_->logger_->debug("[ice:{}] Add remote candidate: {}",
1762 fmt::ptr(pimpl_),
1763 line);
1764 res.rem_candidates.emplace_back(cand);
1765 }
1766 }
1767 }
1768 return res;
1769}
1770
1771void
1772IceTransport::setDefaultRemoteAddress(unsigned comp_id, const IpAddr& addr)
1773{
1774 pimpl_->setDefaultRemoteAddress(comp_id, addr);
1775}
1776
1777std::string
1778IceTransport::link() const
1779{
1780 return pimpl_->link();
1781}
1782
1783//==============================================================================
1784
Adrien Béraud89933c12023-07-26 14:53:30 -04001785IceTransportFactory::IceTransportFactory(const std::shared_ptr<Logger>& logger)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001786 : cp_(new pj_caching_pool(),
1787 [](pj_caching_pool* p) {
1788 pj_caching_pool_destroy(p);
1789 delete p;
1790 })
1791 , ice_cfg_()
Adrien Béraud89933c12023-07-26 14:53:30 -04001792 , logger_(logger)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001793{
1794 pj_caching_pool_init(cp_.get(), NULL, 0);
1795
1796 pj_ice_strans_cfg_default(&ice_cfg_);
1797 ice_cfg_.stun_cfg.pf = &cp_->factory;
1798
1799 // v2.4.5 of PJNATH has a default of 100ms but RFC 5389 since version 14 requires
1800 // a minimum of 500ms on fixed-line links. Our usual case is wireless links.
1801 // This solves too long ICE exchange by DHT.
1802 // Using 500ms with default PJ_STUN_MAX_TRANSMIT_COUNT (7) gives around 33s before timeout.
1803 ice_cfg_.stun_cfg.rto_msec = 500;
1804
1805 // See https://tools.ietf.org/html/rfc5245#section-8.1.1.2
1806 // If enabled, it may help speed-up the connectivity, but may cause
1807 // the nomination of sub-optimal pairs.
1808 ice_cfg_.opt.aggressive = PJ_FALSE;
1809}
1810
1811IceTransportFactory::~IceTransportFactory() {}
1812
1813std::shared_ptr<IceTransport>
1814IceTransportFactory::createTransport(std::string_view name)
1815{
1816 try {
Adrien Béraud89933c12023-07-26 14:53:30 -04001817 return std::make_shared<IceTransport>(name, logger_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001818 } catch (const std::exception& e) {
1819 //JAMI_ERR("%s", e.what());
1820 return nullptr;
1821 }
1822}
1823
1824std::unique_ptr<IceTransport>
1825IceTransportFactory::createUTransport(std::string_view name)
1826{
1827 try {
Adrien Béraud89933c12023-07-26 14:53:30 -04001828 return std::make_unique<IceTransport>(name, logger_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001829 } catch (const std::exception& e) {
1830 //JAMI_ERR("%s", e.what());
1831 return nullptr;
1832 }
1833}
1834
1835//==============================================================================
1836
1837void
1838IceSocket::close()
1839{
1840 if (ice_transport_)
1841 ice_transport_->setOnRecv(compId_, {});
1842 ice_transport_.reset();
1843}
1844
1845ssize_t
1846IceSocket::send(const unsigned char* buf, size_t len)
1847{
1848 if (not ice_transport_)
1849 return -1;
1850 return ice_transport_->send(compId_, buf, len);
1851}
1852
1853ssize_t
1854IceSocket::waitForData(std::chrono::milliseconds timeout)
1855{
1856 if (not ice_transport_)
1857 return -1;
1858
1859 std::error_code ec;
1860 return ice_transport_->waitForData(compId_, timeout, ec);
1861}
1862
1863void
1864IceSocket::setOnRecv(IceRecvCb cb)
1865{
1866 if (ice_transport_)
1867 ice_transport_->setOnRecv(compId_, cb);
1868}
1869
1870uint16_t
1871IceSocket::getTransportOverhead()
1872{
1873 if (not ice_transport_)
1874 return 0;
1875
1876 return (ice_transport_->getRemoteAddress(compId_).getFamily() == AF_INET) ? IPV4_HEADER_SIZE
1877 : IPV6_HEADER_SIZE;
1878}
1879
1880void
1881IceSocket::setDefaultRemoteAddress(const IpAddr& addr)
1882{
1883 if (ice_transport_)
1884 ice_transport_->setDefaultRemoteAddress(compId_, addr);
1885}
1886
Sébastien Blin464bdff2023-07-19 08:02:53 -04001887} // namespace dhtnet