Bug 906968 - Add support for TURN TCP. r=abr
authorEKR <ekr@rtfm.com>
Fri, 06 Dec 2013 10:20:19 -0800
changeset 173981 60e84998a0a2aebe31cae479c8f079547cc72c61
parent 173980 edb01fe9d0002e2b25f472a8e4b62da954bb4cf1
child 173982 2f8a83944e7149d76b94c7bfa85c3495bf9a5097
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersabr
bugs906968
milestone28.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 906968 - Add support for TURN TCP. r=abr
media/mtransport/nr_socket_prsock.cpp
media/mtransport/nr_socket_prsock.h
media/mtransport/nricectx.cpp
media/mtransport/nricectx.h
media/mtransport/nricemediastream.cpp
media/mtransport/nricemediastream.h
media/mtransport/nriceresolver.cpp
media/mtransport/nriceresolver.h
media/mtransport/nriceresolverfake.cpp
media/mtransport/nriceresolverfake.h
media/mtransport/test/Makefile.in
media/mtransport/test/buffered_stun_socket_unittest.cpp
media/mtransport/test/ice_unittest.cpp
media/mtransport/test/moz.build
media/mtransport/test/mtransport_test_utils.h
media/mtransport/test/stunserver.cpp
media/mtransport/test/turn_unittest.cpp
media/mtransport/third_party/nICEr/nicer.gyp
media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
media/mtransport/third_party/nICEr/src/ice/ice_component.c
media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
media/mtransport/third_party/nICEr/src/ice/ice_socket.c
media/mtransport/third_party/nICEr/src/ice/ice_socket.h
media/mtransport/third_party/nICEr/src/net/nr_socket.c
media/mtransport/third_party/nICEr/src/net/nr_socket.h
media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h
media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c
media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c
media/mtransport/third_party/nICEr/src/stun/stun_hint.c
media/mtransport/third_party/nICEr/src/stun/stun_hint.h
media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c
media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h
media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -224,17 +224,16 @@ static int nr_transport_addr_to_praddr(n
   PRNetAddr *naddr)
   {
     int _status;
 
     memset(naddr, 0, sizeof(*naddr));
 
     switch(addr->protocol){
       case IPPROTO_TCP:
-        ABORT(R_INTERNAL); /* Can't happen for now */
         break;
       case IPPROTO_UDP:
         break;
       default:
         ABORT(R_BAD_ARGS);
     }
 
     switch(addr->ip_version){
@@ -312,54 +311,55 @@ static int nr_transport_addr_to_netaddr(
   }
 
   _status = 0;
 abort:
   return(_status);
 }
 
 int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
-  nr_transport_addr *addr)
+                                 nr_transport_addr *addr, int protocol)
   {
     int _status;
     int r;
 
     switch(netaddr->raw.family) {
       case AF_INET:
         if ((r = nr_ip4_port_to_transport_addr(ntohl(netaddr->inet.ip),
                                                ntohs(netaddr->inet.port),
-                                               IPPROTO_UDP, addr)))
+                                               protocol, addr)))
           ABORT(r);
         break;
       case AF_INET6:
         ABORT(R_BAD_ARGS);
       default:
         MOZ_ASSERT(false);
         ABORT(R_BAD_ARGS);
     }
     _status=0;
   abort:
     return(_status);
   }
 
 int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
