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