Bug 786236: Per-context configurable STUN servers. r=abr
authorEKR <ekr@rtfm.com>
Sat, 05 Jan 2013 11:40:34 -0800
changeset 119726 0eda7bbdfedf848d425d4aebaf256c4a443fda25
parent 119725 09ebdefa8c78f17189d2e3fb26ec44a55989c619
child 119727 6b5016ab9ebbb2ec884c4d865266446adebbc162
push id24219
push userryanvm@gmail.com
push dateThu, 24 Jan 2013 17:36:06 +0000
treeherdermozilla-central@fa969919b1bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersabr
bugs786236
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 786236: Per-context configurable STUN servers. r=abr
media/mtransport/nr_socket_prsock.cpp
media/mtransport/nr_socket_prsock.h
media/mtransport/nricectx.cpp
media/mtransport/nricectx.h
media/mtransport/test/ice_unittest.cpp
media/mtransport/test/transport_unittests.cpp
media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -234,17 +234,17 @@ static int nr_transport_addr_to_praddr(n
         ABORT(R_BAD_ARGS);
     }
 
     _status = 0;
   abort:
     return(_status);
   }
 
-static int nr_praddr_to_transport_addr(PRNetAddr *praddr,
+int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
   nr_transport_addr *addr, int keep)
   {
     int _status;
     int r;
     struct sockaddr_in ip4;
 
     switch(praddr->raw.family) {
       case PR_AF_INET:
--- a/media/mtransport/nr_socket_prsock.h
+++ b/media/mtransport/nr_socket_prsock.h
@@ -104,10 +104,13 @@ private:
 
   PRFileDesc *fd_;
   nr_transport_addr my_addr_;
   NR_async_cb cbs_[NR_ASYNC_WAIT_WRITE + 1];
   void *cb_args_[NR_ASYNC_WAIT_WRITE + 1];
   nsCOMPtr<nsISocketTransportService> stservice_;
 };
 
+int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
+                                nr_transport_addr *addr, int keep);
+
 }  // close namespace
 #endif
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -76,16 +76,17 @@ extern "C" {
 #include "ice_candidate.h"
 #include "ice_handler.h"
 }
 
 // Local includes
 #include "logging.h"
 #include "nricectx.h"
 #include "nricemediastream.h"