-  nr_transport_addr *addr, int keep)
+                                nr_transport_addr *addr, int protocol,
+                                int keep)
   {
     int _status;
     int r;
     struct sockaddr_in ip4;
 
     switch(praddr->raw.family) {
       case PR_AF_INET:
         ip4.sin_family = PF_INET;
         ip4.sin_addr.s_addr = praddr->inet.ip;
         ip4.sin_port = praddr->inet.port;
         if ((r = nr_sockaddr_to_transport_addr((sockaddr *)&ip4,
                                                sizeof(ip4),
-                                               IPPROTO_UDP, keep,
+                                               protocol, keep,
                                                addr)))
           ABORT(r);
         break;
       case PR_AF_INET6:
 #if 0
         r = nr_sockaddr_to_transport_addr((sockaddr *)&praddr->raw,
           sizeof(struct sockaddr_in6),IPPROTO_UDP,keep,addr);
         break;
@@ -415,19 +415,31 @@ int NrSocket::create(nr_transport_addr *
 
   if (!NS_SUCCEEDED(rv)) {
     ABORT(R_INTERNAL);
   }
 
   if((r=nr_transport_addr_to_praddr(addr, &naddr)))
     ABORT(r);
 
-  if (!(fd_ = PR_NewUDPSocket())) {
-    r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create socket");
-    ABORT(R_INTERNAL);
+  switch (addr->protocol) {
+    case IPPROTO_UDP:
+      if (!(fd_ = PR_NewUDPSocket())) {
+        r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create socket");
+        ABORT(R_INTERNAL);
+      }
+      break;
+    case IPPROTO_TCP:
+      if (!(fd_ = PR_NewTCPSocket())) {
+        r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create socket");
+        ABORT(R_INTERNAL);
+      }
+      break;
+    default:
+      ABORT(R_INTERNAL);
   }
 
   status = PR_Bind(fd_, &naddr);
   if (status != PR_SUCCESS) {
     r_log(LOG_GENERIC,LOG_CRIT,"Couldn't bind socket to address %s",
           addr->as_string);
     ABORT(R_INTERNAL);
   }
@@ -439,17 +451,17 @@ int NrSocket::create(nr_transport_addr *
   /* If we have a wildcard port, patch up the addr */
   if(nr_transport_addr_is_wildcard(addr)){
     status = PR_GetSockName(fd_, &naddr);
     if (status != PR_SUCCESS){
       r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
       ABORT(R_INTERNAL);
     }
 
-    if((r=nr_praddr_to_transport_addr(&naddr,&my_addr_,1)))
+    if((r=nr_praddr_to_transport_addr(&naddr,&my_addr_,addr->protocol,1)))
       ABORT(r);
   }
 
 
   // Set nonblocking
   PRSocketOptionData option;
   option.option = PR_SockOpt_Nonblocking;
   option.value.non_blocking = PR_TRUE;
@@ -527,31 +539,31 @@ int NrSocket::sendto(const void *msg, si
   }
 
   _status=0;
 abort:
   return(_status);
 }
 
 int NrSocket::recvfrom(void * buf, size_t maxlen,
-                                       size_t *len, int flags,
-                                       nr_transport_addr *from) {
+                       size_t *len, int flags,
+                       nr_transport_addr *from) {
   ASSERT_ON_THREAD(ststhread_);
   int r,_status;
   PRNetAddr nfrom;
   int32_t status;
 
   status = PR_RecvFrom(fd_, buf, maxlen, flags, &nfrom, PR_INTERVAL_NO_WAIT);
   if (status <= 0) {
     r_log(LOG_GENERIC,LOG_ERR,"Error in recvfrom");
     ABORT(R_IO_ERROR);
   }
   *len=status;
 
-  if((r=nr_praddr_to_transport_addr(&nfrom,from,0)))
+  if((r=nr_praddr_to_transport_addr(&nfrom,from,my_addr_.protocol,0)))
     ABORT(r);
 
   //r_log(LOG_GENERIC,LOG_DEBUG,"Read %d bytes from %s",*len,addr->as_string);
 
   _status=0;
 abort:
   return(_status);
 }
@@ -562,16 +574,92 @@ int NrSocket::getaddr(nr_transport_addr 
 }
 
 // Close the socket so that the STS will detach and then kill it
 void NrSocket::close() {
   ASSERT_ON_THREAD(ststhread_);
   mCondition = NS_BASE_STREAM_CLOSED;
 }
 
+
+int NrSocket::connect(nr_transport_addr *addr) {
+  ASSERT_ON_THREAD(ststhread_);
+  int r,_status;
+  PRNetAddr naddr;
+  int32_t status;
+
+  if ((r=nr_transport_addr_to_praddr(addr, &naddr)))
+    ABORT(r);
+
+  if(!fd_)
+    ABORT(R_EOD);
+
+  // Note: this just means we tried to connect, not that we
+  // are actually live.
+  connect_invoked_ = true;
+  status = PR_Connect(fd_, &naddr, PR_INTERVAL_NO_WAIT);
+
+  if (status != PR_SUCCESS) {
+    if (PR_GetError() == PR_IN_PROGRESS_ERROR)
+      ABORT(R_WOULDBLOCK);
+
+    ABORT(R_IO_ERROR);
+  }
+
+  _status=0;
+abort:
+  return(_status);
+}
+
+
+int NrSocket::write(const void *msg, size_t len, size_t *written) {
+  ASSERT_ON_THREAD(ststhread_);
+  int _status;
+  int32_t status;
+
+  if (!connect_invoked_)
+    ABORT(R_FAILED);
+
+  status = PR_Write(fd_, msg, len);
+  if (status < 0) {
+    if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+      ABORT(R_WOULDBLOCK);
+    ABORT(R_IO_ERROR);
+  }
+
+  *written = status;
+
+  _status=0;
+abort:
+  return _status;
+}
+
+int NrSocket::read(void* buf, size_t maxlen, size_t *len) {
+  ASSERT_ON_THREAD(ststhread_);
+  int _status;
+  int32_t status;
+
+  if (!connect_invoked_)
+    ABORT(R_FAILED);
+
+  status = PR_Read(fd_, buf, maxlen);
+  if (status < 0) {
+    if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+      ABORT(R_WOULDBLOCK);
+    ABORT(R_IO_ERROR);
+  }
+  if (status == 0)
+    ABORT(R_EOD);
+
+  *len = (size_t)status;  // Guaranteed to be > 0
+  _status = 0;
+abort:
+  return(_status);
+}
+
 // NrSocketIpc Implementation
 NS_IMPL_ISUPPORTS1(NrSocketIpc, nsIUDPSocketInternal)
 
 NrSocketIpc::NrSocketIpc(const nsCOMPtr<nsIEventTarget> &main_thread)
     : err_(false),
       state_(NR_INIT),
       main_thread_(main_thread),
       monitor_("NrSocketIpc") {
@@ -671,17 +759,17 @@ NS_IMETHODIMP NrSocketIpc::CallListenerV
     }
 
     nr_transport_addr expected_addr;
     if(nr_transport_addr_copy(&expected_addr, &my_addr_)) {
       err_ = true;
       MOZ_ASSERT(false, "Failed to copy my_addr_");
     }
 
-    if (nr_praddr_to_transport_addr(&praddr, &my_addr_, 1)) {
+    if (nr_praddr_to_transport_addr(&praddr, &my_addr_, IPPROTO_UDP, 1)) {
       err_ = true;
       MOZ_ASSERT(false, "Failed to copy local host to my_addr_");
     }
 
     if (nr_transport_addr_cmp(&expected_addr, &my_addr_,
                               NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) {
       err_ = true;
       MOZ_ASSERT(false, "Address of opened socket is not expected");
@@ -846,17 +934,17 @@ int NrSocketIpc::recvfrom(void *buf, siz
     ABORT(R_WOULDBLOCK);
   }
 
   {
     RefPtr<nr_udp_message> msg(received_msgs_.front());
 
     received_msgs_.pop();
 
-    if ((r=nr_praddr_to_transport_addr(&msg->from, from, 0))) {
+    if ((r=nr_praddr_to_transport_addr(&msg->from, from, IPPROTO_UDP, 0))) {
       err_ = true;
       MOZ_ASSERT(false, "Get bogus address for received UDP packet");
       ABORT(r);
     }
 
     consumed_len = std::min(maxlen, msg->data->len());
     if (consumed_len < msg->data->len()) {
       r_log(LOG_GENERIC, LOG_DEBUG, "Partial received UDP packet will be discard");
@@ -878,16 +966,31 @@ int NrSocketIpc::getaddr(nr_transport_ad
 
   if (state_ != NR_CONNECTED) {
     return R_INTERNAL;
   }
 
   return nr_transport_addr_copy(addrp, &my_addr_);
 }
 
+int NrSocketIpc::connect(nr_transport_addr *addr) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrSocketIpc::write(const void *msg, size_t len, size_t *written) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
+int NrSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
+  MOZ_ASSERT(false);
+  return R_INTERNAL;
+}
+
 // Main thread executors
 void NrSocketIpc::create_m(const nsACString &host, const uint16_t port) {
   ASSERT_ON_THREAD(main_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   nsresult rv;
   socket_child_ = do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
@@ -954,23 +1057,32 @@ using namespace mozilla;
 static int nr_socket_local_destroy(void **objp);
 static int nr_socket_local_sendto(void *obj,const void *msg, size_t len,
                                   int flags, nr_transport_addr *to);
 static int nr_socket_local_recvfrom(void *obj,void * restrict buf,
   size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
 static int nr_socket_local_getfd(void *obj, NR_SOCKET *fd);
 static int nr_socket_local_getaddr(void *obj, nr_transport_addr *addrp);
 static int nr_socket_local_close(void *obj);
+static int nr_socket_local_connect(void *sock, nr_transport_addr *addr);
+static int nr_socket_local_write(void *obj,const void *msg, size_t len,
+                                 size_t *written);
+static int nr_socket_local_read(void *obj,void * restrict buf, size_t maxlen,
+                                size_t *len);
 
 static nr_socket_vtbl nr_socket_local_vtbl={
+  1,
   nr_socket_local_destroy,
   nr_socket_local_sendto,
   nr_socket_local_recvfrom,
   nr_socket_local_getfd,
   nr_socket_local_getaddr,
+  nr_socket_local_connect,
+  nr_socket_local_write,
+  nr_socket_local_read,
   nr_socket_local_close
 };
 
 int nr_socket_local_create(nr_transport_addr *addr, nr_socket **sockp) {
   NrSocketBase *sock = nullptr;
 
   // create IPC bridge for content process
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
@@ -982,24 +1094,25 @@ int nr_socket_local_create(nr_transport_
   }
 
   int r, _status;
 
   r = sock->create(addr);
   if (r)
     ABORT(r);
 
-  r = nr_socket_create_int(static_cast<void *>(sock), &nr_socket_local_vtbl, sockp);
+  r = nr_socket_create_int(static_cast<void *>(sock),
+                           sock->vtbl(), sockp);
   if (r)
     ABORT(r);
 
   // Add a reference so that we can delete it in destroy()
   sock->AddRef();
 
-  _status =0;
+  _status = 0;
 
 abort:
   if (_status) {
     delete sock;
   }
   return _status;
 }
 
@@ -1050,22 +1163,46 @@ static int nr_socket_local_getaddr(void 
 static int nr_socket_local_close(void *obj) {
   NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
 
   sock->close();
 
   return 0;
 }
 
+static int nr_socket_local_write(void *obj, const void *msg, size_t len,
+                                 size_t *written) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  return sock->write(msg, len, written);
+}
+
+static int nr_socket_local_read(void *obj, void * restrict buf, size_t maxlen,
+                                size_t *len) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  return sock->read(buf, maxlen, len);
+}
+
+static int nr_socket_local_connect(void *obj, nr_transport_addr *addr) {
+  NrSocket *sock = static_cast<NrSocket *>(obj);
+
+  return sock->connect(addr);
+}
+
 // Implement async api
 int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb,void *cb_arg,
                   char *function,int line) {
   NrSocketBase *s = static_cast<NrSocketBase *>(sock);
 
   return s->async_wait(how, cb, cb_arg, function, line);
 }
 
 int NR_async_cancel(NR_SOCKET sock,int how) {
   NrSocketBase *s = static_cast<NrSocketBase *>(sock);
 
   return s->cancel(how);
 }
 
+nr_socket_vtbl* NrSocketBase::vtbl() {
+  return &nr_socket_local_vtbl;
+}
+
--- a/media/mtransport/nr_socket_prsock.h
+++ b/media/mtransport/nr_socket_prsock.h
@@ -59,56 +59,66 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #include "nsIEventTarget.h"
 #include "nsIUDPSocketChild.h"
 
 #include "databuffer.h"
 #include "m_cpp_utils.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
 
+// Stub declaration for nICEr type
+typedef struct nr_socket_vtbl_ nr_socket_vtbl;
+
 namespace mozilla {
 
 namespace net {
   union NetAddr;
 }
 
 class NrSocketBase {
 public:
-  NrSocketBase() : poll_flags_(0) {
+  NrSocketBase() : connect_invoked_(false), poll_flags_(0) {
     memset(cbs_, 0, sizeof(cbs_));
     memset(cb_args_, 0, sizeof(cb_args_));
     memset(&my_addr_, 0, sizeof(my_addr_));
   }
   virtual ~NrSocketBase() {}
 
   // the nr_socket APIs
   virtual int create(nr_transport_addr *addr) = 0;
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to) = 0;
   virtual int recvfrom(void * buf, size_t maxlen,
                        size_t *len, int flags,
                        nr_transport_addr *from) = 0;
   virtual int getaddr(nr_transport_addr *addrp) = 0;
   virtual void close() = 0;
+  virtual int connect(nr_transport_addr *addr) = 0;
+  virtual int write(const void *msg, size_t len, size_t *written) = 0;
+  virtual int read(void* buf, size_t maxlen, size_t *len) = 0;
 
    // Implementations of the async_event APIs
   virtual int async_wait(int how, NR_async_cb cb, void *cb_arg,
                          char *function, int line);
   virtual int cancel(int how);
 
   // nsISupport reference counted interface
   NS_IMETHOD_(nsrefcnt) AddRef(void) = 0;
   NS_IMETHOD_(nsrefcnt) Release(void) = 0;
 
   uint32_t poll_flags() {
     return poll_flags_;
   }
 
+  virtual nr_socket_vtbl *vtbl();  // To access in test classes.
+
 protected:
   void fire_callback(int how);
+
+  bool connect_invoked_;
   nr_transport_addr my_addr_;
 
 private:
   NR_async_cb cbs_[NR_ASYNC_WAIT_WRITE + 1];
   void *cb_args_[NR_ASYNC_WAIT_WRITE + 1];
   uint32_t poll_flags_;
 };
 
@@ -140,16 +150,19 @@ public:
   virtual int create(nr_transport_addr *addr); // (really init, but it's called create)
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to);
   virtual int recvfrom(void * buf, size_t maxlen,
                        size_t *len, int flags,
                        nr_transport_addr *from);
   virtual int getaddr(nr_transport_addr *addrp);
   virtual void close();
+  virtual int connect(nr_transport_addr *addr);
+  virtual int write(const void *msg, size_t len, size_t *written);
+  virtual int read(void* buf, size_t maxlen, size_t *len);
 
 private:
   DISALLOW_COPY_ASSIGN(NrSocket);
 
   PRFileDesc *fd_;
   nsCOMPtr<nsIEventTarget> ststhread_;
 };
 
@@ -189,16 +202,19 @@ public:
   virtual int create(nr_transport_addr *addr);
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to);
   virtual int recvfrom(void * buf, size_t maxlen,
                        size_t *len, int flags,
                        nr_transport_addr *from);
   virtual int getaddr(nr_transport_addr *addrp);
   virtual void close();
+  virtual int connect(nr_transport_addr *addr);
+  virtual int write(const void *msg, size_t len, size_t *written);
+  virtual int read(void* buf, size_t maxlen, size_t *len);
 
 private:
   DISALLOW_COPY_ASSIGN(NrSocketIpc);
 
   // Main thread executors of the NrSocketBase APIs
   void create_m(const nsACString &host, const uint16_t port);
   void sendto_m(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf);
   void close_m();
@@ -211,15 +227,17 @@ private:
 
   nsCOMPtr<nsIUDPSocketChild> socket_child_;
   nsCOMPtr<nsIEventTarget> sts_thread_;
   const nsCOMPtr<nsIEventTarget> main_thread_;
   ReentrantMonitor monitor_;
 };
 
 int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
-                                 nr_transport_addr *addr);
+                                 nr_transport_addr *addr,
+                                 int protocol);
 int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
-                                nr_transport_addr *addr, int keep);
+                                nr_transport_addr *addr,
+                                int protocol, int keep);
 int nr_transport_addr_get_addrstring_and_port(nr_transport_addr *addr,
                                               nsACString *host, int32_t *port);
 }  // close namespace
 #endif
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -178,23 +178,34 @@ static nr_ice_crypto_vtbl nr_ice_crypto_
   nr_crypto_nss_random_bytes,
   nr_crypto_nss_hmac,
   nr_crypto_nss_md5
 };
 
 
 
 
-nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server *server) const {
+nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server *server,
+                                            std::string transport) const {
   int r;
+  int transport_int;
 
   memset(server, 0, sizeof(nr_ice_stun_server));
+  if (transport == kNrIceTransportUdp) {
+    transport_int = IPPROTO_UDP;
+  } else if (transport == kNrIceTransportTcp) {
+    transport_int = IPPROTO_TCP;
+  } else {
+    MOZ_ASSERT(false);
+    return NS_ERROR_FAILURE;
+  }
 
   if (has_addr_) {
-    r = nr_praddr_to_transport_addr(&addr_, &server->u.addr, 0);
+    r = nr_praddr_to_transport_addr(&addr_, &server->u.addr,
+                                    transport_int, 0);
     if (r) {
       return NS_ERROR_FAILURE;
     }
     server->type=NR_ICE_STUN_SERVER_TYPE_ADDR;
   }
   else {
     MOZ_ASSERT(sizeof(server->u.dnsname.host) > host_.size());
     PL_strncpyz(server->u.dnsname.host, host_.c_str(),
@@ -205,20 +216,29 @@ nsresult NrIceStunServer::ToNicerStunStr
 
   return NS_OK;
 }
 
 
 nsresult NrIceTurnServer::ToNicerTurnStruct(nr_ice_turn_server *server) const {
   memset(server, 0, sizeof(nr_ice_turn_server));
 
-  nsresult rv = ToNicerStunStruct(&server->turn_server);
+  nsresult rv = ToNicerStunStruct(&server->turn_server, transport_);
   if (NS_FAILED(rv))
     return rv;
 
+  if (transport_ == kNrIceTransportUdp) {
+    server->transport = IPPROTO_UDP;
+  } else if (transport_ == kNrIceTransportTcp) {
+    server->transport = IPPROTO_TCP;
+  } else {
+    MOZ_ASSERT(false);
+    return NS_ERROR_FAILURE;
+  }
+
   if (username_.empty())
     return NS_ERROR_INVALID_ARG;
   if (password_.empty())
     return NS_ERROR_INVALID_ARG;
 
   if (!(server->username=r_strdup(username_.c_str())))
     return NS_ERROR_OUT_OF_MEMORY;
 
--- a/media/mtransport/nricectx.h
+++ b/media/mtransport/nricectx.h
@@ -97,17 +97,19 @@ class NrIceStunServer {
 
     nsresult rv = server->Init(addr, port);
     if (NS_FAILED(rv))
       return nullptr;
 
     return server.forget();
   }
 
-  nsresult ToNicerStunStruct(nr_ice_stun_server *server) const;
+  nsresult ToNicerStunStruct(nr_ice_stun_server *server,
+                             const std::string transport =
+                             kNrIceTransportUdp) const;
 
  protected:
   NrIceStunServer() : addr_() {}
 
   nsresult Init(const std::string& addr, uint16_t port) {
     PRStatus status = PR_StringToNetAddr(addr.c_str(), &addr_);
     if (status == PR_SUCCESS) {
       // Parseable as an address
@@ -134,36 +136,37 @@ class NrIceStunServer {
 };
 
 class NrIceTurnServer : public NrIceStunServer {
  public:
   static NrIceTurnServer *Create(const std::string& addr, uint16_t port,
                                  const std::string& username,
                                  const std::vector<unsigned char>& password,
                                  const std::string& transport = kNrIceTransportUdp) {
-    // TODO: Bug 906968 - Support TCP
     ScopedDeletePtr<NrIceTurnServer> server(
-        new NrIceTurnServer(username, password));
+        new NrIceTurnServer(username, password, transport));
 
     nsresult rv = server->Init(addr, port);
     if (NS_FAILED(rv))
       return nullptr;
 
     return server.forget();
   }
 
   nsresult ToNicerTurnStruct(nr_ice_turn_server *server) const;
 
  private:
   NrIceTurnServer(const std::string& username,
-                  const std::vector<unsigned char>& password) :
-      username_(username), password_(password) {}
+                  const std::vector<unsigned char>& password,
+                  const std::string& transport) :
+      username_(username), password_(password), transport_(transport) {}
 
   std::string username_;
   std::vector<unsigned char> password_;
+  std::string transport_;
 };
 
 class NrIceCtx {
  public:
   enum ConnectionState { ICE_CTX_INIT,
                          ICE_CTX_CHECKING,
                          ICE_CTX_OPEN,
                          ICE_CTX_FAILED
--- a/media/mtransport/nricemediastream.cpp
+++ b/media/mtransport/nricemediastream.cpp
@@ -68,32 +68,67 @@ extern "C" {
 // Local includes
 #include "nricectx.h"
 #include "nricemediastream.h"
 
 namespace mozilla {
 
 MOZ_MTLOG_MODULE("mtransport")
 
+static bool ToNrIceAddr(nr_transport_addr &addr,
+                        NrIceAddr *out) {
+  int r;
+  char addrstring[INET6_ADDRSTRLEN + 1];
+
+  r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring));
+  if (r)
+    return false;
+  out->host = addrstring;
+
+  int port;
+  r = nr_transport_addr_get_port(&addr, &port);
+  if (r)
+    return false;
+
+  out->port = port;
+
+  switch (addr.protocol) {
+    case IPPROTO_TCP:
+      out->transport = kNrIceTransportTcp;
+      break;
+    case IPPROTO_UDP:
+      out->transport = kNrIceTransportUdp;
+      break;
+    default:
+      MOZ_CRASH();
+      return false;
+  }
+
+  return true;
+}
+
 static bool ToNrIceCandidate(const nr_ice_candidate& candc,
                              NrIceCandidate* out) {
   MOZ_ASSERT(out);
   int r;
   // Const-cast because the internal nICEr code isn't const-correct.
   nr_ice_candidate *cand = const_cast<nr_ice_candidate *>(&candc);
-  char addr[INET6_ADDRSTRLEN + 1];
 
-  r = nr_transport_addr_get_addrstring(&cand->addr, addr, sizeof(addr));
-  if (r)
+  if (!ToNrIceAddr(cand->addr, &out->cand_addr))
     return false;
 
-  int port;
-  r = nr_transport_addr_get_port(&cand->addr, &port);
-  if (r)
-    return false;
+  if (cand->isock) {
+    nr_transport_addr addr;
+    r = nr_socket_getaddr(cand->isock->sock, &addr);
+    if (r)
+      return false;
+
+    if (!ToNrIceAddr(addr, &out->local_addr))
+      return false;
+  }
 
   NrIceCandidate::Type type;
 
   switch (cand->type) {
     case HOST:
       type = NrIceCandidate::ICE_HOST;
       break;
     case SERVER_REFLEXIVE:
@@ -104,18 +139,16 @@ static bool ToNrIceCandidate(const nr_ic
       break;
     case RELAYED:
       type = NrIceCandidate::ICE_RELAYED;
       break;
     default:
       return false;
   }
 
-  out->host = addr;
-  out->port = port;
   out->type = type;
   out->codeword = candc.codeword;
   return true;
 }
 
 // Make an NrIceCandidate from the candidate |cand|.
 // This is not a member fxn because we want to hide the
 // defn of nr_ice_candidate but we pass by reference.
--- a/media/mtransport/nricemediastream.h
+++ b/media/mtransport/nricemediastream.h
@@ -59,28 +59,34 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 
 namespace mozilla {
 
 typedef struct nr_ice_media_stream_ nr_ice_media_stream;
 
 class NrIceCtx;
 
+struct NrIceAddr {
+  std::string host;
+  uint16_t port;
+  std::string transport;
+};
+
 /* A summary of a candidate, for use in asking which candidate
    pair is active */
 struct NrIceCandidate {
   enum Type {
     ICE_HOST,
     ICE_SERVER_REFLEXIVE,
     ICE_PEER_REFLEXIVE,
     ICE_RELAYED
   };
 
-  std::string host;
-  uint16_t port;
+  NrIceAddr cand_addr;
+  NrIceAddr local_addr;
   Type type;
   std::string codeword;
 };
 
 struct NrIceCandidatePair {
 
   enum State {
     STATE_FROZEN,
--- a/media/mtransport/nriceresolver.cpp
+++ b/media/mtransport/nriceresolver.cpp
@@ -146,21 +146,26 @@ int NrIceResolver::resolve(nr_resolver_r
                            int (*cb)(void *cb_arg, nr_transport_addr *addr),
                            void *cb_arg,
                            void **handle) {
   int _status;
   MOZ_ASSERT(allocated_resolvers_ > 0);
   ASSERT_ON_THREAD(sts_thread_);
   nsCOMPtr<PendingResolution> pr;
 
-  if (resource->transport_protocol != IPPROTO_UDP) {
-    MOZ_MTLOG(ML_ERROR, "Only UDP is supported.");
+  if (resource->transport_protocol != IPPROTO_UDP &&
+      resource->transport_protocol != IPPROTO_TCP) {
+    MOZ_MTLOG(ML_ERROR, "Only UDP and TCP are is supported.");
     ABORT(R_NOT_FOUND);
   }
-  pr = new PendingResolution(sts_thread_, resource->port? resource->port : 3478,
+  pr = new PendingResolution(sts_thread_,
+                             resource->port? resource->port : 3478,
+                             resource->transport_protocol ?
+                             resource->transport_protocol :
+                             IPPROTO_UDP,
                              cb, cb_arg);
   if (NS_FAILED(dns_->AsyncResolve(nsAutoCString(resource->domain_name),
                                    nsIDNSService::RESOLVE_DISABLE_IPV6, pr,
                                    sts_thread_, getter_AddRefs(pr->request_)))) {
     MOZ_MTLOG(ML_ERROR, "AsyncResolve failed.");
     ABORT(R_NOT_FOUND);
   }
   // Because the C API offers no "finished" method to release the handle we
@@ -183,17 +188,18 @@ nsresult NrIceResolver::PendingResolutio
   // thread, but cancel() cannot guarantee this event isn't on the queue.
   if (!canceled_) {
     nr_transport_addr *cb_addr = nullptr;
     nr_transport_addr ta;
     // TODO(jib@mozilla.com): Revisit when we do TURN.
     if (NS_SUCCEEDED(status)) {
       net::NetAddr na;
       if (NS_SUCCEEDED(record->GetNextAddr(port_, &na))) {
-        MOZ_ALWAYS_TRUE (nr_netaddr_to_transport_addr(&na, &ta) == 0);
+        MOZ_ALWAYS_TRUE (nr_netaddr_to_transport_addr(&na, &ta,
+                                                      transport_) == 0);
         cb_addr = &ta;
       }
     }
     cb_(cb_arg_, cb_addr);
     Release();
   }
   return NS_OK;
 }
--- a/media/mtransport/nriceresolver.h
+++ b/media/mtransport/nriceresolver.h
@@ -81,33 +81,37 @@ class NrIceResolver
 
   int resolve(nr_resolver_resource *resource,
               int (*cb)(void *cb_arg, nr_transport_addr *addr),
               void *cb_arg, void **handle);
 
   class PendingResolution : public nsIDNSListener
   {
    public:
-    PendingResolution(nsIEventTarget *thread, uint16_t port,
+    PendingResolution(nsIEventTarget *thread,
+                      uint16_t port,
+                      int transport,
                       int (*cb)(void *cb_arg, nr_transport_addr *addr),
                       void *cb_arg) :
         thread_(thread),
         port_(port),
+        transport_(transport),
         cb_(cb), cb_arg_(cb_arg),
         canceled_ (false) {}
     virtual ~PendingResolution(){};
     NS_IMETHOD OnLookupComplete(nsICancelable *request, nsIDNSRecord *record,
                                 nsresult status);
     int cancel();
     nsCOMPtr<nsICancelable> request_;
     NS_DECL_THREADSAFE_ISUPPORTS
 
    private:
     nsCOMPtr<nsIEventTarget> thread_;
     uint16_t port_;
+    int transport_;
     int (*cb_)(void *cb_arg, nr_transport_addr *addr);
     void *cb_arg_;
     bool canceled_;
   };
 
   nr_resolver_vtbl* vtbl_;
   nsCOMPtr<nsIEventTarget> sts_thread_;
   nsCOMPtr<nsIDNSService> dns_;
--- a/media/mtransport/nriceresolverfake.cpp
+++ b/media/mtransport/nriceresolverfake.cpp
@@ -110,21 +110,24 @@ int NrIceResolverFake::resolve(void *obj
                                void **handle) {
   int r,_status;
 
   MOZ_ASSERT(obj);
   NrIceResolverFake *fake = static_cast<NrIceResolverFake *>(obj);
 
   MOZ_ASSERT(fake->allocated_resolvers_ > 0);
 
-  PendingResolution *pending = new PendingResolution(fake,
-                                                     resource->domain_name,
-                                                     resource->port ?
-                                                     resource->port : 3478,
-                                                     cb, cb_arg);
+  PendingResolution *pending =
+      new PendingResolution(fake,
+                            resource->domain_name,
+                            resource->port ? resource->port : 3478,
+                            resource->transport_protocol ?
+                            resource->transport_protocol :
+                            IPPROTO_UDP,
+                            cb, cb_arg);
 
   if ((r=NR_ASYNC_TIMER_SET(fake->delay_ms_,NrIceResolverFake::resolve_cb,
                             (void *)pending, &pending->timer_handle_))) {
     delete pending;
     ABORT(r);
   }
   *handle = pending;
 
@@ -137,17 +140,18 @@ void NrIceResolverFake::resolve_cb(NR_SO
   MOZ_ASSERT(cb_arg);
   PendingResolution *pending = static_cast<PendingResolution *>(cb_arg);
 
   const PRNetAddr *addr=pending->resolver_->Resolve(pending->hostname_);
 
   if (addr) {
     nr_transport_addr transport_addr;
 
-    int r = nr_praddr_to_transport_addr(addr, &transport_addr, 0);
+    int r = nr_praddr_to_transport_addr(addr, &transport_addr,
+                                        pending->transport_, 0);
     MOZ_ASSERT(!r);
     if (r)
       goto abort;
 
     r=nr_transport_addr_set_port(&transport_addr, pending->port_);
     MOZ_ASSERT(!r);
     if (r)
       goto abort;
--- a/media/mtransport/nriceresolverfake.h
+++ b/media/mtransport/nriceresolverfake.h
@@ -91,26 +91,29 @@ private:
     return &addrs_[hostname];
   }
 
 
   struct PendingResolution {
     PendingResolution(NrIceResolverFake *resolver,
                       const std::string& hostname,
                       uint16_t port,
+                      int transport,
                       int (*cb)(void *cb_arg, nr_transport_addr *addr),
                       void *cb_arg) :
         resolver_(resolver),
         hostname_(hostname),
         port_(port),
+        transport_(transport),
         cb_(cb), cb_arg_(cb_arg) {}
 
     NrIceResolverFake *resolver_;
     std::string hostname_;
     uint16_t port_;
+    int transport_;
     int (*cb_)(void *cb_arg, nr_transport_addr *addr);
     void *cb_arg_;
     void *timer_handle_;
   };
 
   nr_resolver_vtbl* vtbl_;
   std::map<std::string, PRNetAddr> addrs_;
   uint32_t delay_ms_;
--- a/media/mtransport/test/Makefile.in
+++ b/media/mtransport/test/Makefile.in
@@ -29,16 +29,17 @@ LOCAL_INCLUDES += \
  -I$(topsrcdir)/media/mtransport/third_party/ \
  -I$(topsrcdir)/media/mtransport/third_party/ \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/crypto \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/ice \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/net \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/stun \
  -I$(topsrcdir)/media/mtransport/third_party/nICEr/src/util \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/share \
+ -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/util/ \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/util/libekr \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/log \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/registry \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/stats \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/plugin \
  -I$(topsrcdir)/media/mtransport/third_party/nrappkit/src/event \
  $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/media/mtransport/test/buffered_stun_socket_unittest.cpp
@@ -0,0 +1,444 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include <iostream>
+
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+
+#include "mozilla/Scoped.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_socket.h"
+#include "nr_socket_buffered_stun.h"
+#include "transport_addr.h"
+#include "stun.h"
+}
+
+#include "databuffer.h"
+#include "mtransport_test_utils.h"
+
+#include "nr_socket_prsock.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+MtransportTestUtils *test_utils;
+
+static uint8_t kStunMessage[] = {
+  0x00, 0x01, 0x00, 0x08, 0x21, 0x12, 0xa4, 0x42,
+  0x9b, 0x90, 0xbe, 0x2c, 0xae, 0x1a, 0x0c, 0xa8,
+  0xa0, 0xd6, 0x8b, 0x08, 0x80, 0x28, 0x00, 0x04,
+  0xdb, 0x35, 0x5f, 0xaa
+};
+static size_t kStunMessageLen = sizeof(kStunMessage);
+
+class DummySocket : public NrSocketBase {
+ public:
+  DummySocket()
+      : writable_(UINT_MAX),
+        write_buffer_(nullptr),
+        readable_(UINT_MAX),
+        read_buffer_(nullptr),
+        cb_(nullptr),
+        cb_arg_(nullptr),
+        self_(nullptr) {}
+
+  // the nr_socket APIs
+  virtual int create(nr_transport_addr *addr) {
+    return 0;
+  }
+
+  virtual int sendto(const void *msg, size_t len,
+                     int flags, nr_transport_addr *to) {
+    MOZ_CRASH();
+    return 0;
+  }
+
+  virtual int recvfrom(void * buf, size_t maxlen,
+                       size_t *len, int flags,
+                       nr_transport_addr *from) {
+    MOZ_CRASH();
+    return 0;
+  }
+
+  virtual int getaddr(nr_transport_addr *addrp) {
+    MOZ_CRASH();
+    return 0;
+  }
+
+  virtual void close() {
+  }
+
+  virtual int connect(nr_transport_addr *addr) {
+    return 0;
+  }
+
+  virtual int write(const void *msg, size_t len, size_t *written) {
+    // Shouldn't be anything here.
+    EXPECT_EQ(nullptr, write_buffer_.get());
+
+    size_t to_write = std::min(len, writable_);
+
+    if (to_write) {
+      write_buffer_ = new DataBuffer(
+          static_cast<const uint8_t *>(msg), to_write);
+      *written = to_write;
+    }
+
+    return 0;
+  }
+
+  virtual int read(void* buf, size_t maxlen, size_t *len) {
+    if (!read_buffer_.get()) {
+      return R_WOULDBLOCK;
+    }
+
+    size_t to_read = std::min(read_buffer_->len(),
+                              std::min(maxlen, readable_));
+
+    memcpy(buf, read_buffer_->data(), to_read);
+    *len = to_read;
+
+    if (to_read < read_buffer_->len()) {
+      read_buffer_ = new DataBuffer(read_buffer_->data() + to_read,
+                                    read_buffer_->len() - to_read);
+    } else {
+      read_buffer_ = nullptr;
+    }
+
+    return 0;
+  }
+
+  // Implementations of the async_event APIs.
+  // These are no-ops because we handle scheduling manually
+  // for test purposes.
+  virtual int async_wait(int how, NR_async_cb cb, void *cb_arg,
+                         char *function, int line) {
+    EXPECT_EQ(nullptr, cb_);
+    cb_ = cb;
+    cb_arg_ = cb_arg;
+
+    return 0;
+  }
+
+  virtual int cancel(int how) {
+    cb_ = nullptr;
+    cb_arg_ = nullptr;
+
+    return 0;
+  }
+
+
+  // Read/Manipulate the current state.
+  void CheckWriteBuffer(uint8_t *data, size_t len) {
+    if (!len) {
+      EXPECT_EQ(nullptr, write_buffer_.get());
+    } else {
+      EXPECT_NE(nullptr, write_buffer_.get());
+      ASSERT_EQ(len, write_buffer_->len());
+      ASSERT_EQ(0, memcmp(data, write_buffer_->data(), len));
+    }
+  }
+
+  void ClearWriteBuffer() {
+    write_buffer_ = nullptr;
+  }
+
+  void SetWritable(size_t val) {
+    writable_ = val;
+  }
+
+  void FireWritableCb() {
+    NR_async_cb cb = cb_;
+    void *cb_arg = cb_arg_;
+
+    cb_ = nullptr;
+    cb_arg_ = nullptr;
+
+    cb(this, NR_ASYNC_WAIT_WRITE, cb_arg);
+  }
+
+  void SetReadBuffer(uint8_t *data, size_t len) {
+    EXPECT_EQ(nullptr, write_buffer_.get());
+    read_buffer_ = new DataBuffer(data, len);
+  }
+
+  void ClearReadBuffer() {
+    read_buffer_ = nullptr;
+  }
+
+  void SetReadable(size_t val) {
+    readable_ = val;
+  }
+
+  nr_socket *get_nr_socket() {
+    if (!self_) {
+      int r = nr_socket_create_int(this, vtbl(), &self_);
+      AddRef();
+      if (r)
+        return nullptr;
+    }
+
+    return self_;
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket);
+
+ private:
+  DISALLOW_COPY_ASSIGN(DummySocket);
+
+  size_t writable_;  // Amount we allow someone to write.
+  ScopedDeletePtr<DataBuffer> write_buffer_;
+  size_t readable_;   // Amount we allow someone to read.
+  ScopedDeletePtr<DataBuffer> read_buffer_;
+
+  NR_async_cb cb_;
+  void *cb_arg_;
+  nr_socket *self_;
+};
+
+class BufferedStunSocketTest : public ::testing::Test {
+ public:
+  BufferedStunSocketTest()
+      : dummy_(nullptr),
+        test_socket_(nullptr) {}
+
+  ~BufferedStunSocketTest() {
+    nr_socket_destroy(&test_socket_);
+  }
+
+  void SetUp() {
+    ScopedDeletePtr<DummySocket> dummy(new DummySocket());
+
+    int r = nr_socket_buffered_stun_create(
+        dummy->get_nr_socket(),
+        kStunMessageLen,
+        &test_socket_);
+    ASSERT_EQ(0, r);
+    dummy_ = dummy.forget();  // Now owned by test_socket_.
+
+    r = nr_ip4_str_port_to_transport_addr(
+        (char *)"192.0.2.133", 3333, IPPROTO_TCP, &remote_addr_);
+    ASSERT_EQ(0, r);
+
+    r = nr_socket_connect(test_socket_,
+                          &remote_addr_);
+    ASSERT_EQ(0, r);
+  }
+
+  nr_socket *socket() { return test_socket_; }
+
+ protected:
+  DummySocket *dummy_;
+  nr_socket *test_socket_;
+  nr_transport_addr remote_addr_;
+};
+
+
+TEST_F(BufferedStunSocketTest, TestCreate) {
+}
+
+TEST_F(BufferedStunSocketTest, TestSendTo) {
+  int r = nr_socket_sendto(test_socket_,
+                           kStunMessage,
+                           kStunMessageLen,
+                           0, &remote_addr_);
+  ASSERT_EQ(0, r);
+
+  dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToBuffered) {
+  dummy_->SetWritable(0);
+
+  int r = nr_socket_sendto(test_socket_,
+                           kStunMessage,
+                           kStunMessageLen,
+                           0, &remote_addr_);
+  ASSERT_EQ(0, r);
+
+  dummy_->CheckWriteBuffer(nullptr, 0);
+
+  dummy_->SetWritable(kStunMessageLen);
+  dummy_->FireWritableCb();
+  dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToPartialBuffered) {
+  dummy_->SetWritable(10);
+
+  int r = nr_socket_sendto(test_socket_,
+                           kStunMessage,
+                           kStunMessageLen,
+                           0, &remote_addr_);
+  ASSERT_EQ(0, r);
+
+  dummy_->CheckWriteBuffer(kStunMessage, 10);
+  dummy_->ClearWriteBuffer();
+
+  dummy_->SetWritable(kStunMessageLen);
+  dummy_->FireWritableCb();
+  dummy_->CheckWriteBuffer(kStunMessage + 10, kStunMessageLen - 10);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToReject) {
+  dummy_->SetWritable(0);
+
+  int r = nr_socket_sendto(test_socket_,
+                           kStunMessage,
+                           kStunMessageLen,
+                           0, &remote_addr_);
+  ASSERT_EQ(0, r);
+
+  dummy_->CheckWriteBuffer(nullptr, 0);
+
+  r = nr_socket_sendto(test_socket_,
+                       kStunMessage,
+                       kStunMessageLen,
+                       0, &remote_addr_);
+  ASSERT_EQ(R_WOULDBLOCK, r);
+
+  dummy_->CheckWriteBuffer(nullptr, 0);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToWrongAddr) {
+  nr_transport_addr addr;
+
+  int r = nr_ip4_str_port_to_transport_addr(
+      (char *)"192.0.2.134", 3333, IPPROTO_TCP, &addr);
+  ASSERT_EQ(0, r);
+
+  r = nr_socket_sendto(test_socket_,
+                       kStunMessage,
+                       kStunMessageLen,
+                       0, &addr);
+  ASSERT_EQ(R_BAD_DATA, r);
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFrom) {
+  dummy_->SetReadBuffer(kStunMessage, kStunMessageLen);
+
+  unsigned char tmp[2048];
+  size_t len;
+  nr_transport_addr addr;
+
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, sizeof(tmp), &len, 0,
+                             &addr);
+  ASSERT_EQ(0, r);
+  ASSERT_EQ(kStunMessageLen, len);
+  ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen));
+  ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_,
+                                     NR_TRANSPORT_ADDR_CMP_MODE_ALL));
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromPartial) {
+  dummy_->SetReadBuffer(kStunMessage, 15);
+
+  unsigned char tmp[2048];
+  size_t len;
+  nr_transport_addr addr;
+
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, sizeof(tmp), &len, 0,
+                             &addr);
+  ASSERT_EQ(R_WOULDBLOCK, r);
+
+
+  dummy_->SetReadBuffer(kStunMessage + 15, kStunMessageLen - 15);
+
+  r = nr_socket_recvfrom(test_socket_,
+                         tmp, sizeof(tmp), &len, 0,
+                         &addr);
+  ASSERT_EQ(0, r);
+  ASSERT_EQ(kStunMessageLen, len);
+  ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen));
+  ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_,
+                                     NR_TRANSPORT_ADDR_CMP_MODE_ALL));
+
+  r = nr_socket_recvfrom(test_socket_,
+                         tmp, sizeof(tmp), &len, 0,
+                         &addr);
+  ASSERT_EQ(R_WOULDBLOCK, r);
+}
+
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromGarbage) {
+  uint8_t garbage[50];
+  memset(garbage, 0xff, sizeof(garbage));
+
+  dummy_->SetReadBuffer(garbage, sizeof(garbage));
+
+  unsigned char tmp[2048];
+  size_t len;
+  nr_transport_addr addr;
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, sizeof(tmp), &len, 0,
+                             &addr);
+  ASSERT_EQ(R_BAD_DATA, r);
+
+  r = nr_socket_recvfrom(test_socket_,
+                         tmp, sizeof(tmp), &len, 0,
+                         &addr);
+  ASSERT_EQ(R_FAILED, r);
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromTooShort) {
+  dummy_->SetReadBuffer(kStunMessage, kStunMessageLen);
+
+  unsigned char tmp[2048];
+  size_t len;
+  nr_transport_addr addr;
+
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, kStunMessageLen - 1, &len, 0,
+                             &addr);
+  ASSERT_EQ(R_BAD_ARGS, r);
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromReallyLong) {
+  uint8_t garbage[4096];
+  memset(garbage, 0xff, sizeof(garbage));
+  memcpy(garbage, kStunMessage, kStunMessageLen);
+  nr_stun_message_header *hdr = reinterpret_cast<nr_stun_message_header *>
+      (garbage);
+  hdr->length = htons(3000);
+
+  dummy_->SetReadBuffer(garbage, sizeof(garbage));
+
+  unsigned char tmp[4096];
+  size_t len;
+  nr_transport_addr addr;
+
+  int r = nr_socket_recvfrom(test_socket_,
+                             tmp, kStunMessageLen - 1, &len, 0,
+                             &addr);
+  ASSERT_EQ(R_BAD_DATA, r);
+}
+
+
+
+int main(int argc, char **argv)
+{
+  test_utils = new MtransportTestUtils();
+  NSS_NoDB_Init(nullptr);
+  NSS_SetDomesticPolicy();
+
+  // Start the tests
+  ::testing::InitGoogleTest(&argc, argv);
+
+  int rv = RUN_ALL_TESTS();
+
+  delete test_utils;
+  return rv;
+}
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -82,34 +82,31 @@ bool ContainsSucceededPair(const std::ve
   }
   return false;
 }
 
 // Note: Does not correspond to any notion of prioritization; this is just
 // so we can use stl containers/algorithms that need a comparator
 bool operator<(const NrIceCandidate& lhs,
                const NrIceCandidate& rhs) {
-  if (lhs.host == rhs.host) {
-    if (lhs.port == rhs.port) {
-      if (lhs.type == rhs.type) {
-        return lhs.codeword < rhs.codeword;
+  if (lhs.cand_addr.host == rhs.cand_addr.host) {
+    if (lhs.cand_addr.port == rhs.cand_addr.port) {
+      if (lhs.cand_addr.transport == rhs.cand_addr.transport) {
+        return lhs.type < rhs.type;
       }
-      return lhs.type < rhs.type;
+      return lhs.cand_addr.transport < rhs.cand_addr.transport;
     }
-    return lhs.port < rhs.port;
+    return lhs.cand_addr.port < rhs.cand_addr.port;
   }
-  return lhs.host < rhs.host;
+  return lhs.cand_addr.host < rhs.cand_addr.host;
 }
 
 bool operator==(const NrIceCandidate& lhs,
                 const NrIceCandidate& rhs) {
-  return lhs.host == rhs.host &&
-         lhs.port == rhs.port &&
-         lhs.type == rhs.type &&
-         lhs.codeword == rhs.codeword;
+  return !((lhs < rhs) || (rhs < lhs));
 }
 
 class IceCandidatePairCompare {
   public:
     bool operator()(const NrIceCandidatePair& lhs,
                     const NrIceCandidatePair& rhs) const {
       if (lhs.priority == rhs.priority) {
         if (lhs.local == rhs.local) {
@@ -137,16 +134,17 @@ class IceTestPeer : public sigslot::has_
       ice_complete_(false),
       received_(0),
       sent_(0),
       fake_resolver_(),
       dns_resolver_(new NrIceResolver()),
       remote_(nullptr),
       candidate_filter_(nullptr),
       expected_local_type_(NrIceCandidate::ICE_HOST),
+      expected_local_transport_(kNrIceTransportUdp),
       expected_remote_type_(NrIceCandidate::ICE_HOST),
       trickle_mode_(TRICKLE_NONE),
       trickled_(0) {
     ice_ctx_->SignalGatheringStateChange.connect(
         this,
         &IceTestPeer::GatheringStateChange);
     ice_ctx_->SignalConnectionStateChange.connect(
         this,
@@ -184,32 +182,38 @@ class IceTestPeer : public sigslot::has_
     ScopedDeletePtr<NrIceStunServer> server(NrIceStunServer::Create(addr,
                                                                     port));
     stun_servers.push_back(*server);
     ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers)));
   }
 
   void SetTurnServer(const std::string addr, uint16_t port,
                      const std::string username,
-                     const std::string password) {
+                     const std::string password,
+                     const std::string transport) {
     std::vector<unsigned char> password_vec(password.begin(), password.end());
-    SetTurnServer(addr, port, username, password_vec);
+    SetTurnServer(addr, port, username, password_vec, transport);
   }
 
 
   void SetTurnServer(const std::string addr, uint16_t port,
                      const std::string username,
-                     const std::vector<unsigned char> password) {
+                     const std::vector<unsigned char> password,
+                     const std::string transport) {
     std::vector<NrIceTurnServer> turn_servers;
     ScopedDeletePtr<NrIceTurnServer> server(NrIceTurnServer::Create(
-        addr, port, username, password));
+        addr, port, username, password, transport));
     turn_servers.push_back(*server);
     ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetTurnServers(turn_servers)));
   }
 
+  void SetTurnServers(const std::vector<NrIceTurnServer> servers) {
+    ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetTurnServers(servers)));
+  }
+
   void SetFakeResolver() {
     ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init()));
     PRNetAddr addr;
     PRStatus status = PR_StringToNetAddr(g_stun_server_address.c_str(),
                                           &addr);
     addr.inet.port = kDefaultStunServerPort;
     ASSERT_EQ(PR_SUCCESS, status);
     fake_resolver_.SetAddr(g_stun_server_hostname, addr);
