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