From 904f78828de920dbf1540b45a105bcd23480233a Mon Sep 17 00:00:00 2001
From: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
Date: Tue, 24 Apr 2012 10:23:50 -0400
Subject: [PATCH] #9910: create new udp transport to fix registration failure
 with 606 error & received parameter

---
 daemon/src/sip/sipaccount.h     |  4 +++
 daemon/src/sip/siptransport.cpp | 51 +++++++++++++++++++++++++++++++++
 daemon/src/sip/siptransport.h   | 16 +++++++++++
 daemon/src/sip/sipvoiplink.cpp  | 23 +++++++++++----
 4 files changed, 89 insertions(+), 5 deletions(-)

diff --git a/daemon/src/sip/sipaccount.h b/daemon/src/sip/sipaccount.h
index 82ac7d9e87..007c4cab15 100644
--- a/daemon/src/sip/sipaccount.h
+++ b/daemon/src/sip/sipaccount.h
@@ -511,7 +511,11 @@ class SIPAccount : public Account {
          */
         static void keepAliveRegistrationCb(pj_timer_heap_t *th, pj_timer_entry *te);
 
+        /**
+         * Pointer to the transport used by this acccount
+         */
         pjsip_transport* transport_;
+
     private:
         NON_COPYABLE(SIPAccount);
 
diff --git a/daemon/src/sip/siptransport.cpp b/daemon/src/sip/siptransport.cpp
index 74385255c2..e1f10dadcd 100644
--- a/daemon/src/sip/siptransport.cpp
+++ b/daemon/src/sip/siptransport.cpp
@@ -54,6 +54,7 @@
 
 #include "sipaccount.h"
 
+#include "pjsip/sip_types.h"
 #include "dbus/dbusmanager.h"
 #include "dbus/configurationmanager.h"
 
@@ -428,6 +429,9 @@ SipTransport::createUdpTransport(const std::string &interface, unsigned int port
     pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &udpString, &boundAddr);
     pj_status_t status;
     pjsip_transport *transport = NULL;
+
+
+
     if (boundAddr.addr.sa_family == pj_AF_INET()) {
         status = pjsip_udp_transport_start(endpt_, &boundAddr.ipv4, NULL, 1, &transport);
         if (status != PJ_SUCCESS) {
@@ -448,6 +452,53 @@ SipTransport::createUdpTransport(const std::string &interface, unsigned int port
     return transport;
 }
 
+pjsip_transport *
+SipTransport::createUdpTransport(const std::string &interface, unsigned int port, std::string& publicAddr, unsigned int publicPort)
+{
+    // init socket to bind this transport to
+    pj_uint16_t listeningPort = (pj_uint16_t) port;
+    pjsip_transport *transport;
+
+    DEBUG("SipTransport: Update UDP transport on %s:%d with public addr %s:%d", interface.c_str(), port, publicAddr.c_str(), publicPort);
+
+    // determine the ip address for this transport
+    std::string listeningAddress;
+    if (interface == DEFAULT_INTERFACE)
+        listeningAddress = getSIPLocalIP();
+    else
+        listeningAddress = getInterfaceAddrFromName(interface);
+
+    if (listeningAddress.empty()) {
+        ERROR("SipTransport: Could not determine ip address for this transport");
+        return NULL;
+    }
+
+    std::ostringstream fullAddress;
+    fullAddress << listeningAddress << ":" << listeningPort;
+    pj_str_t udpString;
+    std::string fullAddressStr(fullAddress.str());
+    pj_cstr(&udpString, fullAddressStr.c_str());
+    pj_sockaddr boundAddr;
+    pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &udpString, &boundAddr);
+
+    pj_str_t public_addr = pj_str((char *) publicAddr.c_str());
+    pjsip_host_port hostPort;
+    hostPort.host = public_addr;
+    hostPort.port = publicPort;
+
+    pj_status_t status;
+    // status = pjsip_udp_transport_restart(transport, PJSIP_UDP_TRANSPORT_DESTROY_SOCKET, PJ_INVALID_SOCKET, &boundAddr.ipv4, &hostPort);
+    status = pjsip_udp_transport_start(endpt_, &boundAddr.ipv4, &hostPort, 1, &transport);
+    if (status != PJ_SUCCESS) {
+        ERROR("SipTransport: Could not start new transport with address %s:%d, error code %d", publicAddr.c_str(), publicPort, status);
+    }
+
+    // dump debug information to stdout
+    pjsip_tpmgr_dump_transports(pjsip_endpt_get_tpmgr(endpt_));
+
+    return transport;
+}
+
 pjsip_tpselector *SipTransport::initTransportSelector(pjsip_transport *transport, pj_pool_t *tp_pool) const
 {
     if (!transport) {
diff --git a/daemon/src/sip/siptransport.h b/daemon/src/sip/siptransport.h
index cbbad8c7ec..3d5537e7df 100644
--- a/daemon/src/sip/siptransport.h
+++ b/daemon/src/sip/siptransport.h
@@ -101,6 +101,10 @@ class SipTransport {
          */
         void createSipTransport(SIPAccount &account);
 
+        /**
+         * Create the default sip transport on 5060. In case this port is already used
+         * increme
+         */
         void createDefaultSipUdpTransport();
 
         /**
@@ -126,6 +130,18 @@ class SipTransport {
          */
         void findLocalAddressFromTransport(pjsip_transport *transport, pjsip_transport_type_e transportType, std::string &address, std::string &port) const;
 
+        /**
+         * Create a new udp transport specifying the bound address AND the published address.
+         * The published address is the address to appears in the sip VIA header. This is used
+         * essentially when the client is behind a trafic routing device.
+         *
+         * @param The interface to bind this transport with
+         * @param The requested udp port number
+         * @param The public address for this transport
+         * @param The public port for this transport
+         */
+        pjsip_transport *createUdpTransport(const std::string &interface, unsigned int port, std::string& publicAddr, unsigned int publicPort);
+
     private:
         NON_COPYABLE(SipTransport);
 
diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp
index 306c23f6e1..9dce39431a 100644
--- a/daemon/src/sip/sipvoiplink.cpp
+++ b/daemon/src/sip/sipvoiplink.cpp
@@ -489,6 +489,7 @@ bool SIPVoIPLink::getEvent()
 void SIPVoIPLink::sendRegister(Account *a)
 {
     SIPAccount *account = dynamic_cast<SIPAccount*>(a);
+
     if (!account)
         throw VoipLinkException("SipVoipLink: Account is not SIPAccount");
     sipTransport.createSipTransport(*account);
@@ -511,12 +512,19 @@ void SIPVoIPLink::sendRegister(Account *a)
     std::string from(account->getFromUri());
     pj_str_t pjFrom = pj_str((char*) from.c_str());
 
-    // Get the contact header for this account
-    std::string contact(account->getContactHeader());
+    // Get the received header
+    std::string received(account->getReceivedParameter());
+
+    // Get the contact header
+    std::string contact = account->getContactHeader();
     pj_str_t pjContact = pj_str((char*) contact.c_str());
 
-    std::string received(account->getReceivedParameter());
-    pj_str_t pjReceived = pj_str((char *) received.c_str());
+    if(!received.empty()) {
+        // Set received parameter string to empty in order to avoid creating new transport for each register
+        account->setReceivedParameter("");
+        // Explicitely set the bound address port to 0 so that pjsip determine a random port by itself
+        account->transport_= sipTransport.createUdpTransport(account->getLocalInterface(), 0, received, account->getLocalPort());
+    }
 
     if (pjsip_regc_init(regc, &pjSrv, &pjFrom, &pjFrom, 1, &pjContact, account->getRegistrationExpire()) != PJ_SUCCESS)
         throw VoipLinkException("Unable to initialize account registration structure");
@@ -536,7 +544,6 @@ void SIPVoIPLink::sendRegister(Account *a)
     pj_list_push_back(&hdr_list, (pjsip_hdr*) h);
     pjsip_regc_add_headers(regc, &hdr_list);
 
-
     pjsip_tx_data *tdata;
 
     if (pjsip_regc_register(regc, PJ_TRUE, &tdata) != PJ_SUCCESS)
@@ -1588,6 +1595,11 @@ void registration_cb(pjsip_regc_cbparam *param)
         return;
     }
 
+    if(param->code == 200) {
+        account->setRegister(true);
+        account->setRegistrationState(Registered);
+    }
+
     if (account->isContactUpdateEnabled())
         update_contact_header(param, account);
 
@@ -1615,6 +1627,7 @@ void registration_cb(pjsip_regc_cbparam *param)
             case PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE:
                 lookForReceivedParameter(param, account);
                 account->setRegistrationState(ErrorNotAcceptable);
+                SIPVoIPLink::instance()->sendRegister(account);
                 break;
 
             case PJSIP_SC_SERVICE_UNAVAILABLE:
-- 
GitLab