@@ -265,18 +269,20 @@ class IceTestPeer : public sigslot::has_
         candidates.push_back(candidates_in[i]);
       }
     }
 
     return candidates;
   }
 
   void SetExpectedTypes(NrIceCandidate::Type local,
-                        NrIceCandidate::Type remote) {
+                        NrIceCandidate::Type remote,
+                        std::string local_transport = kNrIceTransportUdp) {
     expected_local_type_ = local;
+    expected_local_transport_ = local_transport;
     expected_remote_type_ = remote;
   }
 
   bool gathering_complete() { return gathering_complete_; }
   int ready_ct() { return ready_ct_; }
   bool is_ready(size_t stream) {
     return streams_[stream]->state() == NrIceMediaStream::ICE_OPEN;
   }
@@ -363,58 +369,68 @@ class IceTestPeer : public sigslot::has_
       case NrIceCandidate::ICE_SERVER_REFLEXIVE:
         type = "srflx";
         break;
       case NrIceCandidate::ICE_PEER_REFLEXIVE:
         type = "prflx";
         break;
       case NrIceCandidate::ICE_RELAYED:
         type = "relay";
+        if (which.find("Local") != std::string::npos) {
+          type += "(" + cand.local_addr.transport + ")";
+        }
         break;
       default:
         FAIL();
     };
 
     std::cerr << which
               << " --> "
               << type
               << " "