+#include "nr_socket_prsock.h"
 
 namespace mozilla {
 
 MOZ_MTLOG_MODULE("mtransport")
 
 static bool initialized = false;
 
 // Implement NSPR-based crypto algorithms
@@ -226,18 +227,18 @@ int NrIceCtx::msg_recvd(void *obj, nr_ic
 
   s->SignalPacketReceived(s, component_id, msg, len);
 
   return 0;
 }
 
 
 RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& name,
-                                           bool offerer,
-                                           bool set_interface_priorities) {
+                                  bool offerer,
+                                  bool set_interface_priorities) {
   RefPtr<NrIceCtx> ctx = new NrIceCtx(name, offerer);
 
   // Initialize the crypto callbacks
   if (!initialized) {
     NR_reg_init(NR_REG_MODE_LOCAL);
     nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl;
     initialized = true;
 
@@ -270,18 +271,16 @@ RefPtr<NrIceCtx> NrIceCtx::Create(const 
       NR_reg_set_uchar((char *)"ice.pref.interface.vmnet5", 237);
       NR_reg_set_uchar((char *)"ice.pref.interface.vmnet6", 236);
       NR_reg_set_uchar((char *)"ice.pref.interface.vmnet7", 235);
       NR_reg_set_uchar((char *)"ice.pref.interface.vmnet8", 234);
       NR_reg_set_uchar((char *)"ice.pref.interface.virbr0", 233);
       NR_reg_set_uchar((char *)"ice.pref.interface.wlan0", 232);
     }
 
-    NR_reg_set_string((char *)"ice.stun.server.0.addr", (char *)"23.21.150.121");
-    NR_reg_set_uint2((char *)"ice.stun.server.0.port",3478);
     NR_reg_set_uint4((char *)"stun.client.maximum_transmits",4);
   }
 
   // Create the ICE context
   int r;
 
   UINT4 flags = offerer ? NR_ICE_CTX_FLAGS_OFFERER:
       NR_ICE_CTX_FLAGS_ANSWERER;
@@ -352,16 +351,43 @@ void NrIceCtx::destroy_peer_ctx() {
 nsresult NrIceCtx::SetControlling(Controlling controlling) {
   peer_->controlling = (controlling == ICE_CONTROLLING)? 1 : 0;
 
   MOZ_MTLOG(PR_LOG_DEBUG, "ICE ctx " << name_ << " setting controlling to" <<
             controlling);
   return NS_OK;
 }
 
+nsresult NrIceCtx::SetStunServers(const std::vector<NrIceStunServer>&
+                                  stun_servers) {
+  if (stun_servers.empty())
+    return NS_OK;
+
+  ScopedDeleteArray<nr_ice_stun_server> servers(
+      new nr_ice_stun_server[stun_servers.size()]);
+
+  int r;
+  for (size_t i=0; i < stun_servers.size(); ++i) {
+    r = nr_praddr_to_transport_addr(&stun_servers[i].addr(),
+                                    &servers[i].addr, 0);
+    if (r) {
+      MOZ_MTLOG(PR_LOG_ERROR, "Couldn't set STUN server for '" << name_ << "'");
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  r = nr_ice_ctx_set_stun_servers(ctx_, servers, stun_servers.size());
+  if (r) {
+    MOZ_MTLOG(PR_LOG_ERROR, "Couldn't set STUN server for '" << name_ << "'");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
 nsresult NrIceCtx::StartGathering() {
   this->AddRef();
   int r = nr_ice_initialize(ctx_, &NrIceCtx::initialized_cb,
                             this);
 
   if (r && r != R_WOULDBLOCK) {
       MOZ_MTLOG(PR_LOG_ERROR, "Couldn't gather ICE candidates for '"
            << name_ << "'");
--- a/media/mtransport/nricectx.h
+++ b/media/mtransport/nricectx.h
@@ -49,16 +49,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 // This is a wrapper around the nICEr ICE stack
 #ifndef nricectx_h__
 #define nricectx_h__
 
 #include <vector>
 
 #include "sigslot.h"
 
+#include "prnetdb.h"
+
 #include "mozilla/RefPtr.h"
 #include "mozilla/Scoped.h"
 #include "nsAutoPtr.h"
 #include "nsIEventTarget.h"
 #include "nsITimer.h"
 
 #include "m_cpp_utils.h"
 
@@ -69,33 +71,77 @@ typedef struct nr_ice_ctx_ nr_ice_ctx;
 typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx;
 typedef struct nr_ice_media_stream_ nr_ice_media_stream;
 typedef struct nr_ice_handler_ nr_ice_handler;
 typedef struct nr_ice_handler_vtbl_ nr_ice_handler_vtbl;
 typedef struct nr_ice_cand_pair_ nr_ice_cand_pair;
 
 class NrIceMediaStream;
 
+
+struct NrIceStunServer {
+ public:
+  NrIceStunServer(const PRNetAddr& addr) {
+    memcpy(&addr_, &addr, sizeof(addr));
+  }
+
+  // Convenience function to allow you to pass an IP addr as a string
+  static NrIceStunServer* Create(const std::string& addr, uint16_t port) {
+    ScopedDeletePtr<NrIceStunServer> server(
+        new NrIceStunServer());
+
+    nsresult rv = server->Init(addr, port);
+    if (NS_FAILED(rv))
+      return nullptr;
+
+    return server.forget();
+  }
+  
+
+  const PRNetAddr& addr() const { return addr_; }
+
+ private:
+  NrIceStunServer() : addr_() {}
+
+  nsresult Init(const std::string& addr, uint16_t port) {
+    PRStatus status = PR_StringToNetAddr(addr.c_str(), &addr_);
+    if (status != PR_SUCCESS)
+      return NS_ERROR_INVALID_ARG;
+
+    addr_.inet.port = PR_htons(port);
+
+    return NS_OK;
+  }
+
+  PRNetAddr addr_;
+};
+
+struct NrIceTurnServer {
+  PRNetAddr addr;
+  std::string username;
+  std::string password;
+};
+
 class NrIceCtx {
  public:
   enum State { ICE_CTX_INIT,
                ICE_CTX_GATHERING,
                ICE_CTX_GATHERED,
                ICE_CTX_CHECKING,
                ICE_CTX_OPEN,
                ICE_CTX_FAILED
   };
 
   enum Controlling { ICE_CONTROLLING,
                      ICE_CONTROLLED
   };
 
   static RefPtr<NrIceCtx> Create(const std::string& name,
-                                          bool offerer,
-                                          bool set_interface_priorities = true);
+                                 bool offerer,
+                                 bool set_interface_priorities = true);
   virtual ~NrIceCtx();
 
   nr_ice_ctx *ctx() { return ctx_; }
   nr_ice_peer_ctx *peer() { return peer_; }
 
   // Testing only.
   void destroy_peer_ctx();
 
@@ -113,16 +159,20 @@ class NrIceCtx {
   std::vector<std::string> GetGlobalAttributes();
 
   // Set the other side's global attributes
   nsresult ParseGlobalAttributes(std::vector<std::string> attrs);
 
   // Set whether we are controlling or not.
   nsresult SetControlling(Controlling controlling);
 
+  // Set the STUN servers. Must be called before StartGathering
+  // (if at all).
+  nsresult SetStunServers(const std::vector<NrIceStunServer>& stun_servers);
+
   // Start ICE gathering
   nsresult StartGathering();
 
   // Start checking
   nsresult StartChecks();
 
   // Finalize the ICE negotiation. I.e., there will be no
   // more forking.
@@ -134,25 +184,26 @@ class NrIceCtx {
   sigslot::signal1<NrIceCtx *> SignalCompleted;  // Done handshaking
 
   // The thread to direct method calls to
   nsCOMPtr<nsIEventTarget> thread() { return sts_target_; }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceCtx)
 
  private:
-  NrIceCtx(const std::string& name, bool offerer)
-      : state_(ICE_CTX_INIT),
-      name_(name),
-      offerer_(offerer),
-      streams_(),
-      ctx_(nullptr),
-      peer_(nullptr),
-      ice_handler_vtbl_(nullptr),
-      ice_handler_(nullptr) {}
+  NrIceCtx(const std::string& name,
+           bool offerer)
+  : state_(ICE_CTX_INIT),
+    name_(name),
+    offerer_(offerer),
+    streams_(),
+    ctx_(nullptr),
+    peer_(nullptr),
+    ice_handler_vtbl_(nullptr),
+    ice_handler_(nullptr) {}
 
   DISALLOW_COPY_ASSIGN(NrIceCtx);
 
   // Callbacks for nICEr
   static void initialized_cb(NR_SOCKET s, int h, void *arg);  // ICE initialized
 
   // Handler implementation
   static int select_pair(void *obj,nr_ice_media_stream *stream,
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -12,16 +12,17 @@
 #include <vector>
 
 #include "sigslot.h"
 
 #include "nspr.h"
 #include "nss.h"
 #include "ssl.h"
 
+#include "mozilla/Scoped.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
 
 #include "logging.h"
 #include "nricectx.h"
 #include "nricemediastream.h"
 #include "mtransport_test_utils.h"
 #include "runnable_utils.h"
@@ -70,16 +71,22 @@ class IceTestPeer : public sigslot::has_
     stream->SignalCandidate.connect(this, &IceTestPeer::GotCandidate);
     stream->SignalReady.connect(this, &IceTestPeer::StreamReady);
     stream->SignalPacketReceived.connect(this, &IceTestPeer::PacketReceived);
   }
 
   void Gather() {
     nsresult res;
 
+    std::vector<NrIceStunServer> stun_servers;
+    ScopedDeletePtr<NrIceStunServer> server(NrIceStunServer::Create(
+        std::string((char *)"216.93.246.14"), 3478));
+    stun_servers.push_back(*server);
+    ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers)));
+
     test_utils->sts_target()->Dispatch(
         WrapRunnableRet(ice_ctx_, &NrIceCtx::StartGathering, &res),
         NS_DISPATCH_SYNC);
 
     ASSERT_TRUE(NS_SUCCEEDED(res));
   }
 
   // Get various pieces of state
--- a/media/mtransport/test/transport_unittests.cpp
+++ b/media/mtransport/test/transport_unittests.cpp
@@ -107,16 +107,22 @@ class TransportTestPeer : public sigslot
         ice_ctx_(NrIceCtx::Create(name,
                                   name == "P2" ?
                                   TransportLayerDtls::CLIENT :
                                   TransportLayerDtls::SERVER)),
         streams_(), candidates_(),
         peer_(nullptr),
         gathering_complete_(false)
  {
+    std::vector<NrIceStunServer> stun_servers;
+    ScopedDeletePtr<NrIceStunServer> server(NrIceStunServer::Create(
+        std::string((char *)"216.93.246.14"), 3478));
+    stun_servers.push_back(*server);
+    EXPECT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers)));
+
     dtls_->SetIdentity(identity_);
     dtls_->SetRole(name == "P2" ?
                    TransportLayerDtls::CLIENT :
                    TransportLayerDtls::SERVER);
 
     nsresult res = identity_->ComputeFingerprint("sha-1",
                                              fingerprint_,
                                              sizeof(fingerprint_),
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
@@ -107,16 +107,60 @@ int nr_ice_fetch_stun_servers(int ct, nr
 
     _status=0;
   abort:
     RFREE(addr);
     if (_status) RFREE(servers);
     return(_status);
   }
 
+int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers,int ct)
+  {
+    int _status;
+
+    if(ctx->stun_servers){
+      RFREE(ctx->stun_servers);
+      ctx->stun_server_ct=0;
+    }
+
+    if (ct) {
+      if(!(ctx->stun_servers=RCALLOC(sizeof(nr_ice_stun_server)*ct)))
+        ABORT(R_NO_MEMORY);
+
+      memcpy(ctx->stun_servers,servers,sizeof(nr_ice_stun_server)*ct);
+      ctx->stun_server_ct = ct;
+    }
+
+    _status=0;
+ abort:
+    return(_status);
+  }
+
+int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers,int ct)
+  {
+    int _status;
+
+    if(ctx->turn_servers){
+      RFREE(ctx->turn_servers);
+      ctx->turn_server_ct=0;
+    }
+
+    if(ct) {
+      if(!(ctx->turn_servers=RCALLOC(sizeof(nr_ice_turn_server)*ct)))
+        ABORT(R_NO_MEMORY);
+
+      memcpy(ctx->turn_servers,servers,sizeof(nr_ice_turn_server)*ct);
+      ctx->turn_server_ct = ct;
+    }
+
+    _status=0;
+ abort:
+    return(_status);
+  }
+
 #ifdef USE_TURN
 int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out)
   {
     int r,_status;
     nr_ice_turn_server *servers = 0;
     int i;
     NR_registry child;
     char *addr=0;
@@ -209,34 +253,36 @@ int nr_ice_ctx_create(char *label, UINT4
     if(r=nr_ice_random_string(buf,8))
       ABORT(r);
     if(!(ctx->ufrag=r_strdup(buf)))
       ABORT(r);
     if(r=nr_ice_random_string(buf,32))
       ABORT(r);
     if(!(ctx->pwd=r_strdup(buf)))
       ABORT(r);
-    
+
     /* Get the STUN servers */
     if(r=NR_reg_get_child_count(NR_ICE_REG_STUN_SRV_PRFX,
       (unsigned int *)&ctx->stun_server_ct)||ctx->stun_server_ct==0) {
       r_log(LOG_ICE,LOG_NOTICE,"No STUN servers specified");
       ctx->stun_server_ct=0;
     }
 
     /* 255 is the max for our priority algorithm */
     if(ctx->stun_server_ct>255){
       r_log(LOG_ICE,LOG_WARNING,"Too many STUN servers specified: max=255");
       ctx->stun_server_ct=255;
     }
 
-    if(r=nr_ice_fetch_stun_servers(ctx->stun_server_ct,&ctx->stun_servers)){
-      r_log(LOG_ICE,LOG_ERR,"Couldn't load STUN servers from registry");
-      ctx->turn_server_ct=0;
-      ABORT(r);
+    if(ctx->stun_server_ct>0){
+      if(r=nr_ice_fetch_stun_servers(ctx->stun_server_ct,&ctx->stun_servers)){
+        r_log(LOG_ICE,LOG_ERR,"Couldn't load STUN servers from registry");
+        ctx->stun_server_ct=0;
+        ABORT(r);
+      }
     }
 
 #ifdef USE_TURN
     /* Get the TURN servers */
     if(r=NR_reg_get_child_count(NR_ICE_REG_TURN_SRV_PRFX,
       (unsigned int *)&ctx->turn_server_ct)||ctx->turn_server_ct==0) {
       r_log(LOG_ICE,LOG_NOTICE,"No TURN servers specified");
       ctx->turn_server_ct=0;
@@ -247,20 +293,22 @@ int nr_ice_ctx_create(char *label, UINT4
 
     /* 255 is the max for our priority algorithm */
     if((ctx->stun_server_ct+ctx->turn_server_ct)>255){
       r_log(LOG_ICE,LOG_WARNING,"Too many STUN/TURN servers specified: max=255");
       ctx->turn_server_ct=255-ctx->stun_server_ct;
     }
 
 #ifdef USE_TURN
-    if(r=nr_ice_fetch_turn_servers(ctx->turn_server_ct,&ctx->turn_servers)){
-      ctx->turn_server_ct=0;
-      r_log(LOG_ICE,LOG_ERR,"Couldn't load TURN servers from registry");
-      ABORT(r);
+    if(ctx->turn_server_ct>0){
+      if(r=nr_ice_fetch_turn_servers(ctx->turn_server_ct,&ctx->turn_servers)){
+        ctx->turn_server_ct=0;
+        r_log(LOG_ICE,LOG_ERR,"Couldn't load TURN servers from registry");
+        ABORT(r);
+      }
     }
 #endif /* USE_TURN */
 
 
     ctx->Ta = 20;
 
     STAILQ_INIT(&ctx->streams);
     STAILQ_INIT(&ctx->sockets);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
@@ -142,16 +142,18 @@ int nr_ice_initialize(nr_ice_ctx *ctx, N
 int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
 void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg);
 int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp);
 int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp);
 int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
 int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]);
 int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg);
 int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx);
+int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers, int ct);
+int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers, int ct);
 
 extern int LOG_ICE;
 
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
 #endif
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -73,16 +73,33 @@ nsresult PeerConnectionMedia::Init()
   // TODO(ekr@rtfm.com): need some way to set not offerer later
   // Looks like a bug in the NrIceCtx API.
   mIceCtx = NrIceCtx::Create("PC:" + mParent->GetHandle(), true);
   if(!mIceCtx) {
     CSFLogErrorS(logTag, __FUNCTION__ << ": Failed to create Ice Context");
     return NS_ERROR_FAILURE;
   }
 
+  // Temporarily hardwire the ICE server.
+  // TODO(ekr@rtfm.com): Remove this when we have ICE configuration
+  // settings.
+  std::vector<NrIceStunServer> stun_servers;
+  ScopedDeletePtr<NrIceStunServer> server(NrIceStunServer::Create(
+      std::string((char *)"216.93.246.14"), 3478));
+  MOZ_ASSERT(server);
+  if (!server) {
+    CSFLogErrorS(logTag, __FUNCTION__ << ": Could not parse STUN server string");
+    return NS_ERROR_FAILURE;
+  }
+    
+  stun_servers.push_back(*server);
+  nsresult rv = mIceCtx->SetStunServers(stun_servers);
+  if (NS_FAILED(rv))
+    return rv;
+
   mIceCtx->SignalGatheringCompleted.connect(this,
                                             &PeerConnectionMedia::IceGatheringCompleted);
   mIceCtx->SignalCompleted.connect(this,
                                    &PeerConnectionMedia::IceCompleted);
 
   // Create three streams to start with.
   // One each for audio, video and DataChannel
   // TODO: this will be re-visited