From 1acdde67150df20d3464a91725263330a6401b6a Mon Sep 17 00:00:00 2001
From: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com>
Date: Mon, 9 Aug 2021 12:19:58 -0400
Subject: [PATCH] ICE: add srflx candidates in answer

ICE srflx candidates are not added when answering a call. This
could cause media ICE negotiation to fail if the local endpoint
does not have TURN and UPNP enabled, and peer only has TURN enabled.
Now, srflx candidates are added in the answer as done in the offer.

Gitlab: #607

Change-Id: I5af3e24e85846f518cf4f746d31c067dcf57ff15
---
 src/sip/sipcall.cpp | 54 ++++++++++++++++++++++++++++++++++++++-------
 src/sip/sipcall.h   | 20 +++++++++--------
 2 files changed, 57 insertions(+), 17 deletions(-)

diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index cb48e52772..4f0c24a54d 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -863,13 +863,7 @@ SIPCall::answer(const std::vector<DRing::MediaMap>& mediaList)
 
             auto opts = account->getIceOptions();
 
-            auto publicAddr = account->getPublishedIpAddress();
-            if (not publicAddr) {
-                // If the published address is unknown, just use the local address. Not
-                // optimal, but may work just fine if both endpoints are in the same
-                // local network.
-                publicAddr = ip_utils::getInterfaceAddr(account->getLocalInterface(), pj_AF_INET());
-            }
+            auto publicAddr = getPublicAddress();
 
             if (publicAddr) {
                 opts.accountPublicAddr = publicAddr;
@@ -3014,6 +3008,11 @@ SIPCall::setupIceResponse()
 {
     JAMI_DBG("[call:%s] Setup ICE response", getCallId().c_str());
 
+    auto account = getSIPAccount();
+    if (not account) {
+        JAMI_ERR("No account detected");
+    }
+
     if (not remoteHasValidIceAttributes()) {
         // If ICE attributes are not present, skip the ICE initialization
         // step (most likely ICE is not used).
@@ -3021,7 +3020,24 @@ SIPCall::setupIceResponse()
         return;
     }
 
-    if (not initIceMediaTransport(false)) {
+    auto opt = account->getIceOptions();
+    opt.accountPublicAddr = getPublicAddress();
+    if (not opt.accountPublicAddr) {
+        JAMI_ERR("[call:%s] No public address, ICE can't be initialized", getCallId().c_str());
+        onFailure(EIO);
+        return;
+    }
+
+    opt.accountLocalAddr = ip_utils::getInterfaceAddr(account->getLocalInterface(),
+                                                      opt.accountPublicAddr.getFamily());
+
+    if (not opt.accountLocalAddr) {
+        JAMI_ERR("[call:%s] No local address, ICE can't be initialized", getCallId().c_str());
+        onFailure(EIO);
+        return;
+    }
+
+    if (not initIceMediaTransport(false, opt)) {
         JAMI_ERR("[call:%s] ICE initialization failed", getCallId().c_str());
         // Fatal condition
         // TODO: what's SIP rfc says about that?
@@ -3046,6 +3062,28 @@ SIPCall::newIceSocket(unsigned compId)
     return std::unique_ptr<IceSocket> {new IceSocket(mediaTransport_, compId)};
 }
 
+IpAddr
+SIPCall::getPublicAddress() const
+{
+    auto account = getSIPAccount();
+    if (not account) {
+        JAMI_ERR("No account detected");
+        return {};
+    }
+
+    auto publicAddr = account->getPublishedIpAddress();
+    if (not publicAddr) {
+        // If the published address is unknown, just use the local address. Not
+        // optimal, but may work just fine if both endpoints are in the same
+        // local network.
+        publicAddr = ip_utils::getInterfaceAddr(account->getLocalInterface(), pj_AF_INET());
+        JAMI_WARN("[call:%s] Missing public address, using local address instead",
+                  getCallId().c_str());
+    }
+
+    return publicAddr;
+}
+
 void
 SIPCall::rtpSetupSuccess(MediaType type, bool isRemote)
 {
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index 0ae5430191..6e23fe9c83 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -260,10 +260,6 @@ public:
     // Get the list of current RTP sessions
     std::vector<std::shared_ptr<RtpSession>> getRtpSessionList() const;
 
-    void generateMediaPorts();
-
-    void openPortsUPnP();
-
     void setPeerRegisteredName(const std::string& name) { peerRegisteredName_ = name; }
 
     void setPeerUri(const std::string& peerUri) { peerUri_ = peerUri; }
@@ -271,21 +267,27 @@ public:
     bool initIceMediaTransport(bool master,
                                std::optional<IceTransportOptions> options = std::nullopt);
 
+    void setInviteSession(pjsip_inv_session* inviteSession = nullptr);
+
+    std::unique_ptr<pjsip_inv_session, InvSessionDeleter> inviteSession_;
+
+private:
+    void generateMediaPorts();
+
+    void openPortsUPnP();
+
     bool isIceRunning() const;
 
     std::unique_ptr<IceSocket> newIceSocket(unsigned compId);
 
+    IpAddr getPublicAddress() const;
+
     void deinitRecorder();
 
     void rtpSetupSuccess(MediaType type, bool isRemote);
 
     void setMute(bool state);
 
-    void setInviteSession(pjsip_inv_session* inviteSession = nullptr);
-
-    std::unique_ptr<pjsip_inv_session, InvSessionDeleter> inviteSession_;
-
-private:
     /**
      * Send device orientation through SIP INFO
      * @param rotation Device orientation (0/90/180/270) (counterclockwise)
-- 
GitLab