LCOV - code coverage report
Current view: top level - src/jamidht - svc_tunnel_channel_handler.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 56.5 % 368 208
Test Date: 2026-08-23 08:52:56 Functions: 64.1 % 39 25

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3              :  *
       4              :  *  This program is free software: you can redistribute it and/or modify
       5              :  *  it under the terms of the GNU General Public License as published by
       6              :  *  the Free Software Foundation, either version 3 of the License, or
       7              :  *  (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
      11              :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12              :  *  GNU General Public License for more details.
      13              :  *
      14              :  *  You should have received a copy of the GNU General Public License
      15              :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : #include "jamidht/svc_tunnel_channel_handler.h"
      18              : 
      19              : #include "jamidht/account_manager.h"
      20              : #include "jamidht/contact_list.h"
      21              : #include "jamidht/service_manager.h"
      22              : #include "jamidht/svc_protocol.h"
      23              : #include "logger.h"
      24              : #include "manager.h"
      25              : 
      26              : #include <asio/connect.hpp>
      27              : #include <asio/post.hpp>
      28              : #include <asio/read.hpp>
      29              : #include <asio/write.hpp>
      30              : 
      31              : #include <atomic>
      32              : 
      33              : namespace jami {
      34              : 
      35              : namespace {
      36              : constexpr size_t kRelayBufSize = 16 * 1024;
      37              : }
      38              : 
      39              : struct SvcTunnelChannelHandler::ClientTunnel
      40              : {
      41              :     std::string id;
      42              :     std::string peerUri;
      43              :     DeviceId peerDevice;
      44              :     std::string serviceId;
      45              :     std::string serviceName;
      46              :     std::shared_ptr<asio::ip::tcp::acceptor> acceptor;
      47              :     OnTunnelClosed onClosed;
      48              :     std::atomic_bool closed {false};
      49              :     /// Per-tunnel mutex protecting the active-connection list.
      50              :     std::mutex connsMtx;
      51              :     struct Conn
      52              :     {
      53              :         std::weak_ptr<dhtnet::ChannelSocket> channel;
      54              :         std::weak_ptr<asio::ip::tcp::socket> tcp;
      55              :     };
      56              :     std::vector<Conn> activeConns;
      57              : };
      58              : 
      59          721 : SvcTunnelChannelHandler::SvcTunnelChannelHandler(const std::shared_ptr<JamiAccount>& acc,
      60              :                                                  dhtnet::ConnectionManager& cm,
      61          721 :                                                  std::shared_ptr<asio::io_context> io)
      62              :     : ChannelHandlerInterface()
      63          721 :     , account_(acc)
      64          721 :     , connectionManager_(cm)
      65          721 :     , io_(std::move(io))
      66         1442 :     , rng_(dht::crypto::getDerivedRandomEngine(acc->rand))
      67          721 : {}
      68              : 
      69         1442 : SvcTunnelChannelHandler::~SvcTunnelChannelHandler()
      70              : {
      71          721 :     std::vector<std::shared_ptr<ClientTunnel>> snapshot;
      72              :     {
      73          721 :         std::lock_guard lk(mtx_);
      74          721 :         for (auto& [_id, t] : tunnels_)
      75            0 :             snapshot.push_back(t);
      76          721 :         tunnels_.clear();
      77          721 :     }
      78          721 :     for (auto& t : snapshot) {
      79            0 :         t->closed = true;
      80            0 :         std::error_code ec;
      81            0 :         if (t->acceptor)
      82            0 :             t->acceptor->close(ec);
      83              :     }
      84         1442 : }
      85              : 
      86              : std::string
      87            2 : SvcTunnelChannelHandler::parseServiceId(const std::string& channelName)
      88              : {
      89            2 :     constexpr std::string_view prefix = "svc://";
      90            2 :     if (channelName.size() <= prefix.size())
      91            0 :         return {};
      92            2 :     if (channelName.compare(0, prefix.size(), prefix) != 0)
      93            0 :         return {};
      94            2 :     return channelName.substr(prefix.size());
      95              : }
      96              : 
      97              : void
      98            0 : SvcTunnelChannelHandler::connect(const DeviceId& deviceId,
      99              :                                  const std::string& /*name*/,
     100              :                                  ConnectCb&& cb,
     101              :                                  const std::string& /*connectionType*/,
     102              :                                  bool /*forceNewConnection*/)
     103              : {
     104              :     // Tunnels are managed at a higher level via openTunnel(); the generic
     105              :     // channel-handler entry-point is unused but must not block. Just signal
     106              :     // back asynchronously with a null socket so callers can fall through.
     107            0 :     if (cb && io_)
     108            0 :         asio::post(*io_, [cb = std::move(cb), deviceId]() mutable { cb(nullptr, deviceId); });
     109            0 : }
     110              : 
     111              : bool
     112            1 : SvcTunnelChannelHandler::onRequest(const std::shared_ptr<dht::crypto::Certificate>& peer, const std::string& name)
     113              : {
     114            1 :     if (!peer || !peer->issuer)
     115            0 :         return false;
     116            1 :     auto serviceId = parseServiceId(name);
     117            1 :     if (serviceId.empty())
     118            0 :         return false;
     119            1 :     auto acc = account_.lock();
     120            1 :     if (!acc)
     121            0 :         return false;
     122            1 :     const auto peerUri = peer->issuer->getId().toString();
     123            0 :     auto checker = [&acc](const std::string& uri) {
     124            0 :         return acc->isContact(uri);
     125            1 :     };
     126            1 :     return acc->serviceManager().isAuthorized(serviceId, peerUri, checker);
     127            1 : }
     128              : 
     129              : void
     130            2 : SvcTunnelChannelHandler::onReady(const std::shared_ptr<dht::crypto::Certificate>& peer,
     131              :                                  const std::string& name,
     132              :                                  std::shared_ptr<dhtnet::ChannelSocket> channel)
     133              : {
     134            2 :     if (!channel)
     135            0 :         return;
     136              :     // The initiator side wires its own onClientChannelReady callback via
     137              :     // connectDevice(). Only the receiving (server) side performs the local
     138              :     // TCP connect here.
     139            2 :     if (channel->isInitiator())
     140            1 :         return;
     141            1 :     auto serviceId = parseServiceId(name);
     142            1 :     auto acc = account_.lock();
     143            1 :     if (!acc || serviceId.empty()) {
     144            0 :         channel->shutdown();
     145            0 :         return;
     146              :     }
     147            1 :     auto rec = acc->serviceManager().getService(serviceId);
     148            1 :     if (!rec || !rec->enabled) {
     149            0 :         channel->shutdown();
     150            0 :         return;
     151              :     }
     152            1 :     if (!io_) {
     153            0 :         channel->shutdown();
     154            0 :         return;
     155              :     }
     156            1 :     JAMI_LOG("[SvcTunnel] peer {} opened tunnel to service \"{}\" -> {}:{}",
     157              :              peer && peer->issuer ? peer->issuer->getId().toString() : std::string("<unknown>"),
     158              :              rec->name,
     159              :              rec->localHost,
     160              :              rec->localPort);
     161              : 
     162            1 :     auto tcp = std::make_shared<asio::ip::tcp::socket>(*io_);
     163            1 :     asio::ip::tcp::resolver resolver(*io_);
     164            1 :     std::error_code ec;
     165            1 :     auto endpoints = resolver.resolve(rec->localHost, std::to_string(rec->localPort), ec);
     166            1 :     if (ec) {
     167            0 :         JAMI_WARNING("[SvcTunnel] resolve {}:{} failed: {}", rec->localHost, rec->localPort, ec.message());
     168            0 :         channel->shutdown();
     169            0 :         return;
     170              :     }
     171              : 
     172              :     // Buffer any bytes received from the remote peer before we have a local
     173              :     // TCP connection up; flush them through once async_connect succeeds.
     174              :     struct PreConnectBuf
     175              :     {
     176              :         std::mutex m;
     177              :         std::vector<uint8_t> bytes;
     178              :         bool tcpReady {false};
     179              :         std::shared_ptr<asio::ip::tcp::socket> tcp;
     180              :     };
     181            1 :     auto pre = std::make_shared<PreConnectBuf>();
     182            1 :     pre->tcp = tcp;
     183            1 :     auto channelKeep = channel;
     184            1 :     channel->setOnRecv([pre, channelKeep](const uint8_t* data, size_t size) -> ssize_t {
     185            0 :         std::lock_guard lk(pre->m);
     186            0 :         if (pre->tcpReady) {
     187            0 :             auto buf = std::make_shared<std::vector<uint8_t>>(data, data + size);
     188            0 :             asio::async_write(*pre->tcp,
     189            0 :                               asio::buffer(*buf),
     190            0 :                               [buf, pre, channelKeep](const std::error_code& ec, std::size_t) {
     191            0 :                                   if (ec) {
     192            0 :                                       JAMI_DEBUG("[SvcTunnel] write to TCP failed: {}", ec.message());
     193            0 :                                       channelKeep->shutdown();
     194            0 :                                       std::error_code ig;
     195            0 :                                       pre->tcp->close(ig);
     196              :                                   }
     197            0 :                               });
     198            0 :         } else {
     199            0 :             pre->bytes.insert(pre->bytes.end(), data, data + size);
     200              :         }
     201            0 :         return static_cast<ssize_t>(size);
     202            0 :     });
     203              : 
     204            1 :     asio::async_connect(*tcp,
     205              :                         endpoints,
     206            2 :                         [this, channel = channelKeep, tcp, pre, serviceId](const std::error_code& cec,
     207              :                                                                            const asio::ip::tcp::endpoint&) {
     208            1 :                             if (cec) {
     209            0 :                                 JAMI_WARNING("[SvcTunnel] connect failed: {}", cec.message());
     210            0 :                                 channel->shutdown();
     211            0 :                                 return;
     212              :                             }
     213            1 :                             trackServerChannel(serviceId, channel, tcp);
     214            1 :                             std::vector<uint8_t> drained;
     215              :                             {
     216            1 :                                 std::lock_guard lk(pre->m);
     217            1 :                                 pre->tcpReady = true;
     218            1 :                                 drained.swap(pre->bytes);
     219            1 :                             }
     220            1 :                             if (!drained.empty()) {
     221            0 :                                 auto buf = std::make_shared<std::vector<uint8_t>>(std::move(drained));
     222            0 :                                 asio::async_write(*tcp,
     223            0 :                                                   asio::buffer(*buf),
     224            0 :                                                   [buf, channel, tcp](const std::error_code& ec, std::size_t) {
     225            0 :                                                       if (ec) {
     226            0 :                                                           JAMI_DEBUG("[SvcTunnel] flush failed: {}", ec.message());
     227            0 :                                                           channel->shutdown();
     228            0 :                                                           std::error_code ig;
     229            0 :                                                           tcp->close(ig);
     230              :                                                       }
     231            0 :                                                   });
     232            0 :                             }
     233              :                             // Switch to a hot-path setOnRecv now that tcp is up.
     234            1 :                             channel->setOnRecv([tcp, channel](const uint8_t* data, size_t n) -> ssize_t {
     235            1 :                                 auto buf = std::make_shared<std::vector<uint8_t>>(data, data + n);
     236            1 :                                 asio::async_write(*tcp,
     237            1 :                                                   asio::buffer(*buf),
     238            2 :                                                   [buf, channel, tcp](const std::error_code& ec, std::size_t) {
     239            1 :                                                       if (ec) {
     240            0 :                                                           JAMI_DEBUG("[SvcTunnel] write to TCP failed: {}",
     241              :                                                                      ec.message());
     242            0 :                                                           channel->shutdown();
     243            0 :                                                           std::error_code ig;
     244            0 :                                                           tcp->close(ig);
     245              :                                                       }
     246            1 :                                                   });
     247            1 :                                 return static_cast<ssize_t>(n);
     248            1 :                             });
     249            1 :                             relayTcpToChannel(channel, tcp);
     250            1 :                         });
     251            1 : }
     252              : 
     253              : void
     254            1 : SvcTunnelChannelHandler::relay(std::shared_ptr<dhtnet::ChannelSocket> channel,
     255              :                                std::shared_ptr<asio::ip::tcp::socket> tcp)
     256              : {
     257              :     // ChannelSocket -> TCP
     258            1 :     auto channelHold = channel;
     259            1 :     channel->setOnRecv([tcp, channelHold](const uint8_t* data, size_t size) -> ssize_t {
     260            1 :         auto buf = std::make_shared<std::vector<uint8_t>>(data, data + size);
     261            1 :         asio::async_write(*tcp,
     262            1 :                           asio::buffer(*buf),
     263            2 :                           [buf, channelHold, tcp](const std::error_code& ec, std::size_t /*n*/) {
     264            1 :                               if (ec) {
     265            0 :                                   JAMI_DEBUG("[SvcTunnel] write to TCP failed: {}", ec.message());
     266            0 :                                   channelHold->shutdown();
     267            0 :                                   std::error_code ig;
     268            0 :                                   tcp->close(ig);
     269              :                               }
     270            1 :                           });
     271            1 :         return static_cast<ssize_t>(size);
     272            1 :     });
     273              : 
     274              :     // TCP -> ChannelSocket
     275            1 :     relayTcpToChannel(channel, tcp);
     276            1 : }
     277              : 
     278              : void
     279            2 : SvcTunnelChannelHandler::relayTcpToChannel(std::shared_ptr<dhtnet::ChannelSocket> channel,
     280              :                                            std::shared_ptr<asio::ip::tcp::socket> tcp)
     281              : {
     282            2 :     auto buf = std::make_shared<std::vector<uint8_t>>(kRelayBufSize);
     283            2 :     auto channelKeep = channel;
     284            2 :     auto tcpKeep = tcp;
     285            2 :     auto reader = std::make_shared<std::function<void()>>();
     286            4 :     *reader = [channelKeep, tcpKeep, buf, reader]() {
     287            8 :         tcpKeep->async_read_some(asio::buffer(*buf),
     288            8 :                                  [channelKeep, tcpKeep, buf, reader](const std::error_code& ec, std::size_t n) {
     289            4 :                                      if (ec || n == 0) {
     290            2 :                                          channelKeep->shutdown();
     291            2 :                                          std::error_code ig;
     292            2 :                                          tcpKeep->close(ig);
     293            2 :                                          *reader = nullptr; // break the cycle
     294            2 :                                          return;
     295              :                                      }
     296            2 :                                      std::error_code wec;
     297            2 :                                      channelKeep->write(buf->data(), n, wec);
     298            2 :                                      if (wec) {
     299            0 :                                          channelKeep->shutdown();
     300            0 :                                          std::error_code ig;
     301            0 :                                          tcpKeep->close(ig);
     302            0 :                                          *reader = nullptr; // break the cycle
     303            0 :                                          return;
     304              :                                      }
     305            2 :                                      (*reader)();
     306              :                                  });
     307            6 :     };
     308            2 :     (*reader)();
     309              : 
     310            2 :     channel->onShutdown([tcp = tcpKeep](const std::error_code&) {
     311            2 :         std::error_code ig;
     312            2 :         tcp->close(ig);
     313            2 :     });
     314            2 : }
     315              : 
     316              : std::string
     317            1 : SvcTunnelChannelHandler::openTunnel(std::string peerUri,
     318              :                                     DeviceId peerDevice,
     319              :                                     std::string serviceId,
     320              :                                     std::string serviceName,
     321              :                                     uint16_t localPort,
     322              :                                     OnTunnelOpened onOpened,
     323              :                                     OnTunnelClosed onClosed)
     324              : {
     325            1 :     if (!io_ || serviceId.empty() || peerUri.empty())
     326            0 :         return {};
     327              : 
     328            1 :     auto t = std::make_shared<ClientTunnel>();
     329            1 :     t->id = generateServiceUuid(rng_);
     330            1 :     t->peerUri = std::move(peerUri);
     331            1 :     t->peerDevice = peerDevice;
     332            1 :     t->serviceId = std::move(serviceId);
     333            1 :     t->serviceName = std::move(serviceName);
     334            1 :     t->onClosed = std::move(onClosed);
     335              :     try {
     336              :         // Try dual-stack loopback: prefer IPv4 loopback but fallback to IPv6
     337              :         // for IPv6-only systems where 127.0.0.1 may not be available.
     338            1 :         asio::ip::tcp::endpoint ep(asio::ip::address_v4::loopback(), localPort);
     339            1 :         t->acceptor = std::make_shared<asio::ip::tcp::acceptor>(*io_);
     340            1 :         t->acceptor->open(ep.protocol());
     341            1 :         t->acceptor->set_option(asio::socket_base::reuse_address(true));
     342            1 :         t->acceptor->bind(ep);
     343            1 :         t->acceptor->listen();
     344            0 :     } catch (const std::exception&) {
     345              :         // IPv4 loopback failed, try IPv6 loopback
     346              :         try {
     347            0 :             asio::ip::tcp::endpoint ep6(asio::ip::address_v6::loopback(), localPort);
     348            0 :             t->acceptor = std::make_shared<asio::ip::tcp::acceptor>(*io_);
     349            0 :             t->acceptor->open(ep6.protocol());
     350            0 :             t->acceptor->set_option(asio::socket_base::reuse_address(true));
     351            0 :             t->acceptor->bind(ep6);
     352            0 :             t->acceptor->listen();
     353            0 :         } catch (const std::exception& e) {
     354            0 :             JAMI_WARNING("[SvcTunnel] cannot bind loopback:{}: {}", localPort, e.what());
     355            0 :             return {};
     356            0 :         }
     357            0 :     }
     358              : 
     359            1 :     auto bound = static_cast<uint16_t>(t->acceptor->local_endpoint().port());
     360              : 
     361              :     {
     362            1 :         std::lock_guard lk(mtx_);
     363            1 :         tunnels_[t->id] = t;
     364            1 :     }
     365            1 :     JAMI_LOG("[SvcTunnel] opened tunnel id={} listening on {}:{} -> peer={} service=\"{}\"",
     366              :              t->id,
     367              :              t->acceptor->local_endpoint().address().to_string(),
     368              :              bound,
     369              :              t->peerUri,
     370              :              t->serviceName);
     371            1 :     if (onOpened)
     372            1 :         onOpened(t->id, bound);
     373              : 
     374            1 :     acceptLoop(t);
     375            1 :     return t->id;
     376            1 : }
     377              : 
     378              : void
     379            2 : SvcTunnelChannelHandler::acceptLoop(const std::shared_ptr<ClientTunnel>& tunnel)
     380              : {
     381            2 :     auto self = tunnel;
     382            2 :     auto sock = std::make_shared<asio::ip::tcp::socket>(*io_);
     383            2 :     self->acceptor->async_accept(*sock, [this, self, sock](const std::error_code& ec) {
     384            2 :         if (self->closed) {
     385            1 :             return;
     386              :         }
     387            1 :         if (ec) {
     388            0 :             if (ec == asio::error::operation_aborted)
     389            0 :                 return;
     390            0 :             JAMI_DEBUG("[SvcTunnel] accept error: {}", ec.message());
     391            0 :             return;
     392              :         }
     393              :         // Open a fresh dhtnet channel for this TCP connection.
     394            2 :         std::string channelName = std::string(svc_protocol::TunnelChannelPrefix) + self->serviceId;
     395            1 :         connectionManager_.connectDevice(self->peerDevice,
     396              :                                          channelName,
     397            2 :                                          [this, self, sock](std::shared_ptr<dhtnet::ChannelSocket> channel,
     398              :                                                             const DeviceId&) {
     399            1 :                                              if (!channel) {
     400            0 :                                                  JAMI_WARNING("[SvcTunnel] tunnel id={}: connectDevice to peer "
     401              :                                                               "returned null; closing tunnel",
     402              :                                                               self->id);
     403            0 :                                                  std::error_code ig;
     404            0 :                                                  sock->close(ig);
     405            0 :                                                  closeTunnelInternal(self->id, "connect-failed");
     406            0 :                                                  return;
     407              :                                              }
     408            1 :                                              onClientChannelReady(self, sock, std::move(channel));
     409              :                                          });
     410              :         // Continue accepting.
     411            1 :         acceptLoop(self);
     412            2 :     });
     413            2 : }
     414              : 
     415              : void
     416            1 : SvcTunnelChannelHandler::onClientChannelReady(const std::shared_ptr<ClientTunnel>& tunnel,
     417              :                                               std::shared_ptr<asio::ip::tcp::socket> tcp,
     418              :                                               std::shared_ptr<dhtnet::ChannelSocket> channel)
     419              : {
     420            1 :     if (tunnel->closed) {
     421              :         // The tunnel was torn down while connectDevice was in flight; drop
     422              :         // this late connection instead of wiring up a dangling relay.
     423            0 :         channel->shutdown();
     424            0 :         std::error_code ig;
     425            0 :         tcp->close(ig);
     426            0 :         return;
     427              :     }
     428            1 :     trackClientConnection(tunnel, channel, tcp);
     429            1 :     relay(std::move(channel), std::move(tcp));
     430              : }
     431              : 
     432              : void
     433            1 : SvcTunnelChannelHandler::trackClientConnection(const std::shared_ptr<ClientTunnel>& tunnel,
     434              :                                                const std::shared_ptr<dhtnet::ChannelSocket>& channel,
     435              :                                                const std::shared_ptr<asio::ip::tcp::socket>& tcp)
     436              : {
     437              :     {
     438            1 :         std::lock_guard lk(tunnel->connsMtx);
     439              :         // Compact dead entries opportunistically.
     440            3 :         tunnel->activeConns.erase(std::remove_if(tunnel->activeConns.begin(),
     441            1 :                                                  tunnel->activeConns.end(),
     442            0 :                                                  [](const ClientTunnel::Conn& c) {
     443            0 :                                                      return !c.channel.lock() && !c.tcp.lock();
     444              :                                                  }),
     445            1 :                                   tunnel->activeConns.end());
     446            2 :         tunnel->activeConns.push_back({channel, tcp});
     447            1 :     }
     448            1 :     std::weak_ptr<ClientTunnel> wt = tunnel;
     449            1 :     std::weak_ptr<dhtnet::ChannelSocket> wc = channel;
     450            1 :     channel->onShutdown([wt, wc](const std::error_code&) {
     451            0 :         auto t = wt.lock();
     452            0 :         if (!t)
     453            0 :             return;
     454            0 :         auto c = wc.lock();
     455            0 :         std::lock_guard lk(t->connsMtx);
     456            0 :         t->activeConns.erase(std::remove_if(t->activeConns.begin(),
     457            0 :                                             t->activeConns.end(),
     458            0 :                                             [&](const ClientTunnel::Conn& cn) {
     459            0 :                                                 return cn.channel.lock() == c || !cn.channel.lock();
     460              :                                             }),
     461            0 :                              t->activeConns.end());
     462            0 :     });
     463            2 : }
     464              : 
     465              : bool
     466            1 : SvcTunnelChannelHandler::closeTunnel(const std::string& tunnelId)
     467              : {
     468            2 :     return closeTunnelInternal(tunnelId, "closed");
     469              : }
     470              : 
     471              : bool
     472            1 : SvcTunnelChannelHandler::closeTunnelInternal(const std::string& tunnelId, const std::string& reason)
     473              : {
     474            1 :     std::shared_ptr<ClientTunnel> t;
     475              :     {
     476            1 :         std::lock_guard lk(mtx_);
     477            1 :         auto it = tunnels_.find(tunnelId);
     478            1 :         if (it == tunnels_.end())
     479            0 :             return false;
     480            1 :         t = it->second;
     481            1 :         tunnels_.erase(it);
     482            1 :     }
     483            1 :     t->closed = true;
     484            1 :     std::error_code ec;
     485            1 :     if (t->acceptor)
     486            1 :         t->acceptor->close(ec);
     487              :     // Tear down every per-connection relay (channel + local TCP) currently
     488              :     // serving this tunnel so that close actually severs the byte streams.
     489            1 :     std::vector<ClientTunnel::Conn> conns;
     490              :     {
     491            1 :         std::lock_guard lk(t->connsMtx);
     492            1 :         conns.swap(t->activeConns);
     493            1 :     }
     494            2 :     for (auto& c : conns) {
     495            1 :         if (auto ch = c.channel.lock())
     496            1 :             ch->shutdown();
     497            1 :         if (auto sock = c.tcp.lock()) {
     498            1 :             std::error_code ig;
     499            1 :             sock->close(ig);
     500            1 :         }
     501              :     }
     502            1 :     JAMI_LOG("[SvcTunnel] closed tunnel id={} reason={} ({} live connection(s) torn down)",
     503              :              tunnelId,
     504              :              reason,
     505              :              conns.size());
     506            1 :     if (t->onClosed)
     507            1 :         t->onClosed(tunnelId, reason);
     508            1 :     return true;
     509            1 : }
     510              : 
     511              : void
     512            1 : SvcTunnelChannelHandler::trackServerChannel(const std::string& serviceId,
     513              :                                             const std::shared_ptr<dhtnet::ChannelSocket>& channel,
     514              :                                             const std::shared_ptr<asio::ip::tcp::socket>& tcp)
     515              : {
     516            1 :     if (!channel)
     517            0 :         return;
     518              :     {
     519            1 :         std::lock_guard lk(mtx_);
     520            1 :         auto& vec = serverChannels_[serviceId];
     521              :         // Drop dead entries opportunistically.
     522            1 :         vec.erase(std::remove_if(vec.begin(), vec.end(), [](const ServerConn& c) { return !c.channel.lock(); }),
     523            1 :                   vec.end());
     524            1 :         vec.push_back({channel, tcp});
     525            1 :     }
     526            1 :     std::weak_ptr<dhtnet::ChannelSocket> wc = channel;
     527            1 :     std::weak_ptr<SvcTunnelChannelHandler> wself; // not needed: handler owns map
     528            1 :     auto sid = serviceId;
     529            1 :     channel->onShutdown([this, sid, wc](const std::error_code&) {
     530            0 :         auto c = wc.lock();
     531            0 :         std::lock_guard lk(mtx_);
     532            0 :         auto it = serverChannels_.find(sid);
     533            0 :         if (it == serverChannels_.end())
     534            0 :             return;
     535            0 :         auto& vec = it->second;
     536            0 :         vec.erase(std::remove_if(vec.begin(),
     537              :                                  vec.end(),
     538            0 :                                  [&](const ServerConn& sc) {
     539            0 :                                      auto sch = sc.channel.lock();
     540            0 :                                      return !sch || sch == c;
     541            0 :                                  }),
     542            0 :                   vec.end());
     543            0 :         if (vec.empty())
     544            0 :             serverChannels_.erase(it);
     545            0 :     });
     546            2 : }
     547              : 
     548              : void
     549            0 : SvcTunnelChannelHandler::closeServerChannelsForService(const std::string& serviceId)
     550              : {
     551            0 :     std::vector<ServerConn> conns;
     552              :     {
     553            0 :         std::lock_guard lk(mtx_);
     554            0 :         auto it = serverChannels_.find(serviceId);
     555            0 :         if (it == serverChannels_.end())
     556            0 :             return;
     557            0 :         conns.swap(it->second);
     558            0 :         serverChannels_.erase(it);
     559            0 :     }
     560            0 :     for (auto& c : conns) {
     561            0 :         if (auto ch = c.channel.lock())
     562            0 :             ch->shutdown();
     563            0 :         if (auto sock = c.tcp.lock()) {
     564            0 :             std::error_code ig;
     565            0 :             sock->close(ig);
     566            0 :         }
     567              :     }
     568            0 :     if (!conns.empty())
     569            0 :         JAMI_LOG("[SvcTunnel] closed {} inbound connection(s) for service id={}", conns.size(), serviceId);
     570            0 : }
     571              : 
     572              : std::vector<SvcTunnelChannelHandler::Tunnel>
     573            0 : SvcTunnelChannelHandler::activeTunnels() const
     574              : {
     575            0 :     std::lock_guard lk(mtx_);
     576            0 :     std::vector<Tunnel> out;
     577            0 :     out.reserve(tunnels_.size());
     578            0 :     for (const auto& [_id, t] : tunnels_) {
     579            0 :         Tunnel info;
     580            0 :         info.id = t->id;
     581            0 :         info.peerUri = t->peerUri;
     582            0 :         info.peerDevice = t->peerDevice.toString();
     583            0 :         info.serviceId = t->serviceId;
     584            0 :         info.serviceName = t->serviceName;
     585            0 :         info.localPort = t->acceptor ? static_cast<uint16_t>(t->acceptor->local_endpoint().port()) : 0;
     586            0 :         out.push_back(std::move(info));
     587            0 :     }
     588            0 :     return out;
     589            0 : }
     590              : 
     591              : } // namespace jami
        

Generated by: LCOV version 2.0-1