From ac7f9b109e5977369934ea1035a48da9ca2c80f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com>
Date: Tue, 24 Mar 2015 16:21:53 -0400
Subject: [PATCH] ringaccount: use a random port by default

Refs #69174

(cherry picked from master, commit e0284a70cf6a72d61c0abdbad2a99c3bf0566456)

Change-Id: I4332aa7f3b66b8dbae156ed2f4dc9f9f98ab2a4e
---
 src/ringdht/ringaccount.cpp | 13 +++++++------
 src/ringdht/ringaccount.h   |  3 ++-
 src/sip/sipaccountbase.cpp  | 18 +++++++++++++++++-
 src/sip/sipaccountbase.h    | 36 ++++++++++++++++++++++++++++++++++++
 4 files changed, 62 insertions(+), 8 deletions(-)

diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp
index b9789e213a..8d7987a8c2 100644
--- a/src/ringdht/ringaccount.cpp
+++ b/src/ringdht/ringaccount.cpp
@@ -81,6 +81,7 @@ static constexpr int ICE_INIT_TIMEOUT {5};
 static constexpr int ICE_NEGOTIATION_TIMEOUT {60};
 
 constexpr const char * const RingAccount::ACCOUNT_TYPE;
+constexpr const std::pair<uint16_t, uint16_t> RingAccount::DHT_PORT_RANGE;
 
 RingAccount::RingAccount(const std::string& accountID, bool /* presenceEnabled */)
     : SIPAccountBase(accountID), via_addr_()
@@ -402,9 +403,9 @@ void RingAccount::unserialize(const YAML::Node &node)
     using yaml_utils::parseValue;
 
     SIPAccountBase::unserialize(node);
-    in_port_t port {DHT_DEFAULT_PORT};
-    parseValue(node, Conf::DHT_PORT_KEY, port);
-    dhtPort_ = port ? port : DHT_DEFAULT_PORT;
+    parseValue(node, Conf::DHT_PORT_KEY, dhtPort_);
+    if (not dhtPort_)
+        dhtPort_ = getRandomEvenPort(DHT_PORT_RANGE);
     dhtPortUsed_ = dhtPort_;
     checkIdentityPath();
 }
@@ -498,8 +499,8 @@ void RingAccount::setAccountDetails(const std::map<std::string, std::string> &de
     if (hostname_ == "")
         hostname_ = DHT_DEFAULT_BOOTSTRAP;
     parseInt(details, Conf::CONFIG_DHT_PORT, dhtPort_);
-    if (dhtPort_ == 0)
-        dhtPort_ = DHT_DEFAULT_PORT;
+    if (not dhtPort_)
+        dhtPort_ = getRandomEvenPort(DHT_PORT_RANGE);
     dhtPortUsed_ = dhtPort_;
     checkIdentityPath();
 }
@@ -672,7 +673,7 @@ void RingAccount::doRegister_()
             dht_.join();
         }
         auto identity = loadIdentity();
-        dht_.run(dhtPortUsed_, identity.second, false, [=](dht::Dht::Status s4, dht::Dht::Status s6) {
+        dht_.run((in_port_t)dhtPortUsed_, identity.second, false, [=](dht::Dht::Status s4, dht::Dht::Status s6) {
             RING_WARN("Dht status : IPv4 %s; IPv6 %s", dhtStatusStr(s4), dhtStatusStr(s6));
             auto status = std::max(s4, s6);
             switch(status) {
diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h
index 84cb1411a5..073d93dbb4 100644
--- a/src/ringdht/ringaccount.h
+++ b/src/ringdht/ringaccount.h
@@ -79,6 +79,7 @@ class RingAccount : public SIPAccountBase {
         constexpr static const char * const ACCOUNT_TYPE = "RING";
         constexpr static const in_port_t DHT_DEFAULT_PORT = 4222;
         constexpr static const char * const DHT_DEFAULT_BOOTSTRAP = "bootstrap.ring.cx";
+        constexpr static const std::pair<uint16_t, uint16_t> DHT_PORT_RANGE {4000, 8888};
 
         const char* getAccountType() const {
             return ACCOUNT_TYPE;
@@ -340,7 +341,7 @@ class RingAccount : public SIPAccountBase {
          * selected in the configuration in the case that UPnP is used and the
          * configured port is already used by another client
          */
-        in_port_t dhtPortUsed_ {DHT_DEFAULT_PORT};
+        UsedPort dhtPortUsed_ {DHT_DEFAULT_PORT};
 
         /**
          * The TLS settings, used only if tls is chosen as a sip transport.
diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp
index f497069459..7302913c27 100644
--- a/src/sip/sipaccountbase.cpp
+++ b/src/sip/sipaccountbase.cpp
@@ -247,7 +247,17 @@ SIPAccountBase::getPortsReservation() noexcept -> decltype(getPortsReservation()
     return portsInUse;
 }
 
-// returns even number in range [lower, upper]
+uint16_t
+SIPAccountBase::getRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const
+{
+    std::uniform_int_distribution<uint16_t> dist(range.first/2, range.second/2);
+    uint16_t result;
+    do {
+        result = 2 * dist(rand_);
+    } while (getPortsReservation()[result / 2]);
+    return result;
+}
+
 uint16_t
 SIPAccountBase::acquireRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const
 {
@@ -262,6 +272,12 @@ SIPAccountBase::acquireRandomEvenPort(const std::pair<uint16_t, uint16_t>& range
     return result;
 }
 
+uint16_t
+SIPAccountBase::acquirePort(uint16_t port)
+{
+    getPortsReservation()[port / 2] = true;
+}
+
 void
 SIPAccountBase::releasePort(uint16_t port) noexcept
 {
diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h
index 0874f81cf4..1cef21d25a 100644
--- a/src/sip/sipaccountbase.h
+++ b/src/sip/sipaccountbase.h
@@ -292,7 +292,43 @@ protected:
      */
     std::pair<uint16_t, uint16_t> videoPortRange_ {49152, (MAX_PORT) - 2};
 
+    struct UsedPort {
+        UsedPort() {};
+        UsedPort(UsedPort&& o) : port_(o.port_) {
+            o.port_ = 0;
+        }
+        UsedPort(in_port_t p) : port_(p) {
+            if (port_)
+                acquirePort(port_);
+        };
+        ~UsedPort() {
+            if (port_)
+                releasePort(port_);
+        };
+        UsedPort& operator=(UsedPort&& o) {
+            if (port_)
+                releasePort(port_);
+            port_ = o.port_;
+            o.port_ = 0;
+            return *this;
+        }
+        UsedPort& operator=(in_port_t p) {
+            if (port_)
+                releasePort(port_);
+            port_ = p;
+            if (port_)
+                acquirePort(port_);
+            return *this;
+        }
+        explicit operator in_port_t() const { return port_; }
+    private:
+        in_port_t port_ {0};
+        NON_COPYABLE(UsedPort);
+    };
+
     static std::array<bool, HALF_MAX_PORT>& getPortsReservation() noexcept;
+    static uint16_t acquirePort(uint16_t port);
+    uint16_t getRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const;
     uint16_t acquireRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const;
 
 private:
-- 
GitLab