From 0ae5d6681fcfc9cb42b23671ce2430c8b681170a Mon Sep 17 00:00:00 2001
From: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com>
Date: Tue, 12 Oct 2021 15:14:31 -0400
Subject: [PATCH] SIP - prevent race condition when writing contact header

The contact header field was stored in a member varialbe of Jami/SIP
accounts. There was a condition in which the contact header could
be concurrently accessed by multiple threads. This typically happens
when terminating calls in batch (when calling hangupCalls() for
instance).
Managment of SIP contact header in Jami and SIP accounts was
reworked to prevent such race.

Gitlab: #633

Change-Id: Ib9295070a5295969bf114ec29e66e36b1c5c5e03
---
 src/call.cpp                         |  12 ++-
 src/jamidht/jamiaccount.cpp          |  49 +++++-------
 src/jamidht/jamiaccount.h            |   6 +-
 src/jamidht/sync_channel_handler.cpp |   2 +-
 src/sip/sip_utils.cpp                |  17 ++--
 src/sip/sip_utils.h                  |   2 +-
 src/sip/sipaccount.cpp               | 115 +++++++++++++--------------
 src/sip/sipaccount.h                 |  21 ++---
 src/sip/sipaccountbase.cpp           |   5 +-
 src/sip/sipaccountbase.h             |   4 +-
 src/sip/sipcall.cpp                  |  70 +++++++++-------
 src/sip/sipcall.h                    |   6 +-
 src/sip/sipvoiplink.cpp              |   2 +-
 13 files changed, 151 insertions(+), 160 deletions(-)

diff --git a/src/call.cpp b/src/call.cpp
index 892832c1a2..01b2ed5df0 100644
--- a/src/call.cpp
+++ b/src/call.cpp
@@ -430,11 +430,8 @@ Call::onTextMessage(std::map<std::string, std::string>&& messages)
 #ifdef ENABLE_PLUGIN
     auto& pluginChatManager = Manager::instance().getJamiPluginManager().getChatServicesManager();
     if (pluginChatManager.hasHandlers()) {
-        pluginChatManager.publishMessage(std::make_shared<JamiMessage>(getAccountId(),
-                                            getPeerNumber(),
-                                            true,
-                                            messages,
-                                            false));
+        pluginChatManager.publishMessage(
+            std::make_shared<JamiMessage>(getAccountId(), getPeerNumber(), true, messages, false));
     }
 #endif
     Manager::instance().incomingMessage(getCallId(), getPeerNumber(), messages);