-              << cand.host
+              << cand.local_addr.host
               << ":"
-              << cand.port
+              << cand.local_addr.port
               << " codeword="
               << cand.codeword
               << std::endl;
   }
 
-  void DumpAndCheckActiveCandidates() {
+  void DumpAndCheckActiveCandidates_s() {
     std::cerr << "Active candidates:" << std::endl;
     for (size_t i=0; i < streams_.size(); ++i) {
       for (int j=0; j < streams_[i]->components(); ++j) {
         std::cerr << "Stream " << i << " component " << j+1 << std::endl;
 
         NrIceCandidate *local;
         NrIceCandidate *remote;
 
         nsresult res = streams_[i]->GetActivePair(j+1, &local, &remote);
         if (res == NS_ERROR_NOT_AVAILABLE) {
           std::cerr << "Component unpaired or disabled." << std::endl;
         } else {
           ASSERT_TRUE(NS_SUCCEEDED(res));
           DumpCandidate("Local  ", *local);
           ASSERT_EQ(expected_local_type_, local->type);
+          ASSERT_EQ(expected_local_transport_, local->local_addr.transport);
           DumpCandidate("Remote ", *remote);
           ASSERT_EQ(expected_remote_type_, remote->type);
           delete local;
           delete remote;
         }
       }
     }
   }
 
+  void DumpAndCheckActiveCandidates() {
+    test_utils->sts_target()->Dispatch(
+      WrapRunnable(this, &IceTestPeer::DumpAndCheckActiveCandidates_s),
+      NS_DISPATCH_SYNC);
+  }
+
   void Close() {
     test_utils->sts_target()->Dispatch(
       WrapRunnable(ice_ctx_, &NrIceCtx::destroy_peer_ctx),
       NS_DISPATCH_SYNC);
   }
 
   void Shutdown() {
     ice_ctx_ = nullptr;
@@ -663,16 +679,17 @@ class IceTestPeer : public sigslot::has_
   bool ice_complete_;
   size_t received_;
   size_t sent_;
   NrIceResolverFake fake_resolver_;
   nsRefPtr<NrIceResolver> dns_resolver_;
   IceTestPeer *remote_;
   CandidateFilter candidate_filter_;
   NrIceCandidate::Type expected_local_type_;
+  std::string expected_local_transport_;
   NrIceCandidate::Type expected_remote_type_;
   TrickleMode trickle_mode_;
   int trickled_;
 };
 
 class IceGatherTest : public ::testing::Test {
  public:
   void SetUp() {
@@ -759,19 +776,25 @@ class IceConnectTest : public ::testing:
       if (!p2_->gathering_complete())
         return false;
     }
     return true;
   }
 
   void SetTurnServer(const std::string addr, uint16_t port,
                      const std::string username,
-                     const std::string password) {
-    p1_->SetTurnServer(addr, port, username, password);
-    p2_->SetTurnServer(addr, port, username, password);
+                     const std::string password,
+                     const std::string transport = kNrIceTransportUdp) {
+    p1_->SetTurnServer(addr, port, username, password, transport);
+    p2_->SetTurnServer(addr, port, username, password, transport);
+  }
+
+  void SetTurnServers(const std::vector<NrIceTurnServer>& servers) {
+    p1_->SetTurnServers(servers);
+    p2_->SetTurnServers(servers);
   }
 
   void SetCandidateFilter(CandidateFilter filter, bool both=true) {
     p1_->SetCandidateFilter(filter);
     if (both) {
       p2_->SetCandidateFilter(filter);
     }
   }
@@ -782,19 +805,20 @@ class IceConnectTest : public ::testing:
 
     ASSERT_TRUE_WAIT(p1_->ready_ct() == 1 && p2_->ready_ct() == 1, 5000);
     ASSERT_TRUE_WAIT(p1_->ice_complete() && p2_->ice_complete(), 5000);
 
     p1_->DumpAndCheckActiveCandidates();
     p2_->DumpAndCheckActiveCandidates();
   }
 
-  void SetExpectedTypes(NrIceCandidate::Type local, NrIceCandidate::Type remote) {
-    p1_->SetExpectedTypes(local, remote);
-    p2_->SetExpectedTypes(local, remote);
+  void SetExpectedTypes(NrIceCandidate::Type local, NrIceCandidate::Type remote,
+                        std::string transport = kNrIceTransportUdp) {
+    p1_->SetExpectedTypes(local, remote, transport);
+    p2_->SetExpectedTypes(local, remote, transport);
   }
 
   void SetExpectedTypes(NrIceCandidate::Type local1, NrIceCandidate::Type remote1,
                         NrIceCandidate::Type local2, NrIceCandidate::Type remote2) {
     p1_->SetExpectedTypes(local1, remote1);
     p2_->SetExpectedTypes(local2, remote2);
   }
 
@@ -1011,17 +1035,25 @@ TEST_F(IceGatherTest, TestGatherDNSStunB
   peer_->SetDNSResolver();
   Gather();
 }
 
 TEST_F(IceGatherTest, TestGatherTurn) {
   if (g_turn_server.empty())
     return;
   peer_->SetTurnServer(g_turn_server, kDefaultStunServerPort,
-                       g_turn_user, g_turn_password);
+                       g_turn_user, g_turn_password, kNrIceTransportUdp);
+  Gather();
+}
+
+TEST_F(IceGatherTest, TestGatherTurnTcp) {
+  if (g_turn_server.empty())
+    return;
+  peer_->SetTurnServer(g_turn_server, kDefaultStunServerPort,
+                       g_turn_user, g_turn_password, kNrIceTransportTcp);
   Gather();
 }
 
 TEST_F(IceGatherTest, TestGatherDisableComponent) {
   peer_->SetStunServer(kDefaultStunServerHostname, kDefaultStunServerPort);
   peer_->AddStream(2);
   peer_->DisableComponent(1, 2);
   Gather();
@@ -1193,45 +1225,112 @@ TEST_F(IceConnectTest, TestConnectTurn) 
 
   AddStream("first", 1);
   SetTurnServer(g_turn_server, kDefaultStunServerPort,
                 g_turn_user, g_turn_password);
   ASSERT_TRUE(Gather(true));
   Connect();
 }
 
+TEST_F(IceConnectTest, TestConnectTurnTcp) {
+  if (g_turn_server.empty())
+    return;
+
+  AddStream("first", 1);
+  SetTurnServer(g_turn_server, kDefaultStunServerPort,
+                g_turn_user, g_turn_password, kNrIceTransportTcp);
+  ASSERT_TRUE(Gather(true));
+  Connect();
+}
+
 TEST_F(IceConnectTest, TestConnectTurnOnly) {
   if (g_turn_server.empty())
     return;
 
   AddStream("first", 1);
   SetTurnServer(g_turn_server, kDefaultStunServerPort,
                 g_turn_user, g_turn_password);
   ASSERT_TRUE(Gather(true));
   SetCandidateFilter(IsRelayCandidate);
   SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
                    NrIceCandidate::Type::ICE_RELAYED);
   Connect();
 }
 
+TEST_F(IceConnectTest, TestConnectTurnTcpOnly) {
+  if (g_turn_server.empty())
+    return;
+
+  AddStream("first", 1);
+  SetTurnServer(g_turn_server, kDefaultStunServerPort,
+                g_turn_user, g_turn_password, kNrIceTransportTcp);
+  ASSERT_TRUE(Gather(true));
+  SetCandidateFilter(IsRelayCandidate);
+  SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+                   NrIceCandidate::Type::ICE_RELAYED,
+                   kNrIceTransportTcp);
+  Connect();
+}
+
 TEST_F(IceConnectTest, TestSendReceiveTurnOnly) {
   if (g_turn_server.empty())
     return;
 
   AddStream("first", 1);
   SetTurnServer(g_turn_server, kDefaultStunServerPort,
                 g_turn_user, g_turn_password);
   ASSERT_TRUE(Gather(true));
   SetCandidateFilter(IsRelayCandidate);
   SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
                    NrIceCandidate::Type::ICE_RELAYED);
   Connect();
   SendReceive();
 }
 
