blob: 52a6383ee023862b97de280849ade61487e21027 [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 }
Adrien Béraud738aedb2024-02-18 14:56:26 -0500426 if (options.qosType.size() == 1) {
427 config_.stun.cfg.qos_type = (pj_qos_type)options.qosType[0];
428 config_.turn.cfg.qos_type = (pj_qos_type)options.qosType[0];
429 }
430 if (options.qosType.size() == compCount_) {
431 for (unsigned i = 0; i < compCount_; ++i) {
432 config_.comp[i].qos_type = (pj_qos_type)(options.qosType[i]);
433 }
434 }
Adrien Béraud612b55b2023-05-29 10:42:04 -0400435
436 pool_.reset(
Amna81221ad2023-09-14 17:33:26 -0400437 pj_pool_create(factory->getPoolFactory(), "IceTransport.pool", 512, 512, NULL));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400438 if (not pool_)
439 throw std::runtime_error("pj_pool_create() failed");
440
441 // Note: For server reflexive candidates, UPNP mappings will
442 // be used if available. Then, the public address learnt during
443 // the account registration process will be added only if it
444 // differs from the UPNP public address.
445 // Also note that UPNP candidates should be added first in order
446 // to have a higher priority when performing the connectivity
447 // checks.
448 // STUN configs layout:
449 // - index 0 : host IPv4
450 // - index 1 : host IPv6
451 // - index 2 : upnp/generic srflx IPv4.
452 // - index 3 : generic srflx (if upnp exists and different)
453
454 config_.stun_tp_cnt = 0;
455
Adrien Béraud370257c2023-08-15 20:53:09 -0400456 // if (logger_)
457 // logger_->debug("[ice:{}] Add host candidates", fmt::ptr(this));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400458 addStunConfig(pj_AF_INET());
459 addStunConfig(pj_AF_INET6());
460
461 std::vector<std::pair<IpAddr, IpAddr>> upnpSrflxCand;
462 if (upnp_) {
463 requestUpnpMappings();
464 upnpSrflxCand = setupUpnpReflexiveCandidates();
465 if (not upnpSrflxCand.empty()) {
466 addServerReflexiveCandidates(upnpSrflxCand);
Adrien Béraud370257c2023-08-15 20:53:09 -0400467 // if (logger_)
468 // logger_->debug("[ice:{}] Added UPNP srflx candidates:", fmt::ptr(this));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400469 }
470 }
471
472 auto genericSrflxCand = setupGenericReflexiveCandidates();
473
474 if (not genericSrflxCand.empty()) {
475 // Generic srflx candidates will be added only if different
476 // from upnp candidates.
477 if (upnpSrflxCand.empty()
478 or (upnpSrflxCand[0].second.toString() != genericSrflxCand[0].second.toString())) {
479 addServerReflexiveCandidates(genericSrflxCand);
Adrien Béraud370257c2023-08-15 20:53:09 -0400480 // if (logger_)
481 // logger_->debug("[ice:{}] Added generic srflx candidates:", fmt::ptr(this));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400482 }
483 }
484
485 if (upnpSrflxCand.empty() and genericSrflxCand.empty()) {
486 if (logger_)
487 logger_->warn("[ice:{}] No server reflexive candidates added", fmt::ptr(this));
488 }
489
490 pj_ice_strans_cb icecb;
491 pj_bzero(&icecb, sizeof(icecb));
492
493 icecb.on_rx_data = [](pj_ice_strans* ice_st,
494 unsigned comp_id,
495 void* pkt,
496 pj_size_t size,
497 const pj_sockaddr_t* /*src_addr*/,
498 unsigned /*src_addr_len*/) {
499 if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st)))
500 tr->onReceiveData(comp_id, pkt, size);
501 };
502
503 icecb.on_ice_complete = [](pj_ice_strans* ice_st, pj_ice_strans_op op, pj_status_t status) {
504 if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st)))
505 tr->onComplete(ice_st, op, status);
506 };
507
508 if (isTcp_) {
509 icecb.on_data_sent = [](pj_ice_strans* ice_st, pj_ssize_t size) {
510 if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st))) {
511 std::lock_guard lk(tr->sendDataMutex_);
512 tr->lastSentLen_ += size;
513 tr->waitDataCv_.notify_all();
514 }
515 };
516 }
517
518 icecb.on_destroy = [](pj_ice_strans* ice_st) {
519 if (auto* tr = static_cast<Impl*>(pj_ice_strans_get_user_data(ice_st)))
520 tr->cancelOperations(); // Avoid upper layer to manage this ; Stop read operations
521 };
522
523 // Add STUN servers
524 for (auto& server : stunServers_)
Sébastien Blincf569402023-07-27 09:46:40 -0400525 add_stun_server(*pool_, config_, server, logger_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400526
527 // Add TURN servers
528 for (auto& server : turnServers_)
Sébastien Blincf569402023-07-27 09:46:40 -0400529 add_turn_server(*pool_, config_, server, logger_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400530
531 static constexpr auto IOQUEUE_MAX_HANDLES = std::min(PJ_IOQUEUE_MAX_HANDLES, 64);
532 TRY(pj_timer_heap_create(pool_.get(), 100, &config_.stun_cfg.timer_heap));
533 TRY(pj_ioqueue_create(pool_.get(), IOQUEUE_MAX_HANDLES, &config_.stun_cfg.ioqueue));
534 std::ostringstream sessionName {};
535 // We use the instance pointer as the PJNATH session name in order
536 // to easily identify the logs reported by PJNATH.
537 sessionName << this;
538 pj_status_t status = pj_ice_strans_create(sessionName.str().c_str(),
539 &config_,
540 compCount_,
541 this,
542 &icecb,
543 &icest_);
544
545 if (status != PJ_SUCCESS || icest_ == nullptr) {
546 throw std::runtime_error("pj_ice_strans_create() failed");
547 }
548
549 // Must be created after any potential failure
550 thread_ = std::thread([this] {
551 while (not threadTerminateFlags_) {
552 // NOTE: handleEvents can return false in this case
553 // but here we don't care if there is event or not.
554 handleEvents(HANDLE_EVENT_DURATION);
555 }
556 });
557}
558
559bool
560IceTransport::Impl::_isInitialized() const
561{
562 if (auto *icest = icest_) {
563 auto state = pj_ice_strans_get_state(icest);
564 return state >= PJ_ICE_STRANS_STATE_SESS_READY and state != PJ_ICE_STRANS_STATE_FAILED;
565 }
566 return false;
567}
568
569bool
570IceTransport::Impl::_isStarted() const
571{
572 if (auto *icest = icest_) {
573 auto state = pj_ice_strans_get_state(icest);
574 return state >= PJ_ICE_STRANS_STATE_NEGO and state != PJ_ICE_STRANS_STATE_FAILED;
575 }
576 return false;
577}
578
579bool
580IceTransport::Impl::_isRunning() const
581{
582 if (auto *icest = icest_) {
583 auto state = pj_ice_strans_get_state(icest);
584 return state >= PJ_ICE_STRANS_STATE_RUNNING and state != PJ_ICE_STRANS_STATE_FAILED;
585 }
586 return false;
587}
588
589bool
590IceTransport::Impl::_isFailed() const
591{
592 if (auto *icest = icest_)
593 return pj_ice_strans_get_state(icest) == PJ_ICE_STRANS_STATE_FAILED;
594 return false;
595}
596
597bool
598IceTransport::Impl::handleEvents(unsigned max_msec)
599{
600 // By tests, never seen more than two events per 500ms
601 static constexpr auto MAX_NET_EVENTS = 2;
602
603 pj_time_val max_timeout = {0, static_cast<long>(max_msec)};
604 pj_time_val timeout = {0, 0};
605 unsigned net_event_count = 0;
606
607 pj_timer_heap_poll(config_.stun_cfg.timer_heap, &timeout);
608 auto hasActiveTimer = timeout.sec != PJ_MAXINT32 || timeout.msec != PJ_MAXINT32;
609
610 // timeout limitation
611 if (hasActiveTimer)
612 pj_time_val_normalize(&timeout);
613
614 if (PJ_TIME_VAL_GT(timeout, max_timeout)) {
615 timeout = max_timeout;
616 }
617
618 do {
619 auto n_events = pj_ioqueue_poll(config_.stun_cfg.ioqueue, &timeout);
620
621 // timeout
622 if (not n_events)
623 return hasActiveTimer;
624
625 // error
626 if (n_events < 0) {
627 const auto err = pj_get_os_error();
628 // Kept as debug as some errors are "normal" in regular context
629 if (logger_)
630 logger_->debug("[ice:{}] ioqueue error {:d}: {:s}", fmt::ptr(this), err, sip_utils::sip_strerror(err));
631 std::this_thread::sleep_for(std::chrono::milliseconds(PJ_TIME_VAL_MSEC(timeout)));
632 return hasActiveTimer;
633 }
634
635 net_event_count += n_events;
636 timeout.sec = timeout.msec = 0;
637 } while (net_event_count < MAX_NET_EVENTS);
638 return hasActiveTimer;
639}
640
641int
642IceTransport::Impl::flushTimerHeapAndIoQueue()
643{
644 pj_time_val timerTimeout = {0, 0};
645 pj_time_val defaultWaitTime = {0, HANDLE_EVENT_DURATION};
646 bool hasActiveTimer = false;
647 std::chrono::milliseconds totalWaitTime {0};
Adrien Béraud9a676812023-08-21 09:02:16 -0400648 // auto const start = std::chrono::steady_clock::now();
Adrien Béraud612b55b2023-05-29 10:42:04 -0400649 // We try to process pending events as fast as possible to
650 // speed-up the release.
651 int maxEventToProcess = 10;
652
653 do {
654 if (checkEventQueue(maxEventToProcess) < 0)
655 return -1;
656
657 pj_timer_heap_poll(config_.stun_cfg.timer_heap, &timerTimeout);
658 hasActiveTimer = !(timerTimeout.sec == PJ_MAXINT32 && timerTimeout.msec == PJ_MAXINT32);
659
660 if (hasActiveTimer) {
661 pj_time_val_normalize(&timerTimeout);
662 auto waitTime = std::chrono::milliseconds(
663 std::min(PJ_TIME_VAL_MSEC(timerTimeout), PJ_TIME_VAL_MSEC(defaultWaitTime)));
664 std::this_thread::sleep_for(waitTime);
665 totalWaitTime += waitTime;
666 }
667 } while (hasActiveTimer && totalWaitTime < std::chrono::milliseconds(MAX_DESTRUCTION_TIMEOUT));
668
Adrien Béraud9a676812023-08-21 09:02:16 -0400669 // auto duration = std::chrono::steady_clock::now() - start;
670 // if (logger_)
671 // logger_->debug("[ice:{}] Timer heap flushed after {}", fmt::ptr(this), dht::print_duration(duration));
Adrien Béraud612b55b2023-05-29 10:42:04 -0400672
673 return static_cast<int>(pj_timer_heap_count(config_.stun_cfg.timer_heap));
674}
675
676int
677IceTransport::Impl::checkEventQueue(int maxEventToPoll)
678{
679 pj_time_val timeout = {0, 0};
680 int eventCount = 0;
681 int events = 0;
682
683 do {
684 events = pj_ioqueue_poll(config_.stun_cfg.ioqueue, &timeout);
685 if (events < 0) {
686 const auto err = pj_get_os_error();
687 if (logger_)
688 logger_->error("[ice:{}] ioqueue error {:d}: {:s}", fmt::ptr(this), err, sip_utils::sip_strerror(err));
689 return events;
690 }
691
692 eventCount += events;
693
694 } while (events > 0 && eventCount < maxEventToPoll);
695
696 return eventCount;
697}
698
699void
700IceTransport::Impl::onComplete(pj_ice_strans*, pj_ice_strans_op op, pj_status_t status)
701{
702 const char* opname = op == PJ_ICE_STRANS_OP_INIT ? "initialization"
703 : op == PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation"
704 : "unknown_op";
705
706 const bool done = status == PJ_SUCCESS;
707 if (done) {
708 if (logger_)
709 logger_->debug("[ice:{}] {:s} {:s} success",
710 fmt::ptr(this),
711 (config_.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP"),
712 opname);
713 } else {
714 if (logger_)
715 logger_->error("[ice:{}] {:s} {:s} failed: {:s}",
716 fmt::ptr(this),
717 (config_.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP"),
718 opname,
719 sip_utils::sip_strerror(status));
720 }
721
722 if (done and op == PJ_ICE_STRANS_OP_INIT) {
723 if (initiatorSession_)
724 setInitiatorSession();
725 else
726 setSlaveSession();
727 }
728
729 if (op == PJ_ICE_STRANS_OP_INIT and on_initdone_cb_)
730 on_initdone_cb_(done);
731 else if (op == PJ_ICE_STRANS_OP_NEGOTIATION) {
732 if (done) {
733 // Dump of connection pairs
734 if (logger_)
735 logger_->debug("[ice:{}] {:s} connection pairs ([comp id] local [type] <-> remote [type]):\n{:s}",
736 fmt::ptr(this),
737 (config_.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP"),
738 link());
739 }
740 if (on_negodone_cb_)
741 on_negodone_cb_(done);
742 }
743
744 iceCV_.notify_all();
745}
746
747std::string
748IceTransport::Impl::link() const
749{
750 std::ostringstream out;
751 for (unsigned strm = 1; strm <= streamsCount_ * compCountPerStream_; strm++) {
752 auto absIdx = strm;
753 auto comp = (strm + 1) / compCountPerStream_;
754 auto laddr = getLocalAddress(absIdx);
755 auto raddr = getRemoteAddress(absIdx);
756
757 if (laddr and laddr.getPort() != 0 and raddr and raddr.getPort() != 0) {
758 out << " [" << comp << "] " << laddr.toString(true, true) << " ["
759 << getCandidateType(getSelectedCandidate(absIdx, false)) << "] "
760 << " <-> " << raddr.toString(true, true) << " ["
761 << getCandidateType(getSelectedCandidate(absIdx, true)) << "] " << '\n';
762 } else {
763 out << " [" << comp << "] disabled\n";
764 }
765 }
766 return out.str();
767}
768
769bool
770IceTransport::Impl::setInitiatorSession()
771{
772 if (logger_)
773 logger_->debug("[ice:{}] as master", fmt::ptr(this));
774 initiatorSession_ = true;
775 if (_isInitialized()) {
776 auto status = pj_ice_strans_change_role(icest_, PJ_ICE_SESS_ROLE_CONTROLLING);
777 if (status != PJ_SUCCESS) {
778 if (logger_)
779 logger_->error("[ice:{}] role change failed: {:s}", fmt::ptr(this), sip_utils::sip_strerror(status));
780 return false;
781 }
782 return true;
783 }
784 return createIceSession(PJ_ICE_SESS_ROLE_CONTROLLING);
785}
786
787bool
788IceTransport::Impl::setSlaveSession()
789{
790 if (logger_)
791 logger_->debug("[ice:{}] as slave", fmt::ptr(this));
792 initiatorSession_ = false;
793 if (_isInitialized()) {
794 auto status = pj_ice_strans_change_role(icest_, PJ_ICE_SESS_ROLE_CONTROLLED);
795 if (status != PJ_SUCCESS) {
796 if (logger_)
797 logger_->error("[ice:{}] role change failed: {:s}", fmt::ptr(this), sip_utils::sip_strerror(status));
798 return false;
799 }
800 return true;
801 }
802 return createIceSession(PJ_ICE_SESS_ROLE_CONTROLLED);
803}
804
805const pj_ice_sess_cand*
806IceTransport::Impl::getSelectedCandidate(unsigned comp_id, bool remote) const
807{
808 ASSERT_COMP_ID(comp_id, compCount_);
809
810 // Return the selected candidate pair. Might not be the nominated pair if
811 // ICE has not concluded yet, but should be the nominated pair afterwards.
812 if (not _isRunning()) {
813 if (logger_)
814 logger_->error("[ice:{}] ICE transport is not running", fmt::ptr(this));
815 return nullptr;
816 }
817
818 const auto* sess = pj_ice_strans_get_valid_pair(icest_, comp_id);
819 if (sess == nullptr) {
820 if (logger_)
821 logger_->warn("[ice:{}] Component {} has no valid pair (disabled)", fmt::ptr(this), comp_id);
822 return nullptr;
823 }
824
825 if (remote)
826 return sess->rcand;
827 else
828 return sess->lcand;
829}
830
831IpAddr
832IceTransport::Impl::getLocalAddress(unsigned comp_id) const
833{
834 ASSERT_COMP_ID(comp_id, compCount_);
835
836 if (auto cand = getSelectedCandidate(comp_id, false))
837 return cand->addr;
838
839 return {};
840}
841
842IpAddr
843IceTransport::Impl::getRemoteAddress(unsigned comp_id) const
844{
845 ASSERT_COMP_ID(comp_id, compCount_);
846
847 if (auto cand = getSelectedCandidate(comp_id, true))
848 return cand->addr;
849
850 return {};
851}
852
853const char*
854IceTransport::Impl::getCandidateType(const pj_ice_sess_cand* cand)
855{
856 auto name = cand ? pj_ice_get_cand_type_name(cand->type) : nullptr;
857 return name ? name : "?";
858}
859
860void
861IceTransport::Impl::getUFragPwd()
862{
863 if (icest_) {
864 pj_str_t local_ufrag, local_pwd;
865
866 pj_ice_strans_get_ufrag_pwd(icest_, &local_ufrag, &local_pwd, nullptr, nullptr);
867 local_ufrag_.assign(local_ufrag.ptr, local_ufrag.slen);
868 local_pwd_.assign(local_pwd.ptr, local_pwd.slen);
869 }
870}
871
872bool
873IceTransport::Impl::createIceSession(pj_ice_sess_role role)
874{
875 if (not icest_) {
876 return false;
877 }
878
879 if (pj_ice_strans_init_ice(icest_, role, nullptr, nullptr) != PJ_SUCCESS) {
880 if (logger_)
881 logger_->error("[ice:{}] pj_ice_strans_init_ice() failed", fmt::ptr(this));
882 return false;
883 }
884
885 // Fetch some information on local configuration
886 getUFragPwd();
887
888 if (logger_)
Sébastien Blincf569402023-07-27 09:46:40 -0400889 logger_->debug("[ice:{}] (local) ufrag={}, pwd={}", fmt::ptr(this), local_ufrag_, local_pwd_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400890
891 return true;
892}
893
894bool
895IceTransport::Impl::addStunConfig(int af)
896{
897 if (config_.stun_tp_cnt >= PJ_ICE_MAX_STUN) {
898 if (logger_)
Sébastien Blincf569402023-07-27 09:46:40 -0400899 logger_->error("Max number of STUN configurations reached ({})", PJ_ICE_MAX_STUN);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400900 return false;
901 }
902
903 if (af != pj_AF_INET() and af != pj_AF_INET6()) {
904 if (logger_)
Sébastien Blincf569402023-07-27 09:46:40 -0400905 logger_->error("Invalid address familly ({})", af);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400906 return false;
907 }
908
909 auto& stun = config_.stun_tp[config_.stun_tp_cnt++];
910
911 pj_ice_strans_stun_cfg_default(&stun);
912 stun.cfg.max_pkt_size = STUN_MAX_PACKET_SIZE;
913 stun.af = af;
914 stun.conn_type = config_.stun.conn_type;
915
Adrien Béraud370257c2023-08-15 20:53:09 -0400916 // if (logger_)
917 // logger_->debug("[ice:{}] added host stun config for {:s} transport",
918 // fmt::ptr(this),
919 // config_.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP");
Adrien Béraud612b55b2023-05-29 10:42:04 -0400920
921 return true;
922}
923
924void
925IceTransport::Impl::requestUpnpMappings()
926{
927 // Must be called once !
928
929 std::lock_guard<std::mutex> lock(upnpMutex_);
930
931 if (not upnp_)
932 return;
933
934 auto transport = isTcpEnabled() ? PJ_CAND_TCP_PASSIVE : PJ_CAND_UDP;
935 auto portType = transport == PJ_CAND_UDP ? PortType::UDP : PortType::TCP;
936
937 // Request upnp mapping for each component.
938 for (unsigned id = 1; id <= compCount_; id++) {
939 // Set port number to 0 to get any available port.
940 Mapping requestedMap(portType);
941
942 // Request the mapping
943 Mapping::sharedPtr_t mapPtr = upnp_->reserveMapping(requestedMap);
944
945 // To use a mapping, it must be valid, open and has valid host address.
946 if (mapPtr and mapPtr->getMapKey() and (mapPtr->getState() == MappingState::OPEN)
947 and mapPtr->hasValidHostAddress()) {
948 std::lock_guard<std::mutex> lock(upnpMappingsMutex_);
949 auto ret = upnpMappings_.emplace(mapPtr->getMapKey(), *mapPtr);
950 if (ret.second) {
951 if (logger_)
952 logger_->debug("[ice:{}] UPNP mapping {:s} successfully allocated",
953 fmt::ptr(this),
954 mapPtr->toString(true));
955 } else {
956 if (logger_)
957 logger_->warn("[ice:{}] UPNP mapping {:s} already in the list!",
958 fmt::ptr(this),
959 mapPtr->toString());
960 }
961 } else {
962 if (logger_)
963 logger_->warn("[ice:{}] UPNP mapping request failed!", fmt::ptr(this));
964 upnp_->releaseMapping(requestedMap);
965 }
966 }
967}
968
969bool
970IceTransport::Impl::hasUpnp() const
971{
972 return upnp_ and upnpMappings_.size() == compCount_;
973}
974
975void
976IceTransport::Impl::addServerReflexiveCandidates(
977 const std::vector<std::pair<IpAddr, IpAddr>>& addrList)
978{
979 if (addrList.size() != compCount_) {
980 if (logger_)
981 logger_->warn("[ice:{}] Provided addr list size {} does not match component count {}",
982 fmt::ptr(this),
983 addrList.size(),
984 compCount_);
985 return;
986 }
987 if (compCount_ > PJ_ICE_MAX_COMP) {
988 if (logger_)
989 logger_->error("[ice:{}] Too many components", fmt::ptr(this));
990 return;
991 }
992
993 // Add config for server reflexive candidates (UPNP or from DHT).
994 if (not addStunConfig(pj_AF_INET()))
995 return;
996
997 assert(config_.stun_tp_cnt > 0 && config_.stun_tp_cnt < PJ_ICE_MAX_STUN);
998 auto& stun = config_.stun_tp[config_.stun_tp_cnt - 1];
999
1000 for (unsigned id = 1; id <= compCount_; id++) {
1001 auto idx = id - 1;
1002 auto& localAddr = addrList[idx].first;
1003 auto& publicAddr = addrList[idx].second;
1004
1005 if (logger_)
1006 logger_->debug("[ice:{}] Add srflx reflexive candidates [{:s} : {:s}] for comp {:d}",
1007 fmt::ptr(this),
1008 localAddr.toString(true),
1009 publicAddr.toString(true),
1010 id);
1011
1012 pj_sockaddr_cp(&stun.cfg.user_mapping[idx].local_addr, localAddr.pjPtr());
1013 pj_sockaddr_cp(&stun.cfg.user_mapping[idx].mapped_addr, publicAddr.pjPtr());
1014
1015 if (isTcpEnabled()) {
1016 if (publicAddr.getPort() == 9) {
1017 stun.cfg.user_mapping[idx].tp_type = PJ_CAND_TCP_ACTIVE;
1018 } else {
1019 stun.cfg.user_mapping[idx].tp_type = PJ_CAND_TCP_PASSIVE;
1020 }
1021 } else {
1022 stun.cfg.user_mapping[idx].tp_type = PJ_CAND_UDP;
1023 }
1024 }
1025
1026 stun.cfg.user_mapping_cnt = compCount_;
1027}
1028
1029std::vector<std::pair<IpAddr, IpAddr>>
1030IceTransport::Impl::setupGenericReflexiveCandidates()
1031{
1032 if (not accountLocalAddr_) {
1033 if (logger_)
1034 logger_->warn("[ice:{}] Missing local address, generic srflx candidates wont be generated!",
1035 fmt::ptr(this));
1036 return {};
1037 }
1038
1039 if (not accountPublicAddr_) {
1040 if (logger_)
1041 logger_->warn("[ice:{}] Missing public address, generic srflx candidates wont be generated!",
1042 fmt::ptr(this));
1043 return {};
1044 }
1045
1046 std::vector<std::pair<IpAddr, IpAddr>> addrList;
1047 auto isTcp = isTcpEnabled();
1048
1049 addrList.reserve(compCount_);
1050 for (unsigned id = 1; id <= compCount_; id++) {
1051 // For TCP, the type is set to active, because most likely the incoming
1052 // connection will be blocked by the NAT.
1053 // For UDP use random port number.
1054 uint16_t port = isTcp ? 9
1055 : upnp::Controller::generateRandomPort(isTcp ? PortType::TCP
1056 : PortType::UDP);
1057
1058 accountLocalAddr_.setPort(port);
1059 accountPublicAddr_.setPort(port);
1060 addrList.emplace_back(accountLocalAddr_, accountPublicAddr_);
1061 }
1062
1063 return addrList;
1064}
1065
1066std::vector<std::pair<IpAddr, IpAddr>>
1067IceTransport::Impl::setupUpnpReflexiveCandidates()
1068{
1069 // Add UPNP server reflexive candidates if available.
1070 if (not hasUpnp())
1071 return {};
1072
1073 std::lock_guard<std::mutex> lock(upnpMappingsMutex_);
1074
1075 if (upnpMappings_.size() < (size_t)compCount_) {
1076 if (logger_)
1077 logger_->warn("[ice:{}] Not enough mappings {:d}. Expected {:d}",
1078 fmt::ptr(this),
1079 upnpMappings_.size(),
1080 compCount_);
1081 return {};
1082 }
1083
1084 std::vector<std::pair<IpAddr, IpAddr>> addrList;
1085
1086 addrList.reserve(upnpMappings_.size());
1087 for (auto const& [_, map] : upnpMappings_) {
1088 assert(map.getMapKey());
1089 IpAddr localAddr {map.getInternalAddress()};
1090 localAddr.setPort(map.getInternalPort());
1091 IpAddr publicAddr {map.getExternalAddress()};
1092 publicAddr.setPort(map.getExternalPort());
1093 addrList.emplace_back(localAddr, publicAddr);
1094 }
1095
1096 return addrList;
1097}
1098
1099void
1100IceTransport::Impl::setDefaultRemoteAddress(unsigned compId, const IpAddr& addr)
1101{
1102 ASSERT_COMP_ID(compId, compCount_);
1103
1104 iceDefaultRemoteAddr_[compId - 1] = addr;
1105 // The port does not matter. Set it 0 to avoid confusion.
1106 iceDefaultRemoteAddr_[compId - 1].setPort(0);
1107}
1108
1109IpAddr
1110IceTransport::Impl::getDefaultRemoteAddress(unsigned compId) const
1111{
Adrien Béraud0e1bffa2023-10-15 17:32:55 -04001112 if (compId > compCount_) {
1113 if (logger_)
1114 logger_->error("[ice:{}] Invalid component id {:d}", fmt::ptr(this), compId);
1115 return {};
1116 }
Adrien Béraud612b55b2023-05-29 10:42:04 -04001117 return iceDefaultRemoteAddr_[compId - 1];
1118}
1119
1120void
1121IceTransport::Impl::onReceiveData(unsigned comp_id, void* pkt, pj_size_t size)
1122{
1123 ASSERT_COMP_ID(comp_id, compCount_);
1124
1125 jami_tracepoint_if_enabled(ice_transport_recv,
1126 reinterpret_cast<uint64_t>(this),
1127 comp_id,
1128 size,
1129 getRemoteAddress(comp_id).toString().c_str());
1130 if (size == 0)
1131 return;
1132
1133 {
1134 auto& io = compIO_[comp_id - 1];
1135 std::lock_guard<std::mutex> lk(io.mutex);
1136
1137 if (io.recvCb) {
1138 io.recvCb((uint8_t*) pkt, size);
1139 return;
1140 }
1141 }
1142
1143 std::error_code ec;
1144 auto err = peerChannels_.at(comp_id - 1).write((const char*) pkt, size, ec);
1145 if (err < 0) {
1146 if (logger_)
1147 logger_->error("[ice:{}] rx: channel is closed", fmt::ptr(this));
1148 }
1149}
1150
1151bool
1152IceTransport::Impl::_waitForInitialization(std::chrono::milliseconds timeout)
1153{
1154 IceLock lk(icest_);
1155
1156 if (not iceCV_.wait_for(lk, timeout, [this] {
1157 return threadTerminateFlags_ or _isInitialized() or _isFailed();
1158 })) {
1159 if (logger_)
1160 logger_->warn("[ice:{}] waitForInitialization: timeout", fmt::ptr(this));
1161 return false;
1162 }
1163
1164 return _isInitialized();
1165}
1166
1167//==============================================================================
1168
Adrien Béraud89933c12023-07-26 14:53:30 -04001169IceTransport::IceTransport(std::string_view name, const std::shared_ptr<dht::log::Logger>& logger)
1170 : pimpl_ {std::make_unique<Impl>(name, logger)}
Adrien Béraud612b55b2023-05-29 10:42:04 -04001171{}
1172
1173IceTransport::~IceTransport()
1174{
1175 cancelOperations();
1176}
1177
1178const std::shared_ptr<dht::log::Logger>&
1179IceTransport::logger() const
1180{
1181 return pimpl_->logger_;
1182}
1183
1184void
1185IceTransport::initIceInstance(const IceTransportOptions& options)
1186{
1187 pimpl_->initIceInstance(options);
1188 jami_tracepoint(ice_transport_context, reinterpret_cast<uint64_t>(this));
1189}
1190
1191bool
1192IceTransport::isInitialized() const
1193{
1194 IceLock lk(pimpl_->icest_);
1195 return pimpl_->_isInitialized();
1196}
1197
1198bool
1199IceTransport::isStarted() const
1200{
1201 IceLock lk(pimpl_->icest_);
1202 return pimpl_->_isStarted();
1203}
1204
1205bool
1206IceTransport::isRunning() const
1207{
1208 if (!pimpl_->icest_)
1209 return false;
1210 IceLock lk(pimpl_->icest_);
1211 return pimpl_->_isRunning();
1212}
1213
1214bool
1215IceTransport::isFailed() const
1216{
1217 return pimpl_->_isFailed();
1218}
1219
1220unsigned
1221IceTransport::getComponentCount() const
1222{
1223 return pimpl_->compCount_;
1224}
1225
1226bool
1227IceTransport::setSlaveSession()
1228{
1229 return pimpl_->setSlaveSession();
1230}
1231bool
1232IceTransport::setInitiatorSession()
1233{
1234 return pimpl_->setInitiatorSession();
1235}
1236
1237bool
1238IceTransport::isInitiator() const
1239{
1240 if (isInitialized()) {
1241 return pj_ice_strans_get_role(pimpl_->icest_) == PJ_ICE_SESS_ROLE_CONTROLLING;
1242 }
1243 return pimpl_->initiatorSession_;
1244}
1245
1246bool
1247IceTransport::startIce(const Attribute& rem_attrs, std::vector<IceCandidate>&& rem_candidates)
1248{
1249 if (not isInitialized()) {
1250 if (pimpl_->logger_)
1251 pimpl_->logger_->error("[ice:{}] not initialized transport", fmt::ptr(pimpl_.get()));
1252 pimpl_->is_stopped_ = true;
1253 return false;
1254 }
1255
1256 // pj_ice_strans_start_ice crashes if remote candidates array is empty
1257 if (rem_candidates.empty()) {
1258 if (pimpl_->logger_)
1259 pimpl_->logger_->error("[ice:{}] start failed: no remote candidates", fmt::ptr(pimpl_.get()));
1260 pimpl_->is_stopped_ = true;
1261 return false;
1262 }
1263
1264 auto comp_cnt = std::max(1u, getComponentCount());
1265 if (rem_candidates.size() / comp_cnt > PJ_ICE_ST_MAX_CAND - 1) {
1266 std::vector<IceCandidate> rcands;
1267 rcands.reserve(PJ_ICE_ST_MAX_CAND - 1);
1268 if (pimpl_->logger_)
1269 pimpl_->logger_->warn("[ice:{}] too much candidates detected, trim list.", fmt::ptr(pimpl_.get()));
1270 // Just trim some candidates. To avoid to only take host candidates, iterate
1271 // through the whole list and select some host, some turn and peer reflexives
1272 // It should give at least enough infos to negotiate.
1273 auto maxHosts = 8;
1274 auto maxRelays = PJ_ICE_MAX_TURN;
1275 for (auto& c : rem_candidates) {
1276 if (c.type == PJ_ICE_CAND_TYPE_HOST) {
1277 if (maxHosts == 0)
1278 continue;
1279 maxHosts -= 1;
1280 } else if (c.type == PJ_ICE_CAND_TYPE_RELAYED) {
1281 if (maxRelays == 0)
1282 continue;
1283 maxRelays -= 1;
1284 }
1285 if (rcands.size() == PJ_ICE_ST_MAX_CAND - 1)
1286 break;
1287 rcands.emplace_back(std::move(c));
1288 }
1289 rem_candidates = std::move(rcands);
1290 }
1291
1292 pj_str_t ufrag, pwd;
1293 if (pimpl_->logger_)
1294 pimpl_->logger_->debug("[ice:{}] negotiation starting ({:d} remote candidates)",
1295 fmt::ptr(pimpl_),
1296 rem_candidates.size());
1297
1298 auto status = pj_ice_strans_start_ice(pimpl_->icest_,
1299 pj_strset(&ufrag,
1300 (char*) rem_attrs.ufrag.c_str(),
1301 rem_attrs.ufrag.size()),
1302 pj_strset(&pwd,
1303 (char*) rem_attrs.pwd.c_str(),
1304 rem_attrs.pwd.size()),
1305 rem_candidates.size(),
1306 rem_candidates.data());
1307 if (status != PJ_SUCCESS) {
1308 if (pimpl_->logger_)
1309 pimpl_->logger_->error("[ice:{}] start failed: {:s}", fmt::ptr(pimpl_.get()), sip_utils::sip_strerror(status));
1310 pimpl_->is_stopped_ = true;
1311 return false;
1312 }
1313
1314 return true;
1315}
1316
1317bool
1318IceTransport::startIce(const SDP& sdp)
1319{
1320 if (pimpl_->streamsCount_ != 1) {
1321 if (pimpl_->logger_)
Adrien Béraud49b07192023-06-13 20:03:33 -04001322 pimpl_->logger_->error(FMT_STRING("Expected exactly one stream per SDP (found {:d} streams)"), pimpl_->streamsCount_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001323 return false;
1324 }
1325
1326 if (not isInitialized()) {
1327 if (pimpl_->logger_)
1328 pimpl_->logger_->error(FMT_STRING("[ice:{}] not initialized transport"), fmt::ptr(pimpl_));
1329 pimpl_->is_stopped_ = true;
1330 return false;
1331 }
1332
1333 for (unsigned id = 1; id <= getComponentCount(); id++) {
1334 auto candVec = getLocalCandidates(id);
1335 for (auto const& cand : candVec) {
1336 if (pimpl_->logger_)
1337 pimpl_->logger_->debug("[ice:{}] Using local candidate {:s} for comp {:d}",
1338 fmt::ptr(pimpl_), cand, id);
1339 }
1340 }
1341
1342 if (pimpl_->logger_)
1343 pimpl_->logger_->debug("[ice:{}] negotiation starting ({:u} remote candidates)",
1344 fmt::ptr(pimpl_), sdp.candidates.size());
1345 pj_str_t ufrag, pwd;
1346
1347 std::vector<IceCandidate> rem_candidates;
1348 rem_candidates.reserve(sdp.candidates.size());
1349 IceCandidate cand;
1350 for (const auto& line : sdp.candidates) {
1351 if (parseIceAttributeLine(0, line, cand))
1352 rem_candidates.emplace_back(cand);
1353 }
1354
1355 auto status = pj_ice_strans_start_ice(pimpl_->icest_,
1356 pj_strset(&ufrag,
1357 (char*) sdp.ufrag.c_str(),
1358 sdp.ufrag.size()),
1359 pj_strset(&pwd, (char*) sdp.pwd.c_str(), sdp.pwd.size()),
1360 rem_candidates.size(),
1361 rem_candidates.data());
1362 if (status != PJ_SUCCESS) {
1363 if (pimpl_->logger_)
1364 pimpl_->logger_->error("[ice:{}] start failed: {:s}", fmt::ptr(pimpl_), sip_utils::sip_strerror(status));
1365 pimpl_->is_stopped_ = true;
1366 return false;
1367 }
1368
1369 return true;
1370}
1371
1372void
1373IceTransport::cancelOperations()
1374{
1375 pimpl_->cancelOperations();
1376}
1377
1378IpAddr
1379IceTransport::getLocalAddress(unsigned comp_id) const
1380{
1381 return pimpl_->getLocalAddress(comp_id);
1382}
1383
1384IpAddr
1385IceTransport::getRemoteAddress(unsigned comp_id) const
1386{
1387 // Return the default remote address if set.
1388 // Note that the default remote addresses are the addresses
1389 // set in the 'c=' and 'a=rtcp' lines of the received SDP.
1390 // See pj_ice_strans_sendto2() for more details.
1391 if (auto defaultAddr = pimpl_->getDefaultRemoteAddress(comp_id)) {
1392 return defaultAddr;
1393 }
1394
1395 return pimpl_->getRemoteAddress(comp_id);
1396}
1397
1398const IceTransport::Attribute
1399IceTransport::getLocalAttributes() const
1400{
1401 return {pimpl_->local_ufrag_, pimpl_->local_pwd_};
1402}
1403
1404std::vector<std::string>
1405IceTransport::getLocalCandidates(unsigned comp_id) const
1406{
1407 ASSERT_COMP_ID(comp_id, getComponentCount());
1408 std::vector<std::string> res;
1409 pj_ice_sess_cand cand[MAX_CANDIDATES];
1410 unsigned cand_cnt = PJ_ARRAY_SIZE(cand);
1411
1412 if (!isInitialized()) {
1413 return res;
1414 }
1415
1416 if (pj_ice_strans_enum_cands(pimpl_->icest_, comp_id, &cand_cnt, cand) != PJ_SUCCESS) {
1417 if (pimpl_->logger_)
1418 pimpl_->logger_->error("[ice:{}] pj_ice_strans_enum_cands() failed", fmt::ptr(pimpl_));
1419 return res;
1420 }
1421
1422 res.reserve(cand_cnt);
1423 for (unsigned i = 0; i < cand_cnt; ++i) {
1424 /** Section 4.5, RFC 6544 (https://tools.ietf.org/html/rfc6544)
1425 * candidate-attribute = "candidate" ":" foundation SP component-id
1426 * SP "TCP" SP priority SP connection-address SP port SP cand-type [SP
1427 * rel-addr] [SP rel-port] SP tcp-type-ext
1428 * *(SP extension-att-name SP
1429 * extension-att-value)
1430 *
1431 * tcp-type-ext = "tcptype" SP tcp-type
1432 * tcp-type = "active" / "passive" / "so"
1433 */
1434 char ipaddr[PJ_INET6_ADDRSTRLEN];
1435 std::string tcp_type;
1436 if (cand[i].transport != PJ_CAND_UDP) {
1437 tcp_type += " tcptype";
1438 switch (cand[i].transport) {
1439 case PJ_CAND_TCP_ACTIVE:
1440 tcp_type += " active";
1441 break;
1442 case PJ_CAND_TCP_PASSIVE:
1443 tcp_type += " passive";
1444 break;
1445 case PJ_CAND_TCP_SO:
1446 default:
1447 tcp_type += " so";
1448 break;
1449 }
1450 }
1451 res.emplace_back(
1452 fmt::format("{} {} {} {} {} {} typ {}{}",
1453 sip_utils::as_view(cand[i].foundation),
1454 cand[i].comp_id,
1455 (cand[i].transport == PJ_CAND_UDP ? "UDP" : "TCP"),
1456 cand[i].prio,
1457 pj_sockaddr_print(&cand[i].addr, ipaddr, sizeof(ipaddr), 0),
1458 pj_sockaddr_get_port(&cand[i].addr),
1459 pj_ice_get_cand_type_name(cand[i].type),
1460 tcp_type));
1461 }
1462
1463 return res;
1464}
1465std::vector<std::string>
1466IceTransport::getLocalCandidates(unsigned streamIdx, unsigned compId) const
1467{
1468 ASSERT_COMP_ID(compId, getComponentCount());
1469
1470 std::vector<std::string> res;
1471 pj_ice_sess_cand cand[MAX_CANDIDATES];
1472 unsigned cand_cnt = MAX_CANDIDATES;
1473
1474 if (not isInitialized()) {
1475 return res;
1476 }
1477
1478 // In the implementation, the component IDs are enumerated globally
1479 // (per SDP: 1, 2, 3, 4, ...). This is simpler because we create
1480 // only one pj_ice_strans instance. However, the component IDs are
1481 // enumerated per stream in the generated SDP (1, 2, 1, 2, ...) in
1482 // order to be compliant with the spec.
1483
1484 auto globalCompId = streamIdx * 2 + compId;
1485 if (pj_ice_strans_enum_cands(pimpl_->icest_, globalCompId, &cand_cnt, cand) != PJ_SUCCESS) {
1486 if (pimpl_->logger_)
1487 pimpl_->logger_->error("[ice:{}] pj_ice_strans_enum_cands() failed", fmt::ptr(pimpl_));
1488 return res;
1489 }
1490
1491 res.reserve(cand_cnt);
1492 // Build ICE attributes according to RFC 6544, section 4.5.
1493 for (unsigned i = 0; i < cand_cnt; ++i) {
1494 char ipaddr[PJ_INET6_ADDRSTRLEN];
1495 std::string tcp_type;
1496 if (cand[i].transport != PJ_CAND_UDP) {
1497 tcp_type += " tcptype";
1498 switch (cand[i].transport) {
1499 case PJ_CAND_TCP_ACTIVE:
1500 tcp_type += " active";
1501 break;
1502 case PJ_CAND_TCP_PASSIVE:
1503 tcp_type += " passive";
1504 break;
1505 case PJ_CAND_TCP_SO:
1506 default:
1507 tcp_type += " so";
1508 break;
1509 }
1510 }
1511 res.emplace_back(
1512 fmt::format("{} {} {} {} {} {} typ {}{}",
1513 sip_utils::as_view(cand[i].foundation),
1514 compId,
1515 (cand[i].transport == PJ_CAND_UDP ? "UDP" : "TCP"),
1516 cand[i].prio,
1517 pj_sockaddr_print(&cand[i].addr, ipaddr, sizeof(ipaddr), 0),
1518 pj_sockaddr_get_port(&cand[i].addr),
1519 pj_ice_get_cand_type_name(cand[i].type),
1520 tcp_type));
1521 }
1522
1523 return res;
1524}
1525
1526bool
1527IceTransport::parseIceAttributeLine(unsigned streamIdx,
1528 const std::string& line,
1529 IceCandidate& cand) const
1530{
1531 // Silently ignore empty lines
1532 if (line.empty())
1533 return false;
1534
1535 if (streamIdx >= pimpl_->streamsCount_) {
1536 throw std::runtime_error(fmt::format("Stream index {:d} is invalid!", streamIdx));
1537 }
1538
1539 int af, cnt;
1540 char foundation[32], transport[12], ipaddr[80], type[32], tcp_type[32];
1541 pj_str_t tmpaddr;
1542 unsigned comp_id, prio, port;
1543 pj_status_t status;
1544 pj_bool_t is_tcp = PJ_FALSE;
1545
1546 // Parse ICE attribute line according to RFC-6544 section 4.5.
1547 // TODO/WARNING: There is no fail-safe in case of malformed attributes.
1548 cnt = sscanf(line.c_str(),
1549 "%31s %u %11s %u %79s %u typ %31s tcptype %31s\n",
1550 foundation,
1551 &comp_id,
1552 transport,
1553 &prio,
1554 ipaddr,
1555 &port,
1556 type,
1557 tcp_type);
1558 if (cnt != 7 && cnt != 8) {
1559 if (pimpl_->logger_)
1560 pimpl_->logger_->error("[ice:{}] Invalid ICE candidate line: {:s}", fmt::ptr(pimpl_), line);
1561 return false;
1562 }
1563
1564 if (strcmp(transport, "TCP") == 0) {
1565 is_tcp = PJ_TRUE;
1566 }
1567
1568 pj_bzero(&cand, sizeof(IceCandidate));
1569
1570 if (strcmp(type, "host") == 0)
1571 cand.type = PJ_ICE_CAND_TYPE_HOST;
1572 else if (strcmp(type, "srflx") == 0)
1573 cand.type = PJ_ICE_CAND_TYPE_SRFLX;
1574 else if (strcmp(type, "prflx") == 0)
1575 cand.type = PJ_ICE_CAND_TYPE_PRFLX;
1576 else if (strcmp(type, "relay") == 0)
1577 cand.type = PJ_ICE_CAND_TYPE_RELAYED;
1578 else {
1579 if (pimpl_->logger_)
1580 pimpl_->logger_->warn("[ice:{}] invalid remote candidate type '{:s}'", fmt::ptr(pimpl_), type);
1581 return false;
1582 }
1583
1584 if (is_tcp) {
1585 if (strcmp(tcp_type, "active") == 0)
1586 cand.transport = PJ_CAND_TCP_ACTIVE;
1587 else if (strcmp(tcp_type, "passive") == 0)
1588 cand.transport = PJ_CAND_TCP_PASSIVE;
1589 else if (strcmp(tcp_type, "so") == 0)
1590 cand.transport = PJ_CAND_TCP_SO;
1591 else {
1592 if (pimpl_->logger_)
1593 pimpl_->logger_->warn("[ice:{}] invalid transport type type '{:s}'", fmt::ptr(pimpl_), tcp_type);
1594 return false;
1595 }
1596 } else {
1597 cand.transport = PJ_CAND_UDP;
1598 }
1599
1600 // If the component Id is enumerated relative to media, convert
1601 // it to absolute enumeration.
1602 if (comp_id <= pimpl_->compCountPerStream_) {
1603 comp_id += pimpl_->compCountPerStream_ * streamIdx;
1604 }
1605 cand.comp_id = (pj_uint8_t) comp_id;
1606
1607 cand.prio = prio;
1608
1609 if (strchr(ipaddr, ':'))
1610 af = pj_AF_INET6();
1611 else {
1612 af = pj_AF_INET();
1613 pimpl_->onlyIPv4Private_ &= IpAddr(ipaddr).isPrivate();
1614 }
1615
1616 tmpaddr = pj_str(ipaddr);
1617 pj_sockaddr_init(af, &cand.addr, NULL, 0);
1618 status = pj_sockaddr_set_str_addr(af, &cand.addr, &tmpaddr);
1619 if (status != PJ_SUCCESS) {
1620 if (pimpl_->logger_)
1621 pimpl_->logger_->warn("[ice:{}] invalid IP address '{:s}'", fmt::ptr(pimpl_), ipaddr);
1622 return false;
1623 }
1624
1625 pj_sockaddr_set_port(&cand.addr, (pj_uint16_t) port);
1626 pj_strdup2(pimpl_->pool_.get(), &cand.foundation, foundation);
1627
1628 return true;
1629}
1630
1631ssize_t
1632IceTransport::recv(unsigned compId, unsigned char* buf, size_t len, std::error_code& ec)
1633{
1634 ASSERT_COMP_ID(compId, getComponentCount());
1635 auto& io = pimpl_->compIO_[compId - 1];
1636 std::lock_guard<std::mutex> lk(io.mutex);
1637
1638 if (io.queue.empty()) {
1639 ec = std::make_error_code(std::errc::resource_unavailable_try_again);
1640 return -1;
1641 }
1642
1643 auto& packet = io.queue.front();
Adrien Béraud21d5f462023-08-27 12:06:21 -04001644 const auto count = std::min(len, packet.size());
1645 std::copy_n(packet.begin(), count, buf);
1646 if (count == packet.size()) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001647 io.queue.pop_front();
1648 } else {
Adrien Béraud21d5f462023-08-27 12:06:21 -04001649 packet.erase(packet.begin(), packet.begin() + count);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001650 }
1651
1652 ec.clear();
1653 return count;
1654}
1655
1656ssize_t
1657IceTransport::recvfrom(unsigned compId, char* buf, size_t len, std::error_code& ec)
1658{
1659 ASSERT_COMP_ID(compId, getComponentCount());
1660 return pimpl_->peerChannels_.at(compId - 1).read(buf, len, ec);
1661}
1662
1663void
1664IceTransport::setOnRecv(unsigned compId, IceRecvCb cb)
1665{
1666 ASSERT_COMP_ID(compId, getComponentCount());
1667
1668 auto& io = pimpl_->compIO_[compId - 1];
1669 std::lock_guard<std::mutex> lk(io.mutex);
1670 io.recvCb = std::move(cb);
1671
1672 if (io.recvCb) {
1673 // Flush existing queue using the callback
1674 for (const auto& packet : io.queue)
Adrien Béraud21d5f462023-08-27 12:06:21 -04001675 io.recvCb((uint8_t*) packet.data(), packet.size());
Adrien Béraud612b55b2023-05-29 10:42:04 -04001676 io.queue.clear();
1677 }
1678}
1679
1680void
1681IceTransport::setOnShutdown(onShutdownCb&& cb)
1682{
Adrien Béraudef3e9fd2023-10-01 21:15:15 -04001683 pimpl_->scb = std::move(cb);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001684}
1685
1686ssize_t
1687IceTransport::send(unsigned compId, const unsigned char* buf, size_t len)
1688{
1689 ASSERT_COMP_ID(compId, getComponentCount());
1690
1691 auto remote = getRemoteAddress(compId);
1692
1693 if (!remote) {
1694 if (pimpl_->logger_)
1695 pimpl_->logger_->error("[ice:{}] can't find remote address for component {:d}", fmt::ptr(pimpl_), compId);
1696 errno = EINVAL;
1697 return -1;
1698 }
1699
1700 std::unique_lock dlk(pimpl_->sendDataMutex_, std::defer_lock);
1701 if (isTCPEnabled())
1702 dlk.lock();
1703
1704 jami_tracepoint(ice_transport_send,
1705 reinterpret_cast<uint64_t>(this),
1706 compId,
1707 len,
1708 remote.toString().c_str());
1709
1710 auto status = pj_ice_strans_sendto2(pimpl_->icest_,
1711 compId,
1712 buf,
1713 len,
1714 remote.pjPtr(),
1715 remote.getLength());
1716
1717 jami_tracepoint(ice_transport_send_status, status);
1718
1719 if (status == PJ_EPENDING && isTCPEnabled()) {
1720 // NOTE; because we are in TCP, the sent size will count the header (2
1721 // bytes length).
1722 pimpl_->waitDataCv_.wait(dlk, [&] {
1723 return pimpl_->lastSentLen_ >= static_cast<pj_size_t>(len) or pimpl_->destroying_;
1724 });
1725 pimpl_->lastSentLen_ = 0;
1726 } else if (status != PJ_SUCCESS && status != PJ_EPENDING) {
1727 if (status == PJ_EBUSY) {
1728 errno = EAGAIN;
1729 } else {
1730 if (pimpl_->logger_)
1731 pimpl_->logger_->error("[ice:{}] ice send failed: {:s}", fmt::ptr(pimpl_), sip_utils::sip_strerror(status));
1732 errno = EIO;
1733 }
1734 return -1;
1735 }
1736
1737 return len;
1738}
1739
1740bool
1741IceTransport::waitForInitialization(std::chrono::milliseconds timeout)
1742{
1743 return pimpl_->_waitForInitialization(timeout);
1744}
1745
1746ssize_t
1747IceTransport::waitForData(unsigned compId, std::chrono::milliseconds timeout, std::error_code& ec)
1748{
1749 ASSERT_COMP_ID(compId, getComponentCount());
1750 return pimpl_->peerChannels_.at(compId - 1).wait(timeout, ec);
1751}
1752
1753bool
Adrien Béraud02d6dad2023-11-06 09:26:51 -05001754IceTransport::isTCPEnabled() const
Adrien Béraud612b55b2023-05-29 10:42:04 -04001755{
1756 return pimpl_->isTcpEnabled();
1757}
1758
1759ICESDP
1760IceTransport::parseIceCandidates(std::string_view sdp_msg)
1761{
1762 if (pimpl_->streamsCount_ != 1) {
1763 if (pimpl_->logger_)
Sébastien Blincf569402023-07-27 09:46:40 -04001764 pimpl_->logger_->error("Expected exactly one stream per SDP (found {} streams)", pimpl_->streamsCount_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001765 return {};
1766 }
1767
1768 ICESDP res;
1769 int nr = 0;
Adrien Béraud1ae60aa2023-07-07 09:55:09 -04001770 for (std::string_view line; dhtnet::getline(sdp_msg, line); nr++) {
Adrien Béraud612b55b2023-05-29 10:42:04 -04001771 if (nr == 0) {
1772 res.rem_ufrag = line;
1773 } else if (nr == 1) {
1774 res.rem_pwd = line;
1775 } else {
1776 IceCandidate cand;
1777 if (parseIceAttributeLine(0, std::string(line), cand)) {
1778 if (pimpl_->logger_)
1779 pimpl_->logger_->debug("[ice:{}] Add remote candidate: {}",
1780 fmt::ptr(pimpl_),
1781 line);
1782 res.rem_candidates.emplace_back(cand);
1783 }
1784 }
1785 }
1786 return res;
1787}
1788
1789void
1790IceTransport::setDefaultRemoteAddress(unsigned comp_id, const IpAddr& addr)
1791{
1792 pimpl_->setDefaultRemoteAddress(comp_id, addr);
1793}
1794
1795std::string
1796IceTransport::link() const
1797{
1798 return pimpl_->link();
1799}
1800
1801//==============================================================================
1802
Adrien Béraud89933c12023-07-26 14:53:30 -04001803IceTransportFactory::IceTransportFactory(const std::shared_ptr<Logger>& logger)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001804 : cp_(new pj_caching_pool(),
1805 [](pj_caching_pool* p) {
1806 pj_caching_pool_destroy(p);
1807 delete p;
1808 })
1809 , ice_cfg_()
Adrien Béraud89933c12023-07-26 14:53:30 -04001810 , logger_(logger)
Adrien Béraud612b55b2023-05-29 10:42:04 -04001811{
1812 pj_caching_pool_init(cp_.get(), NULL, 0);
1813
1814 pj_ice_strans_cfg_default(&ice_cfg_);
1815 ice_cfg_.stun_cfg.pf = &cp_->factory;
1816
1817 // v2.4.5 of PJNATH has a default of 100ms but RFC 5389 since version 14 requires
1818 // a minimum of 500ms on fixed-line links. Our usual case is wireless links.
1819 // This solves too long ICE exchange by DHT.
1820 // Using 500ms with default PJ_STUN_MAX_TRANSMIT_COUNT (7) gives around 33s before timeout.
1821 ice_cfg_.stun_cfg.rto_msec = 500;
1822
1823 // See https://tools.ietf.org/html/rfc5245#section-8.1.1.2
1824 // If enabled, it may help speed-up the connectivity, but may cause
1825 // the nomination of sub-optimal pairs.
1826 ice_cfg_.opt.aggressive = PJ_FALSE;
1827}
1828
1829IceTransportFactory::~IceTransportFactory() {}
1830
1831std::shared_ptr<IceTransport>
1832IceTransportFactory::createTransport(std::string_view name)
1833{
Adrien Béraud9cf66f12023-10-01 21:15:15 -04001834 return std::make_shared<IceTransport>(name, logger_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001835}
1836
1837std::unique_ptr<IceTransport>
1838IceTransportFactory::createUTransport(std::string_view name)
1839{
Adrien Béraud9cf66f12023-10-01 21:15:15 -04001840 return std::make_unique<IceTransport>(name, logger_);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001841}
1842
1843//==============================================================================
1844
1845void
1846IceSocket::close()
1847{
1848 if (ice_transport_)
1849 ice_transport_->setOnRecv(compId_, {});
1850 ice_transport_.reset();
1851}
1852
1853ssize_t
1854IceSocket::send(const unsigned char* buf, size_t len)
1855{
1856 if (not ice_transport_)
1857 return -1;
1858 return ice_transport_->send(compId_, buf, len);
1859}
1860
1861ssize_t
1862IceSocket::waitForData(std::chrono::milliseconds timeout)
1863{
1864 if (not ice_transport_)
1865 return -1;
1866
1867 std::error_code ec;
1868 return ice_transport_->waitForData(compId_, timeout, ec);
1869}
1870
1871void
1872IceSocket::setOnRecv(IceRecvCb cb)
1873{
1874 if (ice_transport_)
1875 ice_transport_->setOnRecv(compId_, cb);
1876}
1877
1878uint16_t
1879IceSocket::getTransportOverhead()
1880{
1881 if (not ice_transport_)
1882 return 0;
1883
1884 return (ice_transport_->getRemoteAddress(compId_).getFamily() == AF_INET) ? IPV4_HEADER_SIZE
1885 : IPV6_HEADER_SIZE;
1886}
1887
1888void
1889IceSocket::setDefaultRemoteAddress(const IpAddr& addr)
1890{
1891 if (ice_transport_)
1892 ice_transport_->setDefaultRemoteAddress(compId_, addr);
1893}
1894
Sébastien Blin464bdff2023-07-19 08:02:53 -04001895} // namespace dhtnet