From 600848639b82af2d710d7c1b98baee408ab9eb68 Mon Sep 17 00:00:00 2001
From: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
Date: Wed, 26 Oct 2016 16:23:43 -0400
Subject: [PATCH] ice: fix addReflectiveCandidate() with new PJNATH

addReflectiveCandidate is a hack of how PJNATH ICE transport work
and has made some assumptions only available on PJSIP 2.4.x.

PJNATH has changed of implementation when PJSIP 2.5.5 has been released
to support IPv6 addressing.

This bump to 2.5.5 invalids these assumptions causing some
nasty effects when addReflectiveCandidate is called (ex: UPnP).

This patch adapts the hack by a wonder-more-powerful hack.

It hacks the ICE transport STUN server array, that normally
filled by at leas one or two local host IP (as a local host is
seen as a local STUN server by PJNATH ICE transport layer).
This permits internal PJNATH implementation to be "happy".

-sorry

Change-Id: I38d133bd8891675251521e14916eeeaa557703f2
Reviewed-by: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
---
 src/ice_transport.cpp | 51 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 44 insertions(+), 7 deletions(-)

diff --git a/src/ice_transport.cpp b/src/ice_transport.cpp
index b0af4ac9c3..4ecdbcce67 100644
--- a/src/ice_transport.cpp
+++ b/src/ice_transport.cpp
@@ -634,10 +634,6 @@ IceTransport::registerPublicIP(unsigned compId, const IpAddr& publicIP)
     if (not isInitialized())
         return false;
 
-    // Register only if no NAT traversal methods exists
-    if (upnp_ or config_.stun.port > 0 or config_.turn.port > 0)
-        return false;
-
     // Find the local candidate corresponding to local host,
     // then register a rflx candidate using given public address
     // and this local address as base. It's port is used for both address
@@ -661,13 +657,54 @@ IceTransport::registerPublicIP(unsigned compId, const IpAddr& publicIP)
 void
 IceTransport::addReflectiveCandidate(int comp_id, const IpAddr& base, const IpAddr& addr)
 {
+    // HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK
+    // WARNING: following implementation is a HACK of PJNATH !!
+    // ice_strans doesn't have any API that permit to inject ICE any kind of candidates.
+    // So, the hack consists in accessing hidden ICE session using a patched PJPNATH
+    // library with a new API exposing this session (pj_ice_strans_get_ice_sess).
+    // Then call pj_ice_sess_add_cand() with a carfully forged candidate:
+    // the transport_id field uses an index in ICE transport STUN servers array
+    // corresponding to a STUN server with the same address familly.
+    // This implies we hope they'll not be modification of transport_id meaning in future
+    // and no conflics with the borrowed STUN config.
+    // HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK-HACK
+
+    // borrowed from pjproject/pjnath/ice_strans.c, modified to be C++11'ized.
+    static auto CREATE_TP_ID = [](pj_uint8_t type, pj_uint8_t idx) {
+        return (pj_uint8_t)((type << 6) | idx);
+    };
+    static constexpr int SRFLX_PREF = 65535;
+    static constexpr int TP_STUN = 1;
+
+    // find a compatible STUN host with same address familly, normally all system enabled
+    // host addresses are represented, so we expect to always found this host
+    int idx = -1;
+    auto af = addr.getFamily();
+    if (af == AF_UNSPEC) {
+        RING_ERR("[ice] Unable to add reflective IP %s: unknown addess familly",
+                 addr.toString().c_str());
+        return;
+    }
+
+    for (unsigned i=0; i < config_.stun_tp_cnt; ++i) {
+        if (config_.stun_tp[i].af == af) {
+            idx = i;
+            break;
+        }
+    }
+    if (idx < 0) {
+        RING_ERR("[ice] Unable to add reflective IP %s: no suitable local STUN host found",
+                 addr.toString().c_str());
+        return;
+    }
+
     pj_ice_sess_cand cand;
 
     cand.type = PJ_ICE_CAND_TYPE_SRFLX;
-    cand.status = PJ_EPENDING; /* not used */
+    cand.status = PJ_EPENDING; // not used
     cand.comp_id = comp_id;
-    cand.transport_id = 1; /* 1 = STUN */
-    cand.local_pref = 65535; /* host */
+    cand.transport_id = CREATE_TP_ID(TP_STUN, idx); // HACK!!
+    cand.local_pref = SRFLX_PREF; // reflective
     /* cand.foundation = ? */
     /* cand.prio = calculated by ice session */
     /* make base and addr the same since we're not going through a server */
-- 
GitLab