+TEST_F(IceConnectTest, TestSendReceiveTurnTcpOnly) {
+  if (g_turn_server.empty())
+    return;
+
+  AddStream("first", 1);
+  SetTurnServer(g_turn_server, kDefaultStunServerPort,
+                g_turn_user, g_turn_password, kNrIceTransportTcp);
+  ASSERT_TRUE(Gather(true));
+  SetCandidateFilter(IsRelayCandidate);
+  SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+                   NrIceCandidate::Type::ICE_RELAYED,
+                   kNrIceTransportTcp);
+  Connect();
+  SendReceive();
+}
+
+TEST_F(IceConnectTest, TestSendReceiveTurnBothOnly) {
+  if (g_turn_server.empty())
+    return;
+
+  AddStream("first", 1);
+  std::vector<NrIceTurnServer> turn_servers;
+  std::vector<unsigned char> password_vec(g_turn_password.begin(),
+                                          g_turn_password.end());
+  turn_servers.push_back(*NrIceTurnServer::Create(
+                           g_turn_server, kDefaultStunServerPort,
+                           g_turn_user, password_vec, kNrIceTransportTcp));
+  turn_servers.push_back(*NrIceTurnServer::Create(
+                           g_turn_server, kDefaultStunServerPort,
+                           g_turn_user, password_vec, kNrIceTransportUdp));
+  SetTurnServers(turn_servers);
+  ASSERT_TRUE(Gather(true));
+  SetCandidateFilter(IsRelayCandidate);
+  // UDP is preferred.
+  SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+                   NrIceCandidate::Type::ICE_RELAYED,
+                   kNrIceTransportUdp);
+  Connect();
+  SendReceive();
+}
+
 TEST_F(IceConnectTest, TestConnectShutdownOneSide) {
   AddStream("first", 1);
   ASSERT_TRUE(Gather(true));
   ConnectThenDelete();
 }
 
 TEST_F(IceConnectTest, TestPollCandPairsBeforeConnect) {
   AddStream("first", 1);
--- a/media/mtransport/test/moz.build
+++ b/media/mtransport/test/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     CPP_UNIT_TESTS += [
+        'buffered_stun_socket_unittest.cpp',
         'ice_unittest.cpp',
         'nrappkit_unittest.cpp',
         'rlogringbuffer_unittest.cpp',
         'runnable_utils_unittest.cpp',
         'simpletokenbucket_unittest.cpp',
         'sockettransportservice_unittest.cpp',
         'TestSyncRunnable.cpp',
         'transport_unittests.cpp',
--- a/media/mtransport/test/mtransport_test_utils.h
+++ b/media/mtransport/test/mtransport_test_utils.h
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Original author: ekr@rtfm.com
 
 #ifndef mtransport_test_utils_h__
 #define mtransport_test_utils_h__
 
+#include <iostream>
+
 #include "nspr.h"
 #include "nsCOMPtr.h"
 #include "nsNetCID.h"
 #include "nsXPCOMGlue.h"
 #include "nsXPCOM.h"
 
 #include "nsIComponentManager.h"
 #include "nsIComponentRegistrar.h"
--- a/media/mtransport/test/stunserver.cpp
+++ b/media/mtransport/test/stunserver.cpp
@@ -149,21 +149,25 @@ static int nr_socket_wrapped_close(void 
 
 static int nr_socket_wrapped_set_send_addr(nr_socket *sock, nr_transport_addr *addr) {
   nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(sock->obj);
 
   return nr_transport_addr_copy(&wrapped->addr_, addr);
 }
 
 static nr_socket_vtbl nr_socket_wrapped_vtbl = {
+  1,
   nr_socket_wrapped_destroy,
   nr_socket_wrapped_sendto,
   nr_socket_wrapped_recvfrom,
   nr_socket_wrapped_getfd,
   nr_socket_wrapped_getaddr,
+  0,
+  0,
+  0,
   nr_socket_wrapped_close
 };
 
 int nr_socket_wrapped_create(nr_socket *inner, nr_socket **outp) {
   ScopedDeletePtr<nr_socket_wrapped> wrapped(new nr_socket_wrapped());
 
   wrapped->sock_ = inner;
 
--- a/media/mtransport/test/turn_unittest.cpp
+++ b/media/mtransport/test/turn_unittest.cpp
@@ -66,16 +66,17 @@ extern "C" {
 #include "registry.h"
 #include "async_timer.h"
 #include "r_crc32.h"
 #include "ice_util.h"
 #include "transport_addr.h"
 #include "nr_crypto.h"
 #include "nr_socket.h"
 #include "nr_socket_local.h"
+#include "nr_socket_buffered_stun.h"
 #include "stun_client_ctx.h"
 #include "turn_client_ctx.h"
 }
 
 #include "nricemediastream.h"
 #include "nricectx.h"
 
 
@@ -88,72 +89,91 @@ std::string g_turn_user;
 std::string g_turn_password;
 
 std::string kDummyTurnServer("192.0.2.1");  // From RFC 5737
 
 class TurnClient : public ::testing::Test {
  public:
   TurnClient()
       : turn_server_(g_turn_server),
+        real_socket_(nullptr),
         net_socket_(nullptr),
+        buffered_socket_(nullptr),
+        net_fd_(nullptr),
         turn_ctx_(nullptr),
         allocated_(false),
-        received_(0) {}
+        received_(0),
+        protocol_(IPPROTO_UDP) {
+  }
 
   ~TurnClient() {
   }
 
+  void SetTcp() {
+    protocol_ = IPPROTO_TCP;
+  }
+
   void Init_s() {
     int r;
+    nr_transport_addr addr;
+    r = nr_ip4_port_to_transport_addr(0, 0, protocol_, &addr);
+    ASSERT_EQ(0, r);
 
-    nr_transport_addr addr;
-    r = nr_ip4_port_to_transport_addr(0, 0, IPPROTO_UDP, &addr);
+    r = nr_socket_local_create(&addr, &real_socket_);
     ASSERT_EQ(0, r);
 
-    r = nr_socket_local_create(&addr, &net_socket_);
-    ASSERT_EQ(0, r);
+    if (protocol_ == IPPROTO_TCP) {
+      int r =
+          nr_socket_buffered_stun_create(real_socket_, 100000,
+                                         &buffered_socket_);
+      ASSERT_EQ(0, r);
+      net_socket_ = buffered_socket_;
+    } else {
+      net_socket_ = real_socket_;
+    }
 
     r = nr_ip4_str_port_to_transport_addr(turn_server_.c_str(), 3478,
-      IPPROTO_UDP, &addr);
+      protocol_, &addr);
     ASSERT_EQ(0, r);
 
-
     std::vector<unsigned char> password_vec(
         g_turn_password.begin(), g_turn_password.end());
     Data password;
     INIT_DATA(password, &password_vec[0], password_vec.size());
     r = nr_turn_client_ctx_create("test", net_socket_,
                                   g_turn_user.c_str(),
                                   &password,
                                   &addr, &turn_ctx_);
     ASSERT_EQ(0, r);
 
     r = nr_socket_getfd(net_socket_, &net_fd_);
     ASSERT_EQ(0, r);
 
     NR_ASYNC_WAIT(net_fd_, NR_ASYNC_WAIT_READ, socket_readable_cb,
-                  (void *)this);
+        (void *)this);
   }
 
   void TearDown_s() {
     nr_turn_client_ctx_destroy(&turn_ctx_);
-    if (net_socket_) {
+    if (net_fd_) {
       NR_ASYNC_CANCEL(net_fd_, NR_ASYNC_WAIT_READ);
     }
-    nr_socket_destroy(&net_socket_);
+
+    nr_socket_destroy(&buffered_socket_);
   }
 
   void TearDown() {
     RUN_ON_THREAD(test_utils->sts_target(),
                   WrapRunnable(this, &TurnClient::TearDown_s),
                   NS_DISPATCH_SYNC);
   }
 
   void Allocate_s() {
     Init_s();
+    ASSERT_TRUE(turn_ctx_);
 
     int r = nr_turn_client_allocate(turn_ctx_,
                                     allocate_success_cb,
                                     this);
     ASSERT_EQ(0, r);
   }
 
   void Allocate(bool expect_success=true) {
@@ -288,26 +308,33 @@ class TurnClient : public ::testing::Tes
   }
 
   static void allocate_success_cb(NR_SOCKET s, int how, void *arg){
     static_cast<TurnClient *>(arg)->Allocated();
   }
 
  protected:
   std::string turn_server_;
+  nr_socket *real_socket_;
   nr_socket *net_socket_;
+  nr_socket *buffered_socket_;
   NR_SOCKET net_fd_;
   nr_turn_client_ctx *turn_ctx_;
   std::string relay_addr_;
   bool allocated_;
   int received_;
+  int protocol_;
 };
 
+TEST_F(TurnClient, Allocate) {
+  Allocate();
+}
 
-TEST_F(TurnClient, Allocate) {
+TEST_F(TurnClient, AllocateTcp) {
+  SetTcp();
   Allocate();
 }
 
 TEST_F(TurnClient, AllocateAndHold) {
   Allocate();
   PR_Sleep(20000);
 }
 
@@ -316,16 +343,28 @@ TEST_F(TurnClient, SendToSelf) {
   SendTo(relay_addr_);
   ASSERT_TRUE_WAIT(received() == 100, 1000);
   PR_Sleep(10000); // Wait 10 seconds to make sure the
                    // CreatePermission has time to complete/fail.
   SendTo(relay_addr_);
   ASSERT_TRUE_WAIT(received() == 200, 1000);
 }
 
+
+TEST_F(TurnClient, SendToSelfTcp) {
+  SetTcp();
+  Allocate();
+  SendTo(relay_addr_);
+  ASSERT_TRUE_WAIT(received() == 100, 1000);
+  PR_Sleep(10000); // Wait 10 seconds to make sure the
+                   // CreatePermission has time to complete/fail.
+  SendTo(relay_addr_);
+  ASSERT_TRUE_WAIT(received() == 200, 1000);
+}
+
 TEST_F(TurnClient, AllocateDummyServer) {
   turn_server_ = kDummyTurnServer;
   Allocate(false);
 }
 
 static std::string get_environment(const char *name) {
   char *value = getenv(name);
 
--- a/media/mtransport/third_party/nICEr/nicer.gyp
+++ b/media/mtransport/third_party/nICEr/nicer.gyp
@@ -82,16 +82,18 @@
                 "./src/net/nr_interface_prioritizer.c",
                 "./src/net/nr_interface_prioritizer.h",
 
                 # STUN
                 "./src/stun/addrs.c",
                 "./src/stun/addrs.h",
                 "./src/stun/nr_socket_turn.c",
                 "./src/stun/nr_socket_turn.h",
+                "./src/stun/nr_socket_buffered_stun.c",
+                "./src/stun/nr_socket_buffered_stun.h",
                 "./src/stun/stun.h",
                 "./src/stun/stun_build.c",
                 "./src/stun/stun_build.h",
                 "./src/stun/stun_client_ctx.c",
                 "./src/stun/stun_client_ctx.h",
                 "./src/stun/stun_codec.c",
                 "./src/stun/stun_codec.h",
                 "./src/stun/stun_hint.c",
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
@@ -437,16 +437,17 @@ static void nr_ice_candidate_fire_ready_
     cand->ready_cb_timer = 0;
     cand->ready_cb(0, 0, cand->ready_cb_arg);
   }
 
 int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg)
   {
     int r,_status;
     int protocol=NR_RESOLVE_PROTOCOL_STUN;
+    int transport=IPPROTO_UDP;
     cand->done_cb=ready_cb;
     cand->cb_arg=cb_arg;
 
     switch(cand->type){
       case HOST:
         if(r=nr_socket_getaddr(cand->isock->sock,&cand->addr))
           ABORT(r);
         cand->osock=cand->isock->sock;
@@ -454,16 +455,17 @@ int nr_ice_candidate_initialize(nr_ice_c
         // Post this so that it doesn't happen in-line
         cand->ready_cb = ready_cb;
         cand->ready_cb_arg = cb_arg;
         NR_ASYNC_TIMER_SET(0, nr_ice_candidate_fire_ready_cb, (void *)cand, &cand->ready_cb_timer);
         break;
 #ifdef USE_TURN
       case RELAYED:
         protocol=NR_RESOLVE_PROTOCOL_TURN;
+        transport=cand->u.relayed.server->transport;
         /* Fall through */
 #endif
       case SERVER_REFLEXIVE:
         cand->state=NR_ICE_CAND_STATE_INITIALIZING;
 
         if(cand->stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR) {
           /* Just copy the address */
           if (r=nr_transport_addr_copy(&cand->stun_server_addr,
@@ -475,17 +477,17 @@ int nr_ice_candidate_initialize(nr_ice_c
           if(r=nr_ice_candidate_initialize2(cand))
             ABORT(r);
         }
         else {
           nr_resolver_resource resource;
           resource.domain_name=cand->stun_server->u.dnsname.host;
           resource.port=cand->stun_server->u.dnsname.port;
           resource.stun_turn=protocol;
-          resource.transport_protocol=IPPROTO_UDP;  /* We don't support TCP yet */
+          resource.transport_protocol=transport;
 
           /* Try to resolve */
           if(!cand->ctx->resolver) {
             r_log(LOG_ICE, LOG_ERR, "ICE-CANDIDATE(%s): Can't use DNS names without a resolver", cand->label);
             ABORT(R_BAD_ARGS);
           }
 
           if(r=nr_resolver_resolve(cand->ctx->resolver,
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -39,16 +39,17 @@ static char *RCSSTRING __UNUSED__="$Id: 
 #include <nr_api.h>
 #include <registry.h>
 #include <async_timer.h>
 #include "ice_ctx.h"
 #include "ice_codeword.h"
 #include "stun.h"
 #include "nr_socket_local.h"
 #include "nr_socket_turn.h"
+#include "nr_socket_buffered_stun.h"
 #include "ice_reg.h"
 
 static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error);
 static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp);
 
 /* This function takes ownership of the contents of req (but not req itself) */
 static int nr_ice_pre_answer_request_create(nr_socket *sock, nr_stun_server_request *req, nr_ice_pre_answer_request **parp)
   {
@@ -165,62 +166,46 @@ int nr_ice_component_destroy(nr_ice_comp
     if(component->keepalive_timer)
       NR_async_timer_cancel(component->keepalive_timer);
     nr_stun_client_ctx_destroy(&component->keepalive_ctx);
 
     RFREE(component);
     return(0);
   }
 
-/* Make all the candidates we can make at the beginning */
-int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component)
+static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd)
   {
-    int r,_status;
-    nr_local_addr *addrs=ctx->local_addrs;
     nr_socket *sock;
     nr_ice_socket *isock=0;
     nr_ice_candidate *cand=0;
-    char *lufrag;
-    char *lpwd;
-    Data pwd;
-    int addr_ct=ctx->local_addr_ct;
     int i;
     int j;
     char label[256];
-
-    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/COMP(%d): initializing component",ctx->label,component->component_id);
-
-    if(addr_ct==0){
-      r_log(LOG_ICE,LOG_ERR,"ICE(%s)/COMP(%d): no local addresses available",ctx->label, component->component_id);
-      ABORT(R_NOT_FOUND);
-    }
+    int r,_status;
 
     /* Now one ice_socket for each address */
     for(i=0;i<addr_ct;i++){
       char suppress;
 
       if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
         if(r!=R_NOT_FOUND)
           ABORT(r);
       }
       else{
         if(suppress)
           continue;
       }
-
-
       r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): host address %s",ctx->label,addrs[i].addr.as_string);
       if(r=nr_socket_local_create(&addrs[i].addr,&sock)){
         r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create socket for address %s",ctx->label,addrs[i].addr.as_string);
         continue;
       }
 
       if(r=nr_ice_socket_create(ctx,component,sock,&isock))
         ABORT(r);
-
       /* Create one host candidate */
       if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0,
         component->component_id,&cand))
         ABORT(r);
 
       TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
       component->candidate_ct++;
       cand=0;
@@ -237,16 +222,20 @@ int nr_ice_component_initialize(struct n
       }
 
 #ifdef USE_TURN
       /* And both a srvrflx and relayed candidate for each TURN server */
       for(j=0;j<ctx->turn_server_ct;j++){
         nr_socket *turn_sock;
         nr_ice_candidate *srvflx_cand;
 
+        /* Skip non-UDP */
+        if (ctx->turn_servers[j].transport != IPPROTO_UDP)
+          continue;
+
         /* srvrflx */
         if(r=nr_ice_candidate_create(ctx,component,
           isock,sock,SERVER_REFLEXIVE,
           &ctx->turn_servers[j].turn_server,component->component_id,&cand))
           ABORT(r);
         cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */
         cand->done_cb=nr_ice_initialize_finished_cb;
         cand->cb_arg=cand;
@@ -272,42 +261,160 @@ int nr_ice_component_initialize(struct n
 #endif /* USE_TURN */
 
       /* Create a STUN server context for this socket */
       snprintf(label, sizeof(label), "server(%s)", addrs[i].addr.as_string);
       if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server))
         ABORT(r);
       if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle))
         ABORT(r);
+
       /* Add the default STUN credentials so that we can respond before
-         we hear about the peer. Note: we need to recompute these because
-         we have not yet computed the values in the peer media stream.*/
-      lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag;
-      assert(lufrag);
-      if (!lufrag)
-        ABORT(R_INTERNAL);
-      lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd;
-      assert(lpwd);
-      if (!lpwd)
-        ABORT(R_INTERNAL);
-
-      INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd));
-
-      if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, &pwd, nr_ice_component_stun_server_default_cb, component))
+         we hear about the peer. */
+      if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component))
         ABORT(r);
 
       STAILQ_INSERT_TAIL(&component->sockets,isock,entry);
     }
-    isock=0;
+
+    _status = 0;
+ abort:
+    return(_status);
+  }
+
+static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd)
+  {
+    nr_ice_socket *isock=0;
+    nr_ice_candidate *cand=0;
+    int i;
+    int j;
+    char label[256];
+    int r,_status;
+
+    /* Create a new relayed candidate for each addr/TURN server pair */
+    for(i=0;i<addr_ct;i++){
+      char suppress;
+
+      if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
+        if(r!=R_NOT_FOUND)
+          ABORT(r);
+      }
+      else{
+        if(suppress)
+          continue;
+      }
+
+#ifdef USE_TURN
+      for(j=0;j<ctx->turn_server_ct;j++){
+        nr_transport_addr addr;
+        nr_socket *sock;
+        nr_socket *buffered_sock;
+        nr_socket *turn_sock;
+
+        /* Skip non-TCP */
+        if (ctx->turn_servers[j].transport != IPPROTO_TCP)
+          continue;
+
+        /* Create a local socket */
+        if ((r=nr_transport_addr_copy(&addr, &addrs[i].addr)))
+          ABORT(r);
+        addr.protocol = IPPROTO_TCP;
+        if ((r=nr_transport_addr_fmt_addr_string(&addr)))
+          ABORT(r);
+        if((r=nr_socket_local_create(&addr, &sock))){
+          r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create socket for address %s",ctx->label,addr.as_string);
+          continue;
+        }
+        /* Wrap it */
+        if((r=nr_socket_buffered_stun_create(sock, NR_STUN_MAX_MESSAGE_SIZE, &buffered_sock)))
+          ABORT(r);
+
+        /* The TURN socket */
+        if(r=nr_socket_turn_create(buffered_sock, &turn_sock))
+          ABORT(r);
+
+        /* Create an ICE socket */
+        if((r=nr_ice_socket_create(ctx, component, buffered_sock, &isock)))
+          ABORT(r);
 
+        /* Attach ourselves to it */
+        if(r=nr_ice_candidate_create(ctx,component,
+          isock,turn_sock,RELAYED,
+          &ctx->turn_servers[j].turn_server,component->component_id,&cand))
+          ABORT(r);
+        cand->u.relayed.srvflx_candidate=NULL;
+        cand->u.relayed.server=&ctx->turn_servers[j];
+        TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+        component->candidate_ct++;
+        cand=0;
+
+        /* Create a STUN server context for this socket */
+        snprintf(label, sizeof(label), "server(%s)", addr.as_string);
+        if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server))
+          ABORT(r);
+        if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle))
+          ABORT(r);
+
+       /* Add the default STUN credentials so that we can respond before
+          we hear about the peer.*/
+        if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component))
+          ABORT(r);
+
+        STAILQ_INSERT_TAIL(&component->sockets,isock,entry);
+      }
+    }
+#endif
+
+    _status = 0;
+ abort:
+    return(_status);
+  }
+
+
+/* Make all the candidates we can make at the beginning */
+int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component)
+  {
+    int r,_status;
+    nr_local_addr *addrs=ctx->local_addrs;
+    int addr_ct=ctx->local_addr_ct;
+    char *lufrag;
+    char *lpwd;
+    Data pwd;
+    nr_ice_candidate *cand;
+
+    r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): initializing component with id %d",ctx->label,component->component_id);
+
+    if(addr_ct==0){
+      r_log(LOG_ICE,LOG_ERR,"ICE(%s): no local addresses available",ctx->label);
+      ABORT(R_NOT_FOUND);
+    }
+
+    /* Note: we need to recompute these because
+       we have not yet computed the values in the peer media stream.*/
+    lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag;
+    assert(lufrag);
+    if (!lufrag)
+      ABORT(R_INTERNAL);
+    lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd;
+    assert(lpwd);
+    if (!lpwd)
+      ABORT(R_INTERNAL);
+    INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd));
+
+    /* Initialize the UDP candidates */
+    if (r=nr_ice_component_initialize_udp(ctx, component, addrs, addr_ct, lufrag, &pwd))
+      ABORT(r);
+    /* And the TCP candidates */
+    if (r=nr_ice_component_initialize_tcp(ctx, component, addrs, addr_ct, lufrag, &pwd))
+      ABORT(r);
 
     /* count the candidates that will be initialized */
     cand=TAILQ_FIRST(&component->candidates);
     if(!cand){
-      r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create any valid candidates",ctx->label);
+      r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create any valid candidates",ctx->label);
       ABORT(R_NOT_FOUND);
     }
 
     while(cand){
       ctx->uninitialized_candidates++;
       cand=TAILQ_NEXT(cand,entry_comp);
     }
 