@@ -474,8 +471,9 @@ Call::addSubCall(Call& subcall)
         subcall.sendTextMessage(msg.first, msg.second);
 
     subcall.addStateListener(
-        [sub = subcall.weak(),
-         parent = weak()](Call::CallState new_state, Call::ConnectionState new_cstate, int code) {
+        [sub = subcall.weak(), parent = weak()](Call::CallState new_state,
+                                                Call::ConnectionState new_cstate,
+                                                int /* code */) {
             runOnMainThread([sub, parent, new_state, new_cstate]() {
                 if (auto p = parent.lock()) {
                     if (auto s = sub.lock()) {
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index e1586cd6ca..1895e02a92 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -794,19 +794,14 @@ JamiAccount::SIPStartCall(SIPCall& call, const IpAddr& target)
     std::string targetStr = getToUri(target.toString(true));
     pj_str_t pjTarget = sip_utils::CONST_PJ_STR(targetStr);
 
-    pj_str_t pjContact;
-    {
-        auto transport = call.getTransport();
-        pjContact = getContactHeader(transport ? transport->get() : nullptr);
-    }
+    auto contact = getContactHeader(call.getTransport());
+    auto pjContact = sip_utils::CONST_PJ_STR(contact);
 
-    JAMI_DBG("contact header: %.*s / %s -> %s / %.*s",
-             (int) pjContact.slen,
-             pjContact.ptr,
+    JAMI_DBG("contact header: %s / %s -> %s / %s",
+             contact.c_str(),
              from.c_str(),
              toUri.c_str(),
-             (int) pjTarget.slen,
-             pjTarget.ptr);
+             targetStr.c_str());
 
     auto local_sdp = call.getSDP().getLocalSdpSession();
     pjsip_dialog* dialog {nullptr};
@@ -3058,32 +3053,26 @@ JamiAccount::setMessageDisplayed(const std::string& conversationUri,
     return true;
 }
 
-pj_str_t
-JamiAccount::getContactHeader(pjsip_transport* t)
+std::string
+JamiAccount::getContactHeader(SipTransport* sipTransport)
 {
     std::string quotedDisplayName = "\"" + displayName_ + "\" " + (displayName_.empty() ? "" : " ");
-    if (t) {
-        auto* td = reinterpret_cast<tls::AbstractSIPTransport::TransportData*>(t);
+    std::ostringstream contact;
+
+    if (auto transport = sipTransport->get()) {
+        auto* td = reinterpret_cast<tls::AbstractSIPTransport::TransportData*>(transport);
         auto address = td->self->getLocalAddress().toString(true);
-        bool reliable = t->flag & PJSIP_TRANSPORT_RELIABLE;
-
-        contact_.slen = pj_ansi_snprintf(contact_.ptr,
-                                         PJSIP_MAX_URL_SIZE,
-                                         "%s<sips:%s%s%s;transport=%s>",
-                                         quotedDisplayName.c_str(),
-                                         id_.second->getId().toString().c_str(),
-                                         (address.empty() ? "" : "@"),
-                                         address.c_str(),
-                                         reliable ? "tls" : "dtls");
+        bool reliable = transport->flag & PJSIP_TRANSPORT_RELIABLE;
+
+        contact << quotedDisplayName << "<sips:" << id_.second->getId().toString()
+                << (address.empty() ? "" : "@") << address
+                << (reliable ? ";transport=tls>" : ";transport=dtls");
     } else {
         JAMI_ERR("getContactHeader: no SIP transport provided");
-        contact_.slen = pj_ansi_snprintf(contact_.ptr,
-                                         PJSIP_MAX_URL_SIZE,
-                                         "%s<sips:%s@ring.dht>",
-                                         quotedDisplayName.c_str(),
-                                         id_.second->getId().toString().c_str());
+        contact << quotedDisplayName << "<sips:" << id_.second->getId().toString() << "@ring.dht>";
     }
-    return contact_;
+
+    return contact.str();
 }
 
 /* contacts */
diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h
index 5e2d50754e..0587a2a37a 100644
--- a/src/jamidht/jamiaccount.h
+++ b/src/jamidht/jamiaccount.h
@@ -251,9 +251,9 @@ public:
 
     /**
      * Get the contact header for
-     * @return pj_str_t The contact header based on account information
+     * @return The contact header based on account information
      */
-    pj_str_t getContactHeader(pjsip_transport* = nullptr) override;
+    std::string getContactHeader(SipTransport* transport = nullptr) override;
 
     /* Returns true if the username and/or hostname match this account */
     MatchRank matches(std::string_view username, std::string_view hostname) const override;
@@ -804,8 +804,6 @@ private:
      */
     pjsip_host_port via_addr_ {};
 
-    char contactBuffer_[PJSIP_MAX_URL_SIZE] {};
-    pj_str_t contact_ {contactBuffer_, 0};
     pjsip_transport* via_tp_ {nullptr};
 
     std::unique_ptr<DhtPeerConnector> dhtPeerConnector_;
diff --git a/src/jamidht/sync_channel_handler.cpp b/src/jamidht/sync_channel_handler.cpp
index 3cff8e4530..6adb88073a 100644
--- a/src/jamidht/sync_channel_handler.cpp
+++ b/src/jamidht/sync_channel_handler.cpp
@@ -50,7 +50,7 @@ SyncChannelHandler::connect(const DeviceId& deviceId, const std::string&, Connec
 }
 
 bool
-SyncChannelHandler::onRequest(const DeviceId& deviceId, const std::string& name)
+SyncChannelHandler::onRequest(const DeviceId& deviceId, const std::string& /* name */)
 {
     auto cert = tls::CertificateStore::instance().getCertificate(deviceId.toString());
     auto acc = account_.lock();
diff --git a/src/sip/sip_utils.cpp b/src/sip/sip_utils.cpp
index 5821bd346b..edfed41bed 100644
--- a/src/sip/sip_utils.cpp
+++ b/src/sip/sip_utils.cpp
@@ -174,12 +174,17 @@ getHostFromUri(std::string_view uri)
 }
 
 void
-addContactHeader(pj_str_t contact_str, pjsip_tx_data* tdata)
+addContactHeader(const std::string& contactHdr, pjsip_tx_data* tdata)
 {
+    if (contactHdr.empty()) {
+        JAMI_WARN("Contact header won't be added (empty string)");
+        return;
+    }
+    auto pjContact = sip_utils::CONST_PJ_STR(contactHdr);
     pjsip_contact_hdr* contact = pjsip_contact_hdr_create(tdata->pool);
     contact->uri = pjsip_parse_uri(tdata->pool,
-                                   contact_str.ptr,
-                                   contact_str.slen,
+                                   pjContact.ptr,
+                                   pjContact.slen,
                                    PJSIP_PARSE_URI_AS_NAMEADDR);
     // remove old contact header (if present)
     pjsip_msg_find_remove_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
@@ -189,7 +194,7 @@ addContactHeader(pj_str_t contact_str, pjsip_tx_data* tdata)
 void
 addUserAgentHeader(const std::string& userAgent, pjsip_tx_data* tdata)
 {
-    if (tdata == nullptr)
+    if (tdata == nullptr or userAgent.empty())
         return;
 
     auto pjUserAgent = CONST_PJ_STR(userAgent);
@@ -224,8 +229,8 @@ getPeerUserAgent(const pjsip_rx_data* rdata)
 
     constexpr auto USER_AGENT_STR = CONST_PJ_STR("User-Agent");
     if (auto uaHdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
-                                                                   &USER_AGENT_STR,
-                                                                   nullptr)) {
+                                                                            &USER_AGENT_STR,
+                                                                            nullptr)) {
         return as_view(uaHdr->hvalue);
     }
     return {};
diff --git a/src/sip/sip_utils.h b/src/sip/sip_utils.h
index 31e58ca793..9b72323fac 100644
--- a/src/sip/sip_utils.h
+++ b/src/sip/sip_utils.h
@@ -96,7 +96,7 @@ std::string parseDisplayName(const pjsip_contact_hdr* header);
 
 std::string_view getHostFromUri(std::string_view sipUri);
 
-void addContactHeader(pj_str_t contactStr, pjsip_tx_data* tdata);
+void addContactHeader(const std::string& contact, pjsip_tx_data* tdata);
 void addUserAgentHeader(const std::string& userAgent, pjsip_tx_data* tdata);
 std::string_view getPeerUserAgent(const pjsip_rx_data* rdata);
 void logMessageHeaders(const pjsip_hdr* hdr_list);
diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp
index ca2750ce78..f501adaae6 100644
--- a/src/sip/sipaccount.cpp
+++ b/src/sip/sipaccount.cpp
@@ -146,8 +146,6 @@ SIPAccount::SIPAccount(const std::string& accountID, bool presenceEnabled)
     , receivedParameter_("")
     , rPort_(-1)
     , via_addr_()
-    , contactBuffer_()
-    , contact_ {contactBuffer_, 0}
     , contactRewriteMethod_(2)
     , allowIPAutoRewrite_(true)
     , contactOverwritten_(false)
@@ -451,13 +449,10 @@ SIPAccount::SIPStartCall(std::shared_ptr<SIPCall>& call)
         return false;
     }
 
-    pj_str_t pjContact = getContactHeader(transport->get());
-    JAMI_DBG("contact header: %.*s / %s -> %s",
-             (int) pjContact.slen,
-             pjContact.ptr,
-             from.c_str(),
-             toUri.c_str());
+    std::string contact = getContactHeader(transport);
+    JAMI_DBG("contact header: %s / %s -> %s", contact.c_str(), from.c_str(), toUri.c_str());
 
+    pj_str_t pjContact = sip_utils::CONST_PJ_STR(contact);
     auto local_sdp = isEmptyOffersEnabled() ? nullptr : call->getSDP().getLocalSdpSession();
 
     pjsip_dialog* dialog {nullptr};
@@ -881,8 +876,7 @@ SIPAccount::setPushNotificationToken(const std::string& pushDeviceToken)
         return;
 
     deviceKey_ = pushDeviceToken;
-    pj_bzero(contact_.ptr, sizeof(contact_));
-    contact_.slen = 0;
+    contact_.clear();
 
     if (enabled_)
         doUnregister([&](bool /* transport_free */) { doRegister(); });
@@ -890,7 +884,7 @@ SIPAccount::setPushNotificationToken(const std::string& pushDeviceToken)
 
 void
 SIPAccount::pushNotificationReceived(const std::string& from,
-                                     const std::map<std::string, std::string>& data)
+                                     const std::map<std::string, std::string>&)
 {
     JAMI_WARN("[SIP Account %s] pushNotificationReceived: %s", getAccountID().c_str(), from.c_str());
 
@@ -1099,7 +1093,8 @@ SIPAccount::sendRegister()
     const std::string& received(getReceivedParameter());
 
     // Get the contact header
-    const pj_str_t pjContact(getContactHeader());
+    std::string contact = getContactHeader();
+    pj_str_t pjContact = sip_utils::CONST_PJ_STR(contact);
 
     JAMI_DBG("Using contact header %.*s in registration", (int) pjContact.slen, pjContact.ptr);
 
@@ -1568,17 +1563,17 @@ SIPAccount::getServerUri() const
     return "<" + scheme + host + transport + ">";
 }
 
-pj_str_t
-SIPAccount::getContactHeader(pjsip_transport* t)
+std::string
+SIPAccount::getContactHeader(SipTransport* sipTransport)
 {
-    if (contact_.slen and contactOverwritten_)
+    if (not contact_.empty() and contactOverwritten_)
         return contact_;
 
-    if (!t && transport_)
-        t = transport_->get();
-    if (!t) {
+    if (not sipTransport && transport_)
+        sipTransport = transport_.get();
+    if (not sipTransport) {
         JAMI_ERR("Transport not created yet");
-        return {nullptr, 0};
+        return {};
     }
 
     // The transport type must be specified, in our case START_OTHER refers to stun transport
@@ -1591,7 +1586,7 @@ SIPAccount::getContactHeader(pjsip_transport* t)
     std::string address;
     pj_uint16_t port;
 
-    link_.findLocalAddressFromTransport(t, transportType, hostname_, address, port);
+    link_.findLocalAddressFromTransport(sipTransport->get(), transportType, hostname_, address, port);
 
     if (getUPnPActive() and getUPnPIpAddress()) {
         address = getUPnPIpAddress().toString();
@@ -1603,7 +1598,11 @@ SIPAccount::getContactHeader(pjsip_transport* t)
         port = publishedPort_;
         JAMI_DBG("Using published address %s and port %d", address.c_str(), port);
     } else if (stunEnabled_) {
-        auto success = link_.findLocalAddressFromSTUN(t, &stunServerName_, stunPort_, address, port);
+        auto success = link_.findLocalAddressFromSTUN(sipTransport->get(),
+                                                      &stunServerName_,
+                                                      stunPort_,
+                                                      address,
+                                                      port);
         if (not success)
             emitSignal<DRing::ConfigurationSignal::StunStatusFailed>(getAccountID());
         setPublishedAddress({address});
@@ -1628,55 +1627,48 @@ SIPAccount::getContactHeader(pjsip_transport* t)
 
     const char* scheme = "sip";
     const char* transport = "";
-    if (PJSIP_TRANSPORT_IS_SECURE(t)) {
+    if (PJSIP_TRANSPORT_IS_SECURE(sipTransport->get())) {
         scheme = "sips";
         transport = ";transport=tls";
     }
 
     std::string quotedDisplayName = displayName_.empty() ? "" : "\"" + displayName_ + "\" ";
-    contact_.slen
-        = printContactHeader(contact_.ptr, quotedDisplayName, scheme, address, port, transport);
+    contact_ = printContactHeader(quotedDisplayName, scheme, address, port, transport);
     return contact_;
 }
 
-int
-SIPAccount::printContactHeader(char* data,
-                               const std::string& displayName,
+std::string
+SIPAccount::printContactHeader(const std::string& displayName,
                                const char* scheme,
                                const std::string& address,
                                pj_uint16_t port,
                                const char* transport)
 {
-    if (deviceKey_.empty()) {
-        return pj_ansi_snprintf(data,
-                                PJSIP_MAX_URL_SIZE,
-                                CONTACT_HEADER_WITHOUT_PN,
-                                displayName.c_str(),
-                                scheme,
-                                username_.c_str(),
-                                (username_.empty() ? "" : "@"),
-                                address.c_str(),
-                                port,
-                                transport);
-    } else {
-        return pj_ansi_snprintf(data,
-                                PJSIP_MAX_URL_SIZE,
-                                CONTACT_HEADER_WITH_PN,
-                                displayName.c_str(),
-                                scheme,
-                                username_.c_str(),
-                                (username_.empty() ? "" : "@"),
-                                address.c_str(),
-                                port,
-                                transport,
+    // This method generates SIP contact header field, with push
+    // notification parameters if any.
+    // Example without push notification:
+    // John Doe<sips:jdoe@10.10.10.10:5060;transport=tls>
+    // Example with push notification:
+    // John Doe<sips:jdoe@10.10.10.10:5060;transport=tls;pn-provider=XXX;pn-param=YYY;pn-prid=ZZZ>
+
+    std::ostringstream contact;
+
+    contact << displayName << "<" << scheme << ":" << username_ << (username_.empty() ? "" : "@")
+            << address << ":" << port << transport;
+
+    if (not deviceKey_.empty()) {
+        contact
 #if defined(__ANDROID__) || defined(ANDROID)
-                                PN_FCM,
+            << "pn-provider=" << PN_FCM
 #elif defined(__Apple__)
-                                PN_APNS,
+            << "pn-provider=" << PN_APNS
 #endif
-                                "",
-                                deviceKey_.c_str());
+            << "pn-param=;"
+            << "pn-prid=" << deviceKey_;
     }
+    contact << ">";
+
+    return contact.str();
 }
 
 pjsip_host_port
@@ -2007,9 +1999,10 @@ SIPAccount::checkNATAddress(pjsip_regc_cbparam* param, pj_pool_t* pool)
     setPublishedAddress(IpAddr(via_addrstr));
 
     /* Compare received and rport with the URI in our registration */
+    auto pjContact = sip_utils::CONST_PJ_STR(getContactHeader());
     const pj_str_t STR_CONTACT = {(char*) "Contact", 7};
     pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*)
-        pjsip_parse_hdr(pool, &STR_CONTACT, contact_.ptr, contact_.slen, nullptr);
+        pjsip_parse_hdr(pool, &STR_CONTACT, pjContact.ptr, pjContact.slen, nullptr);
     pj_assert(contact_hdr != nullptr);
     pjsip_sip_uri* uri = (pjsip_sip_uri*) contact_hdr->uri;
     pj_assert(uri != nullptr);
@@ -2106,21 +2099,21 @@ SIPAccount::checkNATAddress(pjsip_regc_cbparam* param, pj_pool_t* pool)
             transport_param = ";transport=tls";
         }
 
-        char* tmp = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
-        int len = printContactHeader(tmp, "", scheme, via_addrstr, rport, transport_param);
+        auto tmp = printContactHeader("", scheme, via_addrstr, rport, transport_param);
 
-        if (len < 1) {
-            JAMI_ERR("URI too long");
+        if (tmp.empty()) {
+            JAMI_ERR("Invalid contact header");
             return false;
         }
 
-        pj_str_t tmp_str = {tmp, len};
-        pj_strncpy_with_null(&contact_, &tmp_str, PJSIP_MAX_URL_SIZE);
+        // Update
+        contact_ = std::move(tmp);
+        pjContact = sip_utils::CONST_PJ_STR(contact_);
     }
 
     if (contactRewriteMethod_ == 2 && regc_ != nullptr) {
         contactOverwritten_ = true;
-        pjsip_regc_update_contact(regc_, 1, &contact_);
+        pjsip_regc_update_contact(regc_, 1, &pjContact);
 
         /*  Perform new registration at the next registration cycle */
     }
diff --git a/src/sip/sipaccount.h b/src/sip/sipaccount.h
index c3d473b190..c71d6c4d05 100644
--- a/src/sip/sipaccount.h
+++ b/src/sip/sipaccount.h
@@ -69,9 +69,6 @@ class SIPAccount : public SIPAccountBase
 {
 public:
     constexpr static const char* const ACCOUNT_TYPE = "SIP";
-    constexpr static const char* const CONTACT_HEADER_WITH_PN
-        = "%s<%s:%s%s%s:%d%s;pn-provider=%s;pn-param=%s;pn-prid=%s>";
-    constexpr static const char* const CONTACT_HEADER_WITHOUT_PN = "%s<%s:%s%s%s:%d%s>";
     constexpr static const char* const PN_FCM = "fcm";
     constexpr static const char* const PN_APNS = "apns";
 
@@ -348,9 +345,9 @@ public:
 
     /**
      * Get the contact header for
-     * @return pj_str_t The contact header based on account information
+     * @return The contact header based on account information
      */
-    pj_str_t getContactHeader(pjsip_transport* = nullptr) override;
+    std::string getContactHeader(SipTransport* transport = nullptr) override;
 
     std::string getServiceRoute() const { return serviceRoute_; }
 
@@ -613,12 +610,11 @@ private:
     /**
      * Print contact header in certain format
      */
-    int printContactHeader(char* data,
-                           const std::string& displayName,
-                           const char* scheme,
-                           const std::string& address,
-                           pj_uint16_t port,
-                           const char* transport);
+    std::string printContactHeader(const std::string& displayName,
+                                   const char* scheme,
+                                   const std::string& address,
+                                   pj_uint16_t port,
+                                   const char* transport);
 
     /**
      * Resolved IP of hostname_ (for registration)
@@ -750,8 +746,7 @@ private:
      */
     std::string upnpIpAddr_;
 
-    char contactBuffer_[PJSIP_MAX_URL_SIZE];
-    pj_str_t contact_;
+    std::string contact_;
     int contactRewriteMethod_;
     bool allowIPAutoRewrite_;
     /* Undocumented feature in pjsip, this can == 2 */
diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp
index a71979d625..498561e7e5 100644
--- a/src/sip/sipaccountbase.cpp
+++ b/src/sip/sipaccountbase.cpp
@@ -458,7 +458,7 @@ SIPAccountBase::getIceOptions() const noexcept
 void
 SIPAccountBase::onTextMessage(const std::string& id,
                               const std::string& from,
-                              const std::string& deviceId,
+                              const std::string& /* deviceId */,
                               const std::map<std::string, std::string>& payloads)
 {
     JAMI_DBG("Text message received from %s, %zu part(s)", from.c_str(), payloads.size());
@@ -476,7 +476,8 @@ SIPAccountBase::onTextMessage(const std::string& id,
 #ifdef ENABLE_PLUGIN
     auto& pluginChatManager = Manager::instance().getJamiPluginManager().getChatServicesManager();
     if (pluginChatManager.hasHandlers()) {
-        pluginChatManager.publishMessage(std::make_shared<JamiMessage>(accountID_, from, true, payloads, false));
+        pluginChatManager.publishMessage(
+            std::make_shared<JamiMessage>(accountID_, from, true, payloads, false));
     }
 #endif
     emitSignal<DRing::ConfigurationSignal::IncomingAccountMessage>(accountID_, from, id, payloads);
diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h
index e5d571fe71..67837d113e 100644
--- a/src/sip/sipaccountbase.h
+++ b/src/sip/sipaccountbase.h
@@ -216,9 +216,9 @@ public:
 
     /**
      * Get the contact header for
-     * @return pj_str_t The contact header based on account information
+     * @return The contact header based on account information
      */
-    virtual pj_str_t getContactHeader(pjsip_transport* = nullptr) = 0;
+    virtual std::string getContactHeader(SipTransport* transport = nullptr) = 0;
 
     virtual std::string getToUri(const std::string& username) const = 0;
 
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index fc0cccd0a1..5b134a62ff 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -420,9 +420,15 @@ SIPCall::generateMediaPorts()
 }
 
 void
-SIPCall::setContactHeader(pj_str_t contact)
+SIPCall::setContactHeader(const std::string& contact)
 {
-    pj_strcpy(&contactHeader_, &contact);
+    contactHeader_ = contact;
+}
+
+const std::string&
+SIPCall::getContactHeader() const
+{
+    return contactHeader_;
 }
 
 void
@@ -437,6 +443,12 @@ SIPCall::setTransport(const std::shared_ptr<SipTransport>& t)
     if (not t) {
         return;
     }
+    auto account = getSIPAccount();
+    if (not account) {
+        JAMI_ERR("[call:%s] No account detected", getCallId().c_str());
+    }
+
+    setContactHeader(account->getContactHeader(transport_.get()));
 
     if (isSrtpEnabled() and not transport_->isSecure()) {
         JAMI_WARN("[call:%s] Crypto (SRTP) is negotiated over an un-encrypted signaling channel",
@@ -701,9 +713,7 @@ SIPCall::terminateSipSession(int status)
             if (tdata) {
                 auto account = getSIPAccount();
                 if (account) {
-                    sip_utils::addContactHeader(account->getContactHeader(
-                                                    transport_ ? transport_->get() : nullptr),
-                                                tdata);
+                    sip_utils::addContactHeader(contactHeader_, tdata);
                     // Add user-agent header
                     sip_utils::addUserAgentHeader(account->getUserAgentName(), tdata);
                 } else {
@@ -750,8 +760,6 @@ SIPCall::answer()
         Manager::instance().sipVoIPLink().createSDPOffer(inviteSession_.get());
     }
 
-    setContactHeader(account->getContactHeader(transport_ ? transport_->get() : nullptr));
-
     pjsip_tx_data* tdata;
     if (!inviteSession_->last_answer)
         throw std::runtime_error("Should only be called for initial answer");
@@ -765,15 +773,16 @@ SIPCall::answer()
         != PJ_SUCCESS)
         throw std::runtime_error("Could not init invite request answer (200 OK)");
 
-    // contactStr must stay in scope as long as tdata
-    if (contactHeader_.slen) {
-        JAMI_DBG("[call:%s] Answering with contact header: %.*s",
-                 getCallId().c_str(),
-                 (int) contactHeader_.slen,
-                 contactHeader_.ptr);
-        sip_utils::addContactHeader(contactHeader_, tdata);
+    if (contactHeader_.empty()) {
+        throw std::runtime_error("Cant answer with an invalid contact header");
     }
 
+    JAMI_DBG("[call:%s] Answering with contact header: %s",
+             getCallId().c_str(),
+             contactHeader_.c_str());
+
+    sip_utils::addContactHeader(contactHeader_, tdata);
+
     // Add user-agent header
     sip_utils::addUserAgentHeader(account->getUserAgentName(), tdata);
 
@@ -879,8 +888,6 @@ SIPCall::answer(const std::vector<DRing::MediaMap>& mediaList)
         }
     }
 
-    setContactHeader(account->getContactHeader(transport_ ? transport_->get() : nullptr));
-
     if (!inviteSession_->last_answer)
         throw std::runtime_error("Should only be called for initial answer");
 
@@ -896,14 +903,16 @@ SIPCall::answer(const std::vector<DRing::MediaMap>& mediaList)
         != PJ_SUCCESS)
         throw std::runtime_error("Could not init invite request answer (200 OK)");
 
-    if (contactHeader_.slen) {
-        JAMI_DBG("[call:%s] Answering with contact header: %.*s",
-                 getCallId().c_str(),
-                 (int) contactHeader_.slen,
-                 contactHeader_.ptr);
-        sip_utils::addContactHeader(contactHeader_, tdata);
+    if (contactHeader_.empty()) {
+        throw std::runtime_error("Cant answer with an invalid contact header");
     }
 
+    JAMI_DBG("[call:%s] Answering with contact header: %s",
+             getCallId().c_str(),
+             contactHeader_.c_str());
+
+    sip_utils::addContactHeader(contactHeader_, tdata);
+
     // Add user-agent header
     sip_utils::addUserAgentHeader(account->getUserAgentName(), tdata);
 
@@ -990,7 +999,7 @@ SIPCall::answerMediaChangeRequest(const std::vector<DRing::MediaMap>& mediaList)
         return;
     }
 
-    if (contactHeader_.slen) {
+    if (not contactHeader_.empty()) {
         sip_utils::addContactHeader(contactHeader_, tdata);
     }
 
@@ -2501,7 +2510,7 @@ SIPCall::onReceiveOffer(const pjmedia_sdp_session* offer, const pjsip_rx_data* r
                                  NULL,
                                  &tdata)
         != PJ_SUCCESS) {
-        JAMI_ERR("Could not create initial answer OK");
+        JAMI_ERR("[call:%s] Could not create initial answer OK", getCallId().c_str());
         return !PJ_SUCCESS;
     }
 
@@ -2514,11 +2523,15 @@ SIPCall::onReceiveOffer(const pjmedia_sdp_session* offer, const pjsip_rx_data* r
         return !PJ_SUCCESS;
     }
 
-    // ContactStr must stay in scope as long as tdata
-    sip_utils::addContactHeader(getSIPAccount()->getContactHeader(getTransport()->get()), tdata);
+    if (contactHeader_.empty()) {
+        JAMI_ERR("[call:%s] Contact header is empty!", getCallId().c_str());
+        return !PJ_SUCCESS;
+    }
+
+    sip_utils::addContactHeader(contactHeader_, tdata);
 
     if (pjsip_inv_send_msg(inviteSession_.get(), tdata) != PJ_SUCCESS) {
-        JAMI_ERR("Could not send msg OK");
+        JAMI_ERR("[call:%s] Could not send msg OK", getCallId().c_str());
         return !PJ_SUCCESS;
     }
 
@@ -3029,8 +3042,7 @@ SIPCall::merge(Call& call)
     sdp_ = std::move(subcall.sdp_);
     peerHolding_ = subcall.peerHolding_;
     upnp_ = std::move(subcall.upnp_);
-    std::copy_n(subcall.contactBuffer_, PJSIP_MAX_URL_SIZE, contactBuffer_);
-    pj_strcpy(&contactHeader_, &subcall.contactHeader_);
+    contactHeader_ = std::move(subcall.contactHeader_);
     localAudioPort_ = subcall.localAudioPort_;
     localVideoPort_ = subcall.localVideoPort_;
     peerUserAgent_ = subcall.peerUserAgent_;
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index d0c1a5465c..1860a1d080 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -219,7 +219,8 @@ public:
     void onMediaNegotiationComplete();
     // End fo SiPVoipLink events
 
-    void setContactHeader(pj_str_t contact);
+    void setContactHeader(const std::string& contact);
+    const std::string& getContactHeader() const;
 
     void setTransport(const std::shared_ptr<SipTransport>& t);
 
@@ -432,8 +433,7 @@ private:
 
     std::string peerRegisteredName_ {};
 
-    char contactBuffer_[PJSIP_MAX_URL_SIZE] {};
-    pj_str_t contactHeader_ {contactBuffer_, 0};
+    std::string contactHeader_ {};
 
     std::shared_ptr<jami::upnp::Controller> upnp_;
 
diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp
index b82c6b4e5e..85301860e0 100644
--- a/src/sip/sipvoiplink.cpp
+++ b/src/sip/sipvoiplink.cpp
@@ -560,7 +560,7 @@ transaction_request_cb(pjsip_rx_data* rdata)
         return PJ_FALSE;
     }
 
-    sip_utils::addContactHeader(account->getContactHeader(transport->get()), tdata);
+    sip_utils::addContactHeader(call->getContactHeader(), tdata);
     if (pjsip_inv_send_msg(call->inviteSession_.get(), tdata) != PJ_SUCCESS) {
         JAMI_ERR("Could not send msg RINGING");
         return PJ_FALSE;
-- 
GitLab