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