@@ -319,19 +426,18 @@ int nr_ice_component_initialize(struct n
           if(r!=R_WOULDBLOCK){
             ctx->uninitialized_candidates--;
             cand->state=NR_ICE_CAND_STATE_FAILED;
           }
         }
       }
       cand=TAILQ_NEXT(cand,entry_comp);
     }
-
     _status=0;
-  abort:
+ abort:
     return(_status);
   }
 
 /*
   Compare this newly initialized candidate against the other initialized
   candidates and discard the lower-priority one if they are redundant.
 
    This algorithm combined with the other algorithms, favors
@@ -635,18 +741,17 @@ int nr_ice_component_service_pre_answer_
         }
         (*serviced)++;
         STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry);
         nr_ice_pre_answer_request_destroy(&r1);
       }
     }
 
     _status=0;
- abort:
-    return(_status);
+     return(_status);
   }
 
 int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote)
   {
     int r, _status;
     nr_ice_candidate *pcand;
     nr_ice_cand_pair *pair=0;
     char codeword[5];
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
@@ -60,16 +60,17 @@ typedef struct nr_ice_stun_server_ {
       UINT2 port;
     } dnsname;
   } u;
   int index;
 } nr_ice_stun_server;
 
 typedef struct nr_ice_turn_server_ {
     nr_ice_stun_server    turn_server;
+    int                   transport;
     char                 *username;
     Data                 *password;
 } nr_ice_turn_server;
 
 typedef struct nr_ice_foundation_ {
   int index;
 
   nr_transport_addr addr;
--- a/media/mtransport/third_party/nICEr/src/ice/ice_socket.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c
@@ -30,16 +30,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 
 
 static char *RCSSTRING __UNUSED__="$Id: ice_socket.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
 
 #include <assert.h>
+#include <string.h>
 #include "nr_api.h"
 #include "ice_ctx.h"
 #include "stun.h"
 
 static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg)
   {
     int r;
     nr_ice_stun_ctx *sc1,*sc2;
@@ -57,17 +58,21 @@ static void nr_ice_socket_readable_cb(NR
     nr_socket *stun_srv_sock=sock->sock;
 
     r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Socket ready to read",sock->ctx->label);
 
     /* Re-arm first! */
     NR_ASYNC_WAIT(s,how,nr_ice_socket_readable_cb,cb_arg);
 
     if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){
-      r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Error reading from socket",sock->ctx->label);
+      if (r != R_WOULDBLOCK && (sock->type != NR_ICE_SOCKET_TYPE_DGRAM)) {
+        /* Report this error upward. Bug 946423 */
+        r_log(LOG_ICE,LOG_ERR,"ICE(%s): Error on reliable socket. Abandoning.",sock->ctx->label);
+        NR_ASYNC_CANCEL(s, NR_ASYNC_WAIT_READ);
+      }
       return;
     }
 
     /* Deal with the fact that sizeof(int) and sizeof(size_t) may not
        be the same */
     if (len_s > (size_t)INT_MAX)
       return;
 
@@ -184,29 +189,43 @@ static void nr_ice_socket_readable_cb(NR
 
     return;
   }
 
 int nr_ice_socket_create(nr_ice_ctx *ctx,nr_ice_component *comp, nr_socket *nsock, nr_ice_socket **sockp)
   {
     nr_ice_socket *sock=0;
     NR_SOCKET fd;
-    int _status;
+    nr_transport_addr addr;
+    int r,_status;
 
     if(!(sock=RCALLOC(sizeof(nr_ice_socket))))
       ABORT(R_NO_MEMORY);
 
     sock->sock=nsock;
     sock->ctx=ctx;
     sock->component=comp;
 
+    if(r=nr_socket_getaddr(nsock, &addr))
+      ABORT(r);
+
+    if (addr.protocol == IPPROTO_UDP) {
+      sock->type = NR_ICE_SOCKET_TYPE_DGRAM;
+    }
+    else {
+      assert(addr.protocol == IPPROTO_TCP);
+      sock->type = NR_ICE_SOCKET_TYPE_STREAM;
+    }
+
     TAILQ_INIT(&sock->candidates);
     TAILQ_INIT(&sock->stun_ctxs);
 
-    nr_socket_getfd(nsock,&fd);
+    if(r=nr_socket_getfd(nsock,&fd))
+      ABORT(r);
+
     NR_ASYNC_WAIT(fd,NR_ASYNC_WAIT_READ,nr_ice_socket_readable_cb,sock);
 
     *sockp=sock;
 
     _status=0;
   abort:
     if(_status) RFREE(sock);
     return(_status);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_socket.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.h
@@ -57,16 +57,20 @@ typedef struct nr_ice_stun_ctx_ {
   } u;
 
   TAILQ_ENTRY(nr_ice_stun_ctx_) entry;
 } nr_ice_stun_ctx;
 
 
 
 typedef struct nr_ice_socket_ {
+  int type;
+#define NR_ICE_SOCKET_TYPE_DGRAM  1
+#define NR_ICE_SOCKET_TYPE_STREAM 2
+
   nr_socket *sock;
   nr_ice_ctx *ctx;
 
   nr_ice_candidate_head candidates;
   nr_ice_component *component;
 
   TAILQ_HEAD(nr_ice_stun_ctx_head_,nr_ice_stun_ctx_) stun_ctxs;
 
--- a/media/mtransport/third_party/nICEr/src/net/nr_socket.c
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket.c
@@ -34,24 +34,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 static char *RCSSTRING __UNUSED__="$Id: nr_socket.c,v 1.2 2008/04/28 17:59:02 ekr Exp $";
 
 #include <assert.h>
 #include <nr_api.h>
 #include "nr_socket.h"
 #include "local_addr.h"
 
+#define CHECK_DEFINED(f) assert(sock->vtbl->f); if (!sock->vtbl->f) ERETURN(R_INTERNAL);
 int nr_socket_create_int(void *obj, nr_socket_vtbl *vtbl, nr_socket **sockp)
   {
     int _status;
     nr_socket *sock=0;
 
     if(!(sock=RCALLOC(sizeof(nr_socket))))
       ABORT(R_NO_MEMORY);
 
+    assert(vtbl->version == 1);
+    if (vtbl->version != 1)
+       ABORT(R_INTERNAL);
+
     sock->obj=obj;
     sock->vtbl=vtbl;
 
     *sockp=sock;
 
     _status=0;
   abort:
     return(_status);
@@ -59,48 +64,74 @@ int nr_socket_create_int(void *obj, nr_s
 
 int nr_socket_destroy(nr_socket **sockp)
   {
     nr_socket *sock;
 
     if(!sockp || !*sockp)
       return(0);
 
+
     sock=*sockp;
     *sockp=0;
 
+    CHECK_DEFINED(destroy);
+
     assert(sock->vtbl);
     if (sock->vtbl)
       sock->vtbl->destroy(&sock->obj);
 
     RFREE(sock);
 
     return(0);
   }
 
 int nr_socket_sendto(nr_socket *sock,const void *msg, size_t len, int flags,
   nr_transport_addr *addr)
   {
+    CHECK_DEFINED(ssendto);
     return sock->vtbl->ssendto(sock->obj,msg,len,flags,addr);
   }
 
-
 int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen,
   size_t *len, int flags, nr_transport_addr *addr)
   {
+    CHECK_DEFINED(srecvfrom);
     return sock->vtbl->srecvfrom(sock->obj, buf, maxlen, len, flags, addr);
   }
 
 int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd)
   {
+    CHECK_DEFINED(getfd);
     return sock->vtbl->getfd(sock->obj, fd);
   }
 
 int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp)
   {
+    CHECK_DEFINED(getaddr);
     return sock->vtbl->getaddr(sock->obj, addrp);
   }
 
 int nr_socket_close(nr_socket *sock)
   {
+    CHECK_DEFINED(close);
     return sock->vtbl->close(sock->obj);
   }
 
+int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr)
+  {
+    CHECK_DEFINED(connect);
+    return sock->vtbl->connect(sock->obj, addr);
+  }
+
+int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags)
+  {
+    CHECK_DEFINED(swrite);
+    return sock->vtbl->swrite(sock->obj, msg, len, written);
+  }
+
+
+int nr_socket_read(nr_socket *sock,void * restrict buf, size_t maxlen,
+  size_t *len, int flags)
+  {
+    CHECK_DEFINED(sread);
+    return sock->vtbl->sread(sock->obj, buf, maxlen, len);
+  }
--- a/media/mtransport/third_party/nICEr/src/net/nr_socket.h
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket.h
@@ -47,23 +47,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
 #ifdef __cplusplus
 #define restrict
 #elif defined(WIN32)
 #define restrict __restrict
 #endif
 
 typedef struct nr_socket_vtbl_ {
+  UINT4 version;   /* Currently 1 */
   int (*destroy)(void **obj);
   int (*ssendto)(void *obj,const void *msg, size_t len, int flags,
     nr_transport_addr *addr);
   int (*srecvfrom)(void *obj,void * restrict buf, size_t maxlen, size_t *len, int flags,
     nr_transport_addr *addr);
   int (*getfd)(void *obj, NR_SOCKET *fd);
   int (*getaddr)(void *obj, nr_transport_addr *addrp);
+  int (*connect)(void *obj, nr_transport_addr *addr);
+  int (*swrite)(void *obj,const void *msg, size_t len, size_t *written);
+  int (*sread)(void *obj,void * restrict buf, size_t maxlen, size_t *len);
   int (*close)(void *obj);
 } nr_socket_vtbl;
 
 typedef struct nr_socket_ {
   void *obj;
   nr_socket_vtbl *vtbl;
 } nr_socket;
 
@@ -73,11 +77,14 @@ int nr_socket_create_int(void *obj, nr_s
 int nr_socket_destroy(nr_socket **sockp);
 int nr_socket_sendto(nr_socket *sock,const void *msg, size_t len,
   int flags,nr_transport_addr *addr);
 int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen,
   size_t *len, int flags, nr_transport_addr *addr);
 int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd);
 int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp);
 int nr_socket_close(nr_socket *sock);
+int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr);
+int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags);
+int nr_socket_read(nr_socket *sock, void * restrict buf, size_t maxlen, size_t *len, int flags);
 
 #endif
 
