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