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