new file mode 100644
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
@@ -0,0 +1,395 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <nr_api.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <assert.h>
+
+#include "p_buf.h"
+#include "nr_socket.h"
+#include "stun.h"
+#include "nr_socket_buffered_stun.h"
+
+
+typedef struct nr_socket_buffered_stun_ {
+  nr_socket *inner;
+  nr_transport_addr remote_addr;
+
+  /* Read state */
+  int read_state;
+#define NR_ICE_SOCKET_READ_NONE 0
+#define NR_ICE_SOCKET_READ_HDR  1
+#define NR_ICE_SOCKET_READ_FAILED 2
+  UCHAR *buffer;
+  size_t buffer_size;
+  size_t bytes_needed;
+  size_t bytes_read;
+
+  /* Write state */
+  nr_p_buf_ctx *p_bufs;
+  nr_p_buf_head pending_writes;
+  size_t pending;
+  size_t max_pending;
+} nr_socket_buffered_stun;
+
+static int nr_socket_buffered_stun_destroy(void **objp);
+static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len,
+  int flags, nr_transport_addr *to);
+static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf,
+  size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
+static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd);
+static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp);
+static int nr_socket_buffered_stun_close(void *obj);
+static int nr_socket_buffered_stun_connect(void *sock, nr_transport_addr *addr);
+static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written);
+static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg);
+
+static nr_socket_vtbl nr_socket_buffered_stun_vtbl={
+  1,
+  nr_socket_buffered_stun_destroy,
+  nr_socket_buffered_stun_sendto,
+  nr_socket_buffered_stun_recvfrom,
+  nr_socket_buffered_stun_getfd,
+  nr_socket_buffered_stun_getaddr,
+  nr_socket_buffered_stun_connect,
+  0,
+  0,
+  nr_socket_buffered_stun_close
+};
+
+int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending, nr_socket **sockp)
+{
+  int r, _status;
+  nr_socket_buffered_stun *sock = 0;
+
+  if (!(sock = RCALLOC(sizeof(nr_socket_buffered_stun))))
+    ABORT(R_NO_MEMORY);
+
+  sock->inner = inner;
+
+  if ((r=nr_ip4_port_to_transport_addr(INADDR_ANY, 0, NR_IPV4, &sock->remote_addr)))
+    ABORT(r);
+
+  /* TODO(ekr@rtfm.com): Check this */
+  if (!(sock->buffer = RMALLOC(NR_STUN_MAX_MESSAGE_SIZE)))
+    ABORT(R_NO_MEMORY);
+  sock->read_state = NR_ICE_SOCKET_READ_NONE;
+  sock->buffer_size = NR_STUN_MAX_MESSAGE_SIZE;
+  sock->bytes_needed = sizeof(nr_stun_message_header);
+
+  STAILQ_INIT(&sock->pending_writes);
+  if ((r=nr_p_buf_ctx_create(NR_STUN_MAX_MESSAGE_SIZE, &sock->p_bufs)))
+    ABORT(r);
+  sock->max_pending=max_pending;
+
+
+  if ((r=nr_socket_create_int(sock, &nr_socket_buffered_stun_vtbl, sockp)))
+    ABORT(r);
+
+  _status=0;
+abort:
+  if (_status) {
+    void *sock_v = sock;
+    sock->inner = 0;  /* Give up ownership so we don't destroy */
+    nr_socket_buffered_stun_destroy(&sock_v);
+  }
+  return(_status);
+}
+
+/* Note: This destroys the inner socket */
+int nr_socket_buffered_stun_destroy(void **objp)
+{
+  nr_socket_buffered_stun *sock;
+  NR_SOCKET fd;
+
+  if (!objp || !*objp)
+    return 0;
+
+  sock = (nr_socket_buffered_stun *)*objp;
+  *objp = 0;
+
+  /* Free the buffer if needed */
+  RFREE(sock->buffer);
+
+  /* Cancel waiting on the socket */
+  if (!nr_socket_getfd(sock->inner, &fd)) {
+    NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
+  }
+
+  nr_p_buf_free_chain(sock->p_bufs, &sock->pending_writes);
+  nr_p_buf_ctx_destroy(&sock->p_bufs);
+  nr_socket_destroy(&sock->inner);
+  RFREE(sock);
+
+  return 0;
+}
+
+static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len,
+  int flags, nr_transport_addr *to)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  int r, _status;
+  size_t written;
+
+  /* Check that we are writing to the connected address if
+     connected */
+  if (!nr_transport_addr_is_wildcard(&sock->remote_addr)) {
+    if (nr_transport_addr_cmp(&sock->remote_addr, to, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+      r_log(LOG_GENERIC, LOG_ERR, "Sendto on connected socket doesn't match");
+      ABORT(R_BAD_DATA);
+    }
+  }
+
+  if ((r=nr_socket_buffered_stun_write(obj, msg, len, &written)))
+    ABORT(r);
+
+  if (len != written)
+    ABORT(R_IO_ERROR);
+
+  _status=0;
+abort:
+  return _status;
+}
+
+static void nr_socket_buffered_stun_failed(nr_socket_buffered_stun *sock)
+  {
+    sock->read_state = NR_ICE_SOCKET_READ_FAILED;
+  }
+
+static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf,
+  size_t maxlen, size_t *len, int flags, nr_transport_addr *from)
+{
+  int r, _status;
+  size_t bytes_read;
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) {
+    ABORT(R_FAILED);
+  }
+
+reread:
+  /* Read all the expected bytes */
+  assert(sock->bytes_needed <= sock->buffer_size - sock->bytes_read);
+
+  if(r=nr_socket_read(sock->inner,
+                      sock->buffer + sock->bytes_read,
+                      sock->bytes_needed, &bytes_read, 0))
+    ABORT(r);
+
+  assert(bytes_read <= sock->bytes_needed);
+  sock->bytes_needed -= bytes_read;
+  sock->bytes_read += bytes_read;
+
+  /* Unfinished */
+  if (sock->bytes_needed)
+    ABORT(R_WOULDBLOCK);
+
+  /* No more bytes expeected */
+  if (sock->read_state == NR_ICE_SOCKET_READ_NONE) {
+    int tmp_length;
+    size_t remaining_length;
+
+    /* Parse the header */
+    if (r = nr_stun_message_length(sock->buffer, sock->bytes_read, &tmp_length))
+      ABORT(r);
+    assert(tmp_length >= 0);
+    if (tmp_length < 0)
+      ABORT(R_BAD_DATA);
+    remaining_length = tmp_length;
+
+    /* Check to see if we have enough room */
+    if ((sock->buffer_size - sock->bytes_read) < remaining_length)
+      ABORT(R_BAD_DATA);
+
+    /* Set ourselves up to read the rest of the data */
+    sock->read_state = NR_ICE_SOCKET_READ_HDR;
+    sock->bytes_needed = remaining_length;
+
+    goto reread;
+  }
+
+  if (maxlen < sock->bytes_read)
+    ABORT(R_BAD_ARGS);
+
+  memcpy(buf, sock->buffer, sock->bytes_read);
+  *len = sock->bytes_read;
+  sock->read_state = NR_ICE_SOCKET_READ_NONE;
+  sock->bytes_read = 0;
+  sock->bytes_needed = sizeof(nr_stun_message_header);
+
+  assert(!nr_transport_addr_is_wildcard(&sock->remote_addr));
+  if (!nr_transport_addr_is_wildcard(&sock->remote_addr)) {
+    if ((r=nr_transport_addr_copy(from, &sock->remote_addr)))
+      ABORT(r);
+  }
+
+  _status=0;
+abort:
+  if (_status && (_status != R_WOULDBLOCK)) {
+    nr_socket_buffered_stun_failed(sock);
+  }
+
+  return(_status);
+}
+
+static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  return nr_socket_getfd(sock->inner, fd);
+}
+
+static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  return nr_socket_getaddr(sock->inner, addrp);
+}
+
+static int nr_socket_buffered_stun_close(void *obj)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+  return nr_socket_close(sock->inner);
+}
+
+static int nr_socket_buffered_stun_connect(void *obj, nr_transport_addr *addr)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+  int r, _status;
+
+  if ((r=nr_transport_addr_copy(&sock->remote_addr, addr)))
+    ABORT(r);
+
+  if ((r=nr_socket_connect(sock->inner, addr)))
+    ABORT(r);
+
+  _status=0;
+abort:
+  return(_status);
+}
+
+static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+  int already_armed = 0;
+  int r,_status;
+  size_t written2 = 0;
+  size_t original_len = len;
+
+  /* Buffers are close to full, report error. Do this now so we never
+     get partial writes */
+  if ((sock->pending + len) > sock->max_pending) {
+    r_log(LOG_GENERIC, LOG_INFO, "Buffers full");
+    ABORT(R_WOULDBLOCK);
+  }
+
+
+  if (!sock->pending) {
+    r = nr_socket_write(sock->inner, msg, len, &written2, 0);
+    if (r) {
+      if (r != R_WOULDBLOCK)
+        ABORT(r);
+
+      written2=0;
+    }
+  } else {
+    already_armed = 1;
+  }
+
+  /* Buffer what's left */
+  len -= written2;
+
+  if (len) {
+    if ((r=nr_p_buf_write_to_chain(sock->p_bufs, &sock->pending_writes,
+                                     ((UCHAR *)msg) + written2, len)))
+      ABORT(r);
+
+    sock->pending += len;
+  }
+
+  if (sock->pending && !already_armed) {
+    NR_SOCKET fd;
+
+    if ((r=nr_socket_getfd(sock->inner, &fd)))
+      ABORT(r);
+
+    NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_writable_cb, sock);
+  }
+
+  *written = original_len;
+
+  _status=0;
+abort:
+  return _status;
+}
+
+static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg)
+{
+  nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg;
+  int r,_status;
+  nr_p_buf *n1, *n2;
+
+  /* Try to flush */
+  STAILQ_FOREACH_SAFE(n1, &sock->pending_writes, entry, n2) {
+    size_t written = 0;
+
+    if ((r=nr_socket_write(sock->inner, n1->data + n1->r_offset,
+                           n1->length - n1->r_offset,
+                           &written, 0))) {
+
+      ABORT(r);
+    }
+
+    n1->r_offset += written;
+    if (n1->r_offset < n1->length) {
+      /* We wrote something, but not everything */
+      ABORT(R_WOULDBLOCK);
+    }
+
+    /* We are done with this p_buf */
+    STAILQ_REMOVE_HEAD(&sock->pending_writes, entry);
+    nr_p_buf_free(sock->p_bufs, n1);
+  }
+
+  _status=0;
+abort:
+  if (_status && _status != R_WOULDBLOCK) {
+    /* TODO(ekr@rtfm.com): Mark the socket as failed */
+  }
+}
copy from media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
copy to media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h
--- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h
@@ -1,10 +1,11 @@
 /*
 Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
 
 * Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
@@ -27,20 +28,25 @@ LIMITED TO, PROCUREMENT OF SUBSTITUTE GO
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 
 
-#ifndef _nr_socket_turn_h
-#define _nr_socket_turn_h
+#ifndef _nr_socket_buffered_stun_h
+#define _nr_socket_buffered_stun_h
+
+#include "nr_socket.h"
+
+/* Wrapper socket which provides buffered STUN-oriented I/O
 
-/* This is a partial implementation of an nr_socket wrapped
-   around TURN. It implements only the nr_socket features
-   actually used by the ICE stack. You can't, for instance,
-   read off the socket */
-int nr_socket_turn_create(nr_socket *sock, nr_socket **sockp);
-int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx);
+   1. Writes don't block and are automatically flushed when needed.
+   2. All reads are in units of STUN messages
+
+   This socket takes ownership of the inner socket |sock|.
+ */
+int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending,
+  nr_socket **sockp);
 
 #endif
 
--- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c
@@ -59,21 +59,25 @@ static int nr_socket_turn_sendto(void *o
   int flags, nr_transport_addr *to);
 static int nr_socket_turn_recvfrom(void *obj,void * restrict buf,
   size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
 static int nr_socket_turn_getfd(void *obj, NR_SOCKET *fd);
 static int nr_socket_turn_getaddr(void *obj, nr_transport_addr *addrp);
 static int nr_socket_turn_close(void *obj);
 
 static nr_socket_vtbl nr_socket_turn_vtbl={
+  1,
   nr_socket_turn_destroy,
   nr_socket_turn_sendto,
   nr_socket_turn_recvfrom,
   nr_socket_turn_getfd,
   nr_socket_turn_getaddr,
+  0,
+  0,
+  0,
   nr_socket_turn_close
 };
 
 int nr_socket_turn_create(nr_socket *sock, nr_socket **sockp)
   {
     int r,_status;
     nr_socket_turn *sturn=0;
 
--- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
@@ -30,16 +30,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 
 
 #ifndef _nr_socket_turn_h
 #define _nr_socket_turn_h
 
+#include "nr_socket.h"
+
 /* This is a partial implementation of an nr_socket wrapped
    around TURN. It implements only the nr_socket features
    actually used by the ICE stack. You can't, for instance,
    read off the socket */
 int nr_socket_turn_create(nr_socket *sock, nr_socket **sockp);
 int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx);
 
 #endif
--- a/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c
@@ -26,17 +26,16 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAM
 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 
-
 static char *RCSSTRING __UNUSED__="$Id: stun_client_ctx.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
 
 #include <assert.h>
 #include <string.h>
 
 #include <nr_api.h>
 #include "stun.h"
 #include "async_timer.h"
@@ -81,16 +80,24 @@ int nr_stun_client_ctx_create(char *labe
         ctx->retransmission_backoff_factor = 2.0;
 
     if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS, &ctx->maximum_transmits))
         ctx->maximum_transmits = 7;
 
     if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF, &ctx->final_retransmit_backoff_ms))
         ctx->final_retransmit_backoff_ms = 16 * ctx->rto_ms;
 
+    /* If we are doing TCP, compute the maximum timeout as if
+       we retransmitted and then set the maximum number of
+       transmits to 1 and the timeout to maximum timeout*/
+    if (ctx->my_addr.protocol == IPPROTO_TCP) {
+      ctx->timeout_ms = ctx->final_retransmit_backoff_ms;
+      ctx->maximum_transmits = 1;
+    }
+
     *ctxp=ctx;
 
     _status=0;
   abort:
     if(_status){
       nr_stun_client_ctx_destroy(&ctx);
     }
     return(_status);
--- a/media/mtransport/third_party/nICEr/src/stun/stun_hint.c
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_hint.c
@@ -225,8 +225,25 @@ nr_has_stun_cookie(UCHAR *buf, int len)
        return 0;
 
    if (memcmp(&cookie, &buf[4], sizeof(UINT4)))
        return 0;
 
    return 1;
 }
 
+int
+nr_stun_message_length(UCHAR *buf, int buf_len, int *msg_len)
+{
+  nr_stun_message_header *hdr;
+
+  if (!nr_is_stun_message(buf, buf_len))
+    return(R_BAD_DATA);
+
+  hdr = (nr_stun_message_header *)buf;
+
+  *msg_len = ntohs(hdr->length);
+
+  return(0);
+}
+
+
+
--- a/media/mtransport/third_party/nICEr/src/stun/stun_hint.h
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_hint.h
@@ -34,10 +34,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #ifndef _stun_hint_h
 #define _stun_hint_h
 
 int nr_is_stun_message(UCHAR *buf, int len);
 int nr_is_stun_request_message(UCHAR *buf, int len);
 int nr_is_stun_response_message(UCHAR *buf, int len);
 int nr_is_stun_indication_message(UCHAR *buf, int len);
 int nr_has_stun_cookie(UCHAR *buf, int len);
+int nr_stun_message_length(UCHAR *buf, int len, int *length);
 
 #endif
--- a/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c
@@ -40,38 +40,44 @@ static char *RCSSTRING __UNUSED__="$Id: 
 #ifdef USE_TURN
 
 #include <assert.h>
 #include <string.h>
 
 #include "nr_api.h"
 #include "r_time.h"
 #include "async_timer.h"
+#include "nr_socket_buffered_stun.h"
 #include "stun.h"
 #include "turn_client_ctx.h"
 
+int NR_LOG_TURN = 0;
 
-int NR_LOG_TURN = 0;
+#define TURN_MAX_PENDING_BYTES 32000
 
 #define TURN_RTO 100  /* Hardcoded RTO estimate */
 #define TURN_LIFETIME_REQUEST_SECONDS    3600  /* One hour */
 #define TURN_USECS_PER_S                 1000000
 #define TURN_REFRESH_SLACK_SECONDS       10    /* How long before expiry to refresh
                                                   allocations/permissions. The RFC 5766
                                                   Section 7 recommendation if 60 seconds,
                                                   but this is silly since the transaction
                                                   times out after about 5. */
 #define TURN_PERMISSION_LIFETIME_SECONDS 300   /* 5 minutes. From RFC 5766 2.3 */
+#define TURN_CONNECT_TIMEOUT 1500  /* 1.5 seconds */
 
 static int nr_turn_stun_ctx_create(nr_turn_client_ctx *tctx, int type,
                                    NR_async_cb success_cb,
                                    NR_async_cb failure_cb,
                                    nr_turn_stun_ctx **ctxp);
 static int nr_turn_stun_ctx_destroy(nr_turn_stun_ctx **ctxp);
 static void nr_turn_stun_ctx_cb(NR_SOCKET s, int how, void *arg);
+static int nr_turn_client_connect(nr_turn_client_ctx *ctx);
+static void nr_turn_client_connect_timeout_cb(NR_SOCKET s, int how, void *cb_arg);
+static void nr_turn_client_connected_cb(NR_SOCKET s, int how, void *cb_arg);
 static int nr_turn_stun_set_auth_params(nr_turn_stun_ctx *ctx,
                                         char *realm, char *nonce);
 static void nr_turn_client_refresh_timer_cb(NR_SOCKET s, int how, void *arg);
 static int nr_turn_client_refresh_setup(nr_turn_client_ctx *ctx,
                                         nr_turn_stun_ctx **sctx);
 static int nr_turn_client_failed(nr_turn_client_ctx *ctx);
 static int nr_turn_client_start_refresh_timer(nr_turn_client_ctx *ctx,
                                               nr_turn_stun_ctx *sctx,
@@ -312,31 +318,43 @@ int nr_turn_client_ctx_create(const char
   if ((r=r_log_register("turn", &NR_LOG_TURN)))
     ABORT(r);
 
   if(!(ctx=RCALLOC(sizeof(nr_turn_client_ctx))))
     ABORT(R_NO_MEMORY);
 
   STAILQ_INIT(&ctx->stun_ctxs);
   STAILQ_INIT(&ctx->permissions);
-  ctx->state=NR_TURN_CLIENT_STATE_INITTED;
 
   if(!(ctx->label=r_strdup(label)))
     ABORT(R_NO_MEMORY);
 
   ctx->sock=sock;
   ctx->username = r_strdup(username);
   if (!ctx->username)
     ABORT(R_NO_MEMORY);
 
   if ((r=r_data_create(&ctx->password, password->data, password->len)))
     ABORT(r);
   if ((r=nr_transport_addr_copy(&ctx->turn_server_addr, addr)))
     ABORT(r);
 
+  if (addr->protocol == IPPROTO_UDP) {
+    ctx->state = NR_TURN_CLIENT_STATE_CONNECTED;
+  }
+  else {
+    assert(addr->protocol == IPPROTO_TCP);
+    ctx->state = NR_TURN_CLIENT_STATE_INITTED;
+
+    if (r=nr_turn_client_connect(ctx)) {
+      if (r != R_WOULDBLOCK)
+        ABORT(r);
+    }
+  }
+
   *ctxp=ctx;
 
   _status=0;
 abort:
   if(_status){
     nr_turn_client_ctx_destroy(&ctx);
   }
   return(_status);
@@ -348,36 +366,127 @@ nr_turn_client_ctx_destroy(nr_turn_clien
   nr_turn_client_ctx *ctx;
 
   if(!ctxp || !*ctxp)
     return(0);
 
   ctx=*ctxp;
   *ctxp = 0;
 
+  if (ctx->label)
+    r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): destroy", ctx->label);
+
   /* Cancel frees the rest of our data */
   RFREE(ctx->label);
   ctx->label = 0;
 
   nr_turn_client_cancel(ctx);
 
   RFREE(ctx);
 
   return(0);
 }
 
+static int nr_turn_client_connect(nr_turn_client_ctx *ctx)
+{
+  int r,_status;
+
+  if (ctx->turn_server_addr.protocol != IPPROTO_TCP)
+    ABORT(R_INTERNAL);
+
+  r = nr_socket_connect(ctx->sock, &ctx->turn_server_addr);
+
+  if (r == R_WOULDBLOCK) {
+    NR_SOCKET fd;
+
+    if (r=nr_socket_getfd(ctx->sock, &fd))
+      ABORT(r);
+
+    NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_turn_client_connected_cb, ctx);
+    NR_ASYNC_TIMER_SET(TURN_CONNECT_TIMEOUT,
+                       nr_turn_client_connect_timeout_cb, ctx,
+                       &ctx->connected_timer_handle);
+    ABORT(R_WOULDBLOCK);
+  }
+  if (r) {
+    ABORT(R_IO_ERROR);
+  }
+
+  ctx->state = NR_TURN_CLIENT_STATE_CONNECTED;
+
+  _status = 0;
+abort:
+  return(_status);
+}
+
+static void nr_turn_client_connect_timeout_cb(NR_SOCKET s, int how, void *cb_arg)
+{
+  nr_turn_client_ctx *ctx = (nr_turn_client_ctx *)cb_arg;
+
+  r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): connect timeout", ctx->label);
+
+  ctx->connected_timer_handle = 0;
+
+  /* This also cancels waiting on the file descriptor */
+  nr_turn_client_failed(ctx);
+}
+
+static void nr_turn_client_connected_cb(NR_SOCKET s, int how, void *cb_arg)
+{
+  int r, _status;
+  nr_turn_client_ctx *ctx = (nr_turn_client_ctx *)cb_arg;
+
+  /* Cancel the connection failure timer */
+  NR_async_timer_cancel(ctx->connected_timer_handle);
+  ctx->connected_timer_handle=0;
+
+  /* Assume we connected successfully */
+  if (ctx->state == NR_TURN_CLIENT_STATE_ALLOCATION_WAIT) {
+    if ((r=nr_turn_stun_ctx_start(STAILQ_FIRST(&ctx->stun_ctxs))))
+      ABORT(r);
+    ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING;
+  }
+  else {
+    ctx->state = NR_TURN_CLIENT_STATE_CONNECTED;
+  }
+
+  _status = 0;
+abort:
+  if (_status) {
+    nr_turn_client_failed(ctx);
+  }
+}
+
 int nr_turn_client_cancel(nr_turn_client_ctx *ctx)
 {
   if (ctx->state == NR_TURN_CLIENT_STATE_CANCELLED ||
       ctx->state == NR_TURN_CLIENT_STATE_FAILED)
     return 0;
 
   if (ctx->label)
     r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): cancelling", ctx->label);
 
+  /* If we are waiting for connect, we need to stop
+     waiting for writability */
+  if (ctx->state == NR_TURN_CLIENT_STATE_INITTED ||
+      ctx->state == NR_TURN_CLIENT_STATE_ALLOCATION_WAIT) {
+    NR_SOCKET fd;
+    int r;
+
+    r = nr_socket_getfd(ctx->sock, &fd);
+    if (r) {
+      /* We should be able to get the fd, but if we get an
+         error, we shouldn't cancel. */
+      r_log(NR_LOG_TURN, LOG_ERR, "TURN: Couldn't get internal fd");
+    }
+    else {
+      NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
+    }
+  }
+
   /* Setting these values to 0 isn't strictly necessary, but
      it protects us in case we double cancel and for
      some reason bungle the states above in future.*/
   RFREE(ctx->username);
   ctx->username = 0;
   r_data_destroy(&ctx->password);
   RFREE(ctx->nonce);
   ctx->nonce = 0;
@@ -393,17 +502,18 @@ int nr_turn_client_cancel(nr_turn_client
 
   /* Destroy the permissions */
   while (!STAILQ_EMPTY(&ctx->permissions)) {
     nr_turn_permission *perm = STAILQ_FIRST(&ctx->permissions);
     STAILQ_REMOVE_HEAD(&ctx->permissions, entry);
     nr_turn_permission_destroy(&perm);
   }
 
-  /* Cancel the timer, if not already cancelled */
+  /* Cancel the timers, if not already cancelled */
+  NR_async_timer_cancel(ctx->connected_timer_handle);
   NR_async_timer_cancel(ctx->refresh_timer_handle);
 
   ctx->state = NR_TURN_CLIENT_STATE_CANCELLED;
 
   return(0);
 }
 
 static int nr_turn_client_failed(nr_turn_client_ctx *ctx)
@@ -508,35 +618,43 @@ static void nr_turn_client_error_cb(NR_S
 }
 
 int nr_turn_client_allocate(nr_turn_client_ctx *ctx,
                             NR_async_cb finished_cb, void *cb_arg)
 {
   nr_turn_stun_ctx *stun = 0;
   int r,_status;
 
-  assert(ctx->state == NR_TURN_CLIENT_STATE_INITTED);
-  if (ctx->state != NR_TURN_CLIENT_STATE_INITTED)
-    ABORT(R_ALREADY);
+  assert(ctx->state == NR_TURN_CLIENT_STATE_INITTED ||
+         ctx->state == NR_TURN_CLIENT_STATE_CONNECTED);
 
   ctx->finished_cb=finished_cb;
   ctx->cb_arg=cb_arg;
 
   if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST,
                                  nr_turn_client_allocate_cb,
                                  nr_turn_client_error_cb,
                                  &stun)))
     ABORT(r);
   stun->stun->params.allocate_request.lifetime_secs =
       TURN_LIFETIME_REQUEST_SECONDS;
 
-  ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING;
-
-  if ((r=nr_turn_stun_ctx_start(stun)))
-    ABORT(r);
+  switch(ctx->state) {
+    case NR_TURN_CLIENT_STATE_INITTED:
+      /* We are waiting for connect before we can allocate */
+      ctx->state = NR_TURN_CLIENT_STATE_ALLOCATION_WAIT;
+      break;
+    case NR_TURN_CLIENT_STATE_CONNECTED:
+      if ((r=nr_turn_stun_ctx_start(stun)))
+        ABORT(r);
+      ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING;
+      break;
+    default:
+      ABORT(R_ALREADY);
+  }
 
   _status=0;
 abort:
   if (_status) {
     nr_turn_client_failed(ctx);
   }
 
   return(_status);
@@ -878,16 +996,17 @@ static int nr_turn_permission_create(nr_
   _status=0;
 abort:
   if (_status) {
     nr_turn_permission_destroy(&perm);
   }
   return(_status);
 }
 
+
 static int nr_turn_permission_find(nr_turn_client_ctx *ctx, nr_transport_addr *addr,
                                    nr_turn_permission **permp)
 {
   nr_turn_permission *perm;
   int _status;
 
   perm = STAILQ_FIRST(&ctx->permissions);
   while (perm) {
--- a/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h
@@ -64,44 +64,47 @@ typedef struct nr_turn_permission_ {
   nr_turn_stun_ctx *stun;
   UINT8 last_used;
 
   STAILQ_ENTRY(nr_turn_permission_) entry;
 } nr_turn_permission;
 typedef STAILQ_HEAD(nr_turn_permission_head_, nr_turn_permission_)
     nr_turn_permission_head;
 
-
 /* A single connection to a TURN server. Use one
    turn_client_ctx per socket/server pair. */
 typedef struct nr_turn_client_ctx_ {
   int state;
 #define NR_TURN_CLIENT_STATE_INITTED         1
-#define NR_TURN_CLIENT_STATE_ALLOCATING      2
-#define NR_TURN_CLIENT_STATE_ALLOCATED       3
+#define NR_TURN_CLIENT_STATE_CONNECTED       2
+#define NR_TURN_CLIENT_STATE_ALLOCATION_WAIT 3
+#define NR_TURN_CLIENT_STATE_ALLOCATING      4
+#define NR_TURN_CLIENT_STATE_ALLOCATED       5
 #define NR_TURN_CLIENT_STATE_FAILED          6
-#define NR_TURN_CLIENT_STATE_CANCELLED       8
+#define NR_TURN_CLIENT_STATE_CANCELLED       7
 
   char *label;
   nr_socket *sock;
+
   char *username;
   Data *password;
   char *nonce;
   char *realm;
 
   nr_transport_addr turn_server_addr;
   nr_transport_addr relay_addr;
   nr_transport_addr mapped_addr;
 
   nr_turn_stun_ctx_head stun_ctxs;
   nr_turn_permission_head permissions;
 
   NR_async_cb finished_cb;
   void *cb_arg;
 
+  void *connected_timer_handle;
   void *refresh_timer_handle;
 } nr_turn_client_ctx;
 
 extern int NR_LOG_TURN;
 
 int nr_turn_client_ctx_create(const char *label, nr_socket *sock,
                               const char *username, Data *password,
                               nr_transport_addr *addr,
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -46,17 +46,17 @@ using namespace mozilla;
 MOZ_MTLOG_MODULE("mediapipeline")
 
 namespace mozilla {
 
 static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp";
 
 MediaPipeline::~MediaPipeline() {
   MOZ_ASSERT(!stream_);  // Check that we have shut down already.
-  MOZ_MTLOG(ML_DEBUG, "Destroying MediaPipeline: " << description_);
+  MOZ_MTLOG(ML_INFO, "Destroying MediaPipeline: " << description_);
 }
 
 nsresult MediaPipeline::Init() {
   ASSERT_ON_THREAD(main_thread_);
 
   RUN_ON_THREAD(sts_thread_,
                 WrapRunnable(
                     nsRefPtr<MediaPipeline>(this),
@@ -129,17 +129,17 @@ void MediaPipeline::ShutdownTransport_s(
 void MediaPipeline::StateChange(TransportFlow *flow, TransportLayer::State state) {
   // If rtcp_transport_ is the same as rtp_transport_ then we are muxing.
   // So the only flow should be the RTP flow.
   if (rtcp_transport_ == rtp_transport_) {
     MOZ_ASSERT(flow == rtp_transport_);
   }
 
   if (state == TransportLayer::TS_OPEN) {
-    MOZ_MTLOG(ML_DEBUG, "Flow is ready");
+    MOZ_MTLOG(ML_INFO, "Flow is ready");
     TransportReady_s(flow);
   } else if (state == TransportLayer::TS_CLOSED ||
              state == TransportLayer::TS_ERROR) {
     TransportFailed_s(flow);
   }
 }
 
 nsresult MediaPipeline::TransportReady_s(TransportFlow *flow) {
@@ -152,17 +152,17 @@ nsresult MediaPipeline::TransportReady_s
   if (*state != MP_CONNECTING) {
     MOZ_MTLOG(ML_ERROR, "Transport ready for flow in wrong state:" <<
               description_ << ": " << (rtcp ? "rtcp" : "rtp"));
     return NS_ERROR_FAILURE;
   }
 
   nsresult res;
 
-  MOZ_MTLOG(ML_DEBUG, "Transport ready for pipeline " <<
+  MOZ_MTLOG(ML_INFO, "Transport ready for pipeline " <<
             static_cast<void *>(this) << " flow " << description_ << ": " <<
             (rtcp ? "rtcp" : "rtp"));
 
   // Now instantiate the SRTP objects
   TransportLayerDtls *dtls = static_cast<TransportLayerDtls *>(
       flow->GetLayer(TransportLayerDtls::ID()));
   MOZ_ASSERT(dtls);  // DTLS is mandatory
 
@@ -229,25 +229,25 @@ nsresult MediaPipeline::TransportReady_s
 
     // Start listening
     // If rtcp_transport_ is the same as rtp_transport_ then we are muxing
     if (rtcp_transport_ == rtp_transport_) {
       MOZ_ASSERT(!rtcp_send_srtp_ && !rtcp_recv_srtp_);
       rtcp_send_srtp_ = rtp_send_srtp_;
       rtcp_recv_srtp_ = rtp_recv_srtp_;
 
-      MOZ_MTLOG(ML_DEBUG, "Listening for packets received on " <<
+      MOZ_MTLOG(ML_INFO, "Listening for packets received on " <<
                 static_cast<void *>(dtls->downward()));
 
       dtls->downward()->SignalPacketReceived.connect(this,
                                                      &MediaPipeline::
                                                      PacketReceived);
       rtcp_state_ = MP_OPEN;
     } else {
-      MOZ_MTLOG(ML_DEBUG, "Listening for RTP packets received on " <<
+      MOZ_MTLOG(ML_INFO, "Listening for RTP packets received on " <<
                 static_cast<void *>(dtls->downward()));
 
       dtls->downward()->SignalPacketReceived.connect(this,
                                                      &MediaPipeline::
                                                      RtpPacketReceived);
     }
   }
   else {
@@ -285,17 +285,17 @@ nsresult MediaPipeline::TransportFailed_
 
   // If rtcp_transport_ is the same as rtp_transport_ then we are muxing
   if(rtcp_transport_ == rtp_transport_) {
     MOZ_ASSERT(state != &rtcp_state_);
     rtcp_state_ = MP_CLOSED;
   }
 
 
-  MOZ_MTLOG(ML_DEBUG, "Transport closed for flow " << (rtcp ? "rtcp" : "rtp"));
+  MOZ_MTLOG(ML_INFO, "Transport closed for flow " << (rtcp ? "rtcp" : "rtp"));
 
   NS_WARNING(
       "MediaPipeline Transport failed. This is not properly cleaned up yet");
 
 
   // TODO(ekr@rtfm.com): SECURITY: Figure out how to clean up if the
   // connection was good and now it is bad.
   // TODO(ekr@rtfm.com): Report up so that the PC knows we
@@ -407,19 +407,30 @@ void MediaPipeline::RtpPacketReceived(Tr
 
   // Make a copy rather than cast away constness
   ScopedDeletePtr<unsigned char> inner_data(
       new unsigned char[len]);
   memcpy(inner_data, data, len);
   int out_len;
   nsresult res = rtp_recv_srtp_->UnprotectRtp(inner_data,
                                               len, len, &out_len);
-  if (!NS_SUCCEEDED(res))
+  if (!NS_SUCCEEDED(res)) {
+    char tmp[16];
+
+    PR_snprintf(tmp, sizeof(tmp), "%.2x %.2x %.2x %.2x",
+                inner_data[0],
+                inner_data[1],
+                inner_data[2],
+                inner_data[3]);
+
+    MOZ_MTLOG(ML_NOTICE, "Error unprotecting RTP in " << description_
+              << "len= " << len << "[" << tmp << "...]");
+
     return;
-
+  }
   (void)conduit_->ReceivedRTPPacket(inner_data, out_len);  // Ignore error codes
 }
 
 void MediaPipeline::RtcpPacketReceived(TransportLayer *layer,
                                        const unsigned char *data,
                                        size_t len) {
   if (!transport_->pipeline()) {
     MOZ_MTLOG(ML_DEBUG, "Discarding incoming packet; transport disconnected");
--- a/media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/SrtpFlow.cpp
@@ -155,17 +155,17 @@ nsresult SrtpFlow::UnprotectRtp(void *in
   nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   err_status_t r = srtp_unprotect(session_, in, &len);
 
   if (r != err_status_ok) {
-    MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTP packet");
+    MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTP packet error=" << (int)r);
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
   MOZ_MTLOG(ML_DEBUG, "Successfully unprotected an SRTP packet of len "
             << *out_len);
@@ -201,17 +201,17 @@ nsresult SrtpFlow::UnprotectRtcp(void *i
   nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
   if (NS_FAILED(res))
     return res;
 
   int len = in_len;
   err_status_t r = srtp_unprotect_rtcp(session_, in, &len);
 
   if (r != err_status_ok) {
-    MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTCP packet");
+    MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTCP packet error=" << (int)r);
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(len <= max_len);
   *out_len = len;
 
   MOZ_MTLOG(ML_DEBUG, "Successfully unprotected an SRTCP packet of len "
             << *out_len);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -641,16 +641,20 @@ PeerConnectionImpl::ConvertRTCConfigurat
     }
     if (port == -1)
       port = (isStuns || isTurns)? 5349 : 3478;
 
     if (isTurn || isTurns) {
       NS_ConvertUTF16toUTF8 credential(server.mCredential);
       NS_ConvertUTF16toUTF8 username(server.mUsername);
 
+#ifdef MOZ_WIDGET_GONK
+      if (transport.get() == kNrIceTransportTcp)
+          continue;
+#endif
       if (!aDst->addTurnServer(host.get(), port,
                                username.get(),
                                credential.get(),
                                (transport.IsEmpty() ?
                                 kNrIceTransportUdp : transport.get()))) {
         return NS_ERROR_FAILURE;
       }
     } else {
@@ -1840,31 +1844,31 @@ void PeerConnectionImpl::GetStats_s(
         {
           RTCIceCandidateStats local;
           local.mId.Construct(localCodeword);
           local.mTimestamp.Construct(now);
           local.mType.Construct(RTCStatsType::Localcandidate);
           local.mCandidateType.Construct(
               RTCStatsIceCandidateType(p->local.type));
           local.mIpAddress.Construct(
-              NS_ConvertASCIItoUTF16(p->local.host.c_str()));
-          local.mPortNumber.Construct(p->local.port);
+              NS_ConvertASCIItoUTF16(p->local.cand_addr.host.c_str()));
+          local.mPortNumber.Construct(p->local.cand_addr.port);
           report->mIceCandidateStats.Value().AppendElement(local);
         }
 
         {
           RTCIceCandidateStats remote;
           remote.mId.Construct(remoteCodeword);
           remote.mTimestamp.Construct(now);
           remote.mType.Construct(RTCStatsType::Remotecandidate);
           remote.mCandidateType.Construct(
               RTCStatsIceCandidateType(p->remote.type));
           remote.mIpAddress.Construct(
-              NS_ConvertASCIItoUTF16(p->remote.host.c_str()));
-          remote.mPortNumber.Construct(p->remote.port);
+              NS_ConvertASCIItoUTF16(p->remote.cand_addr.host.c_str()));
+          remote.mPortNumber.Construct(p->remote.cand_addr.port);
           report->mIceCandidateStats.Value().AppendElement(remote);
         }
       }
     }
   }
 
   nsRefPtr<PeerConnectionImpl> pc(this);
   RUN_ON_THREAD(mThread,