diff options
Diffstat (limited to '')
261 files changed, 67266 insertions, 0 deletions
diff --git a/dom/media/webrtc/transport/README b/dom/media/webrtc/transport/README new file mode 100644 index 0000000000..3630b2aa1f --- /dev/null +++ b/dom/media/webrtc/transport/README @@ -0,0 +1,45 @@ +This is a generic media transport system for WebRTC. + +The basic model is that you have a TransportFlow which contains a +series of TransportLayers, each of which gets an opportunity to +manipulate data up and down the stack (think SysV STREAMS or a +standard networking stack). You can also address individual +sublayers to manipulate them or to bypass reading and writing +at an upper layer; WebRTC uses this to implement DTLS-SRTP. + + +DATAFLOW MODEL +Unlike the existing nsSocket I/O system, this is a push rather +than a pull system. Clients of the interface do writes downward +with SendPacket() and receive notification of incoming packets +via callbacks registed via sigslot.h. It is the responsibility +of the bottom layer (or any other layer which needs to reference +external events) to arrange for that somehow; typically by +using nsITimer or the SocketTansportService. + +This sort of push model is a much better fit for the demands +of WebRTC, expecially because ICE contexts span multiple +network transports. + + +THREADING MODEL +There are no thread locks. It is the responsibility of the caller to +arrange that any given TransportLayer/TransportFlow is only +manipulated in one thread at once. One good way to do this is to run +everything on the STS thread. Many of the existing layer implementations +(TransportLayerIce, TransportLayerLoopback) already run on STS so in those +cases you must run on STS, though you can do setup on the main thread and +then activate them on the STS. + + +EXISTING TRANSPORT LAYERS +The following transport layers are currently implemented: + +* DTLS -- a wrapper around NSS's DTLS [RFC 6347] stack +* ICE -- a wrapper around the nICEr ICE [RFC 5245] stack. +* Loopback -- a loopback IO mechanism +* Logging -- a passthrough that just logs its data + +The last two are primarily for debugging. + + diff --git a/dom/media/webrtc/transport/SrtpFlow.cpp b/dom/media/webrtc/transport/SrtpFlow.cpp new file mode 100644 index 0000000000..827d1a0f6d --- /dev/null +++ b/dom/media/webrtc/transport/SrtpFlow.cpp @@ -0,0 +1,259 @@ +/* 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 "logging.h" +#include "SrtpFlow.h" + +#include "srtp.h" + +#include "transportlayerdtls.h" + +#include "mozilla/RefPtr.h" + +using namespace mozilla; + +namespace mozilla { + +MOZ_MTLOG_MODULE("mtransport") +bool SrtpFlow::initialized; // Static + +SrtpFlow::~SrtpFlow() { + if (session_) { + srtp_dealloc(session_); + } +} + +unsigned int SrtpFlow::KeySize(int cipher_suite) { + srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite); + return srtp_profile_get_master_key_length(profile); +} + +unsigned int SrtpFlow::SaltSize(int cipher_suite) { + srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite); + return srtp_profile_get_master_salt_length(profile); +} + +RefPtr<SrtpFlow> SrtpFlow::Create(int cipher_suite, bool inbound, + const void* key, size_t key_len) { + nsresult res = Init(); + if (!NS_SUCCEEDED(res)) return nullptr; + + RefPtr<SrtpFlow> flow = new SrtpFlow(); + + if (!key) { + MOZ_MTLOG(ML_ERROR, "Null SRTP key specified"); + return nullptr; + } + + if ((key_len > SRTP_MAX_KEY_LENGTH) || (key_len < SRTP_MIN_KEY_LENGTH)) { + MOZ_ASSERT(false, "Invalid SRTP key length"); + return nullptr; + } + + srtp_policy_t policy; + memset(&policy, 0, sizeof(srtp_policy_t)); + + // Note that we set the same cipher suite for RTP and RTCP + // since any flow can only have one cipher suite with DTLS-SRTP + switch (cipher_suite) { + case kDtlsSrtpAeadAes256Gcm: + MOZ_MTLOG(ML_DEBUG, "Setting SRTP cipher suite SRTP_AEAD_AES_256_GCM"); + srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp); + srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp); + break; + case kDtlsSrtpAeadAes128Gcm: + MOZ_MTLOG(ML_DEBUG, "Setting SRTP cipher suite SRTP_AEAD_AES_128_GCM"); + srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp); + srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp); + break; + case kDtlsSrtpAes128CmHmacSha1_80: + MOZ_MTLOG(ML_DEBUG, + "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_80"); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); + break; + case kDtlsSrtpAes128CmHmacSha1_32: + MOZ_MTLOG(ML_DEBUG, + "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_32"); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80( + &policy.rtcp); // 80-bit per RFC 5764 + break; // S 4.1.2. + default: + MOZ_MTLOG(ML_ERROR, "Request to set unknown SRTP cipher suite"); + return nullptr; + } + // This key is copied into the srtp_t object, so we don't + // need to keep it. + policy.key = + const_cast<unsigned char*>(static_cast<const unsigned char*>(key)); + policy.ssrc.type = inbound ? ssrc_any_inbound : ssrc_any_outbound; + policy.ssrc.value = 0; + policy.window_size = + 1024; // Use the Chrome value. Needs to be revisited. Default is 128 + policy.allow_repeat_tx = 1; // Use Chrome value; needed for NACK mode to work + policy.next = nullptr; + + // Now make the session + srtp_err_status_t r = srtp_create(&flow->session_, &policy); + if (r != srtp_err_status_ok) { + MOZ_MTLOG(ML_ERROR, "Error creating srtp session"); + return nullptr; + } + + return flow; +} + +nsresult SrtpFlow::CheckInputs(bool protect, void* in, int in_len, int max_len, + int* out_len) { + MOZ_ASSERT(in); + if (!in) { + MOZ_MTLOG(ML_ERROR, "NULL input value"); + return NS_ERROR_NULL_POINTER; + } + + if (in_len < 0) { + MOZ_MTLOG(ML_ERROR, "Input length is negative"); + return NS_ERROR_ILLEGAL_VALUE; + } + + if (max_len < 0) { + MOZ_MTLOG(ML_ERROR, "Max output length is negative"); + return NS_ERROR_ILLEGAL_VALUE; + } + + if (protect) { + if ((max_len < SRTP_MAX_EXPANSION) || + ((max_len - SRTP_MAX_EXPANSION) < in_len)) { + MOZ_MTLOG(ML_ERROR, "Output too short"); + return NS_ERROR_ILLEGAL_VALUE; + } + } else { + if (in_len > max_len) { + MOZ_MTLOG(ML_ERROR, "Output too short"); + return NS_ERROR_ILLEGAL_VALUE; + } + } + + return NS_OK; +} + +nsresult SrtpFlow::ProtectRtp(void* in, int in_len, int max_len, int* out_len) { + nsresult res = CheckInputs(true, in, in_len, max_len, out_len); + if (NS_FAILED(res)) return res; + + int len = in_len; + srtp_err_status_t r = srtp_protect(session_, in, &len); + + if (r != srtp_err_status_ok) { + MOZ_MTLOG(ML_ERROR, "Error protecting SRTP packet"); + return NS_ERROR_FAILURE; + } + + MOZ_ASSERT(len <= max_len); + *out_len = len; + + MOZ_MTLOG(ML_DEBUG, + "Successfully protected an SRTP packet of len " << *out_len); + + return NS_OK; +} + +nsresult SrtpFlow::UnprotectRtp(void* in, int in_len, int max_len, + int* out_len) { + nsresult res = CheckInputs(false, in, in_len, max_len, out_len); + if (NS_FAILED(res)) return res; + + int len = in_len; + srtp_err_status_t r = srtp_unprotect(session_, in, &len); + + if (r != srtp_err_status_ok) { + 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); + + return NS_OK; +} + +nsresult SrtpFlow::ProtectRtcp(void* in, int in_len, int max_len, + int* out_len) { + nsresult res = CheckInputs(true, in, in_len, max_len, out_len); + if (NS_FAILED(res)) return res; + + int len = in_len; + srtp_err_status_t r = srtp_protect_rtcp(session_, in, &len); + + if (r != srtp_err_status_ok) { + MOZ_MTLOG(ML_ERROR, "Error protecting SRTCP packet"); + return NS_ERROR_FAILURE; + } + + MOZ_ASSERT(len <= max_len); + *out_len = len; + + MOZ_MTLOG(ML_DEBUG, + "Successfully protected an SRTCP packet of len " << *out_len); + + return NS_OK; +} + +nsresult SrtpFlow::UnprotectRtcp(void* in, int in_len, int max_len, + int* out_len) { + nsresult res = CheckInputs(false, in, in_len, max_len, out_len); + if (NS_FAILED(res)) return res; + + int len = in_len; + srtp_err_status_t r = srtp_unprotect_rtcp(session_, in, &len); + + if (r != srtp_err_status_ok) { + 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); + + return NS_OK; +} + +// Statics +void SrtpFlow::srtp_event_handler(srtp_event_data_t* data) { + // TODO(ekr@rtfm.com): Implement this + MOZ_CRASH(); +} + +nsresult SrtpFlow::Init() { + if (!initialized) { + srtp_err_status_t r = srtp_init(); + if (r != srtp_err_status_ok) { + MOZ_MTLOG(ML_ERROR, "Could not initialize SRTP"); + MOZ_ASSERT(PR_FALSE); + return NS_ERROR_FAILURE; + } + + r = srtp_install_event_handler(&SrtpFlow::srtp_event_handler); + if (r != srtp_err_status_ok) { + MOZ_MTLOG(ML_ERROR, "Could not install SRTP event handler"); + MOZ_ASSERT(PR_FALSE); + return NS_ERROR_FAILURE; + } + + initialized = true; + } + + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/SrtpFlow.h b/dom/media/webrtc/transport/SrtpFlow.h new file mode 100644 index 0000000000..92fbfcf1a5 --- /dev/null +++ b/dom/media/webrtc/transport/SrtpFlow.h @@ -0,0 +1,69 @@ +/* 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 + +#ifndef srtpflow_h__ +#define srtpflow_h__ + +#include "mozilla/RefPtr.h" +#include "nsISupportsImpl.h" +#include "srtp.h" + +namespace mozilla { + +#define SRTP_ICM_MASTER_KEY_LENGTH 16 +#define SRTP_ICM_MASTER_SALT_LENGTH 14 +#define SRTP_ICM_MAX_MASTER_LENGTH \ + (SRTP_ICM_MASTER_KEY_LENGTH + SRTP_ICM_MASTER_SALT_LENGTH) + +#define SRTP_GCM_MASTER_KEY_MIN_LENGTH 16 +#define SRTP_GCM_MASTER_KEY_MAX_LENGTH 32 +#define SRTP_GCM_MASTER_SALT_LENGTH 12 + +#define SRTP_GCM_MIN_MASTER_LENGTH \ + (SRTP_GCM_MASTER_KEY_MIN_LENGTH + SRTP_GCM_MASTER_SALT_LENGTH) +#define SRTP_GCM_MAX_MASTER_LENGTH \ + (SRTP_GCM_MASTER_KEY_MAX_LENGTH + SRTP_GCM_MASTER_SALT_LENGTH) + +#define SRTP_MIN_KEY_LENGTH SRTP_GCM_MIN_MASTER_LENGTH +#define SRTP_MAX_KEY_LENGTH SRTP_GCM_MAX_MASTER_LENGTH + +// SRTCP requires an auth tag *plus* a 4-byte index-plus-'E'-bit value (see +// RFC 3711) +#define SRTP_MAX_EXPANSION (SRTP_MAX_TRAILER_LEN + 4) + +class SrtpFlow { + ~SrtpFlow(); + + public: + static unsigned int KeySize(int cipher_suite); + static unsigned int SaltSize(int cipher_suite); + + static RefPtr<SrtpFlow> Create(int cipher_suite, bool inbound, + const void* key, size_t key_len); + + nsresult ProtectRtp(void* in, int in_len, int max_len, int* out_len); + nsresult UnprotectRtp(void* in, int in_len, int max_len, int* out_len); + nsresult ProtectRtcp(void* in, int in_len, int max_len, int* out_len); + nsresult UnprotectRtcp(void* in, int in_len, int max_len, int* out_len); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SrtpFlow) + + static void srtp_event_handler(srtp_event_data_t* data); + + private: + SrtpFlow() : session_(nullptr) {} + + nsresult CheckInputs(bool protect, void* in, int in_len, int max_len, + int* out_len); + + static nsresult Init(); + static bool initialized; // Was libsrtp initialized? Only happens once. + + srtp_t session_; +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.cpp b/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.cpp new file mode 100644 index 0000000000..c126e6b791 --- /dev/null +++ b/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#include "WebrtcTCPSocketWrapper.h" + +#include "mozilla/net/WebrtcTCPSocketChild.h" + +#include "nsNetCID.h" +#include "nsProxyRelease.h" +#include "nsServiceManagerUtils.h" + +#include "nr_socket_proxy_config.h" + +namespace mozilla::net { + +using std::shared_ptr; + +WebrtcTCPSocketWrapper::WebrtcTCPSocketWrapper( + WebrtcTCPSocketCallback* aCallbacks) + : mProxyCallbacks(aCallbacks), + mWebrtcTCPSocket(nullptr), + mMainThread(nullptr), + mSocketThread(nullptr) { + mMainThread = GetMainThreadEventTarget(); + mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID); + MOZ_RELEASE_ASSERT(mMainThread, "no main thread"); + MOZ_RELEASE_ASSERT(mSocketThread, "no socket thread"); +} + +WebrtcTCPSocketWrapper::~WebrtcTCPSocketWrapper() { + MOZ_ASSERT(!mWebrtcTCPSocket, "webrtc TCP socket non-null"); + + // If we're never opened then we never get an OnClose from our parent process. + // We need to release our callbacks here safely. + NS_ProxyRelease("WebrtcTCPSocketWrapper::CleanUpCallbacks", mSocketThread, + mProxyCallbacks.forget()); +} + +void WebrtcTCPSocketWrapper::AsyncOpen( + const nsCString& aHost, const int& aPort, const nsCString& aLocalAddress, + const int& aLocalPort, bool aUseTls, + const shared_ptr<NrSocketProxyConfig>& aConfig) { + if (!NS_IsMainThread()) { + MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch( + NewRunnableMethod<const nsCString, const int, const nsCString, + const int, bool, + const shared_ptr<NrSocketProxyConfig>>( + "WebrtcTCPSocketWrapper::AsyncOpen", this, + &WebrtcTCPSocketWrapper::AsyncOpen, aHost, aPort, aLocalAddress, + aLocalPort, aUseTls, aConfig))); + return; + } + + MOZ_ASSERT(!mWebrtcTCPSocket, "wrapper already open"); + mWebrtcTCPSocket = new WebrtcTCPSocketChild(this); + mWebrtcTCPSocket->AsyncOpen(aHost, aPort, aLocalAddress, aLocalPort, aUseTls, + aConfig); +} + +void WebrtcTCPSocketWrapper::SendWrite(nsTArray<uint8_t>&& aReadData) { + if (!NS_IsMainThread()) { + MOZ_ALWAYS_SUCCEEDS( + mMainThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>( + "WebrtcTCPSocketWrapper::SendWrite", this, + &WebrtcTCPSocketWrapper::SendWrite, std::move(aReadData)))); + return; + } + + MOZ_ASSERT(mWebrtcTCPSocket, "webrtc TCP socket should be non-null"); + mWebrtcTCPSocket->SendWrite(aReadData); +} + +void WebrtcTCPSocketWrapper::Close() { + if (!NS_IsMainThread()) { + MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch( + NewRunnableMethod("WebrtcTCPSocketWrapper::Close", this, + &WebrtcTCPSocketWrapper::Close))); + return; + } + + // We're only open if we have a channel. Also Close() should be idempotent. + if (mWebrtcTCPSocket) { + RefPtr<WebrtcTCPSocketChild> child = mWebrtcTCPSocket; + mWebrtcTCPSocket = nullptr; + child->SendClose(); + } +} + +void WebrtcTCPSocketWrapper::OnClose(nsresult aReason) { + MOZ_ASSERT(NS_IsMainThread(), "not on main thread"); + MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null"); + + MOZ_ALWAYS_SUCCEEDS(mSocketThread->Dispatch(NewRunnableMethod<nsresult>( + "WebrtcTCPSocketWrapper::OnClose", mProxyCallbacks, + &WebrtcTCPSocketCallback::OnClose, aReason))); + + NS_ProxyRelease("WebrtcTCPSocketWrapper::CleanUpCallbacks", mSocketThread, + mProxyCallbacks.forget()); +} + +void WebrtcTCPSocketWrapper::OnRead(nsTArray<uint8_t>&& aReadData) { + MOZ_ASSERT(NS_IsMainThread(), "not on main thread"); + MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null"); + + MOZ_ALWAYS_SUCCEEDS( + mSocketThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>( + "WebrtcTCPSocketWrapper::OnRead", mProxyCallbacks, + &WebrtcTCPSocketCallback::OnRead, std::move(aReadData)))); +} + +void WebrtcTCPSocketWrapper::OnConnected(const nsACString& aProxyType) { + MOZ_ASSERT(NS_IsMainThread(), "not on main thread"); + MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null"); + + MOZ_ALWAYS_SUCCEEDS(mSocketThread->Dispatch(NewRunnableMethod<nsCString>( + "WebrtcTCPSocketWrapper::OnConnected", mProxyCallbacks, + &WebrtcTCPSocketCallback::OnConnected, aProxyType))); +} + +} // namespace mozilla::net diff --git a/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.h b/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.h new file mode 100644 index 0000000000..c0775ee9aa --- /dev/null +++ b/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#ifndef webrtc_tcp_socket_wrapper__ +#define webrtc_tcp_socket_wrapper__ + +#include <memory> + +#include "nsCOMPtr.h" +#include "nsTArray.h" + +#include "mozilla/net/WebrtcTCPSocketCallback.h" + +class nsIEventTarget; + +namespace mozilla { + +class NrSocketProxyConfig; + +namespace net { + +class WebrtcTCPSocketChild; + +/** + * WebrtcTCPSocketWrapper is a protector class for mtransport and IPDL. + * mtransport and IPDL cannot include headers from each other due to conflicting + * typedefs. Also it helps users by dispatching calls to the appropriate thread + * based on mtransport's and IPDL's threading requirements. + * + * WebrtcTCPSocketWrapper is only used in the child process. + * WebrtcTCPSocketWrapper does not dispatch for the parent process. + * WebrtcTCPSocketCallback calls are dispatched to the STS thread. + * IPDL calls are dispatched to the main thread. + */ +class WebrtcTCPSocketWrapper : public WebrtcTCPSocketCallback { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketWrapper, override) + + explicit WebrtcTCPSocketWrapper(WebrtcTCPSocketCallback* aCallbacks); + + virtual void AsyncOpen(const nsCString& aHost, const int& aPort, + const nsCString& aLocalAddress, const int& aLocalPort, + bool aUseTls, + const std::shared_ptr<NrSocketProxyConfig>& aConfig); + virtual void SendWrite(nsTArray<uint8_t>&& aReadData); + virtual void Close(); + + // WebrtcTCPSocketCallback + virtual void OnClose(nsresult aReason) override; + virtual void OnConnected(const nsACString& aProxyType) override; + virtual void OnRead(nsTArray<uint8_t>&& aReadData) override; + + protected: + RefPtr<WebrtcTCPSocketCallback> mProxyCallbacks; + RefPtr<WebrtcTCPSocketChild> mWebrtcTCPSocket; + + nsCOMPtr<nsIEventTarget> mMainThread; + nsCOMPtr<nsIEventTarget> mSocketThread; + + virtual ~WebrtcTCPSocketWrapper(); +}; + +} // namespace net +} // namespace mozilla + +#endif // webrtc_tcp_socket_wrapper__ diff --git a/dom/media/webrtc/transport/build/moz.build b/dom/media/webrtc/transport/build/moz.build new file mode 100644 index 0000000000..7349ee5d71 --- /dev/null +++ b/dom/media/webrtc/transport/build/moz.build @@ -0,0 +1,44 @@ +# -*- Mode: python; 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/. + +include("/ipc/chromium/chromium-config.mozbuild") + +EXPORTS.transport += [ + "../dtlsidentity.h", + "../m_cpp_utils.h", + "../mediapacket.h", + "../nr_socket_proxy_config.h", + "../nricectx.h", + "../nricemediastream.h", + "../nriceresolverfake.h", + "../nricestunaddr.h", + "../rlogconnector.h", + "../runnable_utils.h", + "../sigslot.h", + "../simpletokenbucket.h", + "../SrtpFlow.h", + "../stun_socket_filter.h", + "../transportflow.h", + "../transportlayer.h", + "../transportlayerdtls.h", + "../transportlayerice.h", + "../transportlayerlog.h", + "../transportlayerloopback.h", + "../transportlayersrtp.h", +] + +include("../common.build") + +# Add libFuzzer configuration directives +include("/tools/fuzzing/libfuzzer-config.mozbuild") + +# These files cannot be built in unified mode because of the redefinition of +# getLogModule, UNIMPLEMENTED, nr_socket_long_term_violation_time, +# nr_socket_short_term_violation_time, nrappkit/IPDL typedef conflicts in +# PBrowserOrId and WebrtcTCPSocketChild. +SOURCES += transport_cppsrcs + +FINAL_LIBRARY = "xul" diff --git a/dom/media/webrtc/transport/common.build b/dom/media/webrtc/transport/common.build new file mode 100644 index 0000000000..b907b57ee8 --- /dev/null +++ b/dom/media/webrtc/transport/common.build @@ -0,0 +1,94 @@ +# -*- Mode: python; 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/. + +transport_lcppsrcs = [ + "dtlsidentity.cpp", + "mediapacket.cpp", + "nr_socket_proxy_config.cpp", + "nr_socket_prsock.cpp", + "nr_socket_tcp.cpp", + "nr_timer.cpp", + "nricectx.cpp", + "nricemediastream.cpp", + "nriceresolver.cpp", + "nriceresolverfake.cpp", + "nricestunaddr.cpp", + "nrinterfaceprioritizer.cpp", + "rlogconnector.cpp", + "simpletokenbucket.cpp", + "SrtpFlow.cpp", + "stun_socket_filter.cpp", + "test_nr_socket.cpp", + "transportflow.cpp", + "transportlayer.cpp", + "transportlayerdtls.cpp", + "transportlayerice.cpp", + "transportlayerlog.cpp", + "transportlayerloopback.cpp", + "transportlayersrtp.cpp", + "WebrtcTCPSocketWrapper.cpp", +] + +transport_cppsrcs = [ + "/dom/media/webrtc/transport/%s" % s for s in sorted(transport_lcppsrcs) +] + +LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/", + "/dom/media/webrtc/transport/third_party/", + "/dom/media/webrtc/transport/third_party/nICEr/src/crypto", + "/dom/media/webrtc/transport/third_party/nICEr/src/ice", + "/dom/media/webrtc/transport/third_party/nICEr/src/net", + "/dom/media/webrtc/transport/third_party/nICEr/src/stun", + "/dom/media/webrtc/transport/third_party/nICEr/src/util", + "/dom/media/webrtc/transport/third_party/nrappkit/src/event", + "/dom/media/webrtc/transport/third_party/nrappkit/src/log", + "/dom/media/webrtc/transport/third_party/nrappkit/src/plugin", + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include", + "/dom/media/webrtc/transport/third_party/nrappkit/src/registry", + "/dom/media/webrtc/transport/third_party/nrappkit/src/share", + "/dom/media/webrtc/transport/third_party/nrappkit/src/stats", + "/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr", + "/third_party/libsrtp/src/crypto/include", + "/third_party/libsrtp/src/include", +] + +if CONFIG["OS_TARGET"] in ["Darwin", "DragonFly", "FreeBSD", "NetBSD", "OpenBSD"]: + if CONFIG["OS_TARGET"] == "Darwin": + DEFINES["DARWIN"] = True + else: + DEFINES["BSD"] = True + LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include", + ] +elif CONFIG["OS_TARGET"] == "Linux": + DEFINES["LINUX"] = True + LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include", + ] +elif CONFIG["OS_TARGET"] == "Android": + DEFINES["LINUX"] = True + DEFINES["ANDROID"] = True + LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include", + ] +elif CONFIG["OS_TARGET"] == "WINNT": + DEFINES["WIN"] = True + # for stun.h + DEFINES["WIN32"] = True + LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include", + ] + +for var in ("HAVE_STRDUP", "NR_SOCKET_IS_VOID_PTR"): + DEFINES[var] = True + +DEFINES["R_DEFINED_INT2"] = "int16_t" +DEFINES["R_DEFINED_UINT2"] = "uint16_t" +DEFINES["R_DEFINED_INT4"] = "int32_t" +DEFINES["R_DEFINED_UINT4"] = "uint32_t" +DEFINES["R_DEFINED_INT8"] = "int64_t" +DEFINES["R_DEFINED_UINT8"] = "uint64_t" diff --git a/dom/media/webrtc/transport/dtlsidentity.cpp b/dom/media/webrtc/transport/dtlsidentity.cpp new file mode 100644 index 0000000000..2e08c04b60 --- /dev/null +++ b/dom/media/webrtc/transport/dtlsidentity.cpp @@ -0,0 +1,324 @@ +/* -*- 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/. */ + +#include "dtlsidentity.h" + +#include "cert.h" +#include "cryptohi.h" +#include "keyhi.h" +#include "nsError.h" +#include "pk11pub.h" +#include "sechash.h" +#include "mozpkix/nss_scoped_ptrs.h" +#include "secerr.h" +#include "sslerr.h" + +#include "mozilla/Sprintf.h" + +namespace mozilla { + +SECItem* WrapPrivateKeyInfoWithEmptyPassword( + SECKEYPrivateKey* pk) /* encrypt this private key */ +{ + if (!pk) { + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); + return nullptr; + } + + UniquePK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + return nullptr; + } + + // For private keys, NSS cannot export anything other than RSA, but we need EC + // also. So, we use the private key encryption function to serialize instead, + // using a hard-coded dummy password; this is not intended to provide any + // additional security, it just works around a limitation in NSS. + SECItem dummyPassword = {siBuffer, nullptr, 0}; + UniqueSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo( + slot.get(), SEC_OID_AES_128_CBC, &dummyPassword, pk, 1, nullptr)); + + if (!epki) { + return nullptr; + } + + return SEC_ASN1EncodeItem( + nullptr, nullptr, epki.get(), + NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false)); +} + +SECStatus UnwrapPrivateKeyInfoWithEmptyPassword( + SECItem* derPKI, const UniqueCERTCertificate& aCert, + SECKEYPrivateKey** privk) { + if (!derPKI || !aCert || !privk) { + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); + return SECFailure; + } + + UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(aCert.get())); + // This is a pointer to data inside publicKey + SECItem* publicValue = nullptr; + switch (publicKey->keyType) { + case dsaKey: + publicValue = &publicKey->u.dsa.publicValue; + break; + case dhKey: + publicValue = &publicKey->u.dh.publicValue; + break; + case rsaKey: + publicValue = &publicKey->u.rsa.modulus; + break; + case ecKey: + publicValue = &publicKey->u.ec.publicValue; + break; + default: + MOZ_ASSERT(false); + PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); + return SECFailure; + } + + UniquePK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + return SECFailure; + } + + UniquePLArenaPool temparena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + if (!temparena) { + return SECFailure; + } + + SECKEYEncryptedPrivateKeyInfo* epki = + PORT_ArenaZNew(temparena.get(), SECKEYEncryptedPrivateKeyInfo); + if (!epki) { + return SECFailure; + } + + SECStatus rv = SEC_ASN1DecodeItem( + temparena.get(), epki, + NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false), derPKI); + if (rv != SECSuccess) { + // If SEC_ASN1DecodeItem fails, we cannot assume anything about the + // validity of the data in epki. The best we can do is free the arena + // and return. + return rv; + } + + // See comment in WrapPrivateKeyInfoWithEmptyPassword about this + // dummy password stuff. + SECItem dummyPassword = {siBuffer, nullptr, 0}; + return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( + slot.get(), epki, &dummyPassword, nullptr, publicValue, false, false, + publicKey->keyType, KU_ALL, privk, nullptr); +} + +nsresult DtlsIdentity::Serialize(nsTArray<uint8_t>* aKeyDer, + nsTArray<uint8_t>* aCertDer) { + ScopedSECItem derPki(WrapPrivateKeyInfoWithEmptyPassword(private_key_.get())); + if (!derPki) { + return NS_ERROR_FAILURE; + } + + aKeyDer->AppendElements(derPki->data, derPki->len); + aCertDer->AppendElements(cert_->derCert.data, cert_->derCert.len); + return NS_OK; +} + +/* static */ +RefPtr<DtlsIdentity> DtlsIdentity::Deserialize( + const nsTArray<uint8_t>& aKeyDer, const nsTArray<uint8_t>& aCertDer, + SSLKEAType authType) { + SECItem certDer = {siBuffer, const_cast<uint8_t*>(aCertDer.Elements()), + static_cast<unsigned int>(aCertDer.Length())}; + UniqueCERTCertificate cert(CERT_NewTempCertificate( + CERT_GetDefaultCertDB(), &certDer, nullptr, true, true)); + + SECItem derPKI = {siBuffer, const_cast<uint8_t*>(aKeyDer.Elements()), + static_cast<unsigned int>(aKeyDer.Length())}; + + SECKEYPrivateKey* privateKey; + if (UnwrapPrivateKeyInfoWithEmptyPassword(&derPKI, cert, &privateKey) != + SECSuccess) { + MOZ_ASSERT(false); + return nullptr; + } + + return new DtlsIdentity(UniqueSECKEYPrivateKey(privateKey), std::move(cert), + authType); +} + +RefPtr<DtlsIdentity> DtlsIdentity::Generate() { + UniquePK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + return nullptr; + } + + uint8_t random_name[16]; + + SECStatus rv = + PK11_GenerateRandomOnSlot(slot.get(), random_name, sizeof(random_name)); + if (rv != SECSuccess) return nullptr; + + std::string name; + char chunk[3]; + for (unsigned char r_name : random_name) { + SprintfLiteral(chunk, "%.2x", r_name); + name += chunk; + } + + std::string subject_name_string = "CN=" + name; + UniqueCERTName subject_name(CERT_AsciiToName(subject_name_string.c_str())); + if (!subject_name) { + return nullptr; + } + + unsigned char paramBuf[12]; // OIDs are small + SECItem ecdsaParams = {siBuffer, paramBuf, sizeof(paramBuf)}; + SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); + if (!oidData || (oidData->oid.len > (sizeof(paramBuf) - 2))) { + return nullptr; + } + ecdsaParams.data[0] = SEC_ASN1_OBJECT_ID; + ecdsaParams.data[1] = oidData->oid.len; + memcpy(ecdsaParams.data + 2, oidData->oid.data, oidData->oid.len); + ecdsaParams.len = oidData->oid.len + 2; + + SECKEYPublicKey* pubkey; + UniqueSECKEYPrivateKey private_key( + PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsaParams, + &pubkey, PR_FALSE, PR_TRUE, nullptr)); + if (private_key == nullptr) return nullptr; + UniqueSECKEYPublicKey public_key(pubkey); + pubkey = nullptr; + + UniqueCERTSubjectPublicKeyInfo spki( + SECKEY_CreateSubjectPublicKeyInfo(public_key.get())); + if (!spki) { + return nullptr; + } + + UniqueCERTCertificateRequest certreq( + CERT_CreateCertificateRequest(subject_name.get(), spki.get(), nullptr)); + if (!certreq) { + return nullptr; + } + + // From 1 day before todayto 30 days after. + // This is a sort of arbitrary range designed to be valid + // now with some slack in case the other side expects + // some before expiry. + // + // Note: explicit casts necessary to avoid + // warning C4307: '*' : integral constant overflow + static const PRTime oneDay = PRTime(PR_USEC_PER_SEC) * PRTime(60) // sec + * PRTime(60) // min + * PRTime(24); // hours + PRTime now = PR_Now(); + PRTime notBefore = now - oneDay; + PRTime notAfter = now + (PRTime(30) * oneDay); + + UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter)); + if (!validity) { + return nullptr; + } + + unsigned long serial; + // Note: This serial in principle could collide, but it's unlikely + rv = PK11_GenerateRandomOnSlot( + slot.get(), reinterpret_cast<unsigned char*>(&serial), sizeof(serial)); + if (rv != SECSuccess) { + return nullptr; + } + + UniqueCERTCertificate certificate(CERT_CreateCertificate( + serial, subject_name.get(), validity.get(), certreq.get())); + if (!certificate) { + return nullptr; + } + + PLArenaPool* arena = certificate->arena; + + rv = SECOID_SetAlgorithmID(arena, &certificate->signature, + SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, nullptr); + if (rv != SECSuccess) return nullptr; + + // Set version to X509v3. + *(certificate->version.data) = SEC_CERTIFICATE_VERSION_3; + certificate->version.len = 1; + + SECItem innerDER; + innerDER.len = 0; + innerDER.data = nullptr; + + if (!SEC_ASN1EncodeItem(arena, &innerDER, certificate.get(), + SEC_ASN1_GET(CERT_CertificateTemplate))) { + return nullptr; + } + + SECItem* signedCert = PORT_ArenaZNew(arena, SECItem); + if (!signedCert) { + return nullptr; + } + + rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len, + private_key.get(), + SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); + if (rv != SECSuccess) { + return nullptr; + } + certificate->derCert = *signedCert; + + return new DtlsIdentity(std::move(private_key), std::move(certificate), + ssl_kea_ecdh); +} + +const std::string DtlsIdentity::DEFAULT_HASH_ALGORITHM = "sha-256"; + +nsresult DtlsIdentity::ComputeFingerprint(DtlsDigest* digest) const { + const UniqueCERTCertificate& c = cert(); + MOZ_ASSERT(c); + + return ComputeFingerprint(c, digest); +} + +nsresult DtlsIdentity::ComputeFingerprint(const UniqueCERTCertificate& cert, + DtlsDigest* digest) { + MOZ_ASSERT(cert); + + HASH_HashType ht; + + if (digest->algorithm_ == "sha-1") { + ht = HASH_AlgSHA1; + } else if (digest->algorithm_ == "sha-224") { + ht = HASH_AlgSHA224; + } else if (digest->algorithm_ == "sha-256") { + ht = HASH_AlgSHA256; + } else if (digest->algorithm_ == "sha-384") { + ht = HASH_AlgSHA384; + } else if (digest->algorithm_ == "sha-512") { + ht = HASH_AlgSHA512; + } else { + return NS_ERROR_INVALID_ARG; + } + + const SECHashObject* ho = HASH_GetHashObject(ht); + MOZ_ASSERT(ho); + if (!ho) { + return NS_ERROR_INVALID_ARG; + } + + MOZ_ASSERT(ho->length >= 20); // Double check + digest->value_.resize(ho->length); + + SECStatus rv = HASH_HashBuf(ho->type, digest->value_.data(), + cert->derCert.data, cert->derCert.len); + if (rv != SECSuccess) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/dtlsidentity.h b/dom/media/webrtc/transport/dtlsidentity.h new file mode 100644 index 0000000000..b4f7686618 --- /dev/null +++ b/dom/media/webrtc/transport/dtlsidentity.h @@ -0,0 +1,101 @@ +/* -*- 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/. */ +#ifndef dtls_identity_h__ +#define dtls_identity_h__ + +#include <string> +#include <utility> +#include <vector> + +#include "ScopedNSSTypes.h" +#include "m_cpp_utils.h" +#include "mozilla/RefPtr.h" +#include "nsISupportsImpl.h" +#include "nsTArray.h" +#include "sslt.h" + +// All code in this module requires NSS to be live. +// Callers must initialize NSS and implement the nsNSSShutdownObject +// protocol. +namespace mozilla { + +class DtlsDigest { + public: + const static size_t kMaxDtlsDigestLength = HASH_LENGTH_MAX; + DtlsDigest() = default; + explicit DtlsDigest(const std::string& algorithm) : algorithm_(algorithm) {} + DtlsDigest(const std::string& algorithm, const std::vector<uint8_t>& value) + : algorithm_(algorithm), value_(value) { + MOZ_ASSERT(value.size() <= kMaxDtlsDigestLength); + } + ~DtlsDigest() = default; + + bool operator!=(const DtlsDigest& rhs) const { return !operator==(rhs); } + + bool operator==(const DtlsDigest& rhs) const { + if (algorithm_ != rhs.algorithm_) { + return false; + } + + return value_ == rhs.value_; + } + + std::string algorithm_; + std::vector<uint8_t> value_; +}; + +typedef std::vector<DtlsDigest> DtlsDigestList; + +class DtlsIdentity final { + public: + // This constructor takes ownership of privkey and cert. + DtlsIdentity(UniqueSECKEYPrivateKey privkey, UniqueCERTCertificate cert, + SSLKEAType authType) + : private_key_(std::move(privkey)), + cert_(std::move(cert)), + auth_type_(authType) {} + + // Allows serialization/deserialization; cannot write IPC serialization code + // directly for DtlsIdentity, since IPC-able types need to be constructable + // on the stack. + nsresult Serialize(nsTArray<uint8_t>* aKeyDer, nsTArray<uint8_t>* aCertDer); + static RefPtr<DtlsIdentity> Deserialize(const nsTArray<uint8_t>& aKeyDer, + const nsTArray<uint8_t>& aCertDer, + SSLKEAType authType); + + // This is only for use in tests, or for external linkage. It makes a (bad) + // instance of this class. + static RefPtr<DtlsIdentity> Generate(); + + // These don't create copies or transfer ownership. If you want these to live + // on, make a copy. + const UniqueCERTCertificate& cert() const { return cert_; } + const UniqueSECKEYPrivateKey& privkey() const { return private_key_; } + // Note: this uses SSLKEAType because that is what the libssl API requires. + // This is a giant confusing mess, but libssl indexes certificates based on a + // key exchange type, not authentication type (as you might have reasonably + // expected). + SSLKEAType auth_type() const { return auth_type_; } + + nsresult ComputeFingerprint(DtlsDigest* digest) const; + static nsresult ComputeFingerprint(const UniqueCERTCertificate& cert, + DtlsDigest* digest); + + static const std::string DEFAULT_HASH_ALGORITHM; + enum { HASH_ALGORITHM_MAX_LENGTH = 64 }; + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DtlsIdentity) + + private: + ~DtlsIdentity() = default; + DISALLOW_COPY_ASSIGN(DtlsIdentity); + + UniqueSECKEYPrivateKey private_key_; + UniqueCERTCertificate cert_; + SSLKEAType auth_type_; +}; +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/fuzztest/moz.build b/dom/media/webrtc/transport/fuzztest/moz.build new file mode 100644 index 0000000000..f22a5a702b --- /dev/null +++ b/dom/media/webrtc/transport/fuzztest/moz.build @@ -0,0 +1,31 @@ +# -*- Mode: python; 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/. + +Library("FuzzingStun") + +DEFINES["HAVE_STRDUP"] = True + +LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/third_party/nICEr/src/net", + "/dom/media/webrtc/transport/third_party/nICEr/src/stun", + "/dom/media/webrtc/transport/third_party/nrappkit/src/event", + "/dom/media/webrtc/transport/third_party/nrappkit/src/log", + "/dom/media/webrtc/transport/third_party/nrappkit/src/plugin", + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include", + "/dom/media/webrtc/transport/third_party/nrappkit/src/share", + "/dom/media/webrtc/transport/third_party/nrappkit/src/stats", + "/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr", + "/ipc/chromium/src", +] + +# Add libFuzzer configuration directives +include("/tools/fuzzing/libfuzzer-config.mozbuild") + +SOURCES += [ + "stun_parser_libfuzz.cpp", +] + +FINAL_LIBRARY = "xul-gtest" diff --git a/dom/media/webrtc/transport/fuzztest/stun_parser_libfuzz.cpp b/dom/media/webrtc/transport/fuzztest/stun_parser_libfuzz.cpp new file mode 100644 index 0000000000..73bae5024c --- /dev/null +++ b/dom/media/webrtc/transport/fuzztest/stun_parser_libfuzz.cpp @@ -0,0 +1,35 @@ +/* -*- 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/. */ + +#include <string> + +#include "gtest/gtest.h" + +#include "FuzzingInterface.h" + +extern "C" { +#include <csi_platform.h> +#include "stun_msg.h" +#include "stun_codec.h" +} + +int FuzzingInitStunParser(int* argc, char*** argv) { return 0; } + +static int RunStunParserFuzzing(const uint8_t* data, size_t size) { + nr_stun_message* req = 0; + + UCHAR* mes = (UCHAR*)data; + + if (!nr_stun_message_create2(&req, mes, size)) { + nr_stun_decode_message(req, nullptr, nullptr); + nr_stun_message_destroy(&req); + } + + return 0; +} + +MOZ_FUZZING_INTERFACE_RAW(FuzzingInitStunParser, RunStunParserFuzzing, + StunParser); diff --git a/dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h b/dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h new file mode 100644 index 0000000000..34ec46726e --- /dev/null +++ b/dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h @@ -0,0 +1,54 @@ +/* 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/. */ + +#ifndef mozilla_net_NrIceStunAddrMessageUtils_h +#define mozilla_net_NrIceStunAddrMessageUtils_h + +// forward declare NrIceStunAddr for --disable-webrtc builds where +// the header will not be available. +namespace mozilla { +class NrIceStunAddr; +} // namespace mozilla + +#include "ipc/IPCMessageUtils.h" +#ifdef MOZ_WEBRTC +# include "transport/nricestunaddr.h" +#endif + +namespace IPC { + +template <> +struct ParamTraits<mozilla::NrIceStunAddr> { + static void Write(MessageWriter* aWriter, + const mozilla::NrIceStunAddr& aParam) { +#ifdef MOZ_WEBRTC + const size_t bufSize = aParam.SerializationBufferSize(); + char* buffer = new char[bufSize]; + aParam.Serialize(buffer, bufSize); + aWriter->WriteBytes((void*)buffer, bufSize); + delete[] buffer; +#endif + } + + static bool Read(MessageReader* aReader, mozilla::NrIceStunAddr* aResult) { +#ifdef MOZ_WEBRTC + const size_t bufSize = aResult->SerializationBufferSize(); + char* buffer = new char[bufSize]; + bool result = aReader->ReadBytesInto((void*)buffer, bufSize); + + if (result) { + result = result && (NS_OK == aResult->Deserialize(buffer, bufSize)); + } + delete[] buffer; + + return result; +#else + return false; +#endif + } +}; + +} // namespace IPC + +#endif // mozilla_net_NrIceStunAddrMessageUtils_h diff --git a/dom/media/webrtc/transport/ipc/PStunAddrsParams.h b/dom/media/webrtc/transport/ipc/PStunAddrsParams.h new file mode 100644 index 0000000000..315925609d --- /dev/null +++ b/dom/media/webrtc/transport/ipc/PStunAddrsParams.h @@ -0,0 +1,33 @@ +/* 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/. */ + +#ifndef PStunAddrsParams_h +#define PStunAddrsParams_h + +#include "mozilla/Maybe.h" +#include "nsTArray.h" + +#ifdef MOZ_WEBRTC +# include "transport/nricestunaddr.h" +#endif + +namespace mozilla { +namespace net { + +// Need to define typedef in .h file--can't seem to in ipdl.h file? +#ifdef MOZ_WEBRTC +typedef nsTArray<NrIceStunAddr> NrIceStunAddrArray; +#else +// a "dummy" typedef for --disabled-webrtc builds when the definition +// for NrIceStunAddr is not available (otherwise we get complaints +// about missing definitions for contructor and destructor) +typedef nsTArray<int> NrIceStunAddrArray; +#endif + +typedef Maybe<nsCString> MaybeNsCString; + +} // namespace net +} // namespace mozilla + +#endif // PStunAddrsParams_h diff --git a/dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl b/dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl new file mode 100644 index 0000000000..f9096c8dee --- /dev/null +++ b/dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl @@ -0,0 +1,35 @@ +/* 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/. */ + +include protocol PNecko; + +using NrIceStunAddrArray from "mozilla/net/PStunAddrsParams.h"; +using MaybeNsCString from "mozilla/net/PStunAddrsParams.h"; + +include "mozilla/net/NrIceStunAddrMessageUtils.h"; + +namespace mozilla { +namespace net { + +[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual] +async protocol PStunAddrsRequest +{ + manager PNecko; + +parent: + async GetStunAddrs(); + + async RegisterMDNSHostname(nsCString hostname, nsCString address); + async QueryMDNSHostname(nsCString hostname); + async UnregisterMDNSHostname(nsCString hostname); + + async __delete__(); + +child: + async OnMDNSQueryComplete(nsCString hostname, MaybeNsCString address); + async OnStunAddrsAvailable(NrIceStunAddrArray iceStunAddrs); +}; + +} // namespace net +} // namespace mozilla diff --git a/dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl b/dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl new file mode 100644 index 0000000000..3bc2a89828 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ + +/* 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/. */ + +include protocol PNecko; +include protocol PSocketProcess; + +include WebrtcProxyConfig; + +using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; + +namespace mozilla { +namespace net { + +[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual] +async protocol PWebrtcTCPSocket +{ + manager PNecko or PSocketProcess; + +parent: + async AsyncOpen(nsCString aHost, + int32_t aPort, + nsCString aLocalAddress, + int32_t aLocalPort, + bool aUseTls, + WebrtcProxyConfig? aProxyConfig); + async Write(uint8_t[] aWriteData); + async Close(); + +child: + async OnClose(nsresult aReason); + async OnConnected(nsCString aProxyType); + async OnRead(uint8_t[] aReadData); + + async __delete__(); +}; + +} // namespace net +} // namespace mozilla diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp new file mode 100644 index 0000000000..057fcbd330 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp @@ -0,0 +1,45 @@ +/* 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/. */ + +#include "StunAddrsRequestChild.h" + +#include "mozilla/net/NeckoChild.h" +#include "nsISerialEventTarget.h" + +using namespace mozilla::ipc; + +namespace mozilla::net { + +StunAddrsRequestChild::StunAddrsRequestChild(StunAddrsListener* listener) + : mListener(listener) { + gNeckoChild->SendPStunAddrsRequestConstructor(this); + // IPDL holds a reference until IPDL channel gets destroyed + AddIPDLReference(); +} + +mozilla::ipc::IPCResult StunAddrsRequestChild::RecvOnMDNSQueryComplete( + const nsACString& hostname, const Maybe<nsCString>& address) { + if (mListener) { + mListener->OnMDNSQueryComplete(PromiseFlatCString(hostname), address); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult StunAddrsRequestChild::RecvOnStunAddrsAvailable( + const NrIceStunAddrArray& addrs) { + if (mListener) { + mListener->OnStunAddrsAvailable(addrs); + } + return IPC_OK(); +} + +void StunAddrsRequestChild::Cancel() { mListener = nullptr; } + +NS_IMPL_ADDREF(StunAddrsRequestChild) +NS_IMPL_RELEASE(StunAddrsRequestChild) + +NS_IMPL_ADDREF(StunAddrsListener) +NS_IMPL_RELEASE(StunAddrsListener) + +} // namespace mozilla::net diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h new file mode 100644 index 0000000000..f487f52baf --- /dev/null +++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h @@ -0,0 +1,64 @@ +/* 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/. */ + +#ifndef mozilla_net_StunAddrsRequestChild_h +#define mozilla_net_StunAddrsRequestChild_h + +#include "mozilla/net/PStunAddrsRequestChild.h" + +class nsISerialEventTarget; + +namespace mozilla::net { + +class StunAddrsListener { + public: + virtual void OnMDNSQueryComplete(const nsCString& hostname, + const Maybe<nsCString>& address) = 0; + virtual void OnStunAddrsAvailable(const NrIceStunAddrArray& addrs) = 0; + + NS_IMETHOD_(MozExternalRefCountType) AddRef(); + NS_IMETHOD_(MozExternalRefCountType) Release(); + + protected: + virtual ~StunAddrsListener() = default; + + ThreadSafeAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD +}; + +class StunAddrsRequestChild final : public PStunAddrsRequestChild { + friend class PStunAddrsRequestChild; + + public: + explicit StunAddrsRequestChild(StunAddrsListener* listener); + + NS_IMETHOD_(MozExternalRefCountType) AddRef(); + NS_IMETHOD_(MozExternalRefCountType) Release(); + + // Not sure why AddIPDLReference & ReleaseIPDLReference don't come + // from PStunAddrsRequestChild since the IPC plumbing seem to + // expect this. + void AddIPDLReference() { AddRef(); } + void ReleaseIPDLReference() { Release(); } + + void Cancel(); + + protected: + virtual ~StunAddrsRequestChild() = default; + + virtual mozilla::ipc::IPCResult RecvOnMDNSQueryComplete( + const nsACString& aHostname, const Maybe<nsCString>& aAddress) override; + + virtual mozilla::ipc::IPCResult RecvOnStunAddrsAvailable( + const NrIceStunAddrArray& addrs) override; + + RefPtr<StunAddrsListener> mListener; + + ThreadSafeAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD +}; + +} // namespace mozilla::net + +#endif // mozilla_net_StunAddrsRequestChild_h diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp new file mode 100644 index 0000000000..23ef6dea73 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp @@ -0,0 +1,262 @@ +/* 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/. */ + +#include "StunAddrsRequestParent.h" + +#include "../runnable_utils.h" +#include "mozilla/StaticPtr.h" +#include "nsIThread.h" +#include "nsNetUtil.h" + +#include "transport/nricectx.h" +#include "transport/nricemediastream.h" // needed only for including nricectx.h +#include "transport/nricestunaddr.h" + +#include "../mdns_service/mdns_service.h" + +extern "C" { +#include "local_addr.h" +} + +using namespace mozilla::ipc; + +namespace mozilla::net { + +static void mdns_service_resolved(void* cb, const char* hostname, + const char* addr) { + StunAddrsRequestParent* self = static_cast<StunAddrsRequestParent*>(cb); + self->OnQueryComplete(nsCString(hostname), Some(nsCString(addr))); +} + +void mdns_service_timedout(void* cb, const char* hostname) { + StunAddrsRequestParent* self = static_cast<StunAddrsRequestParent*>(cb); + self->OnQueryComplete(nsCString(hostname), Nothing()); +} + +StunAddrsRequestParent::StunAddrsRequestParent() : mIPCClosed(false) { + NS_GetMainThread(getter_AddRefs(mMainThread)); + + nsresult res; + mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res); + MOZ_ASSERT(mSTSThread); +} + +StunAddrsRequestParent::~StunAddrsRequestParent() { + ASSERT_ON_THREAD(mMainThread); +} + +mozilla::ipc::IPCResult StunAddrsRequestParent::RecvGetStunAddrs() { + ASSERT_ON_THREAD(mMainThread); + + if (mIPCClosed) { + return IPC_OK(); + } + + RUN_ON_THREAD(mSTSThread, + WrapRunnable(RefPtr<StunAddrsRequestParent>(this), + &StunAddrsRequestParent::GetStunAddrs_s), + NS_DISPATCH_NORMAL); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StunAddrsRequestParent::RecvRegisterMDNSHostname( + const nsACString& aHostname, const nsACString& aAddress) { + ASSERT_ON_THREAD(mMainThread); + + if (mIPCClosed) { + return IPC_OK(); + } + + if (mSharedMDNSService) { + mSharedMDNSService->RegisterHostname(aHostname.BeginReading(), + aAddress.BeginReading()); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StunAddrsRequestParent::RecvQueryMDNSHostname( + const nsACString& aHostname) { + ASSERT_ON_THREAD(mMainThread); + + if (mIPCClosed) { + return IPC_OK(); + } + + if (mSharedMDNSService) { + mSharedMDNSService->QueryHostname(this, aHostname.BeginReading()); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StunAddrsRequestParent::RecvUnregisterMDNSHostname( + const nsACString& aHostname) { + ASSERT_ON_THREAD(mMainThread); + + if (mIPCClosed) { + return IPC_OK(); + } + + if (mSharedMDNSService) { + mSharedMDNSService->UnregisterHostname(aHostname.BeginReading()); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult StunAddrsRequestParent::Recv__delete__() { + // see note below in ActorDestroy + mIPCClosed = true; + return IPC_OK(); +} + +void StunAddrsRequestParent::OnQueryComplete(const nsACString& hostname, + const Maybe<nsCString>& address) { + RUN_ON_THREAD(mMainThread, + WrapRunnable(RefPtr<StunAddrsRequestParent>(this), + &StunAddrsRequestParent::OnQueryComplete_m, + nsCString(hostname), address), + NS_DISPATCH_NORMAL); +} + +void StunAddrsRequestParent::ActorDestroy(ActorDestroyReason why) { + // We may still have refcount>0 if we haven't made it through + // GetStunAddrs_s and SendStunAddrs_m yet, but child process + // has crashed. We must not send any more msgs to child, or + // IPDL will kill chrome process, too. + mIPCClosed = true; + + // We need to stop the mDNS service here to ensure that we don't + // end up with any messages queued for the main thread after the + // destructors run. Because of Bug 1569311, all of the + // StunAddrsRequestParent instances end up being destroyed one + // after the other, so it is ok to free the shared service when + // the first one is destroyed rather than waiting for the last one. + // If this behaviour changes, we would potentially end up starting + // and stopping instances repeatedly and should add a refcount and + // a way of cancelling pending queries to avoid churn in that case. + if (mSharedMDNSService) { + mSharedMDNSService = nullptr; + } +} + +void StunAddrsRequestParent::GetStunAddrs_s() { + ASSERT_ON_THREAD(mSTSThread); + + // get the stun addresses while on STS thread + NrIceStunAddrArray addrs = NrIceCtx::GetStunAddrs(); + + if (mIPCClosed) { + return; + } + + // in order to return the result over IPC, we need to be on main thread + RUN_ON_THREAD( + mMainThread, + WrapRunnable(RefPtr<StunAddrsRequestParent>(this), + &StunAddrsRequestParent::SendStunAddrs_m, std::move(addrs)), + NS_DISPATCH_NORMAL); +} + +void StunAddrsRequestParent::SendStunAddrs_m(const NrIceStunAddrArray& addrs) { + ASSERT_ON_THREAD(mMainThread); + + if (mIPCClosed) { + // nothing to do: child probably crashed + return; + } + + // This means that the mDNS service will continue running until shutdown + // once started. The StunAddrsRequestParent destructor does not run until + // shutdown anyway (see Bug 1569311), so there is not much we can do about + // this here. One option would be to add a check if there are no hostnames + // registered after UnregisterHostname is called, and if so, stop the mDNS + // service at that time (see Bug 1569955.) + if (!mSharedMDNSService) { + std::ostringstream o; + char buffer[16]; + for (auto& addr : addrs) { + if (addr.localAddr().addr.ip_version == NR_IPV4 && + !nr_transport_addr_is_loopback(&addr.localAddr().addr)) { + nr_transport_addr_get_addrstring(&addr.localAddr().addr, buffer, 16); + o << buffer << ";"; + } + } + std::string addrstring = o.str(); + if (!addrstring.empty()) { + mSharedMDNSService = new MDNSServiceWrapper(addrstring); + } + } + + // send the new addresses back to the child + Unused << SendOnStunAddrsAvailable(addrs); +} + +void StunAddrsRequestParent::OnQueryComplete_m( + const nsACString& hostname, const Maybe<nsCString>& address) { + ASSERT_ON_THREAD(mMainThread); + + if (mIPCClosed) { + // nothing to do: child probably crashed + return; + } + + // send the hostname and address back to the child + Unused << SendOnMDNSQueryComplete(hostname, address); +} + +StaticRefPtr<StunAddrsRequestParent::MDNSServiceWrapper> + StunAddrsRequestParent::mSharedMDNSService; + +NS_IMPL_ADDREF(StunAddrsRequestParent) +NS_IMPL_RELEASE(StunAddrsRequestParent) + +StunAddrsRequestParent::MDNSServiceWrapper::MDNSServiceWrapper( + const std::string& ifaddr) + : ifaddr(ifaddr) {} + +void StunAddrsRequestParent::MDNSServiceWrapper::RegisterHostname( + const char* hostname, const char* address) { + StartIfRequired(); + if (mMDNSService) { + mdns_service_register_hostname(mMDNSService, hostname, address); + } +} + +void StunAddrsRequestParent::MDNSServiceWrapper::QueryHostname( + void* data, const char* hostname) { + StartIfRequired(); + if (mMDNSService) { + mdns_service_query_hostname(mMDNSService, data, mdns_service_resolved, + mdns_service_timedout, hostname); + } +} + +void StunAddrsRequestParent::MDNSServiceWrapper::UnregisterHostname( + const char* hostname) { + StartIfRequired(); + if (mMDNSService) { + mdns_service_unregister_hostname(mMDNSService, hostname); + } +} + +StunAddrsRequestParent::MDNSServiceWrapper::~MDNSServiceWrapper() { + if (mMDNSService) { + mdns_service_stop(mMDNSService); + mMDNSService = nullptr; + } +} + +void StunAddrsRequestParent::MDNSServiceWrapper::StartIfRequired() { + if (!mMDNSService) { + mMDNSService = mdns_service_start(ifaddr.c_str()); + } +} + +NS_IMPL_ADDREF(StunAddrsRequestParent::MDNSServiceWrapper) +NS_IMPL_RELEASE(StunAddrsRequestParent::MDNSServiceWrapper) + +} // namespace mozilla::net diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h new file mode 100644 index 0000000000..33e71abbc7 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h @@ -0,0 +1,82 @@ +/* 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/. */ + +#ifndef mozilla_net_StunAddrsRequestParent_h +#define mozilla_net_StunAddrsRequestParent_h + +#include "mozilla/net/PStunAddrsRequestParent.h" + +struct MDNSService; + +namespace mozilla::net { + +class StunAddrsRequestParent : public PStunAddrsRequestParent { + friend class PStunAddrsRequestParent; + + public: + StunAddrsRequestParent(); + + NS_IMETHOD_(MozExternalRefCountType) AddRef(); + NS_IMETHOD_(MozExternalRefCountType) Release(); + + mozilla::ipc::IPCResult Recv__delete__() override; + + void OnQueryComplete(const nsACString& hostname, + const Maybe<nsCString>& address); + + protected: + virtual ~StunAddrsRequestParent(); + + virtual mozilla::ipc::IPCResult RecvGetStunAddrs() override; + virtual mozilla::ipc::IPCResult RecvRegisterMDNSHostname( + const nsACString& hostname, const nsACString& address) override; + virtual mozilla::ipc::IPCResult RecvQueryMDNSHostname( + const nsACString& hostname) override; + virtual mozilla::ipc::IPCResult RecvUnregisterMDNSHostname( + const nsACString& hostname) override; + virtual void ActorDestroy(ActorDestroyReason why) override; + + nsCOMPtr<nsIThread> mMainThread; + nsCOMPtr<nsISerialEventTarget> mSTSThread; + + void GetStunAddrs_s(); + void SendStunAddrs_m(const NrIceStunAddrArray& addrs); + + void OnQueryComplete_m(const nsACString& hostname, + const Maybe<nsCString>& address); + + ThreadSafeAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD + + private: + bool mIPCClosed; // true if IPDL channel has been closed (child crash) + + class MDNSServiceWrapper { + public: + explicit MDNSServiceWrapper(const std::string& ifaddr); + void RegisterHostname(const char* hostname, const char* address); + void QueryHostname(void* data, const char* hostname); + void UnregisterHostname(const char* hostname); + + NS_IMETHOD_(MozExternalRefCountType) AddRef(); + NS_IMETHOD_(MozExternalRefCountType) Release(); + + protected: + ThreadSafeAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD + + private: + virtual ~MDNSServiceWrapper(); + void StartIfRequired(); + + std::string ifaddr; + MDNSService* mMDNSService = nullptr; + }; + + static StaticRefPtr<MDNSServiceWrapper> mSharedMDNSService; +}; + +} // namespace mozilla::net + +#endif // mozilla_net_StunAddrsRequestParent_h diff --git a/dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh b/dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh new file mode 100644 index 0000000000..e93e82a6a3 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ + +/* 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/. */ + +using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; +include NeckoChannelParams; + +namespace mozilla { +namespace net { + +struct WebrtcProxyConfig { + TabId tabId; + nsCString alpn; + LoadInfoArgs loadInfoArgs; + bool forceProxy; +}; + +} // namespace net +} // namespace mozilla + diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp new file mode 100644 index 0000000000..b0a7d37a0e --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp @@ -0,0 +1,785 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#include "WebrtcTCPSocket.h" + +#include "nsHttpChannel.h" +#include "nsIChannel.h" +#include "nsIClassOfService.h" +#include "nsIContentPolicy.h" +#include "nsIIOService.h" +#include "nsILoadInfo.h" +#include "nsIProtocolProxyService.h" +#include "nsIURIMutator.h" +#include "nsICookieJarSettings.h" +#include "nsProxyRelease.h" +#include "nsString.h" +#include "mozilla/dom/ContentProcessManager.h" +#include "mozilla/dom/BrowserParent.h" +#include "nsISocketTransportService.h" +#include "nsICancelable.h" +#include "nsSocketTransportService2.h" + +#include "WebrtcTCPSocketCallback.h" +#include "WebrtcTCPSocketLog.h" + +namespace mozilla::net { + +class WebrtcTCPData { + public: + explicit WebrtcTCPData(nsTArray<uint8_t>&& aData) : mData(std::move(aData)) { + MOZ_COUNT_CTOR(WebrtcTCPData); + } + + MOZ_COUNTED_DTOR(WebrtcTCPData) + + const nsTArray<uint8_t>& GetData() const { return mData; } + + private: + nsTArray<uint8_t> mData; +}; + +NS_IMPL_ISUPPORTS(WebrtcTCPSocket, nsIAuthPromptProvider, + nsIHttpUpgradeListener, nsIInputStreamCallback, + nsIInterfaceRequestor, nsIOutputStreamCallback, + nsIRequestObserver, nsIStreamListener, + nsIProtocolProxyCallback) + +WebrtcTCPSocket::WebrtcTCPSocket(WebrtcTCPSocketCallback* aCallbacks) + : mProxyCallbacks(aCallbacks), + mClosed(false), + mOpened(false), + mWriteOffset(0), + mAuthProvider(nullptr), + mTransport(nullptr), + mSocketIn(nullptr), + mSocketOut(nullptr) { + LOG(("WebrtcTCPSocket::WebrtcTCPSocket %p\n", this)); + mMainThread = GetMainThreadEventTarget(); + mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID); + MOZ_RELEASE_ASSERT(mMainThread, "no main thread"); + MOZ_RELEASE_ASSERT(mSocketThread, "no socket thread"); +} + +WebrtcTCPSocket::~WebrtcTCPSocket() { + LOG(("WebrtcTCPSocket::~WebrtcTCPSocket %p\n", this)); + + NS_ProxyRelease("WebrtcTCPSocket::CleanUpAuthProvider", mMainThread, + mAuthProvider.forget()); +} + +void WebrtcTCPSocket::SetTabId(dom::TabId aTabId) { + MOZ_ASSERT(NS_IsMainThread()); + dom::ContentProcessManager* cpm = dom::ContentProcessManager::GetSingleton(); + if (cpm) { + dom::ContentParentId cpId = cpm->GetTabProcessId(aTabId); + mAuthProvider = cpm->GetBrowserParentByProcessAndTabId(cpId, aTabId); + } +} + +nsresult WebrtcTCPSocket::Write(nsTArray<uint8_t>&& aWriteData) { + LOG(("WebrtcTCPSocket::Write %p\n", this)); + MOZ_ASSERT(NS_IsMainThread()); + nsresult rv = mSocketThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>( + "WebrtcTCPSocket::Write", this, &WebrtcTCPSocket::EnqueueWrite_s, + std::move(aWriteData))); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS"); + + return rv; +} + +nsresult WebrtcTCPSocket::Close() { + LOG(("WebrtcTCPSocket::Close %p\n", this)); + + CloseWithReason(NS_OK); + + return NS_OK; +} + +void WebrtcTCPSocket::CloseWithReason(nsresult aReason) { + LOG(("WebrtcTCPSocket::CloseWithReason %p reason=%u\n", this, + static_cast<uint32_t>(aReason))); + + if (!OnSocketThread()) { + MOZ_ASSERT(NS_IsMainThread(), "not on main thread"); + + // Let's pretend we got an open even if we didn't to prevent an Open later. + mOpened = true; + + DebugOnly<nsresult> rv = + mSocketThread->Dispatch(NewRunnableMethod<nsresult>( + "WebrtcTCPSocket::CloseWithReason", this, + &WebrtcTCPSocket::CloseWithReason, aReason)); + + // This was MOZ_ALWAYS_SUCCEEDS, but that now uses NS_WARNING_ASSERTION. + // In order to convert this back to MOZ_ALWAYS_SUCCEEDS we would need + // OnSocketThread to return true if we're shutting down and doing the + // "running all of STS's queued events on main" thing. + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS"); + + return; + } + + if (mClosed) { + return; + } + + mClosed = true; + + if (mSocketIn) { + mSocketIn->AsyncWait(nullptr, 0, 0, nullptr); + mSocketIn = nullptr; + } + + if (mSocketOut) { + mSocketOut->AsyncWait(nullptr, 0, 0, nullptr); + mSocketOut = nullptr; + } + + if (mTransport) { + mTransport->Close(NS_BASE_STREAM_CLOSED); + mTransport = nullptr; + } + + NS_ProxyRelease("WebrtcTCPSocket::CleanUpAuthProvider", mMainThread, + mAuthProvider.forget()); + InvokeOnClose(aReason); +} + +nsresult WebrtcTCPSocket::Open( + const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress, + const int& aLocalPort, bool aUseTls, + const Maybe<net::WebrtcProxyConfig>& aProxyConfig) { + LOG(("WebrtcTCPSocket::Open %p remote-host=%s local-addr=%s local-port=%d", + this, PromiseFlatCString(aHost).get(), + PromiseFlatCString(aLocalAddress).get(), aLocalPort)); + MOZ_ASSERT(NS_IsMainThread()); + + if (NS_WARN_IF(mOpened)) { + LOG(("WebrtcTCPSocket %p: TCP socket already open\n", this)); + CloseWithReason(NS_ERROR_FAILURE); + return NS_ERROR_FAILURE; + } + + mOpened = true; + const nsLiteralCString schemePrefix = aUseTls ? "https://"_ns : "http://"_ns; + nsAutoCString spec(schemePrefix); + + bool ipv6Literal = aHost.Find(":") != kNotFound; + if (ipv6Literal) { + spec += "["; + spec += aHost; + spec += "]"; + } else { + spec += aHost; + } + + nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) + .SetSpec(spec) + .SetPort(aPort) + .Finalize(mURI); + + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(NS_ERROR_FAILURE); + return NS_ERROR_FAILURE; + } + + mTls = aUseTls; + mLocalAddress = aLocalAddress; + mLocalPort = aLocalPort; + mProxyConfig = aProxyConfig; + + if (!mProxyConfig.isSome()) { + OpenWithoutHttpProxy(nullptr); + return NS_OK; + } + + // We need to figure out whether a proxy needs to be used for mURI before + // we can start on establishing a connection. + rv = DoProxyConfigLookup(); + + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + } + + return rv; +} + +nsresult WebrtcTCPSocket::DoProxyConfigLookup() { + MOZ_ASSERT(NS_IsMainThread()); + nsresult rv; + nsCOMPtr<nsIProtocolProxyService> pps = + do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr<nsIChannel> channel; + rv = NS_NewChannel(getter_AddRefs(channel), mURI, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsIContentPolicy::TYPE_OTHER); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = pps->AsyncResolve(channel, + nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY | + nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL, + this, nullptr, getter_AddRefs(mProxyRequest)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // We pick back up in OnProxyAvailable + + return NS_OK; +} + +NS_IMETHODIMP WebrtcTCPSocket::OnProxyAvailable(nsICancelable* aRequest, + nsIChannel* aChannel, + nsIProxyInfo* aProxyinfo, + nsresult aResult) { + MOZ_ASSERT(NS_IsMainThread()); + mProxyRequest = nullptr; + + if (NS_SUCCEEDED(aResult) && aProxyinfo) { + nsresult rv = aProxyinfo->GetType(mProxyType); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + return rv; + } + + if (mProxyType == "http" || mProxyType == "https") { + rv = OpenWithHttpProxy(); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + } + return rv; + } + + if (mProxyType == "socks" || mProxyType == "socks4" || + mProxyType == "direct") { + OpenWithoutHttpProxy(aProxyinfo); + return NS_OK; + } + } + + OpenWithoutHttpProxy(nullptr); + + return NS_OK; +} + +void WebrtcTCPSocket::OpenWithoutHttpProxy(nsIProxyInfo* aSocksProxyInfo) { + if (!OnSocketThread()) { + DebugOnly<nsresult> rv = + mSocketThread->Dispatch(NewRunnableMethod<nsCOMPtr<nsIProxyInfo>>( + "WebrtcTCPSocket::OpenWithoutHttpProxy", this, + &WebrtcTCPSocket::OpenWithoutHttpProxy, aSocksProxyInfo)); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS"); + return; + } + + LOG(("WebrtcTCPSocket::OpenWithoutHttpProxy %p\n", this)); + + if (mClosed) { + return; + } + + if (NS_WARN_IF(mProxyConfig.isSome() && mProxyConfig->forceProxy() && + !aSocksProxyInfo)) { + CloseWithReason(NS_ERROR_FAILURE); + return; + } + + nsCString host; + int32_t port; + + nsresult rv = mURI->GetHost(host); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + return; + } + + rv = mURI->GetPort(&port); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + return; + } + + AutoTArray<nsCString, 1> socketTypes; + if (mTls) { + socketTypes.AppendElement("ssl"_ns); + } + + nsCOMPtr<nsISocketTransportService> sts = + do_GetService("@mozilla.org/network/socket-transport-service;1"); + rv = sts->CreateTransport(socketTypes, host, port, aSocksProxyInfo, nullptr, + getter_AddRefs(mTransport)); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + return; + } + + mTransport->SetReuseAddrPort(true); + + PRNetAddr prAddr; + if (NS_WARN_IF(PR_SUCCESS != + PR_InitializeNetAddr(PR_IpAddrAny, mLocalPort, &prAddr))) { + CloseWithReason(NS_ERROR_FAILURE); + return; + } + + if (NS_WARN_IF(PR_SUCCESS != + PR_StringToNetAddr(mLocalAddress.BeginReading(), &prAddr))) { + CloseWithReason(NS_ERROR_FAILURE); + return; + } + + mozilla::net::NetAddr addr(&prAddr); + rv = mTransport->Bind(&addr); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + return; + } + + // Binding to a V4 address is not sufficient to cause this socket to use + // V4, and the same goes for V6. So, we disable as needed here. + uint32_t flags = 0; + if (addr.raw.family == AF_INET) { + flags |= nsISocketTransport::DISABLE_IPV6; + } else if (addr.raw.family == AF_INET6) { + flags |= nsISocketTransport::DISABLE_IPV4; + } else { + MOZ_CRASH(); + } + + mTransport->SetConnectionFlags(flags); + + nsCOMPtr<nsIInputStream> socketIn; + rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(socketIn)); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + return; + } + mSocketIn = do_QueryInterface(socketIn); + if (NS_WARN_IF(!mSocketIn)) { + CloseWithReason(NS_ERROR_NULL_POINTER); + return; + } + + nsCOMPtr<nsIOutputStream> socketOut; + rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, + getter_AddRefs(socketOut)); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + return; + } + mSocketOut = do_QueryInterface(socketOut); + if (NS_WARN_IF(!mSocketOut)) { + CloseWithReason(NS_ERROR_NULL_POINTER); + return; + } + + FinishOpen(); +} + +nsresult WebrtcTCPSocket::OpenWithHttpProxy() { + MOZ_ASSERT(NS_IsMainThread(), "not on main thread"); + LOG(("WebrtcTCPSocket::OpenWithHttpProxy %p\n", this)); + nsresult rv; + nsCOMPtr<nsIIOService> ioService; + ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + LOG(("WebrtcTCPSocket %p: io service missing\n", this)); + return rv; + } + + nsCOMPtr<nsILoadInfo> loadInfo; + Maybe<net::LoadInfoArgs> loadInfoArgs = Some(mProxyConfig->loadInfoArgs()); + + // FIXME: We don't know the remote type of the process which provided these + // LoadInfoArgs. Pass in `NOT_REMOTE_TYPE` as the origin process to blindly + // accept whatever value was passed by the other side for now, as we aren't + // using it for security checks here. + // If this code ever starts checking the triggering remote type, this needs to + // be changed. + rv = ipc::LoadInfoArgsToLoadInfo(loadInfoArgs, NOT_REMOTE_TYPE, + getter_AddRefs(loadInfo)); + if (NS_FAILED(rv)) { + LOG(("WebrtcTCPSocket %p: could not init load info\n", this)); + return rv; + } + + // -need to always tunnel since we're using a proxy + // -there shouldn't be an opportunity to send cookies, but explicitly disallow + // them anyway. + // -the previous proxy tunnel didn't support redirects e.g. 307. don't need to + // introduce new behavior. can't follow redirects on connect anyway. + nsCOMPtr<nsIChannel> localChannel; + rv = ioService->NewChannelFromURIWithProxyFlags( + mURI, nullptr, + // Proxy flags are overridden by SetConnectOnly() + 0, loadInfo->LoadingNode(), loadInfo->GetLoadingPrincipal(), + loadInfo->TriggeringPrincipal(), + nsILoadInfo::SEC_COOKIES_OMIT | + // We need this flag to allow loads from any origin since this channel + // is being used to CONNECT to an HTTP proxy. + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + nsIContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA, + getter_AddRefs(localChannel)); + if (NS_FAILED(rv)) { + LOG(("WebrtcTCPSocket %p: bad open channel\n", this)); + return rv; + } + + nsCOMPtr<nsILoadInfo> channelLoadInfo = localChannel->LoadInfo(); + nsCOMPtr<nsICookieJarSettings> cookieJarSettings; + loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); + channelLoadInfo->SetCookieJarSettings(cookieJarSettings); + + RefPtr<nsHttpChannel> httpChannel; + CallQueryInterface(localChannel, httpChannel.StartAssignment()); + + if (!httpChannel) { + LOG(("WebrtcTCPSocket %p: not an http channel\n", this)); + return NS_ERROR_FAILURE; + } + + httpChannel->SetNotificationCallbacks(this); + + // don't block webrtc proxy setup with other requests + // often more than one of these channels will be created all at once by ICE + nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(localChannel); + if (cos) { + cos->AddClassFlags(nsIClassOfService::Unblocked | + nsIClassOfService::DontThrottle); + } else { + LOG(("WebrtcTCPSocket %p: could not set class of service\n", this)); + return NS_ERROR_FAILURE; + } + + rv = httpChannel->HTTPUpgrade(mProxyConfig->alpn(), this); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = httpChannel->SetConnectOnly(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = httpChannel->AsyncOpen(this); + + if (NS_FAILED(rv)) { + LOG(("WebrtcTCPSocket %p: cannot async open\n", this)); + return rv; + } + + // This picks back up in OnTransportAvailable once we have connected to the + // proxy, and performed the http upgrade to switch the proxy into passthrough + // mode. + + return NS_OK; +} + +void WebrtcTCPSocket::EnqueueWrite_s(nsTArray<uint8_t>&& aWriteData) { + LOG(("WebrtcTCPSocket::EnqueueWrite %p\n", this)); + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + + if (mClosed) { + return; + } + + mWriteQueue.emplace_back(std::move(aWriteData)); + + if (mSocketOut) { + mSocketOut->AsyncWait(this, 0, 0, nullptr); + } +} + +void WebrtcTCPSocket::InvokeOnClose(nsresult aReason) { + LOG(("WebrtcTCPSocket::InvokeOnClose %p\n", this)); + + if (!NS_IsMainThread()) { + MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch( + NewRunnableMethod<nsresult>("WebrtcTCPSocket::InvokeOnClose", this, + &WebrtcTCPSocket::InvokeOnClose, aReason))); + return; + } + + MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null"); + + if (mProxyRequest) { + mProxyRequest->Cancel(aReason); + mProxyRequest = nullptr; + } + + mProxyCallbacks->OnClose(aReason); + mProxyCallbacks = nullptr; +} + +void WebrtcTCPSocket::InvokeOnConnected() { + LOG(("WebrtcTCPSocket::InvokeOnConnected %p\n", this)); + + if (!NS_IsMainThread()) { + MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch( + NewRunnableMethod("WebrtcTCPSocket::InvokeOnConnected", this, + &WebrtcTCPSocket::InvokeOnConnected))); + return; + } + + MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null"); + + mProxyCallbacks->OnConnected(mProxyType); +} + +void WebrtcTCPSocket::InvokeOnRead(nsTArray<uint8_t>&& aReadData) { + LOG(("WebrtcTCPSocket::InvokeOnRead %p count=%zu\n", this, + aReadData.Length())); + + if (!NS_IsMainThread()) { + MOZ_ALWAYS_SUCCEEDS( + mMainThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>( + "WebrtcTCPSocket::InvokeOnRead", this, + &WebrtcTCPSocket::InvokeOnRead, std::move(aReadData)))); + return; + } + + MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null"); + + mProxyCallbacks->OnRead(std::move(aReadData)); +} + +// nsIHttpUpgradeListener +NS_IMETHODIMP +WebrtcTCPSocket::OnTransportAvailable(nsISocketTransport* aTransport, + nsIAsyncInputStream* aSocketIn, + nsIAsyncOutputStream* aSocketOut) { + // This is called only in the http proxy case, once we have connected to the + // http proxy and performed the http upgrade to switch it over to passthrough + // mode. That process is started async by OpenWithHttpProxy. + LOG(("WebrtcTCPSocket::OnTransportAvailable %p\n", this)); + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + MOZ_ASSERT(!mTransport, + "already called transport available on webrtc TCP socket"); + + // Cancel any pending callbacks. The caller doesn't always cancel these + // awaits. We need to make sure they don't get them. + aSocketIn->AsyncWait(nullptr, 0, 0, nullptr); + aSocketOut->AsyncWait(nullptr, 0, 0, nullptr); + + if (mClosed) { + LOG(("WebrtcTCPSocket::OnTransportAvailable %p closed\n", this)); + return NS_OK; + } + + mTransport = aTransport; + mSocketIn = aSocketIn; + mSocketOut = aSocketOut; + + // pulled from nr_socket_prsock.cpp + uint32_t minBufferSize = 256 * 1024; + nsresult rv = mTransport->SetSendBufferSize(minBufferSize); + if (NS_FAILED(rv)) { + LOG(("WebrtcProxyChannel::OnTransportAvailable %p send failed\n", this)); + CloseWithReason(rv); + return rv; + } + rv = mTransport->SetRecvBufferSize(minBufferSize); + if (NS_FAILED(rv)) { + LOG(("WebrtcProxyChannel::OnTransportAvailable %p recv failed\n", this)); + CloseWithReason(rv); + return rv; + } + + FinishOpen(); + return NS_OK; +} + +void WebrtcTCPSocket::FinishOpen() { + MOZ_ASSERT(OnSocketThread()); + // mTransport, mSocketIn, and mSocketOut are all set. We may have set them in + // OnTransportAvailable (in the http/https proxy case), or in + // OpenWithoutHttpProxy. From here on out, this class functions the same for + // these two cases. + + mSocketIn->AsyncWait(this, 0, 0, nullptr); + + InvokeOnConnected(); +} + +NS_IMETHODIMP +WebrtcTCPSocket::OnUpgradeFailed(nsresult aErrorCode) { + LOG(("WebrtcTCPSocket::OnUpgradeFailed %p\n", this)); + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + MOZ_ASSERT(!mTransport, + "already called transport available on webrtc TCP socket"); + + if (mClosed) { + LOG(("WebrtcTCPSocket::OnUpgradeFailed %p closed\n", this)); + return NS_OK; + } + + CloseWithReason(aErrorCode); + + return NS_OK; +} + +NS_IMETHODIMP +WebrtcTCPSocket::OnWebSocketConnectionAvailable( + WebSocketConnectionBase* aConnection) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +// nsIRequestObserver (from nsIStreamListener) +NS_IMETHODIMP +WebrtcTCPSocket::OnStartRequest(nsIRequest* aRequest) { + LOG(("WebrtcTCPSocket::OnStartRequest %p\n", this)); + + return NS_OK; +} + +NS_IMETHODIMP +WebrtcTCPSocket::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { + LOG(("WebrtcTCPSocket::OnStopRequest %p status=%u\n", this, + static_cast<uint32_t>(aStatusCode))); + + // see nsHttpChannel::ProcessFailedProxyConnect for most error codes + if (NS_FAILED(aStatusCode)) { + CloseWithReason(aStatusCode); + return aStatusCode; + } + + return NS_OK; +} + +// nsIStreamListener +NS_IMETHODIMP +WebrtcTCPSocket::OnDataAvailable(nsIRequest* aRequest, + nsIInputStream* aInputStream, uint64_t aOffset, + uint32_t aCount) { + LOG(("WebrtcTCPSocket::OnDataAvailable %p count=%u\n", this, aCount)); + MOZ_ASSERT(0, "unreachable data available"); + return NS_OK; +} + +// nsIInputStreamCallback +NS_IMETHODIMP +WebrtcTCPSocket::OnInputStreamReady(nsIAsyncInputStream* in) { + LOG(("WebrtcTCPSocket::OnInputStreamReady %p unwritten=%zu\n", this, + CountUnwrittenBytes())); + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + MOZ_ASSERT(!mClosed, "webrtc TCP socket closed"); + MOZ_ASSERT(mTransport, "webrtc TCP socket not connected"); + MOZ_ASSERT(mSocketIn == in, "wrong input stream"); + + char buffer[9216]; + uint32_t remainingCapacity = sizeof(buffer); + uint32_t read = 0; + + while (remainingCapacity > 0) { + uint32_t count = 0; + nsresult rv = mSocketIn->Read(buffer + read, remainingCapacity, &count); + if (rv == NS_BASE_STREAM_WOULD_BLOCK) { + break; + } + + if (NS_FAILED(rv)) { + LOG(("WebrtcTCPSocket::OnInputStreamReady %p failed %u\n", this, + static_cast<uint32_t>(rv))); + CloseWithReason(rv); + return rv; + } + + // base stream closed + if (count == 0) { + LOG(("WebrtcTCPSocket::OnInputStreamReady %p connection closed\n", this)); + CloseWithReason(NS_ERROR_FAILURE); + return NS_OK; + } + + remainingCapacity -= count; + read += count; + } + + if (read > 0) { + nsTArray<uint8_t> array(read); + array.AppendElements(buffer, read); + + InvokeOnRead(std::move(array)); + } + + mSocketIn->AsyncWait(this, 0, 0, nullptr); + + return NS_OK; +} + +// nsIOutputStreamCallback +NS_IMETHODIMP +WebrtcTCPSocket::OnOutputStreamReady(nsIAsyncOutputStream* out) { + LOG(("WebrtcTCPSocket::OnOutputStreamReady %p unwritten=%zu\n", this, + CountUnwrittenBytes())); + MOZ_ASSERT(OnSocketThread(), "not on socket thread"); + MOZ_ASSERT(!mClosed, "webrtc TCP socket closed"); + MOZ_ASSERT(mTransport, "webrtc TCP socket not connected"); + MOZ_ASSERT(mSocketOut == out, "wrong output stream"); + + while (!mWriteQueue.empty()) { + const WebrtcTCPData& data = mWriteQueue.front(); + + char* buffer = reinterpret_cast<char*>( + const_cast<uint8_t*>(data.GetData().Elements())) + + mWriteOffset; + uint32_t toWrite = data.GetData().Length() - mWriteOffset; + + uint32_t wrote = 0; + nsresult rv = mSocketOut->Write(buffer, toWrite, &wrote); + if (rv == NS_BASE_STREAM_WOULD_BLOCK) { + mSocketOut->AsyncWait(this, 0, 0, nullptr); + return NS_OK; + } + + if (NS_FAILED(rv)) { + LOG(("WebrtcTCPSocket::OnOutputStreamReady %p failed %u\n", this, + static_cast<uint32_t>(rv))); + CloseWithReason(rv); + return NS_OK; + } + + mWriteOffset += wrote; + + if (toWrite == wrote) { + mWriteOffset = 0; + mWriteQueue.pop_front(); + } + } + + return NS_OK; +} + +// nsIInterfaceRequestor +NS_IMETHODIMP +WebrtcTCPSocket::GetInterface(const nsIID& iid, void** result) { + LOG(("WebrtcTCPSocket::GetInterface %p\n", this)); + + return QueryInterface(iid, result); +} + +size_t WebrtcTCPSocket::CountUnwrittenBytes() const { + size_t count = 0; + + for (const WebrtcTCPData& data : mWriteQueue) { + count += data.GetData().Length(); + } + + MOZ_ASSERT(count >= mWriteOffset, "offset exceeds write buffer length"); + + count -= mWriteOffset; + + return count; +} + +} // namespace mozilla::net diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h new file mode 100644 index 0000000000..632ba47d32 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#ifndef webrtc_tcp_socket_h__ +#define webrtc_tcp_socket_h__ + +#include <list> + +#include "nsCOMPtr.h" +#include "nsIAsyncInputStream.h" +#include "nsIAsyncOutputStream.h" +#include "nsIAuthPromptProvider.h" +#include "nsIHttpChannelInternal.h" +#include "nsIInterfaceRequestor.h" +#include "nsIStreamListener.h" +#include "nsStringFwd.h" +#include "nsTArray.h" +#include "nsIProtocolProxyCallback.h" +#include "mozilla/net/WebrtcProxyConfig.h" + +class nsISocketTransport; + +namespace mozilla::net { + +class WebrtcTCPSocketCallback; +class WebrtcTCPData; + +class WebrtcTCPSocket : public nsIHttpUpgradeListener, + public nsIStreamListener, + public nsIInputStreamCallback, + public nsIOutputStreamCallback, + public nsIInterfaceRequestor, + public nsIAuthPromptProvider, + public nsIProtocolProxyCallback { + public: + NS_DECL_NSIHTTPUPGRADELISTENER + NS_DECL_NSIINPUTSTREAMCALLBACK + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIOUTPUTSTREAMCALLBACK + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_THREADSAFE_ISUPPORTS + NS_FORWARD_SAFE_NSIAUTHPROMPTPROVIDER(mAuthProvider) + NS_DECL_NSIPROTOCOLPROXYCALLBACK + + explicit WebrtcTCPSocket(WebrtcTCPSocketCallback* aCallbacks); + + void SetTabId(dom::TabId aTabId); + nsresult Open(const nsACString& aHost, const int& aPort, + const nsACString& aLocalAddress, const int& aLocalPort, + bool aUseTls, + const Maybe<net::WebrtcProxyConfig>& aProxyConfig); + nsresult Write(nsTArray<uint8_t>&& aBytes); + nsresult Close(); + + size_t CountUnwrittenBytes() const; + + protected: + virtual ~WebrtcTCPSocket(); + + // protected for gtests + virtual void InvokeOnClose(nsresult aReason); + virtual void InvokeOnConnected(); + virtual void InvokeOnRead(nsTArray<uint8_t>&& aReadData); + + RefPtr<WebrtcTCPSocketCallback> mProxyCallbacks; + + private: + bool mClosed; + bool mOpened; + nsCOMPtr<nsIURI> mURI; + bool mTls = false; + Maybe<WebrtcProxyConfig> mProxyConfig; + nsCString mLocalAddress; + uint16_t mLocalPort = 0; + nsCString mProxyType; + + nsresult DoProxyConfigLookup(); + nsresult OpenWithHttpProxy(); + void OpenWithoutHttpProxy(nsIProxyInfo* aSocksProxyInfo); + void FinishOpen(); + void EnqueueWrite_s(nsTArray<uint8_t>&& aWriteData); + + void CloseWithReason(nsresult aReason); + + size_t mWriteOffset; + std::list<WebrtcTCPData> mWriteQueue; + nsCOMPtr<nsIAuthPromptProvider> mAuthProvider; + + // Indicates that the channel is CONNECTed + nsCOMPtr<nsISocketTransport> mTransport; + nsCOMPtr<nsIAsyncInputStream> mSocketIn; + nsCOMPtr<nsIAsyncOutputStream> mSocketOut; + nsCOMPtr<nsIEventTarget> mMainThread; + nsCOMPtr<nsIEventTarget> mSocketThread; + nsCOMPtr<nsICancelable> mProxyRequest; +}; + +} // namespace mozilla::net + +#endif // webrtc_tcp_socket_h__ diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h new file mode 100644 index 0000000000..1929e55ac2 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#ifndef webrtc_tcp_socket_callback_h__ +#define webrtc_tcp_socket_callback_h__ + +#include "nsTArray.h" + +namespace mozilla::net { + +class WebrtcTCPSocketCallback { + public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + virtual void OnClose(nsresult aReason) = 0; + virtual void OnConnected(const nsACString& aProxyType) = 0; + virtual void OnRead(nsTArray<uint8_t>&& aReadData) = 0; + + protected: + virtual ~WebrtcTCPSocketCallback() = default; +}; + +} // namespace mozilla::net + +#endif // webrtc_tcp_socket_callback_h__ diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp new file mode 100644 index 0000000000..52d1ba8ab2 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#include "WebrtcTCPSocketChild.h" + +#include "mozilla/net/NeckoChild.h" +#include "mozilla/net/SocketProcessChild.h" + +#include "LoadInfo.h" + +#include "WebrtcTCPSocketLog.h" +#include "WebrtcTCPSocketCallback.h" + +using namespace mozilla::ipc; + +namespace mozilla::net { + +mozilla::ipc::IPCResult WebrtcTCPSocketChild::RecvOnClose( + const nsresult& aReason) { + LOG(("WebrtcTCPSocketChild::RecvOnClose %p\n", this)); + + MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null"); + mProxyCallbacks->OnClose(aReason); + mProxyCallbacks = nullptr; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult WebrtcTCPSocketChild::RecvOnConnected( + const nsACString& aProxyType) { + LOG(("WebrtcTCPSocketChild::RecvOnConnected %p\n", this)); + + MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null"); + mProxyCallbacks->OnConnected(aProxyType); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult WebrtcTCPSocketChild::RecvOnRead( + nsTArray<uint8_t>&& aReadData) { + LOG(("WebrtcTCPSocketChild::RecvOnRead %p\n", this)); + + MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null"); + mProxyCallbacks->OnRead(std::move(aReadData)); + + return IPC_OK(); +} + +WebrtcTCPSocketChild::WebrtcTCPSocketChild( + WebrtcTCPSocketCallback* aProxyCallbacks) + : mProxyCallbacks(aProxyCallbacks) { + MOZ_COUNT_CTOR(WebrtcTCPSocketChild); + + LOG(("WebrtcTCPSocketChild::WebrtcTCPSocketChild %p\n", this)); +} + +WebrtcTCPSocketChild::~WebrtcTCPSocketChild() { + MOZ_COUNT_DTOR(WebrtcTCPSocketChild); + + LOG(("WebrtcTCPSocketChild::~WebrtcTCPSocketChild %p\n", this)); +} + +void WebrtcTCPSocketChild::AsyncOpen( + const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress, + const int& aLocalPort, bool aUseTls, + const std::shared_ptr<NrSocketProxyConfig>& aProxyConfig) { + LOG(("WebrtcTCPSocketChild::AsyncOpen %p %s:%d\n", this, + PromiseFlatCString(aHost).get(), aPort)); + + MOZ_ASSERT(NS_IsMainThread(), "not main thread"); + + AddIPDLReference(); + + Maybe<net::WebrtcProxyConfig> proxyConfig; + Maybe<dom::TabId> tabId; + if (aProxyConfig) { + proxyConfig = Some(aProxyConfig->GetConfig()); + tabId = Some(proxyConfig->tabId()); + } + + if (IsNeckoChild()) { + // We're on a content process + gNeckoChild->SendPWebrtcTCPSocketConstructor(this, tabId); + } else if (IsSocketProcessChild()) { + // We're on a socket process + SocketProcessChild::GetSingleton()->SendPWebrtcTCPSocketConstructor(this, + tabId); + } + + SendAsyncOpen(aHost, aPort, aLocalAddress, aLocalPort, aUseTls, proxyConfig); +} + +} // namespace mozilla::net diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h new file mode 100644 index 0000000000..638ffcaac3 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#ifndef mozilla_net_WebrtcTCPSocketChild_h +#define mozilla_net_WebrtcTCPSocketChild_h + +#include "mozilla/net/PWebrtcTCPSocketChild.h" +#include "mozilla/dom/ipc/IdType.h" +#include "transport/nr_socket_proxy_config.h" + +namespace mozilla::net { + +class WebrtcTCPSocketCallback; + +class WebrtcTCPSocketChild : public PWebrtcTCPSocketChild { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketChild) + + mozilla::ipc::IPCResult RecvOnClose(const nsresult& aReason) override; + + mozilla::ipc::IPCResult RecvOnConnected( + const nsACString& aProxyType) override; + + mozilla::ipc::IPCResult RecvOnRead(nsTArray<uint8_t>&& aReadData) override; + + explicit WebrtcTCPSocketChild(WebrtcTCPSocketCallback* aProxyCallbacks); + + void AsyncOpen(const nsACString& aHost, const int& aPort, + const nsACString& aLocalAddress, const int& aLocalPort, + bool aUseTls, + const std::shared_ptr<NrSocketProxyConfig>& aProxyConfig); + + void AddIPDLReference() { AddRef(); } + void ReleaseIPDLReference() { Release(); } + + protected: + virtual ~WebrtcTCPSocketChild(); + + RefPtr<WebrtcTCPSocketCallback> mProxyCallbacks; +}; + +} // namespace mozilla::net + +#endif // mozilla_net_WebrtcTCPSocketChild_h diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp new file mode 100644 index 0000000000..a6b0dcdb75 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp @@ -0,0 +1,11 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#include "WebrtcTCPSocketLog.h" + +namespace mozilla::net { +LazyLogModule webrtcTCPSocketLog("WebrtcTCPSocket"); +} // namespace mozilla::net diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h new file mode 100644 index 0000000000..72d3d0064b --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#ifndef webrtc_tcp_socket_log_h__ +#define webrtc_tcp_socket_log_h__ + +#include "mozilla/Logging.h" + +namespace mozilla::net { +extern LazyLogModule webrtcTCPSocketLog; +} // namespace mozilla::net + +#undef LOG +#define LOG(args) \ + MOZ_LOG(mozilla::net::webrtcTCPSocketLog, mozilla::LogLevel::Debug, args) + +#endif // webrtc_tcp_socket_log_h__ diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp new file mode 100644 index 0000000000..0df8962757 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#include "WebrtcTCPSocketParent.h" + +#include "mozilla/net/NeckoParent.h" + +#include "WebrtcTCPSocket.h" +#include "WebrtcTCPSocketLog.h" + +using namespace mozilla::dom; +using namespace mozilla::ipc; + +namespace mozilla::net { + +mozilla::ipc::IPCResult WebrtcTCPSocketParent::RecvAsyncOpen( + const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress, + const int& aLocalPort, const bool& aUseTls, + const Maybe<WebrtcProxyConfig>& aProxyConfig) { + LOG(("WebrtcTCPSocketParent::RecvAsyncOpen %p to %s:%d\n", this, + PromiseFlatCString(aHost).get(), aPort)); + + MOZ_ASSERT(mChannel, "webrtc TCP socket should be non-null"); + if (!mChannel) { + return IPC_FAIL(this, "Called with null channel."); + } + + mChannel->Open(aHost, aPort, aLocalAddress, aLocalPort, aUseTls, + aProxyConfig); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult WebrtcTCPSocketParent::RecvWrite( + nsTArray<uint8_t>&& aWriteData) { + LOG(("WebrtcTCPSocketParent::RecvWrite %p for %zu\n", this, + aWriteData.Length())); + + // Need to check this here in case there are Writes in the queue after OnClose + if (mChannel) { + mChannel->Write(std::move(aWriteData)); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult WebrtcTCPSocketParent::RecvClose() { + LOG(("WebrtcTCPSocketParent::RecvClose %p\n", this)); + + CleanupChannel(); + + IProtocol* mgr = Manager(); + if (!Send__delete__(this)) { + return IPC_FAIL_NO_REASON(mgr); + } + + return IPC_OK(); +} + +void WebrtcTCPSocketParent::ActorDestroy(ActorDestroyReason aWhy) { + LOG(("WebrtcTCPSocketParent::ActorDestroy %p for %d\n", this, aWhy)); + + CleanupChannel(); +} + +WebrtcTCPSocketParent::WebrtcTCPSocketParent(const Maybe<dom::TabId>& aTabId) { + MOZ_COUNT_CTOR(WebrtcTCPSocketParent); + + LOG(("WebrtcTCPSocketParent::WebrtcTCPSocketParent %p\n", this)); + + mChannel = new WebrtcTCPSocket(this); + if (aTabId.isSome()) { + mChannel->SetTabId(*aTabId); + } +} + +WebrtcTCPSocketParent::~WebrtcTCPSocketParent() { + MOZ_COUNT_DTOR(WebrtcTCPSocketParent); + + LOG(("WebrtcTCPSocketParent::~WebrtcTCPSocketParent %p\n", this)); + + CleanupChannel(); +} + +// WebrtcTCPSocketCallback +void WebrtcTCPSocketParent::OnClose(nsresult aReason) { + LOG(("WebrtcTCPSocketParent::OnClose %p\n", this)); + + if (mChannel) { + Unused << SendOnClose(aReason); + } + + CleanupChannel(); +} + +void WebrtcTCPSocketParent::OnRead(nsTArray<uint8_t>&& aReadData) { + LOG(("WebrtcTCPSocketParent::OnRead %p %zu\n", this, aReadData.Length())); + + if (mChannel && !SendOnRead(std::move(aReadData))) { + CleanupChannel(); + } +} + +void WebrtcTCPSocketParent::OnConnected(const nsACString& aProxyType) { + LOG(("WebrtcTCPSocketParent::OnConnected %p\n", this)); + + if (mChannel && !SendOnConnected(aProxyType)) { + CleanupChannel(); + } +} + +void WebrtcTCPSocketParent::CleanupChannel() { + if (mChannel) { + mChannel->Close(); + mChannel = nullptr; + } +} + +} // namespace mozilla::net diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h new file mode 100644 index 0000000000..df4462609d --- /dev/null +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#ifndef mozilla_net_WebrtcTCPSocketParent_h +#define mozilla_net_WebrtcTCPSocketParent_h + +#include "mozilla/net/PWebrtcTCPSocketParent.h" + +#include "WebrtcTCPSocketCallback.h" + +class nsIAuthPromptProvider; + +namespace mozilla::net { + +class WebrtcTCPSocket; + +class WebrtcTCPSocketParent : public PWebrtcTCPSocketParent, + public WebrtcTCPSocketCallback { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketParent, override) + + mozilla::ipc::IPCResult RecvAsyncOpen( + const nsACString& aHost, const int& aPort, + const nsACString& aLocalAddress, const int& aLocalPort, + const bool& aUseTls, + const Maybe<WebrtcProxyConfig>& aProxyConfig) override; + + mozilla::ipc::IPCResult RecvWrite(nsTArray<uint8_t>&& aWriteData) override; + + mozilla::ipc::IPCResult RecvClose() override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + + explicit WebrtcTCPSocketParent(const Maybe<dom::TabId>& aTabId); + + // WebrtcTCPSocketCallback + void OnClose(nsresult aReason) override; + void OnConnected(const nsACString& aProxyType) override; + void OnRead(nsTArray<uint8_t>&& bytes) override; + + void AddIPDLReference() { AddRef(); } + void ReleaseIPDLReference() { Release(); } + + protected: + virtual ~WebrtcTCPSocketParent(); + + private: + void CleanupChannel(); + + // Indicates that IPC is open. + RefPtr<WebrtcTCPSocket> mChannel; +}; + +} // namespace mozilla::net + +#endif // mozilla_net_WebrtcTCPSocketParent_h diff --git a/dom/media/webrtc/transport/ipc/moz.build b/dom/media/webrtc/transport/ipc/moz.build new file mode 100644 index 0000000000..a2a72bb624 --- /dev/null +++ b/dom/media/webrtc/transport/ipc/moz.build @@ -0,0 +1,54 @@ +# 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/. + +EXPORTS.mozilla.net += [ + "NrIceStunAddrMessageUtils.h", + "PStunAddrsParams.h", + "StunAddrsRequestChild.h", + "StunAddrsRequestParent.h", + "WebrtcTCPSocket.h", + "WebrtcTCPSocketCallback.h", + "WebrtcTCPSocketChild.h", + "WebrtcTCPSocketParent.h", +] + +UNIFIED_SOURCES += [ + "StunAddrsRequestChild.cpp", + "StunAddrsRequestParent.cpp", + "WebrtcTCPSocket.cpp", + "WebrtcTCPSocketChild.cpp", + "WebrtcTCPSocketLog.cpp", + "WebrtcTCPSocketParent.cpp", +] + +IPDL_SOURCES += [ + "PStunAddrsRequest.ipdl", + "PWebrtcTCPSocket.ipdl", + "WebrtcProxyConfig.ipdlh", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" + +DEFINES["R_DEFINED_INT2"] = "int16_t" +DEFINES["R_DEFINED_UINT2"] = "uint16_t" +DEFINES["R_DEFINED_INT4"] = "int32_t" +DEFINES["R_DEFINED_UINT4"] = "uint32_t" +# These are defined to avoid a conflict between typedefs in winsock2.h and +# r_types.h. This is safe because these types are unused by the code here, +# but still deeply unfortunate. There is similar code in the win32 version of +# csi_platform.h, but that trick does not work here, even if that file is +# directly included. +DEFINES["R_DEFINED_INT8"] = "int8_t" +DEFINES["R_DEFINED_UINT8"] = "uint8_t" + +LOCAL_INCLUDES += [ + "/dom/media/webrtc/jsapi", + "/dom/media/webrtc/transport/third_party/nICEr/src/net", + "/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr", + "/media/webrtc", + "/netwerk/base", + "/netwerk/protocol/http", +] diff --git a/dom/media/webrtc/transport/logging.h b/dom/media/webrtc/transport/logging.h new file mode 100644 index 0000000000..98cbd13819 --- /dev/null +++ b/dom/media/webrtc/transport/logging.h @@ -0,0 +1,65 @@ +/* -*- 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 + +#ifndef logging_h__ +#define logging_h__ + +#include <sstream> +#include "mozilla/Logging.h" + +#ifdef MOZILLA_INTERNAL_API + +# define ML_ERROR mozilla::LogLevel::Error +# define ML_WARNING mozilla::LogLevel::Warning +# define ML_NOTICE mozilla::LogLevel::Info +# define ML_INFO mozilla::LogLevel::Debug +# define ML_DEBUG mozilla::LogLevel::Verbose + +# define MOZ_MTLOG_MODULE(n) \ + static mozilla::LogModule* getLogModule() { \ + static mozilla::LazyLogModule log(n); \ + return static_cast<mozilla::LogModule*>(log); \ + } + +# define MOZ_MTLOG(level, b) \ + do { \ + if (MOZ_LOG_TEST(getLogModule(), level)) { \ + std::stringstream str; \ + str << b; \ + MOZ_LOG(getLogModule(), level, ("%s", str.str().c_str())); \ + } \ + } while (0) +#else +// When building mtransport outside of XUL, for example in stand-alone gtests, +// PR_Logging needs to be used instead of mozilla logging. + +# include "prlog.h" + +# define ML_ERROR PR_LOG_ERROR +# define ML_WARNING PR_LOG_WARNING +# define ML_NOTICE PR_LOG_INFO +# define ML_INFO PR_LOG_DEBUG +# define ML_DEBUG PR_LOG_VERBOSE + +# define MOZ_MTLOG_MODULE(n) \ + static PRLogModuleInfo* getLogModule() { \ + static PRLogModuleInfo* log; \ + if (!log) log = PR_NewLogModule(n); \ + return log; \ + } + +# define MOZ_MTLOG(level, b) \ + do { \ + if (PR_LOG_TEST(getLogModule(), level)) { \ + std::stringstream str; \ + str << b; \ + PR_LOG(getLogModule(), level, ("%s", str.str().c_str())); \ + } \ + } while (0) +#endif // MOZILLA_INTERNAL_API +#endif // logging_h__ diff --git a/dom/media/webrtc/transport/m_cpp_utils.h b/dom/media/webrtc/transport/m_cpp_utils.h new file mode 100644 index 0000000000..a1469f798a --- /dev/null +++ b/dom/media/webrtc/transport/m_cpp_utils.h @@ -0,0 +1,25 @@ +/* -*- 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 + +#ifndef m_cpp_utils_h__ +#define m_cpp_utils_h__ + +#include "mozilla/Attributes.h" + +namespace mozilla { + +#define DISALLOW_ASSIGNMENT(T) void operator=(const T& other) = delete + +#define DISALLOW_COPY(T) T(const T& other) = delete + +#define DISALLOW_COPY_ASSIGN(T) \ + DISALLOW_COPY(T); \ + DISALLOW_ASSIGNMENT(T) + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/mdns_service/Cargo.toml b/dom/media/webrtc/transport/mdns_service/Cargo.toml new file mode 100644 index 0000000000..ec7e182d4b --- /dev/null +++ b/dom/media/webrtc/transport/mdns_service/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "mdns_service" +version = "0.1.1" +authors = ["Dan Minor <dminor@mozilla.com>"] +edition = "2018" +license = "MPL-2.0" + +[dependencies] +byteorder = "1.3.1" +dns-parser = "0.8.0" +gecko-profiler = { path = "../../../../../tools/profiler/rust-api" } +log = "0.4" +socket2 = { version = "0.4", features = ["all"] } +uuid = { version = "1.0", features = ["v4"] } diff --git a/dom/media/webrtc/transport/mdns_service/mdns_service.h b/dom/media/webrtc/transport/mdns_service/mdns_service.h new file mode 100644 index 0000000000..5e8c252ebc --- /dev/null +++ b/dom/media/webrtc/transport/mdns_service/mdns_service.h @@ -0,0 +1,28 @@ +/* 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/. */ + +#include <cstdarg> +#include <cstdint> +#include <cstdlib> +#include <new> + +struct MDNSService; + +extern "C" { + +void mdns_service_register_hostname(MDNSService* serv, const char* hostname, + const char* addr); + +MDNSService* mdns_service_start(const char* ifaddr); + +void mdns_service_stop(MDNSService* serv); + +void mdns_service_query_hostname( + MDNSService* serv, void* data, + void (*resolved)(void* data, const char* hostname, const char* address), + void (*timedout)(void* data, const char* hostname), const char* hostname); + +void mdns_service_unregister_hostname(MDNSService* serv, const char* hostname); + +} // extern "C" diff --git a/dom/media/webrtc/transport/mdns_service/src/lib.rs b/dom/media/webrtc/transport/mdns_service/src/lib.rs new file mode 100644 index 0000000000..fbd07b45f8 --- /dev/null +++ b/dom/media/webrtc/transport/mdns_service/src/lib.rs @@ -0,0 +1,843 @@ +/* -*- Mode: rust; rust-indent-offset: 2 -*- */ +/* 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/. */ +use byteorder::{BigEndian, WriteBytesExt}; +use socket2::{Domain, Socket, Type}; +use std::collections::HashMap; +use std::collections::LinkedList; +use std::ffi::{c_void, CStr, CString}; +use std::io; +use std::net; +use std::os::raw::c_char; +use std::sync::mpsc::channel; +use std::thread; +use std::time; +use uuid::Uuid; + +#[macro_use] +extern crate log; + +struct Callback { + data: *const c_void, + resolved: unsafe extern "C" fn(*const c_void, *const c_char, *const c_char), + timedout: unsafe extern "C" fn(*const c_void, *const c_char), +} + +unsafe impl Send for Callback {} + +fn hostname_resolved(callback: &Callback, hostname: &str, addr: &str) { + if let Ok(hostname) = CString::new(hostname) { + if let Ok(addr) = CString::new(addr) { + unsafe { + (callback.resolved)(callback.data, hostname.as_ptr(), addr.as_ptr()); + } + } + } +} + +fn hostname_timedout(callback: &Callback, hostname: &str) { + if let Ok(hostname) = CString::new(hostname) { + unsafe { + (callback.timedout)(callback.data, hostname.as_ptr()); + } + } +} + +// This code is derived from code for creating questions in the dns-parser +// crate. It would be nice to upstream this, or something similar. +fn create_answer(id: u16, answers: &[(String, &[u8])]) -> Result<Vec<u8>, io::Error> { + let mut buf = Vec::with_capacity(512); + let head = dns_parser::Header { + id, + query: false, + opcode: dns_parser::Opcode::StandardQuery, + authoritative: true, + truncated: false, + recursion_desired: false, + recursion_available: false, + authenticated_data: false, + checking_disabled: false, + response_code: dns_parser::ResponseCode::NoError, + questions: 0, + answers: answers.len() as u16, + nameservers: 0, + additional: 0, + }; + + buf.extend([0u8; 12].iter()); + head.write(&mut buf[..12]); + + for (name, addr) in answers { + for part in name.split('.') { + if part.len() > 62 { + return Err(io::Error::new( + io::ErrorKind::Other, + "Name part length too long", + )); + } + let ln = part.len() as u8; + buf.push(ln); + buf.extend(part.as_bytes()); + } + buf.push(0); + + if addr.len() == 4 { + buf.write_u16::<BigEndian>(dns_parser::Type::A as u16)?; + } else { + buf.write_u16::<BigEndian>(dns_parser::Type::AAAA as u16)?; + } + // set cache flush bit + buf.write_u16::<BigEndian>(dns_parser::Class::IN as u16 | (0x1 << 15))?; + buf.write_u32::<BigEndian>(120)?; + buf.write_u16::<BigEndian>(addr.len() as u16)?; + buf.extend(*addr); + } + + Ok(buf) +} + +fn create_query(id: u16, queries: &[String]) -> Result<Vec<u8>, io::Error> { + let mut buf = Vec::with_capacity(512); + let head = dns_parser::Header { + id, + query: true, + opcode: dns_parser::Opcode::StandardQuery, + authoritative: false, + truncated: false, + recursion_desired: false, + recursion_available: false, + authenticated_data: false, + checking_disabled: false, + response_code: dns_parser::ResponseCode::NoError, + questions: queries.len() as u16, + answers: 0, + nameservers: 0, + additional: 0, + }; + + buf.extend([0u8; 12].iter()); + head.write(&mut buf[..12]); + + for name in queries { + for part in name.split('.') { + assert!(part.len() < 63); + let ln = part.len() as u8; + buf.push(ln); + buf.extend(part.as_bytes()); + } + buf.push(0); + + buf.write_u16::<BigEndian>(dns_parser::QueryType::A as u16)?; + buf.write_u16::<BigEndian>(dns_parser::QueryClass::IN as u16)?; + } + + Ok(buf) +} + +fn handle_queries( + socket: &std::net::UdpSocket, + mdns_addr: &std::net::SocketAddr, + pending_queries: &mut HashMap<String, Query>, + unsent_queries: &mut LinkedList<Query>, +) { + if pending_queries.len() < 50 { + let mut queries: Vec<Query> = Vec::new(); + while queries.len() < 5 && !unsent_queries.is_empty() { + if let Some(query) = unsent_queries.pop_front() { + if !pending_queries.contains_key(&query.hostname) { + queries.push(query); + } + } + } + if !queries.is_empty() { + let query_hostnames: Vec<String> = + queries.iter().map(|q| q.hostname.to_string()).collect(); + + if let Ok(buf) = create_query(0, &query_hostnames) { + match socket.send_to(&buf, &mdns_addr) { + Ok(_) => { + for query in queries { + pending_queries.insert(query.hostname.to_string(), query); + } + } + Err(err) => { + warn!("Sending mDNS query failed: {}", err); + if err.kind() != io::ErrorKind::PermissionDenied { + for query in queries { + unsent_queries.push_back(query); + } + } else { + for query in queries { + hostname_timedout(&query.callback, &query.hostname); + } + } + } + } + } + } + } + + let now = time::Instant::now(); + let expired: Vec<String> = pending_queries + .iter() + .filter(|(_, query)| now.duration_since(query.timestamp).as_secs() >= 3) + .map(|(hostname, _)| hostname.to_string()) + .collect(); + for hostname in expired { + if let Some(mut query) = pending_queries.remove(&hostname) { + query.attempts += 1; + if query.attempts < 3 { + query.timestamp = now; + unsent_queries.push_back(query); + } else { + hostname_timedout(&query.callback, &hostname); + } + } + } +} + +fn handle_mdns_socket( + socket: &std::net::UdpSocket, + mdns_addr: &std::net::SocketAddr, + mut buffer: &mut [u8], + hosts: &mut HashMap<String, Vec<u8>>, + pending_queries: &mut HashMap<String, Query>, +) -> bool { + // Record a simple marker to see how often this is called. + gecko_profiler::add_untyped_marker( + "handle_mdns_socket", + gecko_profiler::gecko_profiler_category!(Network), + Default::default(), + ); + + match socket.recv_from(&mut buffer) { + Ok((amt, _)) => { + if amt > 0 { + let buffer = &buffer[0..amt]; + match dns_parser::Packet::parse(&buffer) { + Ok(parsed) => { + let mut answers: Vec<(String, &[u8])> = Vec::new(); + + // If a packet contains both both questions and + // answers, the questions should be ignored. + if parsed.answers.is_empty() { + parsed + .questions + .iter() + .filter(|question| question.qtype == dns_parser::QueryType::A) + .for_each(|question| { + let qname = question.qname.to_string(); + trace!("mDNS question: {} {:?}", qname, question.qtype); + if let Some(octets) = hosts.get(&qname) { + trace!("Sending mDNS answer for {}: {:?}", qname, octets); + answers.push((qname, &octets)); + } + }); + } + for answer in parsed.answers { + let hostname = answer.name.to_string(); + match pending_queries.get(&hostname) { + Some(query) => { + match answer.data { + dns_parser::RData::A(dns_parser::rdata::a::Record( + addr, + )) => { + let addr = addr.to_string(); + trace!("mDNS response: {} {}", hostname, addr); + hostname_resolved(&query.callback, &hostname, &addr); + } + dns_parser::RData::AAAA( + dns_parser::rdata::aaaa::Record(addr), + ) => { + let addr = addr.to_string(); + trace!("mDNS response: {} {}", hostname, addr); + hostname_resolved(&query.callback, &hostname, &addr); + } + _ => {} + } + pending_queries.remove(&hostname); + } + None => { + continue; + } + } + } + // TODO: If we did not answer every query in this + // question, we should wait for a random amount of time + // so as to not collide with someone else responding to + // this query. + if !answers.is_empty() { + if let Ok(buf) = create_answer(parsed.header.id, &answers) { + if let Err(err) = socket.send_to(&buf, &mdns_addr) { + warn!("Sending mDNS answer failed: {}", err); + } + } + } + } + Err(err) => { + warn!("Could not parse mDNS packet: {}", err); + } + } + } + } + Err(err) => { + if err.kind() != io::ErrorKind::Interrupted + && err.kind() != io::ErrorKind::TimedOut + && err.kind() != io::ErrorKind::WouldBlock + { + error!("Socket error: {}", err); + return false; + } + } + } + + true +} + +fn validate_hostname(hostname: &str) -> bool { + match hostname.find(".local") { + Some(index) => match hostname.get(0..index) { + Some(uuid) => match uuid.get(0..36) { + Some(initial) => match Uuid::parse_str(initial) { + Ok(_) => { + // Oddly enough, Safari does not generate valid UUIDs, + // the last part sometimes contains more than 12 digits. + match uuid.get(36..) { + Some(trailing) => { + for c in trailing.chars() { + if !c.is_ascii_hexdigit() { + return false; + } + } + true + } + None => true, + } + } + Err(_) => false, + }, + None => false, + }, + None => false, + }, + None => false, + } +} + +enum ServiceControl { + Register { + hostname: String, + address: String, + }, + Query { + callback: Callback, + hostname: String, + }, + Unregister { + hostname: String, + }, + Stop, +} + +struct Query { + hostname: String, + callback: Callback, + timestamp: time::Instant, + attempts: i32, +} + +impl Query { + fn new(hostname: &str, callback: Callback) -> Query { + Query { + hostname: hostname.to_string(), + callback, + timestamp: time::Instant::now(), + attempts: 0, + } + } +} + +pub struct MDNSService { + handle: Option<std::thread::JoinHandle<()>>, + sender: Option<std::sync::mpsc::Sender<ServiceControl>>, +} + +impl MDNSService { + fn register_hostname(&mut self, hostname: &str, address: &str) { + if let Some(sender) = &self.sender { + if let Err(err) = sender.send(ServiceControl::Register { + hostname: hostname.to_string(), + address: address.to_string(), + }) { + warn!( + "Could not send register hostname {} message: {}", + hostname, err + ); + } + } + } + + fn query_hostname(&mut self, callback: Callback, hostname: &str) { + if let Some(sender) = &self.sender { + if let Err(err) = sender.send(ServiceControl::Query { + callback, + hostname: hostname.to_string(), + }) { + warn!( + "Could not send query hostname {} message: {}", + hostname, err + ); + } + } + } + + fn unregister_hostname(&mut self, hostname: &str) { + if let Some(sender) = &self.sender { + if let Err(err) = sender.send(ServiceControl::Unregister { + hostname: hostname.to_string(), + }) { + warn!( + "Could not send unregister hostname {} message: {}", + hostname, err + ); + } + } + } + + fn start(&mut self, addrs: Vec<std::net::Ipv4Addr>) -> io::Result<()> { + let (sender, receiver) = channel(); + self.sender = Some(sender); + + let mdns_addr = std::net::Ipv4Addr::new(224, 0, 0, 251); + let port = 5353; + + let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?; + socket.set_reuse_address(true)?; + + #[cfg(not(target_os = "windows"))] + socket.set_reuse_port(true)?; + socket.bind(&socket2::SockAddr::from(std::net::SocketAddr::from(( + [0, 0, 0, 0], + port, + ))))?; + + let socket = std::net::UdpSocket::from(socket); + socket.set_multicast_loop_v4(true)?; + socket.set_read_timeout(Some(time::Duration::from_millis(1)))?; + socket.set_write_timeout(Some(time::Duration::from_millis(1)))?; + for addr in addrs { + if let Err(err) = socket.join_multicast_v4(&mdns_addr, &addr) { + warn!( + "Could not join multicast group on interface: {:?}: {}", + addr, err + ); + } + } + + let thread_name = "mdns_service"; + let builder = thread::Builder::new().name(thread_name.into()); + self.handle = Some(builder.spawn(move || { + gecko_profiler::register_thread(thread_name); + let mdns_addr = std::net::SocketAddr::from(([224, 0, 0, 251], port)); + let mut buffer: [u8; 9_000] = [0; 9_000]; + let mut hosts = HashMap::new(); + let mut unsent_queries = LinkedList::new(); + let mut pending_queries = HashMap::new(); + loop { + match receiver.try_recv() { + Ok(msg) => match msg { + ServiceControl::Register { hostname, address } => { + if !validate_hostname(&hostname) { + warn!("Not registering invalid hostname: {}", hostname); + continue; + } + trace!("Registering {} for: {}", hostname, address); + match address.parse().and_then(|ip| { + Ok(match ip { + net::IpAddr::V4(ip) => ip.octets().to_vec(), + net::IpAddr::V6(ip) => ip.octets().to_vec(), + }) + }) { + Ok(octets) => { + let mut v = Vec::new(); + v.extend(octets); + hosts.insert(hostname, v); + } + Err(err) => { + warn!( + "Could not parse address for {}: {}: {}", + hostname, address, err + ); + } + } + } + ServiceControl::Query { callback, hostname } => { + trace!("Querying {}", hostname); + if !validate_hostname(&hostname) { + warn!("Not sending mDNS query for invalid hostname: {}", hostname); + continue; + } + unsent_queries.push_back(Query::new(&hostname, callback)); + } + ServiceControl::Unregister { hostname } => { + trace!("Unregistering {}", hostname); + hosts.remove(&hostname); + } + ServiceControl::Stop => { + trace!("Stopping"); + break; + } + }, + Err(std::sync::mpsc::TryRecvError::Disconnected) => { + break; + } + Err(std::sync::mpsc::TryRecvError::Empty) => {} + } + + handle_queries( + &socket, + &mdns_addr, + &mut pending_queries, + &mut unsent_queries, + ); + + if !handle_mdns_socket( + &socket, + &mdns_addr, + &mut buffer, + &mut hosts, + &mut pending_queries, + ) { + break; + } + } + gecko_profiler::unregister_thread(); + })?); + + Ok(()) + } + + fn stop(self) { + if let Some(sender) = self.sender { + if let Err(err) = sender.send(ServiceControl::Stop) { + warn!("Could not stop mDNS Service: {}", err); + } + if let Some(handle) = self.handle { + if handle.join().is_err() { + error!("Error on thread join"); + } + } + } + } + + fn new() -> MDNSService { + MDNSService { + handle: None, + sender: None, + } + } +} + +/// # Safety +/// +/// This function must only be called with a valid MDNSService pointer. +/// This hostname and address arguments must be zero terminated strings. +#[no_mangle] +pub unsafe extern "C" fn mdns_service_register_hostname( + serv: *mut MDNSService, + hostname: *const c_char, + address: *const c_char, +) { + assert!(!serv.is_null()); + assert!(!hostname.is_null()); + assert!(!address.is_null()); + let hostname = CStr::from_ptr(hostname).to_string_lossy(); + let address = CStr::from_ptr(address).to_string_lossy(); + (*serv).register_hostname(&hostname, &address); +} + +/// # Safety +/// +/// This ifaddrs argument must be a zero terminated string. +#[no_mangle] +pub unsafe extern "C" fn mdns_service_start(ifaddrs: *const c_char) -> *mut MDNSService { + assert!(!ifaddrs.is_null()); + let mut r = Box::new(MDNSService::new()); + let ifaddrs = CStr::from_ptr(ifaddrs).to_string_lossy(); + let addrs: Vec<std::net::Ipv4Addr> = + ifaddrs.split(';').filter_map(|x| x.parse().ok()).collect(); + + if addrs.is_empty() { + warn!("Could not parse interface addresses from: {}", ifaddrs); + } else if let Err(err) = r.start(addrs) { + warn!("Could not start mDNS Service: {}", err); + } + + Box::into_raw(r) +} + +/// # Safety +/// +/// This function must only be called with a valid MDNSService pointer. +#[no_mangle] +pub unsafe extern "C" fn mdns_service_stop(serv: *mut MDNSService) { + assert!(!serv.is_null()); + let boxed = Box::from_raw(serv); + boxed.stop(); +} + +/// # Safety +/// +/// This function must only be called with a valid MDNSService pointer. +/// The data argument will be passed back into the resolved and timedout +/// functions. The object it points to must not be freed until the MDNSService +/// has stopped. +#[no_mangle] +pub unsafe extern "C" fn mdns_service_query_hostname( + serv: *mut MDNSService, + data: *const c_void, + resolved: unsafe extern "C" fn(*const c_void, *const c_char, *const c_char), + timedout: unsafe extern "C" fn(*const c_void, *const c_char), + hostname: *const c_char, +) { + assert!(!serv.is_null()); + assert!(!data.is_null()); + assert!(!hostname.is_null()); + let hostname = CStr::from_ptr(hostname).to_string_lossy(); + let callback = Callback { + data, + resolved, + timedout, + }; + (*serv).query_hostname(callback, &hostname); +} + +/// # Safety +/// +/// This function must only be called with a valid MDNSService pointer. +/// This function should only be called once per hostname. +#[no_mangle] +pub unsafe extern "C" fn mdns_service_unregister_hostname( + serv: *mut MDNSService, + hostname: *const c_char, +) { + assert!(!serv.is_null()); + assert!(!hostname.is_null()); + let hostname = CStr::from_ptr(hostname).to_string_lossy(); + (*serv).unregister_hostname(&hostname); +} + +#[cfg(test)] +mod tests { + use crate::create_query; + use crate::validate_hostname; + use crate::Callback; + use crate::MDNSService; + use socket2::{Domain, Socket, Type}; + use std::collections::HashSet; + use std::ffi::c_void; + use std::io; + use std::iter::FromIterator; + use std::os::raw::c_char; + use std::thread; + use std::time; + use uuid::Uuid; + + #[no_mangle] + pub unsafe extern "C" fn mdns_service_resolved( + _: *const c_void, + _: *const c_char, + _: *const c_char, + ) -> () { + } + + #[no_mangle] + pub unsafe extern "C" fn mdns_service_timedout(_: *const c_void, _: *const c_char) -> () {} + + fn listen_until(addr: &std::net::Ipv4Addr, stop: u64) -> thread::JoinHandle<Vec<String>> { + let port = 5353; + + let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap(); + socket.set_reuse_address(true).unwrap(); + + #[cfg(not(target_os = "windows"))] + socket.set_reuse_port(true).unwrap(); + socket + .bind(&socket2::SockAddr::from(std::net::SocketAddr::from(( + [0, 0, 0, 0], + port, + )))) + .unwrap(); + + let socket = std::net::UdpSocket::from(socket); + socket.set_multicast_loop_v4(true).unwrap(); + socket + .set_read_timeout(Some(time::Duration::from_millis(10))) + .unwrap(); + socket + .set_write_timeout(Some(time::Duration::from_millis(10))) + .unwrap(); + socket + .join_multicast_v4(&std::net::Ipv4Addr::new(224, 0, 0, 251), &addr) + .unwrap(); + + let mut buffer: [u8; 9_000] = [0; 9_000]; + thread::spawn(move || { + let start = time::Instant::now(); + let mut questions = Vec::new(); + while time::Instant::now().duration_since(start).as_secs() < stop { + match socket.recv_from(&mut buffer) { + Ok((amt, _)) => { + if amt > 0 { + let buffer = &buffer[0..amt]; + match dns_parser::Packet::parse(&buffer) { + Ok(parsed) => { + parsed + .questions + .iter() + .filter(|question| { + question.qtype == dns_parser::QueryType::A + }) + .for_each(|question| { + let qname = question.qname.to_string(); + questions.push(qname); + }); + } + Err(err) => { + warn!("Could not parse mDNS packet: {}", err); + } + } + } + } + Err(err) => { + if err.kind() != io::ErrorKind::WouldBlock + && err.kind() != io::ErrorKind::TimedOut + { + error!("Socket error: {}", err); + break; + } + } + } + } + questions + }) + } + + #[test] + fn test_validate_hostname() { + assert_eq!( + validate_hostname("e17f08d4-689a-4df6-ba31-35bb9f041100.local"), + true + ); + assert_eq!( + validate_hostname("62240723-ae6d-4f6a-99b8-94a233e3f84a2.local"), + true + ); + assert_eq!( + validate_hostname("62240723-ae6d-4f6a-99b8.94e3f84a2.local"), + false + ); + assert_eq!(validate_hostname("hi there"), false); + } + + #[test] + fn start_stop() { + let mut service = MDNSService::new(); + let addr = "127.0.0.1".parse().unwrap(); + service.start(vec![addr]).unwrap(); + service.stop(); + } + + #[test] + fn simple_query() { + let mut service = MDNSService::new(); + let addr = "127.0.0.1".parse().unwrap(); + let handle = listen_until(&addr, 1); + + service.start(vec![addr]).unwrap(); + + let callback = Callback { + data: 0 as *const c_void, + resolved: mdns_service_resolved, + timedout: mdns_service_timedout, + }; + let hostname = Uuid::new_v4().as_hyphenated().to_string() + ".local"; + service.query_hostname(callback, &hostname); + service.stop(); + let questions = handle.join().unwrap(); + assert!(questions.contains(&hostname)); + } + + #[test] + fn rate_limited_query() { + let mut service = MDNSService::new(); + let addr = "127.0.0.1".parse().unwrap(); + let handle = listen_until(&addr, 1); + + service.start(vec![addr]).unwrap(); + + let mut hostnames = HashSet::new(); + for _ in 0..100 { + let callback = Callback { + data: 0 as *const c_void, + resolved: mdns_service_resolved, + timedout: mdns_service_timedout, + }; + let hostname = Uuid::new_v4().as_hyphenated().to_string() + ".local"; + service.query_hostname(callback, &hostname); + hostnames.insert(hostname); + } + service.stop(); + let questions = HashSet::from_iter(handle.join().unwrap().iter().map(|x| x.to_string())); + let intersection: HashSet<&String> = questions.intersection(&hostnames).collect(); + assert_eq!(intersection.len(), 50); + } + + #[test] + fn repeat_failed_query() { + let mut service = MDNSService::new(); + let addr = "127.0.0.1".parse().unwrap(); + let handle = listen_until(&addr, 4); + + service.start(vec![addr]).unwrap(); + + let hostname = Uuid::new_v4().as_hyphenated().to_string() + ".local"; + let callback = Callback { + data: 0 as *const c_void, + resolved: mdns_service_resolved, + timedout: mdns_service_timedout, + }; + service.query_hostname(callback, &hostname); + thread::sleep(time::Duration::from_secs(4)); + service.stop(); + + let questions: Vec<String> = handle + .join() + .unwrap() + .iter() + .filter(|x| *x == &hostname) + .map(|x| x.to_string()) + .collect(); + assert_eq!(questions.len(), 2); + } + + #[test] + fn multiple_queries_in_a_single_packet() { + let mut hostnames: Vec<String> = Vec::new(); + for _ in 0..100 { + let hostname = Uuid::new_v4().as_hyphenated().to_string() + ".local"; + hostnames.push(hostname); + } + + match create_query(42, &hostnames) { + Ok(q) => match dns_parser::Packet::parse(&q) { + Ok(parsed) => { + assert_eq!(parsed.questions.len(), 100); + } + Err(_) => assert!(false), + }, + Err(_) => assert!(false), + } + } +} diff --git a/dom/media/webrtc/transport/mediapacket.cpp b/dom/media/webrtc/transport/mediapacket.cpp new file mode 100644 index 0000000000..38ab65ed2a --- /dev/null +++ b/dom/media/webrtc/transport/mediapacket.cpp @@ -0,0 +1,145 @@ +/* -*- 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/. */ + +#include "mediapacket.h" + +#include <cstring> +#include "ipc/IPCMessageUtils.h" + +namespace mozilla { + +void MediaPacket::Copy(const uint8_t* data, size_t len, size_t capacity) { + if (capacity < len) { + capacity = len; + } + data_.reset(new uint8_t[capacity]); + len_ = len; + capacity_ = capacity; + memcpy(data_.get(), data, len); +} + +MediaPacket MediaPacket::Clone() const { + MediaPacket newPacket; + newPacket.type_ = type_; + newPacket.sdp_level_ = sdp_level_; + newPacket.Copy(data_.get(), len_, capacity_); + return newPacket; +} + +void MediaPacket::Serialize(IPC::MessageWriter* aWriter) const { + aWriter->WriteUInt32(len_); + aWriter->WriteUInt32(capacity_); + if (len_) { + aWriter->WriteBytes(data_.get(), len_); + } + aWriter->WriteUInt32(encrypted_len_); + if (encrypted_len_) { + aWriter->WriteBytes(encrypted_data_.get(), encrypted_len_); + } + aWriter->WriteInt32(sdp_level_.isSome() ? *sdp_level_ : -1); + aWriter->WriteInt32(type_); +} + +bool MediaPacket::Deserialize(IPC::MessageReader* aReader) { + Reset(); + uint32_t len; + if (!aReader->ReadUInt32(&len)) { + return false; + } + uint32_t capacity; + if (!aReader->ReadUInt32(&capacity)) { + return false; + } + if (len) { + MOZ_RELEASE_ASSERT(capacity >= len); + UniquePtr<uint8_t[]> data(new uint8_t[capacity]); + if (!aReader->ReadBytesInto(data.get(), len)) { + return false; + } + data_ = std::move(data); + len_ = len; + capacity_ = capacity; + } + + if (!aReader->ReadUInt32(&len)) { + return false; + } + if (len) { + UniquePtr<uint8_t[]> data(new uint8_t[len]); + if (!aReader->ReadBytesInto(data.get(), len)) { + return false; + } + encrypted_data_ = std::move(data); + encrypted_len_ = len; + } + + int32_t sdp_level; + if (!aReader->ReadInt32(&sdp_level)) { + return false; + } + + if (sdp_level >= 0) { + sdp_level_ = Some(sdp_level); + } + + int32_t type; + if (!aReader->ReadInt32(&type)) { + return false; + } + type_ = static_cast<Type>(type); + return true; +} + +static bool IsRtp(const uint8_t* data, size_t len) { + if (len < 2) return false; + + // Check if this is a RTCP packet. Logic based on the types listed in + // media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc + + // Anything outside this range is RTP. + if ((data[1] < 192) || (data[1] > 207)) return true; + + if (data[1] == 192) // FIR + return false; + + if (data[1] == 193) // NACK, but could also be RTP. This makes us sad + return true; // but it's how webrtc.org behaves. + + if (data[1] == 194) return true; + + if (data[1] == 195) // IJ. + return false; + + if ((data[1] > 195) && (data[1] < 200)) // the > 195 is redundant + return true; + + if ((data[1] >= 200) && (data[1] <= 207)) // SR, RR, SDES, BYE, + return false; // APP, RTPFB, PSFB, XR + + MOZ_ASSERT(false); // Not reached, belt and suspenders. + return true; +} + +void MediaPacket::Categorize() { + SetType(MediaPacket::UNCLASSIFIED); + + if (!data_ || len_ < 4) { + return; + } + + if (data_[0] >= 20 && data_[0] <= 63) { + // DTLS per RFC 7983 + SetType(MediaPacket::DTLS); + } else if (data_[0] > 127 && data_[0] < 192) { + // RTP/RTCP per RFC 7983 + if (IsRtp(data_.get(), len_)) { + SetType(MediaPacket::SRTP); + } else { + SetType(MediaPacket::SRTCP); + } + } +} +} // namespace mozilla diff --git a/dom/media/webrtc/transport/mediapacket.h b/dom/media/webrtc/transport/mediapacket.h new file mode 100644 index 0000000000..563056c4a4 --- /dev/null +++ b/dom/media/webrtc/transport/mediapacket.h @@ -0,0 +1,117 @@ +/* -*- 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/. */ + +#ifndef mediapacket_h__ +#define mediapacket_h__ + +#include <cstddef> +#include <cstdint> +#include "mozilla/UniquePtr.h" +#include "mozilla/Maybe.h" + +class PickleIterator; + +namespace IPC { +class Message; +class MessageReader; +class MessageWriter; +} // namespace IPC + +namespace mozilla { + +// TODO: It might be worthwhile to teach this class how to "borrow" a buffer. +// That would make it easier to misuse, however, so maybe not worth it. +class MediaPacket { + public: + MediaPacket() = default; + MediaPacket(MediaPacket&& orig) = default; + MediaPacket& operator=(MediaPacket&& orig) = default; + + MediaPacket Clone() const; + + // Takes ownership of the passed-in data + void Take(UniquePtr<uint8_t[]>&& data, size_t len, size_t capacity = 0) { + data_ = std::move(data); + len_ = len; + if (capacity < len) { + capacity = len; + } + capacity_ = capacity; + } + + void Reset() { + data_.reset(); + len_ = 0; + capacity_ = 0; + encrypted_data_.reset(); + encrypted_len_ = 0; + sdp_level_.reset(); + } + + // Copies the passed-in data + void Copy(const uint8_t* data, size_t len, size_t capacity = 0); + + uint8_t* data() const { return data_.get(); } + + size_t len() const { return len_; } + + void SetLength(size_t length) { len_ = length; } + + size_t capacity() const { return capacity_; } + + Maybe<size_t>& sdp_level() { return sdp_level_; } + + void CopyDataToEncrypted() { + encrypted_data_ = std::move(data_); + encrypted_len_ = len_; + Copy(encrypted_data_.get(), len_); + } + + const uint8_t* encrypted_data() const { return encrypted_data_.get(); } + + size_t encrypted_len() const { return encrypted_len_; } + + enum Type { UNCLASSIFIED, SRTP, SRTCP, DTLS, RTP, RTCP, SCTP }; + + void Categorize(); + + void SetType(Type type) { type_ = type; } + + Type type() const { return type_; } + + void Serialize(IPC::MessageWriter* aWriter) const; + bool Deserialize(IPC::MessageReader* aReader); + + private: + UniquePtr<uint8_t[]> data_; + size_t len_ = 0; + size_t capacity_ = 0; + // Encrypted form of the data, if there is one. + UniquePtr<uint8_t[]> encrypted_data_; + size_t encrypted_len_ = 0; + // SDP level that this packet belongs to, if known. + Maybe<size_t> sdp_level_; + Type type_ = UNCLASSIFIED; +}; +} // namespace mozilla + +namespace IPC { +template <typename> +struct ParamTraits; + +template <> +struct ParamTraits<mozilla::MediaPacket> { + static void Write(MessageWriter* aWriter, + const mozilla::MediaPacket& aParam) { + aParam.Serialize(aWriter); + } + + static bool Read(MessageReader* aReader, mozilla::MediaPacket* aResult) { + return aResult->Deserialize(aReader); + } +}; +} // namespace IPC +#endif // mediapacket_h__ diff --git a/dom/media/webrtc/transport/moz.build b/dom/media/webrtc/transport/moz.build new file mode 100644 index 0000000000..fcf265e22f --- /dev/null +++ b/dom/media/webrtc/transport/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; 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/. + +with Files("**"): + BUG_COMPONENT = ("Core", "WebRTC: Networking") + +include("/ipc/chromium/chromium-config.mozbuild") + +DIRS += [ + "/dom/media/webrtc/transport/third_party", + "/dom/media/webrtc/transport/build", + "/dom/media/webrtc/transport/ipc", + "/dom/media/webrtc/transport/srtp", +] + +# Add libFuzzer configuration directives +include("/tools/fuzzing/libfuzzer-config.mozbuild") + +if CONFIG["FUZZING_INTERFACES"]: + TEST_DIRS += ["fuzztest"] diff --git a/dom/media/webrtc/transport/nr_socket_proxy_config.cpp b/dom/media/webrtc/transport/nr_socket_proxy_config.cpp new file mode 100644 index 0000000000..8c91a5d975 --- /dev/null +++ b/dom/media/webrtc/transport/nr_socket_proxy_config.cpp @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#include "nr_socket_proxy_config.h" + +#include "mozilla/net/WebrtcProxyConfig.h" + +namespace mozilla { + +class NrSocketProxyConfig::Private { + public: + net::WebrtcProxyConfig mProxyConfig; +}; + +NrSocketProxyConfig::NrSocketProxyConfig( + const net::WebrtcProxyConfig& aProxyConfig) + : mPrivate(new Private({aProxyConfig})) {} + +NrSocketProxyConfig::NrSocketProxyConfig(NrSocketProxyConfig&& aOrig) + : mPrivate(std::move(aOrig.mPrivate)) {} + +NrSocketProxyConfig::~NrSocketProxyConfig() = default; + +const net::WebrtcProxyConfig& NrSocketProxyConfig::GetConfig() const { + return mPrivate->mProxyConfig; +} + +bool NrSocketProxyConfig::GetForceProxy() const { + return mPrivate->mProxyConfig.forceProxy(); +} +} // namespace mozilla diff --git a/dom/media/webrtc/transport/nr_socket_proxy_config.h b/dom/media/webrtc/transport/nr_socket_proxy_config.h new file mode 100644 index 0000000000..55ef4fdcb1 --- /dev/null +++ b/dom/media/webrtc/transport/nr_socket_proxy_config.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +#ifndef nr_socket_proxy_config__ +#define nr_socket_proxy_config__ + +#include <memory> + +namespace mozilla { +namespace net { +class WebrtcProxyConfig; +} + +class NrSocketProxyConfig { + public: + explicit NrSocketProxyConfig(const net::WebrtcProxyConfig& aProxyConfig); + + // We need to actually write the default impl ourselves, because the compiler + // needs to know how to destroy mPrivate in case an exception is thrown, even + // though we disable exceptions in our build. + NrSocketProxyConfig(NrSocketProxyConfig&& aOrig); + + ~NrSocketProxyConfig(); + + const net::WebrtcProxyConfig& GetConfig() const; + bool GetForceProxy() const; + + private: + // dom::ProxyConfig includes stuff that conflicts with nICEr includes. + // Make it possible to include this header file without tripping over this + // problem. + class Private; + std::unique_ptr<Private> mPrivate; +}; + +} // namespace mozilla + +#endif // nr_socket_proxy_config__ diff --git a/dom/media/webrtc/transport/nr_socket_prsock.cpp b/dom/media/webrtc/transport/nr_socket_prsock.cpp new file mode 100644 index 0000000000..5681d52ecd --- /dev/null +++ b/dom/media/webrtc/transport/nr_socket_prsock.cpp @@ -0,0 +1,1785 @@ +/* -*- 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/. */ +/* +Modified version of nr_socket_local, adapted for NSPR +*/ + +/* 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 code from nICEr and nrappkit. + +nICEr copyright: + +Copyright (c) 2007, Adobe Systems, Incorporated +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. + + +nrappkit copyright: + + Copyright (C) 2001-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Thu Dec 20 20:14:49 2001 +*/ + +#include <csi_platform.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <assert.h> +#include <errno.h> +#include <string> + +#include "nspr.h" +#include "prerror.h" +#include "prio.h" +#include "prnetdb.h" + +#include "mozilla/net/DNS.h" +#include "nsCOMPtr.h" +#include "nsASocketHandler.h" +#include "nsISocketTransportService.h" +#include "nsNetCID.h" +#include "nsISupportsImpl.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsXPCOM.h" +#include "nsXULAppAPI.h" +#include "runnable_utils.h" +#include "mozilla/SyncRunnable.h" +#include "nsTArray.h" +#include "nsISocketFilter.h" +#include "nsDebug.h" +#include "nsNetUtil.h" + +#ifdef XP_WIN +# include "mozilla/WindowsVersion.h" +#endif + +#if defined(MOZILLA_INTERNAL_API) +// csi_platform.h deep in nrappkit defines LOG_INFO and LOG_WARNING +# ifdef LOG_INFO +# define LOG_TEMP_INFO LOG_INFO +# undef LOG_INFO +# endif +# ifdef LOG_WARNING +# define LOG_TEMP_WARNING LOG_WARNING +# undef LOG_WARNING +# endif +# if defined(LOG_DEBUG) +# define LOG_TEMP_DEBUG LOG_DEBUG +# undef LOG_DEBUG +# endif +# undef strlcpy + +# include "mozilla/dom/network/UDPSocketChild.h" + +# ifdef LOG_TEMP_INFO +# define LOG_INFO LOG_TEMP_INFO +# endif +# ifdef LOG_TEMP_WARNING +# define LOG_WARNING LOG_TEMP_WARNING +# endif + +# ifdef LOG_TEMP_DEBUG +# define LOG_DEBUG LOG_TEMP_DEBUG +# endif +# ifdef XP_WIN +# ifdef LOG_DEBUG +# undef LOG_DEBUG +# endif +// cloned from csi_platform.h. Win32 doesn't like how we hide symbols +# define LOG_DEBUG 7 +# endif +#endif + +extern "C" { +#include "nr_api.h" +#include "async_wait.h" +#include "nr_socket.h" +#include "nr_socket_local.h" +#include "stun_hint.h" +} +#include "nr_socket_prsock.h" +#include "simpletokenbucket.h" +#include "test_nr_socket.h" +#include "nr_socket_tcp.h" +#include "nr_socket_proxy_config.h" + +// Implement the nsISupports ref counting +namespace mozilla { + +#if defined(MOZILLA_INTERNAL_API) +class SingletonThreadHolder final { + private: + ~SingletonThreadHolder() { + r_log(LOG_GENERIC, LOG_DEBUG, "Deleting SingletonThreadHolder"); + if (mThread) { + // Likely a connection is somehow being held in CC or GC + NS_WARNING( + "SingletonThreads should be Released and shut down before exit!"); + mThread->Shutdown(); + mThread = nullptr; + } + } + + DISALLOW_COPY_ASSIGN(SingletonThreadHolder); + + public: + // Must be threadsafe for StaticRefPtr/ClearOnShutdown + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SingletonThreadHolder) + + explicit SingletonThreadHolder(const nsACString& aName) : mName(aName) { + mParentThread = NS_GetCurrentThread(); + } + + nsIThread* GetThread() { return mThread; } + + /* + * Keep track of how many instances are using a SingletonThreadHolder. + * When no one is using it, shut it down + */ + void AddUse() { + MOZ_ASSERT(mParentThread == NS_GetCurrentThread()); + MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt"); + nsrefcnt count = ++mUseCount; + if (count == 1) { + // idle -> in-use + nsresult rv = NS_NewNamedThread(mName, getter_AddRefs(mThread)); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mThread, + "Should successfully create mtransport I/O thread"); + r_log(LOG_GENERIC, LOG_DEBUG, "Created wrapped SingletonThread %p", + mThread.get()); + } + r_log(LOG_GENERIC, LOG_DEBUG, "AddUse_i: %lu", (unsigned long)count); + } + + void ReleaseUse() { + MOZ_ASSERT(mParentThread == NS_GetCurrentThread()); + nsrefcnt count = --mUseCount; + MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt"); + if (mThread && count == 0) { + // in-use -> idle -- no one forcing it to remain instantiated + r_log(LOG_GENERIC, LOG_DEBUG, "Shutting down wrapped SingletonThread %p", + mThread.get()); + mThread->AsyncShutdown(); + mThread = nullptr; + // It'd be nice to use a timer instead... But be careful of + // xpcom-shutdown-threads in that case + } + r_log(LOG_GENERIC, LOG_DEBUG, "ReleaseUse_i: %lu", (unsigned long)count); + } + + private: + nsCString mName; + nsAutoRefCnt mUseCount; + nsCOMPtr<nsIThread> mParentThread; + nsCOMPtr<nsIThread> mThread; +}; + +static StaticRefPtr<SingletonThreadHolder> sThread; + +static void ClearSingletonOnShutdown() { + // We expect everybody to have done ReleaseUse() at the latest during + // xpcom-shutdown-threads. So we need to live longer than that. + ClearOnShutdown(&sThread, ShutdownPhase::XPCOMShutdownFinal); +} +#endif + +static nsIThread* GetIOThreadAndAddUse_s() { + // Always runs on STS thread! +#if defined(MOZILLA_INTERNAL_API) + // We need to safely release this on shutdown to avoid leaks + if (!sThread) { + sThread = new SingletonThreadHolder("mtransport"_ns); + NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown)); + } + // Mark that we're using the shared thread and need it to stick around + sThread->AddUse(); + return sThread->GetThread(); +#else + static nsCOMPtr<nsIThread> sThread; + if (!sThread) { + (void)NS_NewNamedThread("mtransport", getter_AddRefs(sThread)); + } + return sThread; +#endif +} + +NrSocketIpc::NrSocketIpc(nsIEventTarget* aThread) : io_thread_(aThread) {} + +static TimeStamp nr_socket_short_term_violation_time; +static TimeStamp nr_socket_long_term_violation_time; + +TimeStamp NrSocketBase::short_term_violation_time() { + return nr_socket_short_term_violation_time; +} + +TimeStamp NrSocketBase::long_term_violation_time() { + return nr_socket_long_term_violation_time; +} + +// NrSocketBase implementation +// async_event APIs +int NrSocketBase::async_wait(int how, NR_async_cb cb, void* cb_arg, + char* function, int line) { + uint16_t flag; + + switch (how) { + case NR_ASYNC_WAIT_READ: + flag = PR_POLL_READ; + break; + case NR_ASYNC_WAIT_WRITE: + flag = PR_POLL_WRITE; + break; + default: + return R_BAD_ARGS; + } + + cbs_[how] = cb; + cb_args_[how] = cb_arg; + poll_flags_ |= flag; + + return 0; +} + +int NrSocketBase::cancel(int how) { + uint16_t flag; + + switch (how) { + case NR_ASYNC_WAIT_READ: + flag = PR_POLL_READ; + break; + case NR_ASYNC_WAIT_WRITE: + flag = PR_POLL_WRITE; + break; + default: + return R_BAD_ARGS; + } + + poll_flags_ &= ~flag; + + return 0; +} + +void NrSocketBase::fire_callback(int how) { + // This can't happen unless we are armed because we only set + // the flags if we are armed + MOZ_ASSERT(cbs_[how]); + + // Now cancel so that we need to be re-armed. Note that + // the re-arming probably happens in the callback we are + // about to fire. + cancel(how); + + cbs_[how](this, how, cb_args_[how]); +} + +// NrSocket implementation +NS_IMPL_QUERY_INTERFACE0(NrSocket) + +// The nsASocket callbacks +void NrSocket::OnSocketReady(PRFileDesc* fd, int16_t outflags) { + if (outflags & PR_POLL_READ & poll_flags()) fire_callback(NR_ASYNC_WAIT_READ); + if (outflags & PR_POLL_WRITE & poll_flags()) + fire_callback(NR_ASYNC_WAIT_WRITE); + if (outflags & (PR_POLL_ERR | PR_POLL_NVAL | PR_POLL_HUP)) + // TODO: Bug 946423: how do we notify the upper layers about this? + close(); +} + +void NrSocket::OnSocketDetached(PRFileDesc* fd) { + r_log(LOG_GENERIC, LOG_DEBUG, "Socket %p detached", fd); +} + +void NrSocket::IsLocal(bool* aIsLocal) { + // TODO(jesup): better check? Does it matter? (likely no) + *aIsLocal = false; +} + +// async_event APIs +int NrSocket::async_wait(int how, NR_async_cb cb, void* cb_arg, char* function, + int line) { + int r = NrSocketBase::async_wait(how, cb, cb_arg, function, line); + + if (!r) { + mPollFlags = poll_flags(); + } + + return r; +} + +int NrSocket::cancel(int how) { + int r = NrSocketBase::cancel(how); + + if (!r) { + mPollFlags = poll_flags(); + } + + return r; +} + +// Helper functions for addresses +static int nr_transport_addr_to_praddr(const nr_transport_addr* addr, + PRNetAddr* naddr) { + int _status; + + memset(naddr, 0, sizeof(*naddr)); + + switch (addr->protocol) { + case IPPROTO_TCP: + break; + case IPPROTO_UDP: + break; + default: + ABORT(R_BAD_ARGS); + } + + switch (addr->ip_version) { + case NR_IPV4: + naddr->inet.family = PR_AF_INET; + naddr->inet.port = addr->u.addr4.sin_port; + naddr->inet.ip = addr->u.addr4.sin_addr.s_addr; + break; + case NR_IPV6: + naddr->ipv6.family = PR_AF_INET6; + naddr->ipv6.port = addr->u.addr6.sin6_port; + naddr->ipv6.flowinfo = addr->u.addr6.sin6_flowinfo; + memcpy(&naddr->ipv6.ip, &addr->u.addr6.sin6_addr, sizeof(in6_addr)); + naddr->ipv6.scope_id = addr->u.addr6.sin6_scope_id; + break; + default: + ABORT(R_BAD_ARGS); + } + + _status = 0; +abort: + return (_status); +} + +// XXX schien@mozilla.com: copy from PRNetAddrToNetAddr, +// should be removed after fix the link error in signaling_unittests +static int praddr_to_netaddr(const PRNetAddr* prAddr, net::NetAddr* addr) { + int _status; + + switch (prAddr->raw.family) { + case PR_AF_INET: + addr->inet.family = AF_INET; + addr->inet.port = prAddr->inet.port; + addr->inet.ip = prAddr->inet.ip; + break; + case PR_AF_INET6: + addr->inet6.family = AF_INET6; + addr->inet6.port = prAddr->ipv6.port; + addr->inet6.flowinfo = prAddr->ipv6.flowinfo; + memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8)); + addr->inet6.scope_id = prAddr->ipv6.scope_id; + break; + default: + MOZ_ASSERT(false); + ABORT(R_BAD_ARGS); + } + + _status = 0; +abort: + return (_status); +} + +static int nr_transport_addr_to_netaddr(const nr_transport_addr* addr, + net::NetAddr* naddr) { + int r, _status; + PRNetAddr praddr; + + if ((r = nr_transport_addr_to_praddr(addr, &praddr))) { + ABORT(r); + } + + if ((r = praddr_to_netaddr(&praddr, naddr))) { + ABORT(r); + } + + _status = 0; +abort: + return (_status); +} + +int nr_netaddr_to_transport_addr(const net::NetAddr* netaddr, + 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), + protocol, addr))) + ABORT(r); + break; + case AF_INET6: + if ((r = nr_ip6_port_to_transport_addr((in6_addr*)&netaddr->inet6.ip.u8, + ntohs(netaddr->inet6.port), + protocol, addr))) + ABORT(r); + break; + 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 protocol, + int keep) { + int _status; + int r; + struct sockaddr_in ip4; + struct sockaddr_in6 ip6; + + 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, protocol, keep, + addr))) + ABORT(r); + break; + case PR_AF_INET6: + ip6.sin6_family = PF_INET6; + ip6.sin6_port = praddr->ipv6.port; + ip6.sin6_flowinfo = praddr->ipv6.flowinfo; + memcpy(&ip6.sin6_addr, &praddr->ipv6.ip, sizeof(in6_addr)); + ip6.sin6_scope_id = praddr->ipv6.scope_id; + if ((r = nr_sockaddr_to_transport_addr((sockaddr*)&ip6, protocol, keep, + addr))) + ABORT(r); + break; + default: + MOZ_ASSERT(false); + ABORT(R_BAD_ARGS); + } + + _status = 0; +abort: + return (_status); +} + +/* + * nr_transport_addr_get_addrstring_and_port + * convert nr_transport_addr to IP address string and port number + */ +int nr_transport_addr_get_addrstring_and_port(const nr_transport_addr* addr, + nsACString* host, int32_t* port) { + int r, _status; + char addr_string[64]; + + // We cannot directly use |nr_transport_addr.as_string| because it contains + // more than ip address, therefore, we need to explicity convert it + // from |nr_transport_addr_get_addrstring|. + if ((r = nr_transport_addr_get_addrstring(addr, addr_string, + sizeof(addr_string)))) { + ABORT(r); + } + + if ((r = nr_transport_addr_get_port(addr, port))) { + ABORT(r); + } + + *host = addr_string; + + _status = 0; +abort: + return (_status); +} + +// nr_socket APIs (as member functions) +int NrSocket::create(nr_transport_addr* addr) { + int r, _status; + + PRStatus status; + PRNetAddr naddr; + + nsresult rv; + nsCOMPtr<nsISocketTransportService> stservice = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (!NS_SUCCEEDED(rv)) { + ABORT(R_INTERNAL); + } + + if ((r = nr_transport_addr_to_praddr(addr, &naddr))) ABORT(r); + + switch (addr->protocol) { + case IPPROTO_UDP: + if (!(fd_ = PR_OpenUDPSocket(naddr.raw.family))) { + r_log(LOG_GENERIC, LOG_CRIT, + "Couldn't create UDP socket, " + "family=%d, err=%d", + naddr.raw.family, PR_GetError()); + ABORT(R_INTERNAL); + } +#ifdef XP_WIN + if (!mozilla::IsWin8OrLater()) { + // Increase default send and receive buffer sizes on <= Win7 to be able + // to receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K + // size) stream without losing packets. Manual testing showed that 100K + // buffer size was not enough and the packet loss dis-appeared with 256K + // buffer size. See bug 1252769 for future improvements of this. + PRSize min_buffer_size = 256 * 1024; + PRSocketOptionData opt_rcvbuf; + opt_rcvbuf.option = PR_SockOpt_RecvBufferSize; + if ((status = PR_GetSocketOption(fd_, &opt_rcvbuf)) == PR_SUCCESS) { + if (opt_rcvbuf.value.recv_buffer_size < min_buffer_size) { + opt_rcvbuf.value.recv_buffer_size = min_buffer_size; + if ((status = PR_SetSocketOption(fd_, &opt_rcvbuf)) != PR_SUCCESS) { + r_log(LOG_GENERIC, LOG_CRIT, + "Couldn't set socket receive buffer size: %d", status); + } + } else { + r_log(LOG_GENERIC, LOG_INFO, + "Socket receive buffer size is already: %d", + opt_rcvbuf.value.recv_buffer_size); + } + } else { + r_log(LOG_GENERIC, LOG_CRIT, + "Couldn't get socket receive buffer size: %d", status); + } + PRSocketOptionData opt_sndbuf; + opt_sndbuf.option = PR_SockOpt_SendBufferSize; + if ((status = PR_GetSocketOption(fd_, &opt_sndbuf)) == PR_SUCCESS) { + if (opt_sndbuf.value.recv_buffer_size < min_buffer_size) { + opt_sndbuf.value.recv_buffer_size = min_buffer_size; + if ((status = PR_SetSocketOption(fd_, &opt_sndbuf)) != PR_SUCCESS) { + r_log(LOG_GENERIC, LOG_CRIT, + "Couldn't set socket send buffer size: %d", status); + } + } else { + r_log(LOG_GENERIC, LOG_INFO, + "Socket send buffer size is already: %d", + opt_sndbuf.value.recv_buffer_size); + } + } else { + r_log(LOG_GENERIC, LOG_CRIT, + "Couldn't get socket send buffer size: %d", status); + } + } +#endif + break; + case IPPROTO_TCP: + // TODO: Rewrite this to use WebrtcTcpSocket. + // Also use the same logic for TLS. + if (my_addr_.fqdn[0] != '\0') ABORT(R_INTERNAL); + + if (!(fd_ = PR_OpenTCPSocket(naddr.raw.family))) { + r_log(LOG_GENERIC, LOG_CRIT, + "Couldn't create TCP socket, " + "family=%d, err=%d", + naddr.raw.family, PR_GetError()); + ABORT(R_INTERNAL); + } + // Set ReuseAddr for TCP sockets to enable having several + // sockets bound to same local IP and port + PRSocketOptionData opt_reuseaddr; + opt_reuseaddr.option = PR_SockOpt_Reuseaddr; + opt_reuseaddr.value.reuse_addr = PR_TRUE; + status = PR_SetSocketOption(fd_, &opt_reuseaddr); + if (status != PR_SUCCESS) { + r_log(LOG_GENERIC, LOG_CRIT, + "Couldn't set reuse addr socket option: %d", status); + ABORT(R_INTERNAL); + } + // And also set ReusePort for platforms supporting this socket option + PRSocketOptionData opt_reuseport; + opt_reuseport.option = PR_SockOpt_Reuseport; + opt_reuseport.value.reuse_port = PR_TRUE; + status = PR_SetSocketOption(fd_, &opt_reuseport); + if (status != PR_SUCCESS) { + if (PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) { + r_log(LOG_GENERIC, LOG_CRIT, + "Couldn't set reuse port socket option: %d", status); + ABORT(R_INTERNAL); + } + } + // Try to speedup packet delivery by disabling TCP Nagle + PRSocketOptionData opt_nodelay; + opt_nodelay.option = PR_SockOpt_NoDelay; + opt_nodelay.value.no_delay = PR_TRUE; + status = PR_SetSocketOption(fd_, &opt_nodelay); + if (status != PR_SUCCESS) { + r_log(LOG_GENERIC, LOG_WARNING, + "Couldn't set Nodelay socket option: %d", status); + } + 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); + } + + r_log(LOG_GENERIC, LOG_DEBUG, "Creating socket %p with addr %s", fd_, + addr->as_string); + nr_transport_addr_copy(&my_addr_, 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_, addr->protocol, 1))) + ABORT(r); + } + + // Set nonblocking + PRSocketOptionData opt_nonblock; + opt_nonblock.option = PR_SockOpt_Nonblocking; + opt_nonblock.value.non_blocking = PR_TRUE; + status = PR_SetSocketOption(fd_, &opt_nonblock); + if (status != PR_SUCCESS) { + r_log(LOG_GENERIC, LOG_CRIT, "Couldn't make socket nonblocking"); + ABORT(R_INTERNAL); + } + + // Remember our thread. + ststhread_ = do_QueryInterface(stservice, &rv); + if (!NS_SUCCEEDED(rv)) ABORT(R_INTERNAL); + + // Finally, register with the STS + rv = stservice->AttachSocket(fd_, this); + if (!NS_SUCCEEDED(rv)) { + r_log(LOG_GENERIC, LOG_CRIT, "Couldn't attach socket to STS, rv=%u", + static_cast<unsigned>(rv)); + ABORT(R_INTERNAL); + } + + _status = 0; + +abort: + return (_status); +} + +static int ShouldDrop(size_t len) { + // Global rate limiting for stun requests, to mitigate the ice hammer DoS + // (see http://tools.ietf.org/html/draft-thomson-mmusic-ice-webrtc) + + // Tolerate rate of 8k/sec, for one second. + static SimpleTokenBucket burst(16384 * 1, 16384); + // Tolerate rate of 7.2k/sec over twenty seconds. + static SimpleTokenBucket sustained(7372 * 20, 7372); + + // Check number of tokens in each bucket. + if (burst.getTokens(UINT32_MAX) < len) { + r_log(LOG_GENERIC, LOG_ERR, + "Short term global rate limit for STUN requests exceeded."); +#ifdef MOZILLA_INTERNAL_API + nr_socket_short_term_violation_time = TimeStamp::Now(); +#endif + +// Bug 1013007 +#if !EARLY_BETA_OR_EARLIER + return R_WOULDBLOCK; +#else + MOZ_ASSERT(false, + "Short term global rate limit for STUN requests exceeded. Go " + "bug bcampen@mozilla.com if you weren't intentionally " + "spamming ICE candidates, or don't know what that means."); +#endif + } + + if (sustained.getTokens(UINT32_MAX) < len) { + r_log(LOG_GENERIC, LOG_ERR, + "Long term global rate limit for STUN requests exceeded."); +#ifdef MOZILLA_INTERNAL_API + nr_socket_long_term_violation_time = TimeStamp::Now(); +#endif +// Bug 1013007 +#if !EARLY_BETA_OR_EARLIER + return R_WOULDBLOCK; +#else + MOZ_ASSERT(false, + "Long term global rate limit for STUN requests exceeded. Go " + "bug bcampen@mozilla.com if you weren't intentionally " + "spamming ICE candidates, or don't know what that means."); +#endif + } + + // Take len tokens from both buckets. + // (not threadsafe, but no problem since this is only called from STS) + burst.getTokens(len); + sustained.getTokens(len); + return 0; +} + +// This should be called on the STS thread. +int NrSocket::sendto(const void* msg, size_t len, int flags, + const nr_transport_addr* to) { + ASSERT_ON_THREAD(ststhread_); + int r, _status; + PRNetAddr naddr; + int32_t status; + + if ((r = nr_transport_addr_to_praddr(to, &naddr))) ABORT(r); + + if (fd_ == nullptr) ABORT(R_EOD); + + if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) { + ABORT(R_WOULDBLOCK); + } + + // TODO: Convert flags? + status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT); + if (status < 0 || (size_t)status != len) { + if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK); + + r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s: %d", to->as_string, + PR_GetError()); + ABORT(R_IO_ERROR); + } + + _status = 0; +abort: + return (_status); +} + +int NrSocket::recvfrom(void* buf, size_t maxlen, 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) { + if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK); + r_log(LOG_GENERIC, LOG_INFO, "Error in recvfrom: %d", (int)PR_GetError()); + ABORT(R_IO_ERROR); + } + *len = status; + + 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); +} + +int NrSocket::getaddr(nr_transport_addr* addrp) { + ASSERT_ON_THREAD(ststhread_); + return nr_transport_addr_copy(addrp, &my_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; + cancel(NR_ASYNC_WAIT_READ); + cancel(NR_ASYNC_WAIT_WRITE); +} + +int NrSocket::connect(const nr_transport_addr* addr) { + ASSERT_ON_THREAD(ststhread_); + int r, _status; + PRNetAddr naddr; + int32_t connect_status, getsockname_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; + connect_status = PR_Connect(fd_, &naddr, PR_INTERVAL_NO_WAIT); + if (connect_status != PR_SUCCESS) { + if (PR_GetError() != PR_IN_PROGRESS_ERROR) { + r_log(LOG_GENERIC, LOG_CRIT, "PR_Connect failed: %d", PR_GetError()); + ABORT(R_IO_ERROR); + } + } + + // If our local address is wildcard, then fill in the + // address now. + if (nr_transport_addr_is_wildcard(&my_addr_)) { + getsockname_status = PR_GetSockName(fd_, &naddr); + if (getsockname_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_, addr->protocol, 1))) + ABORT(r); + } + + // Now return the WOULDBLOCK if needed. + if (connect_status != PR_SUCCESS) { + ABORT(R_WOULDBLOCK); + } + + _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); + r_log(LOG_GENERIC, LOG_INFO, "Error in write"); + 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); + r_log(LOG_GENERIC, LOG_INFO, "Error in read"); + ABORT(R_IO_ERROR); + } + if (status == 0) ABORT(R_EOD); + + *len = (size_t)status; // Guaranteed to be > 0 + _status = 0; +abort: + return (_status); +} + +int NrSocket::listen(int backlog) { + ASSERT_ON_THREAD(ststhread_); + int32_t status; + int _status; + + assert(fd_); + status = PR_Listen(fd_, backlog); + if (status != PR_SUCCESS) { + r_log(LOG_GENERIC, LOG_CRIT, "%s: PR_GetError() == %d", __FUNCTION__, + PR_GetError()); + ABORT(R_IO_ERROR); + } + + _status = 0; +abort: + return (_status); +} + +int NrSocket::accept(nr_transport_addr* addrp, nr_socket** sockp) { + ASSERT_ON_THREAD(ststhread_); + int _status, r; + PRStatus status; + PRFileDesc* prfd; + PRNetAddr nfrom; + NrSocket* sock = nullptr; + nsresult rv; + PRSocketOptionData opt_nonblock, opt_nodelay; + nsCOMPtr<nsISocketTransportService> stservice = + do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (NS_FAILED(rv)) { + ABORT(R_INTERNAL); + } + + if (!fd_) ABORT(R_EOD); + + prfd = PR_Accept(fd_, &nfrom, PR_INTERVAL_NO_WAIT); + + if (!prfd) { + if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK); + + ABORT(R_IO_ERROR); + } + + sock = new NrSocket(); + + sock->fd_ = prfd; + nr_transport_addr_copy(&sock->my_addr_, &my_addr_); + + if ((r = nr_praddr_to_transport_addr(&nfrom, addrp, my_addr_.protocol, 0))) + ABORT(r); + + // Set nonblocking + opt_nonblock.option = PR_SockOpt_Nonblocking; + opt_nonblock.value.non_blocking = PR_TRUE; + status = PR_SetSocketOption(prfd, &opt_nonblock); + if (status != PR_SUCCESS) { + r_log(LOG_GENERIC, LOG_CRIT, + "Failed to make accepted socket nonblocking: %d", status); + ABORT(R_INTERNAL); + } + // Disable TCP Nagle + opt_nodelay.option = PR_SockOpt_NoDelay; + opt_nodelay.value.no_delay = PR_TRUE; + status = PR_SetSocketOption(prfd, &opt_nodelay); + if (status != PR_SUCCESS) { + r_log(LOG_GENERIC, LOG_WARNING, + "Failed to set Nodelay on accepted socket: %d", status); + } + + // Should fail only with OOM + if ((r = nr_socket_create_int(static_cast<void*>(sock), sock->vtbl(), sockp))) + ABORT(r); + + // Remember our thread. + sock->ststhread_ = do_QueryInterface(stservice, &rv); + if (NS_FAILED(rv)) ABORT(R_INTERNAL); + + // Finally, register with the STS + rv = stservice->AttachSocket(prfd, sock); + if (NS_FAILED(rv)) { + ABORT(R_INTERNAL); + } + + sock->connect_invoked_ = true; + + // Add a reference so that we can delete it in destroy() + sock->AddRef(); + _status = 0; +abort: + if (_status) { + delete sock; + } + + return (_status); +} + +NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy, nsIUDPSocketInternal) + +nsresult NrUdpSocketIpcProxy::Init(const RefPtr<NrUdpSocketIpc>& socket) { + nsresult rv; + sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + MOZ_ASSERT(false, "Failed to get STS thread"); + return rv; + } + + socket_ = socket; + return NS_OK; +} + +NrUdpSocketIpcProxy::~NrUdpSocketIpcProxy() { + // Send our ref to STS to be released + RUN_ON_THREAD(sts_thread_, mozilla::WrapRelease(socket_.forget()), + NS_DISPATCH_NORMAL); +} + +// IUDPSocketInternal interfaces +// callback while error happened in UDP socket operation +NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerError(const nsACString& message, + const nsACString& filename, + uint32_t line_number) { + return socket_->CallListenerError(message, filename, line_number); +} + +// callback while receiving UDP packet +NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerReceivedData( + const nsACString& host, uint16_t port, const nsTArray<uint8_t>& data) { + return socket_->CallListenerReceivedData(host, port, data); +} + +// callback while UDP socket is opened +NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerOpened() { + return socket_->CallListenerOpened(); +} + +// callback while UDP socket is connected +NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerConnected() { + return socket_->CallListenerConnected(); +} + +// callback while UDP socket is closed +NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerClosed() { + return socket_->CallListenerClosed(); +} + +// NrUdpSocketIpc Implementation +NrUdpSocketIpc::NrUdpSocketIpc() + : NrSocketIpc(GetIOThreadAndAddUse_s()), + monitor_("NrUdpSocketIpc"), + err_(false), + state_(NR_INIT) {} + +NrUdpSocketIpc::~NrUdpSocketIpc() = default; + +void NrUdpSocketIpc::Destroy() { +#if defined(MOZILLA_INTERNAL_API) + // destroy_i also dispatches back to STS to call ReleaseUse, to avoid shutting + // down the IO thread before close() runs. + // We use a NonOwning runnable because our refcount has already gone to 0. + io_thread_->Dispatch( + NewNonOwningRunnableMethod(__func__, this, &NrUdpSocketIpc::destroy_i)); +#endif +} + +// IUDPSocketInternal interfaces +// callback while error happened in UDP socket operation +NS_IMETHODIMP NrUdpSocketIpc::CallListenerError(const nsACString& message, + const nsACString& filename, + uint32_t line_number) { + ASSERT_ON_THREAD(io_thread_); + + r_log(LOG_GENERIC, LOG_ERR, "UDP socket error:%s at %s:%d this=%p", + message.BeginReading(), filename.BeginReading(), line_number, + (void*)this); + + ReentrantMonitorAutoEnter mon(monitor_); + err_ = true; + monitor_.NotifyAll(); + + return NS_OK; +} + +// callback while receiving UDP packet +NS_IMETHODIMP NrUdpSocketIpc::CallListenerReceivedData( + const nsACString& host, uint16_t port, const nsTArray<uint8_t>& data) { + ASSERT_ON_THREAD(io_thread_); + + PRNetAddr addr; + memset(&addr, 0, sizeof(addr)); + + { + ReentrantMonitorAutoEnter mon(monitor_); + + if (PR_SUCCESS != PR_StringToNetAddr(host.BeginReading(), &addr)) { + err_ = true; + MOZ_ASSERT(false, "Failed to convert remote host to PRNetAddr"); + return NS_OK; + } + + // Use PR_IpAddrNull to avoid address being reset to 0. + if (PR_SUCCESS != + PR_SetNetAddr(PR_IpAddrNull, addr.raw.family, port, &addr)) { + err_ = true; + MOZ_ASSERT(false, "Failed to set port in PRNetAddr"); + return NS_OK; + } + } + + auto buf = MakeUnique<MediaPacket>(); + buf->Copy(data.Elements(), data.Length()); + RefPtr<nr_udp_message> msg(new nr_udp_message(addr, std::move(buf))); + + RUN_ON_THREAD(sts_thread_, + mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this), + &NrUdpSocketIpc::recv_callback_s, msg), + NS_DISPATCH_NORMAL); + return NS_OK; +} + +nsresult NrUdpSocketIpc::SetAddress() { + uint16_t port = socket_child_->LocalPort(); + + nsAutoCString address(socket_child_->LocalAddress()); + + PRNetAddr praddr; + if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, port, &praddr)) { + err_ = true; + MOZ_ASSERT(false, "Failed to set port in PRNetAddr"); + return NS_OK; + } + + if (PR_SUCCESS != PR_StringToNetAddr(address.BeginReading(), &praddr)) { + err_ = true; + MOZ_ASSERT(false, "Failed to convert local host to PRNetAddr"); + return NS_OK; + } + + 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_, IPPROTO_UDP, 1)) { + err_ = true; + MOZ_ASSERT(false, "Failed to copy local host to my_addr_"); + } + + if (!nr_transport_addr_is_wildcard(&expected_addr) && + 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"); + } + + return NS_OK; +} + +// callback while UDP socket is opened +NS_IMETHODIMP NrUdpSocketIpc::CallListenerOpened() { + ASSERT_ON_THREAD(io_thread_); + ReentrantMonitorAutoEnter mon(monitor_); + + r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket opened this=%p", (void*)this); + nsresult rv = SetAddress(); + if (NS_FAILED(rv)) { + return rv; + } + + mon.NotifyAll(); + + return NS_OK; +} + +// callback while UDP socket is connected +NS_IMETHODIMP NrUdpSocketIpc::CallListenerConnected() { + ASSERT_ON_THREAD(io_thread_); + + ReentrantMonitorAutoEnter mon(monitor_); + + r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket connected this=%p", (void*)this); + MOZ_ASSERT(state_ == NR_CONNECTED); + + nsresult rv = SetAddress(); + if (NS_FAILED(rv)) { + mon.NotifyAll(); + return rv; + } + + r_log(LOG_GENERIC, LOG_INFO, "Exit UDP socket connected"); + mon.NotifyAll(); + + return NS_OK; +} + +// callback while UDP socket is closed +NS_IMETHODIMP NrUdpSocketIpc::CallListenerClosed() { + ASSERT_ON_THREAD(io_thread_); + + ReentrantMonitorAutoEnter mon(monitor_); + + r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket closed this=%p", (void*)this); + MOZ_ASSERT(state_ == NR_CONNECTED || state_ == NR_CLOSING); + state_ = NR_CLOSED; + + return NS_OK; +} + +// +// NrSocketBase methods. +// +int NrUdpSocketIpc::create(nr_transport_addr* addr) { + ASSERT_ON_THREAD(sts_thread_); + + int r, _status; + nsresult rv; + int32_t port; + nsCString host; + + ReentrantMonitorAutoEnter mon(monitor_); + + if (state_ != NR_INIT) { + ABORT(R_INTERNAL); + } + + sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + MOZ_ASSERT(false, "Failed to get STS thread"); + ABORT(R_INTERNAL); + } + + if ((r = nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) { + ABORT(r); + } + + // wildcard address will be resolved at NrUdpSocketIpc::CallListenerVoid + if ((r = nr_transport_addr_copy(&my_addr_, addr))) { + ABORT(r); + } + + state_ = NR_CONNECTING; + + MOZ_ASSERT(io_thread_); + RUN_ON_THREAD(io_thread_, + mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this), + &NrUdpSocketIpc::create_i, host, + static_cast<uint16_t>(port)), + NS_DISPATCH_NORMAL); + + // Wait until socket creation complete. + mon.Wait(); + + if (err_) { + close(); + ABORT(R_INTERNAL); + } + + state_ = NR_CONNECTED; + + _status = 0; +abort: + return (_status); +} + +int NrUdpSocketIpc::sendto(const void* msg, size_t len, int flags, + const nr_transport_addr* to) { + ASSERT_ON_THREAD(sts_thread_); + + ReentrantMonitorAutoEnter mon(monitor_); + + // If send err happened before, simply return the error. + if (err_) { + return R_IO_ERROR; + } + + if (state_ != NR_CONNECTED) { + return R_INTERNAL; + } + + int r; + net::NetAddr addr; + if ((r = nr_transport_addr_to_netaddr(to, &addr))) { + return r; + } + + if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) { + return R_WOULDBLOCK; + } + + UniquePtr<MediaPacket> buf(new MediaPacket); + buf->Copy(static_cast<const uint8_t*>(msg), len); + + RUN_ON_THREAD( + io_thread_, + mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this), + &NrUdpSocketIpc::sendto_i, addr, std::move(buf)), + NS_DISPATCH_NORMAL); + return 0; +} + +void NrUdpSocketIpc::close() { + r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::close()"); + + ASSERT_ON_THREAD(sts_thread_); + + ReentrantMonitorAutoEnter mon(monitor_); + state_ = NR_CLOSING; + + RUN_ON_THREAD(io_thread_, + mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this), + &NrUdpSocketIpc::close_i), + NS_DISPATCH_NORMAL); + + // remove all enqueued messages + std::queue<RefPtr<nr_udp_message>> empty; + std::swap(received_msgs_, empty); +} + +int NrUdpSocketIpc::recvfrom(void* buf, size_t maxlen, size_t* len, int flags, + nr_transport_addr* from) { + ASSERT_ON_THREAD(sts_thread_); + + ReentrantMonitorAutoEnter mon(monitor_); + + int r, _status; + uint32_t consumed_len; + + *len = 0; + + if (state_ != NR_CONNECTED) { + ABORT(R_INTERNAL); + } + + if (received_msgs_.empty()) { + ABORT(R_WOULDBLOCK); + } + + { + RefPtr<nr_udp_message> msg(received_msgs_.front()); + + received_msgs_.pop(); + + 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"); + } + + memcpy(buf, msg->data->data(), consumed_len); + *len = consumed_len; + } + + _status = 0; +abort: + return (_status); +} + +int NrUdpSocketIpc::getaddr(nr_transport_addr* addrp) { + ASSERT_ON_THREAD(sts_thread_); + + ReentrantMonitorAutoEnter mon(monitor_); + + return nr_transport_addr_copy(addrp, &my_addr_); +} + +int NrUdpSocketIpc::connect(const nr_transport_addr* addr) { + int r, _status; + int32_t port; + nsCString host; + + ReentrantMonitorAutoEnter mon(monitor_); + r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::connect(%s) this=%p", + addr->as_string, (void*)this); + + if ((r = nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) { + ABORT(r); + } + + RUN_ON_THREAD(io_thread_, + mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this), + &NrUdpSocketIpc::connect_i, host, + static_cast<uint16_t>(port)), + NS_DISPATCH_NORMAL); + + // Wait until connect() completes. + mon.Wait(); + + r_log(LOG_GENERIC, LOG_DEBUG, + "NrUdpSocketIpc::connect this=%p completed err_ = %s", (void*)this, + err_ ? "true" : "false"); + + if (err_) { + ABORT(R_INTERNAL); + } + + _status = 0; +abort: + return _status; +} + +int NrUdpSocketIpc::write(const void* msg, size_t len, size_t* written) { + MOZ_ASSERT(false); + return R_INTERNAL; +} + +int NrUdpSocketIpc::read(void* buf, size_t maxlen, size_t* len) { + MOZ_ASSERT(false); + return R_INTERNAL; +} + +int NrUdpSocketIpc::listen(int backlog) { + MOZ_ASSERT(false); + return R_INTERNAL; +} + +int NrUdpSocketIpc::accept(nr_transport_addr* addrp, nr_socket** sockp) { + MOZ_ASSERT(false); + return R_INTERNAL; +} + +// IO thread executors +void NrUdpSocketIpc::create_i(const nsACString& host, const uint16_t port) { + ASSERT_ON_THREAD(io_thread_); + + uint32_t minBuffSize = 0; + RefPtr<dom::UDPSocketChild> socketChild = new dom::UDPSocketChild(); + + // This can spin the event loop; don't do that with the monitor held + socketChild->SetBackgroundSpinsEvents(); + + ReentrantMonitorAutoEnter mon(monitor_); + if (!socket_child_) { + socket_child_ = socketChild; + socket_child_->SetFilterName( + nsCString(NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX)); + } else { + socketChild = nullptr; + } + + RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy); + nsresult rv = proxy->Init(this); + if (NS_FAILED(rv)) { + err_ = true; + mon.NotifyAll(); + return; + } + +#ifdef XP_WIN + if (!mozilla::IsWin8OrLater()) { + // Increase default receive and send buffer size on <= Win7 to be able to + // receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K size) + // stream without losing packets. + // Manual testing showed that 100K buffer size was not enough and the + // packet loss dis-appeared with 256K buffer size. + // See bug 1252769 for future improvements of this. + minBuffSize = 256 * 1024; + } +#endif + // XXX bug 1126232 - don't use null Principal! + if (NS_FAILED(socket_child_->Bind(proxy, nullptr, host, port, + /* addressReuse = */ false, + /* loopback = */ false, + /* recv buffer size */ minBuffSize, + /* send buffer size */ minBuffSize))) { + err_ = true; + MOZ_ASSERT(false, "Failed to create UDP socket"); + mon.NotifyAll(); + return; + } +} + +void NrUdpSocketIpc::connect_i(const nsACString& host, const uint16_t port) { + ASSERT_ON_THREAD(io_thread_); + nsresult rv; + ReentrantMonitorAutoEnter mon(monitor_); + + RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy); + rv = proxy->Init(this); + if (NS_FAILED(rv)) { + err_ = true; + mon.NotifyAll(); + return; + } + + socket_child_->Connect(proxy, host, port); +} + +void NrUdpSocketIpc::sendto_i(const net::NetAddr& addr, + UniquePtr<MediaPacket> buf) { + ASSERT_ON_THREAD(io_thread_); + + ReentrantMonitorAutoEnter mon(monitor_); + + if (!socket_child_) { + MOZ_ASSERT(false); + err_ = true; + return; + } + if (NS_FAILED( + socket_child_->SendWithAddress(&addr, buf->data(), buf->len()))) { + err_ = true; + } +} + +void NrUdpSocketIpc::close_i() { + ASSERT_ON_THREAD(io_thread_); + + if (socket_child_) { + socket_child_->Close(); + socket_child_ = nullptr; + } +} + +#if defined(MOZILLA_INTERNAL_API) + +static void ReleaseIOThread_s() { sThread->ReleaseUse(); } + +void NrUdpSocketIpc::destroy_i() { + close_i(); + + RUN_ON_THREAD(sts_thread_, WrapRunnableNM(&ReleaseIOThread_s), + NS_DISPATCH_NORMAL); +} +#endif + +void NrUdpSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) { + ASSERT_ON_THREAD(sts_thread_); + + { + ReentrantMonitorAutoEnter mon(monitor_); + if (state_ != NR_CONNECTED) { + return; + } + } + + // enqueue received message + received_msgs_.push(msg); + + if ((poll_flags() & PR_POLL_READ)) { + fire_callback(NR_ASYNC_WAIT_READ); + } +} + +} // namespace mozilla + +using namespace mozilla; + +// Bridge to the nr_socket interface +static int nr_socket_local_destroy(void** objp); +static int nr_socket_local_sendto(void* obj, const void* msg, size_t len, + int flags, const 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* obj, const 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 int nr_socket_local_listen(void* obj, int backlog); +static int nr_socket_local_accept(void* obj, nr_transport_addr* addrp, + nr_socket** sockp); + +static nr_socket_vtbl nr_socket_local_vtbl = {2, + 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, + nr_socket_local_listen, + nr_socket_local_accept}; + +/* static */ +int NrSocketBase::CreateSocket( + nr_transport_addr* addr, RefPtr<NrSocketBase>* sock, + const std::shared_ptr<NrSocketProxyConfig>& config) { + int r, _status; + + if (IsForbiddenAddress(addr)) { + ABORT(R_REJECTED); + } + + if (config && config->GetForceProxy() && addr->protocol == IPPROTO_UDP) { + ABORT(R_REJECTED); + } + + // create IPC bridge for content process + if (XRE_IsParentProcess()) { + // TODO: Make NrTcpSocket work on the parent process + *sock = new NrSocket(); + } else if (XRE_IsSocketProcess()) { + if (addr->protocol == IPPROTO_TCP) { + *sock = new NrTcpSocket(config); + } else { + *sock = new NrSocket(); + } + } else { + if (addr->protocol == IPPROTO_TCP) { + *sock = new NrTcpSocket(config); + } else { + *sock = new NrUdpSocketIpc(); + } + } + + r = (*sock)->create(addr); + if (r) ABORT(r); + + _status = 0; +abort: + if (_status) { + *sock = nullptr; + } + return _status; +} + +// static +bool NrSocketBase::IsForbiddenAddress(nr_transport_addr* addr) { + int r, port; + + r = nr_transport_addr_get_port(addr, &port); + if (r) { + return true; + } + + // allow auto assigned ports + if (port != 0) { + // Don't need to check an override scheme + nsresult rv = NS_CheckPortSafety(port, nullptr); + if (NS_FAILED(rv)) { + return true; + } + } + + return false; +} + +static int nr_socket_local_destroy(void** objp) { + if (!objp || !*objp) return 0; + + NrSocketBase* sock = static_cast<NrSocketBase*>(*objp); + *objp = nullptr; + + sock->close(); // Signal STS that we want not to listen + sock->Release(); // Decrement the ref count + + return 0; +} + +static int nr_socket_local_sendto(void* obj, const void* msg, size_t len, + int flags, const nr_transport_addr* addr) { + NrSocketBase* sock = static_cast<NrSocketBase*>(obj); + + return sock->sendto(msg, len, flags, addr); +} + +static int nr_socket_local_recvfrom(void* obj, void* restrict buf, + size_t maxlen, size_t* len, int flags, + nr_transport_addr* addr) { + NrSocketBase* sock = static_cast<NrSocketBase*>(obj); + + return sock->recvfrom(buf, maxlen, len, flags, addr); +} + +static int nr_socket_local_getfd(void* obj, NR_SOCKET* fd) { + NrSocketBase* sock = static_cast<NrSocketBase*>(obj); + + *fd = sock; + + return 0; +} + +static int nr_socket_local_getaddr(void* obj, nr_transport_addr* addrp) { + NrSocketBase* sock = static_cast<NrSocketBase*>(obj); + + return sock->getaddr(addrp); +} + +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) { + NrSocketBase* sock = static_cast<NrSocketBase*>(obj); + + return sock->write(msg, len, written); +} + +static int nr_socket_local_read(void* obj, void* restrict buf, size_t maxlen, + size_t* len) { + NrSocketBase* sock = static_cast<NrSocketBase*>(obj); + + return sock->read(buf, maxlen, len); +} + +static int nr_socket_local_connect(void* obj, const nr_transport_addr* addr) { + NrSocketBase* sock = static_cast<NrSocketBase*>(obj); + + return sock->connect(addr); +} + +static int nr_socket_local_listen(void* obj, int backlog) { + NrSocketBase* sock = static_cast<NrSocketBase*>(obj); + + return sock->listen(backlog); +} + +static int nr_socket_local_accept(void* obj, nr_transport_addr* addrp, + nr_socket** sockp) { + NrSocketBase* sock = static_cast<NrSocketBase*>(obj); + + return sock->accept(addrp, sockp); +} + +// 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; } diff --git a/dom/media/webrtc/transport/nr_socket_prsock.h b/dom/media/webrtc/transport/nr_socket_prsock.h new file mode 100644 index 0000000000..010fcb59bc --- /dev/null +++ b/dom/media/webrtc/transport/nr_socket_prsock.h @@ -0,0 +1,320 @@ +/* -*- 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 + +/* Some source code here from nICEr. Copyright is: + +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + +// Implementation of nICEr/nr_socket that is tied to the Gecko +// SocketTransportService. + +#ifndef nr_socket_prsock__ +#define nr_socket_prsock__ + +#include <memory> +#include <queue> + +#include "nspr.h" +#include "prio.h" + +#include "nsCOMPtr.h" +#include "nsASocketHandler.h" +#include "nsXPCOM.h" +#include "nsIEventTarget.h" +#include "nsIUDPSocketChild.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" + +#include "mediapacket.h" +#include "m_cpp_utils.h" +#include "mozilla/ReentrantMonitor.h" +#include "mozilla/RefPtr.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/ClearOnShutdown.h" + +// nICEr includes +extern "C" { +#include "transport_addr.h" +#include "async_wait.h" +} + +// Stub declaration for nICEr type +typedef struct nr_socket_vtbl_ nr_socket_vtbl; +typedef struct nr_socket_ nr_socket; + +#if defined(MOZILLA_INTERNAL_API) +namespace mozilla { +class NrSocketProxyConfig; +} // namespace mozilla +#endif + +namespace mozilla { + +namespace net { +union NetAddr; +} + +namespace dom { +class UDPSocketChild; +} // namespace dom + +class NrSocketBase { + public: + 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() = default; + + // Factory method; will create either an NrSocket, NrUdpSocketIpc, or + // NrTcpSocketIpc as appropriate. + static int CreateSocket(nr_transport_addr* addr, RefPtr<NrSocketBase>* sock, + const std::shared_ptr<NrSocketProxyConfig>& config); + static bool IsForbiddenAddress(nr_transport_addr* addr); + + // the nr_socket APIs + virtual int create(nr_transport_addr* addr) = 0; + virtual int sendto(const void* msg, size_t len, int flags, + const 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(const 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; + virtual int listen(int backlog) = 0; + virtual int accept(nr_transport_addr* addrp, nr_socket** sockp) = 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_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + uint32_t poll_flags() { return poll_flags_; } + + virtual nr_socket_vtbl* vtbl(); // To access in test classes. + + static TimeStamp short_term_violation_time(); + static TimeStamp long_term_violation_time(); + const nr_transport_addr& my_addr() const { return my_addr_; } + + void fire_callback(int how); + + protected: + 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_; +}; + +class NrSocket : public NrSocketBase, public nsASocketHandler { + public: + NrSocket() : fd_(nullptr) {} + + // Implement nsASocket + virtual void OnSocketReady(PRFileDesc* fd, int16_t outflags) override; + virtual void OnSocketDetached(PRFileDesc* fd) override; + virtual void IsLocal(bool* aIsLocal) override; + virtual uint64_t ByteCountSent() override { return 0; } + virtual uint64_t ByteCountReceived() override { return 0; } + + // nsISupports methods + NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override; + NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DESTROY(NrSocket, Destroy(), + override); + virtual void Destroy() { delete this; } + + // Implementations of the async_event APIs + virtual int async_wait(int how, NR_async_cb cb, void* cb_arg, char* function, + int line) override; + virtual int cancel(int how) override; + + // Implementations of the nr_socket APIs + virtual int create(nr_transport_addr* addr) + override; // (really init, but it's called create) + virtual int sendto(const void* msg, size_t len, int flags, + const nr_transport_addr* to) override; + virtual int recvfrom(void* buf, size_t maxlen, size_t* len, int flags, + nr_transport_addr* from) override; + virtual int getaddr(nr_transport_addr* addrp) override; + virtual void close() override; + virtual int connect(const nr_transport_addr* addr) override; + virtual int write(const void* msg, size_t len, size_t* written) override; + virtual int read(void* buf, size_t maxlen, size_t* len) override; + virtual int listen(int backlog) override; + virtual int accept(nr_transport_addr* addrp, nr_socket** sockp) override; + + protected: + virtual ~NrSocket() { + if (fd_) PR_Close(fd_); + } + + DISALLOW_COPY_ASSIGN(NrSocket); + + PRFileDesc* fd_; + nsCOMPtr<nsIEventTarget> ststhread_; +}; + +struct nr_udp_message { + nr_udp_message(const PRNetAddr& from, UniquePtr<MediaPacket>&& data) + : from(from), data(std::move(data)) {} + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_udp_message); + + PRNetAddr from; + UniquePtr<MediaPacket> data; + + private: + ~nr_udp_message() = default; + DISALLOW_COPY_ASSIGN(nr_udp_message); +}; + +class NrSocketIpc : public NrSocketBase { + public: + enum NrSocketIpcState { + NR_INIT, + NR_CONNECTING, + NR_CONNECTED, + NR_CLOSING, + NR_CLOSED, + }; + + NrSocketIpc(nsIEventTarget* aThread); + + protected: + nsCOMPtr<nsIEventTarget> sts_thread_; + // Note: for UDP PBackground, this is a thread held by SingletonThreadHolder. + // For TCP PNecko, this is MainThread (and TCPSocket requires MainThread + // currently) + const nsCOMPtr<nsIEventTarget> io_thread_; + virtual ~NrSocketIpc() = default; + + private: + DISALLOW_COPY_ASSIGN(NrSocketIpc); +}; + +class NrUdpSocketIpc : public NrSocketIpc { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrUdpSocketIpc, override) + + NS_IMETHODIMP CallListenerError(const nsACString& message, + const nsACString& filename, + uint32_t line_number); + NS_IMETHODIMP CallListenerReceivedData(const nsACString& host, uint16_t port, + const nsTArray<uint8_t>& data); + NS_IMETHODIMP CallListenerOpened(); + NS_IMETHODIMP CallListenerConnected(); + NS_IMETHODIMP CallListenerClosed(); + + NrUdpSocketIpc(); + + // Implementations of the NrSocketBase APIs + virtual int create(nr_transport_addr* addr) override; + virtual int sendto(const void* msg, size_t len, int flags, + const nr_transport_addr* to) override; + virtual int recvfrom(void* buf, size_t maxlen, size_t* len, int flags, + nr_transport_addr* from) override; + virtual int getaddr(nr_transport_addr* addrp) override; + virtual void close() override; + virtual int connect(const nr_transport_addr* addr) override; + virtual int write(const void* msg, size_t len, size_t* written) override; + virtual int read(void* buf, size_t maxlen, size_t* len) override; + virtual int listen(int backlog) override; + virtual int accept(nr_transport_addr* addrp, nr_socket** sockp) override; + + private: + virtual ~NrUdpSocketIpc(); + virtual void Destroy(); + + DISALLOW_COPY_ASSIGN(NrUdpSocketIpc); + + nsresult SetAddress(); // Set the local address from parent info. + + // Main or private thread executors of the NrSocketBase APIs + void create_i(const nsACString& host, const uint16_t port); + void connect_i(const nsACString& host, const uint16_t port); + void sendto_i(const net::NetAddr& addr, UniquePtr<MediaPacket> buf); + void close_i(); +#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API) + void destroy_i(); +#endif + // STS thread executor + void recv_callback_s(RefPtr<nr_udp_message> msg); + + ReentrantMonitor monitor_ MOZ_UNANNOTATED; // protects err_and state_ + bool err_; + NrSocketIpcState state_; + + std::queue<RefPtr<nr_udp_message>> received_msgs_; + + // only accessed from the io_thread + RefPtr<dom::UDPSocketChild> socket_child_; +}; + +// The socket child holds onto one of these, which just passes callbacks +// through and makes sure the ref to the NrSocketIpc is released on STS. +class NrUdpSocketIpcProxy : public nsIUDPSocketInternal { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIUDPSOCKETINTERNAL + + nsresult Init(const RefPtr<NrUdpSocketIpc>& socket); + + private: + virtual ~NrUdpSocketIpcProxy(); + + RefPtr<NrUdpSocketIpc> socket_; + nsCOMPtr<nsIEventTarget> sts_thread_; +}; + +int nr_netaddr_to_transport_addr(const net::NetAddr* netaddr, + nr_transport_addr* addr, int protocol); +int nr_praddr_to_transport_addr(const PRNetAddr* praddr, + nr_transport_addr* addr, int protocol, + int keep); +int nr_transport_addr_get_addrstring_and_port(const nr_transport_addr* addr, + nsACString* host, int32_t* port); +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/nr_socket_tcp.cpp b/dom/media/webrtc/transport/nr_socket_tcp.cpp new file mode 100644 index 0000000000..a9cc5db312 --- /dev/null +++ b/dom/media/webrtc/transport/nr_socket_tcp.cpp @@ -0,0 +1,310 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +/* +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, Mozilla 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_socket_tcp.h" + +#include "mozilla/ErrorNames.h" + +#include "WebrtcTCPSocketWrapper.h" + +namespace mozilla { +using namespace net; + +using std::shared_ptr; + +class NrTcpSocketData { + public: + explicit NrTcpSocketData(nsTArray<uint8_t>&& aData) + : mData(std::move(aData)) { + MOZ_COUNT_CTOR(NrTcpSocketData); + } + + MOZ_COUNTED_DTOR(NrTcpSocketData) + + const nsTArray<uint8_t>& GetData() const { return mData; } + + private: + nsTArray<uint8_t> mData; +}; + +NrTcpSocket::NrTcpSocket(const shared_ptr<NrSocketProxyConfig>& aConfig) + : mClosed(false), + mReadOffset(0), + mConfig(aConfig), + mWebrtcTCPSocket(nullptr) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::NrTcpSocket %p\n", this); +} + +NrTcpSocket::~NrTcpSocket() { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::~NrTcpSocket %p\n", this); + MOZ_ASSERT(!mWebrtcTCPSocket, "webrtc TCP socket not null"); +} + +int NrTcpSocket::create(nr_transport_addr* aAddr) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::create %p\n", this); + int32_t port; + nsCString host; + + // Sanity check + if (nr_transport_addr_get_addrstring_and_port(aAddr, &host, &port)) { + return R_FAILED; + } + + if (nr_transport_addr_copy(&my_addr_, aAddr)) { + return R_FAILED; + } + + return 0; +} + +int NrTcpSocket::connect(const nr_transport_addr* aAddr) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::connect %p\n", this); + + nsCString remote_host; + int remote_port; + + if (NS_WARN_IF(nr_transport_addr_get_addrstring_and_port(aAddr, &remote_host, + &remote_port))) { + return R_FAILED; + } + + bool use_tls = aAddr->tls; + + nsCString local_addr; + int local_port; + + if (NS_WARN_IF(nr_transport_addr_get_addrstring_and_port( + &my_addr_, &local_addr, &local_port))) { + return R_FAILED; + } + + mWebrtcTCPSocket = new WebrtcTCPSocketWrapper(this); + + mWebrtcTCPSocket->AsyncOpen(remote_host, remote_port, local_addr, local_port, + use_tls, mConfig); + + // trigger nr_socket_buffered to set write/read callback + return R_WOULDBLOCK; +} + +void NrTcpSocket::close() { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::close %p\n", this); + + if (mClosed) { + return; + } + + mClosed = true; + + // We're not always open at this point. + if (mWebrtcTCPSocket) { + mWebrtcTCPSocket->Close(); + mWebrtcTCPSocket = nullptr; + } +} + +int NrTcpSocket::write(const void* aBuffer, size_t aCount, size_t* aWrote) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::write %p count=%zu\n", this, + aCount); + + if (mClosed) { + return R_FAILED; + } + + if (!aWrote) { + return R_FAILED; + } + + if (NS_WARN_IF(!mWebrtcTCPSocket)) { + return R_FAILED; + } + + *aWrote = aCount; + + if (aCount > 0) { + nsTArray<uint8_t> writeData; + writeData.SetLength(aCount); + memcpy(writeData.Elements(), aBuffer, aCount); + + mWebrtcTCPSocket->SendWrite(std::move(writeData)); + } + + return 0; +} + +int NrTcpSocket::read(void* aBuffer, size_t aCount, size_t* aRead) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::read %p\n", this); + + if (mClosed) { + return R_FAILED; + } + + if (!aRead) { + return R_FAILED; + } + + *aRead = 0; + + if (mReadQueue.empty()) { + return R_WOULDBLOCK; + } + + while (aCount > 0 && !mReadQueue.empty()) { + const NrTcpSocketData& data = mReadQueue.front(); + + size_t remainingCount = data.GetData().Length() - mReadOffset; + size_t amountToCopy = std::min(aCount, remainingCount); + + char* buffer = static_cast<char*>(aBuffer) + (*aRead); + + memcpy(buffer, data.GetData().Elements() + mReadOffset, amountToCopy); + + mReadOffset += amountToCopy; + *aRead += amountToCopy; + aCount -= amountToCopy; + + if (remainingCount == amountToCopy) { + mReadOffset = 0; + mReadQueue.pop_front(); + } + } + + return 0; +} + +int NrTcpSocket::getaddr(nr_transport_addr* aAddr) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::getaddr %p\n", this); + return nr_transport_addr_copy(aAddr, &my_addr_); +} + +int NrTcpSocket::sendto(const void* aBuffer, size_t aCount, int aFlags, + const nr_transport_addr* aAddr) { + // never call this + MOZ_ASSERT(0); + return R_FAILED; +} + +int NrTcpSocket::recvfrom(void* aBuffer, size_t aCount, size_t* aRead, + int aFlags, nr_transport_addr* aAddr) { + // never call this + MOZ_ASSERT(0); + return R_FAILED; +} + +int NrTcpSocket::listen(int aBacklog) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::listen %p\n", this); + return R_INTERNAL; +} + +int NrTcpSocket::accept(nr_transport_addr* aAddr, nr_socket** aSocket) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::accept %p\n", this); + return R_INTERNAL; +} + +// WebrtcTCPSocketCallback +void NrTcpSocket::OnClose(nsresult aReason) { + nsCString errorName; + GetErrorName(aReason, errorName); + + r_log(LOG_GENERIC, LOG_ERR, "NrTcpSocket::OnClose %p reason=%u name=%s\n", + this, static_cast<uint32_t>(aReason), errorName.get()); + + close(); + + DoCallbacks(); +} + +void NrTcpSocket::OnConnected(const nsACString& aProxyType) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::OnConnected %p\n", this); + if (aProxyType != "" && aProxyType != "direct") { + my_addr_.is_proxied = true; + } + + DoCallbacks(); +} + +void NrTcpSocket::OnRead(nsTArray<uint8_t>&& aReadData) { + r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::OnRead %p read=%zu\n", this, + aReadData.Length()); + + mReadQueue.emplace_back(std::move(aReadData)); + + DoCallbacks(); +} + +void NrTcpSocket::DoCallbacks() { + size_t lastCount = -1; + size_t currentCount = 0; + while ((poll_flags() & PR_POLL_READ) != 0 && + // Make sure whatever is reading knows we're closed. This doesn't need + // to happen for writes since ICE doesn't like a failing writable. + (mClosed || (currentCount = CountUnreadBytes()) > 0) && + lastCount != currentCount) { + fire_callback(NR_ASYNC_WAIT_READ); + lastCount = currentCount; + } + + // We're always ready to write after we're connected. The parent process will + // buffer writes for us. + if (!mClosed && mWebrtcTCPSocket && (poll_flags() & PR_POLL_WRITE) != 0) { + fire_callback(NR_ASYNC_WAIT_WRITE); + } +} + +size_t NrTcpSocket::CountUnreadBytes() const { + size_t count = 0; + + for (const NrTcpSocketData& data : mReadQueue) { + count += data.GetData().Length(); + } + + MOZ_ASSERT(count >= mReadOffset, "offset exceeds read buffer length"); + + count -= mReadOffset; + + return count; +} + +void NrTcpSocket::AssignChannel_DoNotUse(WebrtcTCPSocketWrapper* aWrapper) { + mWebrtcTCPSocket = aWrapper; +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/nr_socket_tcp.h b/dom/media/webrtc/transport/nr_socket_tcp.h new file mode 100644 index 0000000000..ebf806a924 --- /dev/null +++ b/dom/media/webrtc/transport/nr_socket_tcp.h @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ +/* 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/. */ + +/* +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, Mozilla 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. +*/ + +#ifndef nr_socket_tcp_h__ +#define nr_socket_tcp_h__ + +#include <list> + +#include "mozilla/net/WebrtcTCPSocketCallback.h" + +#include "nsTArray.h" + +extern "C" { +#include "nr_api.h" +#include "nr_socket.h" +#include "transport_addr.h" +} + +#include "nr_socket_prsock.h" + +namespace mozilla { +using namespace net; + +namespace net { +class WebrtcTCPSocketWrapper; +} // namespace net + +class NrTcpSocketData; +class NrSocketProxyConfig; + +class NrTcpSocket : public NrSocketBase, public WebrtcTCPSocketCallback { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrTcpSocket, override) + + explicit NrTcpSocket(const std::shared_ptr<NrSocketProxyConfig>& aConfig); + + // NrSocketBase + int create(nr_transport_addr* aAddr) override; + int connect(const nr_transport_addr* aAddr) override; + void close() override; + int write(const void* aBuffer, size_t aCount, size_t* aWrote) override; + int read(void* aBuffer, size_t aCount, size_t* aRead) override; + int getaddr(nr_transport_addr* aAddr) override; + int sendto(const void* aBuffer, size_t aCount, int aFlags, + const nr_transport_addr* aAddr) override; + int recvfrom(void* aBuffer, size_t aCount, size_t* aRead, int aFlags, + nr_transport_addr* aAddr) override; + int listen(int aBacklog) override; + int accept(nr_transport_addr* aAddr, nr_socket** aSocket) override; + + // WebrtcTCPSocketCallback + void OnClose(nsresult aReason) override; + void OnConnected(const nsACString& aProxyType) override; + void OnRead(nsTArray<uint8_t>&& aReadData) override; + + size_t CountUnreadBytes() const; + + // for gtests + void AssignChannel_DoNotUse(WebrtcTCPSocketWrapper* aWrapper); + + protected: + virtual ~NrTcpSocket(); + + private: + void DoCallbacks(); + + bool mClosed; + + size_t mReadOffset; + std::list<NrTcpSocketData> mReadQueue; + + std::shared_ptr<NrSocketProxyConfig> mConfig; + + RefPtr<WebrtcTCPSocketWrapper> mWebrtcTCPSocket; +}; + +} // namespace mozilla + +#endif // nr_socket_tcp_h__ diff --git a/dom/media/webrtc/transport/nr_timer.cpp b/dom/media/webrtc/transport/nr_timer.cpp new file mode 100644 index 0000000000..3d5671c7a4 --- /dev/null +++ b/dom/media/webrtc/transport/nr_timer.cpp @@ -0,0 +1,256 @@ +/* -*- 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/. */ + +/* 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 code by: ekr@rtfm.com + +// Implementation of the NR timer interface + +// Some code here copied from nrappkit. The license was. + +/** + Copyright (C) 2004, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Sun Feb 22 19:35:24 2004 + */ + +#include <string> + +#include "nsCOMPtr.h" +#include "nsServiceManagerUtils.h" +#include "nsIEventTarget.h" +#include "nsINamed.h" +#include "nsITimer.h" +#include "nsNetCID.h" +#include "runnable_utils.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/UniquePtr.h" + +extern "C" { +#include "async_wait.h" +#include "async_timer.h" +#include "r_errors.h" +#include "r_log.h" +} + +namespace mozilla { + +class nrappkitCallback { + public: + nrappkitCallback(NR_async_cb cb, void* cb_arg, const char* function, int line) + : cb_(cb), cb_arg_(cb_arg), function_(function), line_(line) {} + virtual ~nrappkitCallback() = default; + + virtual void Cancel() = 0; + + protected: + /* additional members */ + NR_async_cb cb_; + void* cb_arg_; + std::string function_; + int line_; +}; + +class nrappkitTimerCallback : public nrappkitCallback, + public nsITimerCallback, + public nsINamed { + public: + // We're going to release ourself in the callback, so we need to be threadsafe + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITIMERCALLBACK + + nrappkitTimerCallback(NR_async_cb cb, void* cb_arg, const char* function, + int line) + : nrappkitCallback(cb, cb_arg, function, line), timer_(nullptr) {} + + void SetTimer(already_AddRefed<nsITimer>&& timer) { timer_ = timer; } + + virtual void Cancel() override { + AddRef(); // Cancelling the timer causes the callback it holds to + // be released. AddRef() keeps us alive. + timer_->Cancel(); + timer_ = nullptr; + Release(); // Will cause deletion of this object. + } + + NS_IMETHOD + GetName(nsACString& aName) override { + aName.AssignLiteral("nrappkitTimerCallback"); + return NS_OK; + } + + private: + nsCOMPtr<nsITimer> timer_; + virtual ~nrappkitTimerCallback() = default; +}; + +NS_IMPL_ISUPPORTS(nrappkitTimerCallback, nsITimerCallback, nsINamed) + +NS_IMETHODIMP nrappkitTimerCallback::Notify(nsITimer* timer) { + r_log(LOG_GENERIC, LOG_DEBUG, "Timer callback fired (set in %s:%d)", + function_.c_str(), line_); + MOZ_RELEASE_ASSERT(timer == timer_); + cb_(nullptr, 0, cb_arg_); + + // Allow the timer to go away. + timer_ = nullptr; + return NS_OK; +} + +class nrappkitScheduledCallback : public nrappkitCallback { + public: + nrappkitScheduledCallback(NR_async_cb cb, void* cb_arg, const char* function, + int line) + : nrappkitCallback(cb, cb_arg, function, line) {} + + void Run() { + if (cb_) { + cb_(nullptr, 0, cb_arg_); + } + } + + virtual void Cancel() override { cb_ = nullptr; } + + ~nrappkitScheduledCallback() = default; +}; + +} // namespace mozilla + +using namespace mozilla; + +static nsCOMPtr<nsIEventTarget> GetSTSThread() { + nsresult rv; + + nsCOMPtr<nsIEventTarget> sts_thread; + + sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + return sts_thread; +} + +// These timers must only be used from the STS thread. +// This function is a helper that enforces that. +static void CheckSTSThread() { + DebugOnly<nsCOMPtr<nsIEventTarget>> sts_thread = GetSTSThread(); + + ASSERT_ON_THREAD(sts_thread.value); +} + +static int nr_async_timer_set_zero(NR_async_cb cb, void* arg, char* func, int l, + nrappkitCallback** handle) { + nrappkitScheduledCallback* callback( + new nrappkitScheduledCallback(cb, arg, func, l)); + + nsresult rv = GetSTSThread()->Dispatch( + WrapRunnable(UniquePtr<nrappkitScheduledCallback>(callback), + &nrappkitScheduledCallback::Run), + NS_DISPATCH_NORMAL); + if (NS_FAILED(rv)) return R_FAILED; + + *handle = callback; + + // On exit to this function, the only strong reference to callback is in + // the Runnable. Because we are redispatching to the same thread, + // this is always safe. + return 0; +} + +static int nr_async_timer_set_nonzero(int timeout, NR_async_cb cb, void* arg, + char* func, int l, + nrappkitCallback** handle) { + nsresult rv; + CheckSTSThread(); + + nrappkitTimerCallback* callback = new nrappkitTimerCallback(cb, arg, func, l); + + nsCOMPtr<nsITimer> timer; + rv = NS_NewTimerWithCallback(getter_AddRefs(timer), callback, timeout, + nsITimer::TYPE_ONE_SHOT); + if (NS_FAILED(rv)) { + return R_FAILED; + } + + // Move the ownership of the timer to the callback object, which holds the + // timer alive per spec. + callback->SetTimer(timer.forget()); + + *handle = callback; + + return 0; +} + +int NR_async_timer_set(int timeout, NR_async_cb cb, void* arg, char* func, + int l, void** handle) { + CheckSTSThread(); + + nrappkitCallback* callback; + int r; + + if (!timeout) { + r = nr_async_timer_set_zero(cb, arg, func, l, &callback); + } else { + r = nr_async_timer_set_nonzero(timeout, cb, arg, func, l, &callback); + } + + if (r) return r; + + if (handle) *handle = callback; + + return 0; +} + +int NR_async_schedule(NR_async_cb cb, void* arg, char* func, int l) { + // No need to check the thread because we check it next in the + // timer set. + return NR_async_timer_set(0, cb, arg, func, l, nullptr); +} + +int NR_async_timer_cancel(void* handle) { + // Check for the handle being nonzero because sometimes we get + // no-op cancels that aren't on the STS thread. This can be + // non-racy as long as the upper-level code is careful. + if (!handle) return 0; + + CheckSTSThread(); + + nrappkitCallback* callback = static_cast<nrappkitCallback*>(handle); + callback->Cancel(); + + return 0; +} diff --git a/dom/media/webrtc/transport/nricectx.cpp b/dom/media/webrtc/transport/nricectx.cpp new file mode 100644 index 0000000000..697eecdb07 --- /dev/null +++ b/dom/media/webrtc/transport/nricectx.cpp @@ -0,0 +1,1106 @@ +/* -*- 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 + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <string> +#include <vector> + +#include "nr_socket_proxy_config.h" +#include "nsXULAppAPI.h" + +#include "logging.h" +#include "pk11pub.h" +#include "plbase64.h" + +#include "nsCOMPtr.h" +#include "nsError.h" +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" +#include "ScopedNSSTypes.h" +#include "runnable_utils.h" +#include "nsIUUIDGenerator.h" + +// nICEr includes +extern "C" { +#include "nr_api.h" +#include "registry.h" +#include "async_timer.h" +#include "r_crc32.h" +#include "r_memory.h" +#include "ice_reg.h" +#include "transport_addr.h" +#include "nr_crypto.h" +#include "nr_socket.h" +#include "nr_socket_local.h" +#include "stun_reg.h" +#include "stun_util.h" +#include "ice_codeword.h" +#include "ice_ctx.h" +#include "ice_candidate.h" +} + +// Local includes +#include "nricectx.h" +#include "nricemediastream.h" +#include "nr_socket_prsock.h" +#include "nrinterfaceprioritizer.h" +#include "rlogconnector.h" +#include "test_nr_socket.h" + +namespace mozilla { + +using std::shared_ptr; + +TimeStamp nr_socket_short_term_violation_time() { + return NrSocketBase::short_term_violation_time(); +} + +TimeStamp nr_socket_long_term_violation_time() { + return NrSocketBase::long_term_violation_time(); +} + +MOZ_MTLOG_MODULE("mtransport") + +const char kNrIceTransportUdp[] = "udp"; +const char kNrIceTransportTcp[] = "tcp"; +const char kNrIceTransportTls[] = "tls"; + +static bool initialized = false; + +static int noop(void** obj) { return 0; } + +static nr_socket_factory_vtbl ctx_socket_factory_vtbl = {nr_socket_local_create, + noop}; + +// Implement NSPR-based crypto algorithms +static int nr_crypto_nss_random_bytes(UCHAR* buf, size_t len) { + UniquePK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) return R_INTERNAL; + + SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), buf, len); + if (rv != SECSuccess) return R_INTERNAL; + + return 0; +} + +static int nr_crypto_nss_hmac(UCHAR* key, size_t keyl, UCHAR* buf, size_t bufl, + UCHAR* result) { + CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC; + PK11SlotInfo* slot = nullptr; + MOZ_ASSERT(keyl > 0); + SECItem keyi = {siBuffer, key, static_cast<unsigned int>(keyl)}; + PK11SymKey* skey = nullptr; + PK11Context* hmac_ctx = nullptr; + SECStatus status; + unsigned int hmac_len; + SECItem param = {siBuffer, nullptr, 0}; + int err = R_INTERNAL; + + slot = PK11_GetInternalKeySlot(); + if (!slot) goto abort; + + skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_SIGN, &keyi, + nullptr); + if (!skey) goto abort; + + hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, skey, ¶m); + if (!hmac_ctx) goto abort; + + status = PK11_DigestBegin(hmac_ctx); + if (status != SECSuccess) goto abort; + + status = PK11_DigestOp(hmac_ctx, buf, bufl); + if (status != SECSuccess) goto abort; + + status = PK11_DigestFinal(hmac_ctx, result, &hmac_len, 20); + if (status != SECSuccess) goto abort; + + MOZ_ASSERT(hmac_len == 20); + + err = 0; + +abort: + if (hmac_ctx) PK11_DestroyContext(hmac_ctx, PR_TRUE); + if (skey) PK11_FreeSymKey(skey); + if (slot) PK11_FreeSlot(slot); + + return err; +} + +static int nr_crypto_nss_md5(UCHAR* buf, size_t bufl, UCHAR* result) { + int err = R_INTERNAL; + SECStatus rv; + + const SECHashObject* ho = HASH_GetHashObject(HASH_AlgMD5); + MOZ_ASSERT(ho); + if (!ho) goto abort; + + MOZ_ASSERT(ho->length == 16); + + rv = HASH_HashBuf(ho->type, result, buf, bufl); + if (rv != SECSuccess) goto abort; + + err = 0; +abort: + return err; +} + +static nr_ice_crypto_vtbl nr_ice_crypto_nss_vtbl = { + nr_crypto_nss_random_bytes, nr_crypto_nss_hmac, nr_crypto_nss_md5}; + +nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server* server) const { + int r; + + memset(server, 0, sizeof(nr_ice_stun_server)); + uint8_t protocol; + if (transport_ == kNrIceTransportUdp) { + protocol = IPPROTO_UDP; + } else if (transport_ == kNrIceTransportTcp) { + protocol = IPPROTO_TCP; + } else if (transport_ == kNrIceTransportTls) { + protocol = IPPROTO_TCP; + } else { + MOZ_MTLOG(ML_ERROR, "Unsupported STUN server transport: " << transport_); + return NS_ERROR_FAILURE; + } + + if (has_addr_) { + if (transport_ == kNrIceTransportTls) { + // Refuse to try TLS without an FQDN + return NS_ERROR_INVALID_ARG; + } + r = nr_praddr_to_transport_addr(&addr_, &server->addr, protocol, 0); + if (r) { + return NS_ERROR_FAILURE; + } + } else { + MOZ_ASSERT(sizeof(server->addr.fqdn) > host_.size()); + // Dummy information to keep nICEr happy + if (use_ipv6_if_fqdn_) { + nr_str_port_to_transport_addr("::", port_, protocol, &server->addr); + } else { + nr_str_port_to_transport_addr("0.0.0.0", port_, protocol, &server->addr); + } + PL_strncpyz(server->addr.fqdn, host_.c_str(), sizeof(server->addr.fqdn)); + if (transport_ == kNrIceTransportTls) { + server->addr.tls = 1; + } + } + + nr_transport_addr_fmt_addr_string(&server->addr); + + 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); + if (NS_FAILED(rv)) return rv; + + if (!(server->username = r_strdup(username_.c_str()))) + return NS_ERROR_OUT_OF_MEMORY; + + // TODO(ekr@rtfm.com): handle non-ASCII passwords somehow? + // STUN requires they be SASLpreped, but we don't know if + // they are at this point. + + // C++03 23.2.4, Paragraph 1 stipulates that the elements + // in std::vector must be contiguous, and can therefore be + // used as input to functions expecting C arrays. + const UCHAR* data = password_.empty() ? nullptr : &password_[0]; + int r = r_data_create(&server->password, data, password_.size()); + if (r) { + RFREE(server->username); + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +NrIceCtx::NrIceCtx(const std::string& name) + : connection_state_(ICE_CTX_INIT), + gathering_state_(ICE_CTX_GATHER_INIT), + name_(name), + ice_controlling_set_(false), + streams_(), + ctx_(nullptr), + peer_(nullptr), + ice_handler_vtbl_(nullptr), + ice_handler_(nullptr), + trickle_(true), + config_(), + nat_(nullptr), + proxy_config_(nullptr) {} + +/* static */ +RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& aName) { + RefPtr<NrIceCtx> ctx = new NrIceCtx(aName); + + if (!ctx->Initialize()) { + return nullptr; + } + + return ctx; +} + +nsresult NrIceCtx::SetIceConfig(const Config& aConfig) { + config_ = aConfig; + switch (config_.mPolicy) { + case ICE_POLICY_RELAY: + MOZ_MTLOG(ML_DEBUG, "SetIceConfig: relay only"); + nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES); + nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_RELAY_ONLY); + break; + case ICE_POLICY_NO_HOST: + MOZ_MTLOG(ML_DEBUG, "SetIceConfig: no host"); + nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES); + nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_RELAY_ONLY); + break; + case ICE_POLICY_ALL: + MOZ_MTLOG(ML_DEBUG, "SetIceConfig: all"); + nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES); + nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_RELAY_ONLY); + break; + } + + // TODO: Support re-configuring the test NAT someday? + if (!nat_ && config_.mNatSimulatorConfig.isSome()) { + TestNat* test_nat = new TestNat; + test_nat->filtering_type_ = TestNat::ToNatBehavior( + config_.mNatSimulatorConfig->mFilteringType.get()); + test_nat->mapping_type_ = + TestNat::ToNatBehavior(config_.mNatSimulatorConfig->mMappingType.get()); + test_nat->block_udp_ = config_.mNatSimulatorConfig->mBlockUdp; + test_nat->block_tcp_ = config_.mNatSimulatorConfig->mBlockTcp; + test_nat->block_tls_ = config_.mNatSimulatorConfig->mBlockTls; + test_nat->error_code_for_drop_ = + config_.mNatSimulatorConfig->mErrorCodeForDrop; + if (config_.mNatSimulatorConfig->mRedirectAddress.Length()) { + test_nat + ->stun_redirect_map_[config_.mNatSimulatorConfig->mRedirectAddress] = + config_.mNatSimulatorConfig->mRedirectTargets; + } + test_nat->enabled_ = true; + SetNat(test_nat); + } + + return NS_OK; +} + +RefPtr<NrIceMediaStream> NrIceCtx::CreateStream(const std::string& id, + const std::string& name, + int components) { + if (streams_.count(id)) { + MOZ_ASSERT(false); + return nullptr; + } + + RefPtr<NrIceMediaStream> stream = + new NrIceMediaStream(this, id, name, components); + streams_[id] = stream; + return stream; +} + +void NrIceCtx::DestroyStream(const std::string& id) { + auto it = streams_.find(id); + if (it != streams_.end()) { + auto preexisting_stream = it->second; + streams_.erase(it); + preexisting_stream->Close(); + } + + if (streams_.empty()) { + SetGatheringState(ICE_CTX_GATHER_INIT); + } +} + +// Handler callbacks +int NrIceCtx::select_pair(void* obj, nr_ice_media_stream* stream, + int component_id, nr_ice_cand_pair** potentials, + int potential_ct) { + MOZ_MTLOG(ML_DEBUG, "select pair called: potential_ct = " << potential_ct); + MOZ_ASSERT(stream->local_stream); + MOZ_ASSERT(!stream->local_stream->obsolete); + + return 0; +} + +int NrIceCtx::stream_ready(void* obj, nr_ice_media_stream* stream) { + MOZ_MTLOG(ML_DEBUG, "stream_ready called"); + MOZ_ASSERT(!stream->local_stream); + MOZ_ASSERT(!stream->obsolete); + + // Get the ICE ctx. + NrIceCtx* ctx = static_cast<NrIceCtx*>(obj); + + RefPtr<NrIceMediaStream> s = ctx->FindStream(stream); + + // Streams which do not exist should never be ready. + MOZ_ASSERT(s); + + s->Ready(); + + return 0; +} + +int NrIceCtx::stream_failed(void* obj, nr_ice_media_stream* stream) { + MOZ_MTLOG(ML_DEBUG, "stream_failed called"); + MOZ_ASSERT(!stream->local_stream); + MOZ_ASSERT(!stream->obsolete); + + // Get the ICE ctx + NrIceCtx* ctx = static_cast<NrIceCtx*>(obj); + RefPtr<NrIceMediaStream> s = ctx->FindStream(stream); + + // Streams which do not exist should never fail. + MOZ_ASSERT(s); + + ctx->SetConnectionState(ICE_CTX_FAILED); + s->Failed(); + return 0; +} + +int NrIceCtx::ice_checking(void* obj, nr_ice_peer_ctx* pctx) { + MOZ_MTLOG(ML_DEBUG, "ice_checking called"); + + // Get the ICE ctx + NrIceCtx* ctx = static_cast<NrIceCtx*>(obj); + + ctx->SetConnectionState(ICE_CTX_CHECKING); + + return 0; +} + +int NrIceCtx::ice_connected(void* obj, nr_ice_peer_ctx* pctx) { + MOZ_MTLOG(ML_DEBUG, "ice_connected called"); + + // Get the ICE ctx + NrIceCtx* ctx = static_cast<NrIceCtx*>(obj); + + // This is called even on failed contexts. + if (ctx->connection_state() != ICE_CTX_FAILED) { + ctx->SetConnectionState(ICE_CTX_CONNECTED); + } + + return 0; +} + +int NrIceCtx::ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) { + MOZ_MTLOG(ML_DEBUG, "ice_disconnected called"); + + // Get the ICE ctx + NrIceCtx* ctx = static_cast<NrIceCtx*>(obj); + + ctx->SetConnectionState(ICE_CTX_DISCONNECTED); + + return 0; +} + +int NrIceCtx::msg_recvd(void* obj, nr_ice_peer_ctx* pctx, + nr_ice_media_stream* stream, int component_id, + UCHAR* msg, int len) { + // Get the ICE ctx + NrIceCtx* ctx = static_cast<NrIceCtx*>(obj); + RefPtr<NrIceMediaStream> s = ctx->FindStream(stream); + + // Streams which do not exist should never have packets. + MOZ_ASSERT(s); + + s->SignalPacketReceived(s, component_id, msg, len); + + return 0; +} + +void NrIceCtx::trickle_cb(void* arg, nr_ice_ctx* ice_ctx, + nr_ice_media_stream* stream, int component_id, + nr_ice_candidate* candidate) { + if (stream->obsolete) { + // Stream was probably just marked obsolete, resulting in this callback + return; + } + // Get the ICE ctx + NrIceCtx* ctx = static_cast<NrIceCtx*>(arg); + RefPtr<NrIceMediaStream> s = ctx->FindStream(stream); + + if (!s) { + // This stream has been removed because it is inactive + return; + } + + if (!candidate) { + s->SignalCandidate(s, "", stream->ufrag, "", ""); + return; + } + + std::string actual_addr; + std::string mdns_addr; + ctx->GenerateObfuscatedAddress(candidate, &mdns_addr, &actual_addr); + + // Format the candidate. + char candidate_str[NR_ICE_MAX_ATTRIBUTE_SIZE]; + int r = nr_ice_format_candidate_attribute( + candidate, candidate_str, sizeof(candidate_str), + (ctx->ctx()->flags & NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES) ? 1 : 0); + MOZ_ASSERT(!r); + if (r) return; + + MOZ_MTLOG(ML_INFO, "NrIceCtx(" << ctx->name_ << "): trickling candidate " + << candidate_str); + + s->SignalCandidate(s, candidate_str, stream->ufrag, mdns_addr, actual_addr); +} + +void NrIceCtx::InitializeGlobals(const GlobalConfig& aConfig) { + RLogConnector::CreateInstance(); + // Initialize the crypto callbacks and logging stuff + if (!initialized) { + NR_reg_init(NR_REG_MODE_LOCAL); + nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl; + initialized = true; + + // Set the priorites for candidate type preferences. + // These numbers come from RFC 5245 S. 4.1.2.2 + NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_SRV_RFLX, 100); + NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_PEER_RFLX, 110); + NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_HOST, 126); + NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_RELAYED, 5); + NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP, 99); + NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP, 109); + NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_HOST_TCP, 125); + NR_reg_set_uchar((char*)NR_ICE_REG_PREF_TYPE_RELAYED_TCP, 0); + NR_reg_set_uint4((char*)"stun.client.maximum_transmits", + aConfig.mStunClientMaxTransmits); + NR_reg_set_uint4((char*)NR_ICE_REG_TRICKLE_GRACE_PERIOD, + aConfig.mTrickleIceGracePeriod); + NR_reg_set_int4((char*)NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT, + aConfig.mIceTcpSoSockCount); + NR_reg_set_int4((char*)NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG, + aConfig.mIceTcpListenBacklog); + + NR_reg_set_char((char*)NR_ICE_REG_ICE_TCP_DISABLE, !aConfig.mTcpEnabled); + + if (aConfig.mAllowLoopback) { + NR_reg_set_char((char*)NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, 1); + } + + if (aConfig.mAllowLinkLocal) { + NR_reg_set_char((char*)NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, 1); + } + if (!aConfig.mForceNetInterface.Length()) { + NR_reg_set_string((char*)NR_ICE_REG_PREF_FORCE_INTERFACE_NAME, + const_cast<char*>(aConfig.mForceNetInterface.get())); + } + + // For now, always use nr_resolver for UDP. + NR_reg_set_char((char*)NR_ICE_REG_USE_NR_RESOLVER_FOR_UDP, 1); + + // Use nr_resolver for TCP only when not in e10s mode (for unit-tests) + if (XRE_IsParentProcess()) { + NR_reg_set_char((char*)NR_ICE_REG_USE_NR_RESOLVER_FOR_TCP, 1); + } + } +} + +void NrIceCtx::SetTargetForDefaultLocalAddressLookup( + const std::string& target_ip, uint16_t target_port) { + nr_ice_set_target_for_default_local_address_lookup(ctx_, target_ip.c_str(), + target_port); +} + +#define MAXADDRS 100 // mirrors setting in ice_ctx.c + +/* static */ +nsTArray<NrIceStunAddr> NrIceCtx::GetStunAddrs() { + nsTArray<NrIceStunAddr> addrs; + + nr_local_addr local_addrs[MAXADDRS]; + int addr_ct = 0; + + // most likely running on parent process and need crypto vtbl + // initialized on Windows (Linux and OSX don't seem to care) + if (!initialized) { + nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl; + } + + MOZ_MTLOG(ML_INFO, "NrIceCtx static call to find local stun addresses"); + if (nr_stun_find_local_addresses(local_addrs, MAXADDRS, &addr_ct)) { + MOZ_MTLOG(ML_INFO, "Error finding local stun addresses"); + } else { + for (int i = 0; i < addr_ct; ++i) { + NrIceStunAddr addr(&local_addrs[i]); + addrs.AppendElement(addr); + } + } + + return addrs; +} + +void NrIceCtx::SetStunAddrs(const nsTArray<NrIceStunAddr>& addrs) { + nr_local_addr* local_addrs; + local_addrs = new nr_local_addr[addrs.Length()]; + + for (size_t i = 0; i < addrs.Length(); ++i) { + nr_local_addr_copy(&local_addrs[i], + const_cast<nr_local_addr*>(&addrs[i].localAddr())); + } + nr_ice_set_local_addresses(ctx_, local_addrs, addrs.Length()); + + delete[] local_addrs; +} + +bool NrIceCtx::Initialize() { + // Create the ICE context + int r; + + UINT4 flags = NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION; + r = nr_ice_ctx_create(const_cast<char*>(name_.c_str()), flags, &ctx_); + + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name_ << "'"); + return false; + } + + // override default factory to capture optional proxy config when creating + // sockets. + nr_socket_factory* factory; + r = nr_socket_factory_create_int(this, &ctx_socket_factory_vtbl, &factory); + + if (r) { + MOZ_MTLOG(LogLevel::Error, "Couldn't create ctx socket factory."); + return false; + } + nr_ice_ctx_set_socket_factory(ctx_, factory); + + nr_interface_prioritizer* prioritizer = CreateInterfacePrioritizer(); + if (!prioritizer) { + MOZ_MTLOG(LogLevel::Error, "Couldn't create interface prioritizer."); + return false; + } + + r = nr_ice_ctx_set_interface_prioritizer(ctx_, prioritizer); + if (r) { + MOZ_MTLOG(LogLevel::Error, "Couldn't set interface prioritizer."); + return false; + } + + if (generating_trickle()) { + r = nr_ice_ctx_set_trickle_cb(ctx_, &NrIceCtx::trickle_cb, this); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << name_ << "'"); + return false; + } + } + + // Create the handler objects + ice_handler_vtbl_ = new nr_ice_handler_vtbl(); + ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair; + ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready; + ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed; + ice_handler_vtbl_->ice_connected = &NrIceCtx::ice_connected; + ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd; + ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking; + ice_handler_vtbl_->ice_disconnected = &NrIceCtx::ice_disconnected; + + ice_handler_ = new nr_ice_handler(); + ice_handler_->vtbl = ice_handler_vtbl_; + ice_handler_->obj = this; + + // Create the peer ctx. Because we do not support parallel forking, we + // only have one peer ctx. + std::string peer_name = name_ + ":default"; + r = nr_ice_peer_ctx_create(ctx_, ice_handler_, + const_cast<char*>(peer_name.c_str()), &peer_); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't create ICE peer ctx for '" << name_ << "'"); + return false; + } + + nsresult rv; + sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (!NS_SUCCEEDED(rv)) return false; + + return true; +} + +int NrIceCtx::SetNat(const RefPtr<TestNat>& aNat) { + nat_ = aNat; + nr_socket_factory* fac; + int r = nat_->create_socket_factory(&fac); + if (r) { + return r; + } + nr_ice_ctx_set_socket_factory(ctx_, fac); + return 0; +} + +// ONLY USE THIS FOR TESTING. Will cause totally unpredictable and possibly very +// bad effects if ICE is still live. +void NrIceCtx::internal_DeinitializeGlobal() { + NR_reg_del((char*)"stun"); + NR_reg_del((char*)"ice"); + RLogConnector::DestroyInstance(); + nr_crypto_vtbl = nullptr; + initialized = false; +} + +void NrIceCtx::internal_SetTimerAccelarator(int divider) { + ctx_->test_timer_divider = divider; +} + +void NrIceCtx::AccumulateStats(const NrIceStats& stats) { + nr_accumulate_count(&(ctx_->stats.stun_retransmits), stats.stun_retransmits); + nr_accumulate_count(&(ctx_->stats.turn_401s), stats.turn_401s); + nr_accumulate_count(&(ctx_->stats.turn_403s), stats.turn_403s); + nr_accumulate_count(&(ctx_->stats.turn_438s), stats.turn_438s); +} + +NrIceStats NrIceCtx::Destroy() { + // designed to be called more than once so if stats are desired, this can be + // called just prior to the destructor + MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__); + + for (auto& idAndStream : streams_) { + idAndStream.second->Close(); + } + + NrIceStats stats; + if (ctx_) { + stats.stun_retransmits = ctx_->stats.stun_retransmits; + stats.turn_401s = ctx_->stats.turn_401s; + stats.turn_403s = ctx_->stats.turn_403s; + stats.turn_438s = ctx_->stats.turn_438s; + } + + if (peer_) { + nr_ice_peer_ctx_destroy(&peer_); + } + if (ctx_) { + nr_ice_ctx_destroy(&ctx_); + } + + delete ice_handler_vtbl_; + delete ice_handler_; + + ice_handler_vtbl_ = nullptr; + ice_handler_ = nullptr; + proxy_config_ = nullptr; + streams_.clear(); + + return stats; +} + +NrIceCtx::~NrIceCtx() = default; + +void NrIceCtx::destroy_peer_ctx() { nr_ice_peer_ctx_destroy(&peer_); } + +nsresult NrIceCtx::SetControlling(Controlling controlling) { + if (!ice_controlling_set_) { + peer_->controlling = (controlling == ICE_CONTROLLING) ? 1 : 0; + ice_controlling_set_ = true; + + MOZ_MTLOG(ML_DEBUG, + "ICE ctx " << name_ << " setting controlling to" << controlling); + } + return NS_OK; +} + +NrIceCtx::Controlling NrIceCtx::GetControlling() { + return (peer_->controlling) ? ICE_CONTROLLING : ICE_CONTROLLED; +} + +nsresult NrIceCtx::SetStunServers( + const std::vector<NrIceStunServer>& stun_servers) { + MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__); + // We assume nr_ice_stun_server is memmoveable. That's true right now. + std::vector<nr_ice_stun_server> servers; + + for (size_t i = 0; i < stun_servers.size(); ++i) { + nr_ice_stun_server server; + nsresult rv = stun_servers[i].ToNicerStunStruct(&server); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_MTLOG(ML_ERROR, "Couldn't convert STUN server for '" << name_ << "'"); + } else { + servers.push_back(server); + } + } + + int r = nr_ice_ctx_set_stun_servers(ctx_, servers.data(), + static_cast<int>(servers.size())); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't set STUN servers for '" << name_ << "'"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +// TODO(ekr@rtfm.com): This is just SetStunServers with s/Stun/Turn +// Could we do a template or something? +nsresult NrIceCtx::SetTurnServers( + const std::vector<NrIceTurnServer>& turn_servers) { + MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__); + // We assume nr_ice_turn_server is memmoveable. That's true right now. + std::vector<nr_ice_turn_server> servers; + + for (size_t i = 0; i < turn_servers.size(); ++i) { + nr_ice_turn_server server; + nsresult rv = turn_servers[i].ToNicerTurnStruct(&server); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_MTLOG(ML_ERROR, "Couldn't convert TURN server for '" << name_ << "'"); + } else { + servers.push_back(server); + } + } + + int r = nr_ice_ctx_set_turn_servers(ctx_, servers.data(), + static_cast<int>(servers.size())); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't set TURN servers for '" << name_ << "'"); + // TODO(ekr@rtfm.com): This leaks the username/password. Need to free that. + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult NrIceCtx::SetResolver(nr_resolver* resolver) { + int r = nr_ice_ctx_set_resolver(ctx_, resolver); + + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't set resolver for '" << name_ << "'"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult NrIceCtx::SetProxyConfig(NrSocketProxyConfig&& config) { + proxy_config_.reset(new NrSocketProxyConfig(std::move(config))); + if (nat_) { + nat_->set_proxy_config(proxy_config_); + } + + if (proxy_config_->GetForceProxy()) { + nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_PROXY); + } else { + nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_PROXY); + } + + return NS_OK; +} + +void NrIceCtx::SetCtxFlags(bool default_route_only) { + ASSERT_ON_THREAD(sts_target_); + + if (default_route_only) { + nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS); + } else { + nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS); + } +} + +nsresult NrIceCtx::StartGathering(bool default_route_only, + bool obfuscate_host_addresses) { + ASSERT_ON_THREAD(sts_target_); + MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__); + + if (obfuscate_host_addresses) { + nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES); + } + + SetCtxFlags(default_route_only); + + // This might start gathering for the first time, or again after + // renegotiation, or might do nothing at all if gathering has already + // finished. + int r = nr_ice_gather(ctx_, &NrIceCtx::gather_cb, this); + + if (!r) { + SetGatheringState(ICE_CTX_GATHER_COMPLETE); + } else if (r == R_WOULDBLOCK) { + SetGatheringState(ICE_CTX_GATHER_STARTED); + } else { + SetGatheringState(ICE_CTX_GATHER_COMPLETE); + MOZ_MTLOG(ML_ERROR, "ICE FAILED: Couldn't gather ICE candidates for '" + << name_ << "', error=" << r); + SetConnectionState(ICE_CTX_FAILED); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +RefPtr<NrIceMediaStream> NrIceCtx::FindStream(nr_ice_media_stream* stream) { + for (auto& idAndStream : streams_) { + if (idAndStream.second->HasStream(stream)) { + return idAndStream.second; + } + } + + return nullptr; +} + +std::vector<std::string> NrIceCtx::GetGlobalAttributes() { + char** attrs = nullptr; + int attrct; + int r; + std::vector<std::string> ret; + + r = nr_ice_get_global_attributes(ctx_, &attrs, &attrct); + if (r) { + MOZ_MTLOG(ML_ERROR, + "Couldn't get ufrag and password for '" << name_ << "'"); + return ret; + } + + for (int i = 0; i < attrct; i++) { + ret.push_back(std::string(attrs[i])); + RFREE(attrs[i]); + } + RFREE(attrs); + + return ret; +} + +nsresult NrIceCtx::ParseGlobalAttributes(std::vector<std::string> attrs) { + std::vector<char*> attrs_in; + attrs_in.reserve(attrs.size()); + for (auto& attr : attrs) { + attrs_in.push_back(const_cast<char*>(attr.c_str())); + } + + int r = nr_ice_peer_ctx_parse_global_attributes( + peer_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size()); + if (r) { + MOZ_MTLOG(ML_ERROR, + "Couldn't parse global attributes for " << name_ << "'"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +bool NrIceCtx::HasStreamsToConnect() const { + for (auto& idAndStream : streams_) { + if (idAndStream.second->state() != NrIceMediaStream::ICE_CLOSED) { + return true; + } + } + return false; +} + +nsresult NrIceCtx::StartChecks() { + int r; + MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__); + + if (!HasStreamsToConnect()) { + MOZ_MTLOG(ML_NOTICE, "In StartChecks, nothing to do on " << name_); + return NS_OK; + } + + r = nr_ice_peer_ctx_pair_candidates(peer_); + if (r) { + MOZ_MTLOG(ML_ERROR, "ICE FAILED: Couldn't pair candidates on " << name_); + SetConnectionState(ICE_CTX_FAILED); + return NS_ERROR_FAILURE; + } + + r = nr_ice_peer_ctx_start_checks2(peer_, 1); + if (r) { + if (r == R_NOT_FOUND) { + MOZ_MTLOG(ML_INFO, "Couldn't start peer checks on " + << name_ << ", assuming trickle ICE"); + } else { + MOZ_MTLOG(ML_ERROR, + "ICE FAILED: Couldn't start peer checks on " << name_); + SetConnectionState(ICE_CTX_FAILED); + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +void NrIceCtx::gather_cb(NR_SOCKET s, int h, void* arg) { + NrIceCtx* ctx = static_cast<NrIceCtx*>(arg); + + ctx->SetGatheringState(ICE_CTX_GATHER_COMPLETE); +} + +void NrIceCtx::UpdateNetworkState(bool online) { + MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): updating network state to " + << (online ? "online" : "offline")); + if (connection_state_ == ICE_CTX_CLOSED) { + return; + } + + if (online) { + nr_ice_peer_ctx_refresh_consent_all_streams(peer_); + } else { + nr_ice_peer_ctx_disconnect_all_streams(peer_); + } +} + +void NrIceCtx::SetConnectionState(ConnectionState state) { + if (state == connection_state_) return; + + MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): state " << connection_state_ + << "->" << state); + connection_state_ = state; + + if (connection_state_ == ICE_CTX_FAILED) { + MOZ_MTLOG(ML_INFO, + "NrIceCtx(" << name_ << "): dumping r_log ringbuffer... "); + std::deque<std::string> logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + for (auto& log : logs) { + MOZ_MTLOG(ML_INFO, log); + } + } + + SignalConnectionStateChange(this, state); +} + +void NrIceCtx::SetGatheringState(GatheringState state) { + if (state == gathering_state_) return; + + MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << name_ << "): gathering state " + << gathering_state_ << "->" << state); + gathering_state_ = state; + + SignalGatheringStateChange(this, state); +} + +void NrIceCtx::GenerateObfuscatedAddress(nr_ice_candidate* candidate, + std::string* mdns_address, + std::string* actual_address) { + if (candidate->type == HOST && + (ctx_->flags & NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES)) { + char addr[64]; + if (nr_transport_addr_get_addrstring(&candidate->addr, addr, + sizeof(addr))) { + return; + } + + *actual_address = addr; + + const auto& iter = obfuscated_host_addresses_.find(*actual_address); + if (iter != obfuscated_host_addresses_.end()) { + *mdns_address = iter->second; + } else { + nsresult rv; + nsCOMPtr<nsIUUIDGenerator> uuidgen = + do_GetService("@mozilla.org/uuid-generator;1", &rv); + // If this fails, we'll return a zero UUID rather than something + // unexpected. + nsID id = {}; + id.Clear(); + if (NS_SUCCEEDED(rv)) { + rv = uuidgen->GenerateUUIDInPlace(&id); + if (NS_FAILED(rv)) { + id.Clear(); + } + } + + char chars[NSID_LENGTH]; + id.ToProvidedString(chars); + // The string will look like {64888863-a253-424a-9b30-1ed285d20142}, + // we want to trim off the braces. + const char* ptr_to_id = chars; + ++ptr_to_id; + chars[NSID_LENGTH - 2] = 0; + + std::ostringstream o; + o << ptr_to_id << ".local"; + *mdns_address = o.str(); + + obfuscated_host_addresses_[*actual_address] = *mdns_address; + } + candidate->mdns_addr = r_strdup(mdns_address->c_str()); + } +} + +} // namespace mozilla + +// Reimplement nr_ice_compute_codeword to avoid copyright issues +void nr_ice_compute_codeword(char* buf, int len, char* codeword) { + UINT4 c; + + r_crc32(buf, len, &c); + + PL_Base64Encode(reinterpret_cast<char*>(&c), 3, codeword); + codeword[4] = 0; +} + +int nr_socket_local_create(void* obj, nr_transport_addr* addr, + nr_socket** sockp) { + using namespace mozilla; + + RefPtr<NrSocketBase> sock; + int r, _status; + shared_ptr<NrSocketProxyConfig> config = nullptr; + + if (obj) { + config = static_cast<NrIceCtx*>(obj)->GetProxyConfig(); + } + + r = NrSocketBase::CreateSocket(addr, &sock, config); + if (r) { + ABORT(r); + } + + r = nr_socket_create_int(static_cast<void*>(sock), sock->vtbl(), sockp); + if (r) ABORT(r); + + _status = 0; + + { + // We will release this reference in destroy(), not exactly the normal + // ownership model, but it is what it is. + NrSocketBase* dummy = sock.forget().take(); + (void)dummy; + } + +abort: + return _status; +} diff --git a/dom/media/webrtc/transport/nricectx.h b/dom/media/webrtc/transport/nricectx.h new file mode 100644 index 0000000000..a0a0b5b772 --- /dev/null +++ b/dom/media/webrtc/transport/nricectx.h @@ -0,0 +1,421 @@ +/* -*- 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 + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + +/* 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 + +// This is a wrapper around the nICEr ICE stack +#ifndef nricectx_h__ +#define nricectx_h__ + +#include <memory> +#include <string> +#include <vector> +#include <map> + +#include "sigslot.h" + +#include "prnetdb.h" + +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "nsIEventTarget.h" +#include "nsTArray.h" +#include "mozilla/Maybe.h" + +#include "m_cpp_utils.h" +#include "nricestunaddr.h" +#include "nricemediastream.h" + +typedef struct nr_ice_ctx_ nr_ice_ctx; +typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx; +typedef struct nr_ice_media_stream_ nr_ice_media_stream; +typedef struct nr_ice_handler_ nr_ice_handler; +typedef struct nr_ice_handler_vtbl_ nr_ice_handler_vtbl; +typedef struct nr_ice_candidate_ nr_ice_candidate; +typedef struct nr_ice_cand_pair_ nr_ice_cand_pair; +typedef struct nr_ice_stun_server_ nr_ice_stun_server; +typedef struct nr_ice_turn_server_ nr_ice_turn_server; +typedef struct nr_resolver_ nr_resolver; +typedef struct nr_proxy_tunnel_config_ nr_proxy_tunnel_config; + +typedef void* NR_SOCKET; + +namespace mozilla { + +class NrSocketProxyConfig; + +class NrIceMediaStream; + +extern const char kNrIceTransportUdp[]; +extern const char kNrIceTransportTcp[]; +extern const char kNrIceTransportTls[]; + +class NrIceStunServer { + public: + explicit NrIceStunServer(const PRNetAddr& addr) : has_addr_(true) { + memcpy(&addr_, &addr, sizeof(addr)); + } + + // The main function to use. Will take either an address or a hostname. + static UniquePtr<NrIceStunServer> Create( + const std::string& addr, uint16_t port, + const char* transport = kNrIceTransportUdp) { + UniquePtr<NrIceStunServer> server(new NrIceStunServer(transport)); + + nsresult rv = server->Init(addr, port); + if (NS_FAILED(rv)) return nullptr; + + return server; + } + + nsresult ToNicerStunStruct(nr_ice_stun_server* server) const; + + bool HasFqdn() const { return !has_addr_; } + + void SetUseIPv6IfFqdn() { + MOZ_ASSERT(HasFqdn()); + use_ipv6_if_fqdn_ = true; + } + + protected: + explicit NrIceStunServer(const char* transport) + : addr_(), transport_(transport) {} + + 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 + addr_.inet.port = PR_htons(port); + port_ = port; + has_addr_ = true; + return NS_OK; + } else if (addr.size() < 256) { + // Apparently this is a hostname. + host_ = addr; + port_ = port; + has_addr_ = false; + return NS_OK; + } + + return NS_ERROR_FAILURE; + } + + bool has_addr_; + std::string host_; + uint16_t port_; + PRNetAddr addr_; + std::string transport_; + bool use_ipv6_if_fqdn_ = false; +}; + +class NrIceTurnServer : public NrIceStunServer { + public: + static UniquePtr<NrIceTurnServer> Create( + const std::string& addr, uint16_t port, const std::string& username, + const std::vector<unsigned char>& password, + const char* transport = kNrIceTransportUdp) { + UniquePtr<NrIceTurnServer> server( + new NrIceTurnServer(username, password, transport)); + + nsresult rv = server->Init(addr, port); + if (NS_FAILED(rv)) return nullptr; + + return server; + } + + nsresult ToNicerTurnStruct(nr_ice_turn_server* server) const; + + private: + NrIceTurnServer(const std::string& username, + const std::vector<unsigned char>& password, + const char* transport) + : NrIceStunServer(transport), username_(username), password_(password) {} + + std::string username_; + std::vector<unsigned char> password_; +}; + +class TestNat; + +class NrIceStats { + public: + uint16_t stun_retransmits = 0; + uint16_t turn_401s = 0; + uint16_t turn_403s = 0; + uint16_t turn_438s = 0; +}; + +class NrIceCtx { + public: + enum ConnectionState { + ICE_CTX_INIT, + ICE_CTX_CHECKING, + ICE_CTX_CONNECTED, + ICE_CTX_COMPLETED, + ICE_CTX_FAILED, + ICE_CTX_DISCONNECTED, + ICE_CTX_CLOSED + }; + + enum GatheringState { + ICE_CTX_GATHER_INIT, + ICE_CTX_GATHER_STARTED, + ICE_CTX_GATHER_COMPLETE + }; + + enum Controlling { ICE_CONTROLLING, ICE_CONTROLLED }; + + enum Policy { ICE_POLICY_RELAY, ICE_POLICY_NO_HOST, ICE_POLICY_ALL }; + + struct NatSimulatorConfig { + bool mBlockTcp = false; + bool mBlockUdp = false; + bool mBlockTls = false; + int mErrorCodeForDrop = 0; + nsCString mMappingType = "ENDPOINT_INDEPENDENT"_ns; + nsCString mFilteringType = "ENDPOINT_INDEPENDENT"_ns; + nsCString mRedirectAddress; + CopyableTArray<nsCString> mRedirectTargets; + }; + + struct Config { + NrIceCtx::Policy mPolicy = NrIceCtx::ICE_POLICY_ALL; + Maybe<NatSimulatorConfig> mNatSimulatorConfig; + }; + + static RefPtr<NrIceCtx> Create(const std::string& aName); + + nsresult SetIceConfig(const Config& aConfig); + + RefPtr<NrIceMediaStream> CreateStream(const std::string& id, + const std::string& name, + int components); + void DestroyStream(const std::string& id); + + struct GlobalConfig { + bool mAllowLinkLocal = false; + bool mAllowLoopback = false; + bool mTcpEnabled = true; + int mStunClientMaxTransmits = 7; + int mTrickleIceGracePeriod = 5000; + int mIceTcpSoSockCount = 3; + int mIceTcpListenBacklog = 10; + nsCString mForceNetInterface; + }; + + // initialize ICE globals, crypto, and logging + static void InitializeGlobals(const GlobalConfig& aConfig); + + void SetTargetForDefaultLocalAddressLookup(const std::string& target_ip, + uint16_t target_port); + + // static GetStunAddrs for use in parent process to support + // sandboxing restrictions + static nsTArray<NrIceStunAddr> GetStunAddrs(); + void SetStunAddrs(const nsTArray<NrIceStunAddr>& addrs); + + bool Initialize(); + + int SetNat(const RefPtr<TestNat>& aNat); + + // Deinitialize all ICE global state. Used only for testing. + static void internal_DeinitializeGlobal(); + + // Divide some timers to faster testing. Used only for testing. + void internal_SetTimerAccelarator(int divider); + + nr_ice_ctx* ctx() { return ctx_; } + nr_ice_peer_ctx* peer() { return peer_; } + + // Testing only. + void destroy_peer_ctx(); + + RefPtr<NrIceMediaStream> GetStream(const std::string& id) { + auto it = streams_.find(id); + if (it != streams_.end()) { + return it->second; + } + return nullptr; + } + + std::vector<RefPtr<NrIceMediaStream>> GetStreams() const { + std::vector<RefPtr<NrIceMediaStream>> result; + for (auto& idAndStream : streams_) { + result.push_back(idAndStream.second); + } + return result; + } + + bool HasStreamsToConnect() const; + + // The name of the ctx + const std::string& name() const { return name_; } + + // Current state + ConnectionState connection_state() const { return connection_state_; } + + // Current state + GatheringState gathering_state() const { return gathering_state_; } + + // Get the global attributes + std::vector<std::string> GetGlobalAttributes(); + + // Set the other side's global attributes + nsresult ParseGlobalAttributes(std::vector<std::string> attrs); + + // Set whether we are controlling or not. + nsresult SetControlling(Controlling controlling); + + Controlling GetControlling(); + + // Set the STUN servers. Must be called before StartGathering + // (if at all). + nsresult SetStunServers(const std::vector<NrIceStunServer>& stun_servers); + + // Set the TURN servers. Must be called before StartGathering + // (if at all). + nsresult SetTurnServers(const std::vector<NrIceTurnServer>& turn_servers); + + // Provide the resolution provider. Must be called before + // StartGathering. + nsresult SetResolver(nr_resolver* resolver); + + // Provide the proxy address. Must be called before + // StartGathering. + nsresult SetProxyConfig(NrSocketProxyConfig&& config); + + const std::shared_ptr<NrSocketProxyConfig>& GetProxyConfig() { + return proxy_config_; + } + + void SetCtxFlags(bool default_route_only); + + // Start ICE gathering + nsresult StartGathering(bool default_route_only, + bool obfuscate_host_addresses); + + // Start checking + nsresult StartChecks(); + + // Notify that the network has gone online/offline + void UpdateNetworkState(bool online); + + void AccumulateStats(const NrIceStats& stats); + NrIceStats Destroy(); + + // Are we trickling? + bool generating_trickle() const { return trickle_; } + + // Signals to indicate events. API users can (and should) + // register for these. + sigslot::signal2<NrIceCtx*, NrIceCtx::GatheringState> + SignalGatheringStateChange; + sigslot::signal2<NrIceCtx*, NrIceCtx::ConnectionState> + SignalConnectionStateChange; + + // The thread to direct method calls to + nsCOMPtr<nsIEventTarget> thread() { return sts_target_; } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceCtx) + + private: + explicit NrIceCtx(const std::string& name); + + virtual ~NrIceCtx(); + + DISALLOW_COPY_ASSIGN(NrIceCtx); + + // Callbacks for nICEr + static void gather_cb(NR_SOCKET s, int h, void* arg); // ICE gather complete + + // Handler implementation + static int select_pair(void* obj, nr_ice_media_stream* stream, + int component_id, nr_ice_cand_pair** potentials, + int potential_ct); + static int stream_ready(void* obj, nr_ice_media_stream* stream); + static int stream_failed(void* obj, nr_ice_media_stream* stream); + static int ice_checking(void* obj, nr_ice_peer_ctx* pctx); + static int ice_connected(void* obj, nr_ice_peer_ctx* pctx); + static int ice_disconnected(void* obj, nr_ice_peer_ctx* pctx); + static int msg_recvd(void* obj, nr_ice_peer_ctx* pctx, + nr_ice_media_stream* stream, int component_id, + unsigned char* msg, int len); + static void trickle_cb(void* arg, nr_ice_ctx* ctx, + nr_ice_media_stream* stream, int component_id, + nr_ice_candidate* candidate); + + // Find a media stream by stream ptr. Gross + RefPtr<NrIceMediaStream> FindStream(nr_ice_media_stream* stream); + + // Set the state + void SetConnectionState(ConnectionState state); + + // Set the state + void SetGatheringState(GatheringState state); + + void GenerateObfuscatedAddress(nr_ice_candidate* candidate, + std::string* mdns_address, + std::string* actual_address); + + ConnectionState connection_state_; + GatheringState gathering_state_; + const std::string name_; + bool ice_controlling_set_; + std::map<std::string, RefPtr<NrIceMediaStream>> streams_; + nr_ice_ctx* ctx_; + nr_ice_peer_ctx* peer_; + nr_ice_handler_vtbl* ice_handler_vtbl_; // Must be pointer + nr_ice_handler* ice_handler_; // Must be pointer + bool trickle_; + nsCOMPtr<nsIEventTarget> sts_target_; // The thread to run on + Config config_; + RefPtr<TestNat> nat_; + std::shared_ptr<NrSocketProxyConfig> proxy_config_; + std::map<std::string, std::string> obfuscated_host_addresses_; +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/nricemediastream.cpp b/dom/media/webrtc/transport/nricemediastream.cpp new file mode 100644 index 0000000000..ef7980dcf3 --- /dev/null +++ b/dom/media/webrtc/transport/nricemediastream.cpp @@ -0,0 +1,709 @@ +/* -*- 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 + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <string> +#include <vector> + +#include "logging.h" +#include "nsError.h" +#include "nsThreadUtils.h" + +// nICEr includes +extern "C" { +#include "nr_api.h" +#include "transport_addr.h" +#include "nr_socket.h" +#include "ice_ctx.h" +#include "ice_candidate.h" +#include "ice_handler.h" +} + +// 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: + if (addr.tls) { + out->transport = kNrIceTransportTls; + } else { + 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); + + if (!ToNrIceAddr(cand->addr, &out->cand_addr)) return false; + + if (cand->mdns_addr) { + out->mdns_addr = cand->mdns_addr; + } + + if (cand->isock) { + nr_transport_addr addr; + r = nr_socket_getaddr(cand->isock->sock, &addr); + if (r) return false; + + out->is_proxied = addr.is_proxied; + + if (!ToNrIceAddr(addr, &out->local_addr)) return false; + } + + NrIceCandidate::Type type; + + switch (cand->type) { + case HOST: + type = NrIceCandidate::ICE_HOST; + break; + case SERVER_REFLEXIVE: + type = NrIceCandidate::ICE_SERVER_REFLEXIVE; + break; + case PEER_REFLEXIVE: + type = NrIceCandidate::ICE_PEER_REFLEXIVE; + break; + case RELAYED: + type = NrIceCandidate::ICE_RELAYED; + break; + default: + return false; + } + + NrIceCandidate::TcpType tcp_type; + switch (cand->tcp_type) { + case TCP_TYPE_ACTIVE: + tcp_type = NrIceCandidate::ICE_ACTIVE; + break; + case TCP_TYPE_PASSIVE: + tcp_type = NrIceCandidate::ICE_PASSIVE; + break; + case TCP_TYPE_SO: + tcp_type = NrIceCandidate::ICE_SO; + break; + default: + tcp_type = NrIceCandidate::ICE_NONE; + break; + } + + out->type = type; + out->tcp_type = tcp_type; + out->codeword = candc.codeword; + out->label = candc.label; + out->trickled = candc.trickled; + out->priority = candc.priority; + 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. +static UniquePtr<NrIceCandidate> MakeNrIceCandidate( + const nr_ice_candidate& candc) { + UniquePtr<NrIceCandidate> out(new NrIceCandidate()); + + if (!ToNrIceCandidate(candc, out.get())) { + return nullptr; + } + return out; +} + +static bool Matches(const nr_ice_media_stream* stream, const std::string& ufrag, + const std::string& pwd) { + return stream && (stream->ufrag == ufrag) && (stream->pwd == pwd); +} + +NrIceMediaStream::NrIceMediaStream(NrIceCtx* ctx, const std::string& id, + const std::string& name, size_t components) + : state_(ICE_CONNECTING), + ctx_(ctx), + name_(name), + components_(components), + stream_(nullptr), + old_stream_(nullptr), + id_(id) {} + +NrIceMediaStream::~NrIceMediaStream() { + // We do not need to destroy anything. All major resources + // are attached to the ice ctx. +} + +nsresult NrIceMediaStream::ConnectToPeer( + const std::string& ufrag, const std::string& pwd, + const std::vector<std::string>& attributes) { + MOZ_ASSERT(stream_); + + if (Matches(old_stream_, ufrag, pwd)) { + // (We swap before we close so we never have stream_ == nullptr) + MOZ_MTLOG(ML_DEBUG, + "Rolling back to old stream ufrag=" << ufrag << " " << name_); + std::swap(stream_, old_stream_); + CloseStream(&old_stream_); + } else if (old_stream_) { + // Right now we wait for ICE to complete before closing the old stream. + // It might be worth it to close it sooner, but we don't want to close it + // right away. + MOZ_MTLOG(ML_DEBUG, + "ICE restart committed, marking old stream as obsolete, " + "beginning switchover to ufrag=" + << ufrag << " " << name_); + nr_ice_media_stream_set_obsolete(old_stream_); + } + + nr_ice_media_stream* peer_stream; + if (nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream)) { + // No peer yet + std::vector<char*> attributes_in; + attributes_in.reserve(attributes.size()); + for (auto& attribute : attributes) { + MOZ_MTLOG(ML_DEBUG, "Setting " << attribute << " on stream " << name_); + attributes_in.push_back(const_cast<char*>(attribute.c_str())); + } + + // Still need to call nr_ice_ctx_parse_stream_attributes. + int r = nr_ice_peer_ctx_parse_stream_attributes( + ctx_->peer(), stream_, + attributes_in.empty() ? nullptr : &attributes_in[0], + attributes_in.size()); + if (r) { + MOZ_MTLOG(ML_ERROR, + "Couldn't parse attributes for stream " << name_ << "'"); + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +nsresult NrIceMediaStream::SetIceCredentials(const std::string& ufrag, + const std::string& pwd) { + if (Matches(stream_, ufrag, pwd)) { + return NS_OK; + } + + if (Matches(old_stream_, ufrag, pwd)) { + return NS_OK; + } + + MOZ_MTLOG(ML_DEBUG, "Setting ICE credentials for " << name_ << " - " << ufrag + << ":" << pwd); + CloseStream(&old_stream_); + old_stream_ = stream_; + + std::string name(name_ + " - " + ufrag + ":" + pwd); + + int r = nr_ice_add_media_stream(ctx_->ctx(), name.c_str(), ufrag.c_str(), + pwd.c_str(), components_, &stream_); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '" + << name_ << "': error=" << r); + stream_ = old_stream_; + old_stream_ = nullptr; + return NS_ERROR_FAILURE; + } + + state_ = ICE_CONNECTING; + return NS_OK; +} + +// Parse trickle ICE candidate +nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate, + const std::string& ufrag, + const std::string& mdns_addr) { + nr_ice_media_stream* stream = GetStreamForRemoteUfrag(ufrag); + if (!stream) { + return NS_ERROR_FAILURE; + } + + MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << ctx_->ctx()->label << ")/STREAM(" + << name() << ") : parsing trickle candidate " + << candidate); + + int r = nr_ice_peer_ctx_parse_trickle_candidate( + ctx_->peer(), stream, const_cast<char*>(candidate.c_str()), + mdns_addr.c_str()); + + if (r) { + if (r == R_ALREADY) { + MOZ_MTLOG(ML_INFO, "Trickle candidate is redundant for stream '" + << name_ + << "' because it is completed: " << candidate); + } else if (r == R_REJECTED) { + MOZ_MTLOG(ML_INFO, + "Trickle candidate is ignored for stream '" + << name_ + << "', probably because it is for an unused component" + << ": " << candidate); + } else { + MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '" + << name_ << "': " << candidate); + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +// Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled. +nsresult NrIceMediaStream::GetActivePair(int component, + UniquePtr<NrIceCandidate>* localp, + UniquePtr<NrIceCandidate>* remotep) { + int r; + nr_ice_candidate* local_int; + nr_ice_candidate* remote_int; + + if (!stream_) { + return NS_ERROR_NOT_AVAILABLE; + } + + r = nr_ice_media_stream_get_active(ctx_->peer(), stream_, component, + &local_int, &remote_int); + // If result is R_REJECTED then component is unpaired or disabled. + if (r == R_REJECTED) return NS_ERROR_NOT_AVAILABLE; + + if (r) return NS_ERROR_FAILURE; + + UniquePtr<NrIceCandidate> local(MakeNrIceCandidate(*local_int)); + if (!local) return NS_ERROR_FAILURE; + + UniquePtr<NrIceCandidate> remote(MakeNrIceCandidate(*remote_int)); + if (!remote) return NS_ERROR_FAILURE; + + if (localp) *localp = std::move(local); + if (remotep) *remotep = std::move(remote); + + return NS_OK; +} + +nsresult NrIceMediaStream::GetCandidatePairs( + std::vector<NrIceCandidatePair>* out_pairs) const { + MOZ_ASSERT(out_pairs); + if (!stream_) { + return NS_ERROR_NOT_AVAILABLE; + } + + // If we haven't at least started checking then there is nothing to report + if (ctx_->peer()->state != NR_ICE_PEER_STATE_PAIRED) { + return NS_OK; + } + + // Get the check_list on the peer stream (this is where the check_list + // actually lives, not in stream_) + nr_ice_media_stream* peer_stream; + int r = nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream); + if (r != 0) { + return NS_ERROR_FAILURE; + } + + nr_ice_cand_pair *p1, *p2; + out_pairs->clear(); + + TAILQ_FOREACH(p1, &peer_stream->check_list, check_queue_entry) { + MOZ_ASSERT(p1); + MOZ_ASSERT(p1->local); + MOZ_ASSERT(p1->remote); + NrIceCandidatePair pair; + + p2 = TAILQ_FIRST(&peer_stream->check_list); + while (p2) { + if (p1 == p2) { + /* Don't compare with our self. */ + p2 = TAILQ_NEXT(p2, check_queue_entry); + continue; + } + if (strncmp(p1->codeword, p2->codeword, sizeof(p1->codeword)) == 0) { + /* In case of duplicate pairs we only report the one winning pair */ + if (((p2->remote->component && (p2->remote->component->active == p2)) && + !(p1->remote->component && + (p1->remote->component->active == p1))) || + ((p2->peer_nominated || p2->nominated) && + !(p1->peer_nominated || p1->nominated)) || + (p2->priority > p1->priority) || + ((p2->state == NR_ICE_PAIR_STATE_SUCCEEDED) && + (p1->state != NR_ICE_PAIR_STATE_SUCCEEDED)) || + ((p2->state != NR_ICE_PAIR_STATE_CANCELLED) && + (p1->state == NR_ICE_PAIR_STATE_CANCELLED))) { + /* p2 is a better pair. */ + break; + } + } + p2 = TAILQ_NEXT(p2, check_queue_entry); + } + if (p2) { + /* p2 points to a duplicate but better pair so skip this one */ + continue; + } + + switch (p1->state) { + case NR_ICE_PAIR_STATE_FROZEN: + pair.state = NrIceCandidatePair::State::STATE_FROZEN; + break; + case NR_ICE_PAIR_STATE_WAITING: + pair.state = NrIceCandidatePair::State::STATE_WAITING; + break; + case NR_ICE_PAIR_STATE_IN_PROGRESS: + pair.state = NrIceCandidatePair::State::STATE_IN_PROGRESS; + break; + case NR_ICE_PAIR_STATE_FAILED: + pair.state = NrIceCandidatePair::State::STATE_FAILED; + break; + case NR_ICE_PAIR_STATE_SUCCEEDED: + pair.state = NrIceCandidatePair::State::STATE_SUCCEEDED; + break; + case NR_ICE_PAIR_STATE_CANCELLED: + pair.state = NrIceCandidatePair::State::STATE_CANCELLED; + break; + default: + MOZ_ASSERT(0); + } + + pair.priority = p1->priority; + pair.nominated = p1->peer_nominated || p1->nominated; + pair.component_id = p1->remote->component->component_id; + + // As discussed with drno: a component's can_send field (set to true + // by ICE consent) is a very close approximation for writable and + // readable. Note: the component for the local candidate never has + // the can_send member set to true, remote for both readable and + // writable. (mjf) + pair.writable = p1->remote->component->can_send; + pair.readable = p1->remote->component->can_send; + pair.selected = + p1->remote->component && p1->remote->component->active == p1; + pair.codeword = p1->codeword; + pair.bytes_sent = p1->bytes_sent; + pair.bytes_recvd = p1->bytes_recvd; + pair.ms_since_last_send = + p1->last_sent.tv_sec * 1000 + p1->last_sent.tv_usec / 1000; + pair.ms_since_last_recv = + p1->last_recvd.tv_sec * 1000 + p1->last_recvd.tv_usec / 1000; + + if (!ToNrIceCandidate(*(p1->local), &pair.local) || + !ToNrIceCandidate(*(p1->remote), &pair.remote)) { + return NS_ERROR_FAILURE; + } + + out_pairs->push_back(pair); + } + + return NS_OK; +} + +nsresult NrIceMediaStream::GetDefaultCandidate( + int component, NrIceCandidate* candidate) const { + if (!stream_) { + return NS_ERROR_NOT_AVAILABLE; + } + + nr_ice_candidate* cand; + + int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand); + if (r) { + if (r == R_NOT_FOUND) { + MOZ_MTLOG(ML_INFO, "Couldn't get default ICE candidate for '" + << name_ << "', no candidates."); + } else { + MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '" + << name_ << "', " << r); + } + return NS_ERROR_FAILURE; + } + + if (!ToNrIceCandidate(*cand, candidate)) { + MOZ_MTLOG(ML_ERROR, + "Failed to convert default ICE candidate for '" << name_ << "'"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +std::vector<std::string> NrIceMediaStream::GetAttributes() const { + char** attrs = nullptr; + int attrct; + int r; + std::vector<std::string> ret; + + if (!stream_) { + return ret; + } + + r = nr_ice_media_stream_get_attributes(stream_, &attrs, &attrct); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '" << name_ << "'"); + return ret; + } + + for (int i = 0; i < attrct; i++) { + ret.push_back(attrs[i]); + RFREE(attrs[i]); + } + + RFREE(attrs); + + return ret; +} + +static nsresult GetCandidatesFromStream( + nr_ice_media_stream* stream, std::vector<NrIceCandidate>* candidates) { + MOZ_ASSERT(candidates); + nr_ice_component* comp = STAILQ_FIRST(&stream->components); + while (comp) { + if (comp->state != NR_ICE_COMPONENT_DISABLED) { + nr_ice_candidate* cand = TAILQ_FIRST(&comp->candidates); + while (cand) { + NrIceCandidate new_cand; + // This can fail if the candidate is server reflexive or relayed, and + // has not yet received a response (ie; it doesn't know its address + // yet). For the purposes of this code, this isn't a candidate we're + // interested in, since it is not fully baked yet. + if (ToNrIceCandidate(*cand, &new_cand)) { + candidates->push_back(new_cand); + } + cand = TAILQ_NEXT(cand, entry_comp); + } + } + comp = STAILQ_NEXT(comp, entry); + } + + return NS_OK; +} + +nsresult NrIceMediaStream::GetLocalCandidates( + std::vector<NrIceCandidate>* candidates) const { + if (!stream_) { + return NS_ERROR_NOT_AVAILABLE; + } + + return GetCandidatesFromStream(stream_, candidates); +} + +nsresult NrIceMediaStream::GetRemoteCandidates( + std::vector<NrIceCandidate>* candidates) const { + if (!stream_) { + return NS_ERROR_NOT_AVAILABLE; + } + + // If we haven't at least started checking then there is nothing to report + if (ctx_->peer()->state != NR_ICE_PEER_STATE_PAIRED) { + return NS_OK; + } + + nr_ice_media_stream* peer_stream; + int r = nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream); + if (r != 0) { + return NS_ERROR_FAILURE; + } + + return GetCandidatesFromStream(peer_stream, candidates); +} + +nsresult NrIceMediaStream::DisableComponent(int component_id) { + if (!stream_) return NS_ERROR_FAILURE; + + int r = nr_ice_media_stream_disable_component(stream_, component_id); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't disable '" << name_ << "':" << component_id); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult NrIceMediaStream::GetConsentStatus(int component_id, bool* can_send, + struct timeval* ts) { + if (!stream_) return NS_ERROR_FAILURE; + + nr_ice_media_stream* peer_stream; + int r = nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream); + if (r) { + MOZ_MTLOG(ML_ERROR, "Failed to find peer stream for '" + << name_ << "':" << component_id); + return NS_ERROR_FAILURE; + } + + int send = 0; + r = nr_ice_media_stream_get_consent_status(peer_stream, component_id, &send, + ts); + if (r) { + MOZ_MTLOG(ML_ERROR, "Failed to get consent status for '" + << name_ << "':" << component_id); + return NS_ERROR_FAILURE; + } + *can_send = !!send; + + return NS_OK; +} + +bool NrIceMediaStream::HasStream(nr_ice_media_stream* stream) const { + return (stream == stream_) || (stream == old_stream_); +} + +nsresult NrIceMediaStream::SendPacket(int component_id, + const unsigned char* data, size_t len) { + nr_ice_media_stream* stream = old_stream_ ? old_stream_ : stream_; + if (!stream) { + return NS_ERROR_FAILURE; + } + + int r = nr_ice_media_stream_send(ctx_->peer(), stream, component_id, + const_cast<unsigned char*>(data), len); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't send media on '" << name_ << "'"); + if (r == R_WOULDBLOCK) { + return NS_BASE_STREAM_WOULD_BLOCK; + } + + return NS_BASE_STREAM_OSERROR; + } + + return NS_OK; +} + +void NrIceMediaStream::Ready() { + // This function is called whenever a stream becomes ready, but it + // gets fired multiple times when a stream gets nominated repeatedly. + if (state_ != ICE_OPEN) { + MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'"); + state_ = ICE_OPEN; + NS_DispatchToCurrentThread(NewRunnableMethod<nr_ice_media_stream*>( + "NrIceMediaStream::DeferredCloseOldStream", this, + &NrIceMediaStream::DeferredCloseOldStream, old_stream_)); + SignalReady(this); + } else { + MOZ_MTLOG(ML_DEBUG, + "Stream ready callback fired again for '" << name_ << "'"); + } +} + +void NrIceMediaStream::Failed() { + if (state_ != ICE_CLOSED) { + MOZ_MTLOG(ML_DEBUG, "Marking stream failed '" << name_ << "'"); + state_ = ICE_CLOSED; + // We don't need the old stream anymore. + NS_DispatchToCurrentThread(NewRunnableMethod<nr_ice_media_stream*>( + "NrIceMediaStream::DeferredCloseOldStream", this, + &NrIceMediaStream::DeferredCloseOldStream, old_stream_)); + SignalFailed(this); + } +} + +void NrIceMediaStream::Close() { + MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'"); + state_ = ICE_CLOSED; + + CloseStream(&old_stream_); + CloseStream(&stream_); + ctx_ = nullptr; +} + +void NrIceMediaStream::CloseStream(nr_ice_media_stream** stream) { + if (*stream) { + int r = nr_ice_remove_media_stream(ctx_->ctx(), stream); + if (r) { + MOZ_ASSERT(false, "Failed to remove stream"); + MOZ_MTLOG(ML_ERROR, "Failed to remove stream, error=" << r); + } + *stream = nullptr; + } +} + +void NrIceMediaStream::DeferredCloseOldStream(const nr_ice_media_stream* old) { + if (old == old_stream_) { + CloseStream(&old_stream_); + } +} + +nr_ice_media_stream* NrIceMediaStream::GetStreamForRemoteUfrag( + const std::string& aUfrag) { + if (aUfrag.empty()) { + return stream_; + } + + nr_ice_media_stream* peer_stream = nullptr; + + if (!nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream) && + aUfrag == peer_stream->ufrag) { + return stream_; + } + + if (old_stream_ && + !nr_ice_peer_ctx_find_pstream(ctx_->peer(), old_stream_, &peer_stream) && + aUfrag == peer_stream->ufrag) { + return old_stream_; + } + + return nullptr; +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/nricemediastream.h b/dom/media/webrtc/transport/nricemediastream.h new file mode 100644 index 0000000000..f18b09c47f --- /dev/null +++ b/dom/media/webrtc/transport/nricemediastream.h @@ -0,0 +1,225 @@ +/* -*- 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 + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + +// This is a wrapper around the nICEr ICE stack +#ifndef nricemediastream_h__ +#define nricemediastream_h__ + +#include <string> +#include <vector> + +#include "sigslot.h" + +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "nscore.h" + +#include "m_cpp_utils.h" + +namespace mozilla { + +typedef struct nr_ice_ctx_ nr_ice_ctx; +typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx; +typedef struct nr_ice_media_stream_ nr_ice_media_stream; + +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 }; + + enum TcpType { ICE_NONE, ICE_ACTIVE, ICE_PASSIVE, ICE_SO }; + + NrIceAddr cand_addr; + NrIceAddr local_addr; + std::string mdns_addr; + Type type; + TcpType tcp_type; + std::string codeword; + std::string label; + bool trickled; + uint32_t priority; + bool is_proxied = false; +}; + +struct NrIceCandidatePair { + enum State { + STATE_FROZEN, + STATE_WAITING, + STATE_IN_PROGRESS, + STATE_FAILED, + STATE_SUCCEEDED, + STATE_CANCELLED + }; + + State state; + uint64_t priority; + // Set regardless of who nominated it. Does not necessarily mean that it is + // ready to be selected (ie; nominated by peer, but our check has not + // succeeded yet.) Note: since this implementation uses aggressive nomination, + // when we are the controlling agent, this will always be set if the pair is + // in STATE_SUCCEEDED. + bool nominated; + bool writable; + bool readable; + // Set if this candidate pair has been selected. Note: Since we are using + // aggressive nomination, this could change frequently as ICE runs. + bool selected; + NrIceCandidate local; + NrIceCandidate remote; + // TODO(bcampen@mozilla.com): Is it important to put the foundation in here? + std::string codeword; + uint64_t component_id; + + // for RTCIceCandidatePairStats + uint64_t bytes_sent; + uint64_t bytes_recvd; + uint64_t ms_since_last_send; + uint64_t ms_since_last_recv; +}; + +class NrIceMediaStream { + public: + NrIceMediaStream(NrIceCtx* ctx, const std::string& id, + const std::string& name, size_t components); + + nsresult SetIceCredentials(const std::string& ufrag, const std::string& pwd); + nsresult ConnectToPeer(const std::string& ufrag, const std::string& pwd, + const std::vector<std::string>& peer_attrs); + enum State { ICE_CONNECTING, ICE_OPEN, ICE_CLOSED }; + + State state() const { return state_; } + + // The name of the stream + const std::string& name() const { return name_; } + + // Get all the ICE attributes; used for testing + std::vector<std::string> GetAttributes() const; + + nsresult GetLocalCandidates(std::vector<NrIceCandidate>* candidates) const; + nsresult GetRemoteCandidates(std::vector<NrIceCandidate>* candidates) const; + + // Get all candidate pairs, whether in the check list or triggered check + // queue, in priority order. |out_pairs| is cleared before being filled. + nsresult GetCandidatePairs(std::vector<NrIceCandidatePair>* out_pairs) const; + + nsresult GetDefaultCandidate(int component, NrIceCandidate* candidate) const; + + // Parse trickle ICE candidate + nsresult ParseTrickleCandidate(const std::string& candidate, + const std::string& ufrag, + const std::string& mdns_addr); + + // Disable a component + nsresult DisableComponent(int component); + + // Get the candidate pair currently active. It's the + // caller's responsibility to free these. + nsresult GetActivePair(int component, UniquePtr<NrIceCandidate>* local, + UniquePtr<NrIceCandidate>* remote); + + // Get the current ICE consent send status plus the timeval of the last + // consent update time. + nsresult GetConsentStatus(int component, bool* can_send, struct timeval* ts); + + // The number of components + size_t components() const { return components_; } + + bool HasStream(nr_ice_media_stream* stream) const; + // Signals to indicate events. API users can (and should) + // register for these. + + // Send a packet + nsresult SendPacket(int component_id, const unsigned char* data, size_t len); + + // Set your state to ready. Called by the NrIceCtx; + void Ready(); + void Failed(); + + // Close the stream. Called by the NrIceCtx. + // Different from the destructor because other people + // might be holding RefPtrs but we want those writes to fail once + // the context has been destroyed. + void Close(); + + // So the receiver of SignalCandidate can determine which transport + // the candidate belongs to. + const std::string& GetId() const { return id_; } + + sigslot::signal5<NrIceMediaStream*, const std::string&, const std::string&, + const std::string&, const std::string&> + SignalCandidate; // A new ICE candidate: + + sigslot::signal1<NrIceMediaStream*> SignalReady; // Candidate pair ready. + sigslot::signal1<NrIceMediaStream*> SignalFailed; // Candidate pair failed. + sigslot::signal4<NrIceMediaStream*, int, const unsigned char*, int> + SignalPacketReceived; // Incoming packet + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceMediaStream) + + private: + ~NrIceMediaStream(); + + DISALLOW_COPY_ASSIGN(NrIceMediaStream); + + void CloseStream(nr_ice_media_stream** stream); + void DeferredCloseOldStream(const nr_ice_media_stream* old); + nr_ice_media_stream* GetStreamForRemoteUfrag(const std::string& ufrag); + + State state_; + RefPtr<NrIceCtx> ctx_; + const std::string name_; + const size_t components_; + nr_ice_media_stream* stream_; + nr_ice_media_stream* old_stream_; + const std::string id_; +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/nriceresolver.cpp b/dom/media/webrtc/transport/nriceresolver.cpp new file mode 100644 index 0000000000..e1737e0e65 --- /dev/null +++ b/dom/media/webrtc/transport/nriceresolver.cpp @@ -0,0 +1,234 @@ +/* -*- 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 authors: jib@mozilla.com, ekr@rtfm.com + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 "logging.h" +#include "nspr.h" +#include "prnetdb.h" + +#include "mozilla/Assertions.h" + +extern "C" { +#include "nr_api.h" +#include "async_timer.h" +#include "nr_resolver.h" +#include "transport_addr.h" +} + +#include "mozilla/net/DNS.h" // TODO(jib@mozilla.com) down here because bug 848578 +#include "nsThreadUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsIDNSService.h" +#include "nsIDNSListener.h" +#include "nsIDNSRecord.h" +#include "nsNetCID.h" +#include "nsCOMPtr.h" +#include "nriceresolver.h" +#include "nr_socket_prsock.h" +#include "transport/runnable_utils.h" + +namespace mozilla { + +MOZ_MTLOG_MODULE("mtransport") + +NrIceResolver::NrIceResolver() + : vtbl_(new nr_resolver_vtbl()) +#ifdef DEBUG + , + allocated_resolvers_(0) +#endif +{ + vtbl_->destroy = &NrIceResolver::destroy; + vtbl_->resolve = &NrIceResolver::resolve; + vtbl_->cancel = &NrIceResolver::cancel; +} + +NrIceResolver::~NrIceResolver() { + MOZ_ASSERT(!allocated_resolvers_); + delete vtbl_; +} + +nsresult NrIceResolver::Init() { + nsresult rv; + + sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + dns_ = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_MTLOG(ML_ERROR, "Could not acquire DNS service"); + } + return rv; +} + +nr_resolver* NrIceResolver::AllocateResolver() { + nr_resolver* resolver; + + int r = nr_resolver_create_int((void*)this, vtbl_, &resolver); + MOZ_ASSERT(!r); + if (r) { + MOZ_MTLOG(ML_ERROR, "nr_resolver_create_int failed"); + return nullptr; + } + // We must be available to allocators until they all call DestroyResolver, + // because allocators may (and do) outlive the originator of NrIceResolver. + AddRef(); +#ifdef DEBUG + ++allocated_resolvers_; +#endif + return resolver; +} + +void NrIceResolver::DestroyResolver() { +#ifdef DEBUG + --allocated_resolvers_; +#endif + // Undoes Addref in AllocateResolver so the NrIceResolver can be freed. + Release(); +} + +int NrIceResolver::destroy(void** objp) { + if (!objp || !*objp) return 0; + NrIceResolver* resolver = static_cast<NrIceResolver*>(*objp); + *objp = nullptr; + resolver->DestroyResolver(); + return 0; +} + +int NrIceResolver::resolve(void* obj, nr_resolver_resource* resource, + int (*cb)(void* cb_arg, nr_transport_addr* addr), + void* cb_arg, void** handle) { + MOZ_ASSERT(obj); + return static_cast<NrIceResolver*>(obj)->resolve(resource, cb, cb_arg, + handle); +} + +int NrIceResolver::resolve(nr_resolver_resource* resource, + 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_); + RefPtr<PendingResolution> pr; + nsIDNSService::DNSFlags resolve_flags = nsIDNSService::RESOLVE_DEFAULT_FLAGS; + OriginAttributes attrs; + + if (resource->transport_protocol != IPPROTO_UDP && + resource->transport_protocol != IPPROTO_TCP) { + MOZ_MTLOG(ML_ERROR, "Only UDP and TCP are supported."); + ABORT(R_NOT_FOUND); + } + pr = new PendingResolution( + sts_thread_, resource->port ? resource->port : 3478, + resource->transport_protocol ? resource->transport_protocol : IPPROTO_UDP, + cb, cb_arg); + + switch (resource->address_family) { + case AF_INET: + resolve_flags = nsIDNSService::RESOLVE_DISABLE_IPV6; + break; + case AF_INET6: + resolve_flags = nsIDNSService::RESOLVE_DISABLE_IPV4; + break; + default: + ABORT(R_BAD_ARGS); + } + + if (NS_FAILED(dns_->AsyncResolveNative( + nsAutoCString(resource->domain_name), + nsIDNSService::RESOLVE_TYPE_DEFAULT, resolve_flags, nullptr, pr, + sts_thread_, attrs, 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 + // return, we cannot return the request we got from AsyncResolve directly. + // + // Instead, we return an addref'ed reference to PendingResolution itself, + // which in turn holds the request and coordinates between cancel and + // OnLookupComplete to release it only once. + pr.forget(handle); + + _status = 0; +abort: + return _status; +} + +nsresult NrIceResolver::PendingResolution::OnLookupComplete( + nsICancelable* request, nsIDNSRecord* aRecord, nsresult status) { + ASSERT_ON_THREAD(thread_); + // First check if we've been canceled. This is single-threaded on the STS + // thread, but cancel() cannot guarantee this event isn't on the queue. + if (request_) { + 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; + nsCOMPtr<nsIDNSAddrRecord> record = do_QueryInterface(aRecord); + if (record && NS_SUCCEEDED(record->GetNextAddr(port_, &na))) { + MOZ_ALWAYS_TRUE(nr_netaddr_to_transport_addr(&na, &ta, transport_) == + 0); + cb_addr = &ta; + } + } + cb_(cb_arg_, cb_addr); + request_ = nullptr; + Release(); + } + return NS_OK; +} + +int NrIceResolver::cancel(void* obj, void* handle) { + MOZ_ALWAYS_TRUE(obj); + MOZ_ASSERT(handle); + ASSERT_ON_THREAD(static_cast<NrIceResolver*>(obj)->sts_thread_); + return static_cast<PendingResolution*>(handle)->cancel(); +} + +int NrIceResolver::PendingResolution::cancel() { + request_->Cancel(NS_ERROR_ABORT); + request_ = nullptr; + Release(); + return 0; +} + +NS_IMPL_ISUPPORTS(NrIceResolver::PendingResolution, nsIDNSListener); +} // End of namespace mozilla diff --git a/dom/media/webrtc/transport/nriceresolver.h b/dom/media/webrtc/transport/nriceresolver.h new file mode 100644 index 0000000000..7399c78050 --- /dev/null +++ b/dom/media/webrtc/transport/nriceresolver.h @@ -0,0 +1,119 @@ +/* -*- 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 authors: jib@mozilla.com, ekr@rtfm.com + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + +#ifndef nriceresolver_h__ +#define nriceresolver_h__ + +#include "nsIDNSService.h" +#include "nsIDNSListener.h" +#include "nsICancelable.h" +#include "nricectx.h" + +typedef struct nr_resolver_ nr_resolver; +typedef struct nr_resolver_vtbl_ nr_resolver_vtbl; +typedef struct nr_transport_addr_ nr_transport_addr; +typedef struct nr_resolver_resource_ nr_resolver_resource; + +namespace mozilla { + +class NrIceResolver { + private: + ~NrIceResolver(); + + public: + NrIceResolver(); + + nsresult Init(); + nr_resolver* AllocateResolver(); + void DestroyResolver(); + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceResolver) + + int resolve(nr_resolver_resource* resource, + int (*cb)(void* cb_arg, nr_transport_addr* addr), void* cb_arg, + void** handle); + + private: + // Implementations of vtbl functions + static int destroy(void** objp); + static int resolve(void* obj, nr_resolver_resource* resource, + int (*cb)(void* cb_arg, nr_transport_addr* addr), + void* cb_arg, void** handle); + static void resolve_cb(NR_SOCKET s, int how, void* cb_arg); + static int cancel(void* obj, void* handle); + + class PendingResolution : public nsIDNSListener { + public: + 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) {} + NS_IMETHOD OnLookupComplete(nsICancelable* request, nsIDNSRecord* record, + nsresult status) override; + + int cancel(); + nsCOMPtr<nsICancelable> request_; + NS_DECL_THREADSAFE_ISUPPORTS + + private: + virtual ~PendingResolution() = default; + + nsCOMPtr<nsIEventTarget> thread_; + uint16_t port_; + int transport_; + int (*cb_)(void* cb_arg, nr_transport_addr* addr); + void* cb_arg_; + }; + + nr_resolver_vtbl* vtbl_; + nsCOMPtr<nsIEventTarget> sts_thread_; + nsCOMPtr<nsIDNSService> dns_; +#ifdef DEBUG + int allocated_resolvers_; +#endif +}; + +} // End of namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/nriceresolverfake.cpp b/dom/media/webrtc/transport/nriceresolverfake.cpp new file mode 100644 index 0000000000..93ef37efc1 --- /dev/null +++ b/dom/media/webrtc/transport/nriceresolverfake.cpp @@ -0,0 +1,174 @@ +/* -*- 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 + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 "prio.h" +#include "mozilla/Assertions.h" + +extern "C" { +#include "async_wait.h" +#include "async_timer.h" +#include "nr_resolver.h" +#include "r_macros.h" +#include "transport_addr.h" +} + +#include "nriceresolverfake.h" +#include "nr_socket_prsock.h" + +namespace mozilla { + +NrIceResolverFake::NrIceResolverFake() + : vtbl_(new nr_resolver_vtbl), + addrs_(), + delay_ms_(100), + allocated_resolvers_(0) { + vtbl_->destroy = &NrIceResolverFake::destroy; + vtbl_->resolve = &NrIceResolverFake::resolve; + vtbl_->cancel = &NrIceResolverFake::cancel; +} + +NrIceResolverFake::~NrIceResolverFake() { + MOZ_ASSERT(allocated_resolvers_ == 0); + delete vtbl_; +} + +nr_resolver* NrIceResolverFake::AllocateResolver() { + nr_resolver* resolver; + + int r = nr_resolver_create_int((void*)this, vtbl_, &resolver); + MOZ_ASSERT(!r); + if (r) return nullptr; + + ++allocated_resolvers_; + + return resolver; +} + +void NrIceResolverFake::DestroyResolver() { --allocated_resolvers_; } + +int NrIceResolverFake::destroy(void** objp) { + if (!objp || !*objp) return 0; + + NrIceResolverFake* fake = static_cast<NrIceResolverFake*>(*objp); + *objp = nullptr; + + fake->DestroyResolver(); + + return 0; +} + +int NrIceResolverFake::resolve(void* obj, nr_resolver_resource* resource, + int (*cb)(void* cb_arg, nr_transport_addr* addr), + void* cb_arg, 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, + resource->transport_protocol ? resource->transport_protocol : IPPROTO_UDP, + resource->address_family, 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; + + _status = 0; +abort: + return (_status); +} + +void NrIceResolverFake::resolve_cb(NR_SOCKET s, int how, void* cb_arg) { + MOZ_ASSERT(cb_arg); + PendingResolution* pending = static_cast<PendingResolution*>(cb_arg); + + const PRNetAddr* addr = + pending->resolver_->Resolve(pending->hostname_, pending->address_family_); + + if (addr) { + nr_transport_addr transport_addr; + + 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; + + /* Fill in the address string */ + r = nr_transport_addr_fmt_addr_string(&transport_addr); + MOZ_ASSERT(!r); + if (r) goto abort; + + pending->cb_(pending->cb_arg_, &transport_addr); + delete pending; + return; + } + +abort: + // Resolution failed. + pending->cb_(pending->cb_arg_, nullptr); + + delete pending; +} + +int NrIceResolverFake::cancel(void* obj, void* handle) { + MOZ_ASSERT(obj); + MOZ_ASSERT(static_cast<NrIceResolverFake*>(obj)->allocated_resolvers_ > 0); + + PendingResolution* pending = static_cast<PendingResolution*>(handle); + + NR_async_timer_cancel(pending->timer_handle_); + delete pending; + + return (0); +} + +} // End of namespace mozilla diff --git a/dom/media/webrtc/transport/nriceresolverfake.h b/dom/media/webrtc/transport/nriceresolverfake.h new file mode 100644 index 0000000000..e46ce603de --- /dev/null +++ b/dom/media/webrtc/transport/nriceresolverfake.h @@ -0,0 +1,137 @@ +/* -*- 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 + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + +#ifndef nriceresolverfake_h__ +#define nriceresolverfake_h__ + +#include <map> +#include <string> + +#include "csi_platform.h" + +typedef struct nr_resolver_ nr_resolver; +typedef struct nr_resolver_vtbl_ nr_resolver_vtbl; +typedef struct nr_transport_addr_ nr_transport_addr; +typedef struct nr_resolver_resource_ nr_resolver_resource; + +namespace mozilla { + +class NrIceResolverFake { + public: + NrIceResolverFake(); + ~NrIceResolverFake(); + + void SetAddr(const std::string& hostname, const PRNetAddr& addr) { + switch (addr.raw.family) { + case AF_INET: + addrs_[hostname] = addr; + break; + case AF_INET6: + addrs6_[hostname] = addr; + break; + default: + MOZ_CRASH(); + } + } + + nr_resolver* AllocateResolver(); + + void DestroyResolver(); + + private: + // Implementations of vtbl functions + static int destroy(void** objp); + static int resolve(void* obj, nr_resolver_resource* resource, + int (*cb)(void* cb_arg, nr_transport_addr* addr), + void* cb_arg, void** handle); + static void resolve_cb(NR_SOCKET s, int how, void* cb_arg); + static int cancel(void* obj, void* handle); + + // Get an address. + const PRNetAddr* Resolve(const std::string& hostname, int address_family) { + switch (address_family) { + case AF_INET: + if (!addrs_.count(hostname)) return nullptr; + + return &addrs_[hostname]; + case AF_INET6: + if (!addrs6_.count(hostname)) return nullptr; + + return &addrs6_[hostname]; + default: + MOZ_CRASH(); + } + } + + struct PendingResolution { + PendingResolution(NrIceResolverFake* resolver, const std::string& hostname, + uint16_t port, int transport, int address_family, + int (*cb)(void* cb_arg, nr_transport_addr* addr), + void* cb_arg) + : resolver_(resolver), + hostname_(hostname), + port_(port), + transport_(transport), + address_family_(address_family), + cb_(cb), + cb_arg_(cb_arg) {} + + NrIceResolverFake* resolver_; + std::string hostname_; + uint16_t port_; + int transport_; + int address_family_; + 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_; + std::map<std::string, PRNetAddr> addrs6_; + uint32_t delay_ms_; + int allocated_resolvers_; +}; + +} // End of namespace mozilla + +#endif diff --git a/dom/media/webrtc/transport/nricestunaddr.cpp b/dom/media/webrtc/transport/nricestunaddr.cpp new file mode 100644 index 0000000000..d1e8d3ac52 --- /dev/null +++ b/dom/media/webrtc/transport/nricestunaddr.cpp @@ -0,0 +1,93 @@ +/* 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/. */ + +#include "logging.h" + +// nICEr includes +extern "C" { +#include "nr_api.h" +#include "local_addr.h" +} + +// Local includes +#include "nricestunaddr.h" + +namespace mozilla { + +MOZ_MTLOG_MODULE("mtransport") + +NrIceStunAddr::NrIceStunAddr() : localAddr_(new nr_local_addr) { + memset(localAddr_, 0, sizeof(nr_local_addr)); +} + +NrIceStunAddr::NrIceStunAddr(const nr_local_addr* addr) + : localAddr_(new nr_local_addr) { + nr_local_addr_copy(localAddr_, const_cast<nr_local_addr*>(addr)); +} + +NrIceStunAddr::NrIceStunAddr(const NrIceStunAddr& rhs) + : localAddr_(new nr_local_addr) { + nr_local_addr_copy(localAddr_, const_cast<nr_local_addr*>(rhs.localAddr_)); +} + +NrIceStunAddr::~NrIceStunAddr() { delete localAddr_; } + +size_t NrIceStunAddr::SerializationBufferSize() const { + return sizeof(nr_local_addr); +} + +nsresult NrIceStunAddr::Serialize(char* buffer, size_t buffer_size) const { + if (buffer_size != sizeof(nr_local_addr)) { + MOZ_MTLOG(ML_ERROR, + "Failed trying to serialize NrIceStunAddr, " + "input buffer length (" + << buffer_size << ") does not match required length (" + << sizeof(nr_local_addr) << ")"); + MOZ_ASSERT(false, "Failed to serialize NrIceStunAddr, bad buffer size"); + return NS_ERROR_FAILURE; + } + + nr_local_addr* toAddr = (nr_local_addr*)buffer; + if (nr_local_addr_copy(toAddr, localAddr_)) { + MOZ_MTLOG(ML_ERROR, + "Failed trying to serialize NrIceStunAddr, " + "could not copy nr_local_addr."); + MOZ_ASSERT(false, + "Failed to serialize NrIceStunAddr, nr_local_addr_copy failed"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult NrIceStunAddr::Deserialize(const char* buffer, size_t buffer_size) { + if (buffer_size != sizeof(nr_local_addr)) { + MOZ_MTLOG(ML_ERROR, + "Failed trying to deserialize NrIceStunAddr, " + "input buffer length (" + << buffer_size << ") does not match required length (" + << sizeof(nr_local_addr) << ")"); + MOZ_ASSERT(false, "Failed to deserialize NrIceStunAddr, bad buffer size"); + return NS_ERROR_FAILURE; + } + + nr_local_addr* from_addr = + const_cast<nr_local_addr*>((const nr_local_addr*)buffer); + + // At this point, from_addr->addr.addr is invalid (null), but will + // be fixed by nr_local_addr_copy. + if (nr_local_addr_copy(localAddr_, from_addr)) { + MOZ_MTLOG(ML_ERROR, + "Failed trying to deserialize NrIceStunAddr, " + "could not copy nr_local_addr."); + MOZ_ASSERT( + false, + "Failed to deserialize NrIceStunAddr, nr_local_addr_copy failed"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/nricestunaddr.h b/dom/media/webrtc/transport/nricestunaddr.h new file mode 100644 index 0000000000..8ebcdd6fe7 --- /dev/null +++ b/dom/media/webrtc/transport/nricestunaddr.h @@ -0,0 +1,36 @@ +/* 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/. */ + +#ifndef nricestunaddr_h__ +#define nricestunaddr_h__ + +#include "nsError.h" // for nsresult + +typedef struct nr_local_addr_ nr_local_addr; + +namespace mozilla { + +class NrIceStunAddr { + public: + NrIceStunAddr(); // needed for IPC deserialization + explicit NrIceStunAddr(const nr_local_addr* addr); + NrIceStunAddr(const NrIceStunAddr& rhs); + + ~NrIceStunAddr(); + + const nr_local_addr& localAddr() const { return *localAddr_; } + + // serialization/deserialization helper functions for use + // in dom/media/webrtc/transport/ipc/NrIceStunAddrMessagUtils.h + size_t SerializationBufferSize() const; + nsresult Serialize(char* buffer, size_t buffer_size) const; + nsresult Deserialize(const char* buffer, size_t buffer_size); + + private: + nr_local_addr* localAddr_; +}; + +} // namespace mozilla + +#endif // nricestunaddr_h__ diff --git a/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp b/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp new file mode 100644 index 0000000000..f022f8c29a --- /dev/null +++ b/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp @@ -0,0 +1,258 @@ +/* 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/. */ +#include <algorithm> +#include <map> +#include <set> +#include <string> +#include <vector> +#include "logging.h" +#include "nr_api.h" +#include "nrinterfaceprioritizer.h" + +MOZ_MTLOG_MODULE("mtransport") + +namespace { + +class LocalAddress { + public: + LocalAddress() + : ifname_(), + addr_(), + key_(), + is_vpn_(-1), + estimated_speed_(-1), + type_preference_(-1), + ip_version_(-1) {} + + bool Init(const nr_local_addr& local_addr) { + ifname_ = local_addr.addr.ifname; + + char buf[MAXIFNAME + 47]; + int r = nr_transport_addr_fmt_ifname_addr_string(&local_addr.addr, buf, + sizeof(buf)); + if (r) { + MOZ_MTLOG(ML_ERROR, "Error formatting interface key."); + return false; + } + key_ = buf; + + r = nr_transport_addr_get_addrstring(&local_addr.addr, buf, sizeof(buf)); + if (r) { + MOZ_MTLOG(ML_ERROR, "Error formatting address string."); + return false; + } + addr_ = buf; + + is_vpn_ = (local_addr.interface.type & NR_INTERFACE_TYPE_VPN) != 0 ? 1 : 0; + estimated_speed_ = local_addr.interface.estimated_speed; + type_preference_ = GetNetworkTypePreference(local_addr.interface.type); + ip_version_ = local_addr.addr.ip_version; + return true; + } + + bool operator<(const LocalAddress& rhs) const { + // Interface that is "less" here is preferred. + // If type preferences are different, we should simply sort by + // |type_preference_|. + if (type_preference_ != rhs.type_preference_) { + return type_preference_ < rhs.type_preference_; + } + + // If type preferences are the same, the next thing we use to sort is vpn. + // If two LocalAddress are different in |is_vpn_|, the LocalAddress that is + // not in vpn gets priority. + if (is_vpn_ != rhs.is_vpn_) { + return is_vpn_ < rhs.is_vpn_; + } + + // Compare estimated speed. + if (estimated_speed_ != rhs.estimated_speed_) { + return estimated_speed_ > rhs.estimated_speed_; + } + + // See if our hard-coded pref list helps us. + auto thisindex = std::find(interface_preference_list().begin(), + interface_preference_list().end(), ifname_); + auto rhsindex = std::find(interface_preference_list().begin(), + interface_preference_list().end(), rhs.ifname_); + if (thisindex != rhsindex) { + return thisindex < rhsindex; + } + + // Prefer IPV6 over IPV4 + if (ip_version_ != rhs.ip_version_) { + return ip_version_ > rhs.ip_version_; + } + + // Now we start getting into arbitrary stuff + if (ifname_ != rhs.ifname_) { + return ifname_ < rhs.ifname_; + } + + return addr_ < rhs.addr_; + } + + const std::string& GetKey() const { return key_; } + + private: + // Getting the preference corresponding to a type. Getting lower number here + // means the type of network is preferred. + static inline int GetNetworkTypePreference(int type) { + if (type & NR_INTERFACE_TYPE_WIRED) { + return 1; + } + if (type & NR_INTERFACE_TYPE_WIFI) { + return 2; + } + if (type & NR_INTERFACE_TYPE_MOBILE) { + return 3; + } + if (type & NR_INTERFACE_TYPE_TEREDO) { + // Teredo gets penalty because it's IP relayed + return 5; + } + return 4; + } + + // TODO(bug 895790): Once we can get useful interface properties on Darwin, + // we should remove this stuff. + static const std::vector<std::string>& interface_preference_list() { + static std::vector<std::string> list(build_interface_preference_list()); + return list; + } + + static std::vector<std::string> build_interface_preference_list() { + std::vector<std::string> result; + result.push_back("rl0"); + result.push_back("wi0"); + result.push_back("en0"); + result.push_back("enp2s0"); + result.push_back("enp3s0"); + result.push_back("en1"); + result.push_back("en2"); + result.push_back("en3"); + result.push_back("eth0"); + result.push_back("eth1"); + result.push_back("eth2"); + result.push_back("em1"); + result.push_back("em0"); + result.push_back("ppp"); + result.push_back("ppp0"); + result.push_back("vmnet1"); + result.push_back("vmnet0"); + result.push_back("vmnet3"); + result.push_back("vmnet4"); + result.push_back("vmnet5"); + result.push_back("vmnet6"); + result.push_back("vmnet7"); + result.push_back("vmnet8"); + result.push_back("virbr0"); + result.push_back("wlan0"); + result.push_back("lo0"); + return result; + } + + std::string ifname_; + std::string addr_; + std::string key_; + int is_vpn_; + int estimated_speed_; + int type_preference_; + int ip_version_; +}; + +class InterfacePrioritizer { + public: + InterfacePrioritizer() : local_addrs_(), preference_map_(), sorted_(false) {} + + int add(const nr_local_addr* iface) { + LocalAddress addr; + if (!addr.Init(*iface)) { + return R_FAILED; + } + std::pair<std::set<LocalAddress>::iterator, bool> r = + local_addrs_.insert(addr); + if (!r.second) { + return R_ALREADY; // This address is already in the set. + } + sorted_ = false; + return 0; + } + + int sort() { + UCHAR tmp_pref = 127; + preference_map_.clear(); + for (const auto& local_addr : local_addrs_) { + if (tmp_pref == 0) { + return R_FAILED; + } + preference_map_.insert(make_pair(local_addr.GetKey(), tmp_pref--)); + } + sorted_ = true; + return 0; + } + + int getPreference(const char* key, UCHAR* pref) { + if (!sorted_) { + return R_FAILED; + } + std::map<std::string, UCHAR>::iterator i = preference_map_.find(key); + if (i == preference_map_.end()) { + return R_NOT_FOUND; + } + *pref = i->second; + return 0; + } + + private: + std::set<LocalAddress> local_addrs_; + std::map<std::string, UCHAR> preference_map_; + bool sorted_; +}; + +} // anonymous namespace + +static int add_interface(void* obj, nr_local_addr* iface) { + InterfacePrioritizer* ip = static_cast<InterfacePrioritizer*>(obj); + return ip->add(iface); +} + +static int get_priority(void* obj, const char* key, UCHAR* pref) { + InterfacePrioritizer* ip = static_cast<InterfacePrioritizer*>(obj); + return ip->getPreference(key, pref); +} + +static int sort_preference(void* obj) { + InterfacePrioritizer* ip = static_cast<InterfacePrioritizer*>(obj); + return ip->sort(); +} + +static int destroy(void** objp) { + if (!objp || !*objp) { + return 0; + } + + InterfacePrioritizer* ip = static_cast<InterfacePrioritizer*>(*objp); + *objp = nullptr; + delete ip; + + return 0; +} + +static nr_interface_prioritizer_vtbl priorizer_vtbl = { + add_interface, get_priority, sort_preference, destroy}; + +namespace mozilla { + +nr_interface_prioritizer* CreateInterfacePrioritizer() { + nr_interface_prioritizer* ip; + int r = nr_interface_prioritizer_create_int(new InterfacePrioritizer(), + &priorizer_vtbl, &ip); + if (r != 0) { + return nullptr; + } + return ip; +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/nrinterfaceprioritizer.h b/dom/media/webrtc/transport/nrinterfaceprioritizer.h new file mode 100644 index 0000000000..051b586445 --- /dev/null +++ b/dom/media/webrtc/transport/nrinterfaceprioritizer.h @@ -0,0 +1,17 @@ +/* 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/. */ +#ifndef nrinterfacepriority_h__ +#define nrinterfacepriority_h__ + +extern "C" { +#include "nr_interface_prioritizer.h" +} + +namespace mozilla { + +nr_interface_prioritizer* CreateInterfacePrioritizer(); + +} // namespace mozilla + +#endif diff --git a/dom/media/webrtc/transport/rlogconnector.cpp b/dom/media/webrtc/transport/rlogconnector.cpp new file mode 100644 index 0000000000..bdb58aac56 --- /dev/null +++ b/dom/media/webrtc/transport/rlogconnector.cpp @@ -0,0 +1,186 @@ +/* -*- 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: bcampen@mozilla.com */ + +#include "rlogconnector.h" + +#include <cstdarg> +#include <deque> +#include <string> +#include <utility> // Pinch hitting for <utility> and std::move +#include <vector> + +#include "logging.h" +#include "mozilla/Assertions.h" +#include "mozilla/Mutex.h" +#include "mozilla/Sprintf.h" + +extern "C" { +#include <csi_platform.h> +#include "r_log.h" +#include "registry.h" +} + +/* Matches r_dest_vlog type defined in r_log.h */ +static int ringbuffer_vlog(int facility, int level, const char* format, + va_list ap) { + if (mozilla::RLogConnector::GetInstance()->ShouldLog(level)) { + // I could be evil and printf right into a std::string, but unless this + // shows up in profiling, it is not worth doing. + char temp[4096]; + VsprintfLiteral(temp, format, ap); + + mozilla::RLogConnector::GetInstance()->Log(level, std::string(temp)); + } + return 0; +} + +static mozilla::LogLevel rLogLvlToMozLogLvl(int level) { + switch (level) { + case LOG_EMERG: + case LOG_ALERT: + case LOG_CRIT: + case LOG_ERR: + return mozilla::LogLevel::Error; + case LOG_WARNING: + return mozilla::LogLevel::Warning; + case LOG_NOTICE: + return mozilla::LogLevel::Info; + case LOG_INFO: + return mozilla::LogLevel::Debug; + case LOG_DEBUG: + default: + return mozilla::LogLevel::Verbose; + } +} + +MOZ_MTLOG_MODULE("nicer"); + +namespace mozilla { + +RLogConnector* RLogConnector::instance; + +RLogConnector::RLogConnector() + : log_limit_(4096), mutex_("RLogConnector::mutex_"), disableCount_(0) {} + +RLogConnector::~RLogConnector() = default; + +void RLogConnector::SetLogLimit(uint32_t new_limit) { + OffTheBooksMutexAutoLock lock(mutex_); + log_limit_ = new_limit; + RemoveOld(); +} + +bool RLogConnector::ShouldLog(int level) const { + return level <= LOG_INFO || + MOZ_LOG_TEST(getLogModule(), rLogLvlToMozLogLvl(level)); +} + +void RLogConnector::Log(int level, std::string&& log) { + MOZ_MTLOG(rLogLvlToMozLogLvl(level), log); + OffTheBooksMutexAutoLock lock(mutex_); + if (disableCount_ == 0) { + AddMsg(std::move(log)); + } +} + +void RLogConnector::AddMsg(std::string&& msg) { + log_messages_.push_front(std::move(msg)); + RemoveOld(); +} + +inline void RLogConnector::RemoveOld() { + if (log_messages_.size() > log_limit_) { + log_messages_.resize(log_limit_); + } +} + +RLogConnector* RLogConnector::CreateInstance() { + if (!instance) { + instance = new RLogConnector; + NR_reg_init(NR_REG_MODE_LOCAL); + r_log_set_extra_destination(LOG_DEBUG, &ringbuffer_vlog); + } + return instance; +} + +RLogConnector* RLogConnector::GetInstance() { return instance; } + +void RLogConnector::DestroyInstance() { + // First param is ignored when passing null + r_log_set_extra_destination(LOG_DEBUG, nullptr); + delete instance; + instance = nullptr; +} + +// As long as at least one PeerConnection exists in a Private Window rlog +// messages will not be saved in the RLogConnector. This is necessary because +// the log_messages buffer is shared across all instances of +// PeerConnectionImpls. There is no way with the current structure of r_log to +// run separate logs. + +void RLogConnector::EnterPrivateMode() { + OffTheBooksMutexAutoLock lock(mutex_); + ++disableCount_; + MOZ_ASSERT(disableCount_ != 0); + + if (disableCount_ == 1) { + AddMsg("LOGGING SUSPENDED: a connection is active in a Private Window ***"); + } +} + +void RLogConnector::ExitPrivateMode() { + OffTheBooksMutexAutoLock lock(mutex_); + MOZ_ASSERT(disableCount_ != 0); + + if (--disableCount_ == 0) { + AddMsg( + "LOGGING RESUMED: no connections are active in a Private Window ***"); + } +} + +void RLogConnector::Clear() { + OffTheBooksMutexAutoLock lock(mutex_); + log_messages_.clear(); +} + +void RLogConnector::Filter(const std::string& substring, uint32_t limit, + std::deque<std::string>* matching_logs) { + std::vector<std::string> substrings; + substrings.push_back(substring); + FilterAny(substrings, limit, matching_logs); +} + +inline bool AnySubstringMatches(const std::vector<std::string>& substrings, + const std::string& string) { + for (auto sub = substrings.begin(); sub != substrings.end(); ++sub) { + if (string.find(*sub) != std::string::npos) { + return true; + } + } + return false; +} + +void RLogConnector::FilterAny(const std::vector<std::string>& substrings, + uint32_t limit, + std::deque<std::string>* matching_logs) { + OffTheBooksMutexAutoLock lock(mutex_); + if (limit == 0) { + // At a max, all of the log messages. + limit = log_limit_; + } + + for (auto log = log_messages_.begin(); + log != log_messages_.end() && matching_logs->size() < limit; ++log) { + if (AnySubstringMatches(substrings, *log)) { + matching_logs->push_front(*log); + } + } +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/rlogconnector.h b/dom/media/webrtc/transport/rlogconnector.h new file mode 100644 index 0000000000..8236eb2ab3 --- /dev/null +++ b/dom/media/webrtc/transport/rlogconnector.h @@ -0,0 +1,127 @@ +/* -*- 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/. */ + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + +/* 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: bcampen@mozilla.com */ + +/* + This file defines an r_dest_vlog that can be used to accumulate log messages + for later inspection/filtering. The intent is to use this for interactive + debug purposes on an about:webrtc page or similar. +*/ + +#ifndef rlogconnector_h__ +#define rlogconnector_h__ + +#include <stdint.h> + +#include <deque> +#include <string> +#include <vector> + +#include "mozilla/Mutex.h" + +#include "m_cpp_utils.h" + +namespace mozilla { + +class RLogConnector { + public: + /* + NB: These are not threadsafe, nor are they safe to call during static + init/deinit. + */ + static RLogConnector* CreateInstance(); + static RLogConnector* GetInstance(); + static void DestroyInstance(); + + /* + Retrieves log statements that match a given substring, subject to a + limit. |matching_logs| will be filled in chronological order (front() + is oldest, back() is newest). |limit| == 0 will be interpreted as no + limit. + */ + void Filter(const std::string& substring, uint32_t limit, + std::deque<std::string>* matching_logs); + + void FilterAny(const std::vector<std::string>& substrings, uint32_t limit, + std::deque<std::string>* matching_logs); + + inline void GetAny(uint32_t limit, std::deque<std::string>* matching_logs) { + Filter("", limit, matching_logs); + } + + void SetLogLimit(uint32_t new_limit); + bool ShouldLog(int level) const; + void Log(int level, std::string&& log); + void Clear(); + + // Methods to signal when a PeerConnection exists in a Private Window. + void EnterPrivateMode(); + void ExitPrivateMode(); + + private: + RLogConnector(); + ~RLogConnector(); + void RemoveOld(); + void AddMsg(std::string&& msg); + + static RLogConnector* instance; + + /* + * Might be worthwhile making this a circular buffer, but I think it is + * preferable to take up as little space as possible if no logging is + * happening/the ringbuffer is not being used. + */ + std::deque<std::string> log_messages_; + /* Max size of log buffer (should we use time-depth instead/also?) */ + uint32_t log_limit_; + OffTheBooksMutex mutex_; + uint32_t disableCount_; + + DISALLOW_COPY_ASSIGN(RLogConnector); +}; // class RLogConnector + +} // namespace mozilla + +#endif // rlogconnector_h__ diff --git a/dom/media/webrtc/transport/runnable_utils.h b/dom/media/webrtc/transport/runnable_utils.h new file mode 100644 index 0000000000..79f23dac2d --- /dev/null +++ b/dom/media/webrtc/transport/runnable_utils.h @@ -0,0 +1,221 @@ +/* -*- 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 + +#ifndef runnable_utils_h__ +#define runnable_utils_h__ + +#include <utility> + +#include "mozilla/RefPtr.h" +#include "nsThreadUtils.h" +#include <functional> +#include <tuple> +#include <type_traits> + +// Abstract base class for all of our templates +namespace mozilla { + +namespace detail { + +enum RunnableResult { NoResult, ReturnsResult }; + +static inline nsresult RunOnThreadInternal(nsIEventTarget* thread, + nsIRunnable* runnable, + uint32_t flags) { + return thread->Dispatch(runnable, flags); +} + +template <RunnableResult result> +class runnable_args_base : public Runnable { + public: + runnable_args_base() : Runnable("media-runnable_args_base") {} + + NS_IMETHOD Run() final { + MOZ_ASSERT(!mHasRun, "Can only be run once"); + + RunInternal(); +#ifdef DEBUG + mHasRun = true; +#endif + + return NS_OK; + } + + protected: + virtual void RunInternal() = 0; +#ifdef DEBUG + bool mHasRun = false; +#endif +}; + +} // namespace detail + +template <typename FunType, typename... Args> +class runnable_args_func : public detail::runnable_args_base<detail::NoResult> { + public: + // |explicit| to pacify static analysis when there are no |args|. + template <typename... Arguments> + explicit runnable_args_func(FunType f, Arguments&&... args) + : mFunc(f), mArgs(std::forward<Arguments>(args)...) {} + + protected: + void RunInternal() override { + std::apply(std::move(mFunc), std::move(mArgs)); + } + + private: + FunType mFunc; + std::tuple<Args...> mArgs; +}; + +template <typename FunType, typename... Args> +runnable_args_func<FunType, std::decay_t<Args>...>* WrapRunnableNM( + FunType f, Args&&... args) { + return new runnable_args_func<FunType, std::decay_t<Args>...>( + f, std::forward<Args>(args)...); +} + +template <typename Ret, typename FunType, typename... Args> +class runnable_args_func_ret + : public detail::runnable_args_base<detail::ReturnsResult> { + public: + template <typename... Arguments> + runnable_args_func_ret(Ret* ret, FunType f, Arguments&&... args) + : mReturn(ret), mFunc(f), mArgs(std::forward<Arguments>(args)...) {} + + protected: + void RunInternal() override { + *mReturn = std::apply(std::move(mFunc), std::move(mArgs)); + } + + private: + Ret* mReturn; + FunType mFunc; + std::tuple<Args...> mArgs; +}; + +template <typename R, typename FunType, typename... Args> +runnable_args_func_ret<R, FunType, std::decay_t<Args>...>* WrapRunnableNMRet( + R* ret, FunType f, Args&&... args) { + return new runnable_args_func_ret<R, FunType, std::decay_t<Args>...>( + ret, f, std::forward<Args>(args)...); +} + +template <typename Class, typename M, typename... Args> +class runnable_args_memfn + : public detail::runnable_args_base<detail::NoResult> { + public: + template <typename... Arguments> + runnable_args_memfn(Class&& obj, M method, Arguments&&... args) + : mObj(std::forward<Class>(obj)), + mMethod(method), + mArgs(std::forward<Arguments>(args)...) {} + + protected: + void RunInternal() override { + std::apply(std::mem_fn(mMethod), + std::tuple_cat(std::tie(mObj), std::move(mArgs))); + } + + private: + // For holders such as RefPtr and UniquePtr make sure concrete copy is held + // rather than a potential dangling reference. + std::decay_t<Class> mObj; + M mMethod; + std::tuple<Args...> mArgs; +}; + +template <typename Class, typename M, typename... Args> +runnable_args_memfn<Class, M, std::decay_t<Args>...>* WrapRunnable( + Class&& obj, M method, Args&&... args) { + return new runnable_args_memfn<Class, M, std::decay_t<Args>...>( + std::forward<Class>(obj), method, std::forward<Args>(args)...); +} + +template <typename Ret, typename Class, typename M, typename... Args> +class runnable_args_memfn_ret + : public detail::runnable_args_base<detail::ReturnsResult> { + public: + template <typename... Arguments> + runnable_args_memfn_ret(Ret* ret, Class&& obj, M method, Arguments... args) + : mReturn(ret), + mObj(std::forward<Class>(obj)), + mMethod(method), + mArgs(std::forward<Arguments>(args)...) {} + + protected: + void RunInternal() override { + *mReturn = std::apply(std::mem_fn(mMethod), + std::tuple_cat(std::tie(mObj), std::move(mArgs))); + } + + private: + Ret* mReturn; + // For holders such as RefPtr and UniquePtr make sure concrete copy is held + // rather than a potential dangling reference. + std::decay_t<Class> mObj; + M mMethod; + std::tuple<Args...> mArgs; +}; + +template <typename R, typename Class, typename M, typename... Args> +runnable_args_memfn_ret<R, Class, M, std::decay_t<Args>...>* WrapRunnableRet( + R* ret, Class&& obj, M method, Args&&... args) { + return new runnable_args_memfn_ret<R, Class, M, std::decay_t<Args>...>( + ret, std::forward<Class>(obj), method, std::forward<Args>(args)...); +} + +static inline nsresult RUN_ON_THREAD( + nsIEventTarget* thread, + detail::runnable_args_base<detail::NoResult>* runnable, uint32_t flags) { + return detail::RunOnThreadInternal( + thread, static_cast<nsIRunnable*>(runnable), flags); +} + +static inline nsresult RUN_ON_THREAD( + nsIEventTarget* thread, + detail::runnable_args_base<detail::ReturnsResult>* runnable) { + return detail::RunOnThreadInternal( + thread, static_cast<nsIRunnable*>(runnable), NS_DISPATCH_SYNC); +} + +#ifdef DEBUG +# define ASSERT_ON_THREAD(t) \ + do { \ + if (t) { \ + bool on; \ + nsresult rv; \ + rv = t->IsOnCurrentThread(&on); \ + MOZ_ASSERT(NS_SUCCEEDED(rv)); \ + MOZ_ASSERT(on); \ + } \ + } while (0) +#else +# define ASSERT_ON_THREAD(t) +#endif + +template <class T> +class DispatchedRelease : public detail::runnable_args_base<detail::NoResult> { + public: + explicit DispatchedRelease(already_AddRefed<T>& ref) : ref_(ref) {} + + protected: + void RunInternal() override { ref_ = nullptr; } + + private: + RefPtr<T> ref_; +}; + +template <typename T> +DispatchedRelease<T>* WrapRelease(already_AddRefed<T>&& ref) { + return new DispatchedRelease<T>(ref); +} + +} /* namespace mozilla */ + +#endif diff --git a/dom/media/webrtc/transport/sigslot.h b/dom/media/webrtc/transport/sigslot.h new file mode 100644 index 0000000000..448e8137fe --- /dev/null +++ b/dom/media/webrtc/transport/sigslot.h @@ -0,0 +1,619 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with +// the proviso that the author takes on no responsibility or liability for any +// use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO: +// Define this to force ISO C++ compliance. This also disables all of +// the thread safety support on platforms where it is available. +// +// SIGSLOT_USE_POSIX_THREADS: +// Force use of Posix threads when using a C++ compiler other than gcc +// on a platform that supports Posix threads. (When using gcc, this is +// the default - use SIGSLOT_PURE_ISO to disable this if necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY: +// Where thread support is enabled, this defaults to +// multi_threaded_global. Otherwise, the default is single_threaded. +// #define this yourself to override the default. In pure ISO mode, +// anything other than single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32: +// On Win32, the WEBRTC_WIN symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it yourself +// if your build environment is less standard. This causes the Win32 +// thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc.: +// If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this (as +// under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++: +// If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned +// off, along with any code that might cause a pure ISO C++ environment +// to complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded: +// Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global: +// Your program is assumed to be multi threaded. Objects using signals +// and slots can be safely created and destroyed from any thread, even +// when connections exist. In multi_threaded_global mode, this is +// achieved by a single global mutex (actually a critical section on +// Windows because they are faster). This option uses less OS resources, +// but results in more opportunities for contention, possibly resulting +// in more context switches than are strictly necessary. +// +// multi_threaded_local: +// Behaviour in this mode is essentially the same as +// multi_threaded_global, except that each signal, and each object that +// inherits has_slots, all have their own mutex/critical section. In +// practice, this means that mutex collisions (and hence context +// switches) only happen if they are absolutely essential. However, on +// some platforms, creating a lot of mutexes can slow down the whole OS, +// so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// Libjingle specific: +// +// This file has been modified such that has_slots and signalx do not have to be +// using the same threading requirements. E.g. it is possible to connect a +// has_slots<single_threaded> and signal0<multi_threaded_local> or +// has_slots<multi_threaded_local> and signal0<single_threaded>. +// If has_slots is single threaded the user must ensure that it is not trying +// to connect or disconnect to signalx concurrently or data race may occur. +// If signalx is single threaded the user must ensure that disconnect, connect +// or signal is not happening concurrently or data race may occur. + +#ifndef RTC_BASE_SIGSLOT_H_ +#define RTC_BASE_SIGSLOT_H_ + +#include <stdlib.h> +#include <cstring> +#include <list> +#include <set> + +// On our copy of sigslot.h, we set single threading as default. +#define SIGSLOT_DEFAULT_MT_POLICY single_threaded + +#if defined(SIGSLOT_PURE_ISO) || \ + (!defined(WEBRTC_WIN) && !defined(__GNUG__) && \ + !defined(SIGSLOT_USE_POSIX_THREADS)) +# define _SIGSLOT_SINGLE_THREADED +#elif defined(WEBRTC_WIN) +# define _SIGSLOT_HAS_WIN32_THREADS +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif +# include "rtc_base/win32.h" +#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS) +# define _SIGSLOT_HAS_POSIX_THREADS +# include <pthread.h> +#else +# define _SIGSLOT_SINGLE_THREADED +#endif + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + +// TODO: change this namespace to rtc? +namespace sigslot { + +class single_threaded { + public: + void lock() {} + void unlock() {} +}; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS +// The multi threading policies only get compiled in if they are enabled. +class multi_threaded_global { + public: + multi_threaded_global() { + static bool isinitialised = false; + + if (!isinitialised) { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + void lock() { EnterCriticalSection(get_critsec()); } + + void unlock() { LeaveCriticalSection(get_critsec()); } + + private: + CRITICAL_SECTION* get_critsec() { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } +}; + +class multi_threaded_local { + public: + multi_threaded_local() { InitializeCriticalSection(&m_critsec); } + + multi_threaded_local(const multi_threaded_local&) { + InitializeCriticalSection(&m_critsec); + } + + ~multi_threaded_local() { DeleteCriticalSection(&m_critsec); } + + void lock() { EnterCriticalSection(&m_critsec); } + + void unlock() { LeaveCriticalSection(&m_critsec); } + + private: + CRITICAL_SECTION m_critsec; +}; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS +// The multi threading policies only get compiled in if they are enabled. +class multi_threaded_global { + public: + void lock() { pthread_mutex_lock(get_mutex()); } + void unlock() { pthread_mutex_unlock(get_mutex()); } + + private: + static pthread_mutex_t* get_mutex(); +}; + +class multi_threaded_local { + public: + multi_threaded_local() { pthread_mutex_init(&m_mutex, nullptr); } + multi_threaded_local(const multi_threaded_local&) { + pthread_mutex_init(&m_mutex, nullptr); + } + ~multi_threaded_local() { pthread_mutex_destroy(&m_mutex); } + void lock() { pthread_mutex_lock(&m_mutex); } + void unlock() { pthread_mutex_unlock(&m_mutex); } + + private: + pthread_mutex_t m_mutex; +}; +#endif // _SIGSLOT_HAS_POSIX_THREADS + +template <class mt_policy> +class lock_block { + public: + mt_policy* m_mutex; + + explicit lock_block(mt_policy* mtx) : m_mutex(mtx) { m_mutex->lock(); } + + ~lock_block() { m_mutex->unlock(); } +}; + +class _signal_base_interface; + +class has_slots_interface { + private: + typedef void (*signal_connect_t)(has_slots_interface* self, + _signal_base_interface* sender); + typedef void (*signal_disconnect_t)(has_slots_interface* self, + _signal_base_interface* sender); + typedef void (*disconnect_all_t)(has_slots_interface* self); + + const signal_connect_t m_signal_connect; + const signal_disconnect_t m_signal_disconnect; + const disconnect_all_t m_disconnect_all; + + protected: + has_slots_interface(signal_connect_t conn, signal_disconnect_t disc, + disconnect_all_t disc_all) + : m_signal_connect(conn), + m_signal_disconnect(disc), + m_disconnect_all(disc_all) {} + + // Doesn't really need to be virtual, but is for backwards compatibility + // (it was virtual in a previous version of sigslot). + virtual ~has_slots_interface() = default; + + public: + void signal_connect(_signal_base_interface* sender) { + m_signal_connect(this, sender); + } + + void signal_disconnect(_signal_base_interface* sender) { + m_signal_disconnect(this, sender); + } + + void disconnect_all() { m_disconnect_all(this); } +}; + +class _signal_base_interface { + private: + typedef void (*slot_disconnect_t)(_signal_base_interface* self, + has_slots_interface* pslot); + typedef void (*slot_duplicate_t)(_signal_base_interface* self, + const has_slots_interface* poldslot, + has_slots_interface* pnewslot); + + const slot_disconnect_t m_slot_disconnect; + const slot_duplicate_t m_slot_duplicate; + + protected: + _signal_base_interface(slot_disconnect_t disc, slot_duplicate_t dupl) + : m_slot_disconnect(disc), m_slot_duplicate(dupl) {} + + ~_signal_base_interface() = default; + + public: + void slot_disconnect(has_slots_interface* pslot) { + m_slot_disconnect(this, pslot); + } + + void slot_duplicate(const has_slots_interface* poldslot, + has_slots_interface* pnewslot) { + m_slot_duplicate(this, poldslot, pnewslot); + } +}; + +class _opaque_connection { + private: + typedef void (*emit_t)(const _opaque_connection*); + template <typename FromT, typename ToT> + union union_caster { + FromT from; + ToT to; + }; + + emit_t pemit; + has_slots_interface* pdest; + // Pointers to member functions may be up to 16 bytes for virtual classes, + // so make sure we have enough space to store it. + unsigned char pmethod[16]; + + public: + template <typename DestT, typename... Args> + _opaque_connection(DestT* pd, void (DestT::*pm)(Args...)) : pdest(pd) { + typedef void (DestT::*pm_t)(Args...); + static_assert(sizeof(pm_t) <= sizeof(pmethod), + "Size of slot function pointer too large."); + + std::memcpy(pmethod, &pm, sizeof(pm_t)); + + typedef void (*em_t)(const _opaque_connection* self, Args...); + union_caster<em_t, emit_t> caster2; + caster2.from = &_opaque_connection::emitter<DestT, Args...>; + pemit = caster2.to; + } + + has_slots_interface* getdest() const { return pdest; } + + _opaque_connection duplicate(has_slots_interface* newtarget) const { + _opaque_connection res = *this; + res.pdest = newtarget; + return res; + } + + // Just calls the stored "emitter" function pointer stored at construction + // time. + template <typename... Args> + void emit(Args... args) const { + typedef void (*em_t)(const _opaque_connection*, Args...); + union_caster<emit_t, em_t> caster; + caster.from = pemit; + (caster.to)(this, args...); + } + + private: + template <typename DestT, typename... Args> + static void emitter(const _opaque_connection* self, Args... args) { + typedef void (DestT::*pm_t)(Args...); + pm_t pm; + std::memcpy(&pm, self->pmethod, sizeof(pm_t)); + (static_cast<DestT*>(self->pdest)->*(pm))(args...); + } +}; + +template <class mt_policy> +class _signal_base : public _signal_base_interface, public mt_policy { + protected: + typedef std::list<_opaque_connection> connections_list; + + _signal_base() + : _signal_base_interface(&_signal_base::do_slot_disconnect, + &_signal_base::do_slot_duplicate), + m_current_iterator(m_connected_slots.end()) {} + + ~_signal_base() { disconnect_all(); } + + private: + _signal_base& operator=(_signal_base const& that); + + public: + _signal_base(const _signal_base& o) + : _signal_base_interface(&_signal_base::do_slot_disconnect, + &_signal_base::do_slot_duplicate), + m_current_iterator(m_connected_slots.end()) { + lock_block<mt_policy> lock(this); + for (const auto& connection : o.m_connected_slots) { + connection.getdest()->signal_connect(this); + m_connected_slots.push_back(connection); + } + } + + bool is_empty() { + lock_block<mt_policy> lock(this); + return m_connected_slots.empty(); + } + + void disconnect_all() { + lock_block<mt_policy> lock(this); + + while (!m_connected_slots.empty()) { + has_slots_interface* pdest = m_connected_slots.front().getdest(); + m_connected_slots.pop_front(); + pdest->signal_disconnect(static_cast<_signal_base_interface*>(this)); + } + // If disconnect_all is called while the signal is firing, advance the + // current slot iterator to the end to avoid an invalidated iterator from + // being dereferenced. + m_current_iterator = m_connected_slots.end(); + } + +#if !defined(NDEBUG) + bool connected(has_slots_interface* pclass) { + lock_block<mt_policy> lock(this); + connections_list::const_iterator it = m_connected_slots.begin(); + connections_list::const_iterator itEnd = m_connected_slots.end(); + while (it != itEnd) { + if (it->getdest() == pclass) return true; + ++it; + } + return false; + } +#endif + + void disconnect(has_slots_interface* pclass) { + lock_block<mt_policy> lock(this); + connections_list::iterator it = m_connected_slots.begin(); + connections_list::iterator itEnd = m_connected_slots.end(); + + while (it != itEnd) { + if (it->getdest() == pclass) { + // If we're currently using this iterator because the signal is firing, + // advance it to avoid it being invalidated. + if (m_current_iterator == it) { + m_current_iterator = m_connected_slots.erase(it); + } else { + m_connected_slots.erase(it); + } + pclass->signal_disconnect(static_cast<_signal_base_interface*>(this)); + return; + } + ++it; + } + } + + private: + static void do_slot_disconnect(_signal_base_interface* p, + has_slots_interface* pslot) { + _signal_base* const self = static_cast<_signal_base*>(p); + lock_block<mt_policy> lock(self); + connections_list::iterator it = self->m_connected_slots.begin(); + connections_list::iterator itEnd = self->m_connected_slots.end(); + + while (it != itEnd) { + connections_list::iterator itNext = it; + ++itNext; + + if (it->getdest() == pslot) { + // If we're currently using this iterator because the signal is firing, + // advance it to avoid it being invalidated. + if (self->m_current_iterator == it) { + self->m_current_iterator = self->m_connected_slots.erase(it); + } else { + self->m_connected_slots.erase(it); + } + } + + it = itNext; + } + } + + static void do_slot_duplicate(_signal_base_interface* p, + const has_slots_interface* oldtarget, + has_slots_interface* newtarget) { + _signal_base* const self = static_cast<_signal_base*>(p); + lock_block<mt_policy> lock(self); + connections_list::iterator it = self->m_connected_slots.begin(); + connections_list::iterator itEnd = self->m_connected_slots.end(); + + while (it != itEnd) { + if (it->getdest() == oldtarget) { + self->m_connected_slots.push_back(it->duplicate(newtarget)); + } + + ++it; + } + } + + protected: + connections_list m_connected_slots; + + // Used to handle a slot being disconnected while a signal is + // firing (iterating m_connected_slots). + connections_list::iterator m_current_iterator; + bool m_erase_current_iterator = false; +}; + +template <class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +class has_slots : public has_slots_interface, public mt_policy { + private: + typedef std::set<_signal_base_interface*> sender_set; + typedef sender_set::const_iterator const_iterator; + + public: + has_slots() + : has_slots_interface(&has_slots::do_signal_connect, + &has_slots::do_signal_disconnect, + &has_slots::do_disconnect_all) {} + + has_slots(has_slots const& o) + : has_slots_interface(&has_slots::do_signal_connect, + &has_slots::do_signal_disconnect, + &has_slots::do_disconnect_all) { + lock_block<mt_policy> lock(this); + for (auto* sender : o.m_senders) { + sender->slot_duplicate(&o, this); + m_senders.insert(sender); + } + } + + ~has_slots() { this->disconnect_all(); } + + private: + has_slots& operator=(has_slots const&); + + static void do_signal_connect(has_slots_interface* p, + _signal_base_interface* sender) { + has_slots* const self = static_cast<has_slots*>(p); + lock_block<mt_policy> lock(self); + self->m_senders.insert(sender); + } + + static void do_signal_disconnect(has_slots_interface* p, + _signal_base_interface* sender) { + has_slots* const self = static_cast<has_slots*>(p); + lock_block<mt_policy> lock(self); + self->m_senders.erase(sender); + } + + static void do_disconnect_all(has_slots_interface* p) { + has_slots* const self = static_cast<has_slots*>(p); + lock_block<mt_policy> lock(self); + while (!self->m_senders.empty()) { + std::set<_signal_base_interface*> senders; + senders.swap(self->m_senders); + const_iterator it = senders.begin(); + const_iterator itEnd = senders.end(); + + while (it != itEnd) { + _signal_base_interface* s = *it; + ++it; + s->slot_disconnect(p); + } + } + } + + private: + sender_set m_senders; +}; + +template <class mt_policy, typename... Args> +class signal_with_thread_policy : public _signal_base<mt_policy> { + private: + typedef _signal_base<mt_policy> base; + + protected: + typedef typename base::connections_list connections_list; + + public: + signal_with_thread_policy() = default; + + template <class desttype> + void connect(desttype* pclass, void (desttype::*pmemfun)(Args...)) { + lock_block<mt_policy> lock(this); + this->m_connected_slots.push_back(_opaque_connection(pclass, pmemfun)); + pclass->signal_connect(static_cast<_signal_base_interface*>(this)); + } + + void emit(Args... args) { + lock_block<mt_policy> lock(this); + this->m_current_iterator = this->m_connected_slots.begin(); + while (this->m_current_iterator != this->m_connected_slots.end()) { + _opaque_connection const& conn = *this->m_current_iterator; + ++(this->m_current_iterator); + conn.emit<Args...>(args...); + } + } + + void operator()(Args... args) { emit(args...); } +}; + +// Alias with default thread policy. Needed because both default arguments +// and variadic template arguments must go at the end of the list, so we +// can't have both at once. +template <typename... Args> +using signal = signal_with_thread_policy<SIGSLOT_DEFAULT_MT_POLICY, Args...>; + +// The previous verion of sigslot didn't use variadic templates, so you would +// need to write "sigslot::signal2<Arg1, Arg2>", for example. +// Now you can just write "sigslot::signal<Arg1, Arg2>", but these aliases +// exist for backwards compatibility. +template <typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +using signal0 = signal_with_thread_policy<mt_policy>; + +template <typename A1, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +using signal1 = signal_with_thread_policy<mt_policy, A1>; + +template <typename A1, typename A2, + typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +using signal2 = signal_with_thread_policy<mt_policy, A1, A2>; + +template <typename A1, typename A2, typename A3, + typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +using signal3 = signal_with_thread_policy<mt_policy, A1, A2, A3>; + +template <typename A1, typename A2, typename A3, typename A4, + typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +using signal4 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4>; + +template <typename A1, typename A2, typename A3, typename A4, typename A5, + typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +using signal5 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5>; + +template <typename A1, typename A2, typename A3, typename A4, typename A5, + typename A6, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +using signal6 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6>; + +template <typename A1, typename A2, typename A3, typename A4, typename A5, + typename A6, typename A7, + typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +using signal7 = + signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7>; + +template <typename A1, typename A2, typename A3, typename A4, typename A5, + typename A6, typename A7, typename A8, + typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> +using signal8 = + signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7, A8>; + +} // namespace sigslot + +#endif // RTC_BASE_SIGSLOT_H_ diff --git a/dom/media/webrtc/transport/simpletokenbucket.cpp b/dom/media/webrtc/transport/simpletokenbucket.cpp new file mode 100644 index 0000000000..0509697c7b --- /dev/null +++ b/dom/media/webrtc/transport/simpletokenbucket.cpp @@ -0,0 +1,60 @@ +/* -*- 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: bcampen@mozilla.com */ + +#include "simpletokenbucket.h" + +#include <stdint.h> + +#include "prinrval.h" + +namespace mozilla { + +SimpleTokenBucket::SimpleTokenBucket(size_t bucket_size, + size_t tokens_per_second) + : max_tokens_(bucket_size), + num_tokens_(bucket_size), + tokens_per_second_(tokens_per_second), + last_time_tokens_added_(PR_IntervalNow()) {} + +size_t SimpleTokenBucket::getTokens(size_t num_requested_tokens) { + // Only fill if there isn't enough to satisfy the request. + // If we get tokens so seldomly that we are able to roll the timer all + // the way around its range, then we lose that entire range of time + // for token accumulation. Probably not the end of the world. + if (num_requested_tokens > num_tokens_) { + PRIntervalTime now = PR_IntervalNow(); + + // If we roll over the max, since everything in this calculation is the same + // unsigned type, this will still yield the elapsed time (unless we've + // wrapped more than once). + PRIntervalTime elapsed_ticks = now - last_time_tokens_added_; + + uint32_t elapsed_milli_sec = PR_IntervalToMilliseconds(elapsed_ticks); + size_t tokens_to_add = (elapsed_milli_sec * tokens_per_second_) / 1000; + + // Only update our timestamp if we added some tokens + // TODO:(bcampen@mozilla.com) Should we attempt to "save" leftover time? + if (tokens_to_add) { + num_tokens_ += tokens_to_add; + if (num_tokens_ > max_tokens_) { + num_tokens_ = max_tokens_; + } + + last_time_tokens_added_ = now; + } + + if (num_requested_tokens > num_tokens_) { + return num_tokens_; + } + } + + num_tokens_ -= num_requested_tokens; + return num_requested_tokens; +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/simpletokenbucket.h b/dom/media/webrtc/transport/simpletokenbucket.h new file mode 100644 index 0000000000..7e809535b1 --- /dev/null +++ b/dom/media/webrtc/transport/simpletokenbucket.h @@ -0,0 +1,54 @@ +/* -*- 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: bcampen@mozilla.com */ + +/* + * This file defines a dirt-simple token bucket class. + */ + +#ifndef simpletokenbucket_h__ +#define simpletokenbucket_h__ + +#include <stdint.h> + +#include "prinrval.h" + +#include "m_cpp_utils.h" + +namespace mozilla { + +class SimpleTokenBucket { + public: + /* + * Create a SimpleTokenBucket with a given maximum size and + * token replenishment rate. + * (eg; if you want a maximum rate of 5 per second over a 7 second + * period, call SimpleTokenBucket b(5*7, 5);) + */ + SimpleTokenBucket(size_t bucket_size, size_t tokens_per_second); + + /* + * Attempt to acquire a number of tokens. If successful, returns + * |num_tokens|, otherwise returns the number of tokens currently + * in the bucket. + * Note: To get the number of tokens in the bucket, pass something + * like UINT32_MAX. + */ + size_t getTokens(size_t num_tokens); + + protected: // Allow testing to touch these. + uint64_t max_tokens_; + uint64_t num_tokens_; + size_t tokens_per_second_; + PRIntervalTime last_time_tokens_added_; + + DISALLOW_COPY_ASSIGN(SimpleTokenBucket); +}; + +} // namespace mozilla + +#endif // simpletokenbucket_h__ diff --git a/dom/media/webrtc/transport/srtp/README_MOZILLA b/dom/media/webrtc/transport/srtp/README_MOZILLA new file mode 100644 index 0000000000..8533b67c53 --- /dev/null +++ b/dom/media/webrtc/transport/srtp/README_MOZILLA @@ -0,0 +1,7 @@ +This directory contains build files for libsrtp. The actual library +source is in $TOPSRCDIR/third_party/libsrtp/ + +The upstream git repository is https://github.com/cisco/libsrtp + +TBD add code and instructions how to do a clean update import without manual +intervention. diff --git a/dom/media/webrtc/transport/srtp/moz.build b/dom/media/webrtc/transport/srtp/moz.build new file mode 100644 index 0000000000..d3d4971a6d --- /dev/null +++ b/dom/media/webrtc/transport/srtp/moz.build @@ -0,0 +1,8 @@ +# -*- Mode: python; 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["MOZ_SRTP"]: + DIRS += ["/third_party/libsrtp/src"] diff --git a/dom/media/webrtc/transport/stun_socket_filter.cpp b/dom/media/webrtc/transport/stun_socket_filter.cpp new file mode 100644 index 0000000000..b568f97a40 --- /dev/null +++ b/dom/media/webrtc/transport/stun_socket_filter.cpp @@ -0,0 +1,432 @@ +/* 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/. */ +#include <string> +#include <set> +#include <iomanip> + +extern "C" { +#include "nr_api.h" +#include "transport_addr.h" +#include "stun.h" +} + +#include "logging.h" +#include "mozilla/Attributes.h" +#include "mozilla/net/DNS.h" +#include "stun_socket_filter.h" +#include "nr_socket_prsock.h" + +namespace { + +MOZ_MTLOG_MODULE("mtransport") + +class NetAddrCompare { + public: + bool operator()(const mozilla::net::NetAddr& lhs, + const mozilla::net::NetAddr& rhs) const { + if (lhs.raw.family != rhs.raw.family) { + return lhs.raw.family < rhs.raw.family; + } + + switch (lhs.raw.family) { + case AF_INET: + if (lhs.inet.port != rhs.inet.port) { + return lhs.inet.port < rhs.inet.port; + } + return lhs.inet.ip < rhs.inet.ip; + case AF_INET6: + if (lhs.inet6.port != rhs.inet6.port) { + return lhs.inet6.port < rhs.inet6.port; + } + return memcmp(&lhs.inet6.ip, &rhs.inet6.ip, sizeof(lhs.inet6.ip)) < 0; + default: + MOZ_ASSERT(false); + } + return false; + } +}; + +class PendingSTUNRequest { + public: + PendingSTUNRequest(const mozilla::net::NetAddr& netaddr, const UINT12& id) + : id_(id), net_addr_(netaddr), is_id_set_(true) {} + + MOZ_IMPLICIT PendingSTUNRequest(const mozilla::net::NetAddr& netaddr) + : id_(), net_addr_(netaddr), is_id_set_(false) {} + + bool operator<(const PendingSTUNRequest& rhs) const { + if (NetAddrCompare()(net_addr_, rhs.net_addr_)) { + return true; + } + + if (NetAddrCompare()(rhs.net_addr_, net_addr_)) { + return false; + } + + if (!is_id_set_ && !rhs.is_id_set_) { + // PendingSTUNRequest can be stored to set only when it has id, + // so comparing two PendingSTUNRequst without id is not going + // to happen. + MOZ_CRASH(); + } + + if (!(is_id_set_ && rhs.is_id_set_)) { + // one of operands doesn't have id, ignore the difference. + return false; + } + + return memcmp(id_.octet, rhs.id_.octet, sizeof(id_.octet)) < 0; + } + + private: + const UINT12 id_; + const mozilla::net::NetAddr net_addr_; + const bool is_id_set_; +}; + +static uint16_t GetPortInfallible(const mozilla::net::NetAddr& aAddr) { + uint16_t result = 0; + (void)aAddr.GetPort(&result); + return result; +} + +static std::ostream& operator<<(std::ostream& aStream, UINT12 aId) { + for (int octet : aId.octet) { + aStream << std::hex << std::setfill('0') << std::setw(2) << octet; + } + return aStream; +} + +class STUNUDPSocketFilter : public nsISocketFilter { + public: + STUNUDPSocketFilter() : white_list_(), pending_requests_() {} + + // Allocated/freed and used on the PBackground IPC thread + NS_DECL_ISUPPORTS + NS_DECL_NSISOCKETFILTER + + private: + virtual ~STUNUDPSocketFilter() = default; + + bool filter_incoming_packet(const mozilla::net::NetAddr* remote_addr, + const uint8_t* data, uint32_t len); + + bool filter_outgoing_packet(const mozilla::net::NetAddr* remote_addr, + const uint8_t* data, uint32_t len); + + std::set<mozilla::net::NetAddr, NetAddrCompare> white_list_; + std::set<PendingSTUNRequest> pending_requests_; + std::set<PendingSTUNRequest> response_allowed_; +}; + +NS_IMPL_ISUPPORTS(STUNUDPSocketFilter, nsISocketFilter) + +NS_IMETHODIMP +STUNUDPSocketFilter::FilterPacket(const mozilla::net::NetAddr* remote_addr, + const uint8_t* data, uint32_t len, + int32_t direction, bool* result) { + switch (direction) { + case nsISocketFilter::SF_INCOMING: + *result = filter_incoming_packet(remote_addr, data, len); + break; + case nsISocketFilter::SF_OUTGOING: + *result = filter_outgoing_packet(remote_addr, data, len); + break; + default: + MOZ_CRASH("Unknown packet direction"); + } + return NS_OK; +} + +bool STUNUDPSocketFilter::filter_incoming_packet( + const mozilla::net::NetAddr* remote_addr, const uint8_t* data, + uint32_t len) { + // Check white list + if (white_list_.find(*remote_addr) != white_list_.end()) { + MOZ_MTLOG(ML_DEBUG, __func__ << this << " Address in whitelist: " + << remote_addr->ToString() << ":" + << GetPortInfallible(*remote_addr)); + return true; + } + + // If it is a STUN response message and we can match its id with one of the + // pending requests, we can add this address into whitelist. + if (nr_is_stun_response_message( + reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { + const nr_stun_message_header* msg = + reinterpret_cast<const nr_stun_message_header*>(data); + PendingSTUNRequest pending_req(*remote_addr, msg->id); + std::set<PendingSTUNRequest>::iterator it = + pending_requests_.find(pending_req); + if (it != pending_requests_.end()) { + pending_requests_.erase(it); + response_allowed_.erase(pending_req); + white_list_.insert(*remote_addr); + MOZ_MTLOG(ML_DEBUG, __func__ << this + << " Allowing known STUN response, " + "remembering address in whitelist: " + << remote_addr->ToString() << ":" + << GetPortInfallible(*remote_addr) + << " id=" << msg->id); + return true; + } + } + // If it's an incoming STUN request we let it pass and add it to the list of + // pending response for white listing once we answer. + if (nr_is_stun_request_message( + reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { + const nr_stun_message_header* msg = + reinterpret_cast<const nr_stun_message_header*>(data); + response_allowed_.insert(PendingSTUNRequest(*remote_addr, msg->id)); + MOZ_MTLOG( + ML_DEBUG, + __func__ << this + << " Allowing STUN request, will allow packets in return: " + << remote_addr->ToString() << ":" + << GetPortInfallible(*remote_addr) << " id=" << msg->id); + return true; + } + // Lastly if we have send a STUN request to the destination of this + // packet we allow it to send us anything back in case it's for example a + // DTLS message (but we don't white list). + std::set<PendingSTUNRequest>::iterator it = + pending_requests_.find(PendingSTUNRequest(*remote_addr)); + if (it != pending_requests_.end()) { + MOZ_MTLOG( + ML_DEBUG, + __func__ + << this + << " Allowing packet from source while waiting for a response: " + << remote_addr->ToString() << ":" + << GetPortInfallible(*remote_addr)); + return true; + } + + MOZ_MTLOG( + ML_DEBUG, + __func__ + << " Disallowing packet that is neither a STUN request or response: " + << remote_addr->ToString() << ":" << GetPortInfallible(*remote_addr)); + return false; +} + +bool STUNUDPSocketFilter::filter_outgoing_packet( + const mozilla::net::NetAddr* remote_addr, const uint8_t* data, + uint32_t len) { + // Check white list + if (white_list_.find(*remote_addr) != white_list_.end()) { + MOZ_MTLOG(ML_DEBUG, __func__ << this << " Address in whitelist: " + << remote_addr->ToString() << ":" + << GetPortInfallible(*remote_addr)); + return true; + } + + // Check if it is a stun packet. If yes, we put it into a pending list and + // wait for response packet. + if (nr_is_stun_request_message( + reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { + const nr_stun_message_header* msg = + reinterpret_cast<const nr_stun_message_header*>(data); + pending_requests_.insert(PendingSTUNRequest(*remote_addr, msg->id)); + MOZ_MTLOG( + ML_DEBUG, + __func__ << this + << " Allowing STUN request, will allow packets in return: " + << remote_addr->ToString() << ":" + << GetPortInfallible(*remote_addr) << " id=" << msg->id); + return true; + } + + // If it is a stun response packet, and we had received the request before, we + // can allow it packet to pass filter. + if (nr_is_stun_response_message( + reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) { + const nr_stun_message_header* msg = + reinterpret_cast<const nr_stun_message_header*>(data); + std::set<PendingSTUNRequest>::iterator it = + response_allowed_.find(PendingSTUNRequest(*remote_addr, msg->id)); + if (it != response_allowed_.end()) { + white_list_.insert(*remote_addr); + response_allowed_.erase(it); + MOZ_MTLOG(ML_DEBUG, __func__ << this + << " Allowing known STUN response, " + "remembering address in whitelist: " + << remote_addr->ToString() << ":" + << GetPortInfallible(*remote_addr) + << " id=" << msg->id); + return true; + } + + MOZ_MTLOG(ML_DEBUG, + __func__ << this << " Disallowing unknown STUN response: " + << remote_addr->ToString() << ":" + << GetPortInfallible(*remote_addr) << " id=" << msg->id); + return false; + } + + MOZ_MTLOG( + ML_DEBUG, + __func__ + << " Disallowing packet that is neither a STUN request or response: " + << remote_addr->ToString() << ":" << GetPortInfallible(*remote_addr)); + return false; +} + +class PendingSTUNId { + public: + explicit PendingSTUNId(const UINT12& id) : id_(id) {} + + bool operator<(const PendingSTUNId& rhs) const { + return memcmp(id_.octet, rhs.id_.octet, sizeof(id_.octet)) < 0; + } + + private: + const UINT12 id_; +}; + +class STUNTCPSocketFilter : public nsISocketFilter { + public: + STUNTCPSocketFilter() + : white_listed_(false), pending_request_ids_(), response_allowed_ids_() {} + + // Allocated/freed and used on the PBackground IPC thread + NS_DECL_ISUPPORTS + NS_DECL_NSISOCKETFILTER + + private: + virtual ~STUNTCPSocketFilter() = default; + + bool filter_incoming_packet(const uint8_t* data, uint32_t len); + + bool filter_outgoing_packet(const uint8_t* data, uint32_t len); + + bool white_listed_; + std::set<PendingSTUNId> pending_request_ids_; + std::set<PendingSTUNId> response_allowed_ids_; +}; + +NS_IMPL_ISUPPORTS(STUNTCPSocketFilter, nsISocketFilter) + +NS_IMETHODIMP +STUNTCPSocketFilter::FilterPacket(const mozilla::net::NetAddr* remote_addr, + const uint8_t* data, uint32_t len, + int32_t direction, bool* result) { + switch (direction) { + case nsISocketFilter::SF_INCOMING: + *result = filter_incoming_packet(data, len); + break; + case nsISocketFilter::SF_OUTGOING: + *result = filter_outgoing_packet(data, len); + break; + default: + MOZ_CRASH("Unknown packet direction"); + } + return NS_OK; +} + +bool STUNTCPSocketFilter::filter_incoming_packet(const uint8_t* data, + uint32_t len) { + // check if white listed already + if (white_listed_) { + return true; + } + + UCHAR* stun = const_cast<uint8_t*>(data); + uint32_t length = len; + if (!nr_is_stun_message(stun, length)) { + stun += 2; + length -= 2; + if (!nr_is_stun_message(stun, length)) { + // Note: the UDP filter lets incoming packets pass, because order of + // packets is not guaranteed and the next packet is likely an important + // packet for DTLS (which is costly in terms of timing to wait for a + // retransmit). This does not apply to TCP with its guaranteed order. But + // we still let it pass, because otherwise we would have to buffer bytes + // here until the minimum STUN request size of bytes has been received. + return true; + } + } + + const nr_stun_message_header* msg = + reinterpret_cast<const nr_stun_message_header*>(stun); + + // If it is a STUN response message and we can match its id with one of the + // pending requests, we can add this address into whitelist. + if (nr_is_stun_response_message(stun, length)) { + std::set<PendingSTUNId>::iterator it = + pending_request_ids_.find(PendingSTUNId(msg->id)); + if (it != pending_request_ids_.end()) { + pending_request_ids_.erase(it); + white_listed_ = true; + } + } else { + // If it is a STUN message, but not a response message, we add it into + // response allowed list and allow outgoing filter to send a response back. + response_allowed_ids_.insert(PendingSTUNId(msg->id)); + } + + return true; +} + +bool STUNTCPSocketFilter::filter_outgoing_packet(const uint8_t* data, + uint32_t len) { + // check if white listed already + if (white_listed_) { + return true; + } + + UCHAR* stun = const_cast<uint8_t*>(data); + uint32_t length = len; + if (!nr_is_stun_message(stun, length)) { + stun += 2; + length -= 2; + if (!nr_is_stun_message(stun, length)) { + return false; + } + } + + const nr_stun_message_header* msg = + reinterpret_cast<const nr_stun_message_header*>(stun); + + // Check if it is a stun request. If yes, we put it into a pending list and + // wait for response packet. + if (nr_is_stun_request_message(stun, length)) { + pending_request_ids_.insert(PendingSTUNId(msg->id)); + return true; + } + + // If it is a stun response packet, and we had received the request before, we + // can allow it packet to pass filter. + if (nr_is_stun_response_message(stun, length)) { + std::set<PendingSTUNId>::iterator it = + response_allowed_ids_.find(PendingSTUNId(msg->id)); + if (it != response_allowed_ids_.end()) { + response_allowed_ids_.erase(it); + white_listed_ = true; + return true; + } + } + + return false; +} + +} // anonymous namespace + +NS_IMPL_ISUPPORTS(nsStunUDPSocketFilterHandler, nsISocketFilterHandler) + +NS_IMETHODIMP nsStunUDPSocketFilterHandler::NewFilter( + nsISocketFilter** result) { + nsISocketFilter* ret = new STUNUDPSocketFilter(); + NS_ADDREF(*result = ret); + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsStunTCPSocketFilterHandler, nsISocketFilterHandler) + +NS_IMETHODIMP nsStunTCPSocketFilterHandler::NewFilter( + nsISocketFilter** result) { + nsISocketFilter* ret = new STUNTCPSocketFilter(); + NS_ADDREF(*result = ret); + return NS_OK; +} diff --git a/dom/media/webrtc/transport/stun_socket_filter.h b/dom/media/webrtc/transport/stun_socket_filter.h new file mode 100644 index 0000000000..393257577d --- /dev/null +++ b/dom/media/webrtc/transport/stun_socket_filter.h @@ -0,0 +1,41 @@ +/* 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/. */ +#ifndef stun_socket_filter_h__ +#define stun_socket_filter_h__ + +#include "nsISocketFilter.h" + +#define NS_STUN_UDP_SOCKET_FILTER_HANDLER_CID \ + {0x3e43ee93, \ + 0x829e, \ + 0x4ea6, \ + {0xa3, 0x4e, 0x62, 0xd9, 0xe4, 0xc9, 0xf9, 0x93}}; + +class nsStunUDPSocketFilterHandler : public nsISocketFilterHandler { + public: + // Threadsafe because we create off-main-thread, but destroy on MainThread + // via FreeFactoryEntries() + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISOCKETFILTERHANDLER + private: + virtual ~nsStunUDPSocketFilterHandler() = default; +}; + +#define NS_STUN_TCP_SOCKET_FILTER_HANDLER_CID \ + {0x9fea635a, \ + 0x2fc2, \ + 0x4d08, \ + {0x97, 0x21, 0xd2, 0x38, 0xd3, 0xf5, 0x2f, 0x92}}; + +class nsStunTCPSocketFilterHandler : public nsISocketFilterHandler { + public: + // Threadsafe because we create off-main-thread, but destroy on MainThread + // via FreeFactoryEntries() + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISOCKETFILTERHANDLER + private: + virtual ~nsStunTCPSocketFilterHandler() = default; +}; + +#endif // stun_socket_filter_h__ diff --git a/dom/media/webrtc/transport/test/TestSyncRunnable.cpp b/dom/media/webrtc/transport/test/TestSyncRunnable.cpp new file mode 100644 index 0000000000..ca671b4e79 --- /dev/null +++ b/dom/media/webrtc/transport/test/TestSyncRunnable.cpp @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 12; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "nsIThread.h" +#include "nsThreadUtils.h" +#include "mozilla/SyncRunnable.h" + +#include "gtest/gtest.h" + +using namespace mozilla; + +nsIThread* gThread = nullptr; + +class TestRunnable : public Runnable { + public: + TestRunnable() : Runnable("TestRunnable"), ran_(false) {} + + NS_IMETHOD Run() override { + ran_ = true; + + return NS_OK; + } + + bool ran() const { return ran_; } + + private: + bool ran_; +}; + +class TestSyncRunnable : public ::testing::Test { + public: + static void SetUpTestCase() { + nsresult rv = NS_NewNamedThread("thread", &gThread); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + } + + static void TearDownTestCase() { + if (gThread) gThread->Shutdown(); + } +}; + +TEST_F(TestSyncRunnable, TestDispatch) { + RefPtr<TestRunnable> r(new TestRunnable()); + RefPtr<SyncRunnable> s(new SyncRunnable(r)); + s->DispatchToThread(gThread); + + ASSERT_TRUE(r->ran()); +} + +TEST_F(TestSyncRunnable, TestDispatchStatic) { + RefPtr<TestRunnable> r(new TestRunnable()); + SyncRunnable::DispatchToThread(gThread, r); + ASSERT_TRUE(r->ran()); +} diff --git a/dom/media/webrtc/transport/test/buffered_stun_socket_unittest.cpp b/dom/media/webrtc/transport/test/buffered_stun_socket_unittest.cpp new file mode 100644 index 0000000000..e6a9cd38a2 --- /dev/null +++ b/dom/media/webrtc/transport/test/buffered_stun_socket_unittest.cpp @@ -0,0 +1,245 @@ +/* -*- 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 + +extern "C" { +#include "nr_api.h" +#include "nr_socket.h" +#include "nr_socket_buffered_stun.h" +#include "transport_addr.h" +} + +#include "stun_msg.h" + +#include "dummysocket.h" + +#include "nr_socket_prsock.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; + +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 BufferedStunSocketTest : public MtransportTest { + public: + BufferedStunSocketTest() + : MtransportTest(), dummy_(nullptr), test_socket_(nullptr) {} + + ~BufferedStunSocketTest() { nr_socket_destroy(&test_socket_); } + + void SetUp() override { + MtransportTest::SetUp(); + + RefPtr<DummySocket> dummy(new DummySocket()); + + int r = + nr_socket_buffered_stun_create(dummy->get_nr_socket(), kStunMessageLen, + TURN_TCP_FRAMING, &test_socket_); + ASSERT_EQ(0, r); + dummy_ = std::move(dummy); // Now owned by test_socket_. + + r = nr_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: + RefPtr<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, TestSendFullThenDrain) { + dummy_->SetWritable(0); + + for (;;) { + int r = nr_socket_sendto(test_socket_, kStunMessage, kStunMessageLen, 0, + &remote_addr_); + if (r == R_WOULDBLOCK) break; + + ASSERT_EQ(0, r); + } + + // Nothing was written. + dummy_->CheckWriteBuffer(nullptr, 0); + + // Now flush. + dummy_->SetWritable(kStunMessageLen); + dummy_->FireWritableCb(); + dummy_->ClearWriteBuffer(); + + // Verify we can write something. + int r = nr_socket_sendto(test_socket_, kStunMessage, kStunMessageLen, 0, + &remote_addr_); + ASSERT_EQ(0, r); + + // And that it appears. + 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_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); +} diff --git a/dom/media/webrtc/transport/test/dummysocket.h b/dom/media/webrtc/transport/test/dummysocket.h new file mode 100644 index 0000000000..6e20a1f7e7 --- /dev/null +++ b/dom/media/webrtc/transport/test/dummysocket.h @@ -0,0 +1,217 @@ +/* -*- 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 authors: ekr@rtfm.com; ryan@tokbox.com + +#ifndef MTRANSPORT_DUMMY_SOCKET_H_ +#define MTRANSPORT_DUMMY_SOCKET_H_ + +#include "nr_socket_prsock.h" + +extern "C" { +#include "transport_addr.h" +} + +#include "mediapacket.h" +#include "mozilla/UniquePtr.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +namespace mozilla { + +static UniquePtr<MediaPacket> merge(UniquePtr<MediaPacket> a, + UniquePtr<MediaPacket> b) { + if (a && a->len() && b && b->len()) { + UniquePtr<uint8_t[]> data(new uint8_t[a->len() + b->len()]); + memcpy(data.get(), a->data(), a->len()); + memcpy(data.get() + a->len(), b->data(), b->len()); + + UniquePtr<MediaPacket> merged(new MediaPacket); + merged->Take(std::move(data), a->len() + b->len()); + return merged; + } + + if (a && a->len()) { + return a; + } + + if (b && b->len()) { + return b; + } + + return nullptr; +} + +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) override { return 0; } + + virtual int sendto(const void* msg, size_t len, int flags, + const nr_transport_addr* to) override { + MOZ_CRASH(); + return 0; + } + + virtual int recvfrom(void* buf, size_t maxlen, size_t* len, int flags, + nr_transport_addr* from) override { + MOZ_CRASH(); + return 0; + } + + virtual int getaddr(nr_transport_addr* addrp) override { + MOZ_CRASH(); + return 0; + } + + virtual void close() override {} + + virtual int connect(const nr_transport_addr* addr) override { + nr_transport_addr_copy(&connect_addr_, addr); + return 0; + } + + virtual int listen(int backlog) override { return 0; } + + virtual int accept(nr_transport_addr* addrp, nr_socket** sockp) override { + return 0; + } + + virtual int write(const void* msg, size_t len, size_t* written) override { + size_t to_write = std::min(len, writable_); + + if (to_write) { + UniquePtr<MediaPacket> msgbuf(new MediaPacket); + msgbuf->Copy(static_cast<const uint8_t*>(msg), to_write); + write_buffer_ = merge(std::move(write_buffer_), std::move(msgbuf)); + } + + *written = to_write; + + return 0; + } + + virtual int read(void* buf, size_t maxlen, size_t* len) override { + 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()) { + MediaPacket* newPacket = new MediaPacket; + newPacket->Copy(read_buffer_->data() + to_read, + read_buffer_->len() - to_read); + read_buffer_.reset(newPacket); + } else { + read_buffer_.reset(); + } + + 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) override { + EXPECT_EQ(nullptr, cb_); + cb_ = cb; + cb_arg_ = cb_arg; + + return 0; + } + + virtual int cancel(int how) override { + cb_ = nullptr; + cb_arg_ = nullptr; + + return 0; + } + + // Read/Manipulate the current state. + void CheckWriteBuffer(const 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_.reset(); } + + 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(const uint8_t* data, size_t len) { + EXPECT_EQ(nullptr, write_buffer_.get()); + read_buffer_.reset(new MediaPacket); + read_buffer_->Copy(data, len); + } + + void ClearReadBuffer() { read_buffer_.reset(); } + + 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_; + } + + nr_transport_addr* get_connect_addr() { return &connect_addr_; } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket, override); + + private: + ~DummySocket() = default; + + DISALLOW_COPY_ASSIGN(DummySocket); + + size_t writable_; // Amount we allow someone to write. + UniquePtr<MediaPacket> write_buffer_; + size_t readable_; // Amount we allow someone to read. + UniquePtr<MediaPacket> read_buffer_; + + NR_async_cb cb_; + void* cb_arg_; + nr_socket* self_; + + nr_transport_addr connect_addr_; +}; + +} // namespace mozilla + +#endif diff --git a/dom/media/webrtc/transport/test/gtest_ringbuffer_dumper.h b/dom/media/webrtc/transport/test/gtest_ringbuffer_dumper.h new file mode 100644 index 0000000000..25e85c2155 --- /dev/null +++ b/dom/media/webrtc/transport/test/gtest_ringbuffer_dumper.h @@ -0,0 +1,78 @@ +/* -*- 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: bcampen@mozilla.com + +#ifndef gtest_ringbuffer_dumper_h__ +#define gtest_ringbuffer_dumper_h__ + +#include "mozilla/SyncRunnable.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" + +#include "mtransport_test_utils.h" +#include "runnable_utils.h" +#include "rlogconnector.h" + +using mozilla::RLogConnector; +using mozilla::WrapRunnable; + +namespace test { +class RingbufferDumper : public ::testing::EmptyTestEventListener { + public: + explicit RingbufferDumper(MtransportTestUtils* test_utils) + : test_utils_(test_utils) {} + + void ClearRingBuffer_s() { + RLogConnector::CreateInstance(); + // Set limit to zero to clear the ringbuffer + RLogConnector::GetInstance()->SetLogLimit(0); + RLogConnector::GetInstance()->SetLogLimit(UINT32_MAX); + } + + void DestroyRingBuffer_s() { RLogConnector::DestroyInstance(); } + + void DumpRingBuffer_s() { + std::deque<std::string> logs; + // Get an unlimited number of log lines, with no filter + RLogConnector::GetInstance()->GetAny(0, &logs); + for (auto l = logs.begin(); l != logs.end(); ++l) { + std::cout << *l << std::endl; + } + ClearRingBuffer_s(); + } + + virtual void OnTestStart(const ::testing::TestInfo& testInfo) override { + mozilla::SyncRunnable::DispatchToThread( + test_utils_->sts_target(), + WrapRunnable(this, &RingbufferDumper::ClearRingBuffer_s)); + } + + virtual void OnTestEnd(const ::testing::TestInfo& testInfo) override { + mozilla::SyncRunnable::DispatchToThread( + test_utils_->sts_target(), + WrapRunnable(this, &RingbufferDumper::DestroyRingBuffer_s)); + } + + // Called after a failed assertion or a SUCCEED() invocation. + virtual void OnTestPartResult( + const ::testing::TestPartResult& testResult) override { + if (testResult.failed()) { + // Dump (and empty) the RLogConnector + mozilla::SyncRunnable::DispatchToThread( + test_utils_->sts_target(), + WrapRunnable(this, &RingbufferDumper::DumpRingBuffer_s)); + } + } + + private: + MtransportTestUtils* test_utils_; +}; + +} // namespace test + +#endif // gtest_ringbuffer_dumper_h__ diff --git a/dom/media/webrtc/transport/test/gtest_utils.h b/dom/media/webrtc/transport/test/gtest_utils.h new file mode 100644 index 0000000000..40c2570ea1 --- /dev/null +++ b/dom/media/webrtc/transport/test/gtest_utils.h @@ -0,0 +1,201 @@ +/* -*- 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/. */ + +// Utilities to wrap gtest, based on libjingle's gunit + +// Some sections of this code are under the following license: + +/* + * libjingle + * Copyright 2004--2008, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +// Original author: ekr@rtfm.com +#ifndef gtest_utils__h__ +#define gtest_utils__h__ + +#include <iostream> + +#include "nspr.h" +#include "prinrval.h" +#include "prthread.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" + +#include "gtest_ringbuffer_dumper.h" +#include "mtransport_test_utils.h" +#include "nss.h" +#include "ssl.h" + +extern "C" { +#include "registry.h" +#include "transport_addr.h" +} + +// Wait up to timeout seconds for expression to be true +#define WAIT(expression, timeout) \ + do { \ + for (PRIntervalTime start = PR_IntervalNow(); \ + !(expression) && !((PR_IntervalNow() - start) > \ + PR_MillisecondsToInterval(timeout));) { \ + PR_Sleep(10); \ + } \ + } while (0) + +// Same as GTEST_WAIT, but stores the result in res. Used when +// you also want the result of expression but wish to avoid +// double evaluation. +#define WAIT_(expression, timeout, res) \ + do { \ + for (PRIntervalTime start = PR_IntervalNow(); \ + !(res = (expression)) && !((PR_IntervalNow() - start) > \ + PR_MillisecondsToInterval(timeout));) { \ + PR_Sleep(10); \ + } \ + } while (0) + +#define ASSERT_TRUE_WAIT(expression, timeout) \ + do { \ + bool res; \ + WAIT_(expression, timeout, res); \ + ASSERT_TRUE(res); \ + } while (0) + +#define EXPECT_TRUE_WAIT(expression, timeout) \ + do { \ + bool res; \ + WAIT_(expression, timeout, res); \ + EXPECT_TRUE(res); \ + } while (0) + +#define ASSERT_EQ_WAIT(expected, actual, timeout) \ + do { \ + WAIT(expected == actual, timeout); \ + ASSERT_EQ(expected, actual); \ + } while (0) + +using test::RingbufferDumper; + +class MtransportTest : public ::testing::Test { + public: + MtransportTest() : test_utils_(nullptr), dumper_(nullptr) {} + + void SetUp() override { + test_utils_ = new MtransportTestUtils(); + NSS_NoDB_Init(nullptr); + NSS_SetDomesticPolicy(); + + NR_reg_init(NR_REG_MODE_LOCAL); + + // Attempt to load env vars used by tests. + GetEnvironment("TURN_SERVER_ADDRESS", turn_server_); + GetEnvironment("TURN_SERVER_USER", turn_user_); + GetEnvironment("TURN_SERVER_PASSWORD", turn_password_); + GetEnvironment("STUN_SERVER_ADDRESS", stun_server_address_); + GetEnvironment("STUN_SERVER_HOSTNAME", stun_server_hostname_); + + std::string disable_non_local; + GetEnvironment("MOZ_DISABLE_NONLOCAL_CONNECTIONS", disable_non_local); + std::string upload_dir; + GetEnvironment("MOZ_UPLOAD_DIR", upload_dir); + + if ((!disable_non_local.empty() && disable_non_local != "0") || + !upload_dir.empty()) { + // We're assuming that MOZ_UPLOAD_DIR is only set on tbpl; + // MOZ_DISABLE_NONLOCAL_CONNECTIONS probably should be set when running + // the cpp unit-tests, but is not presently. + stun_server_address_ = ""; + stun_server_hostname_ = ""; + turn_server_ = ""; + } + + // Some tests are flaky and need to check if they're supposed to run. + webrtc_enabled_ = CheckEnvironmentFlag("MOZ_WEBRTC_TESTS"); + + ::testing::TestEventListeners& listeners = + ::testing::UnitTest::GetInstance()->listeners(); + + dumper_ = new RingbufferDumper(test_utils_); + listeners.Append(dumper_); + } + + void TearDown() override { + ::testing::UnitTest::GetInstance()->listeners().Release(dumper_); + delete dumper_; + delete test_utils_; + } + + void GetEnvironment(const char* aVar, std::string& out) { + char* value = getenv(aVar); + if (value) { + out = value; + } + } + + bool CheckEnvironmentFlag(const char* aVar) { + std::string value; + GetEnvironment(aVar, value); + return value == "1"; + } + + bool WarnIfTurnNotConfigured() const { + bool configured = + !turn_server_.empty() && !turn_user_.empty() && !turn_password_.empty(); + + if (configured) { + nr_transport_addr addr; + if (nr_str_port_to_transport_addr(turn_server_.c_str(), 3478, IPPROTO_UDP, + &addr)) { + printf( + "Invalid TURN_SERVER_ADDRESS \"%s\". Only IP numbers supported.\n", + turn_server_.c_str()); + configured = false; + } + } else { + printf( + "Set TURN_SERVER_ADDRESS, TURN_SERVER_USER, and " + "TURN_SERVER_PASSWORD\n" + "environment variables to run this test\n"); + } + + return !configured; + } + + MtransportTestUtils* test_utils_; + RingbufferDumper* dumper_; + + std::string turn_server_; + std::string turn_user_; + std::string turn_password_; + std::string stun_server_address_; + std::string stun_server_hostname_; + + bool webrtc_enabled_; +}; +#endif diff --git a/dom/media/webrtc/transport/test/ice_unittest.cpp b/dom/media/webrtc/transport/test/ice_unittest.cpp new file mode 100644 index 0000000000..adc3949dd8 --- /dev/null +++ b/dom/media/webrtc/transport/test/ice_unittest.cpp @@ -0,0 +1,4440 @@ +/* -*- 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 <algorithm> +#include <deque> +#include <iostream> +#include <limits> +#include <map> +#include <string> +#include <vector> + +#include "sigslot.h" + +#include "logging.h" +#include "ssl.h" + +#include "mozilla/Preferences.h" +#include "nsThreadUtils.h" +#include "nsXPCOM.h" + +extern "C" { +#include "r_types.h" +#include "async_wait.h" +#include "async_timer.h" +#include "r_data.h" +#include "util.h" +#include "r_time.h" +} + +#include "ice_ctx.h" +#include "ice_peer_ctx.h" +#include "ice_media_stream.h" + +#include "nricectx.h" +#include "nricemediastream.h" +#include "nriceresolverfake.h" +#include "nriceresolver.h" +#include "nrinterfaceprioritizer.h" +#include "gtest_ringbuffer_dumper.h" +#include "rlogconnector.h" +#include "runnable_utils.h" +#include "stunserver.h" +#include "nr_socket_prsock.h" +#include "test_nr_socket.h" +#include "nsISocketFilter.h" +#include "mozilla/net/DNS.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; + +static unsigned int kDefaultTimeout = 7000; + +// TODO(nils@mozilla.com): This should get replaced with some non-external +// solution like discussed in bug 860775. +const std::string kDefaultStunServerHostname((char*)"stun.l.google.com"); +const std::string kBogusStunServerHostname( + (char*)"stun-server-nonexistent.invalid"); +const uint16_t kDefaultStunServerPort = 19305; +const std::string kBogusIceCandidate( + (char*)"candidate:0 2 UDP 2113601790 192.168.178.20 50769 typ"); + +const std::string kUnreachableHostIceCandidate( + (char*)"candidate:0 1 UDP 2113601790 192.168.178.20 50769 typ host"); + +namespace { + +// DNS resolution helper code +static std::string Resolve(const std::string& fqdn, int address_family) { + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = address_family; + hints.ai_protocol = IPPROTO_UDP; + struct addrinfo* res; + int err = getaddrinfo(fqdn.c_str(), nullptr, &hints, &res); + if (err) { + std::cerr << "Error in getaddrinfo: " << err << std::endl; + return ""; + } + + char str_addr[64] = {0}; + switch (res->ai_family) { + case AF_INET: + inet_ntop(AF_INET, + &reinterpret_cast<struct sockaddr_in*>(res->ai_addr)->sin_addr, + str_addr, sizeof(str_addr)); + break; + case AF_INET6: + inet_ntop( + AF_INET6, + &reinterpret_cast<struct sockaddr_in6*>(res->ai_addr)->sin6_addr, + str_addr, sizeof(str_addr)); + break; + default: + std::cerr << "Got unexpected address family in DNS lookup: " + << res->ai_family << std::endl; + freeaddrinfo(res); + return ""; + } + + if (!strlen(str_addr)) { + std::cerr << "inet_ntop failed" << std::endl; + } + + freeaddrinfo(res); + return str_addr; +} + +class StunTest : public MtransportTest { + public: + StunTest() : MtransportTest() {} + + void SetUp() override { + MtransportTest::SetUp(); + + stun_server_hostname_ = kDefaultStunServerHostname; + // If only a STUN server FQDN was provided, look up its IP address for the + // address-only tests. + if (stun_server_address_.empty() && !stun_server_hostname_.empty()) { + stun_server_address_ = Resolve(stun_server_hostname_, AF_INET); + ASSERT_TRUE(!stun_server_address_.empty()); + } + + // Make sure NrIceCtx is in a testable state. + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&NrIceCtx::internal_DeinitializeGlobal), + NS_DISPATCH_SYNC); + + // NB: NrIceCtx::internal_DeinitializeGlobal destroys the RLogConnector + // singleton. + RLogConnector::CreateInstance(); + + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&TestStunServer::GetInstance, AF_INET), + NS_DISPATCH_SYNC); + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&TestStunServer::GetInstance, AF_INET6), + NS_DISPATCH_SYNC); + + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&TestStunTcpServer::GetInstance, AF_INET), + NS_DISPATCH_SYNC); + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&TestStunTcpServer::GetInstance, AF_INET6), + NS_DISPATCH_SYNC); + } + + void TearDown() override { + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&NrIceCtx::internal_DeinitializeGlobal), + NS_DISPATCH_SYNC); + + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&TestStunServer::ShutdownInstance), NS_DISPATCH_SYNC); + + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&TestStunTcpServer::ShutdownInstance), NS_DISPATCH_SYNC); + + RLogConnector::DestroyInstance(); + + MtransportTest::TearDown(); + } +}; + +enum TrickleMode { TRICKLE_NONE, TRICKLE_SIMULATE, TRICKLE_REAL }; + +enum ConsentStatus { CONSENT_FRESH, CONSENT_STALE, CONSENT_EXPIRED }; + +typedef std::string (*CandidateFilter)(const std::string& candidate); + +std::vector<std::string> split(const std::string& s, char delim) { + std::vector<std::string> elems; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +static std::string IsSrflxCandidate(const std::string& candidate) { + std::vector<std::string> tokens = split(candidate, ' '); + if ((tokens.at(6) == "typ") && (tokens.at(7) == "srflx")) { + return candidate; + } + return std::string(); +} + +static std::string IsRelayCandidate(const std::string& candidate) { + if (candidate.find("typ relay") != std::string::npos) { + return candidate; + } + return std::string(); +} + +static std::string IsTcpCandidate(const std::string& candidate) { + if (candidate.find("TCP") != std::string::npos) { + return candidate; + } + return std::string(); +} + +static std::string IsTcpSoCandidate(const std::string& candidate) { + if (candidate.find("tcptype so") != std::string::npos) { + return candidate; + } + return std::string(); +} + +static std::string IsLoopbackCandidate(const std::string& candidate) { + if (candidate.find("127.0.0.") != std::string::npos) { + return candidate; + } + return std::string(); +} + +static std::string IsIpv4Candidate(const std::string& candidate) { + std::vector<std::string> tokens = split(candidate, ' '); + if (tokens.at(4).find(':') == std::string::npos) { + return candidate; + } + return std::string(); +} + +static std::string SabotageHostCandidateAndDropReflexive( + const std::string& candidate) { + if (candidate.find("typ srflx") != std::string::npos) { + return std::string(); + } + + if (candidate.find("typ host") != std::string::npos) { + return kUnreachableHostIceCandidate; + } + + return candidate; +} + +bool ContainsSucceededPair(const std::vector<NrIceCandidatePair>& pairs) { + for (const auto& pair : pairs) { + if (pair.state == NrIceCandidatePair::STATE_SUCCEEDED) { + return true; + } + } + 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.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) { + if (lhs.type == rhs.type) { + return lhs.tcp_type < rhs.tcp_type; + } + return lhs.type < rhs.type; + } + return lhs.cand_addr.transport < rhs.cand_addr.transport; + } + return lhs.cand_addr.port < rhs.cand_addr.port; + } + return lhs.cand_addr.host < rhs.cand_addr.host; +} + +bool operator==(const NrIceCandidate& lhs, const NrIceCandidate& rhs) { + 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) { + if (lhs.remote == rhs.remote) { + return lhs.codeword < rhs.codeword; + } + return lhs.remote < rhs.remote; + } + return lhs.local < rhs.local; + } + return lhs.priority < rhs.priority; + } +}; + +class IceTestPeer; + +class SchedulableTrickleCandidate { + public: + SchedulableTrickleCandidate(IceTestPeer* peer, size_t stream, + const std::string& candidate, + const std::string& ufrag, + MtransportTestUtils* utils) + : peer_(peer), + stream_(stream), + candidate_(candidate), + ufrag_(ufrag), + timer_handle_(nullptr), + test_utils_(utils) {} + + ~SchedulableTrickleCandidate() { + if (timer_handle_) NR_async_timer_cancel(timer_handle_); + } + + void Schedule(unsigned int ms) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &SchedulableTrickleCandidate::Schedule_s, ms), + NS_DISPATCH_SYNC); + } + + void Schedule_s(unsigned int ms) { + MOZ_ASSERT(!timer_handle_); + NR_ASYNC_TIMER_SET(ms, Trickle_cb, this, &timer_handle_); + } + + static void Trickle_cb(NR_SOCKET s, int how, void* cb_arg) { + static_cast<SchedulableTrickleCandidate*>(cb_arg)->Trickle(); + } + + void Trickle(); + + std::string& Candidate() { return candidate_; } + + const std::string& Candidate() const { return candidate_; } + + bool IsHost() const { + return candidate_.find("typ host") != std::string::npos; + } + + bool IsReflexive() const { + return candidate_.find("typ srflx") != std::string::npos; + } + + bool IsRelay() const { + return candidate_.find("typ relay") != std::string::npos; + } + + private: + IceTestPeer* peer_; + size_t stream_; + std::string candidate_; + std::string ufrag_; + void* timer_handle_; + MtransportTestUtils* test_utils_; + + DISALLOW_COPY_ASSIGN(SchedulableTrickleCandidate); +}; + +class IceTestPeer : public sigslot::has_slots<> { + public: + IceTestPeer(const std::string& name, MtransportTestUtils* utils, bool offerer, + const NrIceCtx::Config& config) + : name_(name), + ice_ctx_(NrIceCtx::Create(name)), + offerer_(offerer), + candidates_(), + stream_counter_(0), + shutting_down_(false), + gathering_complete_(false), + ready_ct_(0), + ice_connected_(false), + ice_failed_(false), + ice_reached_checking_(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), + simulate_ice_lite_(false), + nat_(new TestNat), + test_utils_(utils) { + ice_ctx_->SignalGatheringStateChange.connect( + this, &IceTestPeer::GatheringStateChange); + ice_ctx_->SignalConnectionStateChange.connect( + this, &IceTestPeer::ConnectionStateChange); + + ice_ctx_->SetIceConfig(config); + + consent_timestamp_.tv_sec = 0; + consent_timestamp_.tv_usec = 0; + int r = ice_ctx_->SetNat(nat_); + (void)r; + MOZ_ASSERT(!r); + } + + ~IceTestPeer() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::Shutdown), NS_DISPATCH_SYNC); + + // Give the ICE destruction callback time to fire before + // we destroy the resolver. + PR_Sleep(1000); + } + + std::string MakeTransportId(size_t index) const { + char id[100]; + snprintf(id, sizeof(id), "%s:stream%d", name_.c_str(), (int)index); + return id; + } + + void SetIceCredentials_s(NrIceMediaStream& stream) { + static size_t counter = 0; + std::ostringstream prefix; + prefix << name_ << "-" << counter++; + std::string ufrag = prefix.str() + "-ufrag"; + std::string pwd = prefix.str() + "-pwd"; + if (mIceCredentials.count(stream.GetId())) { + mOldIceCredentials[stream.GetId()] = mIceCredentials[stream.GetId()]; + } + mIceCredentials[stream.GetId()] = std::make_pair(ufrag, pwd); + stream.SetIceCredentials(ufrag, pwd); + } + + void AddStream_s(int components) { + std::string id = MakeTransportId(stream_counter_++); + + RefPtr<NrIceMediaStream> stream = + ice_ctx_->CreateStream(id, id, components); + + ASSERT_TRUE(stream); + SetIceCredentials_s(*stream); + + stream->SignalCandidate.connect(this, &IceTestPeer::CandidateInitialized); + stream->SignalReady.connect(this, &IceTestPeer::StreamReady); + stream->SignalFailed.connect(this, &IceTestPeer::StreamFailed); + stream->SignalPacketReceived.connect(this, &IceTestPeer::PacketReceived); + } + + void AddStream(int components) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::AddStream_s, components), + NS_DISPATCH_SYNC); + } + + void RemoveStream_s(size_t index) { + ice_ctx_->DestroyStream(MakeTransportId(index)); + } + + void RemoveStream(size_t index) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::RemoveStream_s, index), + NS_DISPATCH_SYNC); + } + + RefPtr<NrIceMediaStream> GetStream_s(size_t index) { + std::string id = MakeTransportId(index); + return ice_ctx_->GetStream(id); + } + + void SetStunServer(const std::string addr, uint16_t port, + const char* transport = kNrIceTransportUdp) { + if (addr.empty()) { + // Happens when MOZ_DISABLE_NONLOCAL_CONNECTIONS is set + return; + } + + std::vector<NrIceStunServer> stun_servers; + UniquePtr<NrIceStunServer> server( + NrIceStunServer::Create(addr, port, transport)); + stun_servers.push_back(*server); + SetStunServers(stun_servers); + } + + void SetStunServers(const std::vector<NrIceStunServer>& servers) { + ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(servers))); + } + + void UseTestStunServer() { + SetStunServer(TestStunServer::GetInstance(AF_INET)->addr(), + TestStunServer::GetInstance(AF_INET)->port()); + } + + void SetTurnServer(const std::string addr, uint16_t port, + const std::string username, const std::string password, + const char* transport) { + std::vector<unsigned char> password_vec(password.begin(), password.end()); + 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 char* transport) { + std::vector<NrIceTurnServer> turn_servers; + UniquePtr<NrIceTurnServer> server( + NrIceTurnServer::Create(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(const std::string& ip, const std::string& fqdn) { + ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init())); + if (!ip.empty() && !fqdn.empty()) { + PRNetAddr addr; + PRStatus status = PR_StringToNetAddr(ip.c_str(), &addr); + addr.inet.port = kDefaultStunServerPort; + ASSERT_EQ(PR_SUCCESS, status); + fake_resolver_.SetAddr(fqdn, addr); + } + ASSERT_TRUE( + NS_SUCCEEDED(ice_ctx_->SetResolver(fake_resolver_.AllocateResolver()))); + } + + void SetDNSResolver() { + ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init())); + ASSERT_TRUE( + NS_SUCCEEDED(ice_ctx_->SetResolver(dns_resolver_->AllocateResolver()))); + } + + void Gather(bool default_route_only = false, + bool obfuscate_host_addresses = false) { + nsresult res; + + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&res, ice_ctx_, &NrIceCtx::StartGathering, + default_route_only, obfuscate_host_addresses), + NS_DISPATCH_SYNC); + + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + + void SetCtxFlags(bool default_route_only) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(ice_ctx_, &NrIceCtx::SetCtxFlags, default_route_only), + NS_DISPATCH_SYNC); + } + + nsTArray<NrIceStunAddr> GetStunAddrs() { return ice_ctx_->GetStunAddrs(); } + + void SetStunAddrs(const nsTArray<NrIceStunAddr>& addrs) { + ice_ctx_->SetStunAddrs(addrs); + } + + void UseNat() { nat_->enabled_ = true; } + + void SetTimerDivider(int div) { ice_ctx_->internal_SetTimerAccelarator(div); } + + void SetStunResponseDelay(uint32_t delay) { + nat_->delay_stun_resp_ms_ = delay; + } + + void SetFilteringType(TestNat::NatBehavior type) { + MOZ_ASSERT(!nat_->has_port_mappings()); + nat_->filtering_type_ = type; + } + + void SetMappingType(TestNat::NatBehavior type) { + MOZ_ASSERT(!nat_->has_port_mappings()); + nat_->mapping_type_ = type; + } + + void SetBlockUdp(bool block) { + MOZ_ASSERT(!nat_->has_port_mappings()); + nat_->block_udp_ = block; + } + + void SetBlockStun(bool block) { nat_->block_stun_ = block; } + + // Get various pieces of state + std::vector<std::string> GetGlobalAttributes() { + std::vector<std::string> attrs(ice_ctx_->GetGlobalAttributes()); + if (simulate_ice_lite_) { + attrs.push_back("ice-lite"); + } + return attrs; + } + + std::vector<std::string> GetAttributes(size_t stream) { + std::vector<std::string> v; + + RUN_ON_THREAD( + test_utils_->sts_target(), + WrapRunnableRet(&v, this, &IceTestPeer::GetAttributes_s, stream)); + + return v; + } + + std::string FilterCandidate(const std::string& candidate) { + if (candidate_filter_) { + return candidate_filter_(candidate); + } + return candidate; + } + + std::vector<std::string> GetAttributes_s(size_t index) { + std::vector<std::string> attributes; + + auto stream = GetStream_s(index); + if (!stream) { + EXPECT_TRUE(false) << "No such stream " << index; + return attributes; + } + + std::vector<std::string> attributes_in = stream->GetAttributes(); + + for (const auto& attribute : attributes_in) { + if (attribute.find("candidate:") != std::string::npos) { + std::string candidate(FilterCandidate(attribute)); + if (!candidate.empty()) { + std::cerr << name_ << " Returning candidate: " << candidate + << std::endl; + attributes.push_back(candidate); + } + } else { + attributes.push_back(attribute); + } + } + + return attributes; + } + + void SetExpectedTypes(NrIceCandidate::Type local, NrIceCandidate::Type remote, + std::string local_transport = kNrIceTransportUdp) { + expected_local_type_ = local; + expected_local_transport_ = local_transport; + expected_remote_type_ = remote; + } + + void SetExpectedRemoteCandidateAddr(const std::string& addr) { + expected_remote_addr_ = addr; + } + + int GetCandidatesPrivateIpv4Range(size_t stream) { + std::vector<std::string> attributes = GetAttributes(stream); + + int host_net = 0; + for (const auto& a : attributes) { + if (a.find("typ host") != std::string::npos) { + nr_transport_addr addr; + std::vector<std::string> tokens = split(a, ' '); + int r = nr_str_port_to_transport_addr(tokens.at(4).c_str(), 0, + IPPROTO_UDP, &addr); + MOZ_ASSERT(!r); + if (!r && (addr.ip_version == NR_IPV4)) { + int n = nr_transport_addr_get_private_addr_range(&addr); + if (n) { + if (host_net) { + // TODO: add support for multiple private interfaces + std::cerr + << "This test doesn't support multiple private interfaces"; + return -1; + } + host_net = n; + } + } + } + } + return host_net; + } + + bool gathering_complete() { return gathering_complete_; } + int ready_ct() { return ready_ct_; } + bool is_ready_s(size_t index) { + auto media_stream = GetStream_s(index); + if (!media_stream) { + EXPECT_TRUE(false) << "No such stream " << index; + return false; + } + return media_stream->state() == NrIceMediaStream::ICE_OPEN; + } + bool is_ready(size_t stream) { + bool result; + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&result, this, &IceTestPeer::is_ready_s, stream), + NS_DISPATCH_SYNC); + return result; + } + bool ice_connected() { return ice_connected_; } + bool ice_failed() { return ice_failed_; } + bool ice_reached_checking() { return ice_reached_checking_; } + size_t received() { return received_; } + size_t sent() { return sent_; } + + void RestartIce() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::RestartIce_s), NS_DISPATCH_SYNC); + } + + void RestartIce_s() { + for (auto& stream : ice_ctx_->GetStreams()) { + SetIceCredentials_s(*stream); + } + // take care of some local bookkeeping + ready_ct_ = 0; + gathering_complete_ = false; + ice_connected_ = false; + ice_failed_ = false; + ice_reached_checking_ = false; + remote_ = nullptr; + } + + void RollbackIceRestart() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::RollbackIceRestart_s), + NS_DISPATCH_SYNC); + } + + void RollbackIceRestart_s() { + for (auto& stream : ice_ctx_->GetStreams()) { + mIceCredentials[stream->GetId()] = mOldIceCredentials[stream->GetId()]; + } + } + + // Start connecting to another peer + void Connect_s(IceTestPeer* remote, TrickleMode trickle_mode, + bool start = true) { + nsresult res; + + remote_ = remote; + + trickle_mode_ = trickle_mode; + ice_connected_ = false; + ice_failed_ = false; + ice_reached_checking_ = false; + res = ice_ctx_->ParseGlobalAttributes(remote->GetGlobalAttributes()); + ASSERT_FALSE(remote->simulate_ice_lite_ && + (ice_ctx_->GetControlling() == NrIceCtx::ICE_CONTROLLED)); + ASSERT_TRUE(NS_SUCCEEDED(res)); + + for (size_t i = 0; i < stream_counter_; ++i) { + auto aStream = GetStream_s(i); + if (aStream) { + std::vector<std::string> attributes = remote->GetAttributes(i); + + for (auto it = attributes.begin(); it != attributes.end();) { + if (trickle_mode == TRICKLE_SIMULATE && + it->find("candidate:") != std::string::npos) { + std::cerr << name_ << " Deferring remote candidate: " << *it + << std::endl; + attributes.erase(it); + } else { + std::cerr << name_ << " Adding remote attribute: " + *it + << std::endl; + ++it; + } + } + auto credentials = mIceCredentials[aStream->GetId()]; + res = aStream->ConnectToPeer(credentials.first, credentials.second, + attributes); + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + } + + if (start) { + ice_ctx_->SetControlling(offerer_ ? NrIceCtx::ICE_CONTROLLING + : NrIceCtx::ICE_CONTROLLED); + // Now start checks + res = ice_ctx_->StartChecks(); + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + } + + void Connect(IceTestPeer* remote, TrickleMode trickle_mode, + bool start = true) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::Connect_s, remote, trickle_mode, + start), + NS_DISPATCH_SYNC); + } + + void SimulateTrickle(size_t stream) { + std::cerr << name_ << " Doing trickle for stream " << stream << std::endl; + // If we are in trickle deferred mode, now trickle in the candidates + // for |stream| + + std::vector<SchedulableTrickleCandidate*>& candidates = + ControlTrickle(stream); + + for (auto& candidate : candidates) { + candidate->Schedule(0); + } + } + + // Allows test case to completely control when/if candidates are trickled + // (test could also do things like insert extra trickle candidates, or + // change existing ones, or insert duplicates, really anything is fair game) + std::vector<SchedulableTrickleCandidate*>& ControlTrickle(size_t stream) { + std::cerr << "Doing controlled trickle for stream " << stream << std::endl; + + std::vector<std::string> attributes = remote_->GetAttributes(stream); + + for (const auto& attribute : attributes) { + if (attribute.find("candidate:") != std::string::npos) { + controlled_trickle_candidates_[stream].push_back( + new SchedulableTrickleCandidate(this, stream, attribute, "", + test_utils_)); + } + } + + return controlled_trickle_candidates_[stream]; + } + + nsresult TrickleCandidate_s(const std::string& candidate, + const std::string& ufrag, size_t index) { + auto stream = GetStream_s(index); + if (!stream) { + // stream might have gone away before the trickle timer popped + return NS_OK; + } + return stream->ParseTrickleCandidate(candidate, ufrag, ""); + } + + void DumpCandidate(std::string which, const NrIceCandidate& cand) { + std::string type; + std::string tcp_type; + + std::string addr; + int port; + + if (which.find("Remote") != std::string::npos) { + addr = cand.cand_addr.host; + port = cand.cand_addr.port; + } else { + addr = cand.local_addr.host; + port = cand.local_addr.port; + } + switch (cand.type) { + case NrIceCandidate::ICE_HOST: + type = "host"; + break; + 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(); + }; + + switch (cand.tcp_type) { + case NrIceCandidate::ICE_NONE: + break; + case NrIceCandidate::ICE_ACTIVE: + tcp_type = " tcptype=active"; + break; + case NrIceCandidate::ICE_PASSIVE: + tcp_type = " tcptype=passive"; + break; + case NrIceCandidate::ICE_SO: + tcp_type = " tcptype=so"; + break; + default: + FAIL(); + }; + + std::cerr << which << " --> " << type << " " << addr << ":" << port << "/" + << cand.cand_addr.transport << tcp_type + << " codeword=" << cand.codeword << std::endl; + } + + void DumpAndCheckActiveCandidates_s() { + std::cerr << name_ << " Active candidates:" << std::endl; + for (const auto& stream : ice_ctx_->GetStreams()) { + for (size_t j = 0; j < stream->components(); ++j) { + std::cerr << name_ << " Stream " << stream->GetId() << " component " + << j + 1 << std::endl; + + UniquePtr<NrIceCandidate> local; + UniquePtr<NrIceCandidate> remote; + + nsresult res = stream->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); + /* Depending on timing, and the whims of the network + * stack/configuration we're running on top of, prflx is always a + * possibility. */ + if (expected_local_type_ == NrIceCandidate::ICE_HOST) { + ASSERT_NE(NrIceCandidate::ICE_SERVER_REFLEXIVE, local->type); + ASSERT_NE(NrIceCandidate::ICE_RELAYED, local->type); + } else { + ASSERT_EQ(expected_local_type_, local->type); + } + ASSERT_EQ(expected_local_transport_, local->local_addr.transport); + DumpCandidate("Remote ", *remote); + /* Depending on timing, and the whims of the network + * stack/configuration we're running on top of, prflx is always a + * possibility. */ + if (expected_remote_type_ == NrIceCandidate::ICE_HOST) { + ASSERT_NE(NrIceCandidate::ICE_SERVER_REFLEXIVE, remote->type); + ASSERT_NE(NrIceCandidate::ICE_RELAYED, remote->type); + } else { + ASSERT_EQ(expected_remote_type_, remote->type); + } + if (!expected_remote_addr_.empty()) { + ASSERT_EQ(expected_remote_addr_, remote->cand_addr.host); + } + } + } + } + } + + 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() { + std::cerr << name_ << " Shutdown" << std::endl; + shutting_down_ = true; + for (auto& controlled_trickle_candidate : controlled_trickle_candidates_) { + for (auto& cand : controlled_trickle_candidate.second) { + delete cand; + } + } + + ice_ctx_->Destroy(); + ice_ctx_ = nullptr; + + if (remote_) { + remote_->UnsetRemote(); + remote_ = nullptr; + } + } + + void UnsetRemote() { remote_ = nullptr; } + + void StartChecks() { + nsresult res; + + test_utils_->sts_target()->Dispatch( + WrapRunnableRet( + &res, ice_ctx_, &NrIceCtx::SetControlling, + offerer_ ? NrIceCtx::ICE_CONTROLLING : NrIceCtx::ICE_CONTROLLED), + NS_DISPATCH_SYNC); + // Now start checks + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&res, ice_ctx_, &NrIceCtx::StartChecks), + NS_DISPATCH_SYNC); + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + + // Handle events + void GatheringStateChange(NrIceCtx* ctx, NrIceCtx::GatheringState state) { + if (shutting_down_) { + return; + } + if (state != NrIceCtx::ICE_CTX_GATHER_COMPLETE) { + return; + } + + std::cerr << name_ << " Gathering complete" << std::endl; + gathering_complete_ = true; + + std::cerr << name_ << " ATTRIBUTES:" << std::endl; + for (const auto& stream : ice_ctx_->GetStreams()) { + std::cerr << "Stream " << stream->GetId() << std::endl; + + std::vector<std::string> attributes = stream->GetAttributes(); + + for (const auto& attribute : attributes) { + std::cerr << attribute << std::endl; + } + } + std::cerr << std::endl; + } + + void CandidateInitialized(NrIceMediaStream* stream, + const std::string& raw_candidate, + const std::string& ufrag, + const std::string& mdns_addr, + const std::string& actual_addr) { + std::string candidate(FilterCandidate(raw_candidate)); + if (candidate.empty()) { + return; + } + std::cerr << "Candidate for stream " << stream->name() + << " initialized: " << candidate << std::endl; + candidates_[stream->name()].push_back(candidate); + + // If we are connected, then try to trickle to the other side. + if (remote_ && remote_->remote_ && (trickle_mode_ != TRICKLE_SIMULATE)) { + // first, find the index of the stream we've been given so + // we can get the corresponding stream on the remote side + for (size_t i = 0; i < stream_counter_; ++i) { + if (GetStream_s(i) == stream) { + ASSERT_GT(remote_->stream_counter_, i); + nsresult res = remote_->GetStream_s(i)->ParseTrickleCandidate( + candidate, ufrag, ""); + ASSERT_TRUE(NS_SUCCEEDED(res)); + return; + } + } + ADD_FAILURE() << "No matching stream found for " << stream; + } + } + + nsresult GetCandidatePairs_s(size_t stream_index, + std::vector<NrIceCandidatePair>* pairs) { + MOZ_ASSERT(pairs); + auto stream = GetStream_s(stream_index); + if (!stream) { + // Is there a better error for "no such index"? + ADD_FAILURE() << "No such media stream index: " << stream_index; + return NS_ERROR_INVALID_ARG; + } + + return stream->GetCandidatePairs(pairs); + } + + nsresult GetCandidatePairs(size_t stream_index, + std::vector<NrIceCandidatePair>* pairs) { + nsresult v; + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&v, this, &IceTestPeer::GetCandidatePairs_s, + stream_index, pairs), + NS_DISPATCH_SYNC); + return v; + } + + void DumpCandidatePair(const NrIceCandidatePair& pair) { + std::cerr << std::endl; + DumpCandidate("Local", pair.local); + DumpCandidate("Remote", pair.remote); + std::cerr << "state = " << pair.state << " priority = " << pair.priority + << " nominated = " << pair.nominated + << " selected = " << pair.selected + << " codeword = " << pair.codeword << std::endl; + } + + void DumpCandidatePairs_s(NrIceMediaStream* stream) { + std::vector<NrIceCandidatePair> pairs; + nsresult res = stream->GetCandidatePairs(&pairs); + ASSERT_TRUE(NS_SUCCEEDED(res)); + + std::cerr << "Begin list of candidate pairs [" << std::endl; + + for (auto& pair : pairs) { + DumpCandidatePair(pair); + } + std::cerr << "]" << std::endl; + } + + void DumpCandidatePairs_s() { + std::cerr << "Dumping candidate pairs for all streams [" << std::endl; + for (const auto& stream : ice_ctx_->GetStreams()) { + DumpCandidatePairs_s(stream.get()); + } + std::cerr << "]" << std::endl; + } + + bool CandidatePairsPriorityDescending( + const std::vector<NrIceCandidatePair>& pairs) { + // Verify that priority is descending + uint64_t priority = std::numeric_limits<uint64_t>::max(); + + for (size_t p = 0; p < pairs.size(); ++p) { + if (priority < pairs[p].priority) { + std::cerr << "Priority increased in subsequent pairs:" << std::endl; + DumpCandidatePair(pairs[p - 1]); + DumpCandidatePair(pairs[p]); + return false; + } + if (priority == pairs[p].priority) { + if (!IceCandidatePairCompare()(pairs[p], pairs[p - 1]) && + !IceCandidatePairCompare()(pairs[p - 1], pairs[p])) { + std::cerr << "Ignoring identical pair from trigger check" + << std::endl; + } else { + std::cerr << "Duplicate priority in subseqent pairs:" << std::endl; + DumpCandidatePair(pairs[p - 1]); + DumpCandidatePair(pairs[p]); + return false; + } + } + priority = pairs[p].priority; + } + return true; + } + + void UpdateAndValidateCandidatePairs( + size_t stream_index, std::vector<NrIceCandidatePair>* new_pairs) { + std::vector<NrIceCandidatePair> old_pairs = *new_pairs; + GetCandidatePairs(stream_index, new_pairs); + ASSERT_TRUE(CandidatePairsPriorityDescending(*new_pairs)) + << "New list of " + "candidate pairs is either not sorted in priority order, or has " + "duplicate priorities."; + ASSERT_TRUE(CandidatePairsPriorityDescending(old_pairs)) + << "Old list of " + "candidate pairs is either not sorted in priority order, or has " + "duplicate priorities. This indicates some bug in the test case."; + std::vector<NrIceCandidatePair> added_pairs; + std::vector<NrIceCandidatePair> removed_pairs; + + // set_difference computes the set of elements that are present in the + // first set, but not the second + // NrIceCandidatePair::operator< compares based on the priority, local + // candidate, and remote candidate in that order. This means this will + // catch cases where the priority has remained the same, but one of the + // candidates has changed. + std::set_difference((*new_pairs).begin(), (*new_pairs).end(), + old_pairs.begin(), old_pairs.end(), + std::inserter(added_pairs, added_pairs.begin()), + IceCandidatePairCompare()); + + std::set_difference(old_pairs.begin(), old_pairs.end(), + (*new_pairs).begin(), (*new_pairs).end(), + std::inserter(removed_pairs, removed_pairs.begin()), + IceCandidatePairCompare()); + + for (auto& added_pair : added_pairs) { + std::cerr << "Found new candidate pair." << std::endl; + DumpCandidatePair(added_pair); + } + + for (auto& removed_pair : removed_pairs) { + std::cerr << "Pre-existing candidate pair is now missing:" << std::endl; + DumpCandidatePair(removed_pair); + } + + ASSERT_TRUE(removed_pairs.empty()) + << "At least one candidate pair has " + "gone missing."; + } + + void StreamReady(NrIceMediaStream* stream) { + ++ready_ct_; + std::cerr << name_ << " Stream ready for " << stream->name() + << " ct=" << ready_ct_ << std::endl; + DumpCandidatePairs_s(stream); + } + void StreamFailed(NrIceMediaStream* stream) { + std::cerr << name_ << " Stream failed for " << stream->name() + << " ct=" << ready_ct_ << std::endl; + DumpCandidatePairs_s(stream); + } + + void ConnectionStateChange(NrIceCtx* ctx, NrIceCtx::ConnectionState state) { + (void)ctx; + switch (state) { + case NrIceCtx::ICE_CTX_INIT: + break; + case NrIceCtx::ICE_CTX_CHECKING: + std::cerr << name_ << " ICE reached checking" << std::endl; + ice_reached_checking_ = true; + break; + case NrIceCtx::ICE_CTX_CONNECTED: + std::cerr << name_ << " ICE connected" << std::endl; + ice_connected_ = true; + break; + case NrIceCtx::ICE_CTX_COMPLETED: + std::cerr << name_ << " ICE completed" << std::endl; + break; + case NrIceCtx::ICE_CTX_FAILED: + std::cerr << name_ << " ICE failed" << std::endl; + ice_failed_ = true; + break; + case NrIceCtx::ICE_CTX_DISCONNECTED: + std::cerr << name_ << " ICE disconnected" << std::endl; + ice_connected_ = false; + break; + default: + MOZ_CRASH(); + } + } + + void PacketReceived(NrIceMediaStream* stream, int component, + const unsigned char* data, int len) { + std::cerr << name_ << ": received " << len << " bytes" << std::endl; + ++received_; + } + + void SendPacket(int stream, int component, const unsigned char* data, + int len) { + auto media_stream = GetStream_s(stream); + if (!media_stream) { + ADD_FAILURE() << "No such stream " << stream; + return; + } + + ASSERT_TRUE(NS_SUCCEEDED(media_stream->SendPacket(component, data, len))); + + ++sent_; + std::cerr << name_ << ": sent " << len << " bytes" << std::endl; + } + + void SendFailure(int stream, int component) { + auto media_stream = GetStream_s(stream); + if (!media_stream) { + ADD_FAILURE() << "No such stream " << stream; + return; + } + + const std::string d("FAIL"); + ASSERT_TRUE(NS_FAILED(media_stream->SendPacket( + component, reinterpret_cast<const unsigned char*>(d.c_str()), + d.length()))); + + std::cerr << name_ << ": send failed as expected" << std::endl; + } + + void SetCandidateFilter(CandidateFilter filter) { + candidate_filter_ = filter; + } + + void ParseCandidate_s(size_t i, const std::string& candidate, + const std::string& mdns_addr) { + auto media_stream = GetStream_s(i); + ASSERT_TRUE(media_stream.get()) + << "No such stream " << i; + media_stream->ParseTrickleCandidate(candidate, "", mdns_addr); + } + + void ParseCandidate(size_t i, const std::string& candidate, + const std::string& mdns_addr) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::ParseCandidate_s, i, candidate, + mdns_addr), + NS_DISPATCH_SYNC); + } + + void DisableComponent_s(size_t index, int component_id) { + ASSERT_LT(index, stream_counter_); + auto stream = GetStream_s(index); + ASSERT_TRUE(stream.get()) + << "No such stream " << index; + nsresult res = stream->DisableComponent(component_id); + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + + void DisableComponent(size_t stream, int component_id) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::DisableComponent_s, stream, + component_id), + NS_DISPATCH_SYNC); + } + + void AssertConsentRefresh_s(size_t index, int component_id, + ConsentStatus status) { + ASSERT_LT(index, stream_counter_); + auto stream = GetStream_s(index); + ASSERT_TRUE(stream.get()) + << "No such stream " << index; + bool can_send; + struct timeval timestamp; + nsresult res = + stream->GetConsentStatus(component_id, &can_send, ×tamp); + ASSERT_TRUE(NS_SUCCEEDED(res)); + if (status == CONSENT_EXPIRED) { + ASSERT_EQ(can_send, 0); + } else { + ASSERT_EQ(can_send, 1); + } + if (consent_timestamp_.tv_sec) { + if (status == CONSENT_FRESH) { + ASSERT_EQ(r_timeval_cmp(×tamp, &consent_timestamp_), 1); + } else { + ASSERT_EQ(r_timeval_cmp(×tamp, &consent_timestamp_), 0); + } + } + consent_timestamp_.tv_sec = timestamp.tv_sec; + consent_timestamp_.tv_usec = timestamp.tv_usec; + std::cerr << name_ + << ": new consent timestamp = " << consent_timestamp_.tv_sec + << "." << consent_timestamp_.tv_usec << std::endl; + } + + void AssertConsentRefresh(ConsentStatus status) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::AssertConsentRefresh_s, 0, 1, status), + NS_DISPATCH_SYNC); + } + + void ChangeNetworkState_s(bool online) { + ice_ctx_->UpdateNetworkState(online); + } + + void ChangeNetworkStateToOffline() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::ChangeNetworkState_s, false), + NS_DISPATCH_SYNC); + } + + void ChangeNetworkStateToOnline() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::ChangeNetworkState_s, true), + NS_DISPATCH_SYNC); + } + + void SetControlling(NrIceCtx::Controlling controlling) { + nsresult res; + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&res, ice_ctx_, &NrIceCtx::SetControlling, controlling), + NS_DISPATCH_SYNC); + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + + NrIceCtx::Controlling GetControlling() { return ice_ctx_->GetControlling(); } + + void SetTiebreaker(uint64_t tiebreaker) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IceTestPeer::SetTiebreaker_s, tiebreaker), + NS_DISPATCH_SYNC); + } + + void SetTiebreaker_s(uint64_t tiebreaker) { + ice_ctx_->peer()->tiebreaker = tiebreaker; + } + + void SimulateIceLite() { + simulate_ice_lite_ = true; + SetControlling(NrIceCtx::ICE_CONTROLLED); + } + + nsresult GetDefaultCandidate(unsigned int stream, NrIceCandidate* cand) { + nsresult rv; + + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&rv, this, &IceTestPeer::GetDefaultCandidate_s, stream, + cand), + NS_DISPATCH_SYNC); + + return rv; + } + + nsresult GetDefaultCandidate_s(unsigned int index, NrIceCandidate* cand) { + return GetStream_s(index)->GetDefaultCandidate(1, cand); + } + + private: + std::string name_; + RefPtr<NrIceCtx> ice_ctx_; + bool offerer_; + std::map<std::string, std::vector<std::string>> candidates_; + // Maps from stream id to list of remote trickle candidates + std::map<size_t, std::vector<SchedulableTrickleCandidate*>> + controlled_trickle_candidates_; + std::map<std::string, std::pair<std::string, std::string>> mIceCredentials; + std::map<std::string, std::pair<std::string, std::string>> mOldIceCredentials; + size_t stream_counter_; + bool shutting_down_; + bool gathering_complete_; + int ready_ct_; + bool ice_connected_; + bool ice_failed_; + bool ice_reached_checking_; + size_t received_; + size_t sent_; + struct timeval consent_timestamp_; + NrIceResolverFake fake_resolver_; + RefPtr<NrIceResolver> dns_resolver_; + IceTestPeer* remote_; + CandidateFilter candidate_filter_; + NrIceCandidate::Type expected_local_type_; + std::string expected_local_transport_; + NrIceCandidate::Type expected_remote_type_; + std::string expected_remote_addr_; + TrickleMode trickle_mode_; + bool simulate_ice_lite_; + RefPtr<mozilla::TestNat> nat_; + MtransportTestUtils* test_utils_; +}; + +void SchedulableTrickleCandidate::Trickle() { + timer_handle_ = nullptr; + nsresult res = peer_->TrickleCandidate_s(candidate_, ufrag_, stream_); + ASSERT_TRUE(NS_SUCCEEDED(res)); +} + +class WebRtcIceGatherTest : public StunTest { + public: + void SetUp() override { + StunTest::SetUp(); + + Preferences::SetInt("media.peerconnection.ice.tcp_so_sock_count", 3); + + test_utils_->sts_target()->Dispatch( + WrapRunnable(TestStunServer::GetInstance(AF_INET), + &TestStunServer::Reset), + NS_DISPATCH_SYNC); + if (TestStunServer::GetInstance(AF_INET6)) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(TestStunServer::GetInstance(AF_INET6), + &TestStunServer::Reset), + NS_DISPATCH_SYNC); + } + } + + void TearDown() override { + peer_ = nullptr; + StunTest::TearDown(); + } + + void EnsurePeer() { + if (!peer_) { + peer_ = + MakeUnique<IceTestPeer>("P1", test_utils_, true, NrIceCtx::Config()); + } + } + + void Gather(unsigned int waitTime = kDefaultTimeout, + bool default_route_only = false, + bool obfuscate_host_addresses = false) { + EnsurePeer(); + peer_->Gather(default_route_only, obfuscate_host_addresses); + + if (waitTime) { + WaitForGather(waitTime); + } + } + + void WaitForGather(unsigned int waitTime = kDefaultTimeout) { + ASSERT_TRUE_WAIT(peer_->gathering_complete(), waitTime); + } + + void AddStunServerWithResponse(const std::string& fake_addr, + uint16_t fake_port, const std::string& fqdn, + const std::string& proto, + std::vector<NrIceStunServer>* stun_servers) { + int family; + if (fake_addr.find(':') != std::string::npos) { + family = AF_INET6; + } else { + family = AF_INET; + } + + std::string stun_addr; + uint16_t stun_port; + if (proto == kNrIceTransportUdp) { + TestStunServer::GetInstance(family)->SetResponseAddr(fake_addr, + fake_port); + stun_addr = TestStunServer::GetInstance(family)->addr(); + stun_port = TestStunServer::GetInstance(family)->port(); + } else if (proto == kNrIceTransportTcp) { + TestStunTcpServer::GetInstance(family)->SetResponseAddr(fake_addr, + fake_port); + stun_addr = TestStunTcpServer::GetInstance(family)->addr(); + stun_port = TestStunTcpServer::GetInstance(family)->port(); + } else { + MOZ_CRASH(); + } + + if (!fqdn.empty()) { + peer_->SetFakeResolver(stun_addr, fqdn); + stun_addr = fqdn; + } + + stun_servers->push_back( + *NrIceStunServer::Create(stun_addr, stun_port, proto.c_str())); + + if (family == AF_INET6 && !fqdn.empty()) { + stun_servers->back().SetUseIPv6IfFqdn(); + } + } + + void UseFakeStunUdpServerWithResponse( + const std::string& fake_addr, uint16_t fake_port, + const std::string& fqdn = std::string()) { + EnsurePeer(); + std::vector<NrIceStunServer> stun_servers; + AddStunServerWithResponse(fake_addr, fake_port, fqdn, "udp", &stun_servers); + peer_->SetStunServers(stun_servers); + } + + void UseFakeStunTcpServerWithResponse( + const std::string& fake_addr, uint16_t fake_port, + const std::string& fqdn = std::string()) { + EnsurePeer(); + std::vector<NrIceStunServer> stun_servers; + AddStunServerWithResponse(fake_addr, fake_port, fqdn, "tcp", &stun_servers); + peer_->SetStunServers(stun_servers); + } + + void UseFakeStunUdpTcpServersWithResponse(const std::string& fake_udp_addr, + uint16_t fake_udp_port, + const std::string& fake_tcp_addr, + uint16_t fake_tcp_port) { + EnsurePeer(); + std::vector<NrIceStunServer> stun_servers; + AddStunServerWithResponse(fake_udp_addr, fake_udp_port, + "", // no fqdn + "udp", &stun_servers); + AddStunServerWithResponse(fake_tcp_addr, fake_tcp_port, + "", // no fqdn + "tcp", &stun_servers); + + peer_->SetStunServers(stun_servers); + } + + void UseTestStunServer() { + TestStunServer::GetInstance(AF_INET)->Reset(); + peer_->SetStunServer(TestStunServer::GetInstance(AF_INET)->addr(), + TestStunServer::GetInstance(AF_INET)->port()); + } + + // NB: Only does substring matching, watch out for stuff like "1.2.3.4" + // matching "21.2.3.47". " 1.2.3.4 " should not have false positives. + bool StreamHasMatchingCandidate(unsigned int stream, const std::string& match, + const std::string& match2 = "") { + std::vector<std::string> attributes = peer_->GetAttributes(stream); + for (auto& attribute : attributes) { + if (std::string::npos != attribute.find(match)) { + if (!match2.length() || std::string::npos != attribute.find(match2)) { + return true; + } + } + } + return false; + } + + void DumpAttributes(unsigned int stream) { + std::vector<std::string> attributes = peer_->GetAttributes(stream); + + std::cerr << "Attributes for stream " << stream << "->" << attributes.size() + << std::endl; + + for (const auto& a : attributes) { + std::cerr << "Attribute: " << a << std::endl; + } + } + + protected: + mozilla::UniquePtr<IceTestPeer> peer_; +}; + +class WebRtcIceConnectTest : public StunTest { + public: + WebRtcIceConnectTest() + : initted_(false), + test_stun_server_inited_(false), + use_nat_(false), + filtering_type_(TestNat::ENDPOINT_INDEPENDENT), + mapping_type_(TestNat::ENDPOINT_INDEPENDENT), + block_udp_(false) {} + + void SetUp() override { + StunTest::SetUp(); + + nsresult rv; + target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + } + + void TearDown() override { + p1_ = nullptr; + p2_ = nullptr; + + StunTest::TearDown(); + } + + void AddStream(int components) { + Init(); + p1_->AddStream(components); + p2_->AddStream(components); + } + + void RemoveStream(size_t index) { + p1_->RemoveStream(index); + p2_->RemoveStream(index); + } + + void Init(bool setup_stun_servers = true, + NrIceCtx::Policy ice_policy = NrIceCtx::ICE_POLICY_ALL) { + if (initted_) { + return; + } + + NrIceCtx::Config config; + config.mPolicy = ice_policy; + + p1_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config); + p2_ = MakeUnique<IceTestPeer>("P2", test_utils_, false, config); + InitPeer(p1_.get(), setup_stun_servers); + InitPeer(p2_.get(), setup_stun_servers); + + initted_ = true; + } + + void InitPeer(IceTestPeer* peer, bool setup_stun_servers = true) { + if (use_nat_) { + // If we enable nat simulation, but still use a real STUN server somewhere + // on the internet, we will see failures if there is a real NAT in + // addition to our simulated one, particularly if it disallows + // hairpinning. + if (setup_stun_servers) { + InitTestStunServer(); + peer->UseTestStunServer(); + } + peer->UseNat(); + peer->SetFilteringType(filtering_type_); + peer->SetMappingType(mapping_type_); + peer->SetBlockUdp(block_udp_); + } else if (setup_stun_servers) { + std::vector<NrIceStunServer> stun_servers; + + stun_servers.push_back(*NrIceStunServer::Create( + stun_server_address_, kDefaultStunServerPort, kNrIceTransportUdp)); + + peer->SetStunServers(stun_servers); + } + } + + bool Gather(unsigned int waitTime = kDefaultTimeout, + bool default_route_only = false) { + Init(); + + return GatherCallerAndCallee(p1_.get(), p2_.get(), waitTime, + default_route_only); + } + + bool GatherCallerAndCallee(IceTestPeer* caller, IceTestPeer* callee, + unsigned int waitTime = kDefaultTimeout, + bool default_route_only = false) { + caller->Gather(default_route_only); + callee->Gather(default_route_only); + + if (waitTime) { + EXPECT_TRUE_WAIT(caller->gathering_complete(), waitTime); + if (!caller->gathering_complete()) return false; + EXPECT_TRUE_WAIT(callee->gathering_complete(), waitTime); + if (!callee->gathering_complete()) return false; + } + return true; + } + + void UseNat() { + // to be useful, this method should be called before Init + ASSERT_FALSE(initted_); + use_nat_ = true; + } + + void SetFilteringType(TestNat::NatBehavior type) { + // to be useful, this method should be called before Init + ASSERT_FALSE(initted_); + filtering_type_ = type; + } + + void SetMappingType(TestNat::NatBehavior type) { + // to be useful, this method should be called before Init + ASSERT_FALSE(initted_); + mapping_type_ = type; + } + + void BlockUdp() { + // note: |block_udp_| is used only in InitPeer. + // Use IceTestPeer::SetBlockUdp to act on the peer directly. + block_udp_ = true; + } + + void SetupAndCheckConsent() { + p1_->SetTimerDivider(10); + p2_->SetTimerDivider(10); + ASSERT_TRUE(Gather()); + Connect(); + p1_->AssertConsentRefresh(CONSENT_FRESH); + p2_->AssertConsentRefresh(CONSENT_FRESH); + SendReceive(); + } + + void AssertConsentRefresh(ConsentStatus status = CONSENT_FRESH) { + p1_->AssertConsentRefresh(status); + p2_->AssertConsentRefresh(status); + } + + void InitTestStunServer() { + if (test_stun_server_inited_) { + return; + } + + std::cerr << "Resetting TestStunServer" << std::endl; + TestStunServer::GetInstance(AF_INET)->Reset(); + test_stun_server_inited_ = true; + } + + void UseTestStunServer() { + InitTestStunServer(); + p1_->UseTestStunServer(); + p2_->UseTestStunServer(); + } + + void SetTurnServer(const std::string addr, uint16_t port, + const std::string username, const std::string password, + const char* 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); + } + } + + void Connect() { ConnectCallerAndCallee(p1_.get(), p2_.get()); } + + void ConnectCallerAndCallee(IceTestPeer* caller, IceTestPeer* callee, + TrickleMode mode = TRICKLE_NONE) { + ASSERT_TRUE(caller->ready_ct() == 0); + ASSERT_TRUE(caller->ice_connected() == 0); + ASSERT_TRUE(caller->ice_reached_checking() == 0); + ASSERT_TRUE(callee->ready_ct() == 0); + ASSERT_TRUE(callee->ice_connected() == 0); + ASSERT_TRUE(callee->ice_reached_checking() == 0); + + // IceTestPeer::Connect grabs attributes from the first arg, and + // gives them to |this|, meaning that callee->Connect(caller, ...) + // simulates caller sending an offer to callee. Order matters here + // because it determines which peer is controlling. + callee->Connect(caller, mode); + caller->Connect(callee, mode); + + if (mode != TRICKLE_SIMULATE) { + ASSERT_TRUE_WAIT(caller->ice_connected() && callee->ice_connected(), + kDefaultTimeout); + ASSERT_TRUE(caller->ready_ct() >= 1 && callee->ready_ct() >= 1); + ASSERT_TRUE(caller->ice_reached_checking()); + ASSERT_TRUE(callee->ice_reached_checking()); + + caller->DumpAndCheckActiveCandidates(); + callee->DumpAndCheckActiveCandidates(); + } + } + + void SetExpectedTypes(NrIceCandidate::Type local, NrIceCandidate::Type remote, + std::string transport = kNrIceTransportUdp) { + p1_->SetExpectedTypes(local, remote, transport); + p2_->SetExpectedTypes(local, remote, transport); + } + + void SetExpectedRemoteCandidateAddr(const std::string& addr) { + p1_->SetExpectedRemoteCandidateAddr(addr); + p2_->SetExpectedRemoteCandidateAddr(addr); + } + + void ConnectP1(TrickleMode mode = TRICKLE_NONE) { + p1_->Connect(p2_.get(), mode); + } + + void ConnectP2(TrickleMode mode = TRICKLE_NONE) { + p2_->Connect(p1_.get(), mode); + } + + void WaitForConnectedStreams(int expected_streams = 1) { + ASSERT_TRUE_WAIT(p1_->ready_ct() == expected_streams && + p2_->ready_ct() == expected_streams, + kDefaultTimeout); + ASSERT_TRUE_WAIT(p1_->ice_connected() && p2_->ice_connected(), + kDefaultTimeout); + } + + void AssertCheckingReached() { + ASSERT_TRUE(p1_->ice_reached_checking()); + ASSERT_TRUE(p2_->ice_reached_checking()); + } + + void WaitForConnected(unsigned int timeout = kDefaultTimeout) { + ASSERT_TRUE_WAIT(p1_->ice_connected(), timeout); + ASSERT_TRUE_WAIT(p2_->ice_connected(), timeout); + } + + void WaitForGather() { + ASSERT_TRUE_WAIT(p1_->gathering_complete(), kDefaultTimeout); + ASSERT_TRUE_WAIT(p2_->gathering_complete(), kDefaultTimeout); + } + + void WaitForDisconnected(unsigned int timeout = kDefaultTimeout) { + ASSERT_TRUE(p1_->ice_connected()); + ASSERT_TRUE(p2_->ice_connected()); + ASSERT_TRUE_WAIT(p1_->ice_connected() == 0 && p2_->ice_connected() == 0, + timeout); + } + + void WaitForFailed(unsigned int timeout = kDefaultTimeout) { + ASSERT_TRUE_WAIT(p1_->ice_failed() && p2_->ice_failed(), timeout); + } + + void ConnectTrickle(TrickleMode trickle = TRICKLE_SIMULATE) { + p2_->Connect(p1_.get(), trickle); + p1_->Connect(p2_.get(), trickle); + } + + void SimulateTrickle(size_t stream) { + p1_->SimulateTrickle(stream); + p2_->SimulateTrickle(stream); + ASSERT_TRUE_WAIT(p1_->is_ready(stream), kDefaultTimeout); + ASSERT_TRUE_WAIT(p2_->is_ready(stream), kDefaultTimeout); + } + + void SimulateTrickleP1(size_t stream) { p1_->SimulateTrickle(stream); } + + void SimulateTrickleP2(size_t stream) { p2_->SimulateTrickle(stream); } + + void CloseP1() { p1_->Close(); } + + void ConnectThenDelete() { + p2_->Connect(p1_.get(), TRICKLE_NONE, false); + p1_->Connect(p2_.get(), TRICKLE_NONE, true); + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &WebRtcIceConnectTest::CloseP1), NS_DISPATCH_SYNC); + p2_->StartChecks(); + + // Wait to see if we crash + PR_Sleep(PR_MillisecondsToInterval(kDefaultTimeout)); + } + + // default is p1_ sending to p2_ + void SendReceive() { SendReceive(p1_.get(), p2_.get()); } + + void SendReceive(IceTestPeer* p1, IceTestPeer* p2, + bool expect_tx_failure = false, + bool expect_rx_failure = false) { + size_t previousSent = p1->sent(); + size_t previousReceived = p2->received(); + + if (expect_tx_failure) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(p1, &IceTestPeer::SendFailure, 0, 1), NS_DISPATCH_SYNC); + ASSERT_EQ(previousSent, p1->sent()); + } else { + test_utils_->sts_target()->Dispatch( + WrapRunnable(p1, &IceTestPeer::SendPacket, 0, 1, + reinterpret_cast<const unsigned char*>("TEST"), 4), + NS_DISPATCH_SYNC); + ASSERT_EQ(previousSent + 1, p1->sent()); + } + if (expect_rx_failure) { + usleep(1000); + ASSERT_EQ(previousReceived, p2->received()); + } else { + ASSERT_TRUE_WAIT(p2->received() == previousReceived + 1, 1000); + } + } + + void SendFailure() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(p1_.get(), &IceTestPeer::SendFailure, 0, 1), + NS_DISPATCH_SYNC); + } + + protected: + bool initted_; + bool test_stun_server_inited_; + nsCOMPtr<nsIEventTarget> target_; + mozilla::UniquePtr<IceTestPeer> p1_; + mozilla::UniquePtr<IceTestPeer> p2_; + bool use_nat_; + TestNat::NatBehavior filtering_type_; + TestNat::NatBehavior mapping_type_; + bool block_udp_; +}; + +class WebRtcIcePrioritizerTest : public StunTest { + public: + WebRtcIcePrioritizerTest() : prioritizer_(nullptr) {} + + ~WebRtcIcePrioritizerTest() { + if (prioritizer_) { + nr_interface_prioritizer_destroy(&prioritizer_); + } + } + + void SetPriorizer(nr_interface_prioritizer* prioritizer) { + prioritizer_ = prioritizer; + } + + void AddInterface(const std::string& num, int type, int estimated_speed) { + std::string str_addr = "10.0.0." + num; + std::string ifname = "eth" + num; + nr_local_addr local_addr; + local_addr.interface.type = type; + local_addr.interface.estimated_speed = estimated_speed; + + int r = nr_str_port_to_transport_addr(str_addr.c_str(), 0, IPPROTO_UDP, + &(local_addr.addr)); + ASSERT_EQ(0, r); + strncpy(local_addr.addr.ifname, ifname.c_str(), MAXIFNAME - 1); + local_addr.addr.ifname[MAXIFNAME - 1] = '\0'; + + r = nr_interface_prioritizer_add_interface(prioritizer_, &local_addr); + ASSERT_EQ(0, r); + r = nr_interface_prioritizer_sort_preference(prioritizer_); + ASSERT_EQ(0, r); + } + + void HasLowerPreference(const std::string& num1, const std::string& num2) { + std::string key1 = "eth" + num1 + ":10.0.0." + num1; + std::string key2 = "eth" + num2 + ":10.0.0." + num2; + UCHAR pref1, pref2; + int r = nr_interface_prioritizer_get_priority(prioritizer_, key1.c_str(), + &pref1); + ASSERT_EQ(0, r); + r = nr_interface_prioritizer_get_priority(prioritizer_, key2.c_str(), + &pref2); + ASSERT_EQ(0, r); + ASSERT_LE(pref1, pref2); + } + + private: + nr_interface_prioritizer* prioritizer_; +}; + +class WebRtcIcePacketFilterTest : public StunTest { + public: + WebRtcIcePacketFilterTest() : udp_filter_(nullptr), tcp_filter_(nullptr) {} + + void SetUp() { + StunTest::SetUp(); + + NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig()); + + // Set up enough of the ICE ctx to allow the packet filter to work + ice_ctx_ = NrIceCtx::Create("test"); + + nsCOMPtr<nsISocketFilterHandler> udp_handler = + do_GetService(NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID); + ASSERT_TRUE(udp_handler); + udp_handler->NewFilter(getter_AddRefs(udp_filter_)); + + nsCOMPtr<nsISocketFilterHandler> tcp_handler = + do_GetService(NS_STUN_TCP_SOCKET_FILTER_HANDLER_CONTRACTID); + ASSERT_TRUE(tcp_handler); + tcp_handler->NewFilter(getter_AddRefs(tcp_filter_)); + } + + void TearDown() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &WebRtcIcePacketFilterTest::TearDown_s), + NS_DISPATCH_SYNC); + StunTest::TearDown(); + } + + void TearDown_s() { ice_ctx_ = nullptr; } + + void TestIncoming(const uint8_t* data, uint32_t len, uint8_t from_addr, + int from_port, bool expected_result) { + mozilla::net::NetAddr addr; + MakeNetAddr(&addr, from_addr, from_port); + bool result; + nsresult rv = udp_filter_->FilterPacket( + &addr, data, len, nsISocketFilter::SF_INCOMING, &result); + ASSERT_EQ(NS_OK, rv); + ASSERT_EQ(expected_result, result); + } + + void TestIncomingTcp(const uint8_t* data, uint32_t len, + bool expected_result) { + mozilla::net::NetAddr addr; + bool result; + nsresult rv = tcp_filter_->FilterPacket( + &addr, data, len, nsISocketFilter::SF_INCOMING, &result); + ASSERT_EQ(NS_OK, rv); + ASSERT_EQ(expected_result, result); + } + + void TestIncomingTcpFramed(const uint8_t* data, uint32_t len, + bool expected_result) { + mozilla::net::NetAddr addr; + bool result; + uint8_t* framed_data = new uint8_t[len + 2]; + framed_data[0] = htons(len); + memcpy(&framed_data[2], data, len); + nsresult rv = tcp_filter_->FilterPacket( + &addr, framed_data, len + 2, nsISocketFilter::SF_INCOMING, &result); + ASSERT_EQ(NS_OK, rv); + ASSERT_EQ(expected_result, result); + delete[] framed_data; + } + + void TestOutgoing(const uint8_t* data, uint32_t len, uint8_t to_addr, + int to_port, bool expected_result) { + mozilla::net::NetAddr addr; + MakeNetAddr(&addr, to_addr, to_port); + bool result; + nsresult rv = udp_filter_->FilterPacket( + &addr, data, len, nsISocketFilter::SF_OUTGOING, &result); + ASSERT_EQ(NS_OK, rv); + ASSERT_EQ(expected_result, result); + } + + void TestOutgoingTcp(const uint8_t* data, uint32_t len, + bool expected_result) { + mozilla::net::NetAddr addr; + bool result; + nsresult rv = tcp_filter_->FilterPacket( + &addr, data, len, nsISocketFilter::SF_OUTGOING, &result); + ASSERT_EQ(NS_OK, rv); + ASSERT_EQ(expected_result, result); + } + + void TestOutgoingTcpFramed(const uint8_t* data, uint32_t len, + bool expected_result) { + mozilla::net::NetAddr addr; + bool result; + uint8_t* framed_data = new uint8_t[len + 2]; + framed_data[0] = htons(len); + memcpy(&framed_data[2], data, len); + nsresult rv = tcp_filter_->FilterPacket( + &addr, framed_data, len + 2, nsISocketFilter::SF_OUTGOING, &result); + ASSERT_EQ(NS_OK, rv); + ASSERT_EQ(expected_result, result); + delete[] framed_data; + } + + private: + void MakeNetAddr(mozilla::net::NetAddr* net_addr, uint8_t last_digit, + uint16_t port) { + net_addr->inet.family = AF_INET; + net_addr->inet.ip = 192 << 24 | 168 << 16 | 1 << 8 | last_digit; + net_addr->inet.port = port; + } + + nsCOMPtr<nsISocketFilter> udp_filter_; + nsCOMPtr<nsISocketFilter> tcp_filter_; + RefPtr<NrIceCtx> ice_ctx_; +}; +} // end namespace + +TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerHostnameNoResolver) { + if (stun_server_hostname_.empty()) { + return; + } + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort); + peer_->AddStream(1); + Gather(); +} + +// Disabled because google isn't running any TCP stun servers right now +TEST_F(WebRtcIceGatherTest, + DISABLED_TestGatherFakeStunServerTcpHostnameNoResolver) { + if (stun_server_hostname_.empty()) { + return; + } + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort, + kNrIceTransportTcp); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP ")); +} + +TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerIpAddress) { + if (stun_server_address_.empty()) { + return; + } + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort); + peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_); + peer_->AddStream(1); + Gather(); +} + +TEST_F(WebRtcIceGatherTest, TestGatherStunServerIpAddressNoHost) { + if (stun_server_address_.empty()) { + return; + } + + { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + } + + NrIceCtx::Config config; + config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST; + peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config); + peer_->AddStream(1); + peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort); + peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_); + Gather(); + ASSERT_FALSE(StreamHasMatchingCandidate(0, " host ")); +} + +TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerHostname) { + if (stun_server_hostname_.empty()) { + return; + } + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort); + peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_); + peer_->AddStream(1); + Gather(); +} + +TEST_F(WebRtcIceGatherTest, TestGatherFakeStunBogusHostname) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort); + peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_); + peer_->AddStream(1); + Gather(); +} + +TEST_F(WebRtcIceGatherTest, TestGatherDNSStunServerIpAddress) { + if (stun_server_address_.empty()) { + return; + } + + { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + } + + // A srflx candidate is considered redundant and discarded if its address + // equals that of a host candidate. (Frequently, a srflx candidate and a host + // candidate have equal addresses when the agent is not behind a NAT.) So set + // ICE_POLICY_NO_HOST here to ensure that a srflx candidate is not falsely + // discarded in this test. + NrIceCtx::Config config; + config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST; + peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config); + + peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort); + peer_->SetDNSResolver(); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "typ srflx raddr")); +} + +// Disabled because google isn't running any TCP stun servers right now +TEST_F(WebRtcIceGatherTest, DISABLED_TestGatherDNSStunServerIpAddressTcp) { + if (stun_server_address_.empty()) { + return; + } + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort, + kNrIceTransportTcp); + peer_->SetDNSResolver(); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype passive")); + ASSERT_FALSE(StreamHasMatchingCandidate(0, "tcptype passive", " 9 ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype so")); + ASSERT_FALSE(StreamHasMatchingCandidate(0, "tcptype so", " 9 ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype active", " 9 ")); +} + +TEST_F(WebRtcIceGatherTest, TestGatherDNSStunServerHostname) { + if (stun_server_hostname_.empty()) { + return; + } + + { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + } + + // A srflx candidate is considered redundant and discarded if its address + // equals that of a host candidate. (Frequently, a srflx candidate and a host + // candidate have equal addresses when the agent is not behind a NAT.) So set + // ICE_POLICY_NO_HOST here to ensure that a srflx candidate is not falsely + // discarded in this test. + NrIceCtx::Config config; + config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST; + peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config); + + peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort); + peer_->SetDNSResolver(); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "typ srflx raddr")); +} + +// Disabled because google isn't running any TCP stun servers right now +TEST_F(WebRtcIceGatherTest, DISABLED_TestGatherDNSStunServerHostnameTcp) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort, + kNrIceTransportTcp); + peer_->SetDNSResolver(); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype passive")); + ASSERT_FALSE(StreamHasMatchingCandidate(0, "tcptype passive", " 9 ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype so")); + ASSERT_FALSE(StreamHasMatchingCandidate(0, "tcptype so", " 9 ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype active", " 9 ")); +} + +// Disabled because google isn't running any TCP stun servers right now +TEST_F(WebRtcIceGatherTest, + DISABLED_TestGatherDNSStunServerHostnameBothUdpTcp) { + if (stun_server_hostname_.empty()) { + return; + } + + std::vector<NrIceStunServer> stun_servers; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + stun_servers.push_back(*NrIceStunServer::Create( + stun_server_hostname_, kDefaultStunServerPort, kNrIceTransportUdp)); + stun_servers.push_back(*NrIceStunServer::Create( + stun_server_hostname_, kDefaultStunServerPort, kNrIceTransportTcp)); + peer_->SetStunServers(stun_servers); + peer_->SetDNSResolver(); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP ")); +} + +// Disabled because google isn't running any TCP stun servers right now +TEST_F(WebRtcIceGatherTest, + DISABLED_TestGatherDNSStunServerIpAddressBothUdpTcp) { + if (stun_server_address_.empty()) { + return; + } + + std::vector<NrIceStunServer> stun_servers; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + stun_servers.push_back(*NrIceStunServer::Create( + stun_server_address_, kDefaultStunServerPort, kNrIceTransportUdp)); + stun_servers.push_back(*NrIceStunServer::Create( + stun_server_address_, kDefaultStunServerPort, kNrIceTransportTcp)); + peer_->SetStunServers(stun_servers); + peer_->SetDNSResolver(); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP ")); +} + +TEST_F(WebRtcIceGatherTest, TestGatherDNSStunBogusHostname) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort); + peer_->SetDNSResolver(); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP ")); +} + +// Disabled because google isn't running any TCP stun servers right now +TEST_F(WebRtcIceGatherTest, DISABLED_TestGatherDNSStunBogusHostnameTcp) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort, + kNrIceTransportTcp); + peer_->SetDNSResolver(); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP ")); +} + +TEST_F(WebRtcIceGatherTest, TestDefaultCandidate) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort); + peer_->AddStream(1); + Gather(); + NrIceCandidate default_candidate; + ASSERT_TRUE(NS_SUCCEEDED(peer_->GetDefaultCandidate(0, &default_candidate))); +} + +TEST_F(WebRtcIceGatherTest, TestGatherTurn) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + if (turn_server_.empty()) return; + peer_->SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_, kNrIceTransportUdp); + peer_->AddStream(1); + Gather(); +} + +TEST_F(WebRtcIceGatherTest, TestGatherTurnTcp) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + if (turn_server_.empty()) return; + peer_->SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_, kNrIceTransportTcp); + peer_->AddStream(1); + Gather(); +} + +TEST_F(WebRtcIceGatherTest, TestGatherDisableComponent) { + if (stun_server_hostname_.empty()) { + return; + } + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort); + peer_->AddStream(1); + peer_->AddStream(2); + peer_->DisableComponent(1, 2); + Gather(); + std::vector<std::string> attributes = peer_->GetAttributes(1); + + for (auto& attribute : attributes) { + if (attribute.find("candidate:") != std::string::npos) { + size_t sp1 = attribute.find(' '); + ASSERT_EQ(0, attribute.compare(sp1 + 1, 1, "1", 1)); + } + } +} + +TEST_F(WebRtcIceGatherTest, TestGatherVerifyNoLoopback) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->AddStream(1); + Gather(); + ASSERT_FALSE(StreamHasMatchingCandidate(0, "127.0.0.1")); +} + +TEST_F(WebRtcIceGatherTest, TestGatherAllowLoopback) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + config.mAllowLoopback = true; + NrIceCtx::InitializeGlobals(config); + + // Set up peer with loopback allowed. + peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, NrIceCtx::Config()); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "127.0.0.1")); +} + +TEST_F(WebRtcIceGatherTest, TestGatherTcpDisabledNoStun) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->AddStream(1); + Gather(); + ASSERT_FALSE(StreamHasMatchingCandidate(0, " TCP ")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP ")); +} + +TEST_F(WebRtcIceGatherTest, VerifyTestStunServer) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("192.0.2.133", 3333); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.2.133 3333 ")); +} + +TEST_F(WebRtcIceGatherTest, VerifyTestStunTcpServer) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + UseFakeStunTcpServerWithResponse("192.0.2.233", 3333); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.2.233 3333 typ srflx", + " tcptype ")); +} + +TEST_F(WebRtcIceGatherTest, VerifyTestStunServerV6) { + if (!TestStunServer::GetInstance(AF_INET6)) { + // No V6 addresses + return; + } + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("beef::", 3333); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " beef:: 3333 ")); +} + +TEST_F(WebRtcIceGatherTest, VerifyTestStunServerFQDN) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("192.0.2.133", 3333, "stun.example.com"); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.2.133 3333 ")); +} + +TEST_F(WebRtcIceGatherTest, VerifyTestStunServerV6FQDN) { + if (!TestStunServer::GetInstance(AF_INET6)) { + // No V6 addresses + return; + } + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("beef::", 3333, "stun.example.com"); + peer_->AddStream(1); + Gather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " beef:: 3333 ")); +} + +TEST_F(WebRtcIceGatherTest, TestStunServerReturnsWildcardAddr) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("0.0.0.0", 3333); + peer_->AddStream(1); + Gather(kDefaultTimeout * 3); + ASSERT_FALSE(StreamHasMatchingCandidate(0, " 0.0.0.0 ")); +} + +TEST_F(WebRtcIceGatherTest, TestStunServerReturnsWildcardAddrV6) { + if (!TestStunServer::GetInstance(AF_INET6)) { + // No V6 addresses + return; + } + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("::", 3333); + peer_->AddStream(1); + Gather(kDefaultTimeout * 3); + ASSERT_FALSE(StreamHasMatchingCandidate(0, " :: ")); +} + +TEST_F(WebRtcIceGatherTest, TestStunServerReturnsPort0) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("192.0.2.133", 0); + peer_->AddStream(1); + Gather(kDefaultTimeout * 3); + ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.2.133 0 ")); +} + +TEST_F(WebRtcIceGatherTest, TestStunServerReturnsLoopbackAddr) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("127.0.0.133", 3333); + peer_->AddStream(1); + Gather(kDefaultTimeout * 3); + ASSERT_FALSE(StreamHasMatchingCandidate(0, " 127.0.0.133 ")); +} + +TEST_F(WebRtcIceGatherTest, TestStunServerReturnsLoopbackAddrV6) { + if (!TestStunServer::GetInstance(AF_INET6)) { + // No V6 addresses + return; + } + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("::1", 3333); + peer_->AddStream(1); + Gather(kDefaultTimeout * 3); + ASSERT_FALSE(StreamHasMatchingCandidate(0, " ::1 ")); +} + +TEST_F(WebRtcIceGatherTest, TestStunServerTrickle) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpServerWithResponse("192.0.2.1", 3333); + peer_->AddStream(1); + TestStunServer::GetInstance(AF_INET)->SetDropInitialPackets(3); + Gather(0); + ASSERT_FALSE(StreamHasMatchingCandidate(0, "192.0.2.1")); + WaitForGather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "192.0.2.1")); +} + +// Test no host with our fake STUN server and apparently NATted. +TEST_F(WebRtcIceGatherTest, TestFakeStunServerNatedNoHost) { + { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + } + + NrIceCtx::Config config; + config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST; + peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config); + UseFakeStunUdpServerWithResponse("192.0.2.1", 3333); + peer_->AddStream(1); + Gather(0); + WaitForGather(); + DumpAttributes(0); + ASSERT_FALSE(StreamHasMatchingCandidate(0, "host")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "srflx")); + NrIceCandidate default_candidate; + nsresult rv = peer_->GetDefaultCandidate(0, &default_candidate); + if (NS_SUCCEEDED(rv)) { + ASSERT_NE(NrIceCandidate::ICE_HOST, default_candidate.type); + } +} + +// Test no host with our fake STUN server and apparently non-NATted. +TEST_F(WebRtcIceGatherTest, TestFakeStunServerNoNatNoHost) { + { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + } + + NrIceCtx::Config config; + config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST; + peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config); + UseTestStunServer(); + peer_->AddStream(1); + Gather(0); + WaitForGather(); + DumpAttributes(0); + ASSERT_FALSE(StreamHasMatchingCandidate(0, "host")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "srflx")); +} + +// Test that srflx candidate is discarded in non-NATted environment if host +// address obfuscation is not enabled. +TEST_F(WebRtcIceGatherTest, + TestSrflxCandidateDiscardedWithObfuscateHostAddressesNotEnabled) { + { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + } + + NrIceCtx::Config config; + peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config); + UseTestStunServer(); + peer_->AddStream(1); + Gather(0, false, false); + WaitForGather(); + DumpAttributes(0); + EXPECT_TRUE(StreamHasMatchingCandidate(0, "host")); + EXPECT_FALSE(StreamHasMatchingCandidate(0, "srflx")); +} + +// Test that srflx candidate is generated in non-NATted environment if host +// address obfuscation is enabled. +TEST_F(WebRtcIceGatherTest, + TestSrflxCandidateGeneratedWithObfuscateHostAddressesEnabled) { + { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + } + + NrIceCtx::Config config; + peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config); + UseTestStunServer(); + peer_->AddStream(1); + Gather(0, false, true); + WaitForGather(); + DumpAttributes(0); + EXPECT_TRUE(StreamHasMatchingCandidate(0, "host")); + EXPECT_TRUE(StreamHasMatchingCandidate(0, "srflx")); +} + +TEST_F(WebRtcIceGatherTest, TestStunTcpServerTrickle) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + UseFakeStunTcpServerWithResponse("192.0.3.1", 3333); + TestStunTcpServer::GetInstance(AF_INET)->SetDelay(500); + peer_->AddStream(1); + Gather(0); + ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype ")); + WaitForGather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype ")); +} + +TEST_F(WebRtcIceGatherTest, TestStunTcpAndUdpServerTrickle) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + UseFakeStunUdpTcpServersWithResponse("192.0.2.1", 3333, "192.0.3.1", 3333); + TestStunServer::GetInstance(AF_INET)->SetDropInitialPackets(3); + TestStunTcpServer::GetInstance(AF_INET)->SetDelay(500); + peer_->AddStream(1); + Gather(0); + ASSERT_FALSE(StreamHasMatchingCandidate(0, "192.0.2.1", "UDP")); + ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype ")); + WaitForGather(); + ASSERT_TRUE(StreamHasMatchingCandidate(0, "192.0.2.1", "UDP")); + ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype ")); +} + +TEST_F(WebRtcIceGatherTest, TestSetIceControlling) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->AddStream(1); + peer_->SetControlling(NrIceCtx::ICE_CONTROLLING); + NrIceCtx::Controlling controlling = peer_->GetControlling(); + ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, controlling); + // SetControlling should only allow setting this once + peer_->SetControlling(NrIceCtx::ICE_CONTROLLED); + controlling = peer_->GetControlling(); + ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, controlling); +} + +TEST_F(WebRtcIceGatherTest, TestSetIceControlled) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + EnsurePeer(); + peer_->AddStream(1); + peer_->SetControlling(NrIceCtx::ICE_CONTROLLED); + NrIceCtx::Controlling controlling = peer_->GetControlling(); + ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, controlling); + // SetControlling should only allow setting this once + peer_->SetControlling(NrIceCtx::ICE_CONTROLLING); + controlling = peer_->GetControlling(); + ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, controlling); +} + +TEST_F(WebRtcIceConnectTest, TestGather) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); +} + +TEST_F(WebRtcIceConnectTest, TestGatherTcp) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + Init(); + AddStream(1); + ASSERT_TRUE(Gather()); +} + +TEST_F(WebRtcIceConnectTest, TestGatherAutoPrioritize) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + Init(); + AddStream(1); + ASSERT_TRUE(Gather()); +} + +TEST_F(WebRtcIceConnectTest, TestConnect) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectRestartIce) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); + SendReceive(p1_.get(), p2_.get()); + + p2_->RestartIce(); + ASSERT_FALSE(p2_->gathering_complete()); + + // verify p1 and p2 streams are still connected after restarting ice on p2 + SendReceive(p1_.get(), p2_.get()); + + mozilla::UniquePtr<IceTestPeer> p3_; + p3_ = MakeUnique<IceTestPeer>("P3", test_utils_, true, NrIceCtx::Config()); + InitPeer(p3_.get()); + p3_->AddStream(1); + + ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get())); + std::cout << "-------------------------------------------------" << std::endl; + ConnectCallerAndCallee(p3_.get(), p2_.get(), TRICKLE_SIMULATE); + SendReceive(p1_.get(), p2_.get()); // p1 and p2 are still connected + SendReceive(p3_.get(), p2_.get(), true, true); // p3 and p2 not yet connected + p2_->SimulateTrickle(0); + p3_->SimulateTrickle(0); + ASSERT_TRUE_WAIT(p3_->is_ready(0), kDefaultTimeout); + ASSERT_TRUE_WAIT(p2_->is_ready(0), kDefaultTimeout); + SendReceive(p1_.get(), p2_.get(), false, true); // p1 and p2 not connected + SendReceive(p3_.get(), p2_.get()); // p3 and p2 are now connected + + p3_ = nullptr; +} + +TEST_F(WebRtcIceConnectTest, TestConnectRestartIceThenAbort) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); + SendReceive(p1_.get(), p2_.get()); + + p2_->RestartIce(); + ASSERT_FALSE(p2_->gathering_complete()); + + // verify p1 and p2 streams are still connected after restarting ice on p2 + SendReceive(p1_.get(), p2_.get()); + + mozilla::UniquePtr<IceTestPeer> p3_; + p3_ = MakeUnique<IceTestPeer>("P3", test_utils_, true, NrIceCtx::Config()); + InitPeer(p3_.get()); + p3_->AddStream(1); + + ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get())); + std::cout << "-------------------------------------------------" << std::endl; + p2_->RollbackIceRestart(); + p2_->Connect(p1_.get(), TRICKLE_NONE); + SendReceive(p1_.get(), p2_.get()); + p3_ = nullptr; +} + +TEST_F(WebRtcIceConnectTest, TestConnectIceRestartRoleConflict) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + // Just for fun lets do this with switched rolls + p1_->SetControlling(NrIceCtx::ICE_CONTROLLED); + p2_->SetControlling(NrIceCtx::ICE_CONTROLLING); + Connect(); + SendReceive(p1_.get(), p2_.get()); + // Set rolls should not switch by connecting + ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p1_->GetControlling()); + ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, p2_->GetControlling()); + + p2_->RestartIce(); + ASSERT_FALSE(p2_->gathering_complete()); + p2_->SetControlling(NrIceCtx::ICE_CONTROLLED); + ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, p2_->GetControlling()) + << "ICE restart should not allow role to change, unless ice-lite happens"; + + mozilla::UniquePtr<IceTestPeer> p3_; + p3_ = MakeUnique<IceTestPeer>("P3", test_utils_, true, NrIceCtx::Config()); + InitPeer(p3_.get()); + p3_->AddStream(1); + // Set control role for p3 accordingly (with role conflict) + p3_->SetControlling(NrIceCtx::ICE_CONTROLLING); + ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, p3_->GetControlling()); + + ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get())); + std::cout << "-------------------------------------------------" << std::endl; + ConnectCallerAndCallee(p3_.get(), p2_.get()); + auto p2role = p2_->GetControlling(); + ASSERT_NE(p2role, p3_->GetControlling()) << "Conflict should be resolved"; + ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p1_->GetControlling()) + << "P1 should be unaffected by role conflict"; + + // And again we are not allowed to switch roles at this point any more + p1_->SetControlling(NrIceCtx::ICE_CONTROLLING); + ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p1_->GetControlling()); + p3_->SetControlling(p2role); + ASSERT_NE(p2role, p3_->GetControlling()); + + p3_ = nullptr; +} + +TEST_F(WebRtcIceConnectTest, + TestIceRestartWithMultipleInterfacesAndUserStartingScreenSharing) { + const char* FAKE_WIFI_ADDR = "10.0.0.1"; + const char* FAKE_WIFI_IF_NAME = "wlan9"; + + // prepare a fake wifi interface + nr_local_addr wifi_addr; + wifi_addr.interface.type = NR_INTERFACE_TYPE_WIFI; + wifi_addr.interface.estimated_speed = 1000; + + int r = nr_str_port_to_transport_addr(FAKE_WIFI_ADDR, 0, IPPROTO_UDP, + &(wifi_addr.addr)); + ASSERT_EQ(0, r); + strncpy(wifi_addr.addr.ifname, FAKE_WIFI_IF_NAME, MAXIFNAME); + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + // setup initial ICE connection between p1_ and p2_ + UseNat(); + AddStream(1); + SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE, + NrIceCandidate::Type::ICE_SERVER_REFLEXIVE); + ASSERT_TRUE(Gather(kDefaultTimeout, true)); + Connect(); + + // verify the connection is working + SendReceive(p1_.get(), p2_.get()); + + // simulate user accepting permissions for screen sharing + p2_->SetCtxFlags(false); + + // and having an additional non-default interface + nsTArray<NrIceStunAddr> stunAddr = p2_->GetStunAddrs(); + stunAddr.InsertElementAt(0, NrIceStunAddr(&wifi_addr)); + p2_->SetStunAddrs(stunAddr); + + std::cout << "-------------------------------------------------" << std::endl; + + // now restart ICE + p2_->RestartIce(); + ASSERT_FALSE(p2_->gathering_complete()); + + // verify that we can successfully gather candidates + p2_->Gather(); + EXPECT_TRUE_WAIT(p2_->gathering_complete(), kDefaultTimeout); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTcp) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + Init(); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsTcpCandidate); + SetExpectedTypes(NrIceCandidate::Type::ICE_HOST, + NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp); + Connect(); +} + +// TCP SO tests works on localhost only with delay applied: +// tc qdisc add dev lo root netem delay 10ms +TEST_F(WebRtcIceConnectTest, DISABLED_TestConnectTcpSo) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + Init(); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsTcpSoCandidate); + SetExpectedTypes(NrIceCandidate::Type::ICE_HOST, + NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp); + Connect(); +} + +// Disabled because this breaks with hairpinning. +TEST_F(WebRtcIceConnectTest, DISABLED_TestConnectNoHost) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + Init(false, NrIceCtx::ICE_POLICY_NO_HOST); + AddStream(1); + ASSERT_TRUE(Gather()); + SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE, + NrIceCandidate::Type::ICE_SERVER_REFLEXIVE, + kNrIceTransportTcp); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestLoopbackOnlySortOf) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + config.mAllowLoopback = true; + NrIceCtx::InitializeGlobals(config); + Init(false); + AddStream(1); + SetCandidateFilter(IsLoopbackCandidate); + ASSERT_TRUE(Gather()); + SetExpectedRemoteCandidateAddr("127.0.0.1"); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectBothControllingP1Wins) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + p1_->SetTiebreaker(1); + p2_->SetTiebreaker(0); + ASSERT_TRUE(Gather()); + p1_->SetControlling(NrIceCtx::ICE_CONTROLLING); + p2_->SetControlling(NrIceCtx::ICE_CONTROLLING); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectBothControllingP2Wins) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + p1_->SetTiebreaker(0); + p2_->SetTiebreaker(1); + ASSERT_TRUE(Gather()); + p1_->SetControlling(NrIceCtx::ICE_CONTROLLING); + p2_->SetControlling(NrIceCtx::ICE_CONTROLLING); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectIceLiteOfferer) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + p1_->SimulateIceLite(); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestTrickleBothControllingP1Wins) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + p1_->SetTiebreaker(1); + p2_->SetTiebreaker(0); + ASSERT_TRUE(Gather()); + p1_->SetControlling(NrIceCtx::ICE_CONTROLLING); + p2_->SetControlling(NrIceCtx::ICE_CONTROLLING); + ConnectTrickle(); + SimulateTrickle(0); + WaitForConnected(1000); + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, TestTrickleBothControllingP2Wins) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + p1_->SetTiebreaker(0); + p2_->SetTiebreaker(1); + ASSERT_TRUE(Gather()); + p1_->SetControlling(NrIceCtx::ICE_CONTROLLING); + p2_->SetControlling(NrIceCtx::ICE_CONTROLLING); + ConnectTrickle(); + SimulateTrickle(0); + WaitForConnected(1000); + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, TestTrickleIceLiteOfferer) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + p1_->SimulateIceLite(); + ConnectTrickle(); + SimulateTrickle(0); + WaitForConnected(1000); + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, TestGatherFullCone) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + AddStream(1); + ASSERT_TRUE(Gather()); +} + +TEST_F(WebRtcIceConnectTest, TestGatherFullConeAutoPrioritize) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + Init(); + AddStream(1); + ASSERT_TRUE(Gather()); +} + +TEST_F(WebRtcIceConnectTest, TestConnectFullCone) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + AddStream(1); + SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE, + NrIceCandidate::Type::ICE_SERVER_REFLEXIVE); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectNoNatNoHost) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + Init(false, NrIceCtx::ICE_POLICY_NO_HOST); + UseTestStunServer(); + // Because we are connecting from our host candidate to the + // other side's apparent srflx (which is also their host) + // we see a host/srflx pair. + SetExpectedTypes(NrIceCandidate::Type::ICE_HOST, + NrIceCandidate::Type::ICE_SERVER_REFLEXIVE); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectFullConeNoHost) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + Init(false, NrIceCtx::ICE_POLICY_NO_HOST); + UseTestStunServer(); + SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE, + NrIceCandidate::Type::ICE_SERVER_REFLEXIVE); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestGatherAddressRestrictedCone) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + SetFilteringType(TestNat::ADDRESS_DEPENDENT); + SetMappingType(TestNat::ENDPOINT_INDEPENDENT); + AddStream(1); + ASSERT_TRUE(Gather()); +} + +TEST_F(WebRtcIceConnectTest, TestConnectAddressRestrictedCone) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + SetFilteringType(TestNat::ADDRESS_DEPENDENT); + SetMappingType(TestNat::ENDPOINT_INDEPENDENT); + AddStream(1); + SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE, + NrIceCandidate::Type::ICE_SERVER_REFLEXIVE); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestGatherPortRestrictedCone) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + SetFilteringType(TestNat::PORT_DEPENDENT); + SetMappingType(TestNat::ENDPOINT_INDEPENDENT); + AddStream(1); + ASSERT_TRUE(Gather()); +} + +TEST_F(WebRtcIceConnectTest, TestConnectPortRestrictedCone) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + SetFilteringType(TestNat::PORT_DEPENDENT); + SetMappingType(TestNat::ENDPOINT_INDEPENDENT); + AddStream(1); + SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE, + NrIceCandidate::Type::ICE_SERVER_REFLEXIVE); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestGatherSymmetricNat) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + SetFilteringType(TestNat::PORT_DEPENDENT); + SetMappingType(TestNat::PORT_DEPENDENT); + AddStream(1); + ASSERT_TRUE(Gather()); +} + +TEST_F(WebRtcIceConnectTest, TestConnectSymmetricNat) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + SetFilteringType(TestNat::PORT_DEPENDENT); + SetMappingType(TestNat::PORT_DEPENDENT); + p1_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED, + NrIceCandidate::Type::ICE_RELAYED); + p2_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED, + NrIceCandidate::Type::ICE_RELAYED); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectSymmetricNatAndNoNat) { + { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + } + + NrIceCtx::Config config; + p1_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config); + p1_->UseNat(); + p1_->SetFilteringType(TestNat::PORT_DEPENDENT); + p1_->SetMappingType(TestNat::PORT_DEPENDENT); + + p2_ = MakeUnique<IceTestPeer>("P2", test_utils_, false, config); + initted_ = true; + + AddStream(1); + p1_->SetExpectedTypes(NrIceCandidate::Type::ICE_PEER_REFLEXIVE, + NrIceCandidate::Type::ICE_HOST); + p2_->SetExpectedTypes(NrIceCandidate::Type::ICE_HOST, + NrIceCandidate::Type::ICE_PEER_REFLEXIVE); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestGatherNatBlocksUDP) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + BlockUdp(); + std::vector<NrIceTurnServer> turn_servers; + std::vector<unsigned char> password_vec(turn_password_.begin(), + turn_password_.end()); + turn_servers.push_back( + *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort, turn_user_, + password_vec, kNrIceTransportTcp)); + turn_servers.push_back( + *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort, turn_user_, + password_vec, kNrIceTransportUdp)); + SetTurnServers(turn_servers); + AddStream(1); + // We have to wait for the UDP-based stuff to time out. + ASSERT_TRUE(Gather(kDefaultTimeout * 3)); +} + +TEST_F(WebRtcIceConnectTest, TestConnectNatBlocksUDP) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + UseNat(); + BlockUdp(); + std::vector<NrIceTurnServer> turn_servers; + std::vector<unsigned char> password_vec(turn_password_.begin(), + turn_password_.end()); + turn_servers.push_back( + *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort, turn_user_, + password_vec, kNrIceTransportTcp)); + turn_servers.push_back( + *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort, turn_user_, + password_vec, kNrIceTransportUdp)); + SetTurnServers(turn_servers); + p1_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED, + NrIceCandidate::Type::ICE_RELAYED, kNrIceTransportTcp); + p2_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED, + NrIceCandidate::Type::ICE_RELAYED, kNrIceTransportTcp); + AddStream(1); + ASSERT_TRUE(Gather(kDefaultTimeout * 3)); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTwoComponents) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(2); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTwoComponentsDisableSecond) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(2); + ASSERT_TRUE(Gather()); + p1_->DisableComponent(0, 2); + p2_->DisableComponent(0, 2); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectP2(); + PR_Sleep(1000); + ConnectP1(); + WaitForConnectedStreams(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1Trickle) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectP2(); + PR_Sleep(1000); + ConnectP1(TRICKLE_SIMULATE); + SimulateTrickleP1(0); + WaitForConnectedStreams(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1TrickleTwoComponents) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + AddStream(2); + ASSERT_TRUE(Gather()); + ConnectP2(); + PR_Sleep(1000); + ConnectP1(TRICKLE_SIMULATE); + SimulateTrickleP1(0); + std::cerr << "Sleeping between trickle streams" << std::endl; + PR_Sleep(1000); // Give this some time to settle but not complete + // all of ICE. + SimulateTrickleP1(1); + WaitForConnectedStreams(2); +} + +TEST_F(WebRtcIceConnectTest, TestConnectAutoPrioritize) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + Init(); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTrickleOneStreamOneComponent) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + SimulateTrickle(0); + WaitForConnected(1000); + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTrickleTwoStreamsOneComponent) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + SimulateTrickle(0); + SimulateTrickle(1); + WaitForConnected(1000); + AssertCheckingReached(); +} + +void RealisticTrickleDelay( + std::vector<SchedulableTrickleCandidate*>& candidates) { + for (size_t i = 0; i < candidates.size(); ++i) { + SchedulableTrickleCandidate* cand = candidates[i]; + if (cand->IsHost()) { + cand->Schedule(i * 10); + } else if (cand->IsReflexive()) { + cand->Schedule(i * 10 + 100); + } else if (cand->IsRelay()) { + cand->Schedule(i * 10 + 200); + } + } +} + +void DelayRelayCandidates(std::vector<SchedulableTrickleCandidate*>& candidates, + unsigned int ms) { + for (auto& candidate : candidates) { + if (candidate->IsRelay()) { + candidate->Schedule(ms); + } else { + candidate->Schedule(0); + } + } +} + +void AddNonPairableCandidates( + std::vector<SchedulableTrickleCandidate*>& candidates, IceTestPeer* peer, + size_t stream, int net_type, MtransportTestUtils* test_utils_) { + for (int i = 1; i < 5; i++) { + if (net_type == i) continue; + switch (i) { + case 1: + candidates.push_back(new SchedulableTrickleCandidate( + peer, stream, + "candidate:0 1 UDP 2113601790 10.0.0.1 12345 typ host", "", + test_utils_)); + break; + case 2: + candidates.push_back(new SchedulableTrickleCandidate( + peer, stream, + "candidate:0 1 UDP 2113601791 172.16.1.1 12345 typ host", "", + test_utils_)); + break; + case 3: + candidates.push_back(new SchedulableTrickleCandidate( + peer, stream, + "candidate:0 1 UDP 2113601792 192.168.0.1 12345 typ host", "", + test_utils_)); + break; + case 4: + candidates.push_back(new SchedulableTrickleCandidate( + peer, stream, + "candidate:0 1 UDP 2113601793 100.64.1.1 12345 typ host", "", + test_utils_)); + break; + default: + NR_UNIMPLEMENTED; + } + } + + for (auto i = candidates.rbegin(); i != candidates.rend(); ++i) { + std::cerr << "Scheduling candidate: " << (*i)->Candidate().c_str() + << std::endl; + (*i)->Schedule(0); + } +} + +void DropTrickleCandidates( + std::vector<SchedulableTrickleCandidate*>& candidates) {} + +TEST_F(WebRtcIceConnectTest, TestConnectTrickleAddStreamDuringICE) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + RealisticTrickleDelay(p2_->ControlTrickle(0)); + AddStream(1); + RealisticTrickleDelay(p1_->ControlTrickle(1)); + RealisticTrickleDelay(p2_->ControlTrickle(1)); + WaitForConnected(1000); + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTrickleAddStreamAfterICE) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + RealisticTrickleDelay(p2_->ControlTrickle(0)); + WaitForConnected(1000); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(1)); + RealisticTrickleDelay(p2_->ControlTrickle(1)); + WaitForConnected(1000); + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, RemoveStream) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + RealisticTrickleDelay(p2_->ControlTrickle(0)); + RealisticTrickleDelay(p1_->ControlTrickle(1)); + RealisticTrickleDelay(p2_->ControlTrickle(1)); + WaitForConnected(1000); + + RemoveStream(0); + ASSERT_TRUE(Gather()); + ConnectTrickle(); +} + +TEST_F(WebRtcIceConnectTest, P1NoTrickle) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + DropTrickleCandidates(p1_->ControlTrickle(0)); + RealisticTrickleDelay(p2_->ControlTrickle(0)); + WaitForConnected(1000); +} + +TEST_F(WebRtcIceConnectTest, P2NoTrickle) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + DropTrickleCandidates(p2_->ControlTrickle(0)); + WaitForConnected(1000); +} + +TEST_F(WebRtcIceConnectTest, RemoveAndAddStream) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + RealisticTrickleDelay(p2_->ControlTrickle(0)); + RealisticTrickleDelay(p1_->ControlTrickle(1)); + RealisticTrickleDelay(p2_->ControlTrickle(1)); + WaitForConnected(1000); + + RemoveStream(0); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(2)); + RealisticTrickleDelay(p2_->ControlTrickle(2)); + WaitForConnected(1000); +} + +TEST_F(WebRtcIceConnectTest, RemoveStreamBeforeGather) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + AddStream(1); + ASSERT_TRUE(Gather(0)); + RemoveStream(0); + WaitForGather(); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(1)); + RealisticTrickleDelay(p2_->ControlTrickle(1)); + WaitForConnected(1000); +} + +TEST_F(WebRtcIceConnectTest, RemoveStreamDuringGather) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + AddStream(1); + RemoveStream(0); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(1)); + RealisticTrickleDelay(p2_->ControlTrickle(1)); + WaitForConnected(1000); +} + +TEST_F(WebRtcIceConnectTest, RemoveStreamDuringConnect) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + RealisticTrickleDelay(p2_->ControlTrickle(0)); + RealisticTrickleDelay(p1_->ControlTrickle(1)); + RealisticTrickleDelay(p2_->ControlTrickle(1)); + RemoveStream(0); + WaitForConnected(1000); +} + +TEST_F(WebRtcIceConnectTest, TestConnectRealTrickleOneStreamOneComponent) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + AddStream(1); + ASSERT_TRUE(Gather(0)); + ConnectTrickle(TRICKLE_REAL); + WaitForConnected(); + WaitForGather(); // ICE can complete before we finish gathering. + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, TestSendReceive) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); + SendReceive(); +} + +TEST_F(WebRtcIceConnectTest, TestSendReceiveTcp) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + Init(); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsTcpCandidate); + SetExpectedTypes(NrIceCandidate::Type::ICE_HOST, + NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp); + Connect(); + SendReceive(); +} + +// TCP SO tests works on localhost only with delay applied: +// tc qdisc add dev lo root netem delay 10ms +TEST_F(WebRtcIceConnectTest, DISABLED_TestSendReceiveTcpSo) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + Init(); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsTcpSoCandidate); + SetExpectedTypes(NrIceCandidate::Type::ICE_HOST, + NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp); + Connect(); + SendReceive(); +} + +TEST_F(WebRtcIceConnectTest, TestConsent) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + SetupAndCheckConsent(); + PR_Sleep(1500); + AssertConsentRefresh(); + SendReceive(); +} + +TEST_F(WebRtcIceConnectTest, TestConsentTcp) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = true; + NrIceCtx::InitializeGlobals(config); + Init(); + AddStream(1); + SetCandidateFilter(IsTcpCandidate); + SetExpectedTypes(NrIceCandidate::Type::ICE_HOST, + NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp); + SetupAndCheckConsent(); + PR_Sleep(1500); + AssertConsentRefresh(); + SendReceive(); +} + +TEST_F(WebRtcIceConnectTest, TestConsentIntermittent) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + SetupAndCheckConsent(); + p1_->SetBlockStun(true); + p2_->SetBlockStun(true); + WaitForDisconnected(); + AssertConsentRefresh(CONSENT_STALE); + SendReceive(); + p1_->SetBlockStun(false); + p2_->SetBlockStun(false); + WaitForConnected(); + AssertConsentRefresh(); + SendReceive(); + p1_->SetBlockStun(true); + p2_->SetBlockStun(true); + WaitForDisconnected(); + AssertConsentRefresh(CONSENT_STALE); + SendReceive(); + p1_->SetBlockStun(false); + p2_->SetBlockStun(false); + WaitForConnected(); + AssertConsentRefresh(); +} + +TEST_F(WebRtcIceConnectTest, TestConsentTimeout) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + SetupAndCheckConsent(); + p1_->SetBlockStun(true); + p2_->SetBlockStun(true); + WaitForDisconnected(); + AssertConsentRefresh(CONSENT_STALE); + SendReceive(); + WaitForFailed(); + AssertConsentRefresh(CONSENT_EXPIRED); + SendFailure(); +} + +TEST_F(WebRtcIceConnectTest, TestConsentDelayed) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + SetupAndCheckConsent(); + /* Note: We don't have a list of STUN transaction IDs of the previously timed + out consent requests. Thus responses after sending the next consent + request are ignored. */ + p1_->SetStunResponseDelay(200); + p2_->SetStunResponseDelay(200); + PR_Sleep(1000); + AssertConsentRefresh(); + SendReceive(); +} + +TEST_F(WebRtcIceConnectTest, TestNetworkForcedOfflineAndRecovery) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + SetupAndCheckConsent(); + p1_->ChangeNetworkStateToOffline(); + ASSERT_TRUE_WAIT(p1_->ice_connected() == 0, kDefaultTimeout); + // Next round of consent check should switch it back to online + ASSERT_TRUE_WAIT(p1_->ice_connected(), kDefaultTimeout); +} + +TEST_F(WebRtcIceConnectTest, TestNetworkForcedOfflineTwice) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + SetupAndCheckConsent(); + p2_->ChangeNetworkStateToOffline(); + ASSERT_TRUE_WAIT(p2_->ice_connected() == 0, kDefaultTimeout); + p2_->ChangeNetworkStateToOffline(); + ASSERT_TRUE_WAIT(p2_->ice_connected() == 0, kDefaultTimeout); +} + +TEST_F(WebRtcIceConnectTest, TestNetworkOnlineDoesntChangeState) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + SetupAndCheckConsent(); + p2_->ChangeNetworkStateToOnline(); + ASSERT_TRUE(p2_->ice_connected()); + PR_Sleep(1500); + p2_->ChangeNetworkStateToOnline(); + ASSERT_TRUE(p2_->ice_connected()); +} + +TEST_F(WebRtcIceConnectTest, TestNetworkOnlineTriggersConsent) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + // Let's emulate audio + video w/o rtcp-mux + AddStream(2); + AddStream(2); + SetupAndCheckConsent(); + p1_->ChangeNetworkStateToOffline(); + p1_->SetBlockStun(true); + ASSERT_TRUE_WAIT(p1_->ice_connected() == 0, kDefaultTimeout); + PR_Sleep(1500); + ASSERT_TRUE(p1_->ice_connected() == 0); + p1_->SetBlockStun(false); + p1_->ChangeNetworkStateToOnline(); + ASSERT_TRUE_WAIT(p1_->ice_connected(), 500); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTurn) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTurnWithDelay) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_); + SetCandidateFilter(SabotageHostCandidateAndDropReflexive); + AddStream(1); + p1_->Gather(); + PR_Sleep(500); + p2_->Gather(); + ConnectTrickle(TRICKLE_REAL); + WaitForGather(); + WaitForConnectedStreams(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTurnWithNormalTrickleDelay) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + RealisticTrickleDelay(p2_->ControlTrickle(0)); + + WaitForConnected(); + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTurnWithNormalTrickleDelayOneSided) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + RealisticTrickleDelay(p1_->ControlTrickle(0)); + p2_->SimulateTrickle(0); + + WaitForConnected(); + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTurnWithLargeTrickleDelay) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_); + SetCandidateFilter(SabotageHostCandidateAndDropReflexive); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); + // Trickle host candidates immediately, but delay relay candidates + DelayRelayCandidates(p1_->ControlTrickle(0), 3700); + DelayRelayCandidates(p2_->ControlTrickle(0), 3700); + + WaitForConnected(); + AssertCheckingReached(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTurnTcp) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_, kNrIceTransportTcp); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTurnOnly) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsRelayCandidate); + SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED, + NrIceCandidate::Type::ICE_RELAYED); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectTurnTcpOnly) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_, kNrIceTransportTcp); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsRelayCandidate); + SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED, + NrIceCandidate::Type::ICE_RELAYED, kNrIceTransportTcp); + Connect(); +} + +TEST_F(WebRtcIceConnectTest, TestSendReceiveTurnOnly) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsRelayCandidate); + SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED, + NrIceCandidate::Type::ICE_RELAYED); + Connect(); + SendReceive(); +} + +TEST_F(WebRtcIceConnectTest, TestSendReceiveTurnTcpOnly) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_, + turn_password_, kNrIceTransportTcp); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsRelayCandidate); + SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED, + NrIceCandidate::Type::ICE_RELAYED, kNrIceTransportTcp); + Connect(); + SendReceive(); +} + +TEST_F(WebRtcIceConnectTest, TestSendReceiveTurnBothOnly) { + if (turn_server_.empty()) return; + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + std::vector<NrIceTurnServer> turn_servers; + std::vector<unsigned char> password_vec(turn_password_.begin(), + turn_password_.end()); + turn_servers.push_back( + *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort, turn_user_, + password_vec, kNrIceTransportTcp)); + turn_servers.push_back( + *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort, turn_user_, + password_vec, kNrIceTransportUdp)); + SetTurnServers(turn_servers); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsRelayCandidate); + // UDP is preferred. + SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED, + NrIceCandidate::Type::ICE_RELAYED, kNrIceTransportUdp); + Connect(); + SendReceive(); +} + +TEST_F(WebRtcIceConnectTest, TestConnectShutdownOneSide) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + ConnectThenDelete(); +} + +TEST_F(WebRtcIceConnectTest, TestPollCandPairsBeforeConnect) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + + std::vector<NrIceCandidatePair> pairs; + nsresult res = p1_->GetCandidatePairs(0, &pairs); + // There should be no candidate pairs prior to calling Connect() + ASSERT_EQ(NS_OK, res); + ASSERT_EQ(0U, pairs.size()); + + res = p2_->GetCandidatePairs(0, &pairs); + ASSERT_EQ(NS_OK, res); + ASSERT_EQ(0U, pairs.size()); +} + +TEST_F(WebRtcIceConnectTest, TestPollCandPairsAfterConnect) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + Connect(); + + std::vector<NrIceCandidatePair> pairs; + nsresult r = p1_->GetCandidatePairs(0, &pairs); + ASSERT_EQ(NS_OK, r); + // How detailed of a check do we want to do here? If the turn server is + // functioning, we'll get at least two pairs, but this is probably not + // something we should assume. + ASSERT_NE(0U, pairs.size()); + ASSERT_TRUE(p1_->CandidatePairsPriorityDescending(pairs)); + ASSERT_TRUE(ContainsSucceededPair(pairs)); + pairs.clear(); + + r = p2_->GetCandidatePairs(0, &pairs); + ASSERT_EQ(NS_OK, r); + ASSERT_NE(0U, pairs.size()); + ASSERT_TRUE(p2_->CandidatePairsPriorityDescending(pairs)); + ASSERT_TRUE(ContainsSucceededPair(pairs)); +} + +// TODO Bug 1259842 - disabled until we find a better way to handle two +// candidates from different RFC1918 ranges +TEST_F(WebRtcIceConnectTest, DISABLED_TestHostCandPairingFilter) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + Init(false); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsIpv4Candidate); + + int host_net = p1_->GetCandidatesPrivateIpv4Range(0); + if (host_net <= 0) { + // TODO bug 1226838: make this work with multiple private IPs + FAIL() << "This test needs exactly one private IPv4 host candidate to work" + << std::endl; + } + + ConnectTrickle(); + AddNonPairableCandidates(p1_->ControlTrickle(0), p1_.get(), 0, host_net, + test_utils_); + AddNonPairableCandidates(p2_->ControlTrickle(0), p2_.get(), 0, host_net, + test_utils_); + + std::vector<NrIceCandidatePair> pairs; + p1_->GetCandidatePairs(0, &pairs); + for (auto p : pairs) { + std::cerr << "Verifying pair:" << std::endl; + p1_->DumpCandidatePair(p); + nr_transport_addr addr; + nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, + IPPROTO_UDP, &addr); + ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == host_net); + nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, + IPPROTO_UDP, &addr); + ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == host_net); + } +} + +// TODO Bug 1226838 - See Comment 2 - this test can't work as written +TEST_F(WebRtcIceConnectTest, DISABLED_TestSrflxCandPairingFilter) { + if (stun_server_address_.empty()) { + return; + } + + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + Init(false); + AddStream(1); + ASSERT_TRUE(Gather()); + SetCandidateFilter(IsSrflxCandidate); + + if (p1_->GetCandidatesPrivateIpv4Range(0) <= 0) { + // TODO bug 1226838: make this work with public IP addresses + std::cerr << "Don't run this test at IETF meetings!" << std::endl; + FAIL() << "This test needs one private IPv4 host candidate to work" + << std::endl; + } + + ConnectTrickle(); + SimulateTrickleP1(0); + SimulateTrickleP2(0); + + std::vector<NrIceCandidatePair> pairs; + p1_->GetCandidatePairs(0, &pairs); + for (auto p : pairs) { + std::cerr << "Verifying P1 pair:" << std::endl; + p1_->DumpCandidatePair(p); + nr_transport_addr addr; + nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, + IPPROTO_UDP, &addr); + ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) != 0); + nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, + IPPROTO_UDP, &addr); + ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == 0); + } + p2_->GetCandidatePairs(0, &pairs); + for (auto p : pairs) { + std::cerr << "Verifying P2 pair:" << std::endl; + p2_->DumpCandidatePair(p); + nr_transport_addr addr; + nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, + IPPROTO_UDP, &addr); + ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) != 0); + nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, + IPPROTO_UDP, &addr); + ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == 0); + } +} + +TEST_F(WebRtcIceConnectTest, TestPollCandPairsDuringConnect) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + + p2_->Connect(p1_.get(), TRICKLE_NONE, false); + p1_->Connect(p2_.get(), TRICKLE_NONE, false); + + std::vector<NrIceCandidatePair> pairs1; + std::vector<NrIceCandidatePair> pairs2; + + p1_->StartChecks(); + p1_->UpdateAndValidateCandidatePairs(0, &pairs1); + p2_->UpdateAndValidateCandidatePairs(0, &pairs2); + + p2_->StartChecks(); + p1_->UpdateAndValidateCandidatePairs(0, &pairs1); + p2_->UpdateAndValidateCandidatePairs(0, &pairs2); + + WaitForConnectedStreams(); + p1_->UpdateAndValidateCandidatePairs(0, &pairs1); + p2_->UpdateAndValidateCandidatePairs(0, &pairs2); + ASSERT_TRUE(ContainsSucceededPair(pairs1)); + ASSERT_TRUE(ContainsSucceededPair(pairs2)); +} + +TEST_F(WebRtcIceConnectTest, TestRLogConnector) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + ASSERT_TRUE(Gather()); + + p2_->Connect(p1_.get(), TRICKLE_NONE, false); + p1_->Connect(p2_.get(), TRICKLE_NONE, false); + + std::vector<NrIceCandidatePair> pairs1; + std::vector<NrIceCandidatePair> pairs2; + + p1_->StartChecks(); + p1_->UpdateAndValidateCandidatePairs(0, &pairs1); + p2_->UpdateAndValidateCandidatePairs(0, &pairs2); + + p2_->StartChecks(); + p1_->UpdateAndValidateCandidatePairs(0, &pairs1); + p2_->UpdateAndValidateCandidatePairs(0, &pairs2); + + WaitForConnectedStreams(); + p1_->UpdateAndValidateCandidatePairs(0, &pairs1); + p2_->UpdateAndValidateCandidatePairs(0, &pairs2); + ASSERT_TRUE(ContainsSucceededPair(pairs1)); + ASSERT_TRUE(ContainsSucceededPair(pairs2)); + + for (auto& p : pairs1) { + std::deque<std::string> logs; + std::string substring("CAND-PAIR("); + substring += p.codeword; + RLogConnector::GetInstance()->Filter(substring, 0, &logs); + ASSERT_NE(0U, logs.size()); + } + + for (auto& p : pairs2) { + std::deque<std::string> logs; + std::string substring("CAND-PAIR("); + substring += p.codeword; + RLogConnector::GetInstance()->Filter(substring, 0, &logs); + ASSERT_NE(0U, logs.size()); + } +} + +// Verify that a bogus candidate doesn't cause crashes on the +// main thread. See bug 856433. +TEST_F(WebRtcIceConnectTest, TestBogusCandidate) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + Gather(); + ConnectTrickle(); + p1_->ParseCandidate(0, kBogusIceCandidate, ""); + + std::vector<NrIceCandidatePair> pairs; + nsresult res = p1_->GetCandidatePairs(0, &pairs); + ASSERT_EQ(NS_OK, res); + ASSERT_EQ(0U, pairs.size()); +} + +TEST_F(WebRtcIceConnectTest, TestNonMDNSCandidate) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + Gather(); + ConnectTrickle(); + p1_->ParseCandidate(0, kUnreachableHostIceCandidate, ""); + + std::vector<NrIceCandidatePair> pairs; + nsresult res = p1_->GetCandidatePairs(0, &pairs); + ASSERT_EQ(NS_OK, res); + ASSERT_EQ(1U, pairs.size()); + ASSERT_EQ(pairs[0].remote.mdns_addr, ""); +} + +TEST_F(WebRtcIceConnectTest, TestMDNSCandidate) { + NrIceCtx::GlobalConfig config; + config.mTcpEnabled = false; + NrIceCtx::InitializeGlobals(config); + AddStream(1); + Gather(); + ConnectTrickle(); + p1_->ParseCandidate(0, kUnreachableHostIceCandidate, "host.local"); + + std::vector<NrIceCandidatePair> pairs; + nsresult res = p1_->GetCandidatePairs(0, &pairs); + ASSERT_EQ(NS_OK, res); + ASSERT_EQ(1U, pairs.size()); + ASSERT_EQ(pairs[0].remote.mdns_addr, "host.local"); +} + +TEST_F(WebRtcIcePrioritizerTest, TestPrioritizer) { + SetPriorizer(::mozilla::CreateInterfacePrioritizer()); + + AddInterface("0", NR_INTERFACE_TYPE_VPN, 100); // unknown vpn + AddInterface("1", NR_INTERFACE_TYPE_VPN | NR_INTERFACE_TYPE_WIRED, + 100); // wired vpn + AddInterface("2", NR_INTERFACE_TYPE_VPN | NR_INTERFACE_TYPE_WIFI, + 100); // wifi vpn + AddInterface("3", NR_INTERFACE_TYPE_VPN | NR_INTERFACE_TYPE_MOBILE, + 100); // wifi vpn + AddInterface("4", NR_INTERFACE_TYPE_WIRED, 1000); // wired, high speed + AddInterface("5", NR_INTERFACE_TYPE_WIRED, 10); // wired, low speed + AddInterface("6", NR_INTERFACE_TYPE_WIFI, 10); // wifi, low speed + AddInterface("7", NR_INTERFACE_TYPE_WIFI, 1000); // wifi, high speed + AddInterface("8", NR_INTERFACE_TYPE_MOBILE, 10); // mobile, low speed + AddInterface("9", NR_INTERFACE_TYPE_MOBILE, 1000); // mobile, high speed + AddInterface("10", NR_INTERFACE_TYPE_UNKNOWN, 10); // unknown, low speed + AddInterface("11", NR_INTERFACE_TYPE_UNKNOWN, 1000); // unknown, high speed + + // expected preference "4" > "5" > "1" > "7" > "6" > "2" > "9" > "8" > "3" > + // "11" > "10" > "0" + + HasLowerPreference("0", "10"); + HasLowerPreference("10", "11"); + HasLowerPreference("11", "3"); + HasLowerPreference("3", "8"); + HasLowerPreference("8", "9"); + HasLowerPreference("9", "2"); + HasLowerPreference("2", "6"); + HasLowerPreference("6", "7"); + HasLowerPreference("7", "1"); + HasLowerPreference("1", "5"); + HasLowerPreference("5", "4"); +} + +TEST_F(WebRtcIcePacketFilterTest, TestSendNonStunPacket) { + const unsigned char data[] = "12345abcde"; + TestOutgoing(data, sizeof(data), 123, 45, false); + TestOutgoingTcp(data, sizeof(data), false); +} + +TEST_F(WebRtcIcePacketFilterTest, TestRecvNonStunPacket) { + const unsigned char data[] = "12345abcde"; + TestIncoming(data, sizeof(data), 123, 45, false); + TestIncomingTcp(data, sizeof(data), true); +} + +TEST_F(WebRtcIcePacketFilterTest, TestSendStunPacket) { + nr_stun_message* msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg)); + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + TestOutgoingTcp(msg->buffer, msg->length, true); + TestOutgoingTcpFramed(msg->buffer, msg->length, true); + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithoutAPendingId) { + nr_stun_message* msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg)); + + msg->header.id.octet[0] = 1; + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + TestOutgoingTcp(msg->buffer, msg->length, true); + + msg->header.id.octet[0] = 0; + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncoming(msg->buffer, msg->length, 123, 45, true); + TestIncomingTcp(msg->buffer, msg->length, true); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(WebRtcIcePacketFilterTest, TestRecvStunBindingRequestWithoutAPendingId) { + nr_stun_message* msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg)); + + msg->header.id.octet[0] = 1; + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncoming(msg->buffer, msg->length, 123, 45, true); + TestIncomingTcp(msg->buffer, msg->length, true); + + msg->header.id.octet[0] = 1; + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + TestOutgoingTcp(msg->buffer, msg->length, true); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(WebRtcIcePacketFilterTest, + TestRecvStunPacketWithoutAPendingIdTcpFramed) { + nr_stun_message* msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg)); + + msg->header.id.octet[0] = 1; + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoingTcpFramed(msg->buffer, msg->length, true); + + msg->header.id.octet[0] = 0; + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncomingTcpFramed(msg->buffer, msg->length, true); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithoutAPendingAddress) { + nr_stun_message* msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg)); + + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + // nothing to test here for the TCP filter + + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncoming(msg->buffer, msg->length, 123, 46, false); + TestIncoming(msg->buffer, msg->length, 124, 45, false); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithPendingIdAndAddress) { + nr_stun_message* msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg)); + + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + TestOutgoingTcp(msg->buffer, msg->length, true); + + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncoming(msg->buffer, msg->length, 123, 45, true); + TestIncomingTcp(msg->buffer, msg->length, true); + + // Test whitelist by filtering non-stun packets. + const unsigned char data[] = "12345abcde"; + + // 123:45 is white-listed. + TestOutgoing(data, sizeof(data), 123, 45, true); + TestOutgoingTcp(data, sizeof(data), true); + TestIncoming(data, sizeof(data), 123, 45, true); + TestIncomingTcp(data, sizeof(data), true); + + // Indications pass as well. + msg->header.type = NR_STUN_MSG_BINDING_INDICATION; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + TestOutgoingTcp(msg->buffer, msg->length, true); + TestIncoming(msg->buffer, msg->length, 123, 45, true); + TestIncomingTcp(msg->buffer, msg->length, true); + + // Packets from and to other address are still disallowed. + // Note: this doesn't apply for TCP connections + TestOutgoing(data, sizeof(data), 123, 46, false); + TestIncoming(data, sizeof(data), 123, 46, false); + TestOutgoing(data, sizeof(data), 124, 45, false); + TestIncoming(data, sizeof(data), 124, 45, false); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithPendingIdTcpFramed) { + nr_stun_message* msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg)); + + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoingTcpFramed(msg->buffer, msg->length, true); + + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncomingTcpFramed(msg->buffer, msg->length, true); + + // Test whitelist by filtering non-stun packets. + const unsigned char data[] = "12345abcde"; + + TestOutgoingTcpFramed(data, sizeof(data), true); + TestIncomingTcpFramed(data, sizeof(data), true); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(WebRtcIcePacketFilterTest, TestSendNonRequestStunPacket) { + nr_stun_message* msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg)); + + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, false); + TestOutgoingTcp(msg->buffer, msg->length, false); + + // Send a packet so we allow the incoming request. + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + TestOutgoingTcp(msg->buffer, msg->length, true); + + // This packet makes us able to send a response. + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestIncoming(msg->buffer, msg->length, 123, 45, true); + TestIncomingTcp(msg->buffer, msg->length, true); + + msg->header.type = NR_STUN_MSG_BINDING_RESPONSE; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + TestOutgoingTcp(msg->buffer, msg->length, true); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST_F(WebRtcIcePacketFilterTest, TestRecvDataPacketWithAPendingAddress) { + nr_stun_message* msg; + ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg)); + + msg->header.type = NR_STUN_MSG_BINDING_REQUEST; + ASSERT_EQ(0, nr_stun_encode_message(msg)); + TestOutgoing(msg->buffer, msg->length, 123, 45, true); + TestOutgoingTcp(msg->buffer, msg->length, true); + + const unsigned char data[] = "12345abcde"; + TestIncoming(data, sizeof(data), 123, 45, true); + TestIncomingTcp(data, sizeof(data), true); + + ASSERT_EQ(0, nr_stun_message_destroy(&msg)); +} + +TEST(WebRtcIceInternalsTest, TestAddBogusAttribute) +{ + nr_stun_message* req; + ASSERT_EQ(0, nr_stun_message_create(&req)); + Data* data; + ASSERT_EQ(0, r_data_alloc(&data, 3000)); + memset(data->data, 'A', data->len); + ASSERT_TRUE(nr_stun_message_add_message_integrity_attribute(req, data)); + ASSERT_EQ(0, r_data_destroy(&data)); + ASSERT_EQ(0, nr_stun_message_destroy(&req)); +} diff --git a/dom/media/webrtc/transport/test/moz.build b/dom/media/webrtc/transport/test/moz.build new file mode 100644 index 0000000000..69d3a587a5 --- /dev/null +++ b/dom/media/webrtc/transport/test/moz.build @@ -0,0 +1,104 @@ +# -*- Mode: python; 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/. + +include("/ipc/chromium/chromium-config.mozbuild") + +if CONFIG["OS_TARGET"] != "WINNT": + + if CONFIG["OS_TARGET"] != "Android": + SOURCES += [ + "ice_unittest.cpp", + ] + + SOURCES += [ + "buffered_stun_socket_unittest.cpp", + "multi_tcp_socket_unittest.cpp", + "nrappkit_unittest.cpp", + "proxy_tunnel_socket_unittest.cpp", + "rlogconnector_unittest.cpp", + "runnable_utils_unittest.cpp", + "simpletokenbucket_unittest.cpp", + "sockettransportservice_unittest.cpp", + "stunserver.cpp", + "test_nr_socket_ice_unittest.cpp", + "test_nr_socket_unittest.cpp", + "TestSyncRunnable.cpp", + "transport_unittests.cpp", + "turn_unittest.cpp", + "webrtcproxychannel_unittest.cpp", + ] + + if CONFIG["MOZ_SCTP"]: + SOURCES += [ + "sctp_unittest.cpp", + ] + + +for var in ("HAVE_STRDUP", "NR_SOCKET_IS_VOID_PTR", "SCTP_DEBUG"): + DEFINES[var] = True + +if CONFIG["OS_TARGET"] == "Android": + DEFINES["LINUX"] = True + DEFINES["ANDROID"] = True + LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include", + ] + +if CONFIG["OS_TARGET"] == "Linux": + DEFINES["LINUX"] = True + LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include", + ] + +if CONFIG["OS_TARGET"] == "Darwin": + LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include", + ] + +if CONFIG["OS_TARGET"] in ("DragonFly", "FreeBSD", "NetBSD", "OpenBSD"): + if CONFIG["OS_TARGET"] == "Darwin": + DEFINES["DARWIN"] = True + else: + DEFINES["BSD"] = True + LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include", + ] + +# SCTP DEFINES +if CONFIG["OS_TARGET"] == "WINNT": + DEFINES["WIN"] = True + # for stun.h + DEFINES["WIN32"] = True + DEFINES["__Userspace_os_Windows"] = 1 +else: + # Works for Darwin, Linux, Android. Probably doesn't work for others. + DEFINES["__Userspace_os_%s" % CONFIG["OS_TARGET"]] = 1 + +if CONFIG["OS_TARGET"] in ("Darwin", "Android"): + DEFINES["GTEST_USE_OWN_TR1_TUPLE"] = 1 + +LOCAL_INCLUDES += [ + "/dom/media/webrtc/transport/", + "/dom/media/webrtc/transport/third_party/", + "/dom/media/webrtc/transport/third_party/nICEr/src/crypto", + "/dom/media/webrtc/transport/third_party/nICEr/src/ice", + "/dom/media/webrtc/transport/third_party/nICEr/src/net", + "/dom/media/webrtc/transport/third_party/nICEr/src/stun", + "/dom/media/webrtc/transport/third_party/nICEr/src/util", + "/dom/media/webrtc/transport/third_party/nrappkit/src/event", + "/dom/media/webrtc/transport/third_party/nrappkit/src/log", + "/dom/media/webrtc/transport/third_party/nrappkit/src/plugin", + "/dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include", + "/dom/media/webrtc/transport/third_party/nrappkit/src/registry", + "/dom/media/webrtc/transport/third_party/nrappkit/src/share", + "/dom/media/webrtc/transport/third_party/nrappkit/src/stats", + "/dom/media/webrtc/transport/third_party/nrappkit/src/util/", + "/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr", + "/netwerk/sctp/src/", + "/xpcom/tests/", +] + +FINAL_LIBRARY = "xul-gtest" diff --git a/dom/media/webrtc/transport/test/mtransport_test_utils.h b/dom/media/webrtc/transport/test/mtransport_test_utils.h new file mode 100644 index 0000000000..d519508286 --- /dev/null +++ b/dom/media/webrtc/transport/test/mtransport_test_utils.h @@ -0,0 +1,47 @@ +/* -*- 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 + +#ifndef mtransport_test_utils_h__ +#define mtransport_test_utils_h__ + +#include "nsCOMPtr.h" +#include "nsNetCID.h" + +#include "nsISerialEventTarget.h" +#include "nsPISocketTransportService.h" +#include "nsServiceManagerUtils.h" + +class MtransportTestUtils { + public: + MtransportTestUtils() { InitServices(); } + + ~MtransportTestUtils() = default; + + void InitServices() { + nsresult rv; + sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + sts_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } + + nsISerialEventTarget* sts_target() { return sts_target_; } + + private: + nsCOMPtr<nsISerialEventTarget> sts_target_; + nsCOMPtr<nsPISocketTransportService> sts_; +}; + +#define CHECK_ENVIRONMENT_FLAG(envname) \ + char* test_flag = getenv(envname); \ + if (!test_flag || strcmp(test_flag, "1")) { \ + printf("To run this test set %s=1 in your environment\n", envname); \ + exit(0); \ + } + +#endif diff --git a/dom/media/webrtc/transport/test/multi_tcp_socket_unittest.cpp b/dom/media/webrtc/transport/test/multi_tcp_socket_unittest.cpp new file mode 100644 index 0000000000..8b49b70fde --- /dev/null +++ b/dom/media/webrtc/transport/test/multi_tcp_socket_unittest.cpp @@ -0,0 +1,515 @@ +/* -*- 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/. */ + +#include <iostream> +#include <vector> + +#include "mozilla/Atomics.h" +#include "runnable_utils.h" +#include "pk11pub.h" + +extern "C" { +#include "nr_api.h" +#include "nr_socket.h" +#include "transport_addr.h" +#include "nr_socket_multi_tcp.h" +} + +#include "stunserver.h" + +#include "nricectx.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; + +namespace { + +class MultiTcpSocketTest : public MtransportTest { + public: + MultiTcpSocketTest() + : MtransportTest(), socks(3, nullptr), readable(false), ice_ctx_() {} + + void SetUp() { + MtransportTest::SetUp(); + + NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig()); + ice_ctx_ = NrIceCtx::Create("stun"); + + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&TestStunTcpServer::GetInstance, AF_INET), + NS_DISPATCH_SYNC); + test_utils_->sts_target()->Dispatch( + WrapRunnableNM(&TestStunTcpServer::GetInstance, AF_INET6), + NS_DISPATCH_SYNC); + } + + void TearDown() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::Shutdown_s), NS_DISPATCH_SYNC); + + MtransportTest::TearDown(); + } + + DISALLOW_COPY_ASSIGN(MultiTcpSocketTest); + + static void SockReadable(NR_SOCKET s, int how, void* arg) { + MultiTcpSocketTest* obj = static_cast<MultiTcpSocketTest*>(arg); + obj->SetReadable(true); + } + + void Shutdown_s() { + ice_ctx_ = nullptr; + for (auto& sock : socks) { + nr_socket_destroy(&sock); + } + } + + static uint16_t GetRandomPort() { + uint16_t result; + if (PK11_GenerateRandom((unsigned char*)&result, 2) != SECSuccess) { + MOZ_ASSERT(false); + return 0; + } + return result; + } + + static uint16_t EnsureEphemeral(uint16_t port) { + // IANA ephemeral port range (49152 to 65535) + return port | 49152; + } + + void Create_s(nr_socket_tcp_type tcp_type, std::string stun_server_addr, + uint16_t stun_server_port, nr_socket** sock) { + nr_transport_addr local; + // Get start of port range for test + static unsigned short port_s = GetRandomPort(); + int r; + + if (!stun_server_addr.empty()) { + std::vector<NrIceStunServer> stun_servers; + UniquePtr<NrIceStunServer> server(NrIceStunServer::Create( + stun_server_addr, stun_server_port, kNrIceTransportTcp)); + stun_servers.push_back(*server); + + ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers))); + } + + r = 1; + for (int tries = 10; tries && r; --tries) { + r = nr_str_port_to_transport_addr( + (char*)"127.0.0.1", EnsureEphemeral(port_s++), IPPROTO_TCP, &local); + ASSERT_EQ(0, r); + + r = nr_socket_multi_tcp_create(ice_ctx_->ctx(), nullptr, &local, tcp_type, + 1, 2048, sock); + } + + ASSERT_EQ(0, r); + printf("Creating socket on %s\n", local.as_string); + r = nr_socket_multi_tcp_set_readable_cb( + *sock, &MultiTcpSocketTest::SockReadable, this); + ASSERT_EQ(0, r); + } + + nr_socket* Create(nr_socket_tcp_type tcp_type, + std::string stun_server_addr = "", + uint16_t stun_server_port = 0) { + nr_socket* sock = nullptr; + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::Create_s, tcp_type, + stun_server_addr, stun_server_port, &sock), + NS_DISPATCH_SYNC); + return sock; + } + + void Listen_s(nr_socket* sock) { + nr_transport_addr addr; + int r = nr_socket_getaddr(sock, &addr); + ASSERT_EQ(0, r); + printf("Listening on %s\n", addr.as_string); + r = nr_socket_listen(sock, 5); + ASSERT_EQ(0, r); + } + + void Listen(nr_socket* sock) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::Listen_s, sock), + NS_DISPATCH_SYNC); + } + + void Destroy_s(nr_socket* sock) { + int r = nr_socket_destroy(&sock); + ASSERT_EQ(0, r); + } + + void Destroy(nr_socket* sock) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::Destroy_s, sock), + NS_DISPATCH_SYNC); + } + + void Connect_s(nr_socket* from, nr_socket* to) { + nr_transport_addr addr_to; + nr_transport_addr addr_from; + int r = nr_socket_getaddr(to, &addr_to); + ASSERT_EQ(0, r); + r = nr_socket_getaddr(from, &addr_from); + ASSERT_EQ(0, r); + printf("Connecting from %s to %s\n", addr_from.as_string, + addr_to.as_string); + r = nr_socket_connect(from, &addr_to); + ASSERT_EQ(0, r); + } + + void Connect(nr_socket* from, nr_socket* to) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::Connect_s, from, to), + NS_DISPATCH_SYNC); + } + + void ConnectSo_s(nr_socket* so1, nr_socket* so2) { + nr_transport_addr addr_so1; + nr_transport_addr addr_so2; + int r = nr_socket_getaddr(so1, &addr_so1); + ASSERT_EQ(0, r); + r = nr_socket_getaddr(so2, &addr_so2); + ASSERT_EQ(0, r); + printf("Connecting SO %s <-> %s\n", addr_so1.as_string, addr_so2.as_string); + r = nr_socket_connect(so1, &addr_so2); + ASSERT_EQ(0, r); + r = nr_socket_connect(so2, &addr_so1); + ASSERT_EQ(0, r); + } + + void ConnectSo(nr_socket* from, nr_socket* to) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::ConnectSo_s, from, to), + NS_DISPATCH_SYNC); + } + + void SendDataToAddress_s(nr_socket* from, nr_transport_addr* to, + const char* data, size_t len) { + nr_transport_addr addr_from; + + int r = nr_socket_getaddr(from, &addr_from); + ASSERT_EQ(0, r); + printf("Sending %lu bytes %s -> %s\n", (unsigned long)len, + addr_from.as_string, to->as_string); + r = nr_socket_sendto(from, data, len, 0, to); + ASSERT_EQ(0, r); + } + + void SendData(nr_socket* from, nr_transport_addr* to, const char* data, + size_t len) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::SendDataToAddress_s, from, to, + data, len), + NS_DISPATCH_SYNC); + } + + void SendDataToSocket_s(nr_socket* from, nr_socket* to, const char* data, + size_t len) { + nr_transport_addr addr_to; + + int r = nr_socket_getaddr(to, &addr_to); + ASSERT_EQ(0, r); + SendDataToAddress_s(from, &addr_to, data, len); + } + + void SendData(nr_socket* from, nr_socket* to, const char* data, size_t len) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::SendDataToSocket_s, from, to, + data, len), + NS_DISPATCH_SYNC); + } + + void RecvDataFromAddress_s(nr_transport_addr* expected_from, + nr_socket* sent_to, const char* expected_data, + size_t expected_len) { + SetReadable(false); + size_t buflen = expected_len ? expected_len + 1 : 100; + char received_data[buflen]; + nr_transport_addr addr_to; + nr_transport_addr retaddr; + size_t retlen; + + int r = nr_socket_getaddr(sent_to, &addr_to); + ASSERT_EQ(0, r); + printf("Receiving %lu bytes %s <- %s\n", (unsigned long)expected_len, + addr_to.as_string, expected_from->as_string); + r = nr_socket_recvfrom(sent_to, received_data, buflen, &retlen, 0, + &retaddr); + ASSERT_EQ(0, r); + r = nr_transport_addr_cmp(&retaddr, expected_from, + NR_TRANSPORT_ADDR_CMP_MODE_ALL); + ASSERT_EQ(0, r); + // expected_len == 0 means we just expected some data + if (expected_len == 0) { + ASSERT_GT(retlen, 0U); + } else { + ASSERT_EQ(expected_len, retlen); + r = memcmp(expected_data, received_data, retlen); + ASSERT_EQ(0, r); + } + } + + void RecvData(nr_transport_addr* expected_from, nr_socket* sent_to, + const char* expected_data = nullptr, size_t expected_len = 0) { + ASSERT_TRUE_WAIT(IsReadable(), 1000); + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::RecvDataFromAddress_s, + expected_from, sent_to, expected_data, expected_len), + NS_DISPATCH_SYNC); + } + + void RecvDataFromSocket_s(nr_socket* expected_from, nr_socket* sent_to, + const char* expected_data, size_t expected_len) { + nr_transport_addr addr_from; + + int r = nr_socket_getaddr(expected_from, &addr_from); + ASSERT_EQ(0, r); + + RecvDataFromAddress_s(&addr_from, sent_to, expected_data, expected_len); + } + + void RecvData(nr_socket* expected_from, nr_socket* sent_to, + const char* expected_data, size_t expected_len) { + ASSERT_TRUE_WAIT(IsReadable(), 1000); + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::RecvDataFromSocket_s, + expected_from, sent_to, expected_data, expected_len), + NS_DISPATCH_SYNC); + } + + void RecvDataFailed_s(nr_socket* sent_to, size_t expected_len, + int expected_err) { + SetReadable(false); + char received_data[expected_len + 1]; + nr_transport_addr addr_to; + nr_transport_addr retaddr; + size_t retlen; + + int r = nr_socket_getaddr(sent_to, &addr_to); + ASSERT_EQ(0, r); + r = nr_socket_recvfrom(sent_to, received_data, expected_len + 1, &retlen, 0, + &retaddr); + ASSERT_EQ(expected_err, r) << "Expecting receive failure " << expected_err + << " on " << addr_to.as_string; + } + + void RecvDataFailed(nr_socket* sent_to, size_t expected_len, + int expected_err) { + ASSERT_TRUE_WAIT(IsReadable(), 1000); + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &MultiTcpSocketTest::RecvDataFailed_s, sent_to, + expected_len, expected_err), + NS_DISPATCH_SYNC); + } + + void TransferData(nr_socket* from, nr_socket* to, const char* data, + size_t len) { + SendData(from, to, data, len); + RecvData(from, to, data, len); + } + + protected: + bool IsReadable() const { return readable; } + void SetReadable(bool r) { readable = r; } + std::vector<nr_socket*> socks; + Atomic<bool> readable; + RefPtr<NrIceCtx> ice_ctx_; +}; +} // namespace + +TEST_F(MultiTcpSocketTest, TestListen) { + socks[0] = Create(TCP_TYPE_PASSIVE); + Listen(socks[0]); +} + +TEST_F(MultiTcpSocketTest, TestConnect) { + socks[0] = Create(TCP_TYPE_PASSIVE); + socks[1] = Create(TCP_TYPE_ACTIVE); + socks[2] = Create(TCP_TYPE_ACTIVE); + Listen(socks[0]); + Connect(socks[1], socks[0]); + Connect(socks[2], socks[0]); +} + +TEST_F(MultiTcpSocketTest, TestTransmit) { + const char data[] = "TestTransmit"; + socks[0] = Create(TCP_TYPE_ACTIVE); + socks[1] = Create(TCP_TYPE_PASSIVE); + Listen(socks[1]); + Connect(socks[0], socks[1]); + + TransferData(socks[0], socks[1], data, sizeof(data)); + TransferData(socks[1], socks[0], data, sizeof(data)); +} + +TEST_F(MultiTcpSocketTest, TestClosePassive) { + const char data[] = "TestClosePassive"; + socks[0] = Create(TCP_TYPE_ACTIVE); + socks[1] = Create(TCP_TYPE_PASSIVE); + Listen(socks[1]); + Connect(socks[0], socks[1]); + + TransferData(socks[0], socks[1], data, sizeof(data)); + TransferData(socks[1], socks[0], data, sizeof(data)); + + /* We have to destroy as only that calls PR_Close() */ + std::cerr << "Destructing socket" << std::endl; + Destroy(socks[1]); + + RecvDataFailed(socks[0], sizeof(data), R_EOD); + + socks[1] = nullptr; +} + +TEST_F(MultiTcpSocketTest, TestCloseActive) { + const char data[] = "TestCloseActive"; + socks[0] = Create(TCP_TYPE_ACTIVE); + socks[1] = Create(TCP_TYPE_PASSIVE); + Listen(socks[1]); + Connect(socks[0], socks[1]); + + TransferData(socks[0], socks[1], data, sizeof(data)); + TransferData(socks[1], socks[0], data, sizeof(data)); + + /* We have to destroy as only that calls PR_Close() */ + std::cerr << "Destructing socket" << std::endl; + Destroy(socks[0]); + + RecvDataFailed(socks[1], sizeof(data), R_EOD); + + socks[0] = nullptr; +} + +TEST_F(MultiTcpSocketTest, TestTwoSendsBeforeReceives) { + const char data1[] = "TestTwoSendsBeforeReceives"; + const char data2[] = "2nd data"; + socks[0] = Create(TCP_TYPE_ACTIVE); + socks[1] = Create(TCP_TYPE_PASSIVE); + Listen(socks[1]); + Connect(socks[0], socks[1]); + + SendData(socks[0], socks[1], data1, sizeof(data1)); + SendData(socks[0], socks[1], data2, sizeof(data2)); + RecvData(socks[0], socks[1], data1, sizeof(data1)); + /* ICE TCP framing turns TCP effectively into datagram mode */ + RecvData(socks[0], socks[1], data2, sizeof(data2)); +} + +TEST_F(MultiTcpSocketTest, TestTwoActiveBidirectionalTransmit) { + const char data1[] = "TestTwoActiveBidirectionalTransmit"; + const char data2[] = "ReplyToTheFirstSocket"; + const char data3[] = "TestMessageFromTheSecondSocket"; + const char data4[] = "ThisIsAReplyToTheSecondSocket"; + socks[0] = Create(TCP_TYPE_PASSIVE); + socks[1] = Create(TCP_TYPE_ACTIVE); + socks[2] = Create(TCP_TYPE_ACTIVE); + Listen(socks[0]); + Connect(socks[1], socks[0]); + Connect(socks[2], socks[0]); + + TransferData(socks[1], socks[0], data1, sizeof(data1)); + TransferData(socks[0], socks[1], data2, sizeof(data2)); + TransferData(socks[2], socks[0], data3, sizeof(data3)); + TransferData(socks[0], socks[2], data4, sizeof(data4)); +} + +TEST_F(MultiTcpSocketTest, TestTwoPassiveBidirectionalTransmit) { + const char data1[] = "TestTwoPassiveBidirectionalTransmit"; + const char data2[] = "FirstReply"; + const char data3[] = "TestTwoPassiveBidirectionalTransmitToTheSecondSock"; + const char data4[] = "SecondReply"; + socks[0] = Create(TCP_TYPE_PASSIVE); + socks[1] = Create(TCP_TYPE_PASSIVE); + socks[2] = Create(TCP_TYPE_ACTIVE); + Listen(socks[0]); + Listen(socks[1]); + Connect(socks[2], socks[0]); + Connect(socks[2], socks[1]); + + TransferData(socks[2], socks[0], data1, sizeof(data1)); + TransferData(socks[0], socks[2], data2, sizeof(data2)); + TransferData(socks[2], socks[1], data3, sizeof(data3)); + TransferData(socks[1], socks[2], data4, sizeof(data4)); +} + +TEST_F(MultiTcpSocketTest, TestActivePassiveWithStunServerMockup) { + /* Fake STUN message able to pass the nr_is_stun_msg check + used in nr_socket_buffered_stun */ + const char stunMessage[] = {'\x00', '\x01', '\x00', '\x04', '\x21', '\x12', + '\xa4', '\x42', '\x00', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00', + '\x00', '\x00', '\x1c', '\xed', '\xca', '\xfe'}; + const char data[] = "TestActivePassiveWithStunServerMockup"; + + nr_transport_addr stun_srv_addr; + std::string stun_addr; + uint16_t stun_port; + stun_addr = TestStunTcpServer::GetInstance(AF_INET)->addr(); + stun_port = TestStunTcpServer::GetInstance(AF_INET)->port(); + int r = nr_str_port_to_transport_addr(stun_addr.c_str(), stun_port, + IPPROTO_TCP, &stun_srv_addr); + ASSERT_EQ(0, r); + + socks[0] = Create(TCP_TYPE_PASSIVE, stun_addr, stun_port); + Listen(socks[0]); + socks[1] = Create(TCP_TYPE_ACTIVE, stun_addr, stun_port); + + /* Send a fake STUN request and expect a STUN error response */ + SendData(socks[0], &stun_srv_addr, stunMessage, sizeof(stunMessage)); + RecvData(&stun_srv_addr, socks[0]); + + Connect(socks[1], socks[0]); + TransferData(socks[1], socks[0], data, sizeof(data)); + TransferData(socks[0], socks[1], data, sizeof(data)); +} + +TEST_F(MultiTcpSocketTest, TestConnectTwoSo) { + socks[0] = Create(TCP_TYPE_SO); + socks[1] = Create(TCP_TYPE_SO); + ConnectSo(socks[0], socks[1]); +} + +// test works on localhost only with delay applied: +// tc qdisc add dev lo root netem delay 5ms +TEST_F(MultiTcpSocketTest, DISABLED_TestTwoSoBidirectionalTransmit) { + const char data[] = "TestTwoSoBidirectionalTransmit"; + socks[0] = Create(TCP_TYPE_SO); + socks[1] = Create(TCP_TYPE_SO); + ConnectSo(socks[0], socks[1]); + TransferData(socks[0], socks[1], data, sizeof(data)); + TransferData(socks[1], socks[0], data, sizeof(data)); +} + +TEST_F(MultiTcpSocketTest, TestBigData) { + char buf1[2048]; + char buf2[1024]; + + for (unsigned i = 0; i < sizeof(buf1); ++i) { + buf1[i] = i & 0xff; + } + for (unsigned i = 0; i < sizeof(buf2); ++i) { + buf2[i] = (i + 0x80) & 0xff; + } + socks[0] = Create(TCP_TYPE_ACTIVE); + socks[1] = Create(TCP_TYPE_PASSIVE); + Listen(socks[1]); + Connect(socks[0], socks[1]); + + TransferData(socks[0], socks[1], buf1, sizeof(buf1)); + TransferData(socks[0], socks[1], buf2, sizeof(buf2)); + // opposite dir + SendData(socks[1], socks[0], buf2, sizeof(buf2)); + SendData(socks[1], socks[0], buf1, sizeof(buf1)); + RecvData(socks[1], socks[0], buf2, sizeof(buf2)); + RecvData(socks[1], socks[0], buf1, sizeof(buf1)); +} diff --git a/dom/media/webrtc/transport/test/nrappkit_unittest.cpp b/dom/media/webrtc/transport/test/nrappkit_unittest.cpp new file mode 100644 index 0000000000..b8633e7930 --- /dev/null +++ b/dom/media/webrtc/transport/test/nrappkit_unittest.cpp @@ -0,0 +1,126 @@ + +/* -*- 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> + +// nrappkit includes +extern "C" { +#include "nr_api.h" +#include "async_timer.h" +} + +#include "runnable_utils.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; + +namespace { + +class TimerTest : public MtransportTest { + public: + TimerTest() : MtransportTest(), handle_(nullptr), fired_(false) {} + virtual ~TimerTest() = default; + + int ArmTimer(int timeout) { + int ret; + + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&ret, this, &TimerTest::ArmTimer_w, timeout), + NS_DISPATCH_SYNC); + + return ret; + } + + int ArmCancelTimer(int timeout) { + int ret; + + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&ret, this, &TimerTest::ArmCancelTimer_w, timeout), + NS_DISPATCH_SYNC); + + return ret; + } + + int ArmTimer_w(int timeout) { + return NR_ASYNC_TIMER_SET(timeout, cb, this, &handle_); + } + + int ArmCancelTimer_w(int timeout) { + int r; + r = ArmTimer_w(timeout); + if (r) return r; + + return CancelTimer_w(); + } + + int CancelTimer() { + int ret; + + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&ret, this, &TimerTest::CancelTimer_w), + NS_DISPATCH_SYNC); + + return ret; + } + + int CancelTimer_w() { return NR_async_timer_cancel(handle_); } + + int Schedule() { + int ret; + + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&ret, this, &TimerTest::Schedule_w), NS_DISPATCH_SYNC); + + return ret; + } + + int Schedule_w() { + NR_ASYNC_SCHEDULE(cb, this); + + return 0; + } + + static void cb(NR_SOCKET r, int how, void* arg) { + std::cerr << "Timer fired " << std::endl; + + TimerTest* t = static_cast<TimerTest*>(arg); + + t->fired_ = true; + } + + protected: + void* handle_; + bool fired_; +}; +} // namespace + +TEST_F(TimerTest, SimpleTimer) { + ArmTimer(100); + ASSERT_TRUE_WAIT(fired_, 1000); +} + +TEST_F(TimerTest, CancelTimer) { + ArmTimer(1000); + CancelTimer(); + PR_Sleep(2000); + ASSERT_FALSE(fired_); +} + +TEST_F(TimerTest, CancelTimer0) { + ArmCancelTimer(0); + PR_Sleep(100); + ASSERT_FALSE(fired_); +} + +TEST_F(TimerTest, ScheduleTest) { + Schedule(); + ASSERT_TRUE_WAIT(fired_, 1000); +} diff --git a/dom/media/webrtc/transport/test/proxy_tunnel_socket_unittest.cpp b/dom/media/webrtc/transport/test/proxy_tunnel_socket_unittest.cpp new file mode 100644 index 0000000000..1b54126dd6 --- /dev/null +++ b/dom/media/webrtc/transport/test/proxy_tunnel_socket_unittest.cpp @@ -0,0 +1,277 @@ +/* -*- 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 authors: ekr@rtfm.com; ryan@tokbox.com + +#include <vector> +#include <numeric> + +#include "nr_socket_tcp.h" +#include "WebrtcTCPSocketWrapper.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; + +// update TestReadMultipleSizes if you change this +const std::string kHelloMessage = "HELLO IS IT ME YOU'RE LOOKING FOR?"; + +class NrTcpSocketTest : public MtransportTest { + public: + NrTcpSocketTest() + : mSProxy(nullptr), + nr_socket_(nullptr), + mEmptyArray(0), + mReadChunkSize(0), + mReadChunkSizeIncrement(1), + mReadAllowance(-1), + mConnected(false) {} + + void SetUp() override { + mSProxy = new NrTcpSocket(nullptr); + int r = nr_socket_create_int((void*)mSProxy.get(), mSProxy->vtbl(), + &nr_socket_); + ASSERT_EQ(0, r); + + // fake calling AsyncOpen() due to IPC calls. must be non-null + mSProxy->AssignChannel_DoNotUse(new WebrtcTCPSocketWrapper(nullptr)); + } + + void TearDown() override { mSProxy->close(); } + + static void readable_cb(NR_SOCKET s, int how, void* cb_arg) { + NrTcpSocketTest* test = (NrTcpSocketTest*)cb_arg; + size_t capacity = std::min(test->mReadChunkSize, test->mReadAllowance); + nsTArray<uint8_t> array(capacity); + size_t read; + + nr_socket_read(test->nr_socket_, (char*)array.Elements(), array.Capacity(), + &read, 0); + + ASSERT_TRUE(read <= array.Capacity()); + ASSERT_TRUE(test->mReadAllowance >= read); + + array.SetLength(read); + test->mData.AppendElements(array); + test->mReadAllowance -= read; + + // We may read more bytes each time we're called. This way we can ensure we + // consume buffers partially and across multiple buffers. + test->mReadChunkSize += test->mReadChunkSizeIncrement; + + if (test->mReadAllowance > 0) { + NR_ASYNC_WAIT(s, how, &NrTcpSocketTest::readable_cb, cb_arg); + } + } + + static void writable_cb(NR_SOCKET s, int how, void* cb_arg) { + NrTcpSocketTest* test = (NrTcpSocketTest*)cb_arg; + test->mConnected = true; + } + + const std::string DataString() { + return std::string((char*)mData.Elements(), mData.Length()); + } + + protected: + RefPtr<NrTcpSocket> mSProxy; + nr_socket* nr_socket_; + + nsTArray<uint8_t> mData; + nsTArray<uint8_t> mEmptyArray; + + uint32_t mReadChunkSize; + uint32_t mReadChunkSizeIncrement; + uint32_t mReadAllowance; + + bool mConnected; +}; + +TEST_F(NrTcpSocketTest, TestCreate) {} + +TEST_F(NrTcpSocketTest, TestConnected) { + ASSERT_TRUE(!mConnected); + + NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_WRITE, &NrTcpSocketTest::writable_cb, + this); + + // still not connected just registered for writes... + ASSERT_TRUE(!mConnected); + + mSProxy->OnConnected("http"_ns); + + ASSERT_TRUE(mConnected); +} + +TEST_F(NrTcpSocketTest, TestRead) { + nsTArray<uint8_t> array; + array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length()); + + NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb, + this); + // this will read 0 bytes here + mSProxy->OnRead(std::move(array)); + + ASSERT_EQ(kHelloMessage.length(), mSProxy->CountUnreadBytes()); + + // callback is still set but terminated due to 0 byte read + // start callbacks again (first read is 0 then 1,2,3,...) + mSProxy->OnRead(std::move(mEmptyArray)); + + ASSERT_EQ(kHelloMessage.length(), mData.Length()); + ASSERT_EQ(kHelloMessage, DataString()); +} + +TEST_F(NrTcpSocketTest, TestReadConstantConsumeSize) { + std::string data; + + // triangle number + const int kCount = 32; + + // ~17kb + // triangle number formula n*(n+1)/2 + for (int i = 0; i < kCount * (kCount + 1) / 2; ++i) { + data += kHelloMessage; + } + + // decreasing buffer sizes + for (int i = 0, start = 0; i < kCount; ++i) { + int length = (kCount - i) * kHelloMessage.length(); + + nsTArray<uint8_t> array; + array.AppendElements(data.c_str() + start, length); + start += length; + + mSProxy->OnRead(std::move(array)); + } + + ASSERT_EQ(data.length(), mSProxy->CountUnreadBytes()); + + // read same amount each callback + mReadChunkSize = 128; + mReadChunkSizeIncrement = 0; + NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb, + this); + + ASSERT_EQ(data.length(), mSProxy->CountUnreadBytes()); + + // start callbacks + mSProxy->OnRead(std::move(mEmptyArray)); + + ASSERT_EQ(data.length(), mData.Length()); + ASSERT_EQ(data, DataString()); +} + +TEST_F(NrTcpSocketTest, TestReadNone) { + char buf[4096]; + size_t read = 0; + int r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0); + + ASSERT_EQ(R_WOULDBLOCK, r); + + nsTArray<uint8_t> array; + array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length()); + mSProxy->OnRead(std::move(array)); + + ASSERT_EQ(kHelloMessage.length(), mSProxy->CountUnreadBytes()); + + r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0); + + ASSERT_EQ(0, r); + ASSERT_EQ(kHelloMessage.length(), read); + ASSERT_EQ(kHelloMessage, std::string(buf, read)); +} + +TEST_F(NrTcpSocketTest, TestReadMultipleSizes) { + using namespace std; + + string data; + // 515 * kHelloMessage.length() == 17510 + const size_t kCount = 515; + // randomly generated numbers, sums to 17510, 20 numbers + vector<int> varyingSizes = {404, 622, 1463, 1597, 1676, 389, 389, + 1272, 781, 81, 1030, 1450, 256, 812, + 1571, 29, 1045, 911, 643, 1089}; + + // changing varyingSizes or the test message breaks this so check here + ASSERT_EQ(kCount, 17510 / kHelloMessage.length()); + ASSERT_EQ(17510, accumulate(varyingSizes.begin(), varyingSizes.end(), 0)); + + // ~17kb + for (size_t i = 0; i < kCount; ++i) { + data += kHelloMessage; + } + + nsTArray<uint8_t> array; + array.AppendElements(data.c_str(), data.length()); + + for (int amountToRead : varyingSizes) { + nsTArray<uint8_t> buffer; + buffer.AppendElements(array.Elements(), amountToRead); + array.RemoveElementsAt(0, amountToRead); + mSProxy->OnRead(std::move(buffer)); + } + + ASSERT_EQ(data.length(), mSProxy->CountUnreadBytes()); + + // don't need to read 0 on the first read, so start at 1 and keep going + mReadChunkSize = 1; + NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb, + this); + // start callbacks + mSProxy->OnRead(std::move(mEmptyArray)); + + ASSERT_EQ(data.length(), mData.Length()); + ASSERT_EQ(data, DataString()); +} + +TEST_F(NrTcpSocketTest, TestReadConsumeReadDrain) { + std::string data; + // ~26kb total; should be even + const int kCount = 512; + + // there's some division by 2 here so check that kCount is even + ASSERT_EQ(0, kCount % 2); + + for (int i = 0; i < kCount; ++i) { + data += kHelloMessage; + nsTArray<uint8_t> array; + array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length()); + mSProxy->OnRead(std::move(array)); + } + + // read half at first + mReadAllowance = kCount / 2 * kHelloMessage.length(); + // start by reading 1 byte + mReadChunkSize = 1; + NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb, + this); + mSProxy->OnRead(std::move(mEmptyArray)); + + ASSERT_EQ(data.length() / 2, mSProxy->CountUnreadBytes()); + ASSERT_EQ(data.length() / 2, mData.Length()); + + // fill read buffer back up + for (int i = 0; i < kCount / 2; ++i) { + data += kHelloMessage; + nsTArray<uint8_t> array; + array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length()); + mSProxy->OnRead(std::move(array)); + } + + // remove read limit + mReadAllowance = -1; + // used entire read allowance so we need to setup a new await + NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb, + this); + // start callbacks + mSProxy->OnRead(std::move(mEmptyArray)); + + ASSERT_EQ(data.length(), mData.Length()); + ASSERT_EQ(data, DataString()); +} diff --git a/dom/media/webrtc/transport/test/rlogconnector_unittest.cpp b/dom/media/webrtc/transport/test/rlogconnector_unittest.cpp new file mode 100644 index 0000000000..93fabae481 --- /dev/null +++ b/dom/media/webrtc/transport/test/rlogconnector_unittest.cpp @@ -0,0 +1,255 @@ +/* -*- 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: bcampen@mozilla.com */ + +#include "rlogconnector.h" + +extern "C" { +#include "registry.h" +#include "r_log.h" +} + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" + +#include <deque> +#include <string> +#include <vector> + +using mozilla::RLogConnector; + +int NR_LOG_TEST = 0; + +class RLogConnectorTest : public ::testing::Test { + public: + RLogConnectorTest() { Init(); } + + ~RLogConnectorTest() { Free(); } + + static void SetUpTestCase() { + NR_reg_init(NR_REG_MODE_LOCAL); + r_log_init(); + /* Would be nice to be able to unregister in the fixture */ + const char* facility = "rlogconnector_test"; + r_log_register(const_cast<char*>(facility), &NR_LOG_TEST); + } + + void Init() { RLogConnector::CreateInstance(); } + + void Free() { RLogConnector::DestroyInstance(); } + + void ReInit() { + Free(); + Init(); + } +}; + +TEST_F(RLogConnectorTest, TestGetFree) { + RLogConnector* instance = RLogConnector::GetInstance(); + ASSERT_NE(nullptr, instance); +} + +TEST_F(RLogConnectorTest, TestFilterEmpty) { + std::deque<std::string> logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + ASSERT_EQ(0U, logs.size()); +} + +TEST_F(RLogConnectorTest, TestBasicFilter) { + r_log(NR_LOG_TEST, LOG_INFO, "Test"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->Filter("Test", 0, &logs); + ASSERT_EQ(1U, logs.size()); +} + +TEST_F(RLogConnectorTest, TestBasicFilterContent) { + r_log(NR_LOG_TEST, LOG_INFO, "Test"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->Filter("Test", 0, &logs); + ASSERT_EQ("Test", logs.back()); +} + +TEST_F(RLogConnectorTest, TestFilterAnyFrontMatch) { + r_log(NR_LOG_TEST, LOG_INFO, "Test"); + std::vector<std::string> substrings; + substrings.push_back("foo"); + substrings.push_back("Test"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->FilterAny(substrings, 0, &logs); + ASSERT_EQ("Test", logs.back()); +} + +TEST_F(RLogConnectorTest, TestFilterAnyBackMatch) { + r_log(NR_LOG_TEST, LOG_INFO, "Test"); + std::vector<std::string> substrings; + substrings.push_back("Test"); + substrings.push_back("foo"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->FilterAny(substrings, 0, &logs); + ASSERT_EQ("Test", logs.back()); +} + +TEST_F(RLogConnectorTest, TestFilterAnyBothMatch) { + r_log(NR_LOG_TEST, LOG_INFO, "Test"); + std::vector<std::string> substrings; + substrings.push_back("Tes"); + substrings.push_back("est"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->FilterAny(substrings, 0, &logs); + ASSERT_EQ("Test", logs.back()); +} + +TEST_F(RLogConnectorTest, TestFilterAnyNeitherMatch) { + r_log(NR_LOG_TEST, LOG_INFO, "Test"); + std::vector<std::string> substrings; + substrings.push_back("tes"); + substrings.push_back("esT"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->FilterAny(substrings, 0, &logs); + ASSERT_EQ(0U, logs.size()); +} + +TEST_F(RLogConnectorTest, TestAllMatch) { + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + ASSERT_EQ(2U, logs.size()); +} + +TEST_F(RLogConnectorTest, TestOrder) { + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + ASSERT_EQ("Test2", logs.back()); + ASSERT_EQ("Test1", logs.front()); +} + +TEST_F(RLogConnectorTest, TestNoMatch) { + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->Filter("foo", 0, &logs); + ASSERT_EQ(0U, logs.size()); +} + +TEST_F(RLogConnectorTest, TestSubstringFilter) { + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->Filter("t1", 0, &logs); + ASSERT_EQ(1U, logs.size()); + ASSERT_EQ("Test1", logs.back()); +} + +TEST_F(RLogConnectorTest, TestFilterLimit) { + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + r_log(NR_LOG_TEST, LOG_INFO, "Test3"); + r_log(NR_LOG_TEST, LOG_INFO, "Test4"); + r_log(NR_LOG_TEST, LOG_INFO, "Test5"); + r_log(NR_LOG_TEST, LOG_INFO, "Test6"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->Filter("Test", 2, &logs); + ASSERT_EQ(2U, logs.size()); + ASSERT_EQ("Test6", logs.back()); + ASSERT_EQ("Test5", logs.front()); +} + +TEST_F(RLogConnectorTest, TestFilterAnyLimit) { + r_log(NR_LOG_TEST, LOG_INFO, "TestOne"); + r_log(NR_LOG_TEST, LOG_INFO, "TestTwo"); + r_log(NR_LOG_TEST, LOG_INFO, "TestThree"); + r_log(NR_LOG_TEST, LOG_INFO, "TestFour"); + r_log(NR_LOG_TEST, LOG_INFO, "TestFive"); + r_log(NR_LOG_TEST, LOG_INFO, "TestSix"); + std::vector<std::string> substrings; + // Matches Two, Three, Four, and Six + substrings.push_back("tT"); + substrings.push_back("o"); + substrings.push_back("r"); + substrings.push_back("S"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->FilterAny(substrings, 2, &logs); + ASSERT_EQ(2U, logs.size()); + ASSERT_EQ("TestSix", logs.back()); + ASSERT_EQ("TestFour", logs.front()); +} + +TEST_F(RLogConnectorTest, TestLimit) { + RLogConnector::GetInstance()->SetLogLimit(3); + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + r_log(NR_LOG_TEST, LOG_INFO, "Test3"); + r_log(NR_LOG_TEST, LOG_INFO, "Test4"); + r_log(NR_LOG_TEST, LOG_INFO, "Test5"); + r_log(NR_LOG_TEST, LOG_INFO, "Test6"); + std::deque<std::string> logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + ASSERT_EQ(3U, logs.size()); + ASSERT_EQ("Test6", logs.back()); + ASSERT_EQ("Test4", logs.front()); +} + +TEST_F(RLogConnectorTest, TestLimitBulkDiscard) { + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + r_log(NR_LOG_TEST, LOG_INFO, "Test3"); + r_log(NR_LOG_TEST, LOG_INFO, "Test4"); + r_log(NR_LOG_TEST, LOG_INFO, "Test5"); + r_log(NR_LOG_TEST, LOG_INFO, "Test6"); + RLogConnector::GetInstance()->SetLogLimit(3); + std::deque<std::string> logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + ASSERT_EQ(3U, logs.size()); + ASSERT_EQ("Test6", logs.back()); + ASSERT_EQ("Test4", logs.front()); +} + +TEST_F(RLogConnectorTest, TestIncreaseLimit) { + RLogConnector::GetInstance()->SetLogLimit(3); + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + r_log(NR_LOG_TEST, LOG_INFO, "Test3"); + r_log(NR_LOG_TEST, LOG_INFO, "Test4"); + r_log(NR_LOG_TEST, LOG_INFO, "Test5"); + r_log(NR_LOG_TEST, LOG_INFO, "Test6"); + RLogConnector::GetInstance()->SetLogLimit(300); + std::deque<std::string> logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + ASSERT_EQ(3U, logs.size()); + ASSERT_EQ("Test6", logs.back()); + ASSERT_EQ("Test4", logs.front()); +} + +TEST_F(RLogConnectorTest, TestClear) { + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + r_log(NR_LOG_TEST, LOG_INFO, "Test3"); + r_log(NR_LOG_TEST, LOG_INFO, "Test4"); + r_log(NR_LOG_TEST, LOG_INFO, "Test5"); + r_log(NR_LOG_TEST, LOG_INFO, "Test6"); + RLogConnector::GetInstance()->SetLogLimit(0); + RLogConnector::GetInstance()->SetLogLimit(4096); + std::deque<std::string> logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + ASSERT_EQ(0U, logs.size()); +} + +TEST_F(RLogConnectorTest, TestReInit) { + r_log(NR_LOG_TEST, LOG_INFO, "Test1"); + r_log(NR_LOG_TEST, LOG_INFO, "Test2"); + r_log(NR_LOG_TEST, LOG_INFO, "Test3"); + r_log(NR_LOG_TEST, LOG_INFO, "Test4"); + r_log(NR_LOG_TEST, LOG_INFO, "Test5"); + r_log(NR_LOG_TEST, LOG_INFO, "Test6"); + ReInit(); + std::deque<std::string> logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + ASSERT_EQ(0U, logs.size()); +} diff --git a/dom/media/webrtc/transport/test/runnable_utils_unittest.cpp b/dom/media/webrtc/transport/test/runnable_utils_unittest.cpp new file mode 100644 index 0000000000..c673139867 --- /dev/null +++ b/dom/media/webrtc/transport/test/runnable_utils_unittest.cpp @@ -0,0 +1,343 @@ +/* -*- 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 "nsCOMPtr.h" +#include "nsNetCID.h" + +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" + +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" + +#include "runnable_utils.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; + +namespace { + +// Helper used to make sure args are properly copied and/or moved. +struct CtorDtorState { + enum class State { Empty, Copy, Explicit, Move, Moved }; + + static const char* ToStr(const State& state) { + switch (state) { + case State::Empty: + return "empty"; + case State::Copy: + return "copy"; + case State::Explicit: + return "explicit"; + case State::Move: + return "move"; + case State::Moved: + return "moved"; + default: + return "unknown"; + } + } + + void DumpState() const { std::cerr << ToStr(state_) << std::endl; } + + CtorDtorState() { DumpState(); } + + explicit CtorDtorState(int* destroyed) + : dtor_count_(destroyed), state_(State::Explicit) { + DumpState(); + } + + CtorDtorState(const CtorDtorState& other) + : dtor_count_(other.dtor_count_), state_(State::Copy) { + DumpState(); + } + + // Clear the other's dtor counter so it's not counted if moved. + CtorDtorState(CtorDtorState&& other) + : dtor_count_(std::exchange(other.dtor_count_, nullptr)), + state_(State::Move) { + other.state_ = State::Moved; + DumpState(); + } + + ~CtorDtorState() { + const char* const state = ToStr(state_); + std::cerr << "Destructor called with end state: " << state << std::endl; + + if (dtor_count_) { + ++*dtor_count_; + } + } + + int* dtor_count_ = nullptr; + State state_ = State::Empty; +}; + +class Destructor { + private: + ~Destructor() { + std::cerr << "Destructor called" << std::endl; + *destroyed_ = true; + } + + public: + explicit Destructor(bool* destroyed) : destroyed_(destroyed) {} + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Destructor) + + private: + bool* destroyed_; +}; + +class TargetClass { + public: + explicit TargetClass(int* ran) : ran_(ran) {} + + void m1(int x) { + std::cerr << __FUNCTION__ << " " << x << std::endl; + *ran_ = 1; + } + + void m2(int x, int y) { + std::cerr << __FUNCTION__ << " " << x << " " << y << std::endl; + *ran_ = 2; + } + + void m1set(bool* z) { + std::cerr << __FUNCTION__ << std::endl; + *z = true; + } + int return_int(int x) { + std::cerr << __FUNCTION__ << std::endl; + return x; + } + + void destructor_target_ref(RefPtr<Destructor> destructor) {} + + int* ran_; +}; + +class RunnableArgsTest : public MtransportTest { + public: + RunnableArgsTest() : MtransportTest(), ran_(0), cl_(&ran_) {} + + void Test1Arg() { + Runnable* r = WrapRunnable(&cl_, &TargetClass::m1, 1); + r->Run(); + ASSERT_EQ(1, ran_); + } + + void Test2Args() { + Runnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2); + r->Run(); + ASSERT_EQ(2, ran_); + } + + private: + int ran_; + TargetClass cl_; +}; + +class DispatchTest : public MtransportTest { + public: + DispatchTest() : MtransportTest(), ran_(0), cl_(&ran_) {} + + void SetUp() { + MtransportTest::SetUp(); + + nsresult rv; + target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + } + + void Test1Arg() { + Runnable* r = WrapRunnable(&cl_, &TargetClass::m1, 1); + target_->Dispatch(r, NS_DISPATCH_SYNC); + ASSERT_EQ(1, ran_); + } + + void Test2Args() { + Runnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2); + target_->Dispatch(r, NS_DISPATCH_SYNC); + ASSERT_EQ(2, ran_); + } + + void Test1Set() { + bool x = false; + target_->Dispatch(WrapRunnable(&cl_, &TargetClass::m1set, &x), + NS_DISPATCH_SYNC); + ASSERT_TRUE(x); + } + + void TestRet() { + int z; + int x = 10; + + target_->Dispatch(WrapRunnableRet(&z, &cl_, &TargetClass::return_int, x), + NS_DISPATCH_SYNC); + ASSERT_EQ(10, z); + } + + protected: + int ran_; + TargetClass cl_; + nsCOMPtr<nsIEventTarget> target_; +}; + +TEST_F(RunnableArgsTest, OneArgument) { Test1Arg(); } + +TEST_F(RunnableArgsTest, TwoArguments) { Test2Args(); } + +TEST_F(DispatchTest, OneArgument) { Test1Arg(); } + +TEST_F(DispatchTest, TwoArguments) { Test2Args(); } + +TEST_F(DispatchTest, Test1Set) { Test1Set(); } + +TEST_F(DispatchTest, TestRet) { TestRet(); } + +void SetNonMethod(TargetClass* cl, int x) { cl->m1(x); } + +int SetNonMethodRet(TargetClass* cl, int x) { + cl->m1(x); + + return x; +} + +TEST_F(DispatchTest, TestNonMethod) { + test_utils_->sts_target()->Dispatch(WrapRunnableNM(SetNonMethod, &cl_, 10), + NS_DISPATCH_SYNC); + + ASSERT_EQ(1, ran_); +} + +TEST_F(DispatchTest, TestNonMethodRet) { + int z; + + test_utils_->sts_target()->Dispatch( + WrapRunnableNMRet(&z, SetNonMethodRet, &cl_, 10), NS_DISPATCH_SYNC); + + ASSERT_EQ(1, ran_); + ASSERT_EQ(10, z); +} + +TEST_F(DispatchTest, TestDestructorRef) { + bool destroyed = false; + { + RefPtr<Destructor> destructor = new Destructor(&destroyed); + target_->Dispatch( + WrapRunnable(&cl_, &TargetClass::destructor_target_ref, destructor), + NS_DISPATCH_SYNC); + ASSERT_FALSE(destroyed); + } + ASSERT_TRUE(destroyed); + + // Now try with a move. + destroyed = false; + { + RefPtr<Destructor> destructor = new Destructor(&destroyed); + target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target_ref, + std::move(destructor)), + NS_DISPATCH_SYNC); + ASSERT_TRUE(destroyed); + } +} + +TEST_F(DispatchTest, TestMove) { + int destroyed = 0; + { + CtorDtorState state(&destroyed); + + // Dispatch with: + // - moved arg + // - by-val capture in function should consume a move + // - expect destruction in the function scope + target_->Dispatch(WrapRunnableNM([](CtorDtorState s) {}, std::move(state)), + NS_DISPATCH_SYNC); + ASSERT_EQ(1, destroyed); + } + // Still shouldn't count when we go out of scope as it was moved. + ASSERT_EQ(1, destroyed); + + { + CtorDtorState state(&destroyed); + + // Dispatch with: + // - copied arg + // - by-val capture in function should consume a move + // - expect destruction in the function scope and call scope + target_->Dispatch(WrapRunnableNM([](CtorDtorState s) {}, state), + NS_DISPATCH_SYNC); + ASSERT_EQ(2, destroyed); + } + // Original state should be destroyed + ASSERT_EQ(3, destroyed); + + { + CtorDtorState state(&destroyed); + + // Dispatch with: + // - moved arg + // - by-ref in function should accept a moved arg + // - expect destruction in the wrapper invocation scope + target_->Dispatch( + WrapRunnableNM([](const CtorDtorState& s) {}, std::move(state)), + NS_DISPATCH_SYNC); + ASSERT_EQ(4, destroyed); + } + // Still shouldn't count when we go out of scope as it was moved. + ASSERT_EQ(4, destroyed); + + { + CtorDtorState state(&destroyed); + + // Dispatch with: + // - moved arg + // - r-value function should accept a moved arg + // - expect destruction in the wrapper invocation scope + target_->Dispatch( + WrapRunnableNM([](CtorDtorState&& s) {}, std::move(state)), + NS_DISPATCH_SYNC); + ASSERT_EQ(5, destroyed); + } + // Still shouldn't count when we go out of scope as it was moved. + ASSERT_EQ(5, destroyed); +} + +TEST_F(DispatchTest, TestUniquePtr) { + // Test that holding the class in UniquePtr works + int ran = 0; + auto cl = MakeUnique<TargetClass>(&ran); + + target_->Dispatch(WrapRunnable(std::move(cl), &TargetClass::m1, 1), + NS_DISPATCH_SYNC); + ASSERT_EQ(1, ran); + + // Test that UniquePtr works as a param to the runnable + int destroyed = 0; + { + auto state = MakeUnique<CtorDtorState>(&destroyed); + + // Dispatch with: + // - moved arg + // - Function should move construct from arg + // - expect destruction in the wrapper invocation scope + target_->Dispatch( + WrapRunnableNM([](UniquePtr<CtorDtorState> s) {}, std::move(state)), + NS_DISPATCH_SYNC); + ASSERT_EQ(1, destroyed); + } + // Still shouldn't count when we go out of scope as it was moved. + ASSERT_EQ(1, destroyed); +} + +} // end of namespace diff --git a/dom/media/webrtc/transport/test/sctp_unittest.cpp b/dom/media/webrtc/transport/test/sctp_unittest.cpp new file mode 100644 index 0000000000..55451dfa6b --- /dev/null +++ b/dom/media/webrtc/transport/test/sctp_unittest.cpp @@ -0,0 +1,382 @@ +/* -*- 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 <string> + +#include "sigslot.h" + +#include "nsITimer.h" + +#include "transportflow.h" +#include "transportlayer.h" +#include "transportlayerloopback.h" + +#include "runnable_utils.h" +#include "usrsctp.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; + +static bool sctp_logging = false; +static int port_number = 5000; + +namespace { + +class TransportTestPeer; + +class SendPeriodic : public nsITimerCallback, public nsINamed { + public: + SendPeriodic(TransportTestPeer* peer, int to_send) + : peer_(peer), to_send_(to_send) {} + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITIMERCALLBACK + NS_DECL_NSINAMED + + protected: + virtual ~SendPeriodic() = default; + + TransportTestPeer* peer_; + int to_send_; +}; + +NS_IMPL_ISUPPORTS(SendPeriodic, nsITimerCallback, nsINamed) + +class TransportTestPeer : public sigslot::has_slots<> { + public: + TransportTestPeer(std::string name, int local_port, int remote_port, + MtransportTestUtils* utils) + : name_(name), + connected_(false), + sent_(0), + received_(0), + flow_(new TransportFlow()), + loopback_(new TransportLayerLoopback()), + sctp_(usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, receive_cb, + nullptr, 0, nullptr)), + timer_(NS_NewTimer()), + periodic_(nullptr), + test_utils_(utils) { + std::cerr << "Creating TransportTestPeer; flow=" + << static_cast<void*>(flow_.get()) << " local=" << local_port + << " remote=" << remote_port << std::endl; + + usrsctp_register_address(static_cast<void*>(this)); + int r = usrsctp_set_non_blocking(sctp_, 1); + EXPECT_GE(r, 0); + + struct linger l; + l.l_onoff = 1; + l.l_linger = 0; + r = usrsctp_setsockopt(sctp_, SOL_SOCKET, SO_LINGER, &l, + (socklen_t)sizeof(l)); + EXPECT_GE(r, 0); + + struct sctp_event subscription; + memset(&subscription, 0, sizeof(subscription)); + subscription.se_assoc_id = SCTP_ALL_ASSOC; + subscription.se_on = 1; + subscription.se_type = SCTP_ASSOC_CHANGE; + r = usrsctp_setsockopt(sctp_, IPPROTO_SCTP, SCTP_EVENT, &subscription, + sizeof(subscription)); + EXPECT_GE(r, 0); + + memset(&local_addr_, 0, sizeof(local_addr_)); + local_addr_.sconn_family = AF_CONN; +#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && \ + !defined(__Userspace_os_Android) + local_addr_.sconn_len = sizeof(struct sockaddr_conn); +#endif + local_addr_.sconn_port = htons(local_port); + local_addr_.sconn_addr = static_cast<void*>(this); + + memset(&remote_addr_, 0, sizeof(remote_addr_)); + remote_addr_.sconn_family = AF_CONN; +#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && \ + !defined(__Userspace_os_Android) + remote_addr_.sconn_len = sizeof(struct sockaddr_conn); +#endif + remote_addr_.sconn_port = htons(remote_port); + remote_addr_.sconn_addr = static_cast<void*>(this); + + nsresult res; + res = loopback_->Init(); + EXPECT_EQ((nsresult)NS_OK, res); + } + + ~TransportTestPeer() { + std::cerr << "Destroying sctp connection flow=" + << static_cast<void*>(flow_.get()) << std::endl; + usrsctp_close(sctp_); + usrsctp_deregister_address(static_cast<void*>(this)); + + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &TransportTestPeer::Disconnect_s), NS_DISPATCH_SYNC); + + std::cerr << "~TransportTestPeer() completed" << std::endl; + } + + void ConnectSocket(TransportTestPeer* peer) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &TransportTestPeer::ConnectSocket_s, peer), + NS_DISPATCH_SYNC); + } + + void ConnectSocket_s(TransportTestPeer* peer) { + loopback_->Connect(peer->loopback_); + ASSERT_EQ((nsresult)NS_OK, loopback_->Init()); + flow_->PushLayer(loopback_); + + loopback_->SignalPacketReceived.connect(this, + &TransportTestPeer::PacketReceived); + + // SCTP here! + ASSERT_TRUE(sctp_); + std::cerr << "Calling usrsctp_bind()" << std::endl; + int r = + usrsctp_bind(sctp_, reinterpret_cast<struct sockaddr*>(&local_addr_), + sizeof(local_addr_)); + ASSERT_GE(0, r); + + std::cerr << "Calling usrsctp_connect()" << std::endl; + r = usrsctp_connect(sctp_, + reinterpret_cast<struct sockaddr*>(&remote_addr_), + sizeof(remote_addr_)); + ASSERT_GE(0, r); + } + + void Disconnect_s() { + disconnect_all(); + if (flow_) { + flow_ = nullptr; + } + } + + void Disconnect() { loopback_->Disconnect(); } + + void StartTransfer(size_t to_send) { + periodic_ = new SendPeriodic(this, to_send); + timer_->SetTarget(test_utils_->sts_target()); + timer_->InitWithCallback(periodic_, 10, nsITimer::TYPE_REPEATING_SLACK); + } + + void SendOne() { + unsigned char buf[100]; + memset(buf, sent_ & 0xff, sizeof(buf)); + + struct sctp_sndinfo info; + info.snd_sid = 1; + info.snd_flags = 0; + info.snd_ppid = 50; // What the heck is this? + info.snd_context = 0; + info.snd_assoc_id = 0; + + int r = usrsctp_sendv(sctp_, buf, sizeof(buf), nullptr, 0, + static_cast<void*>(&info), sizeof(info), + SCTP_SENDV_SNDINFO, 0); + ASSERT_TRUE(r >= 0); + ASSERT_EQ(sizeof(buf), (size_t)r); + + ++sent_; + } + + int sent() const { return sent_; } + int received() const { return received_; } + bool connected() const { return connected_; } + + static TransportResult SendPacket_s(UniquePtr<MediaPacket> packet, + const RefPtr<TransportFlow>& flow, + TransportLayer* layer) { + return layer->SendPacket(*packet); + } + + TransportResult SendPacket(const unsigned char* data, size_t len) { + UniquePtr<MediaPacket> packet(new MediaPacket); + packet->Copy(data, len); + + // Uses DISPATCH_NORMAL to avoid possible deadlocks when we're called + // from MainThread especially during shutdown (same as DataChannels). + // RUN_ON_THREAD short-circuits if already on the STS thread, which is + // normal for most transfers outside of connect() and close(). Passes + // a refptr to flow_ to avoid any async deletion issues (since we can't + // make 'this' into a refptr as it isn't refcounted) + RUN_ON_THREAD(test_utils_->sts_target(), + WrapRunnableNM(&TransportTestPeer::SendPacket_s, + std::move(packet), flow_, loopback_), + NS_DISPATCH_NORMAL); + + return 0; + } + + void PacketReceived(TransportLayer* layer, MediaPacket& packet) { + std::cerr << "Received " << packet.len() << " bytes" << std::endl; + + // Pass the data to SCTP + + usrsctp_conninput(static_cast<void*>(this), packet.data(), packet.len(), 0); + } + + // Process SCTP notification + void Notification(union sctp_notification* msg, size_t len) { + ASSERT_EQ(msg->sn_header.sn_length, len); + + if (msg->sn_header.sn_type == SCTP_ASSOC_CHANGE) { + struct sctp_assoc_change* change = &msg->sn_assoc_change; + + if (change->sac_state == SCTP_COMM_UP) { + std::cerr << "Connection up" << std::endl; + SetConnected(true); + } else { + std::cerr << "Connection down" << std::endl; + SetConnected(false); + } + } + } + + void SetConnected(bool state) { connected_ = state; } + + static int conn_output(void* addr, void* buffer, size_t length, uint8_t tos, + uint8_t set_df) { + TransportTestPeer* peer = static_cast<TransportTestPeer*>(addr); + + peer->SendPacket(static_cast<unsigned char*>(buffer), length); + + return 0; + } + + static int receive_cb(struct socket* sock, union sctp_sockstore addr, + void* data, size_t datalen, struct sctp_rcvinfo rcv, + int flags, void* ulp_info) { + TransportTestPeer* me = + static_cast<TransportTestPeer*>(addr.sconn.sconn_addr); + MOZ_ASSERT(me); + + if (flags & MSG_NOTIFICATION) { + union sctp_notification* notif = + static_cast<union sctp_notification*>(data); + + me->Notification(notif, datalen); + return 0; + } + + me->received_ += datalen; + + std::cerr << "receive_cb: sock " << sock << " data " << data << "(" + << datalen << ") total received bytes = " << me->received_ + << std::endl; + + return 0; + } + + private: + std::string name_; + bool connected_; + size_t sent_; + size_t received_; + // Owns the TransportLayerLoopback, but basically does nothing else. + RefPtr<TransportFlow> flow_; + TransportLayerLoopback* loopback_; + + struct sockaddr_conn local_addr_; + struct sockaddr_conn remote_addr_; + struct socket* sctp_; + nsCOMPtr<nsITimer> timer_; + RefPtr<SendPeriodic> periodic_; + MtransportTestUtils* test_utils_; +}; + +// Implemented here because it calls a method of TransportTestPeer +NS_IMETHODIMP SendPeriodic::Notify(nsITimer* timer) { + peer_->SendOne(); + --to_send_; + if (!to_send_) { + timer->Cancel(); + } + return NS_OK; +} + +NS_IMETHODIMP +SendPeriodic::GetName(nsACString& aName) { + aName.AssignLiteral("SendPeriodic"); + return NS_OK; +} + +class SctpTransportTest : public MtransportTest { + public: + SctpTransportTest() = default; + + ~SctpTransportTest() = default; + + static void debug_printf(const char* format, ...) { + va_list ap; + + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + } + + static void SetUpTestCase() { + if (sctp_logging) { + usrsctp_init(0, &TransportTestPeer::conn_output, debug_printf); + usrsctp_sysctl_set_sctp_debug_on(0xffffffff); + } else { + usrsctp_init(0, &TransportTestPeer::conn_output, nullptr); + } + } + + void TearDown() override { + if (p1_) p1_->Disconnect(); + if (p2_) p2_->Disconnect(); + delete p1_; + delete p2_; + + MtransportTest::TearDown(); + } + + void ConnectSocket(int p1port = 0, int p2port = 0) { + if (!p1port) p1port = port_number++; + if (!p2port) p2port = port_number++; + + p1_ = new TransportTestPeer("P1", p1port, p2port, test_utils_); + p2_ = new TransportTestPeer("P2", p2port, p1port, test_utils_); + + p1_->ConnectSocket(p2_); + p2_->ConnectSocket(p1_); + ASSERT_TRUE_WAIT(p1_->connected(), 2000); + ASSERT_TRUE_WAIT(p2_->connected(), 2000); + } + + void TestTransfer(int expected = 1) { + std::cerr << "Starting trasnsfer test" << std::endl; + p1_->StartTransfer(expected); + ASSERT_TRUE_WAIT(p1_->sent() == expected, 10000); + ASSERT_TRUE_WAIT(p2_->received() == (expected * 100), 10000); + std::cerr << "P2 received " << p2_->received() << std::endl; + } + + protected: + TransportTestPeer* p1_ = nullptr; + TransportTestPeer* p2_ = nullptr; +}; + +TEST_F(SctpTransportTest, TestConnect) { ConnectSocket(); } + +TEST_F(SctpTransportTest, TestConnectSymmetricalPorts) { + ConnectSocket(5002, 5002); +} + +TEST_F(SctpTransportTest, TestTransfer) { + ConnectSocket(); + TestTransfer(50); +} + +} // end namespace diff --git a/dom/media/webrtc/transport/test/simpletokenbucket_unittest.cpp b/dom/media/webrtc/transport/test/simpletokenbucket_unittest.cpp new file mode 100644 index 0000000000..66622d795b --- /dev/null +++ b/dom/media/webrtc/transport/test/simpletokenbucket_unittest.cpp @@ -0,0 +1,114 @@ +/* -*- 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: bcampen@mozilla.com */ + +#include "simpletokenbucket.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" + +using mozilla::SimpleTokenBucket; + +class TestSimpleTokenBucket : public SimpleTokenBucket { + public: + TestSimpleTokenBucket(size_t bucketSize, size_t tokensPerSecond) + : SimpleTokenBucket(bucketSize, tokensPerSecond) {} + + void fastForward(int32_t timeMilliSeconds) { + if (timeMilliSeconds >= 0) { + last_time_tokens_added_ -= PR_MillisecondsToInterval(timeMilliSeconds); + } else { + last_time_tokens_added_ += PR_MillisecondsToInterval(-timeMilliSeconds); + } + } +}; + +TEST(SimpleTokenBucketTest, TestConstruct) +{ TestSimpleTokenBucket b(10, 1); } + +TEST(SimpleTokenBucketTest, TestGet) +{ + TestSimpleTokenBucket b(10, 1); + ASSERT_EQ(5U, b.getTokens(5)); +} + +TEST(SimpleTokenBucketTest, TestGetAll) +{ + TestSimpleTokenBucket b(10, 1); + ASSERT_EQ(10U, b.getTokens(10)); +} + +TEST(SimpleTokenBucketTest, TestGetInsufficient) +{ + TestSimpleTokenBucket b(10, 1); + ASSERT_EQ(5U, b.getTokens(5)); + ASSERT_EQ(5U, b.getTokens(6)); +} + +TEST(SimpleTokenBucketTest, TestGetBucketCount) +{ + TestSimpleTokenBucket b(10, 1); + ASSERT_EQ(10U, b.getTokens(UINT32_MAX)); + ASSERT_EQ(5U, b.getTokens(5)); + ASSERT_EQ(5U, b.getTokens(UINT32_MAX)); +} + +TEST(SimpleTokenBucketTest, TestTokenRefill) +{ + TestSimpleTokenBucket b(10, 1); + ASSERT_EQ(5U, b.getTokens(5)); + b.fastForward(1000); + ASSERT_EQ(6U, b.getTokens(6)); +} + +TEST(SimpleTokenBucketTest, TestNoTimeWasted) +{ + // Makes sure that when the time elapsed is insufficient to add any + // tokens to the bucket, the internal timestamp that is used in this + // calculation is not updated (ie; two subsequent 0.5 second elapsed times + // counts as a full second) + TestSimpleTokenBucket b(10, 1); + ASSERT_EQ(5U, b.getTokens(5)); + b.fastForward(500); + ASSERT_EQ(5U, b.getTokens(6)); + b.fastForward(500); + ASSERT_EQ(6U, b.getTokens(6)); +} + +TEST(SimpleTokenBucketTest, TestNegativeTime) +{ + TestSimpleTokenBucket b(10, 1); + b.fastForward(-1000); + // Make sure we don't end up with an invalid number of tokens, but otherwise + // permit anything. + ASSERT_GT(11U, b.getTokens(100)); +} + +TEST(SimpleTokenBucketTest, TestEmptyBucket) +{ + TestSimpleTokenBucket b(10, 1); + ASSERT_EQ(10U, b.getTokens(10)); + ASSERT_EQ(0U, b.getTokens(10)); +} + +TEST(SimpleTokenBucketTest, TestEmptyThenFillBucket) +{ + TestSimpleTokenBucket b(10, 1); + ASSERT_EQ(10U, b.getTokens(10)); + ASSERT_EQ(0U, b.getTokens(1)); + b.fastForward(50000); + ASSERT_EQ(10U, b.getTokens(10)); +} + +TEST(SimpleTokenBucketTest, TestNoOverflow) +{ + TestSimpleTokenBucket b(10, 1); + ASSERT_EQ(10U, b.getTokens(10)); + ASSERT_EQ(0U, b.getTokens(1)); + b.fastForward(50000); + ASSERT_EQ(10U, b.getTokens(11)); +} diff --git a/dom/media/webrtc/transport/test/sockettransportservice_unittest.cpp b/dom/media/webrtc/transport/test/sockettransportservice_unittest.cpp new file mode 100644 index 0000000000..ffa87fe91f --- /dev/null +++ b/dom/media/webrtc/transport/test/sockettransportservice_unittest.cpp @@ -0,0 +1,181 @@ +/* -*- 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 "prio.h" + +#include "nsCOMPtr.h" +#include "nsNetCID.h" + +#include "nsISocketTransportService.h" + +#include "nsASocketHandler.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; + +namespace { +class SocketTransportServiceTest : public MtransportTest { + public: + SocketTransportServiceTest() + : MtransportTest(), + received_(0), + readpipe_(nullptr), + writepipe_(nullptr), + registered_(false) {} + + ~SocketTransportServiceTest() { + if (readpipe_) PR_Close(readpipe_); + if (writepipe_) PR_Close(writepipe_); + } + + void SetUp(); + void RegisterHandler(); + void SendEvent(); + void SendPacket(); + + void ReceivePacket() { ++received_; } + + void ReceiveEvent() { ++received_; } + + size_t Received() { return received_; } + + private: + nsCOMPtr<nsISocketTransportService> stservice_; + nsCOMPtr<nsIEventTarget> target_; + size_t received_; + PRFileDesc* readpipe_; + PRFileDesc* writepipe_; + bool registered_; +}; + +// Received an event. +class EventReceived : public Runnable { + public: + explicit EventReceived(SocketTransportServiceTest* test) + : Runnable("EventReceived"), test_(test) {} + + NS_IMETHOD Run() override { + test_->ReceiveEvent(); + return NS_OK; + } + + SocketTransportServiceTest* test_; +}; + +// Register our listener on the socket +class RegisterEvent : public Runnable { + public: + explicit RegisterEvent(SocketTransportServiceTest* test) + : Runnable("RegisterEvent"), test_(test) {} + + NS_IMETHOD Run() override { + test_->RegisterHandler(); + return NS_OK; + } + + SocketTransportServiceTest* test_; +}; + +class SocketHandler : public nsASocketHandler { + public: + explicit SocketHandler(SocketTransportServiceTest* test) : test_(test) {} + + void OnSocketReady(PRFileDesc* fd, int16_t outflags) override { + unsigned char buf[1600]; + + int32_t rv; + rv = PR_Recv(fd, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT); + if (rv > 0) { + std::cerr << "Read " << rv << " bytes" << std::endl; + test_->ReceivePacket(); + } + } + + void OnSocketDetached(PRFileDesc* fd) override {} + + void IsLocal(bool* aIsLocal) override { + // TODO(jesup): better check? Does it matter? (likely no) + *aIsLocal = false; + } + + virtual uint64_t ByteCountSent() override { return 0; } + virtual uint64_t ByteCountReceived() override { return 0; } + + NS_DECL_ISUPPORTS + + protected: + virtual ~SocketHandler() = default; + + private: + SocketTransportServiceTest* test_; +}; + +NS_IMPL_ISUPPORTS0(SocketHandler) + +void SocketTransportServiceTest::SetUp() { + MtransportTest::SetUp(); + + // Get the transport service as a dispatch target + nsresult rv; + target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + // Get the transport service as a transport service + stservice_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + // Create a loopback pipe + PRStatus status = PR_CreatePipe(&readpipe_, &writepipe_); + ASSERT_EQ(status, PR_SUCCESS); + + // Register ourselves as a listener for the read side of the + // socket. The registration has to happen on the STS thread, + // hence this event stuff. + rv = target_->Dispatch(new RegisterEvent(this), 0); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + ASSERT_TRUE_WAIT(registered_, 10000); +} + +void SocketTransportServiceTest::RegisterHandler() { + nsresult rv; + + rv = stservice_->AttachSocket(readpipe_, new SocketHandler(this)); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + registered_ = true; +} + +void SocketTransportServiceTest::SendEvent() { + nsresult rv; + + rv = target_->Dispatch(new EventReceived(this), 0); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + ASSERT_TRUE_WAIT(Received() == 1, 10000); +} + +void SocketTransportServiceTest::SendPacket() { + unsigned char buffer[1024]; + memset(buffer, 0, sizeof(buffer)); + + int32_t status = PR_Write(writepipe_, buffer, sizeof(buffer)); + uint32_t size = status & 0xffff; + ASSERT_EQ(sizeof(buffer), size); +} + +// The unit tests themselves +TEST_F(SocketTransportServiceTest, SendEvent) { SendEvent(); } + +TEST_F(SocketTransportServiceTest, SendPacket) { SendPacket(); } + +} // end namespace diff --git a/dom/media/webrtc/transport/test/stunserver.cpp b/dom/media/webrtc/transport/test/stunserver.cpp new file mode 100644 index 0000000000..b5fce21e19 --- /dev/null +++ b/dom/media/webrtc/transport/test/stunserver.cpp @@ -0,0 +1,652 @@ +/* -*- 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 + +/* +Original code from nICEr and nrappkit. + +nICEr copyright: + +Copyright (c) 2007, Adobe Systems, Incorporated +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. + + +nrappkit copyright: + + Copyright (C) 2001-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Thu Dec 20 20:14:49 2001 +*/ +#include "logging.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "mediapacket.h" + +// mozilla/utils.h defines this as well +#ifdef UNIMPLEMENTED +# undef UNIMPLEMENTED +#endif + +extern "C" { +#include "nr_api.h" +#include "async_wait.h" +#include "async_timer.h" +#include "nr_socket.h" +#include "nr_socket_local.h" +#include "transport_addr.h" +#include "stun_util.h" +#include "registry.h" +#include "nr_socket_buffered_stun.h" +} + +#include "stunserver.h" + +#include <string> + +MOZ_MTLOG_MODULE("stunserver"); + +namespace mozilla { + +// Wrapper nr_socket which allows us to lie to the stun server about the +// IP address. +struct nr_socket_wrapped { + nr_socket* sock_; + nr_transport_addr addr_; +}; + +static int nr_socket_wrapped_destroy(void** objp) { + if (!objp || !*objp) return 0; + + nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(*objp); + *objp = nullptr; + + delete wrapped; + + return 0; +} + +static int nr_socket_wrapped_sendto(void* obj, const void* msg, size_t len, + int flags, const nr_transport_addr* addr) { + nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(obj); + + return nr_socket_sendto(wrapped->sock_, msg, len, flags, &wrapped->addr_); +} + +static int nr_socket_wrapped_recvfrom(void* obj, void* restrict buf, + size_t maxlen, size_t* len, int flags, + nr_transport_addr* addr) { + nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(obj); + + return nr_socket_recvfrom(wrapped->sock_, buf, maxlen, len, flags, addr); +} + +static int nr_socket_wrapped_getfd(void* obj, NR_SOCKET* fd) { + nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(obj); + + return nr_socket_getfd(wrapped->sock_, fd); +} + +static int nr_socket_wrapped_getaddr(void* obj, nr_transport_addr* addrp) { + nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(obj); + + return nr_socket_getaddr(wrapped->sock_, addrp); +} + +static int nr_socket_wrapped_close(void* obj) { MOZ_CRASH(); } + +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 = {2, + nr_socket_wrapped_destroy, + nr_socket_wrapped_sendto, + nr_socket_wrapped_recvfrom, + nr_socket_wrapped_getfd, + nr_socket_wrapped_getaddr, + nullptr, + nullptr, + nullptr, + nr_socket_wrapped_close, + nullptr, + nullptr}; + +int nr_socket_wrapped_create(nr_socket* inner, nr_socket** outp) { + auto wrapped = MakeUnique<nr_socket_wrapped>(); + + wrapped->sock_ = inner; + + int r = nr_socket_create_int(wrapped.get(), &nr_socket_wrapped_vtbl, outp); + if (r) return r; + + Unused << wrapped.release(); + return 0; +} + +// Instance static. +// Note: Calling Create() at static init time is not going to be safe, since +// we have no reason to expect this will be initted to a nullptr yet. +TestStunServer* TestStunServer::instance; +TestStunTcpServer* TestStunTcpServer::instance; +TestStunServer* TestStunServer::instance6; +TestStunTcpServer* TestStunTcpServer::instance6; +uint16_t TestStunServer::instance_port = 3478; +uint16_t TestStunTcpServer::instance_port = 3478; + +TestStunServer::~TestStunServer() { + // TODO(ekr@rtfm.com): Put this on the right thread. + + // Unhook callback from our listen socket. + if (listen_sock_) { + NR_SOCKET fd; + if (!nr_socket_getfd(listen_sock_, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + } + + // Free up stun context and network resources + nr_stun_server_ctx_destroy(&stun_server_); + nr_socket_destroy(&listen_sock_); + nr_socket_destroy(&send_sock_); + + // Make sure we aren't still waiting on a deferred response timer to pop + if (timer_handle_) NR_async_timer_cancel(timer_handle_); + + delete response_addr_; +} + +int TestStunServer::SetInternalPort(nr_local_addr* addr, uint16_t port) { + if (nr_transport_addr_set_port(&addr->addr, port)) { + MOZ_MTLOG(ML_ERROR, "Couldn't set port"); + return R_INTERNAL; + } + + if (nr_transport_addr_fmt_addr_string(&addr->addr)) { + MOZ_MTLOG(ML_ERROR, "Couldn't re-set addr string"); + return R_INTERNAL; + } + + return 0; +} + +int TestStunServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { + int r = SetInternalPort(addr, port); + + if (r) return r; + + if (nr_socket_local_create(nullptr, &addr->addr, &listen_sock_)) { + MOZ_MTLOG(ML_ERROR, "Couldn't create listen socket"); + return R_ALREADY; + } + + return 0; +} + +static int addressFamilyToIpVersion(int address_family) { + switch (address_family) { + case AF_INET: + return NR_IPV4; + case AF_INET6: + return NR_IPV6; + default: + MOZ_CRASH(); + } + return NR_IPV4; +} + +int TestStunServer::Initialize(int address_family) { + static const size_t max_addrs = 100; + nr_local_addr addrs[max_addrs]; + int addr_ct; + int r; + int i; + + r = nr_stun_find_local_addresses(addrs, max_addrs, &addr_ct); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't retrieve addresses"); + return R_INTERNAL; + } + + // removes duplicates and, based on prefs, loopback and link_local addrs + r = nr_stun_filter_local_addresses(addrs, &addr_ct); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't filter addresses"); + return R_INTERNAL; + } + + if (addr_ct < 1) { + MOZ_MTLOG(ML_ERROR, "No local addresses"); + return R_INTERNAL; + } + + for (i = 0; i < addr_ct; ++i) { + if (addrs[i].addr.ip_version == addressFamilyToIpVersion(address_family)) { + break; + } + } + + if (i == addr_ct) { + MOZ_MTLOG(ML_ERROR, "No local addresses of the configured IP version"); + return R_INTERNAL; + } + + int tries = 100; + while (tries--) { + // Bind on configured port (default 3478) + r = TryOpenListenSocket(&addrs[i], instance_port); + // We interpret R_ALREADY to mean the addr is probably in use. Try another. + // Otherwise, it either worked or it didn't, and we check below. + if (r != R_ALREADY) { + break; + } + ++instance_port; + } + + if (r) { + return R_INTERNAL; + } + + r = nr_socket_wrapped_create(listen_sock_, &send_sock_); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't create send socket"); + return R_INTERNAL; + } + + r = nr_stun_server_ctx_create(const_cast<char*>("Test STUN server"), + &stun_server_); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't create STUN server"); + return R_INTERNAL; + } + + // Cache the address and port. + char addr_string[INET6_ADDRSTRLEN]; + r = nr_transport_addr_get_addrstring(&addrs[i].addr, addr_string, + sizeof(addr_string)); + if (r) { + MOZ_MTLOG(ML_ERROR, + "Failed to convert listen addr to a string representation"); + return R_INTERNAL; + } + + listen_addr_ = addr_string; + listen_port_ = instance_port; + + return 0; +} + +UniquePtr<TestStunServer> TestStunServer::Create(int address_family) { + NR_reg_init(NR_REG_MODE_LOCAL); + + UniquePtr<TestStunServer> server(new TestStunServer()); + + if (server->Initialize(address_family)) return nullptr; + + NR_SOCKET fd; + int r = nr_socket_getfd(server->listen_sock_, &fd); + if (r) { + MOZ_MTLOG(ML_ERROR, "Couldn't get fd"); + return nullptr; + } + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, + server.get()); + + return server; +} + +void TestStunServer::ConfigurePort(uint16_t port) { instance_port = port; } + +TestStunServer* TestStunServer::GetInstance(int address_family) { + switch (address_family) { + case AF_INET: + if (!instance) instance = Create(address_family).release(); + + MOZ_ASSERT(instance); + return instance; + case AF_INET6: + if (!instance6) instance6 = Create(address_family).release(); + + return instance6; + default: + MOZ_CRASH(); + } +} + +void TestStunServer::ShutdownInstance() { + delete instance; + instance = nullptr; + delete instance6; + instance6 = nullptr; +} + +struct DeferredStunOperation { + DeferredStunOperation(TestStunServer* server, const char* data, size_t len, + nr_transport_addr* addr, nr_socket* sock) + : server_(server), buffer_(), sock_(sock) { + buffer_.Copy(reinterpret_cast<const uint8_t*>(data), len); + nr_transport_addr_copy(&addr_, addr); + } + + TestStunServer* server_; + MediaPacket buffer_; + nr_transport_addr addr_; + nr_socket* sock_; +}; + +void TestStunServer::Process(const uint8_t* msg, size_t len, + nr_transport_addr* addr, nr_socket* sock) { + if (!sock) { + sock = send_sock_; + } + + // Set the wrapped address so that the response goes to the right place. + nr_socket_wrapped_set_send_addr(sock, addr); + + nr_stun_server_process_request( + stun_server_, sock, const_cast<char*>(reinterpret_cast<const char*>(msg)), + len, response_addr_ ? response_addr_ : addr, NR_STUN_AUTH_RULE_OPTIONAL); +} + +void TestStunServer::process_cb(NR_SOCKET s, int how, void* cb_arg) { + DeferredStunOperation* op = static_cast<DeferredStunOperation*>(cb_arg); + op->server_->timer_handle_ = nullptr; + op->server_->Process(op->buffer_.data(), op->buffer_.len(), &op->addr_, + op->sock_); + + delete op; +} + +nr_socket* TestStunServer::GetReceivingSocket(NR_SOCKET s) { + return listen_sock_; +} + +nr_socket* TestStunServer::GetSendingSocket(nr_socket* sock) { + return send_sock_; +} + +void TestStunServer::readable_cb(NR_SOCKET s, int how, void* cb_arg) { + TestStunServer* server = static_cast<TestStunServer*>(cb_arg); + + char message[max_stun_message_size]; + size_t message_len; + nr_transport_addr addr; + nr_socket* recv_sock = server->GetReceivingSocket(s); + if (!recv_sock) { + MOZ_MTLOG(ML_ERROR, "Failed to lookup receiving socket"); + return; + } + nr_socket* send_sock = server->GetSendingSocket(recv_sock); + + /* Re-arm. */ + NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server); + + if (nr_socket_recvfrom(recv_sock, message, sizeof(message), &message_len, 0, + &addr)) { + MOZ_MTLOG(ML_ERROR, "Couldn't read STUN message"); + return; + } + + MOZ_MTLOG(ML_DEBUG, "Received data of length " << message_len); + + // If we have initial dropping set, check at this point. + std::string key(addr.as_string); + + if (server->received_ct_.count(key) == 0) { + server->received_ct_[key] = 0; + } + + ++server->received_ct_[key]; + + if (!server->active_ || (server->received_ct_[key] <= server->initial_ct_)) { + MOZ_MTLOG(ML_DEBUG, "Dropping message #" << server->received_ct_[key] + << " from " << key); + return; + } + + if (server->delay_ms_) { + NR_ASYNC_TIMER_SET(server->delay_ms_, process_cb, + new DeferredStunOperation(server, message, message_len, + &addr, send_sock), + &server->timer_handle_); + } else { + server->Process(reinterpret_cast<const uint8_t*>(message), message_len, + &addr, send_sock); + } +} + +void TestStunServer::SetActive(bool active) { active_ = active; } + +void TestStunServer::SetDelay(uint32_t delay_ms) { delay_ms_ = delay_ms; } + +void TestStunServer::SetDropInitialPackets(uint32_t count) { + initial_ct_ = count; +} + +nsresult TestStunServer::SetResponseAddr(nr_transport_addr* addr) { + delete response_addr_; + + response_addr_ = new nr_transport_addr(); + + int r = nr_transport_addr_copy(response_addr_, addr); + if (r) return NS_ERROR_FAILURE; + + return NS_OK; +} + +nsresult TestStunServer::SetResponseAddr(const std::string& addr, + uint16_t port) { + nr_transport_addr addr2; + + int r = + nr_str_port_to_transport_addr(addr.c_str(), port, IPPROTO_UDP, &addr2); + if (r) return NS_ERROR_FAILURE; + + return SetResponseAddr(&addr2); +} + +void TestStunServer::Reset() { + delay_ms_ = 0; + if (timer_handle_) { + NR_async_timer_cancel(timer_handle_); + timer_handle_ = nullptr; + } + delete response_addr_; + response_addr_ = nullptr; + received_ct_.clear(); +} + +// TestStunTcpServer + +void TestStunTcpServer::ConfigurePort(uint16_t port) { instance_port = port; } + +TestStunTcpServer* TestStunTcpServer::GetInstance(int address_family) { + switch (address_family) { + case AF_INET: + if (!instance) instance = Create(address_family).release(); + + MOZ_ASSERT(instance); + return instance; + case AF_INET6: + if (!instance6) instance6 = Create(address_family).release(); + + return instance6; + default: + MOZ_CRASH(); + } +} + +void TestStunTcpServer::ShutdownInstance() { + delete instance; + instance = nullptr; + delete instance6; + instance6 = nullptr; +} + +int TestStunTcpServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { + addr->addr.protocol = IPPROTO_TCP; + + int r = SetInternalPort(addr, port); + + if (r) return r; + + nr_socket* sock; + if (nr_socket_local_create(nullptr, &addr->addr, &sock)) { + MOZ_MTLOG(ML_ERROR, "Couldn't create listen tcp socket"); + return R_ALREADY; + } + + if (nr_socket_buffered_stun_create(sock, 2048, TURN_TCP_FRAMING, + &listen_sock_)) { + MOZ_MTLOG(ML_ERROR, "Couldn't create listen tcp socket"); + return R_ALREADY; + } + + if (nr_socket_listen(listen_sock_, 10)) { + MOZ_MTLOG(ML_ERROR, "Couldn't listen on socket"); + return R_ALREADY; + } + + return 0; +} + +nr_socket* TestStunTcpServer::GetReceivingSocket(NR_SOCKET s) { + return connections_[s]; +} + +nr_socket* TestStunTcpServer::GetSendingSocket(nr_socket* sock) { return sock; } + +void TestStunTcpServer::accept_cb(NR_SOCKET s, int how, void* cb_arg) { + TestStunTcpServer* server = static_cast<TestStunTcpServer*>(cb_arg); + nr_socket *newsock, *bufsock, *wrapsock; + nr_transport_addr remote_addr; + NR_SOCKET fd; + + /* rearm */ + NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunTcpServer::accept_cb, cb_arg); + + /* accept */ + if (nr_socket_accept(server->listen_sock_, &remote_addr, &newsock)) { + MOZ_MTLOG(ML_ERROR, "Couldn't accept incoming tcp connection"); + return; + } + + if (nr_socket_buffered_stun_create(newsock, 2048, TURN_TCP_FRAMING, + &bufsock)) { + MOZ_MTLOG(ML_ERROR, "Couldn't create connected tcp socket"); + nr_socket_destroy(&newsock); + return; + } + + nr_socket_buffered_set_connected_to(bufsock, &remote_addr); + + if (nr_socket_wrapped_create(bufsock, &wrapsock)) { + MOZ_MTLOG(ML_ERROR, "Couldn't wrap connected tcp socket"); + nr_socket_destroy(&bufsock); + return; + } + + if (nr_socket_getfd(wrapsock, &fd)) { + MOZ_MTLOG(ML_ERROR, "Couldn't get fd from connected tcp socket"); + nr_socket_destroy(&wrapsock); + return; + } + + server->connections_[fd] = wrapsock; + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server); +} + +UniquePtr<TestStunTcpServer> TestStunTcpServer::Create(int address_family) { + NR_reg_init(NR_REG_MODE_LOCAL); + + UniquePtr<TestStunTcpServer> server(new TestStunTcpServer()); + + if (server->Initialize(address_family)) { + return nullptr; + } + + NR_SOCKET fd; + if (nr_socket_getfd(server->listen_sock_, &fd)) { + MOZ_MTLOG(ML_ERROR, "Couldn't get tcp fd"); + return nullptr; + } + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunTcpServer::accept_cb, + server.get()); + + return server; +} + +TestStunTcpServer::~TestStunTcpServer() { + for (auto it = connections_.begin(); it != connections_.end();) { + NR_ASYNC_CANCEL(it->first, NR_ASYNC_WAIT_READ); + nr_socket_destroy(&it->second); + connections_.erase(it++); + } +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/test/stunserver.h b/dom/media/webrtc/transport/test/stunserver.h new file mode 100644 index 0000000000..4903cb89ad --- /dev/null +++ b/dom/media/webrtc/transport/test/stunserver.h @@ -0,0 +1,123 @@ +/* -*- 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 + +#ifndef stunserver_h__ +#define stunserver_h__ + +#include <map> +#include <string> +#include "nsError.h" +#include "mozilla/UniquePtr.h" + +typedef struct nr_stun_server_ctx_ nr_stun_server_ctx; +typedef struct nr_socket_ nr_socket; +typedef struct nr_local_addr_ nr_local_addr; + +namespace mozilla { + +class TestStunServer { + public: + // Generally, you should only call API in this class from the same thread that + // the initial |GetInstance| call was made from. + static TestStunServer* GetInstance(int address_family = AF_INET); + static void ShutdownInstance(); + // |ConfigurePort| will only have an effect if called before the first call + // to |GetInstance| (possibly following a |ShutdownInstance| call) + static void ConfigurePort(uint16_t port); + // AF_INET, AF_INET6 + static UniquePtr<TestStunServer> Create(int address_family); + + virtual ~TestStunServer(); + + void SetActive(bool active); + void SetDelay(uint32_t delay_ms); + void SetDropInitialPackets(uint32_t count); + const std::string& addr() const { return listen_addr_; } + uint16_t port() const { return listen_port_; } + + // These should only be called from the same thread as the initial + // |GetInstance| call. + nsresult SetResponseAddr(nr_transport_addr* addr); + nsresult SetResponseAddr(const std::string& addr, uint16_t port); + + void Reset(); + + static const size_t max_stun_message_size = 4096; + + virtual nr_socket* GetReceivingSocket(NR_SOCKET s); + virtual nr_socket* GetSendingSocket(nr_socket* sock); + + protected: + TestStunServer() + : listen_port_(0), + listen_sock_(nullptr), + send_sock_(nullptr), + stun_server_(nullptr), + active_(true), + delay_ms_(0), + initial_ct_(0), + response_addr_(nullptr), + timer_handle_(nullptr) {} + + int SetInternalPort(nr_local_addr* addr, uint16_t port); + int Initialize(int address_family); + + static void readable_cb(NR_SOCKET sock, int how, void* cb_arg); + + private: + void Process(const uint8_t* msg, size_t len, nr_transport_addr* addr_in, + nr_socket* sock); + virtual int TryOpenListenSocket(nr_local_addr* addr, uint16_t port); + static void process_cb(NR_SOCKET sock, int how, void* cb_arg); + + protected: + std::string listen_addr_; + uint16_t listen_port_; + nr_socket* listen_sock_; + nr_socket* send_sock_; + nr_stun_server_ctx* stun_server_; + + private: + bool active_; + uint32_t delay_ms_; + uint32_t initial_ct_; + nr_transport_addr* response_addr_; + void* timer_handle_; + std::map<std::string, uint32_t> received_ct_; + + static TestStunServer* instance; + static TestStunServer* instance6; + static uint16_t instance_port; +}; + +class TestStunTcpServer : public TestStunServer { + public: + static TestStunTcpServer* GetInstance(int address_family); + static void ShutdownInstance(); + static void ConfigurePort(uint16_t port); + virtual ~TestStunTcpServer(); + + virtual nr_socket* GetReceivingSocket(NR_SOCKET s); + virtual nr_socket* GetSendingSocket(nr_socket* sock); + + protected: + TestStunTcpServer() = default; + static void accept_cb(NR_SOCKET sock, int how, void* cb_arg); + + private: + virtual int TryOpenListenSocket(nr_local_addr* addr, uint16_t port); + static UniquePtr<TestStunTcpServer> Create(int address_family); + + static TestStunTcpServer* instance; + static TestStunTcpServer* instance6; + static uint16_t instance_port; + + std::map<NR_SOCKET, nr_socket*> connections_; +}; +} // End of namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp b/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp new file mode 100644 index 0000000000..a411168194 --- /dev/null +++ b/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp @@ -0,0 +1,415 @@ +/* -*- 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/. */ + +// Some of this code is taken from nricectx.cpp and nricemediastream.cpp +// which in turn contains code cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 "gtest/gtest.h" +#include "gtest_utils.h" + +extern "C" { +#include "ice_ctx.h" +#include "ice_peer_ctx.h" +#include "nICEr/src/net/transport_addr.h" +} + +#include "mtransport_test_utils.h" +#include "nricectx.h" +#include "nricemediastream.h" +#include "runnable_utils.h" +#include "test_nr_socket.h" + +namespace mozilla { + +static unsigned int kDefaultTimeout = 7000; + +class IcePeer { + public: + IcePeer(const char* name, TestNat* nat, UINT4 flags, + MtransportTestUtils* test_utils) + : name_(name), + ice_checking_(false), + ice_connected_(false), + ice_disconnected_(false), + gather_cb_(false), + stream_ready_(false), + stream_failed_(false), + ice_ctx_(nullptr), + peer_ctx_(nullptr), + nat_(nat), + test_utils_(test_utils) { + nr_ice_ctx_create(const_cast<char*>(name_.c_str()), flags, &ice_ctx_); + + if (nat_) { + nr_socket_factory* factory; + nat_->create_socket_factory(&factory); + nr_ice_ctx_set_socket_factory(ice_ctx_, factory); + } + + // Create the handler objects + ice_handler_vtbl_ = new nr_ice_handler_vtbl(); + ice_handler_vtbl_->select_pair = &IcePeer::select_pair; + ice_handler_vtbl_->stream_ready = &IcePeer::stream_ready; + ice_handler_vtbl_->stream_failed = &IcePeer::stream_failed; + ice_handler_vtbl_->ice_connected = &IcePeer::ice_connected; + ice_handler_vtbl_->msg_recvd = &IcePeer::msg_recvd; + ice_handler_vtbl_->ice_checking = &IcePeer::ice_checking; + ice_handler_vtbl_->ice_disconnected = &IcePeer::ice_disconnected; + + ice_handler_ = new nr_ice_handler(); + ice_handler_->vtbl = ice_handler_vtbl_; + ice_handler_->obj = this; + + nr_ice_peer_ctx_create(ice_ctx_, ice_handler_, + const_cast<char*>(name_.c_str()), &peer_ctx_); + + nr_ice_add_media_stream(ice_ctx_, const_cast<char*>(name_.c_str()), "ufrag", + "pass", 2, &ice_media_stream_); + EXPECT_EQ(2UL, GetStreamAttributes().size()); + + nr_ice_media_stream_initialize(ice_ctx_, ice_media_stream_); + } + + virtual ~IcePeer() { Destroy(); } + + void Destroy() { + test_utils_->sts_target()->Dispatch(WrapRunnable(this, &IcePeer::Destroy_s), + NS_DISPATCH_SYNC); + } + + void Destroy_s() { + nr_ice_peer_ctx_destroy(&peer_ctx_); + delete ice_handler_; + delete ice_handler_vtbl_; + nr_ice_ctx_destroy(&ice_ctx_); + } + + void Gather(bool default_route_only = false) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IcePeer::Gather_s, default_route_only), + NS_DISPATCH_SYNC); + } + + void Gather_s(bool default_route_only = false) { + int r = nr_ice_gather(ice_ctx_, &IcePeer::gather_cb, this); + ASSERT_TRUE(r == 0 || r == R_WOULDBLOCK); + } + + std::vector<std::string> GetStreamAttributes() { + std::vector<std::string> attributes; + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&attributes, this, &IcePeer::GetStreamAttributes_s), + NS_DISPATCH_SYNC); + return attributes; + } + + std::vector<std::string> GetStreamAttributes_s() { + char** attrs = nullptr; + int attrct; + std::vector<std::string> ret; + + int r = + nr_ice_media_stream_get_attributes(ice_media_stream_, &attrs, &attrct); + EXPECT_EQ(0, r); + + for (int i = 0; i < attrct; i++) { + ret.push_back(std::string(attrs[i])); + RFREE(attrs[i]); + } + RFREE(attrs); + + return ret; + } + + std::vector<std::string> GetGlobalAttributes() { + std::vector<std::string> attributes; + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&attributes, this, &IcePeer::GetGlobalAttributes_s), + NS_DISPATCH_SYNC); + return attributes; + } + + std::vector<std::string> GetGlobalAttributes_s() { + char** attrs = nullptr; + int attrct; + std::vector<std::string> ret; + + nr_ice_get_global_attributes(ice_ctx_, &attrs, &attrct); + + for (int i = 0; i < attrct; i++) { + ret.push_back(std::string(attrs[i])); + RFREE(attrs[i]); + } + RFREE(attrs); + + return ret; + } + + void ParseGlobalAttributes(std::vector<std::string> attrs) { + std::vector<char*> attrs_in; + attrs_in.reserve(attrs.size()); + for (auto& attr : attrs) { + attrs_in.push_back(const_cast<char*>(attr.c_str())); + } + + int r = nr_ice_peer_ctx_parse_global_attributes( + peer_ctx_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size()); + ASSERT_EQ(0, r); + } + + void SetControlling(bool controlling) { + peer_ctx_->controlling = controlling ? 1 : 0; + } + + void SetRemoteAttributes(std::vector<std::string> attributes) { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IcePeer::SetRemoteAttributes_s, attributes), + NS_DISPATCH_SYNC); + } + + void SetRemoteAttributes_s(std::vector<std::string> attributes) { + int r; + + std::vector<char*> attrs; + attrs.reserve(attributes.size()); + for (auto& attr : attributes) { + attrs.push_back(const_cast<char*>(attr.c_str())); + } + + if (!attrs.empty()) { + r = nr_ice_peer_ctx_parse_stream_attributes(peer_ctx_, ice_media_stream_, + &attrs[0], attrs.size()); + ASSERT_EQ(0, r); + } + } + + void StartChecks() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &IcePeer::StartChecks_s), NS_DISPATCH_SYNC); + } + + void StartChecks_s() { + int r = nr_ice_peer_ctx_pair_candidates(peer_ctx_); + ASSERT_EQ(0, r); + + r = nr_ice_peer_ctx_start_checks2(peer_ctx_, 1); + ASSERT_EQ(0, r); + } + + // Handler callbacks + static int select_pair(void* obj, nr_ice_media_stream* stream, + int component_id, nr_ice_cand_pair** potentials, + int potential_ct) { + return 0; + } + + static int stream_ready(void* obj, nr_ice_media_stream* stream) { + IcePeer* peer = static_cast<IcePeer*>(obj); + peer->stream_ready_ = true; + return 0; + } + + static int stream_failed(void* obj, nr_ice_media_stream* stream) { + IcePeer* peer = static_cast<IcePeer*>(obj); + peer->stream_failed_ = true; + return 0; + } + + static int ice_checking(void* obj, nr_ice_peer_ctx* pctx) { + IcePeer* peer = static_cast<IcePeer*>(obj); + peer->ice_checking_ = true; + return 0; + } + + static int ice_connected(void* obj, nr_ice_peer_ctx* pctx) { + IcePeer* peer = static_cast<IcePeer*>(obj); + peer->ice_connected_ = true; + return 0; + } + + static int ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) { + IcePeer* peer = static_cast<IcePeer*>(obj); + peer->ice_disconnected_ = true; + return 0; + } + + static int msg_recvd(void* obj, nr_ice_peer_ctx* pctx, + nr_ice_media_stream* stream, int component_id, + UCHAR* msg, int len) { + return 0; + } + + static void gather_cb(NR_SOCKET s, int h, void* arg) { + IcePeer* peer = static_cast<IcePeer*>(arg); + peer->gather_cb_ = true; + } + + std::string name_; + + bool ice_checking_; + bool ice_connected_; + bool ice_disconnected_; + bool gather_cb_; + bool stream_ready_; + bool stream_failed_; + + nr_ice_ctx* ice_ctx_; + nr_ice_handler* ice_handler_; + nr_ice_handler_vtbl* ice_handler_vtbl_; + nr_ice_media_stream* ice_media_stream_; + nr_ice_peer_ctx* peer_ctx_; + TestNat* nat_; + MtransportTestUtils* test_utils_; +}; + +class TestNrSocketIceUnitTest : public ::testing::Test { + public: + void SetUp() override { + NSS_NoDB_Init(nullptr); + NSS_SetDomesticPolicy(); + + test_utils_ = new MtransportTestUtils(); + test_utils2_ = new MtransportTestUtils(); + + NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig()); + } + + void TearDown() override { + delete test_utils_; + delete test_utils2_; + } + + MtransportTestUtils* test_utils_; + MtransportTestUtils* test_utils2_; +}; + +TEST_F(TestNrSocketIceUnitTest, TestIcePeer) { + IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, + test_utils_); + ASSERT_NE(peer.ice_ctx_, nullptr); + ASSERT_NE(peer.peer_ctx_, nullptr); + ASSERT_NE(peer.ice_media_stream_, nullptr); + ASSERT_EQ(2UL, peer.GetStreamAttributes().size()) + << "Should have ice-ufrag and ice-pwd"; + peer.Gather(); + ASSERT_LT(2UL, peer.GetStreamAttributes().size()) + << "Should have ice-ufrag, ice-pwd, and at least one candidate."; +} + +TEST_F(TestNrSocketIceUnitTest, TestIcePeersNoNAT) { + IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, + test_utils_); + IcePeer peer2("IcePeer2", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, + test_utils2_); + peer.SetControlling(true); + peer2.SetControlling(false); + + peer.Gather(); + peer2.Gather(); + std::vector<std::string> attrs = peer.GetGlobalAttributes(); + peer2.ParseGlobalAttributes(attrs); + std::vector<std::string> attributes = peer.GetStreamAttributes(); + peer2.SetRemoteAttributes(attributes); + + attrs = peer2.GetGlobalAttributes(); + peer.ParseGlobalAttributes(attrs); + attributes = peer2.GetStreamAttributes(); + peer.SetRemoteAttributes(attributes); + peer2.StartChecks(); + peer.StartChecks(); + + ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout); + ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout); +} + +TEST_F(TestNrSocketIceUnitTest, TestIcePeersPacketLoss) { + IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, + test_utils_); + + RefPtr<TestNat> nat(new TestNat); + class NatDelegate : public TestNat::NatDelegate { + public: + NatDelegate() : messages(0) {} + + int on_read(TestNat* nat, void* buf, size_t maxlen, size_t* len) override { + return 0; + } + + int on_sendto(TestNat* nat, const void* msg, size_t len, int flags, + const nr_transport_addr* to) override { + ++messages; + // 25% packet loss + if (messages % 4 == 0) { + return 1; + } + return 0; + } + + int on_write(TestNat* nat, const void* msg, size_t len, + size_t* written) override { + return 0; + } + + int messages; + } delegate; + nat->nat_delegate_ = &delegate; + + IcePeer peer2("IcePeer2", nat, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION, + test_utils2_); + peer.SetControlling(true); + peer2.SetControlling(false); + + peer.Gather(); + peer2.Gather(); + std::vector<std::string> attrs = peer.GetGlobalAttributes(); + peer2.ParseGlobalAttributes(attrs); + std::vector<std::string> attributes = peer.GetStreamAttributes(); + peer2.SetRemoteAttributes(attributes); + + attrs = peer2.GetGlobalAttributes(); + peer.ParseGlobalAttributes(attrs); + attributes = peer2.GetStreamAttributes(); + peer.SetRemoteAttributes(attributes); + peer2.StartChecks(); + peer.StartChecks(); + + ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout); + ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout); +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp b/dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp new file mode 100644 index 0000000000..dcc5001b17 --- /dev/null +++ b/dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp @@ -0,0 +1,808 @@ +/* -*- 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: bcampen@mozilla.com + +#include <cstddef> + +extern "C" { +#include "r_errors.h" +#include "async_wait.h" +} + +#include "test_nr_socket.h" + +#include "nsCOMPtr.h" +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" +#include "runnable_utils.h" + +#include <vector> + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +#define DATA_BUF_SIZE 1024 + +namespace mozilla { + +class TestNrSocketTest : public MtransportTest { + public: + TestNrSocketTest() + : MtransportTest(), + wait_done_for_main_(false), + sts_(), + public_addrs_(), + private_addrs_(), + nats_() {} + + void SetUp() override { + MtransportTest::SetUp(); + + // Get the transport service as a dispatch target + nsresult rv; + sts_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + EXPECT_TRUE(NS_SUCCEEDED(rv)) << "Failed to get STS: " << (int)rv; + } + + void TearDown() override { + sts_->Dispatch(WrapRunnable(this, &TestNrSocketTest::TearDown_s), + NS_DISPATCH_SYNC); + + MtransportTest::TearDown(); + } + + void TearDown_s() { + public_addrs_.clear(); + private_addrs_.clear(); + nats_.clear(); + sts_ = nullptr; + } + + RefPtr<TestNrSocket> CreateTestNrSocket_s(const char* ip_str, int proto, + TestNat* nat) { + // If no nat is supplied, we create a default NAT which is disabled. This + // is how we simulate a non-natted socket. + RefPtr<TestNrSocket> sock(new TestNrSocket(nat ? nat : new TestNat)); + nr_transport_addr address; + nr_str_port_to_transport_addr(ip_str, 0, proto, &address); + int r = sock->create(&address); + if (r) { + return nullptr; + } + return sock; + } + + void CreatePublicAddrs(size_t count, const char* ip_str = "127.0.0.1", + int proto = IPPROTO_UDP) { + sts_->Dispatch(WrapRunnable(this, &TestNrSocketTest::CreatePublicAddrs_s, + count, ip_str, proto), + NS_DISPATCH_SYNC); + } + + void CreatePublicAddrs_s(size_t count, const char* ip_str, int proto) { + while (count--) { + auto sock = CreateTestNrSocket_s(ip_str, proto, nullptr); + ASSERT_TRUE(sock) + << "Failed to create socket"; + public_addrs_.push_back(sock); + } + } + + RefPtr<TestNat> CreatePrivateAddrs(size_t size, + const char* ip_str = "127.0.0.1", + int proto = IPPROTO_UDP) { + RefPtr<TestNat> result; + sts_->Dispatch( + WrapRunnableRet(&result, this, &TestNrSocketTest::CreatePrivateAddrs_s, + size, ip_str, proto), + NS_DISPATCH_SYNC); + return result; + } + + RefPtr<TestNat> CreatePrivateAddrs_s(size_t count, const char* ip_str, + int proto) { + RefPtr<TestNat> nat(new TestNat); + while (count--) { + auto sock = CreateTestNrSocket_s(ip_str, proto, nat); + if (!sock) { + EXPECT_TRUE(false) << "Failed to create socket"; + break; + } + private_addrs_.push_back(sock); + } + nat->enabled_ = true; + nats_.push_back(nat); + return nat; + } + + bool CheckConnectivityVia( + TestNrSocket* from, TestNrSocket* to, const nr_transport_addr& via, + nr_transport_addr* sender_external_address = nullptr) { + MOZ_ASSERT(from); + + if (!WaitForWriteable(from)) { + return false; + } + + int result = 0; + sts_->Dispatch(WrapRunnableRet(&result, this, &TestNrSocketTest::SendData_s, + from, via), + NS_DISPATCH_SYNC); + if (result) { + return false; + } + + if (!WaitForReadable(to)) { + return false; + } + + nr_transport_addr dummy_outparam; + if (!sender_external_address) { + sender_external_address = &dummy_outparam; + } + + MOZ_ASSERT(to); + sts_->Dispatch(WrapRunnableRet(&result, this, &TestNrSocketTest::RecvData_s, + to, sender_external_address), + NS_DISPATCH_SYNC); + + return !result; + } + + bool CheckConnectivity(TestNrSocket* from, TestNrSocket* to, + nr_transport_addr* sender_external_address = nullptr) { + nr_transport_addr destination_address; + int r = GetAddress(to, &destination_address); + if (r) { + return false; + } + + return CheckConnectivityVia(from, to, destination_address, + sender_external_address); + } + + bool CheckTcpConnectivity(TestNrSocket* from, TestNrSocket* to) { + NrSocketBase* accepted_sock; + if (!Connect(from, to, &accepted_sock)) { + std::cerr << "Connect failed" << std::endl; + return false; + } + + // write on |from|, recv on |accepted_sock| + if (!WaitForWriteable(from)) { + std::cerr << __LINE__ << "WaitForWriteable (1) failed" << std::endl; + return false; + } + + int r; + sts_->Dispatch( + WrapRunnableRet(&r, this, &TestNrSocketTest::SendDataTcp_s, from), + NS_DISPATCH_SYNC); + if (r) { + std::cerr << "SendDataTcp_s (1) failed" << std::endl; + return false; + } + + if (!WaitForReadable(accepted_sock)) { + std::cerr << __LINE__ << "WaitForReadable (1) failed" << std::endl; + return false; + } + + sts_->Dispatch(WrapRunnableRet(&r, this, &TestNrSocketTest::RecvDataTcp_s, + accepted_sock), + NS_DISPATCH_SYNC); + if (r) { + std::cerr << "RecvDataTcp_s (1) failed" << std::endl; + return false; + } + + if (!WaitForWriteable(accepted_sock)) { + std::cerr << __LINE__ << "WaitForWriteable (2) failed" << std::endl; + return false; + } + + sts_->Dispatch(WrapRunnableRet(&r, this, &TestNrSocketTest::SendDataTcp_s, + accepted_sock), + NS_DISPATCH_SYNC); + if (r) { + std::cerr << "SendDataTcp_s (2) failed" << std::endl; + return false; + } + + if (!WaitForReadable(from)) { + std::cerr << __LINE__ << "WaitForReadable (2) failed" << std::endl; + return false; + } + + sts_->Dispatch( + WrapRunnableRet(&r, this, &TestNrSocketTest::RecvDataTcp_s, from), + NS_DISPATCH_SYNC); + if (r) { + std::cerr << "RecvDataTcp_s (2) failed" << std::endl; + return false; + } + + return true; + } + + int GetAddress(TestNrSocket* sock, nr_transport_addr_* address) { + MOZ_ASSERT(sock); + MOZ_ASSERT(address); + int r; + sts_->Dispatch(WrapRunnableRet(&r, this, &TestNrSocketTest::GetAddress_s, + sock, address), + NS_DISPATCH_SYNC); + return r; + } + + int GetAddress_s(TestNrSocket* sock, nr_transport_addr* address) { + return sock->getaddr(address); + } + + int SendData_s(TestNrSocket* from, const nr_transport_addr& to) { + // It is up to caller to ensure that |from| is writeable. + const char buf[] = "foobajooba"; + return from->sendto(buf, sizeof(buf), 0, &to); + } + + int SendDataTcp_s(NrSocketBase* from) { + // It is up to caller to ensure that |from| is writeable. + const char buf[] = "foobajooba"; + size_t written; + return from->write(buf, sizeof(buf), &written); + } + + int RecvData_s(TestNrSocket* to, nr_transport_addr* from) { + // It is up to caller to ensure that |to| is readable + char buf[DATA_BUF_SIZE]; + size_t len; + // Maybe check that data matches? + int r = to->recvfrom(buf, sizeof(buf), &len, 0, from); + if (!r && (len == 0)) { + r = R_INTERNAL; + } + return r; + } + + int RecvDataTcp_s(NrSocketBase* to) { + // It is up to caller to ensure that |to| is readable + char buf[DATA_BUF_SIZE]; + size_t len; + // Maybe check that data matches? + int r = to->read(buf, sizeof(buf), &len); + if (!r && (len == 0)) { + r = R_INTERNAL; + } + return r; + } + + int Listen_s(TestNrSocket* to) { + // listen on |to| + int r = to->listen(1); + if (r) { + return r; + } + return 0; + } + + int Connect_s(TestNrSocket* from, TestNrSocket* to) { + // connect on |from| + nr_transport_addr destination_address; + int r = to->getaddr(&destination_address); + if (r) { + return r; + } + + r = from->connect(&destination_address); + if (r) { + return r; + } + + return 0; + } + + int Accept_s(TestNrSocket* to, NrSocketBase** accepted_sock) { + nr_socket* sock; + nr_transport_addr source_address; + int r = to->accept(&source_address, &sock); + if (r) { + return r; + } + + *accepted_sock = reinterpret_cast<NrSocketBase*>(sock->obj); + return 0; + } + + bool Connect(TestNrSocket* from, TestNrSocket* to, + NrSocketBase** accepted_sock) { + int r; + sts_->Dispatch(WrapRunnableRet(&r, this, &TestNrSocketTest::Listen_s, to), + NS_DISPATCH_SYNC); + if (r) { + std::cerr << "Listen_s failed: " << r << std::endl; + return false; + } + + sts_->Dispatch( + WrapRunnableRet(&r, this, &TestNrSocketTest::Connect_s, from, to), + NS_DISPATCH_SYNC); + if (r && r != R_WOULDBLOCK) { + std::cerr << "Connect_s failed: " << r << std::endl; + return false; + } + + if (!WaitForReadable(to)) { + std::cerr << "WaitForReadable failed" << std::endl; + return false; + } + + sts_->Dispatch(WrapRunnableRet(&r, this, &TestNrSocketTest::Accept_s, to, + accepted_sock), + NS_DISPATCH_SYNC); + + if (r) { + std::cerr << "Accept_s failed: " << r << std::endl; + return false; + } + return true; + } + + bool WaitForSocketState(NrSocketBase* sock, int state) { + MOZ_ASSERT(sock); + sts_->Dispatch(WrapRunnable(this, &TestNrSocketTest::WaitForSocketState_s, + sock, state), + NS_DISPATCH_SYNC); + + bool res; + WAIT_(wait_done_for_main_, 500, res); + wait_done_for_main_ = false; + + if (!res) { + sts_->Dispatch( + WrapRunnable(this, &TestNrSocketTest::CancelWait_s, sock, state), + NS_DISPATCH_SYNC); + } + + return res; + } + + void WaitForSocketState_s(NrSocketBase* sock, int state) { + NR_ASYNC_WAIT(sock, state, &WaitDone, this); + } + + void CancelWait_s(NrSocketBase* sock, int state) { sock->cancel(state); } + + bool WaitForReadable(NrSocketBase* sock) { + return WaitForSocketState(sock, NR_ASYNC_WAIT_READ); + } + + bool WaitForWriteable(NrSocketBase* sock) { + return WaitForSocketState(sock, NR_ASYNC_WAIT_WRITE); + } + + static void WaitDone(void* sock, int how, void* test_fixture) { + TestNrSocketTest* test = static_cast<TestNrSocketTest*>(test_fixture); + test->wait_done_for_main_ = true; + } + + // Simple busywait boolean for the test cases to spin on. + Atomic<bool> wait_done_for_main_; + + nsCOMPtr<nsIEventTarget> sts_; + std::vector<RefPtr<TestNrSocket>> public_addrs_; + std::vector<RefPtr<TestNrSocket>> private_addrs_; + std::vector<RefPtr<TestNat>> nats_; +}; + +} // namespace mozilla + +using mozilla::NrSocketBase; +using mozilla::TestNat; +using mozilla::TestNrSocketTest; + +TEST_F(TestNrSocketTest, UnsafePortRejectedUDP) { + nr_transport_addr address; + ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1", + // ssh + 22, IPPROTO_UDP, &address)); + ASSERT_TRUE(NrSocketBase::IsForbiddenAddress(&address)); +} + +TEST_F(TestNrSocketTest, UnsafePortRejectedTCP) { + nr_transport_addr address; + ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1", + // ssh + 22, IPPROTO_TCP, &address)); + ASSERT_TRUE(NrSocketBase::IsForbiddenAddress(&address)); +} + +TEST_F(TestNrSocketTest, SafePortAcceptedUDP) { + nr_transport_addr address; + ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1", + // stuns + 5349, IPPROTO_UDP, &address)); + ASSERT_FALSE(NrSocketBase::IsForbiddenAddress(&address)); +} + +TEST_F(TestNrSocketTest, SafePortAcceptedTCP) { + nr_transport_addr address; + ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1", + // turns + 5349, IPPROTO_TCP, &address)); + ASSERT_FALSE(NrSocketBase::IsForbiddenAddress(&address)); +} + +TEST_F(TestNrSocketTest, PublicConnectivity) { + CreatePublicAddrs(2); + + ASSERT_TRUE(CheckConnectivity(public_addrs_[0], public_addrs_[1])); + ASSERT_TRUE(CheckConnectivity(public_addrs_[1], public_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(public_addrs_[0], public_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(public_addrs_[1], public_addrs_[1])); +} + +TEST_F(TestNrSocketTest, PrivateConnectivity) { + RefPtr<TestNat> nat(CreatePrivateAddrs(2)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[1])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[1])); +} + +TEST_F(TestNrSocketTest, NoConnectivityWithoutPinhole) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(1); + + ASSERT_FALSE(CheckConnectivity(public_addrs_[0], private_addrs_[0])); +} + +TEST_F(TestNrSocketTest, NoConnectivityBetweenSubnets) { + RefPtr<TestNat> nat1(CreatePrivateAddrs(1)); + nat1->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat1->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + RefPtr<TestNat> nat2(CreatePrivateAddrs(1)); + nat2->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat2->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + + ASSERT_FALSE(CheckConnectivity(private_addrs_[0], private_addrs_[1])); + ASSERT_FALSE(CheckConnectivity(private_addrs_[1], private_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[0])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[1])); +} + +TEST_F(TestNrSocketTest, FullConeAcceptIngress) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Verify that other public IP can use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address)); +} + +TEST_F(TestNrSocketTest, FullConeOnePinhole) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Send traffic to other public IP, verify that it uses the same pinhole + nr_transport_addr sender_external_address2; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address2)); + ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address2, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address.as_string + << " addr2: " << sender_external_address2.as_string; +} + +// OS 10.6 doesn't seem to allow us to open ports on 127.0.0.2, and while linux +// does allow this, it has other behavior (see below) that prevents this test +// from working. +TEST_F(TestNrSocketTest, DISABLED_AddressRestrictedCone) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ADDRESS_DEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(2, "127.0.0.1"); + CreatePublicAddrs(1, "127.0.0.2"); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Verify that another address on the same host can use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address)); + + // Linux has a tendency to monkey around with source addresses, doing + // stuff like substituting 127.0.0.1 for packets sent by 127.0.0.2, and even + // going as far as substituting localhost for a packet sent from a real IP + // address when the destination is localhost. The only way to make this test + // work on linux is to have two real IP addresses. +#ifndef __linux__ + // Verify that an address on a different host can't use the pinhole + ASSERT_FALSE(CheckConnectivityVia(public_addrs_[2], private_addrs_[0], + sender_external_address)); +#endif + + // Send traffic to other public IP, verify that it uses the same pinhole + nr_transport_addr sender_external_address2; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address2)); + ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address2, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address.as_string + << " addr2: " << sender_external_address2.as_string; + + // Verify that the other public IP can now use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address2)); + + // Send traffic to other public IP, verify that it uses the same pinhole + nr_transport_addr sender_external_address3; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[2], + &sender_external_address3)); + ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address3, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address.as_string + << " addr2: " << sender_external_address3.as_string; + + // Verify that the other public IP can now use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[2], private_addrs_[0], + sender_external_address3)); +} + +TEST_F(TestNrSocketTest, RestrictedCone) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::PORT_DEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Verify that other public IP cannot use the pinhole + ASSERT_FALSE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address)); + + // Send traffic to other public IP, verify that it uses the same pinhole + nr_transport_addr sender_external_address2; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address2)); + ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address2, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address.as_string + << " addr2: " << sender_external_address2.as_string; + + // Verify that the other public IP can now use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address2)); +} + +TEST_F(TestNrSocketTest, PortDependentMappingFullCone) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::PORT_DEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address0; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address0)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address0)); + + // Verify that other public IP can use the pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address0)); + + // Send traffic to other public IP, verify that it uses a different pinhole + nr_transport_addr sender_external_address1; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address1)); + ASSERT_TRUE(nr_transport_addr_cmp(&sender_external_address0, + &sender_external_address1, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + << "addr1: " << sender_external_address0.as_string + << " addr2: " << sender_external_address1.as_string; + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address1)); + + // Verify that other public IP can use the original pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address1)); +} + +TEST_F(TestNrSocketTest, Symmetric) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::PORT_DEPENDENT; + nat->mapping_type_ = TestNat::PORT_DEPENDENT; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + // Verify that other public IP cannot use the pinhole + ASSERT_FALSE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address)); + + // Send traffic to other public IP, verify that it uses a new pinhole + nr_transport_addr sender_external_address2; + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[1], + &sender_external_address2)); + ASSERT_TRUE(nr_transport_addr_cmp(&sender_external_address, + &sender_external_address2, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)); + + // Verify that the other public IP can use the new pinhole + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1], private_addrs_[0], + sender_external_address2)); +} + +TEST_F(TestNrSocketTest, BlockUdp) { + RefPtr<TestNat> nat(CreatePrivateAddrs(2)); + nat->block_udp_ = true; + CreatePublicAddrs(1); + + nr_transport_addr sender_external_address; + ASSERT_FALSE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Make sure UDP behind the NAT still works + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[1])); + ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[0])); +} + +TEST_F(TestNrSocketTest, DenyHairpinning) { + RefPtr<TestNat> nat(CreatePrivateAddrs(2)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(1); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that hairpinning is disallowed + ASSERT_FALSE(CheckConnectivityVia(private_addrs_[1], private_addrs_[0], + sender_external_address)); +} + +TEST_F(TestNrSocketTest, AllowHairpinning) { + RefPtr<TestNat> nat(CreatePrivateAddrs(2)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_timeout_ = 30000; + nat->allow_hairpinning_ = true; + CreatePublicAddrs(1); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0, obtain external address + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that hairpinning is allowed + ASSERT_TRUE(CheckConnectivityVia(private_addrs_[1], private_addrs_[0], + sender_external_address)); +} + +TEST_F(TestNrSocketTest, FullConeTimeout) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_timeout_ = 200; + CreatePublicAddrs(2); + + nr_transport_addr sender_external_address; + // Open pinhole to public IP 0 + ASSERT_TRUE(CheckConnectivity(private_addrs_[0], public_addrs_[0], + &sender_external_address)); + + // Verify that return traffic works + ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); + + PR_Sleep(201); + + // Verify that return traffic does not work + ASSERT_FALSE(CheckConnectivityVia(public_addrs_[0], private_addrs_[0], + sender_external_address)); +} + +TEST_F(TestNrSocketTest, PublicConnectivityTcp) { + CreatePublicAddrs(2, "127.0.0.1", IPPROTO_TCP); + + ASSERT_TRUE(CheckTcpConnectivity(public_addrs_[0], public_addrs_[1])); +} + +TEST_F(TestNrSocketTest, PrivateConnectivityTcp) { + RefPtr<TestNat> nat(CreatePrivateAddrs(2, "127.0.0.1", IPPROTO_TCP)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + + ASSERT_TRUE(CheckTcpConnectivity(private_addrs_[0], private_addrs_[1])); +} + +TEST_F(TestNrSocketTest, PrivateToPublicConnectivityTcp) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(1, "127.0.0.1", IPPROTO_TCP); + + ASSERT_TRUE(CheckTcpConnectivity(private_addrs_[0], public_addrs_[0])); +} + +TEST_F(TestNrSocketTest, NoConnectivityBetweenSubnetsTcp) { + RefPtr<TestNat> nat1(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP)); + nat1->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat1->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + RefPtr<TestNat> nat2(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP)); + nat2->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat2->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + + ASSERT_FALSE(CheckTcpConnectivity(private_addrs_[0], private_addrs_[1])); +} + +TEST_F(TestNrSocketTest, NoConnectivityPublicToPrivateTcp) { + RefPtr<TestNat> nat(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP)); + nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT; + nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT; + CreatePublicAddrs(1, "127.0.0.1", IPPROTO_TCP); + + ASSERT_FALSE(CheckTcpConnectivity(public_addrs_[0], private_addrs_[0])); +} diff --git a/dom/media/webrtc/transport/test/transport_unittests.cpp b/dom/media/webrtc/transport/test/transport_unittests.cpp new file mode 100644 index 0000000000..b88450486a --- /dev/null +++ b/dom/media/webrtc/transport/test/transport_unittests.cpp @@ -0,0 +1,1416 @@ + +/* 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 <string> +#include <algorithm> +#include <functional> + +#ifdef XP_MACOSX +// ensure that Apple Security kit enum goes before "sslproto.h" +# include <CoreFoundation/CFAvailability.h> +# include <Security/CipherSuite.h> +#endif + +#include "mozilla/UniquePtr.h" + +#include "sigslot.h" + +#include "logging.h" +#include "ssl.h" +#include "sslexp.h" +#include "sslproto.h" + +#include "nsThreadUtils.h" + +#include "mediapacket.h" +#include "dtlsidentity.h" +#include "nricectx.h" +#include "nricemediastream.h" +#include "transportflow.h" +#include "transportlayer.h" +#include "transportlayerdtls.h" +#include "transportlayerice.h" +#include "transportlayerlog.h" +#include "transportlayerloopback.h" + +#include "runnable_utils.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; +MOZ_MTLOG_MODULE("mtransport") + +const uint8_t kTlsChangeCipherSpecType = 0x14; +const uint8_t kTlsHandshakeType = 0x16; + +const uint8_t kTlsHandshakeCertificate = 0x0b; +const uint8_t kTlsHandshakeServerKeyExchange = 0x0c; + +const uint8_t kTlsFakeChangeCipherSpec[] = { + kTlsChangeCipherSpecType, // Type + 0xfe, + 0xff, // Version + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x10, // Fictitious sequence # + 0x00, + 0x01, // Length + 0x01 // Value +}; + +// Layer class which can't be initialized. +class TransportLayerDummy : public TransportLayer { + public: + TransportLayerDummy(bool allow_init, bool* destroyed) + : allow_init_(allow_init), destroyed_(destroyed) { + *destroyed_ = false; + } + + virtual ~TransportLayerDummy() { *destroyed_ = true; } + + nsresult InitInternal() override { + return allow_init_ ? NS_OK : NS_ERROR_FAILURE; + } + + TransportResult SendPacket(MediaPacket& packet) override { + MOZ_CRASH(); // Should never be called. + return 0; + } + + TRANSPORT_LAYER_ID("lossy") + + private: + bool allow_init_; + bool* destroyed_; +}; + +class Inspector { + public: + virtual ~Inspector() = default; + + virtual void Inspect(TransportLayer* layer, const unsigned char* data, + size_t len) = 0; +}; + +// Class to simulate various kinds of network lossage +class TransportLayerLossy : public TransportLayer { + public: + TransportLayerLossy() : loss_mask_(0), packet_(0), inspector_(nullptr) {} + ~TransportLayerLossy() = default; + + TransportResult SendPacket(MediaPacket& packet) override { + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "SendPacket(" << packet.len() << ")"); + + if (loss_mask_ & (1 << (packet_ % 32))) { + MOZ_MTLOG(ML_NOTICE, "Dropping packet"); + ++packet_; + return packet.len(); + } + if (inspector_) { + inspector_->Inspect(this, packet.data(), packet.len()); + } + + ++packet_; + + return downward_->SendPacket(packet); + } + + void SetLoss(uint32_t packet) { loss_mask_ |= (1 << (packet & 32)); } + + void SetInspector(UniquePtr<Inspector> inspector) { + inspector_ = std::move(inspector); + } + + void StateChange(TransportLayer* layer, State state) { TL_SET_STATE(state); } + + void PacketReceived(TransportLayer* layer, MediaPacket& packet) { + SignalPacketReceived(this, packet); + } + + TRANSPORT_LAYER_ID("lossy") + + protected: + void WasInserted() override { + downward_->SignalPacketReceived.connect( + this, &TransportLayerLossy::PacketReceived); + downward_->SignalStateChange.connect(this, + &TransportLayerLossy::StateChange); + + TL_SET_STATE(downward_->state()); + } + + private: + uint32_t loss_mask_; + uint32_t packet_; + UniquePtr<Inspector> inspector_; +}; + +// Process DTLS Records +#define CHECK_LENGTH(expected) \ + do { \ + EXPECT_GE(remaining(), expected); \ + if (remaining() < expected) return false; \ + } while (0) + +class TlsParser { + public: + TlsParser(const unsigned char* data, size_t len) : buffer_(), offset_(0) { + buffer_.Copy(data, len); + } + + bool Read(unsigned char* val) { + if (remaining() < 1) { + return false; + } + *val = *ptr(); + consume(1); + return true; + } + + // Read an integral type of specified width. + bool Read(uint32_t* val, size_t len) { + if (len > sizeof(uint32_t)) return false; + + *val = 0; + + for (size_t i = 0; i < len; ++i) { + unsigned char tmp; + + if (!Read(&tmp)) return false; + + (*val) = ((*val) << 8) + tmp; + } + + return true; + } + + bool Read(unsigned char* val, size_t len) { + if (remaining() < len) { + return false; + } + + if (val) { + memcpy(val, ptr(), len); + } + consume(len); + + return true; + } + + private: + size_t remaining() const { return buffer_.len() - offset_; } + const uint8_t* ptr() const { return buffer_.data() + offset_; } + void consume(size_t len) { offset_ += len; } + + MediaPacket buffer_; + size_t offset_; +}; + +class DtlsRecordParser { + public: + DtlsRecordParser(const unsigned char* data, size_t len) + : buffer_(), offset_(0) { + buffer_.Copy(data, len); + } + + bool NextRecord(uint8_t* ct, UniquePtr<MediaPacket>* buffer) { + if (!remaining()) return false; + + CHECK_LENGTH(13U); + const uint8_t* ctp = reinterpret_cast<const uint8_t*>(ptr()); + consume(11); // ct + version + length + + const uint16_t* tmp = reinterpret_cast<const uint16_t*>(ptr()); + size_t length = ntohs(*tmp); + consume(2); + + CHECK_LENGTH(length); + auto db = MakeUnique<MediaPacket>(); + db->Copy(ptr(), length); + consume(length); + + *ct = *ctp; + *buffer = std::move(db); + + return true; + } + + private: + size_t remaining() const { return buffer_.len() - offset_; } + const uint8_t* ptr() const { return buffer_.data() + offset_; } + void consume(size_t len) { offset_ += len; } + + MediaPacket buffer_; + size_t offset_; +}; + +// Inspector that parses out DTLS records and passes +// them on. +class DtlsRecordInspector : public Inspector { + public: + virtual void Inspect(TransportLayer* layer, const unsigned char* data, + size_t len) { + DtlsRecordParser parser(data, len); + + uint8_t ct; + UniquePtr<MediaPacket> buf; + while (parser.NextRecord(&ct, &buf)) { + OnRecord(layer, ct, buf->data(), buf->len()); + } + } + + virtual void OnRecord(TransportLayer* layer, uint8_t content_type, + const unsigned char* record, size_t len) = 0; +}; + +// Inspector that injects arbitrary packets based on +// DTLS records of various types. +class DtlsInspectorInjector : public DtlsRecordInspector { + public: + DtlsInspectorInjector(uint8_t packet_type, uint8_t handshake_type, + const unsigned char* data, size_t len) + : packet_type_(packet_type), handshake_type_(handshake_type) { + packet_.Copy(data, len); + } + + virtual void OnRecord(TransportLayer* layer, uint8_t content_type, + const unsigned char* data, size_t len) { + // Only inject once. + if (!packet_.data()) { + return; + } + + // Check that the first byte is as requested. + if (content_type != packet_type_) { + return; + } + + if (handshake_type_ != 0xff) { + // Check that the packet is plausibly long enough. + if (len < 1) { + return; + } + + // Check that the handshake type is as requested. + if (data[0] != handshake_type_) { + return; + } + } + + layer->SendPacket(packet_); + packet_.Reset(); + } + + private: + uint8_t packet_type_; + uint8_t handshake_type_; + MediaPacket packet_; +}; + +// Make a copy of the first instance of a message. +class DtlsInspectorRecordHandshakeMessage : public DtlsRecordInspector { + public: + explicit DtlsInspectorRecordHandshakeMessage(uint8_t handshake_type) + : handshake_type_(handshake_type), buffer_() {} + + virtual void OnRecord(TransportLayer* layer, uint8_t content_type, + const unsigned char* data, size_t len) { + // Only do this once. + if (buffer_.len()) { + return; + } + + // Check that the first byte is as requested. + if (content_type != kTlsHandshakeType) { + return; + } + + TlsParser parser(data, len); + unsigned char message_type; + // Read the handshake message type. + if (!parser.Read(&message_type)) { + return; + } + if (message_type != handshake_type_) { + return; + } + + uint32_t length; + if (!parser.Read(&length, 3)) { + return; + } + + uint32_t message_seq; + if (!parser.Read(&message_seq, 2)) { + return; + } + + uint32_t fragment_offset; + if (!parser.Read(&fragment_offset, 3)) { + return; + } + + uint32_t fragment_length; + if (!parser.Read(&fragment_length, 3)) { + return; + } + + if ((fragment_offset != 0) || (fragment_length != length)) { + // This shouldn't happen because all current tests where we + // are using this code don't fragment. + return; + } + + UniquePtr<uint8_t[]> buffer(new uint8_t[length]); + if (!parser.Read(buffer.get(), length)) { + return; + } + buffer_.Take(std::move(buffer), length); + } + + const MediaPacket& buffer() { return buffer_; } + + private: + uint8_t handshake_type_; + MediaPacket buffer_; +}; + +class TlsServerKeyExchangeECDHE { + public: + bool Parse(const unsigned char* data, size_t len) { + TlsParser parser(data, len); + + uint8_t curve_type; + if (!parser.Read(&curve_type)) { + return false; + } + + if (curve_type != 3) { // named_curve + return false; + } + + uint32_t named_curve; + if (!parser.Read(&named_curve, 2)) { + return false; + } + + uint32_t point_length; + if (!parser.Read(&point_length, 1)) { + return false; + } + + UniquePtr<uint8_t[]> key(new uint8_t[point_length]); + if (!parser.Read(key.get(), point_length)) { + return false; + } + public_key_.Take(std::move(key), point_length); + + return true; + } + + MediaPacket public_key_; +}; + +namespace { +class TransportTestPeer : public sigslot::has_slots<> { + public: + TransportTestPeer(nsCOMPtr<nsIEventTarget> target, std::string name, + MtransportTestUtils* utils) + : name_(name), + offerer_(name == "P1"), + target_(target), + received_packets_(0), + received_bytes_(0), + flow_(new TransportFlow(name)), + loopback_(new TransportLayerLoopback()), + logging_(new TransportLayerLogging()), + lossy_(new TransportLayerLossy()), + dtls_(new TransportLayerDtls()), + identity_(DtlsIdentity::Generate()), + ice_ctx_(), + streams_(), + peer_(nullptr), + gathering_complete_(false), + digest_("sha-1"), + enabled_cipersuites_(), + disabled_cipersuites_(), + test_utils_(utils) { + NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig()); + ice_ctx_ = NrIceCtx::Create(name); + std::vector<NrIceStunServer> stun_servers; + UniquePtr<NrIceStunServer> server(NrIceStunServer::Create( + std::string((char*)"stun.services.mozilla.com"), 3478)); + stun_servers.push_back(*server); + EXPECT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers))); + + dtls_->SetIdentity(identity_); + dtls_->SetRole(offerer_ ? TransportLayerDtls::SERVER + : TransportLayerDtls::CLIENT); + + nsresult res = identity_->ComputeFingerprint(&digest_); + EXPECT_TRUE(NS_SUCCEEDED(res)); + EXPECT_EQ(20u, digest_.value_.size()); + } + + ~TransportTestPeer() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(this, &TransportTestPeer::DestroyFlow), NS_DISPATCH_SYNC); + } + + void DestroyFlow() { + disconnect_all(); + if (flow_) { + loopback_->Disconnect(); + flow_ = nullptr; + } + ice_ctx_->Destroy(); + ice_ctx_ = nullptr; + streams_.clear(); + } + + void DisconnectDestroyFlow() { + test_utils_->sts_target()->Dispatch( + NS_NewRunnableFunction( + __func__, + [this] { + loopback_->Disconnect(); + disconnect_all(); // Disconnect from the signals; + flow_ = nullptr; + }), + NS_DISPATCH_SYNC); + } + + void SetDtlsAllowAll() { + nsresult res = dtls_->SetVerificationAllowAll(); + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + + void SetAlpn(std::string str, bool withDefault, std::string extra = "") { + std::set<std::string> alpn; + alpn.insert(str); // the one we want to select + if (!extra.empty()) { + alpn.insert(extra); + } + nsresult res = dtls_->SetAlpn(alpn, withDefault ? str : ""); + ASSERT_EQ(NS_OK, res); + } + + const std::string& GetAlpn() const { return dtls_->GetNegotiatedAlpn(); } + + void SetDtlsPeer(TransportTestPeer* peer, int digests, unsigned int damage) { + unsigned int mask = 1; + + for (int i = 0; i < digests; i++) { + DtlsDigest digest_to_set(peer->digest_); + + if (damage & mask) digest_to_set.value_.data()[0]++; + + nsresult res = dtls_->SetVerificationDigest(digest_to_set); + + ASSERT_TRUE(NS_SUCCEEDED(res)); + + mask <<= 1; + } + } + + void SetupSrtp() { + std::vector<uint16_t> srtp_ciphers = + TransportLayerDtls::GetDefaultSrtpCiphers(); + SetSrtpCiphers(srtp_ciphers); + } + + void SetSrtpCiphers(std::vector<uint16_t>& srtp_ciphers) { + ASSERT_TRUE(NS_SUCCEEDED(dtls_->SetSrtpCiphers(srtp_ciphers))); + } + + void ConnectSocket_s(TransportTestPeer* peer) { + nsresult res; + res = loopback_->Init(); + ASSERT_EQ((nsresult)NS_OK, res); + + loopback_->Connect(peer->loopback_); + ASSERT_EQ((nsresult)NS_OK, loopback_->Init()); + ASSERT_EQ((nsresult)NS_OK, logging_->Init()); + ASSERT_EQ((nsresult)NS_OK, lossy_->Init()); + ASSERT_EQ((nsresult)NS_OK, dtls_->Init()); + dtls_->Chain(lossy_); + lossy_->Chain(logging_); + logging_->Chain(loopback_); + + flow_->PushLayer(loopback_); + flow_->PushLayer(logging_); + flow_->PushLayer(lossy_); + flow_->PushLayer(dtls_); + + if (dtls_->state() != TransportLayer::TS_ERROR) { + // Don't execute these blocks if DTLS didn't initialize. + TweakCiphers(dtls_->internal_fd()); + if (post_setup_) { + post_setup_(dtls_->internal_fd()); + } + } + + dtls_->SignalPacketReceived.connect(this, + &TransportTestPeer::PacketReceived); + } + + void TweakCiphers(PRFileDesc* fd) { + for (unsigned short& enabled_cipersuite : enabled_cipersuites_) { + SSL_CipherPrefSet(fd, enabled_cipersuite, PR_TRUE); + } + for (unsigned short& disabled_cipersuite : disabled_cipersuites_) { + SSL_CipherPrefSet(fd, disabled_cipersuite, PR_FALSE); + } + } + + void ConnectSocket(TransportTestPeer* peer) { + RUN_ON_THREAD(test_utils_->sts_target(), + WrapRunnable(this, &TransportTestPeer::ConnectSocket_s, peer), + NS_DISPATCH_SYNC); + } + + nsresult InitIce_s() { + nsresult rv = ice_->Init(); + NS_ENSURE_SUCCESS(rv, rv); + rv = dtls_->Init(); + NS_ENSURE_SUCCESS(rv, rv); + dtls_->Chain(ice_); + flow_->PushLayer(ice_); + flow_->PushLayer(dtls_); + return NS_OK; + } + + void InitIce() { + nsresult res; + + // Attach our slots + ice_ctx_->SignalGatheringStateChange.connect( + this, &TransportTestPeer::GatheringStateChange); + + char name[100]; + snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(), + (int)streams_.size()); + + // Create the media stream + RefPtr<NrIceMediaStream> stream = ice_ctx_->CreateStream(name, name, 1); + + ASSERT_TRUE(stream != nullptr); + stream->SetIceCredentials("ufrag", "pass"); + streams_.push_back(stream); + + // Listen for candidates + stream->SignalCandidate.connect(this, &TransportTestPeer::GotCandidate); + + // Create the transport layer + ice_ = new TransportLayerIce(); + ice_->SetParameters(stream, 1); + + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&res, this, &TransportTestPeer::InitIce_s), + NS_DISPATCH_SYNC); + + ASSERT_EQ((nsresult)NS_OK, res); + + // Listen for media events + dtls_->SignalPacketReceived.connect(this, + &TransportTestPeer::PacketReceived); + dtls_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged); + + // Start gathering + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&res, ice_ctx_, &NrIceCtx::StartGathering, false, + false), + NS_DISPATCH_SYNC); + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + + void ConnectIce(TransportTestPeer* peer) { + peer_ = peer; + + // If gathering is already complete, push the candidates over + if (gathering_complete_) GatheringComplete(); + } + + // New candidate + void GotCandidate(NrIceMediaStream* stream, const std::string& candidate, + const std::string& ufrag, const std::string& mdns_addr, + const std::string& actual_addr) { + std::cerr << "Got candidate " << candidate << " (ufrag=" << ufrag << ")" + << std::endl; + } + + void GatheringStateChange(NrIceCtx* ctx, NrIceCtx::GatheringState state) { + (void)ctx; + if (state == NrIceCtx::ICE_CTX_GATHER_COMPLETE) { + GatheringComplete(); + } + } + + // Gathering complete, so send our candidates and start + // connecting on the other peer. + void GatheringComplete() { + nsresult res; + + // Don't send to the other side + if (!peer_) { + gathering_complete_ = true; + return; + } + + // First send attributes + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&res, peer_->ice_ctx_, &NrIceCtx::ParseGlobalAttributes, + ice_ctx_->GetGlobalAttributes()), + NS_DISPATCH_SYNC); + ASSERT_TRUE(NS_SUCCEEDED(res)); + + for (size_t i = 0; i < streams_.size(); ++i) { + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&res, peer_->streams_[i], + &NrIceMediaStream::ConnectToPeer, "ufrag", "pass", + streams_[i]->GetAttributes()), + NS_DISPATCH_SYNC); + + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + + // Start checks on the other peer. + test_utils_->sts_target()->Dispatch( + WrapRunnableRet(&res, peer_->ice_ctx_, &NrIceCtx::StartChecks), + NS_DISPATCH_SYNC); + ASSERT_TRUE(NS_SUCCEEDED(res)); + } + + // WrapRunnable/lambda and move semantics (MediaPacket is not copyable) don't + // get along yet, so we need a wrapper. Gross. + static TransportResult SendPacketWrapper(TransportLayer* layer, + MediaPacket* packet) { + return layer->SendPacket(*packet); + } + + TransportResult SendPacket(MediaPacket& packet) { + TransportResult ret; + + test_utils_->sts_target()->Dispatch( + WrapRunnableNMRet(&ret, &TransportTestPeer::SendPacketWrapper, dtls_, + &packet), + NS_DISPATCH_SYNC); + + return ret; + } + + void StateChanged(TransportLayer* layer, TransportLayer::State state) { + if (state == TransportLayer::TS_OPEN) { + std::cerr << "Now connected" << std::endl; + } + } + + void PacketReceived(TransportLayer* layer, MediaPacket& packet) { + std::cerr << "Received " << packet.len() << " bytes" << std::endl; + ++received_packets_; + received_bytes_ += packet.len(); + } + + void SetLoss(uint32_t loss) { lossy_->SetLoss(loss); } + + void SetCombinePackets(bool combine) { loopback_->CombinePackets(combine); } + + void SetInspector(UniquePtr<Inspector> inspector) { + lossy_->SetInspector(std::move(inspector)); + } + + void SetInspector(Inspector* in) { + UniquePtr<Inspector> inspector(in); + + lossy_->SetInspector(std::move(inspector)); + } + + void SetCipherSuiteChanges(const std::vector<uint16_t>& enableThese, + const std::vector<uint16_t>& disableThese) { + disabled_cipersuites_ = disableThese; + enabled_cipersuites_ = enableThese; + } + + void SetPostSetup(const std::function<void(PRFileDesc*)>& setup) { + post_setup_ = std::move(setup); + } + + TransportLayer::State state() { + TransportLayer::State tstate; + + RUN_ON_THREAD(test_utils_->sts_target(), + WrapRunnableRet(&tstate, dtls_, &TransportLayer::state)); + + return tstate; + } + + bool connected() { return state() == TransportLayer::TS_OPEN; } + + bool failed() { return state() == TransportLayer::TS_ERROR; } + + size_t receivedPackets() { return received_packets_; } + + size_t receivedBytes() { return received_bytes_; } + + uint16_t cipherSuite() const { + nsresult rv; + uint16_t cipher; + RUN_ON_THREAD( + test_utils_->sts_target(), + WrapRunnableRet(&rv, dtls_, &TransportLayerDtls::GetCipherSuite, + &cipher)); + + if (NS_FAILED(rv)) { + return TLS_NULL_WITH_NULL_NULL; // i.e., not good + } + return cipher; + } + + uint16_t srtpCipher() const { + nsresult rv; + uint16_t cipher; + RUN_ON_THREAD(test_utils_->sts_target(), + WrapRunnableRet(&rv, dtls_, + &TransportLayerDtls::GetSrtpCipher, &cipher)); + if (NS_FAILED(rv)) { + return 0; // the SRTP equivalent of TLS_NULL_WITH_NULL_NULL + } + return cipher; + } + + private: + std::string name_; + bool offerer_; + nsCOMPtr<nsIEventTarget> target_; + size_t received_packets_; + size_t received_bytes_; + RefPtr<TransportFlow> flow_; + TransportLayerLoopback* loopback_; + TransportLayerLogging* logging_; + TransportLayerLossy* lossy_; + TransportLayerDtls* dtls_; + TransportLayerIce* ice_; + RefPtr<DtlsIdentity> identity_; + RefPtr<NrIceCtx> ice_ctx_; + std::vector<RefPtr<NrIceMediaStream> > streams_; + TransportTestPeer* peer_; + bool gathering_complete_; + DtlsDigest digest_; + std::vector<uint16_t> enabled_cipersuites_; + std::vector<uint16_t> disabled_cipersuites_; + MtransportTestUtils* test_utils_; + std::function<void(PRFileDesc* fd)> post_setup_ = nullptr; +}; + +class TransportTest : public MtransportTest { + public: + TransportTest() { + fds_[0] = nullptr; + fds_[1] = nullptr; + p1_ = nullptr; + p2_ = nullptr; + } + + void TearDown() override { + delete p1_; + delete p2_; + + // Can't detach these + // PR_Close(fds_[0]); + // PR_Close(fds_[1]); + MtransportTest::TearDown(); + } + + void DestroyPeerFlows() { + p1_->DisconnectDestroyFlow(); + p2_->DisconnectDestroyFlow(); + } + + void SetUp() override { + MtransportTest::SetUp(); + + nsresult rv; + target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + Reset(); + } + + void Reset() { + if (p1_) { + delete p1_; + } + if (p2_) { + delete p2_; + } + p1_ = new TransportTestPeer(target_, "P1", test_utils_); + p2_ = new TransportTestPeer(target_, "P2", test_utils_); + } + + void SetupSrtp() { + p1_->SetupSrtp(); + p2_->SetupSrtp(); + } + + void SetDtlsPeer(int digests = 1, unsigned int damage = 0) { + p1_->SetDtlsPeer(p2_, digests, damage); + p2_->SetDtlsPeer(p1_, digests, damage); + } + + void SetDtlsAllowAll() { + p1_->SetDtlsAllowAll(); + p2_->SetDtlsAllowAll(); + } + + void SetAlpn(std::string first, std::string second, + bool withDefaults = true) { + if (!first.empty()) { + p1_->SetAlpn(first, withDefaults, "bogus"); + } + if (!second.empty()) { + p2_->SetAlpn(second, withDefaults); + } + } + + void CheckAlpn(std::string first, std::string second) { + ASSERT_EQ(first, p1_->GetAlpn()); + ASSERT_EQ(second, p2_->GetAlpn()); + } + + void ConnectSocket() { + ConnectSocketInternal(); + ASSERT_TRUE_WAIT(p1_->connected(), 10000); + ASSERT_TRUE_WAIT(p2_->connected(), 10000); + + ASSERT_EQ(p1_->cipherSuite(), p2_->cipherSuite()); + ASSERT_EQ(p1_->srtpCipher(), p2_->srtpCipher()); + } + + void ConnectSocketExpectFail() { + ConnectSocketInternal(); + ASSERT_TRUE_WAIT(p1_->failed(), 10000); + ASSERT_TRUE_WAIT(p2_->failed(), 10000); + } + + void ConnectSocketExpectState(TransportLayer::State s1, + TransportLayer::State s2) { + ConnectSocketInternal(); + ASSERT_EQ_WAIT(s1, p1_->state(), 10000); + ASSERT_EQ_WAIT(s2, p2_->state(), 10000); + } + + void ConnectIce() { + p1_->InitIce(); + p2_->InitIce(); + p1_->ConnectIce(p2_); + p2_->ConnectIce(p1_); + ASSERT_TRUE_WAIT(p1_->connected(), 10000); + ASSERT_TRUE_WAIT(p2_->connected(), 10000); + } + + void TransferTest(size_t count, size_t bytes = 1024) { + unsigned char buf[bytes]; + + for (size_t i = 0; i < count; ++i) { + memset(buf, count & 0xff, sizeof(buf)); + MediaPacket packet; + packet.Copy(buf, sizeof(buf)); + TransportResult rv = p1_->SendPacket(packet); + ASSERT_TRUE(rv > 0); + } + + std::cerr << "Received == " << p2_->receivedPackets() << " packets" + << std::endl; + ASSERT_TRUE_WAIT(count == p2_->receivedPackets(), 10000); + ASSERT_TRUE((count * sizeof(buf)) == p2_->receivedBytes()); + } + + protected: + void ConnectSocketInternal() { + test_utils_->sts_target()->Dispatch( + WrapRunnable(p1_, &TransportTestPeer::ConnectSocket, p2_), + NS_DISPATCH_SYNC); + test_utils_->sts_target()->Dispatch( + WrapRunnable(p2_, &TransportTestPeer::ConnectSocket, p1_), + NS_DISPATCH_SYNC); + } + + PRFileDesc* fds_[2]; + TransportTestPeer* p1_; + TransportTestPeer* p2_; + nsCOMPtr<nsIEventTarget> target_; +}; + +TEST_F(TransportTest, TestNoDtlsVerificationSettings) { + ConnectSocketExpectFail(); +} + +static void DisableChaCha(TransportTestPeer* peer) { + // On ARM, ChaCha20Poly1305 might be preferred; disable it for the tests that + // want to check the cipher suite. It doesn't matter which peer disables the + // suite, disabling on either side has the same effect. + std::vector<uint16_t> chachaSuites; + chachaSuites.push_back(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); + chachaSuites.push_back(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); + peer->SetCipherSuiteChanges(std::vector<uint16_t>(), chachaSuites); +} + +TEST_F(TransportTest, TestConnect) { + SetDtlsPeer(); + DisableChaCha(p1_); + ConnectSocket(); + + // check that we got the right suite + ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, p1_->cipherSuite()); + + // no SRTP on this one + ASSERT_EQ(0, p1_->srtpCipher()); +} + +TEST_F(TransportTest, TestConnectSrtp) { + SetupSrtp(); + SetDtlsPeer(); + DisableChaCha(p2_); + ConnectSocket(); + + ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, p1_->cipherSuite()); + + // SRTP is on with default value + ASSERT_EQ(kDtlsSrtpAeadAes128Gcm, p1_->srtpCipher()); +} + +TEST_F(TransportTest, TestConnectDestroyFlowsMainThread) { + SetDtlsPeer(); + ConnectSocket(); + DestroyPeerFlows(); +} + +TEST_F(TransportTest, TestConnectAllowAll) { + SetDtlsAllowAll(); + ConnectSocket(); +} + +TEST_F(TransportTest, TestConnectAlpn) { + SetDtlsPeer(); + SetAlpn("a", "a"); + ConnectSocket(); + CheckAlpn("a", "a"); +} + +TEST_F(TransportTest, TestConnectAlpnMismatch) { + SetDtlsPeer(); + SetAlpn("something", "different"); + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestConnectAlpnServerDefault) { + SetDtlsPeer(); + SetAlpn("def", ""); + // server allows default, client doesn't support + ConnectSocket(); + CheckAlpn("def", ""); +} + +TEST_F(TransportTest, TestConnectAlpnClientDefault) { + SetDtlsPeer(); + SetAlpn("", "clientdef"); + // client allows default, but server will ignore the extension + ConnectSocket(); + CheckAlpn("", "clientdef"); +} + +TEST_F(TransportTest, TestConnectClientNoAlpn) { + SetDtlsPeer(); + // Here the server has ALPN, but no default is allowed. + // Reminder: p1 == server, p2 == client + SetAlpn("server-nodefault", "", false); + // The server doesn't see the extension, so negotiates without it. + // But then the server is forced to close when it discovers that ALPN wasn't + // negotiated; the client sees a close. + ConnectSocketExpectState(TransportLayer::TS_ERROR, TransportLayer::TS_CLOSED); +} + +TEST_F(TransportTest, TestConnectServerNoAlpn) { + SetDtlsPeer(); + SetAlpn("", "client-nodefault", false); + // The client aborts; the server doesn't realize this is a problem and just + // sees the close. + ConnectSocketExpectState(TransportLayer::TS_CLOSED, TransportLayer::TS_ERROR); +} + +TEST_F(TransportTest, TestConnectNoDigest) { + SetDtlsPeer(0, 0); + + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestConnectBadDigest) { + SetDtlsPeer(1, 1); + + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestConnectTwoDigests) { + SetDtlsPeer(2, 0); + + ConnectSocket(); +} + +TEST_F(TransportTest, TestConnectTwoDigestsFirstBad) { + SetDtlsPeer(2, 1); + + ConnectSocket(); +} + +TEST_F(TransportTest, TestConnectTwoDigestsSecondBad) { + SetDtlsPeer(2, 2); + + ConnectSocket(); +} + +TEST_F(TransportTest, TestConnectTwoDigestsBothBad) { + SetDtlsPeer(2, 3); + + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestConnectInjectCCS) { + SetDtlsPeer(); + p2_->SetInspector(MakeUnique<DtlsInspectorInjector>( + kTlsHandshakeType, kTlsHandshakeCertificate, kTlsFakeChangeCipherSpec, + sizeof(kTlsFakeChangeCipherSpec))); + + ConnectSocket(); +} + +TEST_F(TransportTest, TestConnectVerifyNewECDHE) { + SetDtlsPeer(); + DtlsInspectorRecordHandshakeMessage* i1 = + new DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); + p1_->SetInspector(i1); + ConnectSocket(); + TlsServerKeyExchangeECDHE dhe1; + ASSERT_TRUE(dhe1.Parse(i1->buffer().data(), i1->buffer().len())); + + Reset(); + SetDtlsPeer(); + DtlsInspectorRecordHandshakeMessage* i2 = + new DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); + p1_->SetInspector(i2); + ConnectSocket(); + TlsServerKeyExchangeECDHE dhe2; + ASSERT_TRUE(dhe2.Parse(i2->buffer().data(), i2->buffer().len())); + + // Now compare these two to see if they are the same. + ASSERT_FALSE((dhe1.public_key_.len() == dhe2.public_key_.len()) && + (!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(), + dhe1.public_key_.len()))); +} + +TEST_F(TransportTest, TestConnectVerifyReusedECDHE) { + auto set_reuse_ecdhe_key = [](PRFileDesc* fd) { + // TransportLayerDtls automatically sets this pref to false + // so set it back for test. + // This is pretty gross. Dig directly into the NSS FD. The problem + // is that we are testing a feature which TransaportLayerDtls doesn't + // expose. + SECStatus rv = SSL_OptionSet(fd, SSL_REUSE_SERVER_ECDHE_KEY, PR_TRUE); + ASSERT_EQ(SECSuccess, rv); + }; + + SetDtlsPeer(); + DtlsInspectorRecordHandshakeMessage* i1 = + new DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); + p1_->SetInspector(i1); + p1_->SetPostSetup(set_reuse_ecdhe_key); + ConnectSocket(); + TlsServerKeyExchangeECDHE dhe1; + ASSERT_TRUE(dhe1.Parse(i1->buffer().data(), i1->buffer().len())); + + Reset(); + SetDtlsPeer(); + DtlsInspectorRecordHandshakeMessage* i2 = + new DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange); + + p1_->SetInspector(i2); + p1_->SetPostSetup(set_reuse_ecdhe_key); + + ConnectSocket(); + TlsServerKeyExchangeECDHE dhe2; + ASSERT_TRUE(dhe2.Parse(i2->buffer().data(), i2->buffer().len())); + + // Now compare these two to see if they are the same. + ASSERT_EQ(dhe1.public_key_.len(), dhe2.public_key_.len()); + ASSERT_TRUE(!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(), + dhe1.public_key_.len())); +} + +TEST_F(TransportTest, TestTransfer) { + SetDtlsPeer(); + ConnectSocket(); + TransferTest(1); +} + +TEST_F(TransportTest, TestTransferMaxSize) { + SetDtlsPeer(); + ConnectSocket(); + /* transportlayerdtls uses a 9216 bytes buffer - as this test uses the + * loopback implementation it does not have to take into account the extra + * bytes added by the DTLS layer below. */ + TransferTest(1, 9216); +} + +TEST_F(TransportTest, TestTransferMultiple) { + SetDtlsPeer(); + ConnectSocket(); + TransferTest(3); +} + +TEST_F(TransportTest, TestTransferCombinedPackets) { + SetDtlsPeer(); + ConnectSocket(); + p2_->SetCombinePackets(true); + TransferTest(3); +} + +TEST_F(TransportTest, TestConnectLoseFirst) { + SetDtlsPeer(); + p1_->SetLoss(0); + ConnectSocket(); + TransferTest(1); +} + +TEST_F(TransportTest, TestConnectIce) { + SetDtlsPeer(); + ConnectIce(); +} + +TEST_F(TransportTest, TestTransferIceMaxSize) { + SetDtlsPeer(); + ConnectIce(); + /* nICEr and transportlayerdtls both use 9216 bytes buffers. But the DTLS + * layer add extra bytes to the packet, which size depends on chosen cipher + * etc. Sending more then 9216 bytes works, but on the receiving side the call + * to PR_recvfrom() will truncate any packet bigger then nICEr's buffer size + * of 9216 bytes, which then results in the DTLS layer discarding the packet. + * Therefore we leave some headroom (according to + * https://bugzilla.mozilla.org/show_bug.cgi?id=1214269#c29 256 bytes should + * be save choice) here for the DTLS bytes to make it safely into the + * receiving buffer in nICEr. */ + TransferTest(1, 8960); +} + +TEST_F(TransportTest, TestTransferIceMultiple) { + SetDtlsPeer(); + ConnectIce(); + TransferTest(3); +} + +TEST_F(TransportTest, TestTransferIceCombinedPackets) { + SetDtlsPeer(); + ConnectIce(); + p2_->SetCombinePackets(true); + TransferTest(3); +} + +// test the default configuration against a peer that supports only +// one of the mandatory-to-implement suites, which should succeed +static void ConfigureOneCipher(TransportTestPeer* peer, uint16_t suite) { + std::vector<uint16_t> justOne; + justOne.push_back(suite); + std::vector<uint16_t> everythingElse( + SSL_GetImplementedCiphers(), + SSL_GetImplementedCiphers() + SSL_GetNumImplementedCiphers()); + std::remove(everythingElse.begin(), everythingElse.end(), suite); + peer->SetCipherSuiteChanges(justOne, everythingElse); +} + +TEST_F(TransportTest, TestCipherMismatch) { + SetDtlsPeer(); + ConfigureOneCipher(p1_, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + ConfigureOneCipher(p2_, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestCipherMandatoryOnlyGcm) { + SetDtlsPeer(); + ConfigureOneCipher(p1_, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); + ConnectSocket(); + ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, p1_->cipherSuite()); +} + +TEST_F(TransportTest, TestCipherMandatoryOnlyCbc) { + SetDtlsPeer(); + ConfigureOneCipher(p1_, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); + ConnectSocket(); + ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, p1_->cipherSuite()); +} + +TEST_F(TransportTest, TestSrtpMismatch) { + std::vector<uint16_t> setA; + setA.push_back(kDtlsSrtpAes128CmHmacSha1_80); + std::vector<uint16_t> setB; + setB.push_back(kDtlsSrtpAes128CmHmacSha1_32); + + p1_->SetSrtpCiphers(setA); + p2_->SetSrtpCiphers(setB); + SetDtlsPeer(); + ConnectSocketExpectFail(); + + ASSERT_EQ(0, p1_->srtpCipher()); + ASSERT_EQ(0, p2_->srtpCipher()); +} + +static SECStatus NoopXtnHandler(PRFileDesc* fd, SSLHandshakeType message, + const uint8_t* data, unsigned int len, + SSLAlertDescription* alert, void* arg) { + return SECSuccess; +} + +static PRBool WriteFixedXtn(PRFileDesc* fd, SSLHandshakeType message, + uint8_t* data, unsigned int* len, + unsigned int max_len, void* arg) { + // When we enable TLS 1.3, change ssl_hs_server_hello here to + // ssl_hs_encrypted_extensions. At the same time, add a test that writes to + // ssl_hs_server_hello, which should fail. + if (message != ssl_hs_client_hello && message != ssl_hs_server_hello) { + return false; + } + + auto v = reinterpret_cast<std::vector<uint8_t>*>(arg); + memcpy(data, &((*v)[0]), v->size()); + *len = v->size(); + return true; +} + +// Note that |value| needs to be readable after this function returns. +static void InstallBadSrtpExtensionWriter(TransportTestPeer* peer, + std::vector<uint8_t>* value) { + peer->SetPostSetup([value](PRFileDesc* fd) { + // Override the handler that is installed by the DTLS setup. + SECStatus rv = SSL_InstallExtensionHooks( + fd, ssl_use_srtp_xtn, WriteFixedXtn, value, NoopXtnHandler, nullptr); + ASSERT_EQ(SECSuccess, rv); + }); +} + +TEST_F(TransportTest, TestSrtpErrorServerSendsTwoSrtpCiphers) { + // Server (p1_) sends an extension with two values, and empty MKI. + std::vector<uint8_t> xtn = {0x04, 0x00, 0x01, 0x00, 0x02, 0x00}; + InstallBadSrtpExtensionWriter(p1_, &xtn); + SetupSrtp(); + SetDtlsPeer(); + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestSrtpErrorServerSendsTwoMki) { + // Server (p1_) sends an MKI. + std::vector<uint8_t> xtn = {0x02, 0x00, 0x01, 0x01, 0x00}; + InstallBadSrtpExtensionWriter(p1_, &xtn); + SetupSrtp(); + SetDtlsPeer(); + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestSrtpErrorServerSendsUnknownValue) { + std::vector<uint8_t> xtn = {0x02, 0x9a, 0xf1, 0x00}; + InstallBadSrtpExtensionWriter(p1_, &xtn); + SetupSrtp(); + SetDtlsPeer(); + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestSrtpErrorServerSendsOverflow) { + std::vector<uint8_t> xtn = {0x32, 0x00, 0x01, 0x00}; + InstallBadSrtpExtensionWriter(p1_, &xtn); + SetupSrtp(); + SetDtlsPeer(); + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestSrtpErrorServerSendsUnevenList) { + std::vector<uint8_t> xtn = {0x01, 0x00, 0x00}; + InstallBadSrtpExtensionWriter(p1_, &xtn); + SetupSrtp(); + SetDtlsPeer(); + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, TestSrtpErrorClientSendsUnevenList) { + std::vector<uint8_t> xtn = {0x01, 0x00, 0x00}; + InstallBadSrtpExtensionWriter(p2_, &xtn); + SetupSrtp(); + SetDtlsPeer(); + ConnectSocketExpectFail(); +} + +TEST_F(TransportTest, OnlyServerSendsSrtpXtn) { + p1_->SetupSrtp(); + SetDtlsPeer(); + // This should connect, but with no SRTP extension neogtiated. + // The client side might negotiate a data channel only. + ConnectSocket(); + ASSERT_NE(TLS_NULL_WITH_NULL_NULL, p1_->cipherSuite()); + ASSERT_EQ(0, p1_->srtpCipher()); +} + +TEST_F(TransportTest, OnlyClientSendsSrtpXtn) { + p2_->SetupSrtp(); + SetDtlsPeer(); + // This should connect, but with no SRTP extension neogtiated. + // The server side might negotiate a data channel only. + ConnectSocket(); + ASSERT_NE(TLS_NULL_WITH_NULL_NULL, p1_->cipherSuite()); + ASSERT_EQ(0, p1_->srtpCipher()); +} + +class TransportSrtpParameterTest + : public TransportTest, + public ::testing::WithParamInterface<uint16_t> {}; + +INSTANTIATE_TEST_SUITE_P( + SrtpParamInit, TransportSrtpParameterTest, + ::testing::ValuesIn(TransportLayerDtls::GetDefaultSrtpCiphers())); + +TEST_P(TransportSrtpParameterTest, TestSrtpCiphersMismatchCombinations) { + uint16_t cipher = GetParam(); + std::cerr << "Checking cipher: " << cipher << std::endl; + + p1_->SetupSrtp(); + + std::vector<uint16_t> setB; + setB.push_back(cipher); + + p2_->SetSrtpCiphers(setB); + SetDtlsPeer(); + ConnectSocket(); + + ASSERT_EQ(cipher, p1_->srtpCipher()); + ASSERT_EQ(cipher, p2_->srtpCipher()); +} + +// NSS doesn't support DHE suites on the server end. +// This checks to see if we barf when that's the only option available. +TEST_F(TransportTest, TestDheOnlyFails) { + SetDtlsPeer(); + + // p2_ is the client + // setting this on p1_ (the server) causes NSS to assert + ConfigureOneCipher(p2_, TLS_DHE_RSA_WITH_AES_128_CBC_SHA); + ConnectSocketExpectFail(); +} + +} // end namespace diff --git a/dom/media/webrtc/transport/test/turn_unittest.cpp b/dom/media/webrtc/transport/test/turn_unittest.cpp new file mode 100644 index 0000000000..d9e6facc0b --- /dev/null +++ b/dom/media/webrtc/transport/test/turn_unittest.cpp @@ -0,0 +1,440 @@ +/* -*- 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 + +// Some code copied from nICEr. License is: +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <stdlib.h> +#include <iostream> + +#include "runnable_utils.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +#define USE_TURN + +// nICEr includes +extern "C" { +#include "nr_api.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 "nricectx.h" + +using namespace mozilla; + +static std::string kDummyTurnServer("192.0.2.1"); // From RFC 5737 + +class TurnClient : public MtransportTest { + public: + TurnClient() + : MtransportTest(), + real_socket_(nullptr), + net_socket_(nullptr), + buffered_socket_(nullptr), + net_fd_(nullptr), + turn_ctx_(nullptr), + allocated_(false), + received_(0), + protocol_(IPPROTO_UDP) {} + + ~TurnClient() = default; + + static void SetUpTestCase() { + NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig()); + } + + 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); + + r = nr_socket_local_create(nullptr, &addr, &real_socket_); + ASSERT_EQ(0, r); + + if (protocol_ == IPPROTO_TCP) { + int r = nr_socket_buffered_stun_create( + real_socket_, 100000, TURN_TCP_FRAMING, &buffered_socket_); + ASSERT_EQ(0, r); + net_socket_ = buffered_socket_; + } else { + net_socket_ = real_socket_; + } + + r = nr_str_port_to_transport_addr(turn_server_.c_str(), 3478, protocol_, + &addr); + ASSERT_EQ(0, r); + + std::vector<unsigned char> password_vec(turn_password_.begin(), + turn_password_.end()); + Data password; + INIT_DATA(password, &password_vec[0], password_vec.size()); + r = nr_turn_client_ctx_create("test", net_socket_, turn_user_.c_str(), + &password, &addr, nullptr, &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 TearDown_s() { + nr_turn_client_ctx_destroy(&turn_ctx_); + if (net_fd_) { + NR_ASYNC_CANCEL(net_fd_, NR_ASYNC_WAIT_READ); + } + + 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) { + RUN_ON_THREAD(test_utils_->sts_target(), + WrapRunnable(this, &TurnClient::Allocate_s), + NS_DISPATCH_SYNC); + + if (expect_success) { + ASSERT_TRUE_WAIT(allocated_, 5000); + } else { + PR_Sleep(10000); + ASSERT_FALSE(allocated_); + } + } + + void Allocated() { + if (turn_ctx_->state != NR_TURN_CLIENT_STATE_ALLOCATED) { + std::cerr << "Allocation failed" << std::endl; + return; + } + allocated_ = true; + + int r; + nr_transport_addr addr; + + r = nr_turn_client_get_relayed_address(turn_ctx_, &addr); + ASSERT_EQ(0, r); + + relay_addr_ = addr.as_string; + + std::cerr << "Allocation succeeded with addr=" << relay_addr_ << std::endl; + } + + void Deallocate_s() { + ASSERT_TRUE(turn_ctx_); + + std::cerr << "De-Allocating..." << std::endl; + int r = nr_turn_client_deallocate(turn_ctx_); + ASSERT_EQ(0, r); + } + + void Deallocate() { + RUN_ON_THREAD(test_utils_->sts_target(), + WrapRunnable(this, &TurnClient::Deallocate_s), + NS_DISPATCH_SYNC); + } + + void RequestPermission_s(const std::string& target) { + nr_transport_addr addr; + int r; + + // Expected pattern here is "IP4:127.0.0.1:3487" + ASSERT_EQ(0, target.compare(0, 4, "IP4:")); + + size_t offset = target.rfind(':'); + ASSERT_NE(std::string::npos, offset); + + std::string host = target.substr(4, offset - 4); + std::string port = target.substr(offset + 1); + + r = nr_str_port_to_transport_addr(host.c_str(), atoi(port.c_str()), + IPPROTO_UDP, &addr); + ASSERT_EQ(0, r); + + r = nr_turn_client_ensure_perm(turn_ctx_, &addr); + ASSERT_EQ(0, r); + } + + void RequestPermission(const std::string& target) { + RUN_ON_THREAD(test_utils_->sts_target(), + WrapRunnable(this, &TurnClient::RequestPermission_s, target), + NS_DISPATCH_SYNC); + } + + void Readable(NR_SOCKET s, int how, void* arg) { + // Re-arm + std::cerr << "Socket is readable" << std::endl; + NR_ASYNC_WAIT(s, how, socket_readable_cb, arg); + + UCHAR buf[8192]; + size_t len_s; + nr_transport_addr addr; + + int r = nr_socket_recvfrom(net_socket_, buf, sizeof(buf), &len_s, 0, &addr); + if (r) { + std::cerr << "Error reading from socket" << std::endl; + return; + } + + ASSERT_LT(len_s, (size_t)INT_MAX); + int len = (int)len_s; + + if (nr_is_stun_response_message(buf, len)) { + std::cerr << "STUN response" << std::endl; + r = nr_turn_client_process_response(turn_ctx_, buf, len, &addr); + + if (r && r != R_REJECTED && r != R_RETRY) { + std::cerr << "Error processing STUN: " << r << std::endl; + } + } else if (nr_is_stun_indication_message(buf, len)) { + std::cerr << "STUN indication" << std::endl; + + /* Process the indication */ + unsigned char data[NR_STUN_MAX_MESSAGE_SIZE]; + size_t datal; + nr_transport_addr remote_addr; + + r = nr_turn_client_parse_data_indication( + turn_ctx_, &addr, buf, len, data, &datal, sizeof(data), &remote_addr); + ASSERT_EQ(0, r); + std::cerr << "Received " << datal << " bytes from " + << remote_addr.as_string << std::endl; + + received_ += datal; + + for (size_t i = 0; i < datal; i++) { + ASSERT_EQ(i & 0xff, data[i]); + } + } else { + if (nr_is_stun_message(buf, len)) { + std::cerr << "STUN message of unexpected type" << std::endl; + } else { + std::cerr << "Not a STUN message" << std::endl; + } + return; + } + } + + void SendTo_s(const std::string& target, int expect_return) { + nr_transport_addr addr; + int r; + + // Expected pattern here is "IP4:127.0.0.1:3487" + ASSERT_EQ(0, target.compare(0, 4, "IP4:")); + + size_t offset = target.rfind(':'); + ASSERT_NE(std::string::npos, offset); + + std::string host = target.substr(4, offset - 4); + std::string port = target.substr(offset + 1); + + r = nr_str_port_to_transport_addr(host.c_str(), atoi(port.c_str()), + IPPROTO_UDP, &addr); + ASSERT_EQ(0, r); + + unsigned char test[100]; + for (size_t i = 0; i < sizeof(test); i++) { + test[i] = i & 0xff; + } + + std::cerr << "Sending test message to " << target << " ..." << std::endl; + + r = nr_turn_client_send_indication(turn_ctx_, test, sizeof(test), 0, &addr); + if (expect_return >= 0) { + ASSERT_EQ(expect_return, r); + } + } + + void SendTo(const std::string& target, int expect_return = 0) { + RUN_ON_THREAD( + test_utils_->sts_target(), + WrapRunnable(this, &TurnClient::SendTo_s, target, expect_return), + NS_DISPATCH_SYNC); + } + + int received() const { return received_; } + + static void socket_readable_cb(NR_SOCKET s, int how, void* arg) { + static_cast<TurnClient*>(arg)->Readable(s, how, arg); + } + + 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) { + if (WarnIfTurnNotConfigured()) return; + + Allocate(); +} + +TEST_F(TurnClient, AllocateTcp) { + if (WarnIfTurnNotConfigured()) return; + + SetTcp(); + Allocate(); +} + +TEST_F(TurnClient, AllocateAndHold) { + if (WarnIfTurnNotConfigured()) return; + + Allocate(); + PR_Sleep(20000); + ASSERT_TRUE(turn_ctx_->state == NR_TURN_CLIENT_STATE_ALLOCATED); +} + +TEST_F(TurnClient, SendToSelf) { + if (WarnIfTurnNotConfigured()) return; + + Allocate(); + SendTo(relay_addr_); + ASSERT_TRUE_WAIT(received() == 100, 5000); + SendTo(relay_addr_); + ASSERT_TRUE_WAIT(received() == 200, 1000); +} + +TEST_F(TurnClient, SendToSelfTcp) { + if (WarnIfTurnNotConfigured()) return; + + SetTcp(); + Allocate(); + SendTo(relay_addr_); + ASSERT_TRUE_WAIT(received() == 100, 5000); + SendTo(relay_addr_); + ASSERT_TRUE_WAIT(received() == 200, 1000); +} + +TEST_F(TurnClient, PermissionDenied) { + if (WarnIfTurnNotConfigured()) return; + + Allocate(); + RequestPermission(relay_addr_); + PR_Sleep(1000); + + /* Fake a 403 response */ + nr_turn_permission* perm; + perm = STAILQ_FIRST(&turn_ctx_->permissions); + ASSERT_TRUE(perm); + while (perm) { + perm->stun->last_error_code = 403; + std::cerr << "Set 403's on permission" << std::endl; + perm = STAILQ_NEXT(perm, entry); + } + + SendTo(relay_addr_, R_NOT_PERMITTED); + ASSERT_TRUE(received() == 0); + + // TODO: We should check if we can still send to a second destination, but + // we would need a second TURN client as one client can only handle one + // allocation (maybe as part of bug 1128128 ?). +} + +TEST_F(TurnClient, DeallocateReceiveFailure) { + if (WarnIfTurnNotConfigured()) return; + + Allocate(); + SendTo(relay_addr_); + ASSERT_TRUE_WAIT(received() == 100, 5000); + Deallocate(); + turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED; + SendTo(relay_addr_); + PR_Sleep(1000); + ASSERT_TRUE(received() == 100); +} + +TEST_F(TurnClient, DeallocateReceiveFailureTcp) { + if (WarnIfTurnNotConfigured()) return; + + SetTcp(); + Allocate(); + SendTo(relay_addr_); + ASSERT_TRUE_WAIT(received() == 100, 5000); + Deallocate(); + turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED; + /* Either the connection got closed by the TURN server already, then the send + * is going to fail, which we simply ignore. Or the connection is still alive + * and we cand send the data, but it should not get forwarded to us. In either + * case we should not receive more data. */ + SendTo(relay_addr_, -1); + PR_Sleep(1000); + ASSERT_TRUE(received() == 100); +} + +TEST_F(TurnClient, AllocateDummyServer) { + if (WarnIfTurnNotConfigured()) return; + + turn_server_ = kDummyTurnServer; + Allocate(false); +} diff --git a/dom/media/webrtc/transport/test/webrtcproxychannel_unittest.cpp b/dom/media/webrtc/transport/test/webrtcproxychannel_unittest.cpp new file mode 100644 index 0000000000..234af9dac6 --- /dev/null +++ b/dom/media/webrtc/transport/test/webrtcproxychannel_unittest.cpp @@ -0,0 +1,739 @@ +/* -*- 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/. */ + +#include <algorithm> +#include <mutex> + +#include "mozilla/net/WebrtcTCPSocket.h" +#include "mozilla/net/WebrtcTCPSocketCallback.h" + +#include "nsISocketTransport.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +static const uint32_t kDefaultTestTimeout = 2000; +static const char kReadData[] = "Hello, World!"; +static const size_t kReadDataLength = sizeof(kReadData) - 1; +static const std::string kReadDataString = + std::string(kReadData, kReadDataLength); +static int kDataLargeOuterLoopCount = 128; +static int kDataLargeInnerLoopCount = 1024; + +namespace mozilla { + +using namespace net; +using namespace testing; + +class WebrtcTCPSocketTestCallback; + +class FakeSocketTransportProvider : public nsISocketTransport { + public: + NS_DECL_THREADSAFE_ISUPPORTS + + // nsISocketTransport + NS_IMETHOD GetHost(nsACString& aHost) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetPort(int32_t* aPort) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetScriptableOriginAttributes( + JSContext* cx, JS::MutableHandle<JS::Value> aOriginAttributes) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetScriptableOriginAttributes( + JSContext* cx, JS::Handle<JS::Value> aOriginAttributes) override { + MOZ_ASSERT(false); + return NS_OK; + } + virtual nsresult GetOriginAttributes( + mozilla::OriginAttributes* _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + virtual nsresult SetOriginAttributes( + const mozilla::OriginAttributes& aOriginAttrs) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetPeerAddr(mozilla::net::NetAddr* _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetSelfAddr(mozilla::net::NetAddr* _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD Bind(mozilla::net::NetAddr* aLocalAddr) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetScriptablePeerAddr(nsINetAddr** _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetScriptableSelfAddr(nsINetAddr** _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetTlsSocketControl( + nsITLSSocketControl** aTLSSocketControl) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetSecurityCallbacks( + nsIInterfaceRequestor** aSecurityCallbacks) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetSecurityCallbacks( + nsIInterfaceRequestor* aSecurityCallbacks) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD IsAlive(bool* _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetTimeout(uint32_t aType, uint32_t* _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetTimeout(uint32_t aType, uint32_t aValue) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetLinger(bool aPolarity, int16_t aTimeout) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetReuseAddrPort(bool reuseAddrPort) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetConnectionFlags(uint32_t* aConnectionFlags) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetConnectionFlags(uint32_t aConnectionFlags) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetIsPrivate(bool) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetTlsFlags(uint32_t* aTlsFlags) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetTlsFlags(uint32_t aTlsFlags) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetQoSBits(uint8_t* aQoSBits) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetQoSBits(uint8_t aQoSBits) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetRecvBufferSize(uint32_t* aRecvBufferSize) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetSendBufferSize(uint32_t* aSendBufferSize) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetKeepaliveEnabled(bool* aKeepaliveEnabled) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetKeepaliveEnabled(bool aKeepaliveEnabled) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetKeepaliveVals(int32_t keepaliveIdleTime, + int32_t keepaliveRetryInterval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetResetIPFamilyPreference( + bool* aResetIPFamilyPreference) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetEchConfigUsed(bool* aEchConfigUsed) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetEchConfig(const nsACString& aEchConfig) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD ResolvedByTRR(bool* _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetRetryDnsIfPossible(bool* aRetryDns) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD GetStatus(nsresult* aStatus) override { + MOZ_ASSERT(false); + return NS_OK; + } + + // nsITransport + NS_IMETHOD OpenInputStream(uint32_t aFlags, uint32_t aSegmentSize, + uint32_t aSegmentCount, + nsIInputStream** _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD OpenOutputStream(uint32_t aFlags, uint32_t aSegmentSize, + uint32_t aSegmentCount, + nsIOutputStream** _retval) override { + MOZ_ASSERT(false); + return NS_OK; + } + NS_IMETHOD SetEventSink(nsITransportEventSink* aSink, + nsIEventTarget* aEventTarget) override { + MOZ_ASSERT(false); + return NS_OK; + } + + // fake except for these methods which are OK to call + // nsISocketTransport + NS_IMETHOD SetRecvBufferSize(uint32_t aRecvBufferSize) override { + return NS_OK; + } + NS_IMETHOD SetSendBufferSize(uint32_t aSendBufferSize) override { + return NS_OK; + } + // nsITransport + NS_IMETHOD Close(nsresult aReason) override { return NS_OK; } + + protected: + virtual ~FakeSocketTransportProvider() = default; +}; + +NS_IMPL_ISUPPORTS(FakeSocketTransportProvider, nsISocketTransport, nsITransport) + +// Implements some common elements to WebrtcTCPSocketTestOutputStream and +// WebrtcTCPSocketTestInputStream. +class WebrtcTCPSocketTestStream { + public: + WebrtcTCPSocketTestStream(); + + void Fail() { mMustFail = true; } + + size_t DataLength(); + template <typename T> + void AppendElements(const T* aBuffer, size_t aLength); + + protected: + virtual ~WebrtcTCPSocketTestStream() = default; + + nsTArray<uint8_t> mData; + std::mutex mDataMutex; + + bool mMustFail; +}; + +WebrtcTCPSocketTestStream::WebrtcTCPSocketTestStream() : mMustFail(false) {} + +template <typename T> +void WebrtcTCPSocketTestStream::AppendElements(const T* aBuffer, + size_t aLength) { + std::lock_guard<std::mutex> guard(mDataMutex); + mData.AppendElements(aBuffer, aLength); +} + +size_t WebrtcTCPSocketTestStream::DataLength() { + std::lock_guard<std::mutex> guard(mDataMutex); + return mData.Length(); +} + +class WebrtcTCPSocketTestInputStream : public nsIAsyncInputStream, + public WebrtcTCPSocketTestStream { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIASYNCINPUTSTREAM + NS_DECL_NSIINPUTSTREAM + + WebrtcTCPSocketTestInputStream() + : mMaxReadSize(1024 * 1024), mAllowCallbacks(false) {} + + void DoCallback(); + void CallCallback(const nsCOMPtr<nsIInputStreamCallback>& aCallback); + void AllowCallbacks() { mAllowCallbacks = true; } + + size_t mMaxReadSize; + + protected: + virtual ~WebrtcTCPSocketTestInputStream() = default; + + private: + nsCOMPtr<nsIInputStreamCallback> mCallback; + nsCOMPtr<nsIEventTarget> mCallbackTarget; + + bool mAllowCallbacks; +}; + +NS_IMPL_ISUPPORTS(WebrtcTCPSocketTestInputStream, nsIAsyncInputStream, + nsIInputStream) + +nsresult WebrtcTCPSocketTestInputStream::AsyncWait( + nsIInputStreamCallback* aCallback, uint32_t aFlags, + uint32_t aRequestedCount, nsIEventTarget* aEventTarget) { + MOZ_ASSERT(!aEventTarget, "no event target should be set"); + + mCallback = aCallback; + mCallbackTarget = NS_GetCurrentThread(); + + if (mAllowCallbacks && DataLength() > 0) { + DoCallback(); + } + + return NS_OK; +} + +nsresult WebrtcTCPSocketTestInputStream::CloseWithStatus(nsresult aStatus) { + return Close(); +} + +nsresult WebrtcTCPSocketTestInputStream::Close() { return NS_OK; } + +nsresult WebrtcTCPSocketTestInputStream::Available(uint64_t* aAvailable) { + *aAvailable = DataLength(); + return NS_OK; +} + +nsresult WebrtcTCPSocketTestInputStream::Read(char* aBuffer, uint32_t aCount, + uint32_t* aRead) { + std::lock_guard<std::mutex> guard(mDataMutex); + if (mMustFail) { + return NS_ERROR_FAILURE; + } + *aRead = std::min({(size_t)aCount, mData.Length(), mMaxReadSize}); + memcpy(aBuffer, mData.Elements(), *aRead); + mData.RemoveElementsAt(0, *aRead); + return *aRead > 0 ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK; +} + +nsresult WebrtcTCPSocketTestInputStream::ReadSegments(nsWriteSegmentFun aWriter, + void* aClosure, + uint32_t aCount, + uint32_t* _retval) { + MOZ_ASSERT(false); + return NS_OK; +} + +nsresult WebrtcTCPSocketTestInputStream::IsNonBlocking(bool* aIsNonBlocking) { + *aIsNonBlocking = true; + return NS_OK; +} + +void WebrtcTCPSocketTestInputStream::CallCallback( + const nsCOMPtr<nsIInputStreamCallback>& aCallback) { + aCallback->OnInputStreamReady(this); +} + +void WebrtcTCPSocketTestInputStream::DoCallback() { + if (mCallback) { + mCallbackTarget->Dispatch( + NewRunnableMethod<const nsCOMPtr<nsIInputStreamCallback>&>( + "WebrtcTCPSocketTestInputStream::DoCallback", this, + &WebrtcTCPSocketTestInputStream::CallCallback, + std::move(mCallback))); + + mCallbackTarget = nullptr; + } +} + +class WebrtcTCPSocketTestOutputStream : public nsIAsyncOutputStream, + public WebrtcTCPSocketTestStream { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIASYNCOUTPUTSTREAM + NS_DECL_NSIOUTPUTSTREAM + + WebrtcTCPSocketTestOutputStream() : mMaxWriteSize(1024 * 1024) {} + + void DoCallback(); + void CallCallback(const nsCOMPtr<nsIOutputStreamCallback>& aCallback); + + std::string DataString(); + + uint32_t mMaxWriteSize; + + protected: + virtual ~WebrtcTCPSocketTestOutputStream() = default; + + private: + nsCOMPtr<nsIOutputStreamCallback> mCallback; + nsCOMPtr<nsIEventTarget> mCallbackTarget; +}; + +NS_IMPL_ISUPPORTS(WebrtcTCPSocketTestOutputStream, nsIAsyncOutputStream, + nsIOutputStream) + +nsresult WebrtcTCPSocketTestOutputStream::AsyncWait( + nsIOutputStreamCallback* aCallback, uint32_t aFlags, + uint32_t aRequestedCount, nsIEventTarget* aEventTarget) { + MOZ_ASSERT(!aEventTarget, "no event target should be set"); + + mCallback = aCallback; + mCallbackTarget = NS_GetCurrentThread(); + + return NS_OK; +} + +nsresult WebrtcTCPSocketTestOutputStream::CloseWithStatus(nsresult aStatus) { + return Close(); +} + +nsresult WebrtcTCPSocketTestOutputStream::Close() { return NS_OK; } + +nsresult WebrtcTCPSocketTestOutputStream::Flush() { return NS_OK; } + +nsresult WebrtcTCPSocketTestOutputStream::Write(const char* aBuffer, + uint32_t aCount, + uint32_t* aWrote) { + if (mMustFail) { + return NS_ERROR_FAILURE; + } + *aWrote = std::min(aCount, mMaxWriteSize); + AppendElements(aBuffer, *aWrote); + return NS_OK; +} + +nsresult WebrtcTCPSocketTestOutputStream::WriteSegments( + nsReadSegmentFun aReader, void* aClosure, uint32_t aCount, + uint32_t* _retval) { + MOZ_ASSERT(false); + return NS_OK; +} + +nsresult WebrtcTCPSocketTestOutputStream::WriteFrom(nsIInputStream* aFromStream, + uint32_t aCount, + uint32_t* _retval) { + MOZ_ASSERT(false); + return NS_OK; +} + +nsresult WebrtcTCPSocketTestOutputStream::IsNonBlocking(bool* aIsNonBlocking) { + *aIsNonBlocking = true; + return NS_OK; +} + +void WebrtcTCPSocketTestOutputStream::CallCallback( + const nsCOMPtr<nsIOutputStreamCallback>& aCallback) { + aCallback->OnOutputStreamReady(this); +} + +void WebrtcTCPSocketTestOutputStream::DoCallback() { + if (mCallback) { + mCallbackTarget->Dispatch( + NewRunnableMethod<const nsCOMPtr<nsIOutputStreamCallback>&>( + "WebrtcTCPSocketTestOutputStream::CallCallback", this, + &WebrtcTCPSocketTestOutputStream::CallCallback, + std::move(mCallback))); + + mCallbackTarget = nullptr; + } +} + +std::string WebrtcTCPSocketTestOutputStream::DataString() { + std::lock_guard<std::mutex> guard(mDataMutex); + return std::string((char*)mData.Elements(), mData.Length()); +} + +// Fake as in not the real WebrtcTCPSocket but real enough +class FakeWebrtcTCPSocket : public WebrtcTCPSocket { + public: + explicit FakeWebrtcTCPSocket(WebrtcTCPSocketCallback* aCallback) + : WebrtcTCPSocket(aCallback) {} + + protected: + virtual ~FakeWebrtcTCPSocket() = default; + + void InvokeOnClose(nsresult aReason) override; + void InvokeOnConnected() override; + void InvokeOnRead(nsTArray<uint8_t>&& aReadData) override; +}; + +void FakeWebrtcTCPSocket::InvokeOnClose(nsresult aReason) { + mProxyCallbacks->OnClose(aReason); +} + +void FakeWebrtcTCPSocket::InvokeOnConnected() { + mProxyCallbacks->OnConnected("http"_ns); +} + +void FakeWebrtcTCPSocket::InvokeOnRead(nsTArray<uint8_t>&& aReadData) { + mProxyCallbacks->OnRead(std::move(aReadData)); +} + +class WebrtcTCPSocketTest : public MtransportTest { + public: + WebrtcTCPSocketTest() + : MtransportTest(), + mSocketThread(nullptr), + mSocketTransport(nullptr), + mInputStream(nullptr), + mOutputStream(nullptr), + mChannel(nullptr), + mCallback(nullptr), + mOnCloseCalled(false), + mOnConnectedCalled(false) {} + + // WebrtcTCPSocketCallback forwards from mCallback + void OnClose(nsresult aReason); + void OnConnected(const nsACString& aProxyType); + void OnRead(nsTArray<uint8_t>&& aReadData); + + void SetUp() override; + void TearDown() override; + + void DoTransportAvailable(); + + std::string ReadDataAsString(); + std::string GetDataLarge(); + + nsCOMPtr<nsIEventTarget> mSocketThread; + + nsCOMPtr<nsISocketTransport> mSocketTransport; + RefPtr<WebrtcTCPSocketTestInputStream> mInputStream; + RefPtr<WebrtcTCPSocketTestOutputStream> mOutputStream; + RefPtr<FakeWebrtcTCPSocket> mChannel; + RefPtr<WebrtcTCPSocketTestCallback> mCallback; + + bool mOnCloseCalled; + bool mOnConnectedCalled; + + size_t ReadDataLength(); + template <typename T> + void AppendReadData(const T* aBuffer, size_t aLength); + + private: + nsTArray<uint8_t> mReadData; + std::mutex mReadDataMutex; +}; + +class WebrtcTCPSocketTestCallback : public WebrtcTCPSocketCallback { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketTestCallback, override) + + explicit WebrtcTCPSocketTestCallback(WebrtcTCPSocketTest* aTest) + : mTest(aTest) {} + + // WebrtcTCPSocketCallback + void OnClose(nsresult aReason) override; + void OnConnected(const nsACString& aProxyType) override; + void OnRead(nsTArray<uint8_t>&& aReadData) override; + + protected: + virtual ~WebrtcTCPSocketTestCallback() = default; + + private: + WebrtcTCPSocketTest* mTest; +}; + +void WebrtcTCPSocketTest::SetUp() { + nsresult rv; + // WebrtcTCPSocket's threading model is the same as mtransport + // all socket operations are done on the socket thread + // callbacks are invoked on the main thread + mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + mSocketTransport = new FakeSocketTransportProvider(); + mInputStream = new WebrtcTCPSocketTestInputStream(); + mOutputStream = new WebrtcTCPSocketTestOutputStream(); + mCallback = new WebrtcTCPSocketTestCallback(this); + mChannel = new FakeWebrtcTCPSocket(mCallback.get()); +} + +void WebrtcTCPSocketTest::TearDown() {} + +// WebrtcTCPSocketCallback +void WebrtcTCPSocketTest::OnRead(nsTArray<uint8_t>&& aReadData) { + AppendReadData(aReadData.Elements(), aReadData.Length()); +} + +void WebrtcTCPSocketTest::OnConnected(const nsACString& aProxyType) { + mOnConnectedCalled = true; +} + +void WebrtcTCPSocketTest::OnClose(nsresult aReason) { mOnCloseCalled = true; } + +void WebrtcTCPSocketTest::DoTransportAvailable() { + if (!mSocketThread->IsOnCurrentThread()) { + mSocketThread->Dispatch( + NS_NewRunnableFunction("DoTransportAvailable", [this]() -> void { + nsresult rv; + rv = mChannel->OnTransportAvailable(mSocketTransport, mInputStream, + mOutputStream); + ASSERT_EQ(NS_OK, rv); + })); + } else { + // should always be called on the main thread + MOZ_ASSERT(0); + } +} + +std::string WebrtcTCPSocketTest::ReadDataAsString() { + std::lock_guard<std::mutex> guard(mReadDataMutex); + return std::string((char*)mReadData.Elements(), mReadData.Length()); +} + +std::string WebrtcTCPSocketTest::GetDataLarge() { + std::string data; + for (int i = 0; i < kDataLargeOuterLoopCount * kDataLargeInnerLoopCount; + ++i) { + data += kReadData; + } + return data; +} + +template <typename T> +void WebrtcTCPSocketTest::AppendReadData(const T* aBuffer, size_t aLength) { + std::lock_guard<std::mutex> guard(mReadDataMutex); + mReadData.AppendElements(aBuffer, aLength); +} + +size_t WebrtcTCPSocketTest::ReadDataLength() { + std::lock_guard<std::mutex> guard(mReadDataMutex); + return mReadData.Length(); +} + +void WebrtcTCPSocketTestCallback::OnClose(nsresult aReason) { + mTest->OnClose(aReason); +} + +void WebrtcTCPSocketTestCallback::OnConnected(const nsACString& aProxyType) { + mTest->OnConnected(aProxyType); +} + +void WebrtcTCPSocketTestCallback::OnRead(nsTArray<uint8_t>&& aReadData) { + mTest->OnRead(std::move(aReadData)); +} + +} // namespace mozilla + +typedef mozilla::WebrtcTCPSocketTest WebrtcTCPSocketTest; + +TEST_F(WebrtcTCPSocketTest, SetUp) {} + +TEST_F(WebrtcTCPSocketTest, TransportAvailable) { + DoTransportAvailable(); + ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout); +} + +TEST_F(WebrtcTCPSocketTest, Read) { + DoTransportAvailable(); + ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout); + + mInputStream->AppendElements(kReadData, kReadDataLength); + mInputStream->DoCallback(); + + ASSERT_TRUE_WAIT(ReadDataAsString() == kReadDataString, kDefaultTestTimeout); +} + +TEST_F(WebrtcTCPSocketTest, Write) { + DoTransportAvailable(); + ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout); + + nsTArray<uint8_t> data; + data.AppendElements(kReadData, kReadDataLength); + mChannel->Write(std::move(data)); + + ASSERT_TRUE_WAIT(mChannel->CountUnwrittenBytes() == kReadDataLength, + kDefaultTestTimeout); + + mOutputStream->DoCallback(); + + ASSERT_TRUE_WAIT(mOutputStream->DataString() == kReadDataString, + kDefaultTestTimeout); +} + +TEST_F(WebrtcTCPSocketTest, ReadFail) { + DoTransportAvailable(); + ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout); + + mInputStream->AppendElements(kReadData, kReadDataLength); + mInputStream->Fail(); + mInputStream->DoCallback(); + + ASSERT_TRUE_WAIT(mOnCloseCalled, kDefaultTestTimeout); + ASSERT_EQ(0U, ReadDataLength()); +} + +TEST_F(WebrtcTCPSocketTest, WriteFail) { + DoTransportAvailable(); + ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout); + + nsTArray<uint8_t> array; + array.AppendElements(kReadData, kReadDataLength); + mChannel->Write(std::move(array)); + + ASSERT_TRUE_WAIT(mChannel->CountUnwrittenBytes() == kReadDataLength, + kDefaultTestTimeout); + + mOutputStream->Fail(); + mOutputStream->DoCallback(); + + ASSERT_TRUE_WAIT(mOnCloseCalled, kDefaultTestTimeout); + ASSERT_EQ(0U, mOutputStream->DataLength()); +} + +TEST_F(WebrtcTCPSocketTest, ReadLarge) { + DoTransportAvailable(); + ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout); + + const std::string data = GetDataLarge(); + + mInputStream->AppendElements(data.c_str(), data.length()); + // make sure reading loops more than once + mInputStream->mMaxReadSize = 3072; + mInputStream->AllowCallbacks(); + mInputStream->DoCallback(); + + ASSERT_TRUE_WAIT(ReadDataAsString() == data, kDefaultTestTimeout); +} + +TEST_F(WebrtcTCPSocketTest, WriteLarge) { + DoTransportAvailable(); + ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout); + + const std::string data = GetDataLarge(); + + for (int i = 0; i < kDataLargeOuterLoopCount; ++i) { + nsTArray<uint8_t> array; + int chunkSize = kReadDataString.length() * kDataLargeInnerLoopCount; + int offset = i * chunkSize; + array.AppendElements(data.c_str() + offset, chunkSize); + mChannel->Write(std::move(array)); + } + + ASSERT_TRUE_WAIT(mChannel->CountUnwrittenBytes() == data.length(), + kDefaultTestTimeout); + + // make sure writing loops more than once per write request + mOutputStream->mMaxWriteSize = 1024; + mOutputStream->DoCallback(); + + ASSERT_TRUE_WAIT(mOutputStream->DataString() == data, kDefaultTestTimeout); +} diff --git a/dom/media/webrtc/transport/test_nr_socket.cpp b/dom/media/webrtc/transport/test_nr_socket.cpp new file mode 100644 index 0000000000..064013a8b7 --- /dev/null +++ b/dom/media/webrtc/transport/test_nr_socket.cpp @@ -0,0 +1,1135 @@ +/* -*- 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/. */ +/* + */ + +/* +Based partially on original code from nICEr and nrappkit. + +nICEr copyright: + +Copyright (c) 2007, Adobe Systems, Incorporated +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. + + +nrappkit copyright: + + Copyright (C) 2001-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Thu Dec 20 20:14:49 2001 +*/ + +// Original author: bcampen@mozilla.com [:bwc] + +extern "C" { +#include "stun_msg.h" // for NR_STUN_MAX_MESSAGE_SIZE +#include "async_wait.h" +#include "async_timer.h" +#include "nr_socket.h" +#include "stun.h" +#include "transport_addr.h" +} + +#include "mozilla/RefPtr.h" +#include "test_nr_socket.h" + +namespace mozilla { + +static int test_nat_socket_create(void* obj, nr_transport_addr* addr, + nr_socket** sockp) { + RefPtr<NrSocketBase> sock = new TestNrSocket(static_cast<TestNat*>(obj)); + + int r, _status; + + r = sock->create(addr); + if (r) ABORT(r); + + r = nr_socket_create_int(static_cast<void*>(sock), sock->vtbl(), sockp); + if (r) ABORT(r); + + _status = 0; + + { + // We will release this reference in destroy(), not exactly the normal + // ownership model, but it is what it is. + NrSocketBase* dummy = sock.forget().take(); + (void)dummy; + } + +abort: + return _status; +} + +static int test_nat_socket_factory_destroy(void** obj) { + TestNat* nat = static_cast<TestNat*>(*obj); + *obj = nullptr; + nat->Release(); + return 0; +} + +static nr_socket_factory_vtbl test_nat_socket_factory_vtbl = { + test_nat_socket_create, test_nat_socket_factory_destroy}; + +/* static */ +TestNat::NatBehavior TestNat::ToNatBehavior(const std::string& type) { + if (type.empty() || !type.compare("ENDPOINT_INDEPENDENT")) { + return TestNat::ENDPOINT_INDEPENDENT; + } + if (!type.compare("ADDRESS_DEPENDENT")) { + return TestNat::ADDRESS_DEPENDENT; + } + if (!type.compare("PORT_DEPENDENT")) { + return TestNat::PORT_DEPENDENT; + } + + MOZ_ASSERT(false, "Invalid NAT behavior"); + return TestNat::ENDPOINT_INDEPENDENT; +} + +bool TestNat::has_port_mappings() const { + for (TestNrSocket* sock : sockets_) { + if (sock->has_port_mappings()) { + return true; + } + } + return false; +} + +bool TestNat::is_my_external_tuple(const nr_transport_addr& addr) const { + for (TestNrSocket* sock : sockets_) { + if (sock->is_my_external_tuple(addr)) { + return true; + } + } + + return false; +} + +bool TestNat::is_an_internal_tuple(const nr_transport_addr& addr) const { + for (TestNrSocket* sock : sockets_) { + nr_transport_addr addr_behind_nat; + if (sock->getaddr(&addr_behind_nat)) { + MOZ_CRASH("TestNrSocket::getaddr failed!"); + } + + if (!nr_transport_addr_cmp(&addr, &addr_behind_nat, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + return true; + } + } + return false; +} + +int TestNat::create_socket_factory(nr_socket_factory** factorypp) { + int r = nr_socket_factory_create_int(this, &test_nat_socket_factory_vtbl, + factorypp); + if (!r) { + AddRef(); + } + return r; +} + +void TestNat::set_proxy_config( + std::shared_ptr<NrSocketProxyConfig> aProxyConfig) { + proxy_config_ = std::move(aProxyConfig); +} + +TestNrSocket::TestNrSocket(TestNat* nat) + : nat_(nat), tls_(false), timer_handle_(nullptr) { + nat_->insert_socket(this); +} + +TestNrSocket::~TestNrSocket() { nat_->erase_socket(this); } + +RefPtr<NrSocketBase> TestNrSocket::create_external_socket( + const nr_transport_addr& dest_addr) const { + MOZ_ASSERT(nat_->enabled_); + MOZ_ASSERT(!nat_->is_an_internal_tuple(dest_addr)); + + int r; + nr_transport_addr nat_external_addr; + + // Open the socket on an arbitrary port, on the same address. + if ((r = nr_transport_addr_copy(&nat_external_addr, + &internal_socket_->my_addr()))) { + r_log(LOG_GENERIC, LOG_CRIT, "%s: Failure in nr_transport_addr_copy: %d", + __FUNCTION__, r); + return nullptr; + } + + if ((r = nr_transport_addr_set_port(&nat_external_addr, 0))) { + r_log(LOG_GENERIC, LOG_CRIT, + "%s: Failure in nr_transport_addr_set_port: %d", __FUNCTION__, r); + return nullptr; + } + + RefPtr<NrSocketBase> external_socket; + r = NrSocketBase::CreateSocket(&nat_external_addr, &external_socket, + nat_->proxy_config_); + + if (r) { + r_log(LOG_GENERIC, LOG_CRIT, "%s: Failure in NrSocket::create: %d", + __FUNCTION__, r); + return nullptr; + } + + return external_socket; +} + +int TestNrSocket::create(nr_transport_addr* addr) { + tls_ = addr->tls; + + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p create %s", this, + addr->as_string); + return NrSocketBase::CreateSocket(addr, &internal_socket_, nullptr); +} + +int TestNrSocket::getaddr(nr_transport_addr* addrp) { + return internal_socket_->getaddr(addrp); +} + +void TestNrSocket::close() { + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s closing", this, + internal_socket_->my_addr().as_string); + if (timer_handle_) { + NR_async_timer_cancel(timer_handle_); + timer_handle_ = nullptr; + } + internal_socket_->close(); + for (RefPtr<PortMapping>& port_mapping : port_mappings_) { + port_mapping->external_socket_->close(); + } +} + +int TestNrSocket::listen(int backlog) { + MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP); + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s listening", this, + internal_socket_->my_addr().as_string); + + return internal_socket_->listen(backlog); +} + +int TestNrSocket::accept(nr_transport_addr* addrp, nr_socket** sockp) { + MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP); + int r = internal_socket_->accept(addrp, sockp); + if (r) { + return r; + } + + if (nat_->enabled_ && !nat_->is_an_internal_tuple(*addrp)) { + nr_socket_destroy(sockp); + return R_IO_ERROR; + } + + return 0; +} + +void TestNrSocket::process_delayed_cb(NR_SOCKET s, int how, void* cb_arg) { + DeferredPacket* op = static_cast<DeferredPacket*>(cb_arg); + op->socket_->timer_handle_ = nullptr; + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s sending delayed STUN response", + op->internal_socket_->my_addr().as_string); + op->internal_socket_->sendto(op->buffer_.data(), op->buffer_.len(), + op->flags_, &op->to_); + + delete op; +} + +int TestNrSocket::sendto(const void* msg, size_t len, int flags, + const nr_transport_addr* to) { + MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP); + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s %s", this, __FUNCTION__, + to->as_string); + + if (nat_->nat_delegate_ && + nat_->nat_delegate_->on_sendto(nat_, msg, len, flags, to)) { + return nat_->error_code_for_drop_; + } + + UCHAR* buf = static_cast<UCHAR*>(const_cast<void*>(msg)); + if (nat_->block_stun_ && nr_is_stun_message(buf, len)) { + return nat_->error_code_for_drop_; + } + + if (nr_is_stun_request_message(buf, len) && + maybe_send_fake_response(buf, len, to)) { + return 0; + } + + /* TODO: improve the functionality of this in bug 1253657 */ + if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) { + if (nat_->delay_stun_resp_ms_ && nr_is_stun_response_message(buf, len)) { + NR_ASYNC_TIMER_SET( + nat_->delay_stun_resp_ms_, process_delayed_cb, + new DeferredPacket(this, msg, len, flags, to, internal_socket_), + &timer_handle_); + return 0; + } + return internal_socket_->sendto(msg, len, flags, to); + } + + destroy_stale_port_mappings(); + + if (to->protocol == IPPROTO_UDP && nat_->block_udp_) { + return nat_->error_code_for_drop_; + } + + // Choose our port mapping based on our most selective criteria + PortMapping* port_mapping = get_port_mapping( + *to, std::max(nat_->filtering_type_, nat_->mapping_type_)); + + if (!port_mapping) { + // See if we have already made the external socket we need to use. + PortMapping* similar_port_mapping = + get_port_mapping(*to, nat_->mapping_type_); + RefPtr<NrSocketBase> external_socket; + + if (similar_port_mapping) { + external_socket = similar_port_mapping->external_socket_; + } else { + external_socket = create_external_socket(*to); + if (!external_socket) { + MOZ_ASSERT(false); + return R_INTERNAL; + } + } + + port_mapping = create_port_mapping(*to, external_socket); + port_mappings_.push_back(port_mapping); + + if (poll_flags() & PR_POLL_READ) { + // Make sure the new port mapping is ready to receive traffic if the + // TestNrSocket is already waiting. + port_mapping->async_wait(NR_ASYNC_WAIT_READ, socket_readable_callback, + this, (char*)__FUNCTION__, __LINE__); + } + } + + // We probably don't want to propagate the flags, since this is a simulated + // external IP address. + return port_mapping->sendto(msg, len, *to); +} + +int TestNrSocket::recvfrom(void* buf, size_t maxlen, size_t* len, int flags, + nr_transport_addr* from) { + MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP); + + if (!read_buffer_.empty()) { + UdpPacket& packet = read_buffer_.front(); + *len = std::min(maxlen, packet.buffer_->len()); + memcpy(buf, packet.buffer_->data(), *len); + nr_transport_addr_copy(from, &packet.remote_address_); + read_buffer_.pop_front(); + return 0; + } + + int r; + bool ingress_allowed = false; + + if (readable_socket_) { + // If any of the external sockets got data, see if it will be passed through + r = readable_socket_->recvfrom(buf, maxlen, len, 0, from); + const nr_transport_addr to = readable_socket_->my_addr(); + readable_socket_ = nullptr; + if (!r) { + PortMapping* port_mapping_used; + ingress_allowed = allow_ingress(to, *from, &port_mapping_used); + if (ingress_allowed) { + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s via %s", + internal_socket_->my_addr().as_string, from->as_string, + port_mapping_used->external_socket_->my_addr().as_string); + if (nat_->refresh_on_ingress_) { + port_mapping_used->last_used_ = PR_IntervalNow(); + } + } + } + } else { + // If no external socket has data, see if there's any data that was sent + // directly to the TestNrSocket, and eat it if it isn't supposed to get + // through. + r = internal_socket_->recvfrom(buf, maxlen, len, flags, from); + if (!r) { + // We do not use allow_ingress() here because that only handles traffic + // landing on an external port. + ingress_allowed = (!nat_->enabled_ || nat_->is_an_internal_tuple(*from)); + if (!ingress_allowed) { + r_log(LOG_GENERIC, LOG_INFO, + "TestNrSocket %s denying ingress from %s: " + "Not behind the same NAT", + internal_socket_->my_addr().as_string, from->as_string); + } else { + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s", + internal_socket_->my_addr().as_string, from->as_string); + } + } + } + + // Kinda bad that we are forced to give the app a readable callback and then + // say "Oh, never mind...", but the alternative is to totally decouple the + // callbacks from STS and the callbacks the app sets. On the bright side, this + // speeds up unit tests where we are verifying that ingress is forbidden, + // since they'll get a readable callback and then an error, instead of having + // to wait for a timeout. + if (!ingress_allowed) { + *len = 0; + r = R_WOULDBLOCK; + } + + return r; +} + +bool TestNrSocket::allow_ingress(const nr_transport_addr& to, + const nr_transport_addr& from, + PortMapping** port_mapping_used) const { + // This is only called for traffic arriving at a port mapping + MOZ_ASSERT(nat_->enabled_); + MOZ_ASSERT(!nat_->is_an_internal_tuple(from)); + + // Find the port mapping (if any) that this packet landed on + for (PortMapping* port_mapping : port_mappings_) { + if (!nr_transport_addr_cmp(&to, &port_mapping->external_socket_->my_addr(), + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + *port_mapping_used = port_mapping; + } + } + + if (NS_WARN_IF(!(*port_mapping_used))) { + MOZ_ASSERT(false); + r_log(LOG_GENERIC, LOG_INFO, + "TestNrSocket %s denying ingress from %s: " + "No port mapping for this local port! What?", + internal_socket_->my_addr().as_string, from.as_string); + return false; + } + + if (!port_mapping_matches(**port_mapping_used, from, nat_->filtering_type_)) { + r_log(LOG_GENERIC, LOG_INFO, + "TestNrSocket %s denying ingress from %s: " + "Filtered (no port mapping for source)", + internal_socket_->my_addr().as_string, from.as_string); + return false; + } + + if (is_port_mapping_stale(**port_mapping_used)) { + r_log(LOG_GENERIC, LOG_INFO, + "TestNrSocket %s denying ingress from %s: " + "Stale port mapping", + internal_socket_->my_addr().as_string, from.as_string); + return false; + } + + if (!nat_->allow_hairpinning_ && nat_->is_my_external_tuple(from)) { + r_log(LOG_GENERIC, LOG_INFO, + "TestNrSocket %s denying ingress from %s: " + "Hairpinning disallowed", + internal_socket_->my_addr().as_string, from.as_string); + return false; + } + + return true; +} + +int TestNrSocket::connect(const nr_transport_addr* addr) { + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s connecting to %s", this, + internal_socket_->my_addr().as_string, addr->as_string); + + if (connect_invoked_ || !port_mappings_.empty()) { + MOZ_CRASH("TestNrSocket::connect() called more than once!"); + return R_INTERNAL; + } + + if (maybe_get_redirect_targets(addr).isSome()) { + // If we are simulating STUN redirects for |addr|, we need to pretend that + // the TCP connection worked, since |addr| probably does not actually point + // at something that exists. + connect_fake_stun_address_.reset(new nr_transport_addr); + nr_transport_addr_copy(connect_fake_stun_address_.get(), addr); + + // We dispatch this, otherwise nICEr can trip over its shoelaces + GetCurrentSerialEventTarget()->Dispatch( + NS_NewRunnableFunction("Async writeable callback for TestNrSocket", + [this, self = RefPtr<TestNrSocket>(this)] { + if (poll_flags() & PR_POLL_WRITE) { + fire_callback(NR_ASYNC_WAIT_WRITE); + } + })); + + return R_WOULDBLOCK; + } + + if (!nat_->enabled_ || + addr->protocol == IPPROTO_UDP // Horrible hack to allow default address + // discovery to work. Only works because + // we don't normally connect on UDP. + || nat_->is_an_internal_tuple(*addr)) { + // This will set connect_invoked_ + return internal_socket_->connect(addr); + } + + RefPtr<NrSocketBase> external_socket(create_external_socket(*addr)); + if (!external_socket) { + return R_INTERNAL; + } + + PortMapping* port_mapping = create_port_mapping(*addr, external_socket); + port_mappings_.push_back(port_mapping); + int r = port_mapping->external_socket_->connect(addr); + if (r && r != R_WOULDBLOCK) { + return r; + } + + port_mapping->last_used_ = PR_IntervalNow(); + + if (poll_flags() & PR_POLL_READ) { + port_mapping->async_wait(NR_ASYNC_WAIT_READ, + port_mapping_tcp_passthrough_callback, this, + (char*)__FUNCTION__, __LINE__); + } + + return r; +} + +int TestNrSocket::write(const void* msg, size_t len, size_t* written) { + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s writing", this, + internal_socket_->my_addr().as_string); + + UCHAR* buf = static_cast<UCHAR*>(const_cast<void*>(msg)); + + if (nat_->nat_delegate_ && + nat_->nat_delegate_->on_write(nat_, msg, len, written)) { + return R_INTERNAL; + } + + if (nat_->block_stun_ && nr_is_stun_message(buf, len)) { + // Should cause this socket to be abandoned + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %s dropping outgoing TCP " + "because it is configured to drop STUN", + my_addr().as_string); + return R_INTERNAL; + } + + if (nr_is_stun_request_message(buf, len) && connect_fake_stun_address_ && + maybe_send_fake_response(buf, len, connect_fake_stun_address_.get())) { + return 0; + } + + if (nat_->block_tcp_ && !tls_) { + // Should cause this socket to be abandoned + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %s dropping outgoing TCP " + "because it is configured to drop TCP", + my_addr().as_string); + return R_INTERNAL; + } + + if (nat_->block_tls_ && tls_) { + // Should cause this socket to be abandoned + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %s dropping outgoing TLS " + "because it is configured to drop TLS", + my_addr().as_string); + return R_INTERNAL; + } + + if (port_mappings_.empty()) { + // The no-nat case, just pass call through. + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s writing", + my_addr().as_string); + + return internal_socket_->write(msg, len, written); + } + destroy_stale_port_mappings(); + if (port_mappings_.empty()) { + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %s dropping outgoing TCP " + "because the port mapping was stale", + my_addr().as_string); + return R_INTERNAL; + } + // This is TCP only + MOZ_ASSERT(port_mappings_.size() == 1); + r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s writing", + port_mappings_.front()->external_socket_->my_addr().as_string, + port_mappings_.front()->remote_address_.as_string); + port_mappings_.front()->last_used_ = PR_IntervalNow(); + return port_mappings_.front()->external_socket_->write(msg, len, written); +} + +int TestNrSocket::read(void* buf, size_t maxlen, size_t* len) { + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s reading", this, + internal_socket_->my_addr().as_string); + + if (!read_buffer_.empty()) { + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %p %s has stuff in read_buffer_", this, + internal_socket_->my_addr().as_string); + UdpPacket packet(std::move(read_buffer_.front())); + read_buffer_.pop_front(); + *len = std::min(maxlen, packet.buffer_->len()); + memcpy(buf, packet.buffer_->data(), *len); + if (*len != packet.buffer_->len()) { + // Put remaining bytes in new packet, at the front. + read_buffer_.emplace_front(packet.buffer_->data() + *len, + packet.buffer_->len() - *len, + packet.remote_address_); + } + return 0; + } + + if (connect_fake_stun_address_) { + return R_WOULDBLOCK; + } + + int r; + + if (port_mappings_.empty()) { + r = internal_socket_->read(buf, maxlen, len); + } else { + MOZ_ASSERT(port_mappings_.size() == 1); + r = port_mappings_.front()->external_socket_->read(buf, maxlen, len); + if (!r && nat_->refresh_on_ingress_) { + port_mappings_.front()->last_used_ = PR_IntervalNow(); + } + } + + if (r) { + return r; + } + + if (nat_->nat_delegate_ && + nat_->nat_delegate_->on_read(nat_, buf, maxlen, len)) { + return R_INTERNAL; + } + + if (nat_->block_tcp_ && !tls_) { + // Should cause this socket to be abandoned + return R_INTERNAL; + } + + if (nat_->block_tls_ && tls_) { + // Should cause this socket to be abandoned + return R_INTERNAL; + } + + UCHAR* cbuf = static_cast<UCHAR*>(const_cast<void*>(buf)); + if (nat_->block_stun_ && nr_is_stun_message(cbuf, *len)) { + // Should cause this socket to be abandoned + return R_INTERNAL; + } + + return r; +} + +int TestNrSocket::async_wait(int how, NR_async_cb cb, void* cb_arg, + char* function, int line) { + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s waiting for %s", + internal_socket_->my_addr().as_string, + how == NR_ASYNC_WAIT_READ ? "read" : "write"); + + int r; + + if (how == NR_ASYNC_WAIT_READ) { + NrSocketBase::async_wait(how, cb, cb_arg, function, line); + if (!read_buffer_.empty()) { + fire_readable_callback(); + return 0; + } + + // Make sure we're waiting on the socket for the internal address + r = internal_socket_->async_wait(how, socket_readable_callback, this, + function, line); + } else { + if (connect_fake_stun_address_) { + // Fake TCP connection case; register the callback on this socket, not + // a real one. + return NrSocketBase::async_wait(how, cb, cb_arg, function, line); + } + + // For write, just use the readiness of the internal socket, since we queue + // everything for the port mappings. + r = internal_socket_->async_wait(how, cb, cb_arg, function, line); + } + + if (r) { + r_log(LOG_GENERIC, LOG_ERR, + "TestNrSocket %s failed to async_wait for " + "internal socket: %d\n", + internal_socket_->my_addr().as_string, r); + return r; + } + + if (is_tcp_connection_behind_nat()) { + // Bypass all port-mapping related logic + return 0; + } + + if (internal_socket_->my_addr().protocol == IPPROTO_TCP) { + // For a TCP connection through a simulated NAT, these signals are + // just passed through. + MOZ_ASSERT(port_mappings_.size() == 1); + + return port_mappings_.front()->async_wait( + how, port_mapping_tcp_passthrough_callback, this, function, line); + } + if (how == NR_ASYNC_WAIT_READ) { + // For UDP port mappings, we decouple the writeable callbacks + for (PortMapping* port_mapping : port_mappings_) { + // Be ready to receive traffic on our port mappings + r = port_mapping->async_wait(how, socket_readable_callback, this, + function, line); + if (r) { + r_log(LOG_GENERIC, LOG_ERR, + "TestNrSocket %s failed to async_wait for " + "port mapping: %d\n", + internal_socket_->my_addr().as_string, r); + return r; + } + } + } + + return 0; +} + +void TestNrSocket::cancel_port_mapping_async_wait(int how) { + for (PortMapping* port_mapping : port_mappings_) { + port_mapping->cancel(how); + } +} + +int TestNrSocket::cancel(int how) { + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s stop waiting for %s", + internal_socket_->my_addr().as_string, + how == NR_ASYNC_WAIT_READ ? "read" : "write"); + + if (connect_fake_stun_address_) { + return NrSocketBase::cancel(how); + } + + // Writable callbacks are decoupled except for the TCP case + if (how == NR_ASYNC_WAIT_READ || + internal_socket_->my_addr().protocol == IPPROTO_TCP) { + cancel_port_mapping_async_wait(how); + } + + return internal_socket_->cancel(how); +} + +bool TestNrSocket::has_port_mappings() const { return !port_mappings_.empty(); } + +bool TestNrSocket::is_my_external_tuple(const nr_transport_addr& addr) const { + for (PortMapping* port_mapping : port_mappings_) { + nr_transport_addr port_mapping_addr; + if (port_mapping->external_socket_->getaddr(&port_mapping_addr)) { + MOZ_CRASH("NrSocket::getaddr failed!"); + } + + if (!nr_transport_addr_cmp(&addr, &port_mapping_addr, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + return true; + } + } + return false; +} + +bool TestNrSocket::is_port_mapping_stale( + const PortMapping& port_mapping) const { + PRIntervalTime now = PR_IntervalNow(); + PRIntervalTime elapsed_ticks = now - port_mapping.last_used_; + uint32_t idle_duration = PR_IntervalToMilliseconds(elapsed_ticks); + return idle_duration > nat_->mapping_timeout_; +} + +void TestNrSocket::destroy_stale_port_mappings() { + for (auto i = port_mappings_.begin(); i != port_mappings_.end();) { + auto temp = i; + ++i; + if (is_port_mapping_stale(**temp)) { + r_log(LOG_GENERIC, LOG_INFO, + "TestNrSocket %s destroying port mapping %s -> %s", + internal_socket_->my_addr().as_string, + (*temp)->external_socket_->my_addr().as_string, + (*temp)->remote_address_.as_string); + + port_mappings_.erase(temp); + } + } +} + +void TestNrSocket::socket_readable_callback(void* real_sock_v, int how, + void* test_sock_v) { + TestNrSocket* test_socket = static_cast<TestNrSocket*>(test_sock_v); + NrSocketBase* real_socket = static_cast<NrSocketBase*>(real_sock_v); + + test_socket->on_socket_readable(real_socket); +} + +void TestNrSocket::on_socket_readable(NrSocketBase* real_socket) { + if (!readable_socket_ && (real_socket != internal_socket_)) { + readable_socket_ = real_socket; + } + + fire_readable_callback(); +} + +void TestNrSocket::fire_readable_callback() { + MOZ_ASSERT(poll_flags() & PR_POLL_READ); + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s ready for read", this, + internal_socket_->my_addr().as_string); + fire_callback(NR_ASYNC_WAIT_READ); +} + +void TestNrSocket::port_mapping_writeable_callback(void* ext_sock_v, int how, + void* test_sock_v) { + TestNrSocket* test_socket = static_cast<TestNrSocket*>(test_sock_v); + NrSocketBase* external_socket = static_cast<NrSocketBase*>(ext_sock_v); + + test_socket->write_to_port_mapping(external_socket); +} + +void TestNrSocket::write_to_port_mapping(NrSocketBase* external_socket) { + MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP); + + int r = 0; + for (PortMapping* port_mapping : port_mappings_) { + if (port_mapping->external_socket_ == external_socket) { + // If the send succeeds, or if there was nothing to send, we keep going + r = port_mapping->send_from_queue(); + if (r) { + break; + } + } + } + + if (r == R_WOULDBLOCK) { + // Re-register for writeable callbacks, since we still have stuff to send + NR_ASYNC_WAIT(external_socket, NR_ASYNC_WAIT_WRITE, + &TestNrSocket::port_mapping_writeable_callback, this); + } +} + +void TestNrSocket::port_mapping_tcp_passthrough_callback(void* ext_sock_v, + int how, + void* test_sock_v) { + TestNrSocket* test_socket = static_cast<TestNrSocket*>(test_sock_v); + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s firing %s callback", + test_socket->internal_socket_->my_addr().as_string, + how == NR_ASYNC_WAIT_READ ? "readable" : "writeable"); + + test_socket->internal_socket_->fire_callback(how); +} + +bool TestNrSocket::is_tcp_connection_behind_nat() const { + return internal_socket_->my_addr().protocol == IPPROTO_TCP && + port_mappings_.empty(); +} + +TestNrSocket::PortMapping* TestNrSocket::get_port_mapping( + const nr_transport_addr& remote_address, + TestNat::NatBehavior filter) const { + for (PortMapping* port_mapping : port_mappings_) { + if (port_mapping_matches(*port_mapping, remote_address, filter)) { + return port_mapping; + } + } + return nullptr; +} + +/* static */ +bool TestNrSocket::port_mapping_matches(const PortMapping& port_mapping, + const nr_transport_addr& remote_addr, + TestNat::NatBehavior filter) { + int compare_flags; + switch (filter) { + case TestNat::ENDPOINT_INDEPENDENT: + compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL; + break; + case TestNat::ADDRESS_DEPENDENT: + compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ADDR; + break; + case TestNat::PORT_DEPENDENT: + compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ALL; + break; + } + + return !nr_transport_addr_cmp(&remote_addr, &port_mapping.remote_address_, + compare_flags); +} + +TestNrSocket::PortMapping* TestNrSocket::create_port_mapping( + const nr_transport_addr& remote_address, + const RefPtr<NrSocketBase>& external_socket) const { + r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s creating port mapping %s -> %s", + internal_socket_->my_addr().as_string, + external_socket->my_addr().as_string, remote_address.as_string); + + return new PortMapping(remote_address, external_socket); +} + +TestNrSocket::PortMapping::PortMapping( + const nr_transport_addr& remote_address, + const RefPtr<NrSocketBase>& external_socket) + : external_socket_(external_socket) { + nr_transport_addr_copy(&remote_address_, &remote_address); +} + +int TestNrSocket::PortMapping::send_from_queue() { + MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP); + int r = 0; + + while (!send_queue_.empty()) { + UdpPacket& packet = send_queue_.front(); + r_log(LOG_GENERIC, LOG_DEBUG, + "PortMapping %s -> %s sending from queue to %s", + external_socket_->my_addr().as_string, remote_address_.as_string, + packet.remote_address_.as_string); + + r = external_socket_->sendto(packet.buffer_->data(), packet.buffer_->len(), + 0, &packet.remote_address_); + + if (r) { + if (r != R_WOULDBLOCK) { + r_log(LOG_GENERIC, LOG_ERR, "%s: Fatal error %d, stop trying", + __FUNCTION__, r); + send_queue_.clear(); + } else { + r_log(LOG_GENERIC, LOG_DEBUG, "Would block, will retry later"); + } + break; + } + + send_queue_.pop_front(); + } + + return r; +} + +int TestNrSocket::PortMapping::sendto(const void* msg, size_t len, + const nr_transport_addr& to) { + MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP); + r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s sending to %s", + external_socket_->my_addr().as_string, remote_address_.as_string, + to.as_string); + + last_used_ = PR_IntervalNow(); + int r = external_socket_->sendto(msg, len, 0, &to); + + if (r == R_WOULDBLOCK) { + r_log(LOG_GENERIC, LOG_DEBUG, "Enqueueing UDP packet to %s", to.as_string); + send_queue_.emplace_back(msg, len, to); + return 0; + } + if (r) { + r_log(LOG_GENERIC, LOG_ERR, "Error: %d", r); + } + + return r; +} + +int TestNrSocket::PortMapping::async_wait(int how, NR_async_cb cb, void* cb_arg, + char* function, int line) { + r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s waiting for %s", + external_socket_->my_addr().as_string, remote_address_.as_string, + how == NR_ASYNC_WAIT_READ ? "read" : "write"); + + return external_socket_->async_wait(how, cb, cb_arg, function, line); +} + +int TestNrSocket::PortMapping::cancel(int how) { + r_log(LOG_GENERIC, LOG_DEBUG, "PortMapping %s -> %s stop waiting for %s", + external_socket_->my_addr().as_string, remote_address_.as_string, + how == NR_ASYNC_WAIT_READ ? "read" : "write"); + + return external_socket_->cancel(how); +} + +class nr_stun_message_deleter { + public: + nr_stun_message_deleter() = default; + void operator()(nr_stun_message* msg) const { nr_stun_message_destroy(&msg); } +}; + +bool TestNrSocket::maybe_send_fake_response(const void* msg, size_t len, + const nr_transport_addr* to) { + Maybe<nsTArray<nsCString>> redirect_targets = maybe_get_redirect_targets(to); + if (!redirect_targets.isSome()) { + return false; + } + + std::unique_ptr<nr_stun_message, nr_stun_message_deleter> request; + { + nr_stun_message* temp = nullptr; + if (NS_WARN_IF(nr_stun_message_create2(&temp, (unsigned char*)msg, len))) { + return false; + } + request.reset(temp); + } + + if (NS_WARN_IF(nr_stun_decode_message(request.get(), nullptr, nullptr))) { + return false; + } + + std::unique_ptr<nr_stun_message, nr_stun_message_deleter> response; + { + nr_stun_message* temp = nullptr; + if (nr_stun_message_create(&temp)) { + MOZ_CRASH("nr_stun_message_create failed!"); + } + response.reset(temp); + } + + nr_stun_form_error_response(request.get(), response.get(), 300, + (char*)"Try alternate"); + + int port = 0; + if (nr_transport_addr_get_port(to, &port)) { + MOZ_CRASH(); + } + + for (const nsCString& address : *redirect_targets) { + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket attempting to add alternate server %s", address.Data()); + nr_transport_addr addr; + if (NS_WARN_IF(nr_str_port_to_transport_addr(address.Data(), port, + IPPROTO_UDP, &addr))) { + continue; + } + if (nr_stun_message_add_alternate_server_attribute(response.get(), &addr)) { + MOZ_CRASH("nr_stun_message_add_alternate_server_attribute failed!"); + } + } + + if (nr_stun_encode_message(response.get())) { + MOZ_CRASH("nr_stun_encode_message failed!"); + } + + nr_transport_addr response_from; + if (nr_transport_addr_is_wildcard(to)) { + // |to| points to an FQDN, and nICEr is delegating DNS lookup to us; we + // aren't _actually_ going to do that though, so we select a bogus address + // for the response to come from. TEST-NET is a fairly reasonable thing to + // use for this. + int port = 0; + if (nr_transport_addr_get_port(to, &port)) { + MOZ_CRASH(); + } + switch (to->ip_version) { + case NR_IPV4: + if (nr_str_port_to_transport_addr("198.51.100.1", port, to->protocol, + &response_from)) { + MOZ_CRASH(); + } + break; + case NR_IPV6: + if (nr_str_port_to_transport_addr("::ffff:198.51.100.1", port, + to->protocol, &response_from)) { + MOZ_CRASH(); + } + break; + default: + MOZ_CRASH(); + } + } else { + nr_transport_addr_copy(&response_from, to); + } + + read_buffer_.emplace_back(response->buffer, response->length, response_from); + + // We dispatch this, otherwise nICEr can trip over its shoelaces + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %p scheduling callback for redirect response", this); + GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction( + "Async readable callback for TestNrSocket", + [this, self = RefPtr<TestNrSocket>(this)] { + if (poll_flags() & PR_POLL_READ) { + fire_readable_callback(); + } else { + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %p deferring callback for redirect response", + this); + } + })); + + return true; +} + +Maybe<nsTArray<nsCString>> TestNrSocket::maybe_get_redirect_targets( + const nr_transport_addr* to) const { + Maybe<nsTArray<nsCString>> result; + + // 256 is overkill, but it hardly matters + char addrstring[256]; + if (nr_transport_addr_get_addrstring(to, addrstring, 256)) { + MOZ_CRASH("nr_transport_addr_get_addrstring failed!"); + } + + r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket checking redirect rules for %s", + addrstring); + auto it = nat_->stun_redirect_map_.find(nsCString(addrstring)); + if (it != nat_->stun_redirect_map_.end()) { + result = Some(it->second); + } + + return result; +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/test_nr_socket.h b/dom/media/webrtc/transport/test_nr_socket.h new file mode 100644 index 0000000000..c6a796c063 --- /dev/null +++ b/dom/media/webrtc/transport/test_nr_socket.h @@ -0,0 +1,370 @@ +/* -*- 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/. */ +/* + */ + +/* +Based partially on original code from nICEr and nrappkit. + +nICEr copyright: + +Copyright (c) 2007, Adobe Systems, Incorporated +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. + + +nrappkit copyright: + + Copyright (C) 2001-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Thu Dec 20 20:14:49 2001 +*/ + +// Original author: bcampen@mozilla.com [:bwc] + +#ifndef test_nr_socket__ +#define test_nr_socket__ + +extern "C" { +#include "transport_addr.h" +} + +#include "nr_socket_prsock.h" + +extern "C" { +#include "nr_socket.h" +} + +#include <set> +#include <map> +#include <list> +#include <string> + +#include "mozilla/UniquePtr.h" +#include "prinrval.h" +#include "mediapacket.h" + +namespace mozilla { + +class TestNrSocket; +class NrSocketProxyConfig; + +/** + * A group of TestNrSockets that behave as if they were behind the same NAT. + * @note We deliberately avoid addref/release of TestNrSocket here to avoid + * masking lifetime errors elsewhere. + */ +class TestNat { + public: + /** + * This allows TestNat traffic to be passively inspected. + * If a non-zero (error) value is returned, the packet will be dropped, + * allowing for tests to extend how packet manipulation is done by + * TestNat with having to modify TestNat itself. + */ + class NatDelegate { + public: + virtual int on_read(TestNat* nat, void* buf, size_t maxlen, + size_t* len) = 0; + virtual int on_sendto(TestNat* nat, const void* msg, size_t len, int flags, + const nr_transport_addr* to) = 0; + virtual int on_write(TestNat* nat, const void* msg, size_t len, + size_t* written) = 0; + }; + + typedef enum { + /** For mapping, one port is used for all destinations. + * For filtering, allow any external address/port. */ + ENDPOINT_INDEPENDENT, + + /** For mapping, one port for each destination address (for any port). + * For filtering, allow incoming traffic from addresses that outgoing + * traffic has been sent to. */ + ADDRESS_DEPENDENT, + + /** For mapping, one port for each destination address/port. + * For filtering, allow incoming traffic only from addresses/ports that + * outgoing traffic has been sent to. */ + PORT_DEPENDENT, + } NatBehavior; + + TestNat() + : enabled_(false), + filtering_type_(ENDPOINT_INDEPENDENT), + mapping_type_(ENDPOINT_INDEPENDENT), + mapping_timeout_(30000), + allow_hairpinning_(false), + refresh_on_ingress_(false), + block_udp_(false), + block_stun_(false), + block_tcp_(false), + block_tls_(false), + error_code_for_drop_(0), + delay_stun_resp_ms_(0), + nat_delegate_(nullptr), + sockets_() {} + + bool has_port_mappings() const; + + // Helps determine whether we're hairpinning + bool is_my_external_tuple(const nr_transport_addr& addr) const; + bool is_an_internal_tuple(const nr_transport_addr& addr) const; + + int create_socket_factory(nr_socket_factory** factorypp); + + void insert_socket(TestNrSocket* socket) { sockets_.insert(socket); } + + void erase_socket(TestNrSocket* socket) { sockets_.erase(socket); } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNat); + + static NatBehavior ToNatBehavior(const std::string& type); + + void set_proxy_config(std::shared_ptr<NrSocketProxyConfig> aProxyConfig); + + bool enabled_; + TestNat::NatBehavior filtering_type_; + TestNat::NatBehavior mapping_type_; + uint32_t mapping_timeout_; + bool allow_hairpinning_; + bool refresh_on_ingress_; + bool block_udp_; + bool block_stun_; + bool block_tcp_; + bool block_tls_; + bool error_code_for_drop_; + /* Note: this can only delay a single response so far (bug 1253657) */ + uint32_t delay_stun_resp_ms_; + + // When we see an outgoing STUN request with a destination address or + // destination FQDN that matches a key in this map, we respond with a STUN/300 + // with a list of ALTERNATE-SERVER fields based on the value in this map. + std::map<nsCString, CopyableTArray<nsCString>> stun_redirect_map_; + + NatDelegate* nat_delegate_; + std::shared_ptr<NrSocketProxyConfig> proxy_config_; + + private: + std::set<TestNrSocket*> sockets_; + + ~TestNat() = default; +}; + +/** + * Subclass of NrSocketBase that can simulate things like being behind a NAT, + * packet loss, latency, packet rewriting, etc. Also exposes some stuff that + * assists in diagnostics. + * This is accomplished by wrapping an "internal" socket (that handles traffic + * behind the NAT), and a collection of "external" sockets (that handle traffic + * into/out of the NAT) + */ +class TestNrSocket : public NrSocketBase { + public: + explicit TestNrSocket(TestNat* nat); + + bool has_port_mappings() const; + bool is_my_external_tuple(const nr_transport_addr& addr) const; + + // Overrides of NrSocketBase + int create(nr_transport_addr* addr) override; + int sendto(const void* msg, size_t len, int flags, + const nr_transport_addr* to) override; + int recvfrom(void* buf, size_t maxlen, size_t* len, int flags, + nr_transport_addr* from) override; + int getaddr(nr_transport_addr* addrp) override; + void close() override; + int connect(const nr_transport_addr* addr) override; + int write(const void* msg, size_t len, size_t* written) override; + int read(void* buf, size_t maxlen, size_t* len) override; + + int listen(int backlog) override; + int accept(nr_transport_addr* addrp, nr_socket** sockp) override; + int async_wait(int how, NR_async_cb cb, void* cb_arg, char* function, + int line) override; + int cancel(int how) override; + + // Need override since this is virtual in NrSocketBase + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNrSocket, override) + + private: + virtual ~TestNrSocket(); + + class UdpPacket { + public: + UdpPacket(const void* msg, size_t len, const nr_transport_addr& addr) + : buffer_(new MediaPacket) { + buffer_->Copy(static_cast<const uint8_t*>(msg), len); + nr_transport_addr_copy(&remote_address_, &addr); + } + + UdpPacket(UdpPacket&& aOrig) = default; + + ~UdpPacket() = default; + + nr_transport_addr remote_address_; + UniquePtr<MediaPacket> buffer_; + }; + + class PortMapping { + public: + PortMapping(const nr_transport_addr& remote_address, + const RefPtr<NrSocketBase>& external_socket); + + int sendto(const void* msg, size_t len, const nr_transport_addr& to); + int async_wait(int how, NR_async_cb cb, void* cb_arg, char* function, + int line); + int cancel(int how); + int send_from_queue(); + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PortMapping); + + PRIntervalTime last_used_; + RefPtr<NrSocketBase> external_socket_; + // For non-symmetric, most of the data here doesn't matter + nr_transport_addr remote_address_; + + private: + ~PortMapping() { external_socket_->close(); } + + // If external_socket_ returns E_WOULDBLOCK, we don't want to propagate + // that to the code using the TestNrSocket. We can also perhaps use this + // to help simulate things like latency. + std::list<UdpPacket> send_queue_; + }; + + struct DeferredPacket { + DeferredPacket(TestNrSocket* sock, const void* data, size_t len, int flags, + const nr_transport_addr* addr, + RefPtr<NrSocketBase> internal_socket) + : socket_(sock), + buffer_(), + flags_(flags), + internal_socket_(internal_socket) { + buffer_.Copy(reinterpret_cast<const uint8_t*>(data), len); + nr_transport_addr_copy(&to_, addr); + } + + TestNrSocket* socket_; + MediaPacket buffer_; + int flags_; + nr_transport_addr to_; + RefPtr<NrSocketBase> internal_socket_; + }; + + bool is_port_mapping_stale(const PortMapping& port_mapping) const; + bool allow_ingress(const nr_transport_addr& to, const nr_transport_addr& from, + PortMapping** port_mapping_used) const; + void destroy_stale_port_mappings(); + + static void socket_readable_callback(void* real_sock_v, int how, + void* test_sock_v); + void on_socket_readable(NrSocketBase* external_or_internal_socket); + void fire_readable_callback(); + + static void port_mapping_tcp_passthrough_callback(void* ext_sock_v, int how, + void* test_sock_v); + void cancel_port_mapping_async_wait(int how); + + static void port_mapping_writeable_callback(void* ext_sock_v, int how, + void* test_sock_v); + void write_to_port_mapping(NrSocketBase* external_socket); + bool is_tcp_connection_behind_nat() const; + + PortMapping* get_port_mapping(const nr_transport_addr& remote_addr, + TestNat::NatBehavior filter) const; + static bool port_mapping_matches(const PortMapping& port_mapping, + const nr_transport_addr& remote_addr, + TestNat::NatBehavior filter); + PortMapping* create_port_mapping( + const nr_transport_addr& remote_addr, + const RefPtr<NrSocketBase>& external_socket) const; + RefPtr<NrSocketBase> create_external_socket( + const nr_transport_addr& remote_addr) const; + + static void process_delayed_cb(NR_SOCKET s, int how, void* cb_arg); + + bool maybe_send_fake_response(const void* msg, size_t len, + const nr_transport_addr* to); + Maybe<nsTArray<nsCString>> maybe_get_redirect_targets( + const nr_transport_addr* to) const; + + RefPtr<NrSocketBase> readable_socket_; + // The socket for the "internal" address; used to talk to stuff behind the + // same nat. + RefPtr<NrSocketBase> internal_socket_; + RefPtr<TestNat> nat_; + bool tls_; + // Since our comparison logic is different depending on what kind of NAT + // we simulate, and the STL does not make it very easy to switch out the + // comparison function at runtime, and these lists are going to be very + // small anyway, we just brute-force it. + std::list<RefPtr<PortMapping>> port_mappings_; + + void* timer_handle_; + + // Just used for fake stun responses right now. Not _necessarily_ just UDP + // stuff, UdpPacket just has what we need to make this work for UDP. + std::list<UdpPacket> read_buffer_; + std::unique_ptr<nr_transport_addr> connect_fake_stun_address_; +}; + +} // namespace mozilla + +#endif // test_nr_socket__ diff --git a/dom/media/webrtc/transport/third_party/import.py b/dom/media/webrtc/transport/third_party/import.py new file mode 100644 index 0000000000..6defdf26b7 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/import.py @@ -0,0 +1,40 @@ +#!/usr/bin/env 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/. +# +# Execute as: /import.py <source-distribution> <target-dir> +# <target-dir> must have 'IMPORT_FILES' in it + +import sys +import re +import os +import shutil + +def die(msg): + sys.stderr.write('ERROR:' + msg + '\n') + sys.exit(1) + +DISTRO = sys.argv[1] +IMPORT_DIR = sys.argv[2] +FILES = [] + +f = open("%s/IMPORT_FILES" % IMPORT_DIR) +for l in f: + l = l.strip() + l = l.strip("'") + if l.startswith("#"): + continue + if not l: + continue + FILES.append(l) + +for f in FILES: + print f + SOURCE_PATH = "%s/%s"%(DISTRO,f) + DEST_PATH = "%s/%s"%(IMPORT_DIR,f) + if not os.path.exists(SOURCE_PATH): + die("%s does not exist"%SOURCE_PATH) + if not os.path.exists(os.path.dirname(DEST_PATH)): + os.makedirs(os.path.dirname(DEST_PATH)) + shutil.copyfile(SOURCE_PATH, DEST_PATH) diff --git a/dom/media/webrtc/transport/third_party/moz.build b/dom/media/webrtc/transport/third_party/moz.build new file mode 100644 index 0000000000..40de3c1ebb --- /dev/null +++ b/dom/media/webrtc/transport/third_party/moz.build @@ -0,0 +1,40 @@ +# -*- Mode: python; 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/. + +include('/build/gyp.mozbuild') + +GYP_DIRS += [ + 'nICEr', + 'nrappkit', +] + +# Set gyp vars that webrtc needs when building under various analysis tools. +# Primarily this prevents webrtc from setting NVALGRIND and breaking builds. +gyp_vars_copy = gyp_vars.copy() +if CONFIG['MOZ_VALGRIND']: + gyp_vars_copy.update(build_for_tool="memcheck") +elif CONFIG['MOZ_ASAN']: + gyp_vars_copy.update(build_for_tool="asan") +elif CONFIG['MOZ_TSAN']: + gyp_vars_copy.update(build_for_tool="tsan") + +# These files cannot be built in unified mode because of name clashes on RCSSTRING + +GYP_DIRS['nICEr'].input = 'nICEr/nicer.gyp' +GYP_DIRS['nICEr'].variables = gyp_vars_copy +GYP_DIRS['nICEr'].sandbox_vars['REQUIRES_UNIFIED_BUILD'] = True +GYP_DIRS['nICEr'].sandbox_vars['FINAL_LIBRARY'] = 'xul' +GYP_DIRS['nrappkit'].input = 'nrappkit/nrappkit.gyp' +GYP_DIRS['nrappkit'].variables = gyp_vars_copy +GYP_DIRS['nrappkit'].sandbox_vars['FINAL_LIBRARY'] = 'xul' + + +if CONFIG["CC_TYPE"] == "gcc": + GYP_DIRS['nICEr'].sandbox_vars['CFLAGS'] = ['-Wno-stringop-overflow'] + GYP_DIRS['nrappkit'].sandbox_vars['CFLAGS'] = ['-Wno-stringop-overflow'] + if int(CONFIG["CC_VERSION"].split(".")[0]) >= 8: + GYP_DIRS['nICEr'].sandbox_vars['CFLAGS'] += ['-Wno-stringop-truncation'] + GYP_DIRS['nrappkit'].sandbox_vars['CFLAGS'] += ['-Wno-stringop-truncation'] diff --git a/dom/media/webrtc/transport/third_party/nICEr/IMPORT_FILES b/dom/media/webrtc/transport/third_party/nICEr/IMPORT_FILES new file mode 100644 index 0000000000..c9b3938a49 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/IMPORT_FILES @@ -0,0 +1,73 @@ + # Crypto + ./src/crypto/nr_crypto.c + ./src/crypto/nr_crypto.h + #./src/crypto/nr_crypto_openssl.c + #./src/crypto/nr_crypto_openssl.h + + # ICE + ./src/ice/ice_candidate.c + ./src/ice/ice_candidate.h + ./src/ice/ice_candidate_pair.c + ./src/ice/ice_candidate_pair.h + ./src/ice/ice_codeword.h + ./src/ice/ice_component.c + ./src/ice/ice_component.h + ./src/ice/ice_ctx.c + ./src/ice/ice_ctx.h + ./src/ice/ice_handler.h + ./src/ice/ice_media_stream.c + ./src/ice/ice_media_stream.h + ./src/ice/ice_parser.c + ./src/ice/ice_peer_ctx.c + ./src/ice/ice_peer_ctx.h + ./src/ice/ice_reg.h + ./src/ice/ice_socket.c + ./src/ice/ice_socket.h + + # Net + ./src/net/nr_socket.c + ./src/net/nr_socket.h + #./src/net/nr_socket_local.c + ./src/net/nr_socket_local.h + ./src/net/transport_addr.c + ./src/net/transport_addr.h + ./src/net/transport_addr_reg.c + ./src/net/transport_addr_reg.h + + # STUN + ./src/stun/addrs.c + ./src/stun/addrs.h + ./src/stun/addrs-bsd.c + ./src/stun/addrs-bsd.h + ./src/stun/addrs-netlink.c + ./src/stun/addrs-netlink.h + ./src/stun/addrs-win32.c + ./src/stun/addrs-win32.h + ./src/stun/nr_socket_turn.c + ./src/stun/nr_socket_turn.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 + ./src/stun/stun_hint.h + ./src/stun/stun_msg.c + ./src/stun/stun_msg.h + ./src/stun/stun_proc.c + ./src/stun/stun_proc.h + ./src/stun/stun_reg.h + ./src/stun/stun_server_ctx.c + ./src/stun/stun_server_ctx.h + ./src/stun/stun_util.c + ./src/stun/stun_util.h + ./src/stun/turn_client_ctx.c + ./src/stun/turn_client_ctx.h + + # Util + ./src/util/cb_args.c + ./src/util/cb_args.h + ./src/util/ice_util.c + ./src/util/ice_util.h diff --git a/dom/media/webrtc/transport/third_party/nICEr/README b/dom/media/webrtc/transport/third_party/nICEr/README new file mode 100644 index 0000000000..390203dec5 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/README @@ -0,0 +1,74 @@ + nICEr 1.0 + +PREREQUISITES: +------------- +You must first obtain and build the following packages: + +* nrappkit + - http://nrappkit.sourceforge.net/ + +* OpenSSL-0.9.8g + - http://www.openssl.org/source/openssl-0.9.8g.tar.gz + + +For best results, the "ice-<version>" directory should be at +the same level as the "nrappkit" and "openssl-0.9.8g" +directories. I.e., the directory structure should look like: + + nrappkit/ + ice-<version>/ + openssl/ + include/ + lib/VC/ + + +BUILDING ON UNIX: +---------------- +Once the prerequisite packages are built, 'cd' to the +relevant build directory, one of: + + ice-<version>/make/darwin + ice-<version>/make/linux-fedora + ice-<version>/make/ubuntu + +and simply do a "make". + + +BUILDING ON WINDOWS: +------------------- +The Visual C++ project files are configured to expect the +directory structure described above. + +Note: Binary Windows builds of OpenSSL can be found at: + http://www.slproweb.com/products/Win32OpenSSL.html + +Once the prerequisite packages are built, open the VC++ 9.0 +solution file: ICE/make/win32/ice.sln and build the solution. +Note: Since the VC++ project/solution files are version 9.0, +Visual Studio 2008 is required. + + +STATUS: +------ +The ICE code has been tested on the following platforms: +-- Fedora Core 4 (Intel 32-bit) +-- Fedora Core 6 (Intel 32-bit) +-- Ubuntu 6.10 +-- MacOSX 10.4.9 +-- Windows Vista (Home Premium) +-- Windows XP Pro +-- Windows 2000 SP4 + + +KNOWN ISSUES: +------------ +-- TURN SET-ACTIVE-DESTINATION mode not yet supported. + +-- Problems may exist with the TURN client implementation; the TURN code + has received minimal testing due to the unavailability of a real + TURN server to test against. + +-- The ICE-Lite implementation is not complete. + +-- The new "impatient" timeout has not yet been thoroughly tested. + diff --git a/dom/media/webrtc/transport/third_party/nICEr/README_MOZILLA b/dom/media/webrtc/transport/third_party/nICEr/README_MOZILLA new file mode 100644 index 0000000000..8e058ac7da --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/README_MOZILLA @@ -0,0 +1,11 @@ +This code was imported from: + +https://svn.resiprocate.org/rep/resiprocate/main/nICEr + +SVN revision 9873 + +Some minor changes have been made to improve portability as well +as to add trickle ICE. These changes are being submitted for +upstreaming and can be found in upstream.diff. + + diff --git a/dom/media/webrtc/transport/third_party/nICEr/nicer.gyp b/dom/media/webrtc/transport/third_party/nICEr/nicer.gyp new file mode 100644 index 0000000000..488b9b229c --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/nicer.gyp @@ -0,0 +1,276 @@ +# 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/. +# +# nrappkit.gyp +# +# +{ + 'variables' : { + 'have_ethtool_cmd_speed_hi%': 1 + }, + 'targets' : [ + { + 'target_name' : 'nicer', + 'type' : 'static_library', + + 'include_dirs' : [ + ## EXTERNAL + # nrappkit + '../nrappkit/src/event', + '../nrappkit/src/log', + '../nrappkit/src/plugin', + '../nrappkit/src/registry', + '../nrappkit/src/share', + '../nrappkit/src/stats', + '../nrappkit/src/util', + '../nrappkit/src/util/libekr', + '../nrappkit/src/port/generic/include', + + # INTERNAL + "./src/crypto", + "./src/ice", + "./src/net", + "./src/stun", + "./src/util", + ], + + 'sources' : [ + # Crypto + "./src/crypto/nr_crypto.c", + "./src/crypto/nr_crypto.h", + #"./src/crypto/nr_crypto_openssl.c", + #"./src/crypto/nr_crypto_openssl.h", + + # ICE + "./src/ice/ice_candidate.c", + "./src/ice/ice_candidate.h", + "./src/ice/ice_candidate_pair.c", + "./src/ice/ice_candidate_pair.h", + "./src/ice/ice_codeword.h", + "./src/ice/ice_component.c", + "./src/ice/ice_component.h", + "./src/ice/ice_ctx.c", + "./src/ice/ice_ctx.h", + "./src/ice/ice_handler.h", + "./src/ice/ice_media_stream.c", + "./src/ice/ice_media_stream.h", + "./src/ice/ice_parser.c", + "./src/ice/ice_peer_ctx.c", + "./src/ice/ice_peer_ctx.h", + "./src/ice/ice_reg.h", + "./src/ice/ice_socket.c", + "./src/ice/ice_socket.h", + + # Net + "./src/net/nr_resolver.c", + "./src/net/nr_resolver.h", + "./src/net/nr_socket_wrapper.c", + "./src/net/nr_socket_wrapper.h", + "./src/net/nr_socket.c", + "./src/net/nr_socket.h", + #"./src/net/nr_socket_local.c", + "./src/net/nr_socket_local.h", + "./src/net/nr_socket_multi_tcp.c", + "./src/net/nr_socket_multi_tcp.h", + "./src/net/transport_addr.c", + "./src/net/transport_addr.h", + "./src/net/transport_addr_reg.c", + "./src/net/transport_addr_reg.h", + "./src/net/local_addr.c", + "./src/net/local_addr.h", + "./src/net/nr_interface_prioritizer.c", + "./src/net/nr_interface_prioritizer.h", + + # STUN + "./src/stun/addrs.c", + "./src/stun/addrs.h", + "./src/stun/addrs-bsd.c", + "./src/stun/addrs-bsd.h", + "./src/stun/addrs-netlink.c", + "./src/stun/addrs-netlink.h", + "./src/stun/addrs-win32.c", + "./src/stun/addrs-win32.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", + "./src/stun/stun_hint.h", + "./src/stun/stun_msg.c", + "./src/stun/stun_msg.h", + "./src/stun/stun_proc.c", + "./src/stun/stun_proc.h", + "./src/stun/stun_reg.h", + "./src/stun/stun_server_ctx.c", + "./src/stun/stun_server_ctx.h", + "./src/stun/stun_util.c", + "./src/stun/stun_util.h", + "./src/stun/turn_client_ctx.c", + "./src/stun/turn_client_ctx.h", + + # Util + "./src/util/cb_args.c", + "./src/util/cb_args.h", + "./src/util/ice_util.c", + "./src/util/ice_util.h", + + + ], + + 'defines' : [ + 'SANITY_CHECKS', + 'USE_TURN', + 'USE_ICE', + 'USE_RFC_3489_BACKWARDS_COMPATIBLE', + 'USE_STUND_0_96', + 'USE_STUN_PEDANTIC', + 'USE_TURN', + 'NR_SOCKET_IS_VOID_PTR', + 'restrict=', + 'R_PLATFORM_INT_TYPES=<stdint.h>', + 'R_DEFINED_INT2=int16_t', + 'R_DEFINED_UINT2=uint16_t', + 'R_DEFINED_INT4=int32_t', + 'R_DEFINED_UINT4=uint32_t', + 'R_DEFINED_INT8=int64_t', + 'R_DEFINED_UINT8=uint64_t', + ], + + 'conditions' : [ + ## Mac and BSDs + [ 'OS == "mac" or OS == "ios"', { + 'defines' : [ + 'DARWIN', + ], + }], + [ 'os_bsd == 1', { + 'defines' : [ + 'BSD', + ], + }], + [ 'OS == "mac" or OS == "ios" or os_bsd == 1', { + 'cflags_mozilla': [ + '-Wall', + '-Wno-parentheses', + '-Wno-strict-prototypes', + '-Wmissing-prototypes', + '-Wno-format', + '-Wno-format-security', + ], + 'defines' : [ + 'HAVE_LIBM=1', + 'HAVE_STRDUP=1', + 'HAVE_STRLCPY=1', + 'HAVE_SYS_TIME_H=1', + 'HAVE_VFPRINTF=1', + 'NEW_STDIO' + 'RETSIGTYPE=void', + 'TIME_WITH_SYS_TIME_H=1', + '__UNUSED__=__attribute__((unused))', + ], + + 'include_dirs': [ + '../nrappkit/src/port/darwin/include' + ], + + 'sources': [ + ], + }], + + ## Win + [ 'OS == "win"', { + 'defines' : [ + 'WIN32', + '_WINSOCK_DEPRECATED_NO_WARNINGS', + 'USE_ICE', + 'USE_TURN', + 'USE_RFC_3489_BACKWARDS_COMPATIBLE', + 'USE_STUND_0_96', + 'USE_STUN_PEDANTIC', + '_CRT_SECURE_NO_WARNINGS', + '__UNUSED__=', + 'HAVE_STRDUP', + 'NO_REG_RPC' + ], + + 'include_dirs': [ + '../nrappkit/src/port/win32/include' + ], + }], + + # Windows, clang-cl build + [ 'clang_cl == 1', { + 'cflags_mozilla': [ + '-Xclang', + '-Wall', + '-Xclang', + '-Wno-parentheses', + '-Wno-pointer-sign', + '-Wno-strict-prototypes', + '-Xclang', + '-Wno-unused-function', + '-Wmissing-prototypes', + '-Wno-format', + '-Wno-format-security', + ], + }], + + ## Linux/Android + [ '(OS == "linux") or (OS == "android")', { + 'cflags_mozilla': [ + '-Wall', + '-Wno-parentheses', + '-Wno-strict-prototypes', + '-Wmissing-prototypes', + '-Wno-format', + '-Wno-format-security', + ], + 'defines' : [ + 'LINUX', + 'HAVE_LIBM=1', + 'HAVE_STRDUP=1', + 'HAVE_STRLCPY=1', + 'HAVE_SYS_TIME_H=1', + 'HAVE_VFPRINTF=1', + 'NEW_STDIO' + 'RETSIGTYPE=void', + 'TIME_WITH_SYS_TIME_H=1', + '__UNUSED__=__attribute__((unused))', + ], + + 'include_dirs': [ + '../nrappkit/src/port/linux/include' + ], + + 'sources': [ + ], + }], + ['have_ethtool_cmd_speed_hi==0', { + 'defines': [ + "DONT_HAVE_ETHTOOL_SPEED_HI", + ] + }], + # libFuzzer instrumentation is not compatible with TSan. + # See also the comment in build/moz.configure/toolchain.configure. + ['(libfuzzer == 1) and (tsan == 0) and (libfuzzer_fuzzer_no_link_flag == 1)', { + 'cflags_mozilla': [ + '-fsanitize=fuzzer-no-link' + ], + }], + ['(libfuzzer == 1) and (tsan == 0) and (libfuzzer_fuzzer_no_link_flag == 0)', { + 'cflags_mozilla': [ + '-fsanitize-coverage=trace-pc-guard,trace-cmp' + ], + }], + ], + }] +} + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.c b/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.c new file mode 100644 index 0000000000..4c430b0e8b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.c @@ -0,0 +1,67 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 "nr_crypto.h" + +static int nr_ice_crypto_dummy_random_bytes(UCHAR *buf, size_t len) + { + fprintf(stderr,"Need to define crypto API implementation\n"); + + exit(1); + } + +static int nr_ice_crypto_dummy_hmac_sha1(UCHAR *key, size_t key_l, UCHAR *buf, size_t buf_l, UCHAR digest[20]) + { + fprintf(stderr,"Need to define crypto API implementation\n"); + + exit(1); + } + +static int nr_ice_crypto_dummy_md5(UCHAR *buf, size_t buf_l, UCHAR digest[16]) + { + fprintf(stderr,"Need to define crypto API implementation\n"); + + exit(1); + } + +static nr_ice_crypto_vtbl nr_ice_crypto_dummy_vtbl= { + nr_ice_crypto_dummy_random_bytes, + nr_ice_crypto_dummy_hmac_sha1, + nr_ice_crypto_dummy_md5 +}; + + + +nr_ice_crypto_vtbl *nr_crypto_vtbl=&nr_ice_crypto_dummy_vtbl; + + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.h b/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.h new file mode 100644 index 0000000000..5fbbd8f097 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.h @@ -0,0 +1,52 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _nr_crypto_h +#define _nr_crypto_h + + +typedef struct nr_ice_crypto_vtbl_ { + int (*random_bytes)(UCHAR *buf, size_t len); + int (*hmac_sha1)(UCHAR *key, size_t key_l, UCHAR *buf, size_t buf_l, UCHAR digest[20]); + int (*md5)(UCHAR *buf, size_t buf_l, UCHAR digest[16]); +} nr_ice_crypto_vtbl; + +extern nr_ice_crypto_vtbl *nr_crypto_vtbl; + +#define nr_crypto_random_bytes(a,b) nr_crypto_vtbl->random_bytes(a,b) +#define nr_crypto_hmac_sha1(a,b,c,d,e) nr_crypto_vtbl->hmac_sha1(a,b,c,d,e) +#define nr_crypto_md5(a,b,c) nr_crypto_vtbl->md5(a,b,c) + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c new file mode 100644 index 0000000000..b7cf2c9a99 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c @@ -0,0 +1,1052 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <csi_platform.h> +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <sys/queue.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include "nr_api.h" +#include "registry.h" +#include "nr_socket.h" +#include "async_timer.h" + +#include "stun_client_ctx.h" +#include "stun_server_ctx.h" +#include "turn_client_ctx.h" +#include "ice_ctx.h" +#include "ice_candidate.h" +#include "ice_codeword.h" +#include "ice_reg.h" +#include "ice_util.h" +#include "nr_socket_turn.h" +#include "nr_socket.h" +#include "nr_socket_multi_tcp.h" + +static int next_automatic_preference = 127; + +static int nr_ice_candidate_initialize2(nr_ice_candidate *cand); +static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand); +static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand); +static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg); +#ifdef USE_TURN +static int nr_ice_start_relay_turn(nr_ice_candidate *cand); +static void nr_ice_turn_allocated_cb(NR_SOCKET sock, int how, void *cb_arg); +static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr); +#endif /* USE_TURN */ + +void nr_ice_candidate_compute_codeword(nr_ice_candidate *cand) + { + char as_string[1024]; + + snprintf(as_string, + sizeof(as_string), + "%s(%s)", + cand->addr.as_string, + cand->label); + + nr_ice_compute_codeword(as_string,strlen(as_string),cand->codeword); + } + +char *nr_ice_candidate_type_names[]={0,"host","srflx","prflx","relay",0}; +char *nr_ice_candidate_tcp_type_names[]={0,"active","passive","so",0}; + +static const char *nr_ctype_name(nr_ice_candidate_type ctype) { + assert(ctype<CTYPE_MAX && ctype>0); + if (ctype <= 0 || ctype >= CTYPE_MAX) { + return "ERROR"; + } + return nr_ice_candidate_type_names[ctype]; +} + +static const char *nr_tcp_type_name(nr_socket_tcp_type tcp_type) { + assert(tcp_type<TCP_TYPE_MAX && tcp_type>0); + if (tcp_type <= 0 || tcp_type >= TCP_TYPE_MAX) { + return "ERROR"; + } + return nr_ice_candidate_tcp_type_names[tcp_type]; +} + +static int nr_ice_candidate_format_stun_label(char *label, size_t size, nr_ice_candidate *cand) + { + *label = 0; + snprintf(label, size, "%s(%s|%s)", nr_ctype_name(cand->type), + cand->base.as_string, cand->stun_server->addr.as_string); + + return (0); + } + +int nr_ice_candidate_create(nr_ice_ctx *ctx,nr_ice_component *comp,nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_socket_tcp_type tcp_type, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp) + { + assert(!(comp->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) || ctype == RELAYED); + nr_ice_candidate *cand=0; + nr_ice_candidate *tmp=0; + int r,_status; + char label[512]; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + cand->state=NR_ICE_CAND_STATE_CREATED; + cand->ctx=ctx; + cand->isock=isock; + cand->osock=osock; + cand->type=ctype; + cand->tcp_type=tcp_type; + cand->stun_server=stun_server; + cand->component_id=component_id; + cand->component=comp; + cand->stream=comp->stream; + + /* Extract the addr as the base */ + if(r=nr_socket_getaddr(cand->isock->sock,&cand->base)) + ABORT(r); + + switch(ctype) { + case HOST: + snprintf(label, sizeof(label), "host(%s)", cand->base.as_string); + break; + + case SERVER_REFLEXIVE: + if(r=nr_ice_candidate_format_stun_label(label, sizeof(label), cand)) + ABORT(r); + break; + + case RELAYED: + if(r=nr_ice_candidate_format_stun_label(label, sizeof(label), cand)) + ABORT(r); + break; + + case PEER_REFLEXIVE: + snprintf(label, sizeof(label), "prflx"); + break; + + default: + assert(0); /* Can't happen */ + ABORT(R_BAD_ARGS); + } + + if (tcp_type) { + const char* ttype = nr_tcp_type_name(tcp_type); + const int tlen = strlen(ttype)+1; /* plus space */ + const size_t llen=strlen(label); + if (snprintf(label+llen, sizeof(label)-llen, " %s", ttype) != tlen) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): truncated tcp type added to buffer", + ctx->label); + } + } + + if(!(cand->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + if(r=nr_ice_get_foundation(ctx,cand)) + ABORT(r); + if(r=nr_ice_candidate_compute_priority(cand)) + ABORT(r); + + TAILQ_FOREACH(tmp,&isock->candidates,entry_sock){ + if(cand->priority==tmp->priority){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): duplicate priority %u candidate %s and candidate %s", + ctx->label,cand->priority,cand->label,tmp->label); + } + } + + if(ctype==RELAYED) + cand->u.relayed.turn_sock=osock; + + + /* Add the candidate to the isock list*/ + TAILQ_INSERT_TAIL(&isock->candidates,cand,entry_sock); + + nr_ice_candidate_compute_codeword(cand); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): created candidate %s with type %s", + ctx->label,cand->codeword,cand->label,nr_ctype_name(ctype)); + + *candp=cand; + + _status=0; + abort: + if (_status){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Failed to create candidate of type %s", ctx->label,nr_ctype_name(ctype)); + nr_ice_candidate_destroy(&cand); + } + return(_status); + } + + +/* Create a peer reflexive candidate */ +int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp) + { + nr_ice_candidate *cand=0; + nr_ice_candidate_type ctype=PEER_REFLEXIVE; + int r,_status; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + if(!(cand->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + cand->state=NR_ICE_CAND_STATE_INITIALIZED; + cand->ctx=ctx; + cand->type=ctype; + cand->component_id=comp->component_id; + cand->component=comp; + cand->stream=comp->stream; + + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): creating candidate with type %s", + ctx->label,label,nr_ctype_name(ctype)); + + if(r=nr_transport_addr_copy(&cand->base,addr)) + ABORT(r); + if(r=nr_transport_addr_copy(&cand->addr,addr)) + ABORT(r); + /* Bogus foundation */ + if(!(cand->foundation=r_strdup(cand->addr.as_string))) + ABORT(R_NO_MEMORY); + + nr_ice_candidate_compute_codeword(cand); + + *candp=cand; + + _status=0; + abort: + if (_status){ + nr_ice_candidate_destroy(&cand); + } + return(_status); + } + +static void nr_ice_candidate_mark_done(nr_ice_candidate *cand, int state) + { + if (!cand) { + assert(0); + return; + } + + /* If this is a relay candidate, there's likely to be a srflx that is + * piggybacking on it. Make sure it is marked done too. */ + if ((cand->type == RELAYED) && cand->u.relayed.srvflx_candidate) { + nr_ice_candidate *srflx=cand->u.relayed.srvflx_candidate; + if (state == NR_ICE_CAND_STATE_INITIALIZED && + nr_turn_client_get_mapped_address(cand->u.relayed.turn, + &srflx->addr)) { + r_log(LOG_ICE, LOG_WARNING, "ICE(%s)/CAND(%s): Failed to get mapped address from TURN allocate response, srflx failed.", cand->ctx->label, cand->label); + nr_ice_candidate_mark_done(srflx, NR_ICE_CAND_STATE_FAILED); + } else { + nr_ice_candidate_mark_done(srflx, state); + } + } + + NR_async_cb done_cb=cand->done_cb; + cand->done_cb=0; + cand->state=state; + /* This might destroy cand! */ + if (done_cb) { + done_cb(0,0,cand->cb_arg); + } + } + +int nr_ice_candidate_destroy(nr_ice_candidate **candp) + { + nr_ice_candidate *cand=0; + + if(!candp || !*candp) + return(0); + + cand=*candp; + + nr_ice_candidate_stop_gathering(cand); + + switch(cand->type){ + case HOST: + break; +#ifdef USE_TURN + case RELAYED: + // record stats back to the ice ctx on destruction + if (cand->u.relayed.turn) { + nr_accumulate_count(&(cand->ctx->stats.turn_401s), cand->u.relayed.turn->cnt_401s); + nr_accumulate_count(&(cand->ctx->stats.turn_403s), cand->u.relayed.turn->cnt_403s); + nr_accumulate_count(&(cand->ctx->stats.turn_438s), cand->u.relayed.turn->cnt_438s); + + nr_turn_stun_ctx* stun_ctx; + stun_ctx = STAILQ_FIRST(&cand->u.relayed.turn->stun_ctxs); + while (stun_ctx) { + nr_accumulate_count(&(cand->ctx->stats.stun_retransmits), stun_ctx->stun->retransmit_ct); + + stun_ctx = STAILQ_NEXT(stun_ctx, entry); + } + } + if (cand->u.relayed.turn_handle) + nr_ice_socket_deregister(cand->isock, cand->u.relayed.turn_handle); + if (cand->u.relayed.srvflx_candidate) + cand->u.relayed.srvflx_candidate->u.srvrflx.relay_candidate=0; + nr_turn_client_ctx_destroy(&cand->u.relayed.turn); + nr_socket_destroy(&cand->u.relayed.turn_sock); + break; +#endif /* USE_TURN */ + case SERVER_REFLEXIVE: + if (cand->u.srvrflx.stun_handle) + nr_ice_socket_deregister(cand->isock, cand->u.srvrflx.stun_handle); + if (cand->u.srvrflx.relay_candidate) + cand->u.srvrflx.relay_candidate->u.relayed.srvflx_candidate=0; + nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun); + break; + default: + break; + } + + RFREE(cand->mdns_addr); + RFREE(cand->foundation); + RFREE(cand->label); + RFREE(cand); + + return(0); + } + +void nr_ice_candidate_stop_gathering(nr_ice_candidate *cand) + { + if (cand->state == NR_ICE_CAND_STATE_INITIALIZING) { + /* Make sure the ICE ctx isn't still waiting around for this candidate + * to init. */ + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + + NR_async_timer_cancel(cand->delay_timer); + cand->delay_timer=0; + NR_async_timer_cancel(cand->ready_cb_timer); + cand->ready_cb_timer=0; + + if(cand->resolver_handle){ + nr_resolver_cancel(cand->ctx->resolver,cand->resolver_handle); + cand->resolver_handle=0; + } + } + +/* This algorithm is not super-fast, but I don't think we need a hash + table just yet and it produces a small foundation string */ +static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand) + { + nr_ice_foundation *foundation; + int i=0; + char fnd[20]; + int _status; + + foundation=STAILQ_FIRST(&ctx->foundations); + while(foundation){ + if(nr_transport_addr_cmp(&cand->base,&foundation->addr,NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) + goto next; + // cast necessary because there is no guarantee that enum is signed. + // foundation->type should probably match nr_ice_candidate_type + if((int)cand->type != foundation->type) + goto next; + if(cand->type == SERVER_REFLEXIVE || cand->type == RELAYED) { + if(nr_transport_addr_cmp(&cand->stun_server->addr, &foundation->stun_server_addr, NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) + goto next; + } + + snprintf(fnd,sizeof(fnd),"%d",i); + if(!(cand->foundation=r_strdup(fnd))) + ABORT(R_NO_MEMORY); + return(0); + + next: + foundation=STAILQ_NEXT(foundation,entry); + i++; + } + + if(!(foundation=RCALLOC(sizeof(nr_ice_foundation)))) + ABORT(R_NO_MEMORY); + nr_transport_addr_copy(&foundation->addr,&cand->base); + foundation->type=cand->type; + if(cand->type == SERVER_REFLEXIVE || cand->type == RELAYED) { + nr_transport_addr_copy(&foundation->stun_server_addr, &cand->stun_server->addr); + } + STAILQ_INSERT_TAIL(&ctx->foundations,foundation,entry); + + snprintf(fnd,sizeof(fnd),"%d",i); + if(!(cand->foundation=r_strdup(fnd))) + ABORT(R_NO_MEMORY); + + _status=0; + abort: + return(_status); + } + +int nr_ice_candidate_compute_priority(nr_ice_candidate *cand) + { + UCHAR type_preference; + UCHAR interface_preference; + UCHAR stun_priority; + UCHAR direction_priority=0; + int r,_status; + + if (cand->base.protocol != IPPROTO_UDP && cand->base.protocol != IPPROTO_TCP){ + r_log(LOG_ICE,LOG_ERR,"Unknown protocol type %u", + (unsigned int)cand->base.protocol); + ABORT(R_INTERNAL); + } + + switch(cand->type){ + case HOST: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_HOST,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_HOST_TCP,&type_preference)) + ABORT(r); + } + stun_priority=0; + break; + case RELAYED: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED_TCP,&type_preference)) + ABORT(r); + } + stun_priority=31-cand->stun_server->id; + break; + case SERVER_REFLEXIVE: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_SRV_RFLX,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP,&type_preference)) + ABORT(r); + } + stun_priority=31-cand->stun_server->id; + break; + case PEER_REFLEXIVE: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_PEER_RFLX,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP,&type_preference)) + ABORT(r); + } + stun_priority=0; + break; + default: + ABORT(R_INTERNAL); + } + + if(cand->base.protocol == IPPROTO_TCP){ + switch (cand->tcp_type) { + case TCP_TYPE_ACTIVE: + if (cand->type == HOST) + direction_priority=6; + else + direction_priority=4; + break; + case TCP_TYPE_PASSIVE: + if (cand->type == HOST) + direction_priority=4; + else + direction_priority=2; + break; + case TCP_TYPE_SO: + if (cand->type == HOST) + direction_priority=2; + else + direction_priority=6; + break; + case TCP_TYPE_NONE: + break; + case TCP_TYPE_MAX: + default: + assert(0); + ABORT(R_INTERNAL); + } + } + + if(type_preference > 126) + r_log(LOG_ICE,LOG_ERR,"Illegal type preference %d",type_preference); + + if(!cand->ctx->interface_prioritizer) { + /* Prioritizer is not set, read from registry */ + if(r=NR_reg_get2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname, + &interface_preference)) { + if (r==R_NOT_FOUND) { + if (next_automatic_preference == 1) { + r_log(LOG_ICE,LOG_ERR,"Out of preference values. Can't assign one for interface %s",cand->base.ifname); + ABORT(R_NOT_FOUND); + } + r_log(LOG_ICE,LOG_DEBUG,"Automatically assigning preference for interface %s->%d",cand->base.ifname, + next_automatic_preference); + if (r=NR_reg_set2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,next_automatic_preference)){ + ABORT(r); + } + interface_preference=next_automatic_preference << 1; + next_automatic_preference--; + if (cand->base.ip_version == NR_IPV6) { + /* Prefer IPV6 over IPV4 on the same interface. */ + interface_preference += 1; + } + } + else { + ABORT(r); + } + } + } + else { + char key_of_interface[MAXIFNAME + 41]; + nr_transport_addr addr; + + if(r=nr_socket_getaddr(cand->isock->sock, &addr)) + ABORT(r); + + if(r=nr_transport_addr_fmt_ifname_addr_string(&addr,key_of_interface, + sizeof(key_of_interface))) { + ABORT(r); + } + if(r=nr_interface_prioritizer_get_priority(cand->ctx->interface_prioritizer, + key_of_interface,&interface_preference)) { + ABORT(r); + } + } + + assert(stun_priority < 32); + assert(direction_priority < 8); + + cand->priority= + (type_preference << 24) | + (interface_preference << 16) | + (direction_priority << 13) | + (stun_priority << 8) | + (256 - cand->component_id); + + /* S 4.1.2 */ + assert(cand->priority>=1&&cand->priority<=2147483647); + + _status=0; + abort: + return(_status); + } + +static void nr_ice_candidate_fire_ready_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand = cb_arg; + + cand->ready_cb_timer = 0; + cand->ready_cb(0, 0, cand->ready_cb_arg); + } + +static int nr_ice_candidate_use_nr_resolver(nr_transport_addr *addr) + { + if(addr->fqdn[0] == 0) { + return 0; + } + + char use = 0; + if(addr->protocol == IPPROTO_UDP) { + NR_reg_get_char(NR_ICE_REG_USE_NR_RESOLVER_FOR_UDP, &use); + } else if(addr->protocol == IPPROTO_TCP) { + NR_reg_get_char(NR_ICE_REG_USE_NR_RESOLVER_FOR_TCP, &use); + } else { + assert(0); + } + + return use; + } + +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; + cand->done_cb=ready_cb; + cand->cb_arg=cb_arg; + cand->state=NR_ICE_CAND_STATE_INITIALIZING; + + switch(cand->type){ + case HOST: + if(r=nr_socket_getaddr(cand->isock->sock,&cand->addr)) + ABORT(r); + cand->osock=cand->isock->sock; + // 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; + /* Fall through */ +#endif + case SERVER_REFLEXIVE: + if (nr_transport_addr_cmp(&cand->base, &cand->stun_server->addr, + NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL)) { + r_log(LOG_ICE, LOG_INFO, + "ICE-CANDIDATE(%s): Skipping srflx/relayed candidate because " + "of IP version/transport mis-match with STUN/TURN server " + "(%u/%s - %u/%s).", + cand->label, cand->base.ip_version, + cand->base.protocol == IPPROTO_UDP ? "UDP" : "TCP", + cand->stun_server->addr.ip_version, + cand->stun_server->addr.protocol == IPPROTO_UDP ? "UDP" + : "TCP"); + ABORT(R_NOT_FOUND); /* Same error code when DNS lookup fails */ + } + + if(nr_ice_candidate_use_nr_resolver(&cand->stun_server->addr)) { + r_log( + LOG_ICE, LOG_DEBUG, + "ICE-CANDIDATE(%s): Starting DNS resolution (%u/%s - %u/%s).", + cand->label, cand->base.ip_version, + cand->base.protocol == IPPROTO_UDP ? "UDP" : "TCP", + cand->stun_server->addr.ip_version, + cand->stun_server->addr.protocol == IPPROTO_UDP ? "UDP" : "TCP"); + nr_resolver_resource resource; + int port; + resource.domain_name = cand->stun_server->addr.fqdn; + if (r = nr_transport_addr_get_port(&cand->stun_server->addr, &port)) { + ABORT(r); + } + resource.port = (uint16_t)port; + resource.stun_turn=protocol; + resource.transport_protocol = cand->stun_server->addr.protocol; + + switch (cand->base.ip_version) { + case NR_IPV4: + resource.address_family=AF_INET; + break; + case NR_IPV6: + resource.address_family=AF_INET6; + break; + default: + assert(0); + ABORT(R_BAD_ARGS); + } + + /* 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, + &resource, + nr_ice_candidate_resolved_cb, + (void *)cand, + &cand->resolver_handle)){ + r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not invoke DNS resolver",cand->label); + ABORT(r); + } + } else { + /* No nr_resolver for this, just copy the address and finish init */ + if (r = nr_transport_addr_copy(&cand->stun_server_addr, + &cand->stun_server->addr)) { + r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not copy STUN server addr", cand->label); + ABORT(r); + } + + if(r=nr_ice_candidate_initialize2(cand)) + ABORT(r); + } + break; + default: + ABORT(R_INTERNAL); + } + + nr_ice_candidate_compute_codeword(cand); + + _status=0; + abort: + if(_status && _status!=R_WOULDBLOCK) + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + return(_status); + } + + +static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->resolver_handle=0; + + if(addr){ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): resolved candidate %s. addr=%s", + cand->ctx->label,cand->label,addr->as_string); + } + else { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to resolve candidate %s.", + cand->ctx->label,cand->label); + ABORT(R_NOT_FOUND); + } + + if (nr_transport_addr_check_compatibility(addr, &cand->base)) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Skipping STUN server because of link local mis-match for candidate %s",cand->ctx->label,cand->label); + ABORT(R_NOT_FOUND); + } + + /* Copy the address */ + if(r=nr_transport_addr_copy(&cand->stun_server_addr,addr)) + ABORT(r); + + if (cand->tcp_type == TCP_TYPE_PASSIVE || cand->tcp_type == TCP_TYPE_SO){ + if (r=nr_socket_multi_tcp_stun_server_connect(cand->osock, addr)) + ABORT(r); + } + + /* Now start initializing */ + if(r=nr_ice_candidate_initialize2(cand)) + ABORT(r); + + _status=0; + abort: + if(_status && _status!=R_WOULDBLOCK) { + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + return(_status); + } + +static int nr_ice_candidate_initialize2(nr_ice_candidate *cand) + { + int r,_status; + + switch(cand->type){ + case HOST: + assert(0); /* Can't happen */ + ABORT(R_INTERNAL); + break; +#ifdef USE_TURN + case RELAYED: + if(r=nr_ice_start_relay_turn(cand)) + ABORT(r); + ABORT(R_WOULDBLOCK); + break; +#endif /* USE_TURN */ + case SERVER_REFLEXIVE: + /* Need to start stun */ + if(r=nr_ice_srvrflx_start_stun(cand)) + ABORT(r); + cand->osock=cand->isock->sock; + ABORT(R_WOULDBLOCK); + break; + default: + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +static void nr_ice_srvrflx_start_stun_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->delay_timer=0; + +/* TODO: if the response is a BINDING-ERROR-RESPONSE, then restart + * TODO: using NR_STUN_CLIENT_MODE_BINDING_REQUEST because the + * TODO: server may not have understood the 0.96-style request */ + if(r=nr_stun_client_start(cand->u.srvrflx.stun, NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH, nr_ice_srvrflx_stun_finished_cb, cand)) + ABORT(r); + + if(r=nr_ice_ctx_remember_id(cand->ctx, cand->u.srvrflx.stun->request)) + ABORT(r); + + if(r=nr_ice_socket_register_stun_client(cand->isock,cand->u.srvrflx.stun,&cand->u.srvrflx.stun_handle)) + ABORT(r); + + _status=0; + abort: + if (_status && (cand->u.srvrflx.stun->state==NR_STUN_CLIENT_STATE_RUNNING)) { + nr_stun_client_failed(cand->u.srvrflx.stun); + } + return; + } + +static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand) + { + int r,_status; + + assert(!cand->delay_timer); + if(r=nr_stun_client_ctx_create(cand->label, cand->isock->sock, + &cand->stun_server_addr, cand->stream->ctx->gather_rto, + &cand->u.srvrflx.stun)) + ABORT(r); + + NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_srvrflx_start_stun_timer_cb,cand,&cand->delay_timer); + cand->stream->ctx->stun_delay += cand->stream->ctx->Ta; + + _status=0; + abort: + return(_status); + } + +#ifdef USE_TURN +static void nr_ice_start_relay_turn_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->delay_timer=0; + + if(r=nr_turn_client_allocate(cand->u.relayed.turn, nr_ice_turn_allocated_cb, cb_arg)) + ABORT(r); + + if(r=nr_ice_socket_register_turn_client(cand->isock, cand->u.relayed.turn, + cand->osock, &cand->u.relayed.turn_handle)) + ABORT(r); + + _status=0; + abort: + if(_status && (cand->u.relayed.turn->state==NR_TURN_CLIENT_STATE_ALLOCATING)){ + nr_turn_client_failed(cand->u.relayed.turn); + } + return; + } + +static int nr_ice_start_relay_turn(nr_ice_candidate *cand) + { + int r,_status; + assert(!cand->delay_timer); + if(r=nr_turn_client_ctx_create(cand->label, cand->isock->sock, + cand->u.relayed.server->username, + cand->u.relayed.server->password, + &cand->stun_server_addr, + cand->component->ctx, + &cand->u.relayed.turn)) + ABORT(r); + + if(r=nr_socket_turn_set_ctx(cand->osock, cand->u.relayed.turn)) + ABORT(r); + + NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_start_relay_turn_timer_cb,cand,&cand->delay_timer); + cand->stream->ctx->stun_delay += cand->stream->ctx->Ta; + + _status=0; + abort: + return(_status); + } +#endif /* USE_TURN */ + +static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg) + { + int _status; + nr_ice_candidate *cand=cb_arg; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): %s",cand->ctx->label,cand->label,__FUNCTION__); + + /* Deregister to suppress duplicates */ + if(cand->u.srvrflx.stun_handle){ /* This test because we might have failed before CB registered */ + nr_ice_socket_deregister(cand->isock,cand->u.srvrflx.stun_handle); + cand->u.srvrflx.stun_handle=0; + } + + switch(cand->u.srvrflx.stun->state){ + /* OK, we should have a mapped address */ + case NR_STUN_CLIENT_STATE_DONE: + /* Copy the address */ + nr_transport_addr_copy(&cand->addr, &cand->u.srvrflx.stun->results.stun_binding_response.mapped_addr); + cand->addr.protocol=cand->base.protocol; + nr_transport_addr_fmt_addr_string(&cand->addr); + nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun); + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_INITIALIZED); + cand=0; + break; + + /* This failed, so go to the next STUN server if there is one */ + case NR_STUN_CLIENT_STATE_FAILED: + ABORT(R_NOT_FOUND); + break; + default: + ABORT(R_INTERNAL); + } + _status = 0; + abort: + if(_status){ + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + } + +#ifdef USE_TURN +static void nr_ice_turn_allocated_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_ice_candidate *cand=cb_arg; + nr_turn_client_ctx *turn=cand->u.relayed.turn; + char *label; + nr_transport_addr relay_addr; + + switch(turn->state){ + /* OK, we should have a mapped address */ + case NR_TURN_CLIENT_STATE_ALLOCATED: + if (r=nr_turn_client_get_relayed_address(turn, &relay_addr)) + ABORT(r); + + if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|", + relay_addr.as_string,")",NULL)) + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG,"TURN-CLIENT(%s)/CAND(%s): Switching from TURN to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label); + + /* Copy the relayed address into the candidate addr and + into the candidate base. Note that we need to keep the + ifname in the base. */ + if (r=nr_transport_addr_copy(&cand->addr, &relay_addr)) + ABORT(r); + if (r=nr_transport_addr_copy_keep_ifname(&cand->base, &relay_addr)) /* Need to keep interface for priority calculation */ + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): new relay base=%s addr=%s", cand->ctx->label, cand->label, cand->base.as_string, cand->addr.as_string); + + RFREE(cand->label); + cand->label=label; + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_INITIALIZED); + cand = 0; + + break; + + case NR_TURN_CLIENT_STATE_FAILED: + case NR_TURN_CLIENT_STATE_CANCELLED: + r_log(NR_LOG_TURN, LOG_WARNING, + "ICE-CANDIDATE(%s): nr_turn_allocated_cb called with state %d", + cand->label, turn->state); + /* This failed, so go to the next TURN server if there is one */ + ABORT(R_NOT_FOUND); + break; + default: + assert(0); /* should never happen */ + ABORT(R_INTERNAL); + } + + _status=0; + abort: + if(_status){ + if (cand) { + r_log(NR_LOG_TURN, LOG_WARNING, + "ICE-CANDIDATE(%s): nr_turn_allocated_cb failed", cand->label); + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + } + } +#endif /* USE_TURN */ + +/* Format the candidate attribute as per ICE S 15.1 */ +int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen, int obfuscate_srflx_addr) + { + int r,_status; + char addr[64]; + int port; + int len; + nr_transport_addr *raddr; + + assert(!strcmp(nr_ice_candidate_type_names[HOST], "host")); + assert(!strcmp(nr_ice_candidate_type_names[RELAYED], "relay")); + + if (cand->mdns_addr) { + /* mdns_addr is NSID_LENGTH which is 39, - 2 for removing the "{" and "}" + + 6 for ".local" for a total of 43. */ + strncpy(addr, cand->mdns_addr, sizeof(addr) - 1); + } else { + if(r=nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr))) + ABORT(r); + } + if(r=nr_transport_addr_get_port(&cand->addr,&port)) + ABORT(r); + /* https://tools.ietf.org/html/rfc6544#section-4.5 */ + if (cand->base.protocol==IPPROTO_TCP && cand->tcp_type==TCP_TYPE_ACTIVE) + port=9; + snprintf(attr,maxlen,"candidate:%s %d %s %u %s %d typ %s", + cand->foundation, cand->component_id, cand->addr.protocol==IPPROTO_UDP?"UDP":"TCP",cand->priority, addr, port, + nr_ctype_name(cand->type)); + + len=strlen(attr); attr+=len; maxlen-=len; + + /* raddr, rport */ + raddr = (cand->stream->flags & + (NR_ICE_CTX_FLAGS_RELAY_ONLY | + NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES)) ? + &cand->addr : &cand->base; + + switch(cand->type){ + case HOST: + break; + case SERVER_REFLEXIVE: + if (obfuscate_srflx_addr) { + snprintf(attr,maxlen," raddr 0.0.0.0 rport 0"); + } else { + if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr))) + ABORT(r); + if(r=nr_transport_addr_get_port(raddr,&port)) + ABORT(r); + snprintf(attr,maxlen," raddr %s rport %d",addr,port); + } + break; + case PEER_REFLEXIVE: + if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr))) + ABORT(r); + if(r=nr_transport_addr_get_port(raddr,&port)) + ABORT(r); + snprintf(attr,maxlen," raddr %s rport %d",addr,port); + break; + case RELAYED: + // comes from XorMappedAddress via AllocateResponse + if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr))) + ABORT(r); + if(r=nr_transport_addr_get_port(raddr,&port)) + ABORT(r); + + snprintf(attr,maxlen," raddr %s rport %d",addr,port); + break; + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + if (cand->base.protocol==IPPROTO_TCP && cand->tcp_type){ + len=strlen(attr); + attr+=len; + maxlen-=len; + snprintf(attr,maxlen," tcptype %s",nr_tcp_type_name(cand->tcp_type)); + } + + _status=0; + abort: + return(_status); + } + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.h new file mode 100644 index 0000000000..40e6545e37 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.h @@ -0,0 +1,124 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_candidate_h +#define _ice_candidate_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +typedef enum {HOST=1, SERVER_REFLEXIVE, PEER_REFLEXIVE, RELAYED, CTYPE_MAX} nr_ice_candidate_type; + +struct nr_ice_candidate_ { + char *label; + char codeword[5]; + int state; + int trickled; +#define NR_ICE_CAND_STATE_CREATED 1 +#define NR_ICE_CAND_STATE_INITIALIZING 2 +#define NR_ICE_CAND_STATE_INITIALIZED 3 +#define NR_ICE_CAND_STATE_FAILED 4 +#define NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED 9 +#define NR_ICE_CAND_PEER_CANDIDATE_PAIRED 10 + struct nr_ice_ctx_ *ctx; + nr_ice_socket *isock; /* The socket to read from + (it contains all other candidates + on this socket) */ + nr_socket *osock; /* The socket to write to */ + nr_ice_media_stream *stream; /* The media stream this is associated with */ + nr_ice_component *component; /* The component this is associated with */ + nr_ice_candidate_type type; /* The type of the candidate (S 4.1.1) */ + nr_socket_tcp_type tcp_type; + UCHAR component_id; /* The component id (S 4.1.2.1) */ + nr_transport_addr addr; /* The advertised address; + JDR calls this the candidate */ + nr_transport_addr base; /* The base address (S 2.1)*/ + char *mdns_addr; /* MDNS address, if any */ + char *foundation; /* Foundation for the candidate (S 4) */ + UINT4 priority; /* The priority value (S 5.4 */ + nr_ice_stun_server *stun_server; + nr_transport_addr stun_server_addr; /* Resolved STUN server address */ + void *delay_timer; + void *resolver_handle; + + /* Holding data for STUN and TURN */ + union { + struct { + nr_stun_client_ctx *stun; + void *stun_handle; + /* If this is a srflx that is piggybacking on a relay candidate, this is + * a back pointer to that relay candidate. */ + nr_ice_candidate *relay_candidate; + } srvrflx; + struct { + nr_turn_client_ctx *turn; + nr_ice_turn_server *server; + nr_ice_candidate *srvflx_candidate; + nr_socket *turn_sock; + void *turn_handle; + } relayed; + } u; + + NR_async_cb done_cb; + void *cb_arg; + + NR_async_cb ready_cb; + void *ready_cb_arg; + void *ready_cb_timer; + + TAILQ_ENTRY(nr_ice_candidate_) entry_sock; + TAILQ_ENTRY(nr_ice_candidate_) entry_comp; +}; + +extern char *nr_ice_candidate_type_names[]; +extern char *nr_ice_candidate_tcp_type_names[]; + + +int nr_ice_candidate_create(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_socket_tcp_type tcp_type, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp); +int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg); +void nr_ice_candidate_compute_codeword(nr_ice_candidate *cand); +int nr_ice_candidate_process_stun(nr_ice_candidate *cand, UCHAR *msg, int len, nr_transport_addr *faddr); +int nr_ice_candidate_destroy(nr_ice_candidate **candp); +void nr_ice_candidate_stop_gathering(nr_ice_candidate *cand); +int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen, int obfuscate_srflx_addr); +int nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *attr,nr_ice_media_stream *stream,nr_ice_candidate **candp); +int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp); +int nr_ice_candidate_compute_priority(nr_ice_candidate *cand); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.c new file mode 100644 index 0000000000..6a7233963d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.c @@ -0,0 +1,689 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <assert.h> +#include <string.h> +#include <nr_api.h> +#include "async_timer.h" +#include "ice_ctx.h" +#include "ice_util.h" +#include "ice_codeword.h" +#include "stun.h" + +static char *nr_ice_cand_pair_states[]={"UNKNOWN","FROZEN","WAITING","IN_PROGRESS","FAILED","SUCCEEDED","CANCELLED"}; + +static void nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s, int how, void *cb_arg); +static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair, + nr_ice_candidate *lcand, nr_ice_candidate *rcand); + +static void nr_ice_candidate_pair_set_priority(nr_ice_cand_pair *pair) + { + /* Priority computation S 5.7.2 */ + UINT8 controlling_priority, controlled_priority; + if(pair->pctx->controlling) + { + controlling_priority=pair->local->priority; + controlled_priority=pair->remote->priority; + } + else{ + controlling_priority=pair->remote->priority; + controlled_priority=pair->local->priority; + } + pair->priority=(MIN(controlling_priority, controlled_priority))<<32 | + (MAX(controlling_priority, controlled_priority))<<1 | + (controlled_priority > controlling_priority?0:1); + } + +int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp) + { + nr_ice_cand_pair *pair=0; + int r,_status; + UINT4 RTO; + nr_ice_candidate tmpcand; + UINT8 t_priority; + + if(!(pair=RCALLOC(sizeof(nr_ice_cand_pair)))) + ABORT(R_NO_MEMORY); + + pair->pctx=pctx; + + nr_ice_candidate_pair_compute_codeword(pair,lcand,rcand); + + if(r=nr_concat_strings(&pair->as_string,pair->codeword,"|",lcand->addr.as_string,"|", + rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")", NULL)) + ABORT(r); + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_FROZEN); + pair->local=lcand; + pair->remote=rcand; + + nr_ice_candidate_pair_set_priority(pair); + + /* + TODO(bcampen@mozilla.com): Would be nice to log why this candidate was + created (initial pair generation, triggered check, and new trickle + candidate seem to be the possibilities here). + */ + r_log(LOG_ICE,LOG_INFO,"ICE(%s)/CAND-PAIR(%s): Pairing candidate %s (%x):%s (%x) priority=%llu (%llx)",pctx->ctx->label,pair->codeword,lcand->addr.as_string,lcand->priority,rcand->addr.as_string,rcand->priority,pair->priority,pair->priority); + + /* Foundation */ + if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|", + rcand->foundation,NULL)) + ABORT(r); + + /* Compute the RTO per S 16 */ + RTO = MAX(100, (pctx->ctx->Ta * pctx->waiting_pairs)); + + /* Make a bogus candidate to compute a theoretical peer reflexive + * priority per S 7.1.1.1 */ + memcpy(&tmpcand, lcand, sizeof(tmpcand)); + tmpcand.type = PEER_REFLEXIVE; + if (r=nr_ice_candidate_compute_priority(&tmpcand)) + ABORT(r); + t_priority = tmpcand.priority; + + /* Our sending context */ + if(r=nr_stun_client_ctx_create(pair->as_string, + lcand->osock, + &rcand->addr,RTO,&pair->stun_client)) + ABORT(r); + if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(rcand->stream->l2r_user))) + ABORT(R_NO_MEMORY); + if(r=r_data_copy(&pair->stun_client->params.ice_binding_request.password, + &rcand->stream->l2r_pass)) + ABORT(r); + /* TODO(ekr@rtfm.com): Do we need to frob this when we change role. Bug 890667 */ + pair->stun_client->params.ice_binding_request.control = pctx->controlling? + NR_ICE_CONTROLLING:NR_ICE_CONTROLLED; + pair->stun_client->params.ice_binding_request.priority=t_priority; + + pair->stun_client->params.ice_binding_request.tiebreaker=pctx->tiebreaker; + + *pairp=pair; + + _status=0; + abort: + if(_status){ + nr_ice_candidate_pair_destroy(&pair); + } + return(_status); + } + +int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp) + { + nr_ice_cand_pair *pair; + + if(!pairp || !*pairp) + return(0); + + pair=*pairp; + *pairp=0; + + // record stats back to the ice ctx on destruction + if (pair->stun_client) { + nr_accumulate_count(&(pair->local->ctx->stats.stun_retransmits), pair->stun_client->retransmit_ct); + } + + RFREE(pair->as_string); + RFREE(pair->foundation); + nr_ice_socket_deregister(pair->local->isock,pair->stun_client_handle); + if (pair->stun_client) { + RFREE(pair->stun_client->params.ice_binding_request.username); + RFREE(pair->stun_client->params.ice_binding_request.password.data); + nr_stun_client_ctx_destroy(&pair->stun_client); + } + + NR_async_timer_cancel(pair->stun_cb_timer); + NR_async_timer_cancel(pair->restart_role_change_cb_timer); + NR_async_timer_cancel(pair->restart_nominated_cb_timer); + + RFREE(pair); + return(0); + } + +int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + assert(pair->state==NR_ICE_PAIR_STATE_FROZEN); + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING); + + return(0); + } + +static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_ice_cand_pair *pair=cb_arg; + nr_ice_cand_pair *actual_pair=0; + nr_ice_candidate *cand=0; + nr_stun_message *sres; + nr_transport_addr *request_src; + nr_transport_addr *request_dst; + nr_transport_addr *response_src; + nr_transport_addr response_dst; + nr_stun_message_attribute *attr; + + pair->stun_cb_timer=0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s): STUN cb on pair addr = %s", + pair->pctx->label,pair->local->stream->label,pair->codeword,pair->as_string); + + /* This ordinarily shouldn't happen, but can if we're + doing the second check to confirm nomination. + Just bail out */ + if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED) + goto done; + + switch(pair->stun_client->state){ + case NR_STUN_CLIENT_STATE_FAILED: + sres=pair->stun_client->response; + if(sres && nr_stun_message_has_attribute(sres,NR_STUN_ATTR_ERROR_CODE,&attr)&&attr->u.error_code.number==487){ + + /* + * Flip the controlling bit; subsequent 487s for other pairs will be + * ignored, since we abandon their STUN transactions. + */ + nr_ice_peer_ctx_switch_controlling_role(pair->pctx); + + return; + } + /* Fall through */ + case NR_STUN_CLIENT_STATE_TIMED_OUT: + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + break; + case NR_STUN_CLIENT_STATE_DONE: + /* make sure the addresses match up S 7.1.2.2 */ + response_src=&pair->stun_client->peer_addr; + request_dst=&pair->remote->addr; + if (nr_transport_addr_cmp(response_src,request_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Peer address mismatch %s != %s",pair->pctx->label,pair->codeword,response_src->as_string,request_dst->as_string); + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + break; + } + request_src=&pair->stun_client->my_addr; + nr_socket_getaddr(pair->local->osock,&response_dst); + if (nr_transport_addr_cmp(request_src,&response_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Local address mismatch %s != %s",pair->pctx->label,pair->codeword,request_src->as_string,response_dst.as_string); + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + break; + } + + if(strlen(pair->stun_client->results.ice_binding_response.mapped_addr.as_string)==0){ + /* we're using the mapped_addr returned by the server to lookup our + * candidate, but if the server fails to do that we can't perform + * the lookup -- this may be a BUG because if we've gotten here + * then the transaction ID check succeeded, and perhaps we should + * just assume that it's the server we're talking to and that our + * peer is ok, but I'm not sure how that'll interact with the + * peer reflexive logic below */ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): server failed to return mapped address on pair %s", pair->pctx->label,pair->codeword,pair->as_string); + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + break; + } + else if(!nr_transport_addr_cmp(&pair->local->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED); + } + else if(pair->stun_client->state == NR_STUN_CLIENT_STATE_DONE) { + /* OK, this didn't correspond to a pair on the check list, but + it probably matches one of our candidates */ + + cand=TAILQ_FIRST(&pair->local->component->candidates); + while(cand){ + if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): found pre-existing local candidate of type %d for mapped address %s", pair->pctx->label,cand->type,cand->addr.as_string); + assert(cand->type != HOST); + break; + } + + cand=TAILQ_NEXT(cand,entry_comp); + } + + if(!cand) { + /* OK, nothing found, must be a new peer reflexive */ + if (pair->local->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) { + /* Any STUN response with a reflexive address in it is unwanted + when we'll send on relay only. Bail since cand is used below. */ + goto done; + } + if(r=nr_ice_candidate_create(pair->pctx->ctx, + pair->local->component,pair->local->isock,pair->local->osock, + PEER_REFLEXIVE,pair->local->tcp_type,0,pair->local->component->component_id,&cand)) + ABORT(r); + if(r=nr_transport_addr_copy(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr)) + ABORT(r); + cand->state=NR_ICE_CAND_STATE_INITIALIZED; + TAILQ_INSERT_TAIL(&pair->local->component->candidates,cand,entry_comp); + } else { + /* Check if we have a pair for this candidate already. */ + if(r=nr_ice_media_stream_find_pair(pair->remote->stream, cand, pair->remote, &actual_pair)) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no pair exists for %s and %s", pair->pctx->label,cand->addr.as_string, pair->remote->addr.as_string); + } + } + + if(!actual_pair) { + if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, &actual_pair)) + ABORT(r); + + if(r=nr_ice_component_insert_pair(actual_pair->remote->component,actual_pair)) + ABORT(r); + + /* If the original pair was nominated, make us nominated too. */ + if(pair->peer_nominated) + actual_pair->peer_nominated=1; + + /* Now mark the orig pair failed */ + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + } + + assert(actual_pair); + nr_ice_candidate_pair_set_state(actual_pair->pctx,actual_pair,NR_ICE_PAIR_STATE_SUCCEEDED); + pair=actual_pair; + + } + + /* Should we set nominated? */ + if(pair->pctx->controlling){ + if(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION) + pair->nominated=1; + } + else{ + if(pair->peer_nominated) + pair->nominated=1; + } + + + /* increment the number of valid pairs in the component */ + /* We don't bother to maintain a separate valid list */ + pair->remote->component->valid_pairs++; + + /* S 7.1.2.2: unfreeze other pairs with the same foundation*/ + if(r=nr_ice_media_stream_unfreeze_pairs_foundation(pair->remote->stream,pair->foundation)) + ABORT(r); + + /* Deal with this pair being nominated */ + if(pair->nominated){ + if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) + ABORT(r); + } + + break; + default: + ABORT(R_INTERNAL); + } + + /* If we're controlling but in regular mode, ask the handler + if he wants to nominate something and stop... */ + if(pair->pctx->controlling && !(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)){ + + if(r=nr_ice_component_select_pair(pair->pctx,pair->remote->component)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + } + + done: + _status=0; + abort: + if (_status) { + // cb doesn't return anything, but we should probably log that we aborted + // This also quiets the unused variable warnings. + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s): STUN cb pair addr = %s abort with status: %d", + pair->pctx->label,pair->local->stream->label,pair->codeword,pair->as_string, _status); + } + return; + } + +static void nr_ice_candidate_pair_restart(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + int r,_status; + UINT4 mode; + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_IN_PROGRESS); + + /* Start STUN */ + if(pair->pctx->controlling && (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)) + mode=NR_ICE_CLIENT_MODE_USE_CANDIDATE; + else + mode=NR_ICE_CLIENT_MODE_BINDING_REQUEST; + + nr_stun_client_reset(pair->stun_client); + + if(r=nr_stun_client_start(pair->stun_client,mode,nr_ice_candidate_pair_stun_cb,pair)) + ABORT(r); + + if ((r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))) { + /* ignore if this fails (which it shouldn't) because it's only an + * optimization and the cleanup routines are not going to do the right + * thing if this fails */ + assert(0); + } + + _status=0; + abort: + if(_status){ + /* Don't fire the CB, but schedule it to fire ASAP */ + assert(!pair->stun_cb_timer); + NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_stun_cb,pair, &pair->stun_cb_timer); + _status=0; + } + } + +int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + int r,_status; + + /* Register the stun ctx for when responses come in*/ + if(r=nr_ice_socket_register_stun_client(pair->local->isock,pair->stun_client,&pair->stun_client_handle)) + ABORT(r); + + nr_ice_candidate_pair_restart(pctx, pair); + + _status=0; + abort: + return(_status); + } + +static int nr_ice_candidate_copy_for_triggered_check(nr_ice_cand_pair *pair) + { + int r,_status; + nr_ice_cand_pair *copy; + + if(r=nr_ice_candidate_pair_create(pair->pctx, pair->local, pair->remote, ©)) + ABORT(r); + + /* Preserve nomination status */ + copy->peer_nominated= pair->peer_nominated; + copy->nominated = pair->nominated; + + r_log(LOG_ICE,LOG_INFO,"CAND-PAIR(%s): Adding pair to check list and trigger check queue: %s",pair->codeword,pair->as_string); + nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,copy); + nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,copy); + + copy->triggered = 1; + nr_ice_candidate_pair_set_state(copy->pctx,copy,NR_ICE_PAIR_STATE_WAITING); + + _status=0; + abort: + return(_status); +} + +int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + int r,_status; + + if(pair->state==NR_ICE_PAIR_STATE_CANCELLED) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Ignoring matching but canceled pair",pctx->label,pair->codeword); + return(0); + } else if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): No new trigger check for succeeded pair",pctx->label,pair->codeword); + return(0); + } else if (pair->local->stream->obsolete) { + r_log(LOG_ICE, LOG_DEBUG, + "ICE-PEER(%s)/CAND_PAIR(%s): No new trigger check for pair with " + "obsolete stream", + pctx->label, pair->codeword); + return (0); + } + + /* Do not run this logic more than once on a given pair */ + if(!pair->triggered){ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): triggered check on %s",pctx->label,pair->codeword,pair->as_string); + + pair->triggered=1; + + switch(pair->state){ + case NR_ICE_PAIR_STATE_FAILED: + /* OK, there was a pair, it's just invalid: According to Section + * 7.2.1.4, we need to resurrect it */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on failed pair, resurrecting: %s",pctx->label,pair->codeword,pair->as_string); + /* fall through */ + case NR_ICE_PAIR_STATE_FROZEN: + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING); + /* fall through even further */ + case NR_ICE_PAIR_STATE_WAITING: + /* Append it additionally to the trigger check queue */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): Inserting pair to trigger check queue: %s",pctx->label,pair->codeword,pair->as_string); + nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,pair); + break; + case NR_ICE_PAIR_STATE_IN_PROGRESS: + /* Instead of trying to maintain two stun contexts on the same pair, + * and handling heterogenous responses and error conditions, we instead + * create a second pair that is identical except that it has the + * |triggered| bit set. We also cancel the original pair, but it can + * still succeed on its own in the special waiting state. */ + if(r=nr_ice_candidate_copy_for_triggered_check(pair)) + ABORT(r); + nr_ice_candidate_pair_cancel(pair->pctx,pair,1); + break; + default: + /* all states are handled - a new/unknown state should not + * automatically enter the start_checks() below */ + assert(0); + break; + } + + /* Ensure that the timers are running to start checks on the topmost entry + * of the triggered check queue. */ + if(r=nr_ice_media_stream_start_checks(pair->pctx,pair->remote->stream)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +void nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state) + { + if(pair->state != NR_ICE_PAIR_STATE_FAILED){ + /* If it's already running we need to terminate the stun */ + if(pair->state==NR_ICE_PAIR_STATE_IN_PROGRESS){ + if(move_to_wait_state) { + nr_stun_client_wait(pair->stun_client); + } else { + nr_stun_client_cancel(pair->stun_client); + } + } + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_CANCELLED); + } + } + +int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair) + { + int r,_status; + + if(!pair){ + r_log(LOG_ICE,LOG_ERR,"ICE-PAIR: No pair chosen"); + ABORT(R_BAD_ARGS); + } + + if(pair->state!=NR_ICE_PAIR_STATE_SUCCEEDED){ + r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): tried to install non-succeeded pair, ignoring: %s",pair->pctx->label,pair->codeword,pair->as_string); + } + else{ + /* Ok, they chose one */ + /* 1. Send a new request with nominated. Do it as a scheduled + event to avoid reentrancy issues. Only do this if it hasn't + happened already (though this shouldn't happen.) + */ + if(!pair->restart_nominated_cb_timer) + NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_nominated_cb,pair,&pair->restart_nominated_cb_timer); + + /* 2. Tell ourselves this pair is ready */ + if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +void nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state) + { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): setting pair to state %s: %s", + pctx->label,pair->codeword,nr_ice_cand_pair_states[state],pair->as_string); + + /* NOTE: This function used to reference pctx->state instead of + pair->state and the assignment to pair->state was at the top + of this function. Because pctx->state was never changed, this seems to have + been a typo. The natural logic is "if the state changed + decrement the counter" so this implies we should be checking + the pair state rather than the pctx->state. + + This didn't cause big problems because waiting_pairs was only + used for pacing, so the pacing just was kind of broken. + + This note is here as a reminder until we do more testing + and make sure that in fact this was a typo. + */ + if(pair->state!=NR_ICE_PAIR_STATE_WAITING){ + if(state==NR_ICE_PAIR_STATE_WAITING) + pctx->waiting_pairs++; + } + else{ + if(state!=NR_ICE_PAIR_STATE_WAITING) + pctx->waiting_pairs--; + + assert(pctx->waiting_pairs>=0); + } + pair->state=state; + + + if(pair->state==NR_ICE_PAIR_STATE_FAILED || + pair->state==NR_ICE_PAIR_STATE_CANCELLED){ + nr_ice_component_failed_pair(pair->remote->component, pair); + } + } + +void nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, int log_level) + { + r_log(LOG_ICE,log_level,"CAND-PAIR(%s): pair %s: state=%s, priority=0x%llx\n",pair->codeword,pair->as_string,nr_ice_cand_pair_states[pair->state],pair->priority); + } + + +int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair) + { + if(pair->triggered_check_queue_entry.tqe_next || + pair->triggered_check_queue_entry.tqe_prev) + return(0); + + TAILQ_INSERT_TAIL(head,pair,triggered_check_queue_entry); + + return(0); + } + +void nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair) + { + nr_ice_cand_pair *c1; + + c1=TAILQ_FIRST(head); + while(c1){ + if(c1->priority < pair->priority){ + TAILQ_INSERT_BEFORE(c1,pair,check_queue_entry); + break; + } + + c1=TAILQ_NEXT(c1,check_queue_entry); + } + if(!c1) TAILQ_INSERT_TAIL(head,pair,check_queue_entry); + } + +void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_cand_pair *pair=cb_arg; + int r,_status; + + pair->restart_nominated_cb_timer=0; + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s)/COMP(%d): Restarting pair as nominated: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->as_string); + + nr_stun_client_reset(pair->stun_client); + + if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair)) + ABORT(r); + + if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request)) + ABORT(r); + + _status=0; + abort: + if (_status) { + // cb doesn't return anything, but we should probably log that we aborted + // This also quiets the unused variable warnings. + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s)/COMP(%d): STUN nominated cb pair as nominated: %s abort with status: %d", + pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->as_string, _status); + } + return; + } + +static void nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_cand_pair *pair=cb_arg; + + pair->restart_role_change_cb_timer=0; + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s):COMP(%d): Restarting pair as %s: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->pctx->controlling ? "CONTROLLING" : "CONTROLLED",pair->as_string); + + nr_ice_candidate_pair_restart(pair->pctx, pair); + } + +void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair) + { + pair->stun_client->params.ice_binding_request.control = pair->pctx->controlling ? NR_ICE_CONTROLLING : NR_ICE_CONTROLLED; + nr_ice_candidate_pair_set_priority(pair); + + if(pair->state == NR_ICE_PAIR_STATE_IN_PROGRESS) { + /* We could try only restarting in-progress pairs when they receive their + * 487, but this ends up being simpler, because any extra 487 are dropped. + */ + if(!pair->restart_role_change_cb_timer) + NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_role_change_cb,pair,&pair->restart_role_change_cb_timer); + } + } + +static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair, + nr_ice_candidate *lcand, nr_ice_candidate *rcand) + { + char as_string[2048]; + + snprintf(as_string, + sizeof(as_string), + "%s|%s(%s|%s)", + lcand->addr.as_string, + rcand->addr.as_string, + lcand->label, + rcand->label); + + nr_ice_compute_codeword(as_string,strlen(as_string),pair->codeword); + } + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.h new file mode 100644 index 0000000000..49da1f7802 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.h @@ -0,0 +1,101 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_candidate_pair_h +#define _ice_candidate_pair_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + + +struct nr_ice_cand_pair_ { + nr_ice_peer_ctx *pctx; + char codeword[5]; + char *as_string; + int state; /* The current state (S 5.7.3) */ +#define NR_ICE_PAIR_STATE_FROZEN 1 +#define NR_ICE_PAIR_STATE_WAITING 2 +#define NR_ICE_PAIR_STATE_IN_PROGRESS 3 +#define NR_ICE_PAIR_STATE_FAILED 4 +#define NR_ICE_PAIR_STATE_SUCCEEDED 5 +#define NR_ICE_PAIR_STATE_CANCELLED 6 + + UCHAR peer_nominated; /* The peer sent USE-CANDIDATE + on this check */ + UCHAR nominated; /* Is this nominated or not */ + + UCHAR triggered; /* Ignore further trigger check requests */ + + UINT8 priority; /* The priority for this pair */ + nr_ice_candidate *local; /* The local candidate */ + nr_ice_candidate *remote; /* The remote candidate */ + char *foundation; /* The combined foundations */ + + // for RTCIceCandidatePairStats + UINT8 bytes_sent; + UINT8 bytes_recvd; + struct timeval last_sent; + struct timeval last_recvd; + + nr_stun_client_ctx *stun_client; /* STUN context when acting as a client */ + void *stun_client_handle; + + void *stun_cb_timer; + void *restart_role_change_cb_timer; + void *restart_nominated_cb_timer; + + TAILQ_ENTRY(nr_ice_cand_pair_) check_queue_entry; /* the check list */ + TAILQ_ENTRY(nr_ice_cand_pair_) triggered_check_queue_entry; /* the trigger check queue */ +}; + +int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp); +int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); +int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); +void nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state); +void nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, int log_level); +void nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state); +int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair); +int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); +void nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair); +int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair); +void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg); +int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp); +void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_codeword.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_codeword.h new file mode 100644 index 0000000000..76fce0b69b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_codeword.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_codeword_h +#define _ice_codeword_h + +void nr_ice_compute_codeword(char *buf, int len,char *codeword); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.c new file mode 100644 index 0000000000..584a9466bc --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.c @@ -0,0 +1,1786 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <string.h> +#include <assert.h> +#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_wrapper.h" +#include "nr_socket_buffered_stun.h" +#include "nr_socket_multi_tcp.h" +#include "ice_reg.h" +#include "nr_crypto.h" +#include "r_time.h" + +static void nr_ice_component_refresh_consent_cb(NR_SOCKET s, int how, void *cb_arg); +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); +int nr_ice_component_can_candidate_addr_pair(nr_transport_addr *local, nr_transport_addr *remote); +int nr_ice_component_can_candidate_tcptype_pair(nr_socket_tcp_type left, nr_socket_tcp_type right); +void nr_ice_component_consent_calc_consent_timer(nr_ice_component *comp); +void nr_ice_component_consent_schedule_consent_timer(nr_ice_component *comp); +int nr_ice_component_refresh_consent(nr_stun_client_ctx *ctx, NR_async_cb finished_cb, void *cb_arg); +int nr_ice_component_setup_consent(nr_ice_component *comp); +int nr_ice_pre_answer_enqueue(nr_ice_component *comp, nr_socket *sock, nr_stun_server_request *req, int *dont_free); + +/* This function takes ownership of the contents of req (but not req itself) */ +static int nr_ice_pre_answer_request_create(nr_transport_addr *dst, nr_stun_server_request *req, nr_ice_pre_answer_request **parp) + { + int r, _status; + nr_ice_pre_answer_request *par = 0; + nr_stun_message_attribute *attr; + + if (!(par = RCALLOC(sizeof(nr_ice_pre_answer_request)))) + ABORT(R_NO_MEMORY); + + par->req = *req; /* Struct assignment */ + memset(req, 0, sizeof(*req)); /* Zero contents to avoid confusion */ + + if (r=nr_transport_addr_copy(&par->local_addr, dst)) + ABORT(r); + if (!nr_stun_message_has_attribute(par->req.request, NR_STUN_ATTR_USERNAME, &attr)) + ABORT(R_INTERNAL); + if (!(par->username = r_strdup(attr->u.username))) + ABORT(R_NO_MEMORY); + + *parp=par; + _status=0; + abort: + if (_status) { + /* Erase the request so we don't free it */ + memset(&par->req, 0, sizeof(nr_stun_server_request)); + nr_ice_pre_answer_request_destroy(&par); + } + + return(_status); + } + +static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp) + { + nr_ice_pre_answer_request *par; + + if (!parp || !*parp) + return(0); + + par = *parp; + *parp = 0; + + nr_stun_message_destroy(&par->req.request); + nr_stun_message_destroy(&par->req.response); + + RFREE(par->username); + RFREE(par); + + return(0); + } + +int nr_ice_component_create(nr_ice_media_stream *stream, int component_id, nr_ice_component **componentp) + { + int _status; + nr_ice_component *comp=0; + + if(!(comp=RCALLOC(sizeof(nr_ice_component)))) + ABORT(R_NO_MEMORY); + + comp->state=NR_ICE_COMPONENT_UNPAIRED; + comp->component_id=component_id; + comp->stream=stream; + comp->ctx=stream->ctx; + + STAILQ_INIT(&comp->sockets); + TAILQ_INIT(&comp->candidates); + STAILQ_INIT(&comp->pre_answer_reqs); + + STAILQ_INSERT_TAIL(&stream->components,comp,entry); + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_destroy(nr_ice_component **componentp) + { + nr_ice_component *component; + nr_ice_socket *s1,*s2; + nr_ice_candidate *c1,*c2; + nr_ice_pre_answer_request *r1,*r2; + + if(!componentp || !*componentp) + return(0); + + component=*componentp; + *componentp=0; + + nr_ice_component_consent_destroy(component); + + /* Detach ourselves from the sockets */ + if (component->local_component){ + nr_ice_socket *isock=STAILQ_FIRST(&component->local_component->sockets); + while(isock){ + nr_stun_server_remove_client(isock->stun_server, component); + isock=STAILQ_NEXT(isock, entry); + } + } + + /* candidates MUST be destroyed before the sockets so that + they can deregister */ + TAILQ_FOREACH_SAFE(c1, &component->candidates, entry_comp, c2){ + TAILQ_REMOVE(&component->candidates,c1,entry_comp); + nr_ice_candidate_destroy(&c1); + } + + STAILQ_FOREACH_SAFE(s1, &component->sockets, entry, s2){ + STAILQ_REMOVE(&component->sockets,s1,nr_ice_socket_,entry); + nr_ice_socket_destroy(&s1); + } + + STAILQ_FOREACH_SAFE(r1, &component->pre_answer_reqs, entry, r2){ + STAILQ_REMOVE(&component->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry); + nr_ice_pre_answer_request_destroy(&r1); + } + + RFREE(component); + return(0); + } + +static int nr_ice_component_create_stun_server_ctx(nr_ice_component *component, nr_ice_socket *isock, nr_transport_addr *addr, char *lufrag, Data *pwd) + { + char label[256]; + int r,_status; + + /* 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,&isock->stun_server)) + 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); + + /* Do this last; if this function fails, we should not be taking a reference to isock */ + if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle)) + ABORT(r); + + _status = 0; + abort: + return(_status); + } + +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) + { + nr_socket *sock; + nr_ice_socket *isock=0; + nr_ice_candidate *cand=0; + int i; + int j; + int r,_status; + + if(ctx->flags & NR_ICE_CTX_FLAGS_ONLY_PROXY) { + /* No UDP support if we must use a proxy */ + return 0; + } + + /* 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-STREAM(%s): host address %s",component->stream->label,addrs[i].addr.as_string); + if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addrs[i].addr,&sock))){ + r_log(LOG_ICE,LOG_WARNING,"ICE-STREAM(%s): couldn't create socket for address %s",component->stream->label,addrs[i].addr.as_string); + continue; + } + + if(r=nr_ice_socket_create(ctx,component,sock,NR_ICE_SOCKET_TYPE_DGRAM,&isock)) + ABORT(r); + + /* Create a STUN server context for this socket */ + if ((r=nr_ice_component_create_stun_server_ctx(component,isock,&addrs[i].addr,lufrag,pwd))) + ABORT(r); + + /* Make sure we don't leak this. Failures might result in it being + * unused, but we hand off references to this in enough places below + * that unwinding it all becomes impractical. */ + STAILQ_INSERT_TAIL(&component->sockets,isock,entry); + + if (!(component->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) { + /* Create one host candidate */ + if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0,0, + component->component_id,&cand)) + ABORT(r); + + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + + /* And a srvrflx candidate for each STUN server */ + for(j=0;j<component->stream->stun_server_ct;j++){ + r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): Checking STUN server %s %s", component->stream->label, component->stream->stun_servers[j].addr.fqdn, component->stream->stun_servers[j].addr.as_string); + /* Skip non-UDP */ + if (component->stream->stun_servers[j].addr.protocol != IPPROTO_UDP) continue; + + if (nr_transport_addr_check_compatibility( + &addrs[i].addr, &component->stream->stun_servers[j].addr)) { + r_log(LOG_ICE,LOG_INFO,"ICE-STREAM(%s): Skipping STUN server because of address type mis-match",component->stream->label); + continue; + } + + /* Ensure id is set (nr_ice_ctx_set_stun_servers does not) */ + component->stream->stun_servers[j].id = j; + if(r=nr_ice_candidate_create(ctx,component, + isock,sock,SERVER_REFLEXIVE,0, + &component->stream->stun_servers[j],component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + } + else{ + r_log(LOG_ICE,LOG_WARNING,"ICE-STREAM(%s): relay only option results in no host candidate for %s",component->stream->label,addrs[i].addr.as_string); + } + +#ifdef USE_TURN + if ((component->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) && + (component->stream->turn_server_ct == 0)) { + r_log(LOG_ICE,LOG_ERR,"ICE-STREAM(%s): relay only option is set without any TURN server configured",component->stream->label); + } + /* And both a srvrflx and relayed candidate for each TURN server (unless + we're in relay-only mode, in which case just the relayed one) */ + for(j=0;j<component->stream->turn_server_ct;j++){ + nr_socket *turn_sock; + nr_ice_candidate *srvflx_cand=0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): Checking TURN server %s %s", component->stream->label, component->stream->turn_servers[j].turn_server.addr.fqdn, component->stream->turn_servers[j].turn_server.addr.as_string); + + /* Skip non-UDP */ + if (component->stream->turn_servers[j].turn_server.addr.protocol != IPPROTO_UDP) + continue; + + if (nr_transport_addr_check_compatibility( + &addrs[i].addr, &component->stream->turn_servers[j].turn_server.addr)) { + r_log(LOG_ICE,LOG_INFO,"ICE-STREAM(%s): Skipping TURN server because of address type mis-match",component->stream->label); + continue; + } + + if (!(component->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) { + /* Ensure id is set with a unique value */ + component->stream->turn_servers[j].turn_server.id = j + component->stream->stun_server_ct; + /* srvrflx */ + if(r=nr_ice_candidate_create(ctx,component, + isock,sock,SERVER_REFLEXIVE,0, + &component->stream->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_gather_finished_cb; + cand->cb_arg=cand; + + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + srvflx_cand=cand; + cand=0; + } + /* relayed*/ + if(r=nr_socket_turn_create(&turn_sock)) + ABORT(r); + if(r=nr_ice_candidate_create(ctx,component, + isock,turn_sock,RELAYED,0, + &component->stream->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + if (srvflx_cand) { + cand->u.relayed.srvflx_candidate=srvflx_cand; + srvflx_cand->u.srvrflx.relay_candidate=cand; + } + cand->u.relayed.server=&component->stream->turn_servers[j]; + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + + cand=0; + } +#endif /* USE_TURN */ + } + + _status = 0; + abort: + return(_status); + } + +static int nr_ice_component_get_port_from_ephemeral_range(uint16_t *port) + { + int _status, r; + void *buf = port; + if(r=nr_crypto_random_bytes(buf, 2)) + ABORT(r); + *port|=49152; /* make it fit into IANA ephemeral port range >= 49152 */ + _status=0; +abort: + return(_status); + } + +static int nr_ice_component_create_tcp_host_candidate(struct nr_ice_ctx_ *ctx, + nr_ice_component *component, nr_transport_addr *interface_addr, nr_socket_tcp_type tcp_type, + int backlog, int so_sock_ct, char *lufrag, Data *pwd, nr_ice_socket **isock) + { + int r,_status; + nr_ice_candidate *cand=0; + int tries=3; + nr_ice_socket *isock_tmp=0; + nr_socket *nrsock=0; + nr_transport_addr addr; + uint16_t local_port; + + if ((r=nr_transport_addr_copy(&addr,interface_addr))) + ABORT(r); + addr.protocol=IPPROTO_TCP; + + do{ + if (!tries--) + ABORT(r); + + if((r=nr_ice_component_get_port_from_ephemeral_range(&local_port))) + ABORT(r); + + if ((r=nr_transport_addr_set_port(&addr, local_port))) + ABORT(r); + + if((r=nr_transport_addr_fmt_addr_string(&addr))) + ABORT(r); + + /* It would be better to stop trying if there is error other than + port already used, but it'd require significant work to support this. */ + r=nr_socket_multi_tcp_create(ctx,component,&addr,tcp_type,so_sock_ct,NR_STUN_MAX_MESSAGE_SIZE,&nrsock); + + } while(r); + + if((tcp_type == TCP_TYPE_PASSIVE) && (r=nr_socket_listen(nrsock,backlog))) + ABORT(r); + + if((r=nr_ice_socket_create(ctx,component,nrsock,NR_ICE_SOCKET_TYPE_STREAM_TCP,&isock_tmp))) + ABORT(r); + + /* nr_ice_socket took ownership of nrsock */ + nrsock=NULL; + + /* Create a STUN server context for this socket */ + if ((r=nr_ice_component_create_stun_server_ctx(component,isock_tmp,&addr,lufrag,pwd))) + ABORT(r); + + if((r=nr_ice_candidate_create(ctx,component,isock_tmp,isock_tmp->sock,HOST,tcp_type,0, + component->component_id,&cand))) + ABORT(r); + + if (isock) + *isock=isock_tmp; + + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + + STAILQ_INSERT_TAIL(&component->sockets,isock_tmp,entry); + + _status=0; +abort: + if (_status) { + nr_ice_socket_destroy(&isock_tmp); + nr_socket_destroy(&nrsock); + } + 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_candidate *cand=0; + int i; + int j; + int r,_status; + int so_sock_ct=0; + int backlog=10; + char ice_tcp_disabled=1; + + r_log(LOG_ICE,LOG_DEBUG,"nr_ice_component_initialize_tcp"); + + if(r=NR_reg_get_int4(NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT,&so_sock_ct)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + + if(r=NR_reg_get_int4(NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG,&backlog)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + + if ((r=NR_reg_get_char(NR_ICE_REG_ICE_TCP_DISABLE, &ice_tcp_disabled))) { + if (r != R_NOT_FOUND) + ABORT(r); + } + if ((component->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) || + (component->stream->flags & NR_ICE_CTX_FLAGS_ONLY_PROXY)) { + r_log(LOG_ICE,LOG_WARNING,"ICE-STREAM(%s): relay/proxy only option results in ICE TCP being disabled",component->stream->label); + ice_tcp_disabled = 1; + } + + for(i=0;i<addr_ct;i++){ + char suppress; + nr_ice_socket *isock_psv=0; + nr_ice_socket *isock_so=0; + + 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; + } + + if (!ice_tcp_disabled) { + /* passive host candidate */ + if ((r=nr_ice_component_create_tcp_host_candidate(ctx, component, &addrs[i].addr, + TCP_TYPE_PASSIVE, backlog, 0, lufrag, pwd, &isock_psv))) { + r_log(LOG_ICE,LOG_WARNING,"ICE-STREAM(%s): failed to create passive TCP host candidate: %d",component->stream->label,r); + } + + /* active host candidate */ + if ((r=nr_ice_component_create_tcp_host_candidate(ctx, component, &addrs[i].addr, + TCP_TYPE_ACTIVE, 0, 0, lufrag, pwd, NULL))) { + r_log(LOG_ICE,LOG_WARNING,"ICE-STREAM(%s): failed to create active TCP host candidate: %d",component->stream->label,r); + } + + /* simultaneous-open host candidate */ + if (so_sock_ct) { + if ((r=nr_ice_component_create_tcp_host_candidate(ctx, component, &addrs[i].addr, + TCP_TYPE_SO, 0, so_sock_ct, lufrag, pwd, &isock_so))) { + r_log(LOG_ICE,LOG_WARNING,"ICE-STREAM(%s): failed to create simultanous open TCP host candidate: %d",component->stream->label,r); + } + } + + /* And srvrflx candidates for each STUN server */ + for(j=0;j<component->stream->stun_server_ct;j++){ + if (component->stream->stun_servers[j].addr.protocol != IPPROTO_TCP) continue; + + if (isock_psv) { + if(r=nr_ice_candidate_create(ctx,component, + isock_psv,isock_psv->sock,SERVER_REFLEXIVE,TCP_TYPE_PASSIVE, + &component->stream->stun_servers[j],component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + + if (isock_so) { + if(r=nr_ice_candidate_create(ctx,component, + isock_so,isock_so->sock,SERVER_REFLEXIVE,TCP_TYPE_SO, + &component->stream->stun_servers[j],component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + } + } + +#ifdef USE_TURN + /* Create a new relayed candidate for each addr/TURN server pair */ + for(j=0;j<component->stream->turn_server_ct;j++){ + nr_transport_addr addr; + nr_socket *local_sock; + nr_socket *buffered_sock; + nr_socket *turn_sock; + nr_ice_socket *turn_isock; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): Checking TURN server %s %s", component->stream->label, component->stream->turn_servers[j].turn_server.addr.fqdn, component->stream->turn_servers[j].turn_server.addr.as_string); + + /* Skip non-TCP */ + if (component->stream->turn_servers[j].turn_server.addr.protocol != IPPROTO_TCP) + continue; + + /* Create relay candidate */ + if ((r=nr_transport_addr_copy(&addr, &addrs[i].addr))) + ABORT(r); + addr.protocol = IPPROTO_TCP; + + if (nr_transport_addr_check_compatibility( + &addr, &component->stream->turn_servers[j].turn_server.addr)) { + r_log(LOG_ICE,LOG_INFO,"ICE-STREAM(%s): Skipping TURN server because of address type mis-match",component->stream->label); + continue; + } + + if (!ice_tcp_disabled) { + /* Use TURN server to get srflx candidates */ + if (isock_psv) { + if(r=nr_ice_candidate_create(ctx,component, + isock_psv,isock_psv->sock,SERVER_REFLEXIVE,TCP_TYPE_PASSIVE, + &component->stream->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + + if (isock_so) { + if(r=nr_ice_candidate_create(ctx,component, + isock_so,isock_so->sock,SERVER_REFLEXIVE,TCP_TYPE_SO, + &component->stream->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + } + + if (component->stream->turn_servers[j].turn_server.addr.fqdn[0] != 0) { + /* If we're going to use TLS, make sure that's recorded */ + addr.tls = component->stream->turn_servers[j].turn_server.addr.tls; + } + + if ((r=nr_transport_addr_fmt_addr_string(&addr))) + ABORT(r); + + r_log(LOG_ICE, LOG_DEBUG, + "ICE-STREAM(%s): Creating socket for address %s (turn server %s)", + component->stream->label, addr.as_string, + component->stream->turn_servers[j].turn_server.addr.as_string); + + /* Create a local socket */ + if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addr,&local_sock))){ + r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): couldn't create socket for address %s",component->stream->label,addr.as_string); + continue; + } + + r_log(LOG_ICE,LOG_DEBUG,"nr_ice_component_initialize_tcp creating TURN TCP wrappers"); + + /* The TCP buffered socket */ + if((r=nr_socket_buffered_stun_create(local_sock, NR_STUN_MAX_MESSAGE_SIZE, TURN_TCP_FRAMING, &buffered_sock))) + ABORT(r); + + /* The TURN socket */ + if(r=nr_socket_turn_create(&turn_sock)) + ABORT(r); + + /* Create an ICE socket */ + if((r=nr_ice_socket_create(ctx, component, buffered_sock, NR_ICE_SOCKET_TYPE_STREAM_TURN, &turn_isock))) + ABORT(r); + + + /* Create a STUN server context for this socket */ + if ((r=nr_ice_component_create_stun_server_ctx(component,turn_isock,&addr,lufrag,pwd))) + ABORT(r); + + /* Make sure we don't leak this. Failures might result in it being + * unused, but we hand off references to this in enough places below + * that unwinding it all becomes impractical. */ + STAILQ_INSERT_TAIL(&component->sockets,turn_isock,entry); + + /* Attach ourselves to it */ + if(r=nr_ice_candidate_create(ctx,component, + turn_isock,turn_sock,RELAYED,TCP_TYPE_NONE, + &component->stream->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + cand->u.relayed.srvflx_candidate=NULL; + cand->u.relayed.server=&component->stream->turn_servers[j]; + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } +#endif /* USE_TURN */ + } + + _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; + + if (component->candidate_ct) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): component with id %d already has candidates, probably restarting gathering because of a new stream",ctx->label,component->component_id); + return(0); + } + + 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; + assert(lufrag); + if (!lufrag) + ABORT(R_INTERNAL); + lpwd=component->stream->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)) + r_log(LOG_ICE,LOG_INFO,"ICE(%s): failed to create UDP candidates with error %d",ctx->label,r); + /* And the TCP candidates */ + if (r=nr_ice_component_initialize_tcp(ctx, component, addrs, addr_ct, lufrag, &pwd)) + r_log(LOG_ICE,LOG_INFO,"ICE(%s): failed to create TCP candidates with error %d",ctx->label,r); + + /* count the candidates that will be initialized */ + cand=TAILQ_FIRST(&component->candidates); + if(!cand){ + r_log(LOG_ICE,LOG_ERR,"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); + } + + /* Now initialize all the candidates */ + cand=TAILQ_FIRST(&component->candidates); + while(cand){ + if(cand->state!=NR_ICE_CAND_STATE_INITIALIZING){ + nr_ice_candidate_initialize(cand,nr_ice_gather_finished_cb,cand); + } + cand=TAILQ_NEXT(cand,entry_comp); + } + _status=0; + abort: + return(_status); + } + +void nr_ice_component_stop_gathering(nr_ice_component *component) + { + nr_ice_candidate *c1,*c2; + TAILQ_FOREACH_SAFE(c1, &component->candidates, entry_comp, c2){ + nr_ice_candidate_stop_gathering(c1); + } + } + +int nr_ice_component_is_done_gathering(nr_ice_component *comp) + { + nr_ice_candidate *cand=TAILQ_FIRST(&comp->candidates); + while(cand){ + if(cand->state != NR_ICE_CAND_STATE_INITIALIZED && + cand->state != NR_ICE_CAND_STATE_FAILED){ + return 0; + } + cand=TAILQ_NEXT(cand,entry_comp); + } + return 1; + } + + +static int nr_ice_any_peer_paired(nr_ice_candidate* cand) { + nr_ice_peer_ctx* pctx=STAILQ_FIRST(&cand->ctx->peers); + while(pctx && pctx->state == NR_ICE_PEER_STATE_UNPAIRED){ + /* Is it worth actually looking through the check lists? Probably not. */ + pctx=STAILQ_NEXT(pctx,entry); + } + return pctx != NULL; +} + +/* + 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 + host > srflx > relay + */ +int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned) + { + nr_ice_candidate *c2, *tmp = NULL; + + *was_pruned = 0; + c2 = TAILQ_FIRST(&comp->candidates); + while(c2){ + if((c1 != c2) && + (c2->state == NR_ICE_CAND_STATE_INITIALIZED) && + !nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) && + !nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ + + if((c1->type == c2->type) || + (!(ctx->flags & NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES) && + !(ctx->flags & NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES) && + ((c1->type==HOST && c2->type == SERVER_REFLEXIVE) || + (c2->type==HOST && c1->type == SERVER_REFLEXIVE)))){ + + /* + These are redundant. Remove the lower pri one, or if pairing has + already occurred, remove the newest one. + + Since this algorithmis run whenever a new candidate + is initialized, there should at most one duplicate. + */ + if ((c1->priority <= c2->priority) || nr_ice_any_peer_paired(c2)) { + tmp = c1; + *was_pruned = 1; + } + else { + tmp = c2; + } + break; + } + } + + c2=TAILQ_NEXT(c2,entry_comp); + } + + if (tmp) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): Removing redundant candidate", + ctx->label,tmp->label); + + TAILQ_REMOVE(&comp->candidates,tmp,entry_comp); + comp->candidate_ct--; + TAILQ_REMOVE(&tmp->isock->candidates,tmp,entry_sock); + + nr_ice_candidate_destroy(&tmp); + } + + return 0; + } + +static int nr_ice_component_pair_matches_check(nr_ice_component *comp, nr_ice_cand_pair *pair, nr_transport_addr *local_addr, nr_stun_server_request *req) + { + if(pair->remote->component->component_id!=comp->component_id) + return(0); + + if(nr_transport_addr_cmp(&pair->local->base,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + return(0); + + if(nr_transport_addr_cmp(&pair->remote->addr,&req->src_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + return(0); + + return(1); + } + +static int nr_ice_component_handle_triggered_check(nr_ice_component *comp, nr_ice_cand_pair *pair, nr_stun_server_request *req, int *error) + { + nr_stun_message *sreq=req->request; + int r=0,_status; + + if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_USE_CANDIDATE,0)){ + if(comp->stream->pctx->controlling){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND_PAIR(%s): Peer sent USE-CANDIDATE but is controlled",comp->stream->pctx->label, pair->codeword); + } + else{ + /* If this is the first time we've noticed this is nominated...*/ + pair->peer_nominated=1; + + if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED && !pair->nominated){ + pair->nominated=1; + + if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + } + } + } + + /* Note: the RFC says to trigger first and then nominate. But in that case + * the canceled trigger pair would get nominated and the cloned trigger pair + * would not get the nomination status cloned with it.*/ + if(r=nr_ice_candidate_pair_do_triggered_check(comp->stream->pctx,pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +/* Section 7.2.1 */ +static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_transport_addr *local_addr, nr_stun_server_request *req, int *error) + { + nr_ice_cand_pair *pair; + nr_ice_candidate *pcand=0; + nr_stun_message *sreq=req->request; + nr_stun_message_attribute *attr; + int r=0,_status; + int found_valid=0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): received request from %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,req->src_addr.as_string); + + if (comp->state == NR_ICE_COMPONENT_DISABLED) + ABORT(R_REJECTED); + + /* Check for role conficts (7.2.1.1) */ + if(comp->stream->pctx->controlling){ + if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLING,&attr)){ + /* OK, there is a conflict. Who's right? */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlling",comp->stream->pctx->label); + + if(attr->u.ice_controlling > comp->stream->pctx->tiebreaker){ + /* Update the peer ctx. This will propagate to all candidate pairs + in the context. */ + nr_ice_peer_ctx_switch_controlling_role(comp->stream->pctx); + } + else { + /* We are: throw an error */ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label); + + *error=487; + ABORT(R_REJECTED); + } + } + } + else{ + if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLED,&attr)){ + /* OK, there is a conflict. Who's right? */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlled",comp->stream->pctx->label); + + if(attr->u.ice_controlled < comp->stream->pctx->tiebreaker){ + /* Update the peer ctx. This will propagate to all candidate pairs + in the context. */ + nr_ice_peer_ctx_switch_controlling_role(comp->stream->pctx); + } + else { + /* We are: throw an error */ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label); + + *error=487; + ABORT(R_REJECTED); + } + } + } + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): This STUN request appears to map to local addr %s",comp->stream->pctx->label,local_addr->as_string); + + pair=TAILQ_FIRST(&comp->stream->check_list); + while(pair){ + /* Since triggered checks create duplicate pairs (in this implementation) + * we are willing to handle multiple matches here. */ + if(nr_ice_component_pair_matches_check(comp, pair, local_addr, req)){ + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Found a matching pair for received check: %s",comp->stream->pctx->label,pair->codeword,pair->as_string); + if(r=nr_ice_component_handle_triggered_check(comp, pair, req, error)) + ABORT(r); + ++found_valid; + } + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + if(!found_valid){ + /* There were no matching pairs, so we need to create a new peer + * reflexive candidate pair. */ + + if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label); + *error=400; + ABORT(R_BAD_DATA); + } + + /* Find our local component candidate */ + nr_ice_candidate *cand; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no matching pair",comp->stream->pctx->label); + cand=TAILQ_FIRST(&comp->local_component->candidates); + while(cand){ + if(!nr_transport_addr_cmp(&cand->addr,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + break; + + cand=TAILQ_NEXT(cand,entry_comp); + } + + /* Well, this really shouldn't happen, but it's an error from the + other side, so we just throw an error and keep going */ + if(!cand){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): stun request to unknown local address %s, discarding",comp->stream->pctx->label,local_addr->as_string); + + *error=400; + ABORT(R_NOT_FOUND); + } + + /* Now make a peer reflexive (remote) candidate */ + if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + pcand->priority=attr->u.priority; + pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED; + + /* Finally, create the candidate pair, insert into the check list, and + * apply the incoming check to it. */ + if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand, + &pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN); + if(r=nr_ice_component_insert_pair(comp,pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + + /* Do this last, since any call to ABORT will destroy pcand */ + TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp); + pcand=0; + + /* Finally start the trigger check if needed */ + if(r=nr_ice_component_handle_triggered_check(comp, pair, req, error)) + ABORT(r); + } + + _status=0; + abort: + if(_status){ + nr_ice_candidate_destroy(&pcand); + assert(*error != 0); + if(r!=R_NO_MEMORY) assert(*error != 500); + } + return(_status); + } + +static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error) + { + nr_ice_component *pcomp=cb_arg; + nr_transport_addr local_addr; + int r,_status; + + if(pcomp->state==NR_ICE_COMPONENT_FAILED) { + *error=400; + ABORT(R_REJECTED); + } + + if (pcomp->local_component->stream->obsolete) { + /* Don't do any triggered check stuff in thiis case. */ + return 0; + } + + /* Find the candidate pair that this maps to */ + if(r=nr_socket_getaddr(sock,&local_addr)) { + *error=500; + ABORT(r); + } + + if (r=nr_ice_component_process_incoming_check(pcomp, &local_addr, req, error)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced) + { + nr_ice_pre_answer_request *r1,*r2; + nr_ice_component *comp = pcomp->local_component; + int r,_status; + + if (serviced) + *serviced = 0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): looking for pre-answer requests",pctx->label,comp->stream->label,comp->component_id); + + STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) { + if (!strcmp(r1->username, username)) { + int error = 0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): found pre-answer request",pctx->label,comp->stream->label,comp->component_id); + r = nr_ice_component_process_incoming_check(pcomp, &r1->local_addr, &r1->req, &error); + if (r) { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): error processing pre-answer request. Would have returned %d",pctx->label,comp->stream->label,comp->component_id, error); + } + (*serviced)++; + STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry); + nr_ice_pre_answer_request_destroy(&r1); + } + } + + _status=0; + return(_status); + } + +int nr_ice_component_can_candidate_tcptype_pair(nr_socket_tcp_type left, nr_socket_tcp_type right) + { + if (left && !right) + return(0); + if (!left && right) + return(0); + if (left == TCP_TYPE_ACTIVE && right != TCP_TYPE_PASSIVE) + return(0); + if (left == TCP_TYPE_SO && right != TCP_TYPE_SO) + return(0); + if (left == TCP_TYPE_PASSIVE) + return(0); + + return(1); + } + +/* filter out pairings which won't work. */ +int nr_ice_component_can_candidate_addr_pair(nr_transport_addr *local, nr_transport_addr *remote) + { + if(local->ip_version != remote->ip_version) + return(0); + if(local->protocol != remote->protocol) + return(0); + if(nr_transport_addr_is_link_local(local) != + nr_transport_addr_is_link_local(remote)) + return(0); + /* This prevents our ice_unittest (or broken clients) from pairing a + * loopback with a host candidate. */ + if(nr_transport_addr_is_loopback(local) != + nr_transport_addr_is_loopback(remote)) + return(0); + + return(1); + } + +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]; + + nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword); + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing local candidate %s",pctx->label,codeword,lcand->label); + + switch(lcand->type){ + case HOST: + break; + case SERVER_REFLEXIVE: + case PEER_REFLEXIVE: + /* Don't actually pair these candidates */ + goto done; + break; + case RELAYED: + break; + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + TAILQ_FOREACH(pcand, &pcomp->candidates, entry_comp){ + if(!nr_ice_component_can_candidate_addr_pair(&lcand->addr, &pcand->addr)) + continue; + if(!nr_ice_component_can_candidate_tcptype_pair(lcand->tcp_type, pcand->tcp_type)) + continue; + + /* https://tools.ietf.org/html/draft-ietf-rtcweb-mdns-ice-candidates-03#section-3.3.2 */ + if(lcand->type == RELAYED && pcand->mdns_addr && strlen(pcand->mdns_addr)) { + continue; + } + + /* + Two modes, depending on |pair_all_remote| + + 1. Pair remote candidates which have not been paired + (used in initial pairing or in processing the other side's + trickle candidates). + 2. Pair any remote candidate (used when processing our own + trickle candidates). + */ + if (pair_all_remote || (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED)) { + if (pair_all_remote) { + /* When a remote candidate arrives after the start of checking, but + * before the gathering of local candidates, it can be in UNPAIRED */ + pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED; + } + + nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword); + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing with peer candidate %s", pctx->label, codeword, pcand->label); + + if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair)) + ABORT(r); + + if(r=nr_ice_component_insert_pair(pcomp, pair)) + ABORT(r); + } + } + + done: + _status = 0; + abort: + return(_status); + } + +int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp) + { + nr_ice_candidate *lcand, *pcand; + nr_ice_socket *isock; + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"Pairing candidates======"); + + /* Create the candidate pairs */ + lcand=TAILQ_FIRST(&lcomp->candidates); + + if (!lcand) { + /* No local candidates, initialized or not! */ + ABORT(R_FAILED); + } + + while(lcand){ + if (lcand->state == NR_ICE_CAND_STATE_INITIALIZED) { + if ((r = nr_ice_component_pair_candidate(pctx, pcomp, lcand, 0))) + ABORT(r); + } + + lcand=TAILQ_NEXT(lcand,entry_comp); + } + + /* Mark all peer candidates as paired */ + pcand=TAILQ_FIRST(&pcomp->candidates); + while(pcand){ + pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED; + + pcand=TAILQ_NEXT(pcand,entry_comp); + + } + + /* Now register the STUN server callback for this component. + Note that this is a per-component CB so we only need to + do this once. + */ + if (pcomp->state != NR_ICE_COMPONENT_RUNNING) { + isock=STAILQ_FIRST(&lcomp->sockets); + while(isock){ + if(r=nr_stun_server_add_client(isock->stun_server,pctx->label, + pcomp->stream->r2l_user,&pcomp->stream->r2l_pass,nr_ice_component_stun_server_cb,pcomp)) { + ABORT(r); + } + isock=STAILQ_NEXT(isock,entry); + } + } + + pcomp->state = NR_ICE_COMPONENT_RUNNING; + + _status=0; + abort: + return(_status); + } + +int nr_ice_pre_answer_enqueue(nr_ice_component *comp, nr_socket *sock, nr_stun_server_request *req, int *dont_free) + { + int r = 0; + int _status; + nr_ice_pre_answer_request *r1, *r2; + nr_transport_addr dst_addr; + nr_ice_pre_answer_request *par = 0; + + if (r=nr_socket_getaddr(sock, &dst_addr)) + ABORT(r); + + STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) { + if (!nr_transport_addr_cmp(&r1->local_addr, &dst_addr, + NR_TRANSPORT_ADDR_CMP_MODE_ALL) && + !nr_transport_addr_cmp(&r1->req.src_addr, &req->src_addr, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + return(0); + } + } + + if (r=nr_ice_pre_answer_request_create(&dst_addr, req, &par)) + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG, "ICE(%s)/STREAM(%s)/COMP(%d): Enqueuing STUN request pre-answer from %s", + comp->ctx->label, comp->stream->label, comp->component_id, + req->src_addr.as_string); + + *dont_free = 1; + STAILQ_INSERT_TAIL(&comp->pre_answer_reqs, par, entry); + + _status=0; +abort: + return(_status); + } + +/* Fires when we have an incoming candidate that doesn't correspond to an existing + remote peer. This is either pre-answer or just spurious. Store it in the + component for use when we see the actual answer, at which point we need + to do the procedures from S 7.2.1 in nr_ice_component_stun_server_cb. + */ +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) + { + int r, _status; + nr_ice_component *comp = (nr_ice_component *)cb_arg; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Received STUN request pre-answer from %s", + comp->ctx->label, comp->stream->label, comp->component_id, + req->src_addr.as_string); + + if (r=nr_ice_pre_answer_enqueue(comp, sock, req, dont_free)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): Failed (%d) to enque pre-answer request from %s", + comp->ctx->label, comp->stream->label, comp->component_id, r, + req->src_addr.as_string); + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +#define NR_ICE_CONSENT_TIMER_DEFAULT 5000 +#define NR_ICE_CONSENT_TIMEOUT_DEFAULT 30000 + +static void nr_ice_component_consent_failed(nr_ice_component *comp) + { + if (!comp->can_send) { + return; + } + + r_log(LOG_ICE,LOG_INFO,"ICE(%s)/STREAM(%s)/COMP(%d): Consent refresh failed", + comp->ctx->label, comp->stream->label, comp->component_id); + comp->can_send = 0; + + if (comp->consent_timeout) { + NR_async_timer_cancel(comp->consent_timeout); + comp->consent_timeout = 0; + } + if (comp->consent_timer) { + NR_async_timer_cancel(comp->consent_timer); + comp->consent_timer = 0; + } + /* We are turning the consent failure into a ICE component failure to + * alert the browser via ICE connection state change about this event. */ + nr_ice_media_stream_component_failed(comp->stream, comp); + } + +static void nr_ice_component_consent_timeout_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_component *comp=cb_arg; + + comp->consent_timeout = 0; + + r_log(LOG_ICE,LOG_WARNING,"ICE(%s)/STREAM(%s)/COMP(%d): Consent refresh final time out", + comp->ctx->label, comp->stream->label, comp->component_id); + nr_ice_component_consent_failed(comp); + } + + +void nr_ice_component_disconnected(nr_ice_component *comp) + { + if (!comp->can_send) { + return; + } + + if (comp->disconnected) { + return; + } + + r_log(LOG_ICE,LOG_WARNING,"ICE(%s)/STREAM(%s)/COMP(%d): component disconnected", + comp->ctx->label, comp->stream->label, comp->component_id); + comp->disconnected = 1; + + /* a single disconnected component disconnects the stream */ + nr_ice_media_stream_set_disconnected(comp->stream, NR_ICE_MEDIA_STREAM_DISCONNECTED); + } + +static void nr_ice_component_consent_refreshed(nr_ice_component *comp) + { + uint16_t tval; + + if (!comp->can_send) { + return; + } + + gettimeofday(&comp->consent_last_seen, 0); + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): consent_last_seen is now %lu", + comp->ctx->label, comp->stream->label, comp->component_id, + comp->consent_last_seen.tv_sec); + + comp->disconnected = 0; + + nr_ice_media_stream_check_if_connected(comp->stream); + + if (comp->consent_timeout) + NR_async_timer_cancel(comp->consent_timeout); + + tval = NR_ICE_CONSENT_TIMEOUT_DEFAULT; + if (comp->ctx->test_timer_divider) + tval = tval / comp->ctx->test_timer_divider; + + NR_ASYNC_TIMER_SET(tval, nr_ice_component_consent_timeout_cb, comp, + &comp->consent_timeout); + } + +static void nr_ice_component_refresh_consent_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_component *comp=cb_arg; + + switch (comp->consent_ctx->state) { + case NR_STUN_CLIENT_STATE_FAILED: + if (comp->consent_ctx->error_code == 403) { + r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): Consent revoked by peer", + comp->ctx->label, comp->stream->label, comp->component_id); + nr_ice_component_consent_failed(comp); + } + break; + case NR_STUN_CLIENT_STATE_DONE: + r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): Consent refreshed", + comp->ctx->label, comp->stream->label, comp->component_id); + nr_ice_component_consent_refreshed(comp); + break; + case NR_STUN_CLIENT_STATE_TIMED_OUT: + r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): A single consent refresh request timed out", + comp->ctx->label, comp->stream->label, comp->component_id); + nr_ice_component_disconnected(comp); + break; + default: + break; + } + } + +int nr_ice_component_refresh_consent(nr_stun_client_ctx *ctx, NR_async_cb finished_cb, void *cb_arg) + { + int r,_status; + + nr_stun_client_reset(ctx); + + if (r=nr_stun_client_start(ctx, NR_ICE_CLIENT_MODE_BINDING_REQUEST, finished_cb, cb_arg)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +void nr_ice_component_consent_calc_consent_timer(nr_ice_component *comp) + { + uint16_t trange, trand, tval; + + trange = NR_ICE_CONSENT_TIMER_DEFAULT * 20 / 100; + tval = NR_ICE_CONSENT_TIMER_DEFAULT - trange; + if (!nr_crypto_random_bytes((UCHAR*)&trand, sizeof(trand))) + tval += (trand % (trange * 2)); + + if (comp->ctx->test_timer_divider) + tval = tval / comp->ctx->test_timer_divider; + + /* The timeout of the transaction is the maximum time until we send the + * next consent request. */ + comp->consent_ctx->maximum_transmits_timeout_ms = tval; + } + +static void nr_ice_component_consent_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_component *comp=cb_arg; + int r; + + if (!comp->consent_ctx) { + return; + } + + if (comp->consent_timer) { + NR_async_timer_cancel(comp->consent_timer); + } + comp->consent_timer = 0; + + comp->consent_ctx->params.ice_binding_request.username = + comp->stream->l2r_user; + comp->consent_ctx->params.ice_binding_request.password = + comp->stream->l2r_pass; + comp->consent_ctx->params.ice_binding_request.control = + comp->stream->pctx->controlling? + NR_ICE_CONTROLLING:NR_ICE_CONTROLLED; + comp->consent_ctx->params.ice_binding_request.tiebreaker = + comp->stream->pctx->tiebreaker; + comp->consent_ctx->params.ice_binding_request.priority = + comp->active->local->priority; + + nr_ice_component_consent_calc_consent_timer(comp); + + if (r=nr_ice_component_refresh_consent(comp->consent_ctx, + nr_ice_component_refresh_consent_cb, + comp)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): Refresh consent failed with %d", + comp->ctx->label, comp->stream->label, comp->component_id, r); + } + + nr_ice_component_consent_schedule_consent_timer(comp); + + } + +void nr_ice_component_consent_schedule_consent_timer(nr_ice_component *comp) + { + if (!comp->can_send) { + return; + } + + NR_ASYNC_TIMER_SET(comp->consent_ctx->maximum_transmits_timeout_ms, + nr_ice_component_consent_timer_cb, comp, + &comp->consent_timer); + } + +void nr_ice_component_refresh_consent_now(nr_ice_component *comp) + { + nr_ice_component_consent_timer_cb(0, 0, comp); + } + +void nr_ice_component_consent_destroy(nr_ice_component *comp) + { + if (comp->consent_timer) { + NR_async_timer_cancel(comp->consent_timer); + comp->consent_timer = 0; + } + if (comp->consent_timeout) { + NR_async_timer_cancel(comp->consent_timeout); + comp->consent_timeout = 0; + } + if (comp->consent_handle) { + nr_ice_socket_deregister(comp->active->local->isock, + comp->consent_handle); + comp->consent_handle = 0; + } + if (comp->consent_ctx) { + nr_stun_client_ctx_destroy(&comp->consent_ctx); + comp->consent_ctx = 0; + } + } + +int nr_ice_component_setup_consent(nr_ice_component *comp) + { + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Setting up refresh consent", + comp->ctx->label, comp->stream->label, comp->component_id); + + nr_ice_component_consent_destroy(comp); + + if (r=nr_stun_client_ctx_create("consent", comp->active->local->osock, + &comp->active->remote->addr, 0, + &comp->consent_ctx)) + ABORT(r); + /* Consent request get send only once. */ + comp->consent_ctx->maximum_transmits = 1; + + if (r=nr_ice_socket_register_stun_client(comp->active->local->isock, + comp->consent_ctx, &comp->consent_handle)) + ABORT(r); + + comp->can_send = 1; + comp->disconnected = 0; + nr_ice_component_consent_refreshed(comp); + + nr_ice_component_consent_calc_consent_timer(comp); + nr_ice_component_consent_schedule_consent_timer(comp); + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) + { + int r,_status; + nr_ice_cand_pair *p2; + + /* Are we changing what the nominated pair is? */ + if(comp->nominated){ + if(comp->nominated->priority >= pair->priority) + return(0); + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): replacing pair %s with CAND-PAIR(%s)",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->codeword,comp->nominated->as_string,pair->codeword); + /* As consent doesn't hold a reference to its isock this needs to happen + * before making the new pair the active one. */ + nr_ice_component_consent_destroy(comp); + } + + /* Set the new nominated pair */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): nominated pair is %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string); + comp->state=NR_ICE_COMPONENT_NOMINATED; + comp->nominated=pair; + comp->active=pair; + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling all pairs but %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string); + + /* Cancel checks in WAITING and FROZEN per ICE S 8.1.2 */ + p2=TAILQ_FIRST(&comp->stream->trigger_check_queue); + while(p2){ + if((p2 != pair) && + (p2->remote->component->component_id == comp->component_id)) { + assert(p2->state == NR_ICE_PAIR_STATE_WAITING || + p2->state == NR_ICE_PAIR_STATE_CANCELLED); + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s in trigger check queue because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword); + + nr_ice_candidate_pair_cancel(pair->pctx,p2,0); + } + + p2=TAILQ_NEXT(p2,triggered_check_queue_entry); + } + p2=TAILQ_FIRST(&comp->stream->check_list); + while(p2){ + if((p2 != pair) && + (p2->remote->component->component_id == comp->component_id) && + ((p2->state == NR_ICE_PAIR_STATE_FROZEN) || + (p2->state == NR_ICE_PAIR_STATE_WAITING))) { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword); + + nr_ice_candidate_pair_cancel(pair->pctx,p2,0); + } + + p2=TAILQ_NEXT(p2,check_queue_entry); + } + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id); + + if(r=nr_ice_component_setup_consent(comp)) + ABORT(r); + + nr_ice_media_stream_component_nominated(comp->stream,comp); + + _status=0; + abort: + return(_status); + } + +static int nr_ice_component_have_all_pairs_failed(nr_ice_component *comp) + { + nr_ice_cand_pair *p2; + + p2=TAILQ_FIRST(&comp->stream->check_list); + while(p2){ + if(comp->component_id==p2->local->component_id){ + switch(p2->state){ + case NR_ICE_PAIR_STATE_FROZEN: + case NR_ICE_PAIR_STATE_WAITING: + case NR_ICE_PAIR_STATE_IN_PROGRESS: + case NR_ICE_PAIR_STATE_SUCCEEDED: + return(0); + case NR_ICE_PAIR_STATE_FAILED: + case NR_ICE_PAIR_STATE_CANCELLED: + /* states that will never be recovered from */ + break; + default: + assert(0); + break; + } + } + + p2=TAILQ_NEXT(p2,check_queue_entry); + } + + return(1); + } + +void nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) + { + nr_ice_component_check_if_failed(comp); + } + +void nr_ice_component_check_if_failed(nr_ice_component *comp) + { + if (comp->state == NR_ICE_COMPONENT_RUNNING) { + /* Don't do anything to streams that aren't currently running */ + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): Checking whether component needs to be marked failed.",comp->stream->pctx->label,comp->stream->label,comp->component_id); + + if (!comp->stream->pctx->trickle_grace_period_timer && + nr_ice_component_have_all_pairs_failed(comp)) { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): All pairs are failed, and grace period has elapsed. Marking component as failed.",comp->stream->pctx->label,comp->stream->label,comp->component_id); + nr_ice_media_stream_component_failed(comp->stream,comp); + } + } + } + +int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp) + { + nr_ice_cand_pair **pairs=0; + int ct=0; + nr_ice_cand_pair *pair; + int r,_status; + + /* Size the array */ + pair=TAILQ_FIRST(&comp->stream->check_list); + while(pair){ + if (comp->component_id == pair->local->component_id) + ct++; + + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + /* Make and fill the array */ + if(!(pairs=RCALLOC(sizeof(nr_ice_cand_pair *)*ct))) + ABORT(R_NO_MEMORY); + + ct=0; + pair=TAILQ_FIRST(&comp->stream->check_list); + while(pair){ + if (comp->component_id == pair->local->component_id) + pairs[ct++]=pair; + + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + if (pctx->handler) { + if(r=pctx->handler->vtbl->select_pair(pctx->handler->obj, + comp->stream,comp->component_id,pairs,ct)) + ABORT(r); + } + + _status=0; + abort: + RFREE(pairs); + return(_status); + } + + +/* Close the underlying sockets for everything but the nominated candidate */ +int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp) + { + nr_ice_socket *isock=0; + nr_ice_socket *s1,*s2; + + if(rcomp->state==NR_ICE_COMPONENT_NOMINATED){ + assert(rcomp->active == rcomp->nominated); + isock=rcomp->nominated->local->isock; + } + + STAILQ_FOREACH_SAFE(s1, &lcomp->sockets, entry, s2){ + if(s1!=isock){ + STAILQ_REMOVE(&lcomp->sockets,s1,nr_ice_socket_,entry); + nr_ice_socket_destroy(&s1); + } + } + + return(0); + } + + +int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair) + { + int _status; + + /* Pairs for peer reflexive are marked SUCCEEDED immediately */ + if (pair->state != NR_ICE_PAIR_STATE_FROZEN && + pair->state != NR_ICE_PAIR_STATE_SUCCEEDED){ + assert(0); + ABORT(R_BAD_ARGS); + } + + /* We do not throw an error after this, because we've inserted the pair. */ + nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,pair); + + /* Make sure the check timer is running, if the stream was previously + * started. We will not start streams just because a pair was created, + * unless it is the first pair to be created across all streams. */ + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND-PAIR(%s): Ensure that check timer is running for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string); + + if(pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE || + (pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN && + !pair->remote->stream->pctx->checks_started)){ + if(nr_ice_media_stream_start_checks(pair->remote->stream->pctx, pair->remote->stream)) { + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Could not restart checks for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string); + } + } + + _status=0; + abort: + if (_status) { + nr_ice_candidate_pair_destroy(&pair); + } + return(_status); + } + +int nr_ice_component_get_default_candidate(nr_ice_component *comp, nr_ice_candidate **candp, int ip_version) + { + int _status; + nr_ice_candidate *cand; + nr_ice_candidate *best_cand = NULL; + + /* We have the component. Now find the "best" candidate, making + use of the fact that more "reliable" candidate types have + higher numbers. So, we sort by type and then priority within + type + */ + cand=TAILQ_FIRST(&comp->candidates); + while(cand){ + if (!nr_ice_ctx_hide_candidate(comp->ctx, cand) && + cand->addr.ip_version == ip_version) { + if (!best_cand) { + best_cand = cand; + } + else if (best_cand->type < cand->type) { + best_cand = cand; + } else if (best_cand->type == cand->type && + best_cand->priority < cand->priority) { + best_cand = cand; + } + } + + cand=TAILQ_NEXT(cand,entry_comp); + } + + /* No candidates */ + if (!best_cand) + ABORT(R_NOT_FOUND); + + *candp = best_cand; + + _status=0; + abort: + return(_status); + + } + + +void nr_ice_component_dump_state(nr_ice_component *comp, int log_level) + { + nr_ice_candidate *cand; + + if (comp->local_component) { + r_log(LOG_ICE,log_level,"ICE(%s)/ICE-STREAM(%s): Remote component %d in state %d - dumping candidates",comp->ctx->label,comp->stream->label,comp->component_id,comp->state); + } else { + r_log(LOG_ICE,log_level,"ICE(%s)/ICE-STREAM(%s): Local component %d - dumping candidates",comp->ctx->label,comp->stream->label,comp->component_id); + } + + cand=TAILQ_FIRST(&comp->candidates); + while(cand){ + r_log(LOG_ICE,log_level,"ICE(%s)/ICE-STREAM(%s)/CAND(%s): %s",comp->ctx->label,comp->stream->label,cand->codeword,cand->label); + cand=TAILQ_NEXT(cand,entry_comp); + } + } + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.h new file mode 100644 index 0000000000..0b12a68d58 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.h @@ -0,0 +1,111 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_component_h +#define _ice_component_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +typedef struct nr_ice_pre_answer_request_ { + nr_stun_server_request req; + char *username; + nr_transport_addr local_addr; + + STAILQ_ENTRY(nr_ice_pre_answer_request_) entry; +} nr_ice_pre_answer_request; + +typedef STAILQ_HEAD(nr_ice_pre_answer_request_head_, nr_ice_pre_answer_request_) nr_ice_pre_answer_request_head; + +struct nr_ice_component_ { + int state; +#define NR_ICE_COMPONENT_UNPAIRED 0 +#define NR_ICE_COMPONENT_RUNNING 1 +#define NR_ICE_COMPONENT_NOMINATED 2 +#define NR_ICE_COMPONENT_FAILED 3 +#define NR_ICE_COMPONENT_DISABLED 4 + struct nr_ice_ctx_ *ctx; + struct nr_ice_media_stream_ *stream; + nr_ice_component *local_component; + + int component_id; + nr_ice_socket_head sockets; + nr_ice_candidate_head candidates; + int candidate_ct; + nr_ice_pre_answer_request_head pre_answer_reqs; + + int valid_pairs; + struct nr_ice_cand_pair_ *nominated; /* Highest priority nomninated pair */ + struct nr_ice_cand_pair_ *active; + + nr_stun_client_ctx *consent_ctx; + void *consent_timer; + void *consent_timeout; + void *consent_handle; + int can_send; + int disconnected; + struct timeval consent_last_seen; + + STAILQ_ENTRY(nr_ice_component_)entry; +}; + +typedef STAILQ_HEAD(nr_ice_component_head_,nr_ice_component_) nr_ice_component_head; + +int nr_ice_component_create(struct nr_ice_media_stream_ *stream, int component_id, nr_ice_component **componentp); +int nr_ice_component_destroy(nr_ice_component **componentp); +int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component); +void nr_ice_component_stop_gathering(nr_ice_component *component); +int nr_ice_component_is_done_gathering(nr_ice_component *comp); +int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned); +int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote); +int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp, nr_ice_component *pcomp); +int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced); +int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair); +void nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair); +void nr_ice_component_check_if_failed(nr_ice_component *comp); +int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp); +int nr_ice_component_set_failed(nr_ice_component *comp); +int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp); +int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair); +int nr_ice_component_get_default_candidate(nr_ice_component *comp, nr_ice_candidate **candp, int ip_version); +void nr_ice_component_consent_destroy(nr_ice_component *comp); +void nr_ice_component_refresh_consent_now(nr_ice_component *comp); +void nr_ice_component_disconnected(nr_ice_component *comp); +void nr_ice_component_dump_state(nr_ice_component *comp, int log_level); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c new file mode 100644 index 0000000000..0d498845a4 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c @@ -0,0 +1,1125 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <csi_platform.h> +#include <assert.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include <sys/queue.h> +#include <string.h> +#include <nr_api.h> +#include <registry.h> +#include "stun.h" +#include "ice_ctx.h" +#include "ice_reg.h" +#include "nr_crypto.h" +#include "async_timer.h" +#include "util.h" +#include "nr_socket_local.h" + +#define ICE_UFRAG_LEN 8 +#define ICE_PWD_LEN 32 + +int LOG_ICE = 0; + +static int nr_ice_random_string(char *str, int len); +static int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out); +#ifdef USE_TURN +static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out); +#endif /* USE_TURN */ +static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand); +static int no_op(void **obj) { + return 0; +} + +static nr_socket_factory_vtbl default_socket_factory_vtbl = { + nr_socket_local_create, + no_op +}; + +int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out) + { + int r,_status; + nr_ice_stun_server *servers = 0; + int i; + NR_registry child; + char *addr=0; + UINT2 port; + in_addr_t addr_int; + + if(!(servers=RCALLOC(sizeof(nr_ice_stun_server)*ct))) + ABORT(R_NO_MEMORY); + + for(i=0;i<ct;i++){ + if(r=NR_reg_get_child_registry(NR_ICE_REG_STUN_SRV_PRFX,i,child)) + ABORT(r); + /* Assume we have a v4 addr for now */ + if(r=NR_reg_alloc2_string(child,"addr",&addr)) + ABORT(r); + addr_int=inet_addr(addr); + if(addr_int==INADDR_NONE){ + r_log(LOG_ICE,LOG_ERR,"Invalid address %s;",addr); + ABORT(R_BAD_ARGS); + } + if(r=NR_reg_get2_uint2(child,"port",&port)) { + if (r != R_NOT_FOUND) + ABORT(r); + port = 3478; + } + if (r = nr_ip4_port_to_transport_addr(ntohl(addr_int), port, IPPROTO_UDP, + &servers[i].addr)) + ABORT(r); + RFREE(addr); + addr=0; + } + + *out = servers; + + _status=0; + abort: + RFREE(addr); + if (_status) RFREE(servers); + return(_status); + } + +int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers,int ct) + { + int _status; + + if(ctx->stun_servers_cfg){ + RFREE(ctx->stun_servers_cfg); + ctx->stun_servers_cfg=NULL; + ctx->stun_server_ct_cfg=0; + } + + if (ct) { + if(!(ctx->stun_servers_cfg=RCALLOC(sizeof(nr_ice_stun_server)*ct))) + ABORT(R_NO_MEMORY); + + memcpy(ctx->stun_servers_cfg,servers,sizeof(nr_ice_stun_server)*ct); + ctx->stun_server_ct_cfg = ct; + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers,int ct) + { + int _status; + + if(ctx->turn_servers_cfg){ + for (int i = 0; i < ctx->turn_server_ct_cfg; i++) { + RFREE(ctx->turn_servers_cfg[i].username); + r_data_destroy(&ctx->turn_servers_cfg[i].password); + } + RFREE(ctx->turn_servers_cfg); + ctx->turn_servers_cfg=NULL; + ctx->turn_server_ct_cfg=0; + } + + if(ct) { + if(!(ctx->turn_servers_cfg=RCALLOC(sizeof(nr_ice_turn_server)*ct))) + ABORT(R_NO_MEMORY); + + memcpy(ctx->turn_servers_cfg,servers,sizeof(nr_ice_turn_server)*ct); + ctx->turn_server_ct_cfg = ct; + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_ctx_copy_turn_servers(nr_ice_ctx *ctx, nr_ice_turn_server *servers, int ct) + { + int _status, i, r; + + if (r = nr_ice_ctx_set_turn_servers(ctx, servers, ct)) { + ABORT(r); + } + + // make copies of the username and password so they aren't freed twice + for (i = 0; i < ct; ++i) { + if (!(ctx->turn_servers_cfg[i].username = r_strdup(servers[i].username))) { + ABORT(R_NO_MEMORY); + } + if (r = r_data_create(&ctx->turn_servers_cfg[i].password, + servers[i].password->data, + servers[i].password->len)) { + ABORT(r); + } + } + + _status=0; + abort: + return(_status); + } + +static int nr_ice_ctx_set_local_addrs(nr_ice_ctx *ctx,nr_local_addr *addrs,int ct) + { + int _status,i,r; + + if(ctx->local_addrs) { + RFREE(ctx->local_addrs); + ctx->local_addr_ct=0; + ctx->local_addrs=0; + } + + if (ct) { + if(!(ctx->local_addrs=RCALLOC(sizeof(nr_local_addr)*ct))) + ABORT(R_NO_MEMORY); + + for (i=0;i<ct;++i) { + if (r=nr_local_addr_copy(ctx->local_addrs+i,addrs+i)) { + ABORT(r); + } + } + ctx->local_addr_ct = ct; + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver) + { + int _status; + + if (ctx->resolver) { + ABORT(R_ALREADY); + } + + ctx->resolver = resolver; + + _status=0; + abort: + return(_status); + } + +int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *ip) + { + int _status; + + if (ctx->interface_prioritizer) { + ABORT(R_ALREADY); + } + + ctx->interface_prioritizer = ip; + + _status=0; + abort: + return(_status); + } + +void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory) + { + nr_socket_factory_destroy(&ctx->socket_factory); + ctx->socket_factory = factory; + } + +#ifdef USE_TURN +int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out) + { + int r,_status; + nr_ice_turn_server *servers = 0; + int i; + NR_registry child; + char *addr=0; + UINT2 port; + in_addr_t addr_int; + Data data={0}; + + if(!(servers=RCALLOC(sizeof(nr_ice_turn_server)*ct))) + ABORT(R_NO_MEMORY); + + for(i=0;i<ct;i++){ + if(r=NR_reg_get_child_registry(NR_ICE_REG_TURN_SRV_PRFX,i,child)) + ABORT(r); + /* Assume we have a v4 addr for now */ + if(r=NR_reg_alloc2_string(child,"addr",&addr)) + ABORT(r); + addr_int=inet_addr(addr); + if(addr_int==INADDR_NONE){ + r_log(LOG_ICE,LOG_ERR,"Invalid address %s",addr); + ABORT(R_BAD_ARGS); + } + if(r=NR_reg_get2_uint2(child,"port",&port)) { + if (r != R_NOT_FOUND) + ABORT(r); + port = 3478; + } + if (r = nr_ip4_port_to_transport_addr(ntohl(addr_int), port, IPPROTO_UDP, + &servers[i].turn_server.addr)) + ABORT(r); + + + if(r=NR_reg_alloc2_string(child,NR_ICE_REG_TURN_SRV_USERNAME,&servers[i].username)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + + if(r=NR_reg_alloc2_data(child,NR_ICE_REG_TURN_SRV_PASSWORD,&data)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + else { + servers[i].password=RCALLOC(sizeof(*servers[i].password)); + if(!servers[i].password) + ABORT(R_NO_MEMORY); + servers[i].password->data = data.data; + servers[i].password->len = data.len; + data.data=0; + } + + RFREE(addr); + addr=0; + } + + *out = servers; + + _status=0; + abort: + RFREE(data.data); + RFREE(addr); + if (_status) RFREE(servers); + return(_status); + } +#endif /* USE_TURN */ + +#define MAXADDRS 100 /* Ridiculously high */ +int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp) + { + nr_ice_ctx *ctx=0; + int r,_status; + + if(r=r_log_register("ice", &LOG_ICE)) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_ice_ctx)))) + ABORT(R_NO_MEMORY); + + ctx->flags=flags; + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + /* Get the STUN servers */ + if(r=NR_reg_get_child_count(NR_ICE_REG_STUN_SRV_PRFX, + (unsigned int *)&ctx->stun_server_ct_cfg)||ctx->stun_server_ct_cfg==0) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No STUN servers specified in nICEr registry", ctx->label); + ctx->stun_server_ct_cfg=0; + } + + /* 31 is the max for our priority algorithm */ + if(ctx->stun_server_ct_cfg>31){ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN servers specified: max=31", ctx->label); + ctx->stun_server_ct_cfg=31; + } + + if(ctx->stun_server_ct_cfg>0){ + if(r=nr_ice_fetch_stun_servers(ctx->stun_server_ct_cfg,&ctx->stun_servers_cfg)){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Couldn't load STUN servers from registry", ctx->label); + ctx->stun_server_ct_cfg=0; + ABORT(r); + } + } + +#ifdef USE_TURN + /* Get the TURN servers */ + if(r=NR_reg_get_child_count(NR_ICE_REG_TURN_SRV_PRFX, + (unsigned int *)&ctx->turn_server_ct_cfg)||ctx->turn_server_ct_cfg==0) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No TURN servers specified in nICEr registry", ctx->label); + ctx->turn_server_ct_cfg=0; + } +#else + ctx->turn_server_ct_cfg=0; +#endif /* USE_TURN */ + + ctx->local_addrs=0; + ctx->local_addr_ct=0; + + /* 31 is the max for our priority algorithm */ + if((ctx->stun_server_ct_cfg+ctx->turn_server_ct_cfg)>31){ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN/TURN servers specified: max=31", ctx->label); + ctx->turn_server_ct_cfg=31-ctx->stun_server_ct_cfg; + } + +#ifdef USE_TURN + if(ctx->turn_server_ct_cfg>0){ + if(r=nr_ice_fetch_turn_servers(ctx->turn_server_ct_cfg,&ctx->turn_servers_cfg)){ + ctx->turn_server_ct_cfg=0; + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Couldn't load TURN servers from registry", ctx->label); + ABORT(r); + } + } +#endif /* USE_TURN */ + + + ctx->Ta = 20; + + ctx->test_timer_divider = 0; + + if (r=nr_socket_factory_create_int(NULL, &default_socket_factory_vtbl, &ctx->socket_factory)) + ABORT(r); + + if ((r=NR_reg_get_string((char *)NR_ICE_REG_PREF_FORCE_INTERFACE_NAME, ctx->force_net_interface, sizeof(ctx->force_net_interface)))) { + if (r == R_NOT_FOUND) { + ctx->force_net_interface[0] = 0; + } else { + ABORT(r); + } + } + + ctx->target_for_default_local_address_lookup=0; + + STAILQ_INIT(&ctx->streams); + STAILQ_INIT(&ctx->sockets); + STAILQ_INIT(&ctx->foundations); + STAILQ_INIT(&ctx->peers); + STAILQ_INIT(&ctx->ids); + + *ctxp=ctx; + + _status=0; + abort: + if (_status && ctx) nr_ice_ctx_destroy(&ctx); + + return(_status); + } + + void nr_ice_ctx_add_flags(nr_ice_ctx* ctx, UINT4 flags) { + ctx->flags |= flags; + } + + void nr_ice_ctx_remove_flags(nr_ice_ctx* ctx, UINT4 flags) { + ctx->flags &= ~flags; + } + + void nr_ice_ctx_destroy(nr_ice_ctx** ctxp) { + if (!ctxp || !*ctxp) return; + + nr_ice_ctx* ctx = *ctxp; + nr_ice_foundation *f1,*f2; + nr_ice_media_stream *s1,*s2; + int i; + nr_ice_stun_id *id1,*id2; + + ctx->done_cb = 0; + ctx->trickle_cb = 0; + + STAILQ_FOREACH_SAFE(s1, &ctx->streams, entry, s2){ + STAILQ_REMOVE(&ctx->streams,s1,nr_ice_media_stream_,entry); + nr_ice_media_stream_destroy(&s1); + } + + RFREE(ctx->label); + + RFREE(ctx->stun_servers_cfg); + + RFREE(ctx->local_addrs); + + RFREE(ctx->target_for_default_local_address_lookup); + + for (i = 0; i < ctx->turn_server_ct_cfg; i++) { + RFREE(ctx->turn_servers_cfg[i].username); + r_data_destroy(&ctx->turn_servers_cfg[i].password); + } + RFREE(ctx->turn_servers_cfg); + + f1=STAILQ_FIRST(&ctx->foundations); + while(f1){ + f2=STAILQ_NEXT(f1,entry); + RFREE(f1); + f1=f2; + } + + STAILQ_FOREACH_SAFE(id1, &ctx->ids, entry, id2){ + STAILQ_REMOVE(&ctx->ids,id1,nr_ice_stun_id_,entry); + RFREE(id1); + } + + nr_resolver_destroy(&ctx->resolver); + nr_interface_prioritizer_destroy(&ctx->interface_prioritizer); + nr_socket_factory_destroy(&ctx->socket_factory); + + RFREE(ctx); + + *ctxp=0; + } + +void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg) + { + int r; + nr_ice_candidate *cand=cb_arg; + nr_ice_ctx *ctx; + nr_ice_media_stream *stream; + int component_id; + + assert(cb_arg); + if (!cb_arg) + return; + ctx = cand->ctx; + stream = cand->stream; + component_id = cand->component_id; + + ctx->uninitialized_candidates--; + if (cand->state == NR_ICE_CAND_STATE_FAILED) { + r_log(LOG_ICE, LOG_WARNING, + "ICE(%s)/CAND(%s): failed to initialize, %d remaining", ctx->label, + cand->label, ctx->uninitialized_candidates); + } else { + r_log(LOG_ICE, LOG_DEBUG, "ICE(%s)/CAND(%s): initialized, %d remaining", + ctx->label, cand->label, ctx->uninitialized_candidates); + } + + /* Avoid the need for yet another initialization function */ + if (cand->state == NR_ICE_CAND_STATE_INITIALIZING && cand->type == HOST) + cand->state = NR_ICE_CAND_STATE_INITIALIZED; + + if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) { + int was_pruned = 0; + + if (r=nr_ice_component_maybe_prune_candidate(ctx, cand->component, + cand, &was_pruned)) { + r_log(LOG_ICE, LOG_NOTICE, "ICE(%s): Problem pruning candidates",ctx->label); + } + + if (was_pruned) { + cand = NULL; + } + + /* If we are initialized, the candidate wasn't pruned, + and we have a trickle ICE callback fire the callback */ + if (ctx->trickle_cb && cand && + !nr_ice_ctx_hide_candidate(ctx, cand)) { + ctx->trickle_cb(ctx->trickle_cb_arg, ctx, cand->stream, cand->component_id, cand); + + if (nr_ice_ctx_pair_new_trickle_candidates(ctx, cand)) { + r_log(LOG_ICE,LOG_ERR, "ICE(%s): All could not pair new trickle candidate",ctx->label); + /* But continue */ + } + } + } + + if (nr_ice_media_stream_is_done_gathering(stream) && + ctx->trickle_cb) { + ctx->trickle_cb(ctx->trickle_cb_arg, ctx, stream, component_id, NULL); + } + + if(ctx->uninitialized_candidates==0){ + r_log(LOG_ICE, LOG_INFO, "ICE(%s): All candidates initialized", + ctx->label); + if (ctx->done_cb) { + ctx->done_cb(0,0,ctx->cb_arg); + } + else { + r_log(LOG_ICE, LOG_INFO, + "ICE(%s): No done_cb. We were probably destroyed.", ctx->label); + } + } + else { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Waiting for %d candidates to be initialized",ctx->label, ctx->uninitialized_candidates); + } + } + +static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand) + { + int r,_status; + nr_ice_peer_ctx *pctx; + + pctx=STAILQ_FIRST(&ctx->peers); + while(pctx){ + if (pctx->state == NR_ICE_PEER_STATE_PAIRED) { + r = nr_ice_peer_ctx_pair_new_trickle_candidate(ctx, pctx, cand); + if (r) + ABORT(r); + } + + pctx=STAILQ_NEXT(pctx,entry); + } + + _status=0; + abort: + return(_status); + } + +/* Get the default address by creating a UDP socket, binding it to a wildcard + address, and connecting it to the remote IP. Because this is UDP, no packets + are sent. This lets us query the local address assigned to the socket by the + kernel. + + If the context's remote address is NULL, then the application wasn't loaded + over the network, and we can fall back on connecting to a known public + address (namely Google's): + + IPv4: 8.8.8.8 + IPv6: 2001:4860:4860::8888 +*/ +static int nr_ice_get_default_address(nr_ice_ctx *ctx, int ip_version, nr_transport_addr* addrp) + { + int r,_status; + nr_transport_addr addr, known_remote_addr; + nr_transport_addr *remote_addr=ctx->target_for_default_local_address_lookup; + nr_socket *sock=0; + + switch(ip_version) { + case NR_IPV4: + if ((r=nr_str_port_to_transport_addr("0.0.0.0", 0, IPPROTO_UDP, &addr))) + ABORT(r); + if (!remote_addr || nr_transport_addr_is_loopback(remote_addr)) { + if ((r=nr_str_port_to_transport_addr("8.8.8.8", 53, IPPROTO_UDP, &known_remote_addr))) + ABORT(r); + remote_addr=&known_remote_addr; + } + break; + case NR_IPV6: + if ((r=nr_str_port_to_transport_addr("::0", 0, IPPROTO_UDP, &addr))) + ABORT(r); + if (!remote_addr || nr_transport_addr_is_loopback(remote_addr)) { + if ((r=nr_str_port_to_transport_addr("2001:4860:4860::8888", 53, IPPROTO_UDP, &known_remote_addr))) + ABORT(r); + remote_addr=&known_remote_addr; + } + break; + default: + assert(0); + ABORT(R_INTERNAL); + } + + if ((r=nr_socket_factory_create_socket(ctx->socket_factory, &addr, &sock))) + ABORT(r); + if ((r=nr_socket_connect(sock, remote_addr))) + ABORT(r); + if ((r=nr_socket_getaddr(sock, addrp))) + ABORT(r); + + r_log(LOG_GENERIC, LOG_DEBUG, "Default address: %s", addrp->as_string); + + _status=0; + abort: + nr_socket_destroy(&sock); + return(_status); + } + +static int nr_ice_get_default_local_address(nr_ice_ctx *ctx, int ip_version, nr_local_addr* addrs, int addr_ct, nr_local_addr *addrp) + { + int r,_status; + nr_transport_addr default_addr; + int i; + + if ((r=nr_ice_get_default_address(ctx, ip_version, &default_addr))) + ABORT(r); + + for (i=0; i < addr_ct; ++i) { + // if default addr is found in local addrs, copy the more fully + // complete local addr to the output arg. Don't need to worry + // about comparing ports here. + if (!nr_transport_addr_cmp(&default_addr, &addrs[i].addr, + NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) { + if ((r=nr_local_addr_copy(addrp, &addrs[i]))) + ABORT(r); + break; + } + } + + // if default addr is not in local addrs, just copy the transport addr + // to output arg. + if (i == addr_ct) { + if ((r=nr_transport_addr_copy(&addrp->addr, &default_addr))) + ABORT(r); + (void)strlcpy(addrp->addr.ifname, "default route", sizeof(addrp->addr.ifname)); + } + + _status=0; + abort: + return(_status); + } + +/* if handed a IPv4 default_local_addr, looks for IPv6 address on same interface + if handed a IPv6 default_local_addr, looks for IPv4 address on same interface +*/ +static int nr_ice_get_assoc_interface_address(nr_local_addr* default_local_addr, + nr_local_addr* local_addrs, int addr_ct, + nr_local_addr* assoc_addrp) + { + int r, _status; + int i, ip_version; + + if (!default_local_addr || !local_addrs || !addr_ct) { + ABORT(R_BAD_ARGS); + } + + /* set _status to R_EOD in case we don't find an associated address */ + _status = R_EOD; + + /* look for IPv6 if we have IPv4, look for IPv4 if we have IPv6 */ + ip_version = (NR_IPV4 == default_local_addr->addr.ip_version?NR_IPV6:NR_IPV4); + + for (i=0; i<addr_ct; ++i) { + /* if we find the ip_version we're looking for on the matching interface, + copy it to assoc_addrp. + */ + if (local_addrs[i].addr.ip_version == ip_version && + !strcmp(local_addrs[i].addr.ifname, default_local_addr->addr.ifname)) { + if (r=nr_local_addr_copy(assoc_addrp, &local_addrs[i])) { + ABORT(r); + } + _status = 0; + break; + } + } + + abort: + return(_status); + } + +int nr_ice_set_local_addresses(nr_ice_ctx *ctx, + nr_local_addr* stun_addrs, int stun_addr_ct) + { + int r,_status; + nr_local_addr local_addrs[MAXADDRS]; + nr_local_addr *addrs = 0; + int i,addr_ct; + nr_local_addr default_addrs[2]; + int default_addr_ct = 0; + + if (!stun_addrs || !stun_addr_ct) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): no stun addrs provided",ctx->label); + ABORT(R_BAD_ARGS); + } + + addr_ct = MIN(stun_addr_ct, MAXADDRS); + r_log(LOG_ICE, LOG_DEBUG, "ICE(%s): copy %d pre-fetched stun addrs", ctx->label, addr_ct); + for (i=0; i<addr_ct; ++i) { + if (r=nr_local_addr_copy(&local_addrs[i], &stun_addrs[i])) { + ABORT(r); + } + } + + // removes duplicates and, based on prefs, loopback and link_local addrs + if (r=nr_stun_filter_local_addresses(local_addrs, &addr_ct)) { + ABORT(r); + } + + if (ctx->force_net_interface[0] && addr_ct) { + /* Limit us to only addresses on a single interface */ + int force_addr_ct = 0; + for(i=0;i<addr_ct;i++){ + if (!strcmp(local_addrs[i].addr.ifname, ctx->force_net_interface)) { + // copy it down in the array, if needed + if (i != force_addr_ct) { + if (r=nr_local_addr_copy(&local_addrs[force_addr_ct], &local_addrs[i])) { + ABORT(r); + } + } + force_addr_ct++; + } + } + addr_ct = force_addr_ct; + } + + r_log(LOG_ICE, LOG_DEBUG, + "ICE(%s): use only default local addresses: %s\n", + ctx->label, + (char*)(ctx->flags & NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS?"yes":"no")); + if ((!addr_ct) || (ctx->flags & NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS)) { + if (ctx->target_for_default_local_address_lookup) { + /* Get just the default IPv4 or IPv6 addr */ + if(!nr_ice_get_default_local_address( + ctx, ctx->target_for_default_local_address_lookup->ip_version, + local_addrs, addr_ct, &default_addrs[default_addr_ct])) { + nr_local_addr *new_addr = &default_addrs[default_addr_ct]; + + ++default_addr_ct; + + /* If we have a default target address, check for an associated + address on the same interface. For example, if the default + target address is IPv6, this will find an associated IPv4 + address on the same interface. + This makes ICE w/ dual stacks work better - Bug 1609124. + */ + if(!nr_ice_get_assoc_interface_address( + new_addr, local_addrs, addr_ct, + &default_addrs[default_addr_ct])) { + ++default_addr_ct; + } + } + } else { + /* Get just the default IPv4 and IPv6 addrs */ + if(!nr_ice_get_default_local_address(ctx, NR_IPV4, local_addrs, addr_ct, + &default_addrs[default_addr_ct])) { + ++default_addr_ct; + } + if(!nr_ice_get_default_local_address(ctx, NR_IPV6, local_addrs, addr_ct, + &default_addrs[default_addr_ct])) { + ++default_addr_ct; + } + } + if (!default_addr_ct) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): failed to find default addresses",ctx->label); + ABORT(R_FAILED); + } + addrs = default_addrs; + addr_ct = default_addr_ct; + } + else { + addrs = local_addrs; + } + + /* Sort interfaces by preference */ + if(ctx->interface_prioritizer) { + for(i=0;i<addr_ct;i++){ + if((r=nr_interface_prioritizer_add_interface(ctx->interface_prioritizer,addrs+i)) && (r!=R_ALREADY)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to add interface ",ctx->label); + ABORT(r); + } + } + if(r=nr_interface_prioritizer_sort_preference(ctx->interface_prioritizer)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to sort interface by preference",ctx->label); + ABORT(r); + } + } + + if (r=nr_ice_ctx_set_local_addrs(ctx,addrs,addr_ct)) { + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_set_target_for_default_local_address_lookup(nr_ice_ctx *ctx, const char *target_ip, UINT2 target_port) + { + int r,_status; + + if (ctx->target_for_default_local_address_lookup) { + RFREE(ctx->target_for_default_local_address_lookup); + ctx->target_for_default_local_address_lookup=0; + } + + if (!(ctx->target_for_default_local_address_lookup=RCALLOC(sizeof(nr_transport_addr)))) + ABORT(R_NO_MEMORY); + + if ((r=nr_str_port_to_transport_addr(target_ip, target_port, IPPROTO_UDP, ctx->target_for_default_local_address_lookup))) { + RFREE(ctx->target_for_default_local_address_lookup); + ctx->target_for_default_local_address_lookup=0; + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg) + { + int r,_status; + nr_ice_media_stream *stream; + nr_local_addr stun_addrs[MAXADDRS]; + int stun_addr_ct; + + if (!ctx->local_addrs) { + if((r=nr_stun_find_local_addresses(stun_addrs,MAXADDRS,&stun_addr_ct))) { + ABORT(r); + } + if((r=nr_ice_set_local_addresses(ctx,stun_addrs,stun_addr_ct))) { + ABORT(r); + } + } + + if(STAILQ_EMPTY(&ctx->streams)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Missing streams to initialize",ctx->label); + ABORT(R_BAD_ARGS); + } + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Initializing candidates",ctx->label); + ctx->done_cb=done_cb; + ctx->cb_arg=cb_arg; + + /* Initialize all the media stream/component pairs */ + stream=STAILQ_FIRST(&ctx->streams); + while(stream){ + if(!stream->obsolete) { + if(r=nr_ice_media_stream_initialize(ctx,stream)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Failed to initialize a stream; this might not be an unrecoverable error, if this stream will be marked obsolete soon due to an ICE restart.",ctx->label); + } + } + + stream=STAILQ_NEXT(stream,entry); + } + + if(ctx->uninitialized_candidates) + ABORT(R_WOULDBLOCK); + + _status=0; + abort: + return(_status); + } + +int nr_ice_add_media_stream(nr_ice_ctx *ctx,const char *label,const char *ufrag,const char *pwd,int components, nr_ice_media_stream **streamp) + { + int r,_status; + + if(r=nr_ice_media_stream_create(ctx,label,ufrag,pwd,components,streamp)) + ABORT(r); + + STAILQ_INSERT_TAIL(&ctx->streams,*streamp,entry); + + _status=0; + abort: + return(_status); + } + +int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp) + { + int r,_status; + nr_ice_peer_ctx *pctx; + nr_ice_media_stream *peer_stream; + + pctx=STAILQ_FIRST(&ctx->peers); + while(pctx){ + if(!nr_ice_peer_ctx_find_pstream(pctx, *streamp, &peer_stream)) { + if(r=nr_ice_peer_ctx_remove_pstream(pctx, &peer_stream)) { + ABORT(r); + } + } + + pctx=STAILQ_NEXT(pctx,entry); + } + + STAILQ_REMOVE(&ctx->streams,*streamp,nr_ice_media_stream_,entry); + if(r=nr_ice_media_stream_destroy(streamp)) { + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp) + { + *attrctp=0; + *attrsp=0; + return(0); + } + +static int nr_ice_random_string(char *str, int len) + { + unsigned char bytes[100]; + size_t needed; + int r,_status; + + if(len%2) ABORT(R_BAD_ARGS); + needed=len/2; + + if(needed>sizeof(bytes)) ABORT(R_BAD_ARGS); + + if(r=nr_crypto_random_bytes(bytes,needed)) + ABORT(r); + + if(r=nr_bin2hex(bytes,needed,(unsigned char *)str)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +/* This is incredibly annoying: we now have a datagram but we don't + know which peer it's from, and we need to be able to tell the + API user. So, offer it to each peer and if one bites, assume + the others don't want it +*/ +int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len) + { + nr_ice_peer_ctx *pctx; + int r; + + pctx=STAILQ_FIRST(&ctx->peers); + while(pctx){ + r=nr_ice_peer_ctx_deliver_packet_maybe(pctx, comp, source_addr, data, len); + if(!r) + break; + + pctx=STAILQ_NEXT(pctx,entry); + } + + if(!pctx) + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Packet received from %s which doesn't match any known peer",ctx->label,source_addr->as_string); + + return(0); + } + +int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]) + { + nr_ice_stun_id *xid; + + xid=STAILQ_FIRST(&ctx->ids); + while(xid){ + if (!memcmp(xid->id, id, 12)) + return 1; + + xid=STAILQ_NEXT(xid,entry); + } + + return 0; + } + +int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg) +{ + int _status; + nr_ice_stun_id *xid; + + xid = RCALLOC(sizeof(*xid)); + if (!xid) + ABORT(R_NO_MEMORY); + + assert(sizeof(xid->id) == sizeof(msg->header.id)); +#if __STDC_VERSION__ >= 201112L + _Static_assert(sizeof(xid->id) == sizeof(msg->header.id),"Message ID Size Mismatch"); +#endif + memcpy(xid->id, &msg->header.id, sizeof(xid->id)); + + STAILQ_INSERT_TAIL(&ctx->ids,xid,entry); + + _status=0; + abort: + return(_status); +} + + +/* Clean up some of the resources (mostly file descriptors) used + by candidates we didn't choose. Note that this still leaves + a fair amount of non-system stuff floating around. This gets + cleaned up when you destroy the ICE ctx */ +int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx) + { + nr_ice_media_stream *lstr,*rstr; + + r_log(LOG_ICE,LOG_DEBUG,"Finalizing ICE ctx %s, peer=%s",ctx->label,pctx->label); + /* + First find the peer stream, if any + */ + lstr=STAILQ_FIRST(&ctx->streams); + while(lstr){ + rstr=STAILQ_FIRST(&pctx->peer_streams); + + while(rstr){ + if(rstr->local_stream==lstr) + break; + + rstr=STAILQ_NEXT(rstr,entry); + } + + nr_ice_media_stream_finalize(lstr,rstr); + + lstr=STAILQ_NEXT(lstr,entry); + } + + return(0); + } + + +int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg) +{ + ctx->trickle_cb = cb; + ctx->trickle_cb_arg = cb_arg; + + return 0; +} + +int nr_ice_ctx_hide_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand) + { + if (cand->state != NR_ICE_CAND_STATE_INITIALIZED) { + return 1; + } + + if (ctx->flags & NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES) { + if (cand->type == HOST) + return 1; + } + + if (cand->stream->obsolete) { + return 1; + } + + return 0; + } + +int nr_ice_get_new_ice_ufrag(char** ufrag) + { + int r,_status; + char buf[ICE_UFRAG_LEN+1]; + + if(r=nr_ice_random_string(buf,ICE_UFRAG_LEN)) + ABORT(r); + if(!(*ufrag=r_strdup(buf))) + ABORT(r); + + _status=0; + abort: + if(_status) { + RFREE(*ufrag); + *ufrag = 0; + } + return(_status); + } + +int nr_ice_get_new_ice_pwd(char** pwd) + { + int r,_status; + char buf[ICE_PWD_LEN+1]; + + if(r=nr_ice_random_string(buf,ICE_PWD_LEN)) + ABORT(r); + if(!(*pwd=r_strdup(buf))) + ABORT(r); + + _status=0; + abort: + if(_status) { + RFREE(*pwd); + *pwd = 0; + } + return(_status); + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h new file mode 100644 index 0000000000..8b3081f567 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h @@ -0,0 +1,188 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_ctx_h +#define _ice_ctx_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +/* Not good practice but making includes simpler */ +#include "transport_addr.h" +#include "nr_socket.h" +#include "nr_resolver.h" +#include "nr_interface_prioritizer.h" +#include "nr_socket_wrapper.h" +#include "stun_client_ctx.h" +#include "stun_server_ctx.h" +#include "turn_client_ctx.h" + +typedef struct nr_ice_foundation_ { + int index; + + nr_transport_addr addr; + int type; + /* ICE spec says that we only compare IP address, not port */ + nr_transport_addr stun_server_addr; + + STAILQ_ENTRY(nr_ice_foundation_) entry; +} nr_ice_foundation; + +typedef STAILQ_HEAD(nr_ice_foundation_head_,nr_ice_foundation_) nr_ice_foundation_head; + +typedef TAILQ_HEAD(nr_ice_candidate_head_,nr_ice_candidate_) nr_ice_candidate_head; +typedef TAILQ_HEAD(nr_ice_cand_pair_head_,nr_ice_cand_pair_) nr_ice_cand_pair_head; +typedef struct nr_ice_component_ nr_ice_component; +typedef struct nr_ice_media_stream_ nr_ice_media_stream; +typedef struct nr_ice_ctx_ nr_ice_ctx; +typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx; +typedef struct nr_ice_candidate_ nr_ice_candidate; +typedef struct nr_ice_cand_pair_ nr_ice_cand_pair; +typedef void (*nr_ice_trickle_candidate_cb) (void *cb_arg, + nr_ice_ctx *ctx, nr_ice_media_stream *stream, int component_id, + nr_ice_candidate *candidate); + +#include "ice_socket.h" +#include "ice_component.h" +#include "ice_media_stream.h" +#include "ice_candidate.h" +#include "ice_candidate_pair.h" +#include "ice_handler.h" +#include "ice_peer_ctx.h" + +typedef struct nr_ice_stun_id_ { + UCHAR id[12]; + + STAILQ_ENTRY(nr_ice_stun_id_) entry; +} nr_ice_stun_id; + +typedef STAILQ_HEAD(nr_ice_stun_id_head_,nr_ice_stun_id_) nr_ice_stun_id_head; + +typedef struct nr_ice_stats_ { + UINT2 stun_retransmits; + UINT2 turn_401s; + UINT2 turn_403s; + UINT2 turn_438s; +} nr_ice_stats; + +struct nr_ice_ctx_ { + UINT4 flags; + char *label; + + UINT4 Ta; + + nr_ice_stun_server *stun_servers_cfg; /* The list of stun servers */ + int stun_server_ct_cfg; + nr_ice_turn_server *turn_servers_cfg; /* The list of turn servers */ + int turn_server_ct_cfg; + nr_local_addr *local_addrs; /* The list of available local addresses and corresponding interface information */ + int local_addr_ct; + + nr_resolver *resolver; /* The resolver to use */ + nr_interface_prioritizer *interface_prioritizer; /* Priority decision logic */ + nr_socket_factory *socket_factory; + + nr_ice_foundation_head foundations; + + nr_ice_media_stream_head streams; /* Media streams */ + int stream_ct; + nr_ice_socket_head sockets; /* The sockets we're using */ + int uninitialized_candidates; + + UINT4 gather_rto; + UINT4 stun_delay; + + UINT4 test_timer_divider; + + nr_ice_peer_ctx_head peers; + nr_ice_stun_id_head ids; + + NR_async_cb done_cb; + void *cb_arg; + + nr_ice_trickle_candidate_cb trickle_cb; + void *trickle_cb_arg; + + char force_net_interface[MAXIFNAME]; + nr_ice_stats stats; + + nr_transport_addr *target_for_default_local_address_lookup; +}; + +int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp); +int nr_ice_ctx_create_with_credentials(char *label, UINT4 flags, char* ufrag, char* pwd, nr_ice_ctx **ctxp); +#define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION (1) +#define NR_ICE_CTX_FLAGS_LITE (1<<1) +#define NR_ICE_CTX_FLAGS_RELAY_ONLY (1<<2) +#define NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES (1<<3) +#define NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS (1<<4) +#define NR_ICE_CTX_FLAGS_ONLY_PROXY (1<<5) +#define NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES (1<<6) + +void nr_ice_ctx_add_flags(nr_ice_ctx *ctx, UINT4 flags); +void nr_ice_ctx_remove_flags(nr_ice_ctx *ctx, UINT4 flags); +void nr_ice_ctx_destroy(nr_ice_ctx** ctxp); +int nr_ice_set_local_addresses(nr_ice_ctx *ctx, nr_local_addr* stun_addrs, int stun_addr_ct); +int nr_ice_set_target_for_default_local_address_lookup(nr_ice_ctx *ctx, const char *target_ip, UINT2 target_port); +int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg); +int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand); +void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg); +int nr_ice_add_media_stream(nr_ice_ctx *ctx,const char *label,const char *ufrag,const char *pwd,int components, nr_ice_media_stream **streamp); +int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp); +int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp); +int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len); +int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]); +int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg); +int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx); +int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers, int ct); +int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers, int ct); +int nr_ice_ctx_copy_turn_servers(nr_ice_ctx *ctx, nr_ice_turn_server *servers, int ct); +int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver); +int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *prioritizer); +void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory); +int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg); +int nr_ice_ctx_hide_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand); +int nr_ice_get_new_ice_ufrag(char** ufrag); +int nr_ice_get_new_ice_pwd(char** pwd); + +#define NR_ICE_MAX_ATTRIBUTE_SIZE 256 + +extern int LOG_ICE; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h new file mode 100644 index 0000000000..5a0690adad --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h @@ -0,0 +1,84 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_h +#define _ice_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +typedef struct nr_ice_handler_vtbl_ { + /* The checks on this media stream are done. The handler needs to + select a single pair to proceed with (regular nomination). + Once this returns the check starts and the pair can be + written on. Use nr_ice_candidate_pair_select() to perform the + selection. + TODO: !ekr! is this right? + */ + int (*select_pair)(void *obj,nr_ice_media_stream *stream, +int component_id, nr_ice_cand_pair **potentials,int potential_ct); + + /* This media stream is ready to read/write (aggressive nomination). + May be called again if the nominated pair changes due to + ICE instability. TODO: !ekr! think about this + */ + int (*stream_ready)(void *obj, nr_ice_media_stream *stream); + + /* This media stream has failed */ + int (*stream_failed)(void *obj, nr_ice_media_stream *stream); + + /* ICE is connected for this peer ctx */ + int (*ice_connected)(void *obj, nr_ice_peer_ctx *pctx); + + /* A message was delivered to us */ + int (*msg_recvd)(void *obj, nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int component_id, UCHAR *msg, int len); + + /* ICE has started checking. */ + int (*ice_checking)(void *obj, nr_ice_peer_ctx *pctx); + + /* ICE detected a (temporary?) disconnect. */ + int (*ice_disconnected)(void *obj, nr_ice_peer_ctx *pctx); +} nr_ice_handler_vtbl; + +typedef struct nr_ice_handler_ { + void *obj; + nr_ice_handler_vtbl *vtbl; +} nr_ice_handler; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c new file mode 100644 index 0000000000..62bfbad629 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c @@ -0,0 +1,1087 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <string.h> +#include <assert.h> +#include <nr_api.h> +#include <r_assoc.h> +#include <async_timer.h> +#include "ice_util.h" +#include "ice_ctx.h" + +static char *nr_ice_media_stream_states[]={"INVALID", + "UNPAIRED","FROZEN","ACTIVE","CONNECTED","FAILED" +}; + +int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state); + +int nr_ice_media_stream_create(nr_ice_ctx *ctx,const char *label,const char *ufrag,const char *pwd,int components, nr_ice_media_stream **streamp) + { + int r,_status; + nr_ice_media_stream *stream=0; + nr_ice_component *comp=0; + int i; + + if(!(stream=RCALLOC(sizeof(nr_ice_media_stream)))) + ABORT(R_NO_MEMORY); + + if(!(stream->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + if(!(stream->ufrag=r_strdup(ufrag))) + ABORT(R_NO_MEMORY); + + if(!(stream->pwd=r_strdup(pwd))) + ABORT(R_NO_MEMORY); + + stream->ctx=ctx; + + STAILQ_INIT(&stream->components); + for(i=0;i<components;i++){ + /* component-id must be > 0, so increment by 1 */ + if(r=nr_ice_component_create(stream, i+1, &comp)) + ABORT(r); + + } + + TAILQ_INIT(&stream->check_list); + TAILQ_INIT(&stream->trigger_check_queue); + + stream->disconnected = 0; + stream->component_ct=components; + stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED; + stream->obsolete = 0; + stream->r2l_user = 0; + stream->l2r_user = 0; + stream->flags = ctx->flags; + if(ctx->stun_server_ct_cfg) { + if(!(stream->stun_servers=RCALLOC(sizeof(nr_ice_stun_server)*(ctx->stun_server_ct_cfg)))) + ABORT(R_NO_MEMORY); + + memcpy(stream->stun_servers,ctx->stun_servers_cfg,sizeof(nr_ice_stun_server)*(ctx->stun_server_ct_cfg)); + stream->stun_server_ct = ctx->stun_server_ct_cfg; + } + + if(ctx->turn_server_ct_cfg) { + if(!(stream->turn_servers=RCALLOC(sizeof(nr_ice_turn_server)*(ctx->turn_server_ct_cfg)))) + ABORT(R_NO_MEMORY); + + for(int i = 0; i < ctx->turn_server_ct_cfg; ++i) { + nr_ice_turn_server *dst = &stream->turn_servers[i]; + nr_ice_turn_server *src = &ctx->turn_servers_cfg[i]; + memcpy(&dst->turn_server, &src->turn_server, sizeof(nr_ice_stun_server)); + dst->username = r_strdup(src->username); + r_data_create(&dst->password, src->password->data, src->password->len); + } + stream->turn_server_ct = ctx->turn_server_ct_cfg; + } + + r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): flags %d",stream->label,stream->flags); + *streamp=stream; + + _status=0; + abort: + if(_status){ + nr_ice_media_stream_destroy(&stream); + } + return(_status); + } + +int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp) + { + nr_ice_media_stream *stream; + nr_ice_component *c1,*c2; + nr_ice_cand_pair *p1,*p2; + if(!streamp || !*streamp) + return(0); + + stream=*streamp; + *streamp=0; + + STAILQ_FOREACH_SAFE(c1, &stream->components, entry, c2){ + STAILQ_REMOVE(&stream->components,c1,nr_ice_component_,entry); + nr_ice_component_destroy(&c1); + } + + /* Note: all the entries from the trigger check queue are held in here as + * well, so we only clean up the super set. */ + TAILQ_FOREACH_SAFE(p1, &stream->check_list, check_queue_entry, p2){ + TAILQ_REMOVE(&stream->check_list,p1,check_queue_entry); + nr_ice_candidate_pair_destroy(&p1); + } + + RFREE(stream->label); + + RFREE(stream->ufrag); + RFREE(stream->pwd); + RFREE(stream->r2l_user); + RFREE(stream->l2r_user); + r_data_zfree(&stream->r2l_pass); + r_data_zfree(&stream->l2r_pass); + + RFREE(stream->stun_servers); + for (int i = 0; i < stream->turn_server_ct; i++) { + RFREE(stream->turn_servers[i].username); + r_data_destroy(&stream->turn_servers[i].password); + } + RFREE(stream->turn_servers); + + if(stream->timer) + NR_async_timer_cancel(stream->timer); + + RFREE(stream); + + return(0); + } + +int nr_ice_media_stream_initialize(nr_ice_ctx *ctx, nr_ice_media_stream *stream) + { + int r,_status; + nr_ice_component *comp; + + assert(!stream->obsolete); + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if(r=nr_ice_component_initialize(ctx,comp)) + ABORT(r); + comp=STAILQ_NEXT(comp,entry); + } + + _status=0; + abort: + return(_status); + } + + +int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp, int *attrctp) + { + int attrct=2; + nr_ice_component *comp; + char **attrs=0; + int index=0; + nr_ice_candidate *cand; + int r,_status; + char *tmp=0; + + *attrctp=0; + + /* First find out how many attributes we need */ + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if (comp->state != NR_ICE_COMPONENT_DISABLED) { + cand = TAILQ_FIRST(&comp->candidates); + while(cand){ + if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) { + ++attrct; + } + + cand = TAILQ_NEXT(cand, entry_comp); + } + } + comp=STAILQ_NEXT(comp,entry); + } + + /* Make the array we'll need */ + if(!(attrs=RCALLOC(sizeof(char *)*attrct))) + ABORT(R_NO_MEMORY); + for(index=0;index<attrct;index++){ + if(!(attrs[index]=RMALLOC(NR_ICE_MAX_ATTRIBUTE_SIZE))) + ABORT(R_NO_MEMORY); + } + + index=0; + /* Now format the attributes */ + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if (comp->state != NR_ICE_COMPONENT_DISABLED) { + nr_ice_candidate *cand; + + cand=TAILQ_FIRST(&comp->candidates); + while(cand){ + if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) { + assert(index < attrct); + + if (index >= attrct) + ABORT(R_INTERNAL); + + if(r=nr_ice_format_candidate_attribute(cand, attrs[index],NR_ICE_MAX_ATTRIBUTE_SIZE, 0)) + ABORT(r); + + index++; + } + + cand=TAILQ_NEXT(cand,entry_comp); + } + } + comp=STAILQ_NEXT(comp,entry); + } + + /* Now, ufrag and pwd */ + if(!(tmp=RMALLOC(100))) + ABORT(R_NO_MEMORY); + snprintf(tmp,100,"ice-ufrag:%s",stream->ufrag); + attrs[index++]=tmp; + + if(!(tmp=RMALLOC(100))) + ABORT(R_NO_MEMORY); + snprintf(tmp,100,"ice-pwd:%s",stream->pwd); + attrs[index++]=tmp; + + *attrsp=attrs; + *attrctp=attrct; + + _status=0; + abort: + if(_status){ + if(attrs){ + for(index=0;index<attrct;index++){ + RFREE(attrs[index]); + } + RFREE(attrs); + } + } + return(_status); + } + +/* Get a default candidate per 4.1.4 */ +int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp) + { + int r,_status; + nr_ice_component *comp; + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if (comp->component_id == component) + break; + + comp=STAILQ_NEXT(comp,entry); + } + + if (!comp) + ABORT(R_NOT_FOUND); + + /* If there aren't any IPV4 candidates, try IPV6 */ + if((r=nr_ice_component_get_default_candidate(comp, candp, NR_IPV4)) && + (r=nr_ice_component_get_default_candidate(comp, candp, NR_IPV6))) { + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + + +int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream) + { + int r,_status; + nr_ice_component *pcomp,*lcomp; + + pcomp=STAILQ_FIRST(&pstream->components); + lcomp=STAILQ_FIRST(&lstream->components); + while(pcomp){ + if ((lcomp->state != NR_ICE_COMPONENT_DISABLED) && + (pcomp->state != NR_ICE_COMPONENT_DISABLED)) { + if(r=nr_ice_component_pair_candidates(pctx,lcomp,pcomp)) + ABORT(r); + } + + lcomp=STAILQ_NEXT(lcomp,entry); + pcomp=STAILQ_NEXT(pcomp,entry); + }; + + if (pstream->ice_state == NR_ICE_MEDIA_STREAM_UNPAIRED) { + nr_ice_media_stream_set_state(pstream, NR_ICE_MEDIA_STREAM_CHECKS_FROZEN); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, nr_ice_media_stream *pstream, int *serviced) + { + nr_ice_component *pcomp; + int r,_status; + char *user = 0; + + if (serviced) + *serviced = 0; + + pcomp=STAILQ_FIRST(&pstream->components); + while(pcomp){ + int serviced_inner=0; + + /* Flush all the pre-answer requests */ + if(r=nr_ice_component_service_pre_answer_requests(pctx, pcomp, pstream->r2l_user, &serviced_inner)) + ABORT(r); + if (serviced) + *serviced += serviced_inner; + + pcomp=STAILQ_NEXT(pcomp,entry); + } + + _status=0; + abort: + RFREE(user); + return(_status); + } + +/* S 5.8 -- run the first pair from the triggered check queue (even after + * checks have completed S 8.1.2) or run the highest priority WAITING pair or + * if not available FROZEN pair from the check queue */ +static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg) + { + int r,_status; + nr_ice_media_stream *stream=cb_arg; + nr_ice_cand_pair *pair = 0; + int timer_multiplier=stream->pctx->active_streams ? stream->pctx->active_streams : 1; + int timer_val=stream->pctx->ctx->Ta*timer_multiplier; + + assert(timer_val>0); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer expired for media stream %s",stream->pctx->label,stream->label); + stream->timer=0; + + /* The trigger check queue has the highest priority */ + pair=TAILQ_FIRST(&stream->trigger_check_queue); + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_WAITING){ + /* Remove the pair from he trigger check queue */ + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Removing pair from trigger check queue %s",stream->pctx->label,pair->as_string); + TAILQ_REMOVE(&stream->trigger_check_queue,pair,triggered_check_queue_entry); + break; + } + pair=TAILQ_NEXT(pair,triggered_check_queue_entry); + } + + if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) { + if(!pair){ + /* Find the highest priority WAITING check and move it to RUNNING */ + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_WAITING) + break; + pair=TAILQ_NEXT(pair,check_queue_entry); + } + } + + /* Hmmm... No WAITING. Let's look for FROZEN */ + if(!pair){ + pair=TAILQ_FIRST(&stream->check_list); + + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_FROZEN){ + if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair)) + ABORT(r); + break; + } + pair=TAILQ_NEXT(pair,check_queue_entry); + } + } + } + + if(pair){ + nr_ice_candidate_pair_start(pair->pctx,pair); /* Ignore failures */ + NR_ASYNC_TIMER_SET(timer_val,nr_ice_media_stream_check_timer_cb,cb_arg,&stream->timer); + } + else { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): no FROZEN/WAITING pairs for %s",stream->pctx->label,stream->label); + } + + _status=0; + abort: + if (_status) { + // cb doesn't return anything, but we should probably log that we aborted + // This also quiets the unused variable warnings. + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer cb for media stream %s abort with status: %d", + stream->pctx->label,stream->label, _status); + } + return; + } + +/* Start checks for this media stream (aka check list) */ +int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + int r,_status; + + /* Don't start the check timer if the stream is failed */ + if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) { + assert(0); + ABORT(R_INTERNAL); + } + + if (stream->local_stream->obsolete) { + assert(0); + ABORT(R_INTERNAL); + } + + /* Even if the stream is completed already remote can still create a new + * triggered check request which needs to fire, but not change our stream + * state. */ + if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) { + if(r=nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)) { + ABORT(r); + } + } + + if (!stream->timer) { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): Starting check timer for stream.",pctx->label,stream->label); + nr_ice_media_stream_check_timer_cb(0,0,stream); + } + + nr_ice_peer_ctx_stream_started_checks(pctx, stream); + + _status=0; + abort: + return(_status); + } + +/* Start checks for this media stream (aka check list) S 5.7 */ +int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + int r,_status; + r_assoc *assoc=0; + nr_ice_cand_pair *pair=0; + + /* Already seen assoc */ + if(r=r_assoc_create(&assoc,r_assoc_crc32_hash_compute,5)) + ABORT(r); + + /* S 5.7.4. Set the highest priority pairs in each foundation to WAITING */ + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + void *v; + + if(r=r_assoc_fetch(assoc,pair->foundation,strlen(pair->foundation),&v)){ + if(r!=R_NOT_FOUND) + ABORT(r); + if(r=nr_ice_candidate_pair_unfreeze(pctx,pair)) + ABORT(r); + + if(r=r_assoc_insert(assoc,pair->foundation,strlen(pair->foundation), + 0,0,0,R_ASSOC_NEW)) + ABORT(r); + } + + /* Already exists... fall through */ + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + _status=0; + abort: + r_assoc_destroy(&assoc); + return(_status); + } + +static int nr_ice_media_stream_unfreeze_pairs_match(nr_ice_media_stream *stream, char *foundation) + { + nr_ice_cand_pair *pair; + int r,_status; + int unfroze=0; + + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_FROZEN && + !strcmp(foundation,pair->foundation)){ + if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair)) + ABORT(r); + unfroze++; + } + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + if(!unfroze) + return(R_NOT_FOUND); + + _status=0; + abort: + return(_status); + } + +/* S 7.1.2.2 */ +int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation) + { + int r,_status; + nr_ice_media_stream *str; + + /* 1. Unfreeze all frozen pairs with the same foundation + in this stream */ + if(r=nr_ice_media_stream_unfreeze_pairs_match(stream,foundation)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + + /* Now go through the check lists for the other streams */ + str=STAILQ_FIRST(&stream->pctx->peer_streams); + while(str){ + if(str!=stream && !str->local_stream->obsolete){ + switch(str->ice_state){ + case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE: + /* Unfreeze matching pairs */ + if(r=nr_ice_media_stream_unfreeze_pairs_match(str,foundation)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + break; + case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN: + /* Unfreeze matching pairs if any */ + r=nr_ice_media_stream_unfreeze_pairs_match(str,foundation); + if(r){ + if(r!=R_NOT_FOUND) + ABORT(r); + + /* OK, no matching pairs: execute the algorithm from 5.7 + for this stream */ + if(r=nr_ice_media_stream_unfreeze_pairs(str->pctx,str)) + ABORT(r); + } + if(r=nr_ice_media_stream_start_checks(str->pctx,str)) + ABORT(r); + + break; + default: + break; + } + } + + str=STAILQ_NEXT(str,entry); + } + + _status=0; + abort: + return(_status); + } + + +void nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int log_level) + { + nr_ice_cand_pair *pair; + nr_ice_component *comp; + + if(stream->local_stream){ + /* stream has a corresponding local_stream */ + nr_ice_media_stream_dump_state(stream->local_stream->pctx,stream->local_stream, log_level); + r_log(LOG_ICE,log_level,"ICE-PEER(%s)/STREAM(%s): state dump", stream->pctx->label, stream->label); + } else { + r_log(LOG_ICE,log_level,"ICE(%s)/STREAM(%s): state dump", stream->ctx->label, stream->label); + } + + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + nr_ice_candidate_pair_dump_state(pair, log_level); + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + nr_ice_component_dump_state(comp, log_level); + comp=STAILQ_NEXT(comp,entry); + } + } + +int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state) + { + /* Make no-change a no-op */ + if (state == str->ice_state) + return 0; + + assert((size_t)state < sizeof(nr_ice_media_stream_states)/sizeof(char *)); + assert((size_t)str->ice_state < sizeof(nr_ice_media_stream_states)/sizeof(char *)); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): stream %s state %s->%s", + str->pctx->label,str->label, + nr_ice_media_stream_states[str->ice_state], + nr_ice_media_stream_states[state]); + + if(state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE) + str->pctx->active_streams++; + if(str->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE) + str->pctx->active_streams--; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): %d active streams", + str->pctx->label, str->pctx->active_streams); + + str->ice_state=state; + if (state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) { + nr_ice_media_stream_dump_state(str->pctx,str,LOG_ERR); + } + + return(0); + } + +void nr_ice_media_stream_stop_checking(nr_ice_media_stream *str) + { + nr_ice_cand_pair *p; + nr_ice_component *comp; + + /* Cancel candidate pairs */ + p=TAILQ_FIRST(&str->check_list); + while(p){ + nr_ice_candidate_pair_cancel(p->pctx,p,0); + p=TAILQ_NEXT(p,check_queue_entry); + } + + if(str->timer) { + NR_async_timer_cancel(str->timer); + str->timer = 0; + } + + /* Cancel consent timers in case it is running already */ + comp=STAILQ_FIRST(&str->components); + while(comp){ + nr_ice_component_consent_destroy(comp); + comp=STAILQ_NEXT(comp,entry); + } + } + +void nr_ice_media_stream_set_obsolete(nr_ice_media_stream *str) + { + nr_ice_component *c1,*c2; + str->obsolete = 1; + + STAILQ_FOREACH_SAFE(c1, &str->components, entry, c2){ + nr_ice_component_stop_gathering(c1); + } + + nr_ice_media_stream_stop_checking(str); + } + +int nr_ice_media_stream_is_done_gathering(nr_ice_media_stream *str) + { + nr_ice_component *comp; + comp=STAILQ_FIRST(&str->components); + while(comp){ + if(!nr_ice_component_is_done_gathering(comp)) { + return 0; + } + comp=STAILQ_NEXT(comp,entry); + } + return 1; + } + +void nr_ice_media_stream_refresh_consent_all(nr_ice_media_stream *stream) + { + nr_ice_component *comp; + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if(comp->disconnected) { + nr_ice_component_refresh_consent_now(comp); + } + + comp=STAILQ_NEXT(comp,entry); + } + } + +void nr_ice_media_stream_disconnect_all_components(nr_ice_media_stream *stream) + { + nr_ice_component *comp; + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + comp->disconnected = 1; + + comp=STAILQ_NEXT(comp,entry); + } + } + +void nr_ice_media_stream_set_disconnected(nr_ice_media_stream *stream, int disconnected) + { + if (stream->disconnected == disconnected) { + return; + } + + if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) { + return; + } + stream->disconnected = disconnected; + + if (disconnected == NR_ICE_MEDIA_STREAM_DISCONNECTED) { + if (!stream->local_stream->obsolete) { + nr_ice_peer_ctx_disconnected(stream->pctx); + } + } else { + nr_ice_peer_ctx_check_if_connected(stream->pctx); + } + } + +int nr_ice_media_stream_check_if_connected(nr_ice_media_stream *stream) + { + nr_ice_component *comp; + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if((comp->state != NR_ICE_COMPONENT_DISABLED) && + (comp->local_component->state != NR_ICE_COMPONENT_DISABLED) && + comp->disconnected) + break; + + comp=STAILQ_NEXT(comp,entry); + } + + /* At least one disconnected component */ + if(comp) + goto done; + + nr_ice_media_stream_set_disconnected(stream, NR_ICE_MEDIA_STREAM_CONNECTED); + + done: + return(0); + } + +/* S OK, this component has a nominated. If every component has a nominated, + the stream is ready */ +void nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component) + { + nr_ice_component *comp; + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if((comp->state != NR_ICE_COMPONENT_DISABLED) && + (comp->local_component->state != NR_ICE_COMPONENT_DISABLED) && + !comp->nominated) + break; + + comp=STAILQ_NEXT(comp,entry); + } + + /* At least one un-nominated component */ + if(comp) + return; + + /* All done... */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): all active components have nominated candidate pairs",stream->pctx->label,stream->label); + nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED); + + /* Cancel our timer */ + if(stream->timer){ + NR_async_timer_cancel(stream->timer); + stream->timer=0; + } + + if (stream->pctx->handler && !stream->local_stream->obsolete) { + stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream); + } + + /* Now tell the peer_ctx that we're connected */ + nr_ice_peer_ctx_check_if_connected(stream->pctx); + } + +void nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component) + { + component->state=NR_ICE_COMPONENT_FAILED; + + /* at least one component failed in this media stream, so the entire + * media stream is marked failed */ + + nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_FAILED); + + nr_ice_media_stream_stop_checking(stream); + + if (stream->pctx->handler && !stream->local_stream->obsolete) { + stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream); + } + + /* Now tell the peer_ctx that we've failed */ + nr_ice_peer_ctx_check_if_connected(stream->pctx); + } + +int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp) + { + nr_ice_candidate *cand; + nr_ice_candidate *best_cand=0; + nr_ice_component *comp; + int r,_status; + + if(r=nr_ice_media_stream_find_component(str,component,&comp)) + ABORT(r); + + cand=TAILQ_FIRST(&comp->candidates); + while(cand){ + if(cand->state==NR_ICE_CAND_STATE_INITIALIZED){ + if(!best_cand || (cand->priority>best_cand->priority)) + best_cand=cand; + + } + cand=TAILQ_NEXT(cand,entry_comp); + } + + if(!best_cand) + ABORT(R_NOT_FOUND); + + *candp=best_cand; + + _status=0; + abort: + return(_status); + } + + +/* OK, we have the stream the user created, but that reflects the base + ICE ctx, not the peer_ctx. So, find the related stream in the pctx, + and then find the component */ +int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp) + { + int _status; + nr_ice_component *comp; + + comp=STAILQ_FIRST(&str->components); + while(comp){ + if(comp->component_id==comp_id) + break; + + comp=STAILQ_NEXT(comp,entry); + } + if(!comp) + ABORT(R_NOT_FOUND); + + *compp=comp; + + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len) + { + int r,_status; + nr_ice_component *comp; + + /* First find the peer component */ + if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp)) + ABORT(r); + + /* Do we have an active pair yet? We should... */ + if(!comp->active) + ABORT(R_NOT_FOUND); + + /* Does fresh ICE consent exist? */ + if(!comp->can_send) + ABORT(R_FAILED); + + /* OK, write to that pair, which means: + 1. Use the socket on our local side. + 2. Use the address on the remote side + */ + if(r=nr_socket_sendto(comp->active->local->osock,data,len,0, + &comp->active->remote->addr)) { + if ((r==R_IO_ERROR) || (r==R_EOD)) { + nr_ice_component_disconnected(comp); + } + ABORT(r); + } + + // accumulate the sent bytes for the active candidate pair + comp->active->bytes_sent += len; + gettimeofday(&comp->active->last_sent, 0); + + _status=0; + abort: + return(_status); + } + +/* Returns R_REJECTED if the component is unpaired or has been disabled. */ +int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote) + { + int r,_status; + nr_ice_component *comp; + + /* First find the peer component */ + if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp)) + ABORT(r); + + if (comp->state == NR_ICE_COMPONENT_UNPAIRED || + comp->state == NR_ICE_COMPONENT_DISABLED) + ABORT(R_REJECTED); + + if(!comp->active) + ABORT(R_NOT_FOUND); + + if (local) *local = comp->active->local; + if (remote) *remote = comp->active->remote; + + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote) + { + int r,_status; + nr_ice_component *comp; + + /* First find the peer component */ + if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp)) + ABORT(r); + + /* Do we have an active pair yet? We should... */ + if(!comp->active) + ABORT(R_BAD_ARGS); + + /* Use the socket on our local side */ + if(r=nr_socket_getaddr(comp->active->local->osock,local)) + ABORT(r); + + /* Use the address on the remote side */ + if(r=nr_transport_addr_copy(remote,&comp->active->remote->addr)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + + + +int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr) + { + nr_ice_component *lcomp,*rcomp; + + r_log(LOG_ICE,LOG_DEBUG,"Finalizing media stream %s, peer=%s",lstr->label, + rstr?rstr->label:"NONE"); + + lcomp=STAILQ_FIRST(&lstr->components); + if(rstr) + rcomp=STAILQ_FIRST(&rstr->components); + else + rcomp=0; + + while(lcomp){ + nr_ice_component_finalize(lcomp,rcomp); + + lcomp=STAILQ_NEXT(lcomp,entry); + if(rcomp){ + rcomp=STAILQ_NEXT(rcomp,entry); + } + } + + return(0); + } + +int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand) + { + int r,_status; + nr_ice_component *comp; + + if ((r=nr_ice_media_stream_find_component(pstream, cand->component_id, &comp))) + ABORT(R_NOT_FOUND); + + if (r=nr_ice_component_pair_candidate(pctx, comp, cand, 1)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_get_consent_status(nr_ice_media_stream *stream, int +component_id, int *can_send, struct timeval *ts) + { + int r,_status; + nr_ice_component *comp; + + if ((r=nr_ice_media_stream_find_component(stream, component_id, &comp))) + ABORT(r); + + *can_send = comp->can_send; + ts->tv_sec = comp->consent_last_seen.tv_sec; + ts->tv_usec = comp->consent_last_seen.tv_usec; + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id) + { + int r,_status; + nr_ice_component *comp; + + if (stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED) + ABORT(R_FAILED); + + if ((r=nr_ice_media_stream_find_component(stream, component_id, &comp))) + ABORT(r); + + /* Can only disable before pairing */ + if (comp->state != NR_ICE_COMPONENT_UNPAIRED && + comp->state != NR_ICE_COMPONENT_DISABLED) + ABORT(R_FAILED); + + comp->state = NR_ICE_COMPONENT_DISABLED; + + _status=0; + abort: + return(_status); + } + +void nr_ice_media_stream_role_change(nr_ice_media_stream *stream) + { + nr_ice_cand_pair *pair,*temp_pair; + /* Changing role causes candidate pair priority to change, which requires + * re-sorting the check list. */ + nr_ice_cand_pair_head old_checklist; + + /* Move check_list to old_checklist (not POD, have to do the hard way) */ + TAILQ_INIT(&old_checklist); + TAILQ_FOREACH_SAFE(pair,&stream->check_list,check_queue_entry,temp_pair) { + TAILQ_REMOVE(&stream->check_list,pair,check_queue_entry); + TAILQ_INSERT_TAIL(&old_checklist,pair,check_queue_entry); + } + + /* Re-insert into the check list */ + TAILQ_FOREACH_SAFE(pair,&old_checklist,check_queue_entry,temp_pair) { + TAILQ_REMOVE(&old_checklist,pair,check_queue_entry); + nr_ice_candidate_pair_role_change(pair); + nr_ice_candidate_pair_insert(&stream->check_list,pair); + } + } + +int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *lcand, nr_ice_candidate *rcand, nr_ice_cand_pair **pair) + { + nr_ice_cand_pair_head *head = &str->check_list; + nr_ice_cand_pair *c1; + + c1=TAILQ_FIRST(head); + while(c1){ + if(c1->local == lcand && + c1->remote == rcand) { + *pair=c1; + return(0); + } + + c1=TAILQ_NEXT(c1,check_queue_entry); + } + + return(R_NOT_FOUND); + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h new file mode 100644 index 0000000000..99f906c100 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h @@ -0,0 +1,146 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_media_stream_h +#define _ice_media_stream_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +#include "transport_addr.h" + +typedef struct nr_ice_stun_server_ { + nr_transport_addr addr; + int id; +} nr_ice_stun_server; + +typedef struct nr_ice_turn_server_ { + nr_ice_stun_server turn_server; + char *username; + Data *password; +} nr_ice_turn_server; + +struct nr_ice_media_stream_ { + char *label; + struct nr_ice_ctx_ *ctx; + struct nr_ice_peer_ctx_ *pctx; + + struct nr_ice_media_stream_ *local_stream; /* used when this is a peer */ + int component_ct; + nr_ice_component_head components; + + char *ufrag; /* ICE username */ + char *pwd; /* ICE password */ + char *r2l_user; /* The username for incoming requests */ + char *l2r_user; /* The username for outgoing requests */ + Data r2l_pass; /* The password for incoming requests */ + Data l2r_pass; /* The password for outcoming requests */ + int ice_state; + /* The stream is being replaced by another, so it will not continue any ICE + * processing. If this stream is connected already, traffic can continue to + * flow for a limited time while the new stream gets ready. */ + int obsolete; + +#define NR_ICE_MEDIA_STREAM_UNPAIRED 1 +#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 2 +#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 3 +#define NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED 4 +#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 5 + + int disconnected; + +#define NR_ICE_MEDIA_STREAM_CONNECTED 0 +#define NR_ICE_MEDIA_STREAM_DISCONNECTED 1 + + /* Copy of flags field from nr_ice_ctx */ + int flags; + + /* Copy of STUN/TURN servers from nr_ice_ctx */ + nr_ice_stun_server *stun_servers; /* The list of stun servers */ + int stun_server_ct; + nr_ice_turn_server *turn_servers; /* The list of turn servers */ + int turn_server_ct; + + nr_ice_cand_pair_head check_list; + nr_ice_cand_pair_head trigger_check_queue; + void *timer; /* Check list periodic timer */ + +/* nr_ice_cand_pair_head valid_list; */ + + STAILQ_ENTRY(nr_ice_media_stream_) entry; +}; + +typedef STAILQ_HEAD(nr_ice_media_stream_head_,nr_ice_media_stream_) nr_ice_media_stream_head; + +int nr_ice_media_stream_create(struct nr_ice_ctx_ *ctx,const char *label,const char *ufrag,const char *pwd,int components, nr_ice_media_stream **streamp); +int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp); +int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr); +int nr_ice_media_stream_initialize(struct nr_ice_ctx_ *ctx, nr_ice_media_stream *stream); +int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp,int *attrctp); +int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp); +int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream); +int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); +int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream, int *serviced); +int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); +int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation); +void nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int log_level); +void nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component); +void nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component); +void nr_ice_media_stream_refresh_consent_all(nr_ice_media_stream *stream); +void nr_ice_media_stream_disconnect_all_components(nr_ice_media_stream *stream); +void nr_ice_media_stream_set_disconnected(nr_ice_media_stream *stream, int disconnected); +int nr_ice_media_stream_check_if_connected(nr_ice_media_stream *stream); +int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state); +void nr_ice_media_stream_stop_checking(nr_ice_media_stream *str); +void nr_ice_media_stream_set_obsolete(nr_ice_media_stream *str); +int nr_ice_media_stream_is_done_gathering(nr_ice_media_stream *str); +int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp); +int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len); +int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote); +int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp); +int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *local, nr_ice_candidate *remote, nr_ice_cand_pair **pair); +int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote); +int +nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr); +int nr_ice_media_stream_get_consent_status(nr_ice_media_stream *stream, int component_id, int *can_send, struct timeval *ts); +int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id); +int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand); +void nr_ice_media_stream_role_change(nr_ice_media_stream *stream); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_parser.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_parser.c new file mode 100644 index 0000000000..25cda3364a --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_parser.c @@ -0,0 +1,564 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <csi_platform.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <strings.h> +#endif +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include "nr_api.h" +#include "ice_ctx.h" +#include "ice_candidate.h" +#include "ice_reg.h" + +static void +skip_whitespace(char **str) +{ + char *c = *str; + while (*c == ' ') + ++c; + + *str = c; +} + +static void +fast_forward(char **str, int skip) +{ + char *c = *str; + while (*c != '\0' && skip-- > 0) + ++c; + + *str = c; +} + +static void +skip_to_past_space(char **str) +{ + char *c = *str; + while (*c != ' ' && *c != '\0') + ++c; + + *str = c; + + skip_whitespace(str); +} + +static int +grab_token(char **str, char **out) +{ + int _status; + char *c = *str; + int len; + char *tmp; + + while (*c != ' ' && *c != '\0') + ++c; + + len = c - *str; + + tmp = RMALLOC(len + 1); + if (!tmp) + ABORT(R_NO_MEMORY); + + memcpy(tmp, *str, len); + tmp[len] = '\0'; + + *str = c; + *out = tmp; + + _status = 0; +abort: + return _status; +} + +int +nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *orig,nr_ice_media_stream *stream,nr_ice_candidate **candp) +{ + int r,_status; + char* str = orig; + nr_ice_candidate *cand; + char *connection_address=0; + unsigned int port; + int i; + unsigned int component_id; + char *rel_addr=0; + unsigned char transport; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + + if(!(cand->label=r_strdup(orig))) + ABORT(R_NO_MEMORY); + + cand->ctx=ctx; + cand->isock=0; + cand->state=NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED; + cand->stream=stream; + skip_whitespace(&str); + + /* Skip a= if present */ + if (!strncmp(str, "a=", 2)) + str += 2; + + /* Candidate attr */ + if (strncasecmp(str, "candidate:", 10)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 10); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* Foundation */ + if ((r=grab_token(&str, &cand->foundation))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* component */ + if (sscanf(str, "%u", &component_id) != 1) + ABORT(R_BAD_DATA); + + if (component_id < 1 || component_id > 256) + ABORT(R_BAD_DATA); + + cand->component_id = (UCHAR)component_id; + + skip_to_past_space(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* Protocol */ + if (!strncasecmp(str, "UDP", 3)) + transport=IPPROTO_UDP; + else if (!strncasecmp(str, "TCP", 3)) + transport=IPPROTO_TCP; + else + ABORT(R_BAD_DATA); + + fast_forward(&str, 3); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* priority */ + if (sscanf(str, "%u", &cand->priority) != 1) + ABORT(R_BAD_DATA); + + if (cand->priority < 1) + ABORT(R_BAD_DATA); + + skip_to_past_space(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* Peer address/port */ + if ((r=grab_token(&str, &connection_address))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (sscanf(str, "%u", &port) != 1) + ABORT(R_BAD_DATA); + + if (port < 1 || port > 0x0FFFF) + ABORT(R_BAD_DATA); + + if ((r=nr_str_port_to_transport_addr(connection_address,port,transport,&cand->addr))) + ABORT(r); + + skip_to_past_space(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* Type */ + if (strncasecmp("typ", str, 3)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 3); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + assert(nr_ice_candidate_type_names[0] == 0); + + for (i = 1; nr_ice_candidate_type_names[i]; ++i) { + if(!strncasecmp(nr_ice_candidate_type_names[i], str, strlen(nr_ice_candidate_type_names[i]))) { + cand->type=i; + break; + } + } + if (nr_ice_candidate_type_names[i] == 0) + ABORT(R_BAD_DATA); + + fast_forward(&str, strlen(nr_ice_candidate_type_names[i])); + + /* Look for the other side's raddr, rport */ + /* raddr, rport */ + switch (cand->type) { + case HOST: + break; + case SERVER_REFLEXIVE: + case PEER_REFLEXIVE: + case RELAYED: + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (strncasecmp("raddr", str, 5)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 5); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if ((r=grab_token(&str, &rel_addr))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (strncasecmp("rport", str, 5)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 5); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (sscanf(str, "%u", &port) != 1) + ABORT(R_BAD_DATA); + + if (port > 0x0FFFF) + ABORT(R_BAD_DATA); + + if ((r=nr_str_port_to_transport_addr(rel_addr,port,transport,&cand->base))) + ABORT(r); + + skip_to_past_space(&str); + /* it's expected to be at EOD at this point */ + + break; + default: + ABORT(R_INTERNAL); + break; + } + + skip_whitespace(&str); + + if (transport == IPPROTO_TCP && cand->type != RELAYED) { + /* Parse tcptype extension per RFC 6544 S 4.5 */ + if (strncasecmp("tcptype ", str, 8)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 8); + skip_whitespace(&str); + + for (i = 1; nr_ice_candidate_tcp_type_names[i]; ++i) { + if(!strncasecmp(nr_ice_candidate_tcp_type_names[i], str, strlen(nr_ice_candidate_tcp_type_names[i]))) { + cand->tcp_type=i; + fast_forward(&str, strlen(nr_ice_candidate_tcp_type_names[i])); + break; + } + } + + if (cand->tcp_type == 0) + ABORT(R_BAD_DATA); + + if (*str && *str != ' ') + ABORT(R_BAD_DATA); + } + /* Ignore extensions per RFC 5245 S 15.1 */ +#if 0 + /* This used to be an assert, but we don't want to exit on invalid + remote data */ + if (strlen(str) != 0) { + ABORT(R_BAD_DATA); + } +#endif + + nr_ice_candidate_compute_codeword(cand); + + *candp=cand; + + _status=0; + abort: + if (_status){ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Error parsing attribute: %s",ctx->label,orig); + nr_ice_candidate_destroy(&cand); + } + + RFREE(connection_address); + RFREE(rel_addr); + return(_status); +} + + +int +nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr) +{ + int r,_status; + char *orig = 0; + char *str; + + orig = str = attr; + + if (!strncasecmp(str, "ice-ufrag:", 10)) { + fast_forward(&str, 10); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + RFREE(stream->ufrag); + if ((r=grab_token(&str, &stream->ufrag))) + ABORT(r); + } + else if (!strncasecmp(str, "ice-pwd:", 8)) { + fast_forward(&str, 8); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + RFREE(stream->pwd); + if ((r=grab_token(&str, &stream->pwd))) + ABORT(r); + } + else { + ABORT(R_BAD_DATA); + } + + skip_whitespace(&str); + + /* RFC 5245 grammar doesn't have an extension point for ice-pwd or + ice-ufrag: if there's anything left on the line, we treat it as bad. */ + if (str[0] != '\0') { + ABORT(R_BAD_DATA); + } + + _status=0; + abort: + if (_status) { + if (orig) + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Error parsing attribute: %s",pctx->label,orig); + } + + return(_status); +} + +int +nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct) +{ + int r,_status; + int i; + char *orig = 0; + char *str; + char *component_id = 0; + char *connection_address = 0; + unsigned int port; + in_addr_t addr; + char *ice_option_tag = 0; + + for(i=0;i<attr_ct;i++){ + orig = str = attrs[i]; + + component_id = 0; + connection_address = 0; + ice_option_tag = 0; + + if (!strncasecmp(str, "remote-candidates:", 18)) { + fast_forward(&str, 18); + skip_whitespace(&str); + + while (*str != '\0') { + if ((r=grab_token(&str, &component_id))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if ((r=grab_token(&str, &connection_address))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + addr = inet_addr(connection_address); + if (addr == INADDR_NONE) + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (sscanf(str, "%u", &port) != 1) + ABORT(R_BAD_DATA); + + if (port < 1 || port > 0x0FFFF) + ABORT(R_BAD_DATA); + + skip_to_past_space(&str); + +#if 0 + /* TODO: !nn! just drop on the floor for now, later put somewhere */ + /* Assume v4 for now */ + if(r=nr_ip4_port_to_transport_addr(ntohl(addr),port,IPPROTO_UDP,&candidate->base)) + ABORT(r); + + TAILQ_INSERT_TAIL(head, elm, field); +#endif + + component_id = 0; /* prevent free */ + RFREE(connection_address); + connection_address = 0; /* prevent free */ + } + } + else if (!strncasecmp(str, "ice-lite", 8)) { + pctx->peer_lite = 1; + pctx->controlling = 1; + + fast_forward(&str, 8); + } + else if (!strncasecmp(str, "ice-mismatch", 12)) { + pctx->peer_ice_mismatch = 1; + + fast_forward(&str, 12); + } + else if (!strncasecmp(str, "ice-ufrag:", 10)) { + fast_forward(&str, 10); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + } + else if (!strncasecmp(str, "ice-pwd:", 8)) { + fast_forward(&str, 8); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + } + else if (!strncasecmp(str, "ice-options:", 12)) { + fast_forward(&str, 12); + skip_whitespace(&str); + + while (*str != '\0') { + if ((r=grab_token(&str, &ice_option_tag))) + ABORT(r); + + skip_whitespace(&str); + + //TODO: for now, just throw away; later put somewhere + RFREE(ice_option_tag); + + ice_option_tag = 0; /* prevent free */ + } + } + else { + ABORT(R_BAD_DATA); + } + + skip_whitespace(&str); + + /* RFC 5245 grammar doesn't have an extension point for any of the + preceding attributes: if there's anything left on the line, we + treat it as bad data. */ + if (str[0] != '\0') { + ABORT(R_BAD_DATA); + } + } + + _status=0; + abort: + if (_status) { + if (orig) + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Error parsing attribute: %s",pctx->label,orig); + } + + RFREE(connection_address); + RFREE(component_id); + RFREE(ice_option_tag); + return(_status); +} + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c new file mode 100644 index 0000000000..0bf97eb984 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c @@ -0,0 +1,875 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <string.h> +#include <assert.h> +#include <registry.h> +#include <nr_api.h> +#include "ice_ctx.h" +#include "ice_peer_ctx.h" +#include "ice_media_stream.h" +#include "ice_util.h" +#include "nr_crypto.h" +#include "async_timer.h" +#include "ice_reg.h" + +static void nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct); +static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate, int trickled, const char *mdns_addr); +static void nr_ice_peer_ctx_start_trickle_timer(nr_ice_peer_ctx *pctx); + +int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp) + { + int r,_status; + nr_ice_peer_ctx *pctx=0; + + if(!(pctx=RCALLOC(sizeof(nr_ice_peer_ctx)))) + ABORT(R_NO_MEMORY); + + pctx->state = NR_ICE_PEER_STATE_UNPAIRED; + + if(!(pctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + pctx->ctx=ctx; + pctx->handler=handler; + + /* Decide controlling vs. controlled */ + if(ctx->flags & NR_ICE_CTX_FLAGS_LITE){ + pctx->controlling=0; + } else { + pctx->controlling=1; + } + if(r=nr_crypto_random_bytes((UCHAR *)&pctx->tiebreaker,8)) + ABORT(r); + + STAILQ_INIT(&pctx->peer_streams); + + STAILQ_INSERT_TAIL(&ctx->peers,pctx,entry); + + *pctxp=pctx; + + _status = 0; + abort: + if(_status){ + nr_ice_peer_ctx_destroy(&pctx); + } + return(_status); + } + + + +int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct) + { + nr_ice_media_stream *pstream=0; + nr_ice_component *comp,*comp2; + char *lufrag,*rufrag; + char *lpwd,*rpwd; + int r,_status; + + /* + Note: use component_ct from our own stream since components other + than this offered by the other side are unusable */ + if(r=nr_ice_media_stream_create(pctx->ctx,stream->label,"","",stream->component_ct,&pstream)) + ABORT(r); + + /* Match up the local and remote components */ + comp=STAILQ_FIRST(&stream->components); + comp2=STAILQ_FIRST(&pstream->components); + while(comp){ + comp2->local_component=comp; + + comp=STAILQ_NEXT(comp,entry); + comp2=STAILQ_NEXT(comp2,entry); + } + + pstream->local_stream=stream; + pstream->pctx=pctx; + + nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct); + + /* Now that we have the ufrag and password, compute all the username/password + pairs */ + lufrag=stream->ufrag; + lpwd=stream->pwd; + assert(lufrag); + assert(lpwd); + rufrag=pstream->ufrag; + rpwd=pstream->pwd; + if (!rufrag || !rpwd) + ABORT(R_BAD_DATA); + + if(r=nr_concat_strings(&pstream->r2l_user,lufrag,":",rufrag,NULL)) + ABORT(r); + if(r=nr_concat_strings(&pstream->l2r_user,rufrag,":",lufrag,NULL)) + ABORT(r); + if(r=r_data_make(&pstream->r2l_pass, (UCHAR *)lpwd, strlen(lpwd))) + ABORT(r); + if(r=r_data_make(&pstream->l2r_pass, (UCHAR *)rpwd, strlen(rpwd))) + ABORT(r); + + STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry); + pstream=0; + + _status=0; + abort: + if (_status) { + nr_ice_media_stream_destroy(&pstream); + } + return(_status); + } + +static void nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct) + { + int r; + int i; + + for(i=0;i<attr_ct;i++){ + if(!strncmp(attrs[i],"ice-",4)){ + if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i])) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus ICE attribute",pctx->ctx->label,pctx->label); + continue; + } + } + else if (!strncmp(attrs[i],"candidate",9)){ + if(r=nr_ice_ctx_parse_candidate(pctx,pstream,attrs[i],0,0)) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus candidate",pctx->ctx->label,pctx->label); + continue; + } + } + else { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus attribute: %s",pctx->ctx->label,pctx->label,attrs[i]); + } + } + + /* Doesn't fail because we just skip errors */ + } + +static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate, int trickled, const char *mdns_addr) + { + nr_ice_candidate *cand=0; + nr_ice_component *comp; + int j; + int r, _status; + + if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,candidate,pstream,&cand)) + ABORT(r); + + /* set the trickled flag on the candidate */ + cand->trickled = trickled; + + if (mdns_addr) { + cand->mdns_addr = r_strdup(mdns_addr); + if (!cand->mdns_addr) { + ABORT(R_NO_MEMORY); + } + } + + /* Not the fastest way to find a component, but it's what we got */ + j=1; + for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){ + if(j==cand->component_id) + break; + + j++; + } + + if(!comp){ + /* Very common for the answerer when it uses rtcp-mux */ + r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) no such component for candidate %s",pctx->ctx->label,pctx->label, candidate); + ABORT(R_REJECTED); + } + + if (comp->state == NR_ICE_COMPONENT_DISABLED) { + r_log(LOG_ICE,LOG_WARNING,"Peer offered candidate for disabled remote component: %s", candidate); + ABORT(R_BAD_DATA); + } + if (comp->local_component->state == NR_ICE_COMPONENT_DISABLED) { + r_log(LOG_ICE,LOG_WARNING,"Peer offered candidate for disabled local component: %s", candidate); + ABORT(R_BAD_DATA); + } + + cand->component=comp; + + TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): creating peer candidate", + pctx->label,cand->label); + + _status=0; + abort: + if (_status) { + nr_ice_candidate_destroy(&cand); + } + return(_status); + } + +int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream **pstreamp) + { + int _status; + nr_ice_media_stream *pstream; + + /* Because we don't have forward pointers, iterate through all the + peer streams to find one that matches us */ + pstream=STAILQ_FIRST(&pctx->peer_streams); + + if(!pstream) { + /* No peer streams at all, presumably because they do not exist yet. + * Don't log a warning here. */ + ABORT(R_NOT_FOUND); + } + + while(pstream) { + if (pstream->local_stream == stream) + break; + + pstream = STAILQ_NEXT(pstream, entry); + } + + if (!pstream) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) has no stream matching stream %s",pctx->ctx->label,pctx->label,stream->label); + ABORT(R_NOT_FOUND); + } + + *pstreamp = pstream; + + _status=0; + abort: + return(_status); + } + +int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp) + { + int r,_status; + + STAILQ_REMOVE(&pctx->peer_streams,*pstreamp,nr_ice_media_stream_,entry); + + if(r=nr_ice_media_stream_destroy(pstreamp)) { + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *candidate, const char *mdns_addr) + { + nr_ice_media_stream *pstream; + int r,_status; + int needs_pairing = 0; + + if (stream->obsolete) { + return 0; + } + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): peer (%s) parsing trickle ICE candidate %s",pctx->ctx->label,pctx->label,candidate); + r = nr_ice_peer_ctx_find_pstream(pctx, stream, &pstream); + if (r) + ABORT(r); + + switch(pstream->ice_state) { + case NR_ICE_MEDIA_STREAM_UNPAIRED: + break; + case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN: + case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE: + needs_pairing = 1; + break; + default: + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) tried to trickle ICE in inappropriate state %d",pctx->ctx->label,pctx->label,stream->label,pstream->ice_state); + ABORT(R_ALREADY); + break; + } + + if(r=nr_ice_ctx_parse_candidate(pctx,pstream,candidate,1,mdns_addr)){ + ABORT(r); + } + + /* If ICE is running (i.e., we are in FROZEN or ACTIVE states) + then we need to pair this new candidate. For now we + just re-pair the stream which is inefficient but still + fine because we suppress duplicate pairing */ + if (needs_pairing) { + /* Start the remote trickle grace timeout if it hasn't been started by + another trickled candidate or from the SDP. */ + if (!pctx->trickle_grace_period_timer) { + nr_ice_peer_ctx_start_trickle_timer(pctx); + } + + if(r=nr_ice_media_stream_pair_candidates(pctx, stream, pstream)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to pair trickle ICE candidates",pctx->ctx->label,pctx->label,stream->label); + ABORT(r); + } + + /* Start checks if this stream is not checking yet or if it has checked + all the available candidates but not had a completed check for all + components. + + Note that this is not compliant with RFC 5245, but consistent with + the libjingle trickle ICE behavior. Note that we will not restart + checks if either (a) the stream has failed or (b) all components + have a successful pair because the switch statement above jumps + will in both states. + + TODO(ekr@rtfm.com): restart checks. + TODO(ekr@rtfm.com): update when the trickle ICE RFC is published + */ + if (!pstream->timer) { + if(r=nr_ice_media_stream_start_checks(pctx, pstream)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to start checks",pctx->ctx->label,pctx->label,stream->label); + ABORT(r); + } + } + } + + _status=0; + abort: + return(_status); + + } + + +static void nr_ice_peer_ctx_trickle_wait_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_peer_ctx *pctx=cb_arg; + nr_ice_media_stream *stream; + nr_ice_component *comp; + + pctx->trickle_grace_period_timer=0; + + r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) Trickle grace period is over; marking every component with only failed pairs as failed.",pctx->ctx->label,pctx->label); + + stream=STAILQ_FIRST(&pctx->peer_streams); + while(stream){ + comp=STAILQ_FIRST(&stream->components); + while(comp){ + nr_ice_component_check_if_failed(comp); + comp=STAILQ_NEXT(comp,entry); + } + stream=STAILQ_NEXT(stream,entry); + } + } + +static void nr_ice_peer_ctx_start_trickle_timer(nr_ice_peer_ctx *pctx) + { + UINT4 grace_period_timeout=0; + + if(pctx->trickle_grace_period_timer) { + NR_async_timer_cancel(pctx->trickle_grace_period_timer); + pctx->trickle_grace_period_timer=0; + } + + NR_reg_get_uint4(NR_ICE_REG_TRICKLE_GRACE_PERIOD,&grace_period_timeout); + + if (grace_period_timeout) { + r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) starting grace period timer for %u ms",pctx->ctx->label,pctx->label, grace_period_timeout); + /* If we're doing trickle, we need to allow a grace period for new + * trickle candidates to arrive in case the pairs we have fail quickly. */ + NR_ASYNC_TIMER_SET(grace_period_timeout,nr_ice_peer_ctx_trickle_wait_cb,pctx,&pctx->trickle_grace_period_timer); + } + } + +int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx) + { + nr_ice_media_stream *stream; + int r,_status; + + if(pctx->peer_lite && !pctx->controlling) { + if(pctx->ctx->flags & NR_ICE_CTX_FLAGS_LITE){ + r_log(LOG_ICE,LOG_ERR,"Both sides are ICE-Lite"); + ABORT(R_BAD_DATA); + } + nr_ice_peer_ctx_switch_controlling_role(pctx); + } + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): peer (%s) pairing candidates",pctx->ctx->label,pctx->label); + + if(STAILQ_EMPTY(&pctx->peer_streams)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) received no media stream attributes",pctx->ctx->label,pctx->label); + ABORT(R_FAILED); + } + + /* Set this first; if we fail partway through, we do not want to end + * up in UNPAIRED after creating some pairs. */ + pctx->state = NR_ICE_PEER_STATE_PAIRED; + + stream=STAILQ_FIRST(&pctx->peer_streams); + while(stream){ + if(!stream->local_stream->obsolete) { + if(r=nr_ice_media_stream_pair_candidates(pctx, stream->local_stream, + stream)) + ABORT(r); + } + + stream=STAILQ_NEXT(stream,entry); + } + + + _status=0; + abort: + return(_status); + } + + +int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand) + { + int r, _status; + nr_ice_media_stream *pstream; + + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) pairing local trickle ICE candidate %s",pctx->ctx->label,pctx->label,cand->label); + if ((r = nr_ice_peer_ctx_find_pstream(pctx, cand->stream, &pstream))) + ABORT(r); + + /* Start the remote trickle grace timeout if it hasn't been started + already. */ + if (!pctx->trickle_grace_period_timer) { + nr_ice_peer_ctx_start_trickle_timer(pctx); + } + + if ((r = nr_ice_media_stream_pair_new_trickle_candidate(pctx, pstream, cand))) + ABORT(r); + + _status=0; + abort: + return _status; + } + +int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id) + { + int r, _status; + nr_ice_media_stream *pstream; + nr_ice_component *component; + + if ((r=nr_ice_peer_ctx_find_pstream(pctx, lstream, &pstream))) + ABORT(r); + + /* We shouldn't be calling this after we have started pairing */ + if (pstream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED) + ABORT(R_FAILED); + + if ((r=nr_ice_media_stream_find_component(pstream, component_id, + &component))) + ABORT(r); + + component->state = NR_ICE_COMPONENT_DISABLED; + + _status=0; + abort: + return(_status); + } + + void nr_ice_peer_ctx_destroy(nr_ice_peer_ctx** pctxp) { + if (!pctxp || !*pctxp) return; + + nr_ice_peer_ctx* pctx = *pctxp; + nr_ice_media_stream *str1,*str2; + + /* Stop calling the handler */ + pctx->handler = 0; + + NR_async_timer_cancel(pctx->connected_cb_timer); + RFREE(pctx->label); + + STAILQ_FOREACH_SAFE(str1, &pctx->peer_streams, entry, str2){ + STAILQ_REMOVE(&pctx->peer_streams,str1,nr_ice_media_stream_,entry); + nr_ice_media_stream_destroy(&str1); + } + assert(pctx->ctx); + if (pctx->ctx) + STAILQ_REMOVE(&pctx->ctx->peers, pctx, nr_ice_peer_ctx_, entry); + + if(pctx->trickle_grace_period_timer) { + NR_async_timer_cancel(pctx->trickle_grace_period_timer); + pctx->trickle_grace_period_timer=0; + } + + RFREE(pctx); + + *pctxp=0; + } + +/* Start the checks for the first media stream (S 5.7) + The rest remain FROZEN */ +int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx) + { + return nr_ice_peer_ctx_start_checks2(pctx, 0); + } + +/* Start checks for some media stream. + + If allow_non_first == 0, then we only look at the first stream, + which is 5245-complaint. + + If allow_non_first == 1 then we find the first non-empty stream + This is not compliant with RFC 5245 but is necessary to make trickle ICE + work plausibly +*/ +int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first) + { + int r,_status; + nr_ice_media_stream *stream; + int started = 0; + + /* Ensure that grace period timer is running. We might cancel this if we + * didn't actually start any pairs. */ + nr_ice_peer_ctx_start_trickle_timer(pctx); + + /* Might have added some streams */ + pctx->reported_connected = 0; + NR_async_timer_cancel(pctx->connected_cb_timer); + pctx->connected_cb_timer = 0; + pctx->checks_started = 0; + + nr_ice_peer_ctx_check_if_connected(pctx); + + if (pctx->reported_connected) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) in %s all streams were done",pctx->ctx->label,pctx->label,__FUNCTION__); + return (0); + } + + stream=STAILQ_FIRST(&pctx->peer_streams); + if(!stream) + ABORT(R_FAILED); + + while (stream) { + if(!stream->local_stream->obsolete) { + assert(stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED); + + if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) { + if(!TAILQ_EMPTY(&stream->check_list)) + break; + + if(!allow_non_first){ + /* This test applies if: + + 1. allow_non_first is 0 (i.e., non-trickle ICE) + 2. the first stream has an empty check list. + + But in the non-trickle ICE case, the other side should have provided + some candidates or ICE is pretty much not going to work and we're + just going to fail. Hence R_FAILED as opposed to R_NOT_FOUND and + immediate termination here. + */ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label); + ABORT(R_FAILED); + } + } + } + + stream=STAILQ_NEXT(stream, entry); + } + + if (!stream) { + /* + We fail above if we aren't doing trickle, and this is not all that + unusual in the trickle case. + */ + r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with non-empty check lists",pctx->ctx->label,pctx->label); + } + else if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) { + if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream)) + ABORT(r); + if(r=nr_ice_media_stream_start_checks(pctx,stream)) + ABORT(r); + ++started; + } + + stream=STAILQ_FIRST(&pctx->peer_streams); + while (stream) { + int serviced = 0; + if (r=nr_ice_media_stream_service_pre_answer_requests(pctx, stream->local_stream, stream, &serviced)) + ABORT(r); + + if (serviced) { + ++started; + } + else { + r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with pre-answer requests",pctx->ctx->label,pctx->label); + } + + + stream=STAILQ_NEXT(stream, entry); + } + + if (!started && pctx->ctx->uninitialized_candidates) { + r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) no checks to start, but gathering is not done yet, cancelling grace period timer",pctx->ctx->label,pctx->label); + /* Never mind on the grace period timer */ + NR_async_timer_cancel(pctx->trickle_grace_period_timer); + pctx->trickle_grace_period_timer=0; + ABORT(R_NOT_FOUND); + } + + _status=0; + abort: + return(_status); + } + +void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + if (!pctx->checks_started) { + r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) is now checking",pctx->ctx->label,pctx->label); + pctx->checks_started = 1; + if (pctx->handler && pctx->handler->vtbl->ice_checking) { + pctx->handler->vtbl->ice_checking(pctx->handler->obj, pctx); + } + } + } + +void nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx, int log_level) + { + nr_ice_media_stream *stream; + + r_log(LOG_ICE,log_level,"PEER %s STATE DUMP",pctx->label); + r_log(LOG_ICE,log_level,"=========================================="); + stream=STAILQ_FIRST(&pctx->peer_streams); + while(stream){ + nr_ice_media_stream_dump_state(pctx,stream,log_level); + } + r_log(LOG_ICE,log_level,"=========================================="); + } + +void nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx *pctx) + { + nr_ice_media_stream *str; + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): refreshing consent on all streams",pctx->label); + + str=STAILQ_FIRST(&pctx->peer_streams); + while(str) { + nr_ice_media_stream_refresh_consent_all(str); + str=STAILQ_NEXT(str,entry); + } + } + +void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx) + { + if (pctx->reported_connected && + pctx->handler && + pctx->handler->vtbl->ice_disconnected) { + pctx->handler->vtbl->ice_disconnected(pctx->handler->obj, pctx); + + pctx->reported_connected = 0; + } + } + +void nr_ice_peer_ctx_disconnect_all_streams(nr_ice_peer_ctx *pctx) + { + nr_ice_media_stream *str; + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): disconnecting all streams",pctx->label); + + str=STAILQ_FIRST(&pctx->peer_streams); + while(str) { + nr_ice_media_stream_disconnect_all_components(str); + + /* The first stream to be disconnected will cause the peer ctx to signal + the disconnect up. */ + nr_ice_media_stream_set_disconnected(str, NR_ICE_MEDIA_STREAM_DISCONNECTED); + + str=STAILQ_NEXT(str,entry); + } + } + +void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx) + { + /* Fire the handler callback to say we're done */ + if (pctx->reported_connected && + pctx->handler && + pctx->handler->vtbl->ice_connected) { + pctx->handler->vtbl->ice_connected(pctx->handler->obj, pctx); + } + } + +static void nr_ice_peer_ctx_fire_connected(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_peer_ctx *pctx=cb_arg; + + pctx->connected_cb_timer=0; + + nr_ice_peer_ctx_connected(pctx); + } + +/* Examine all the streams to see if we're + maybe miraculously connected */ +void nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx) + { + nr_ice_media_stream *str; + int failed=0; + int succeeded=0; + + str=STAILQ_FIRST(&pctx->peer_streams); + while(str){ + if (!str->local_stream->obsolete){ + if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED){ + succeeded++; + } + else if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FAILED){ + failed++; + } + else{ + break; + } + } + str=STAILQ_NEXT(str,entry); + } + + if(str) + return; /* Something isn't done */ + + /* OK, we're finished, one way or another */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): all checks completed success=%d fail=%d",pctx->label,succeeded,failed); + + /* Make sure grace period timer is cancelled */ + if(pctx->trickle_grace_period_timer) { + r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) cancelling grace period timer",pctx->ctx->label,pctx->label); + NR_async_timer_cancel(pctx->trickle_grace_period_timer); + pctx->trickle_grace_period_timer=0; + } + + /* Schedule a connected notification for the first connected event. + IMPORTANT: This is done in a callback because we expect destructors + of various kinds to be fired from here */ + if (!pctx->reported_connected) { + pctx->reported_connected = 1; + assert(!pctx->connected_cb_timer); + NR_ASYNC_TIMER_SET(0,nr_ice_peer_ctx_fire_connected,pctx,&pctx->connected_cb_timer); + } + } + + +/* Given a component in the main ICE ctx, find the relevant component in + the peer_ctx */ +int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp) + { + nr_ice_media_stream *pstr; + int r,_status; + + pstr=STAILQ_FIRST(&pctx->peer_streams); + while(pstr){ + if(pstr->local_stream==str) + break; + + pstr=STAILQ_NEXT(pstr,entry); + } + if(!pstr) + ABORT(R_BAD_ARGS); + + if(r=nr_ice_media_stream_find_component(pstr,component_id,compp)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +/* + This packet may be for us. + + 1. Find the matching peer component + 2. Examine the packet source address to see if it matches + one of the peer candidates. + 3. Fire the relevant callback handler if there is a match + + Return 0 if match, R_REJECTED if no match, other errors + if we can't even find the component or something like that. +*/ + +int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len) + { + nr_ice_component *peer_comp; + nr_ice_candidate *cand; + int r,_status; + + if(r=nr_ice_peer_ctx_find_component(pctx, comp->stream, comp->component_id, + &peer_comp)) + ABORT(r); + + /* OK, we've found the component, now look for matches */ + cand=TAILQ_FIRST(&peer_comp->candidates); + while(cand){ + if(!nr_transport_addr_cmp(source_addr,&cand->addr, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + break; + + cand=TAILQ_NEXT(cand,entry_comp); + } + + if(!cand) + ABORT(R_REJECTED); + + // accumulate the received bytes for the active candidate pair + if (peer_comp->active) { + peer_comp->active->bytes_recvd += len; + gettimeofday(&peer_comp->active->last_recvd, 0); + } + + /* OK, there's a match. Call the handler */ + + if (pctx->handler) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label); + + pctx->handler->vtbl->msg_recvd(pctx->handler->obj, + pctx,comp->stream,comp->component_id,data,len); + } + + _status=0; + abort: + return(_status); + } + +void nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx *pctx) + { + int controlling = !(pctx->controlling); + if(pctx->controlling_conflict_resolved) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) %s called more than once; " + "this probably means the peer is confused. Not switching roles.", + pctx->ctx->label,pctx->label,__FUNCTION__); + return; + } + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): detected " + "role conflict. Switching to %s", + pctx->label, + controlling ? "controlling" : "controlled"); + + pctx->controlling = controlling; + pctx->controlling_conflict_resolved = 1; + + if(pctx->state == NR_ICE_PEER_STATE_PAIRED) { + /* We have formed candidate pairs. We need to inform them. */ + nr_ice_media_stream *pstream=STAILQ_FIRST(&pctx->peer_streams); + while(pstream) { + nr_ice_media_stream_role_change(pstream); + pstream = STAILQ_NEXT(pstream, entry); + } + } + } + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.h new file mode 100644 index 0000000000..ec73d96a03 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.h @@ -0,0 +1,101 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_peer_ctx_h +#define _ice_peer_ctx_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +struct nr_ice_peer_ctx_ { + int state; +#define NR_ICE_PEER_STATE_UNPAIRED 1 +#define NR_ICE_PEER_STATE_PAIRED 2 + + char *label; + nr_ice_ctx *ctx; + nr_ice_handler *handler; + + UCHAR controlling; /* 1 for controlling, 0 for controlled */ + UCHAR controlling_conflict_resolved; + UINT8 tiebreaker; + + int peer_lite; + int peer_ice_mismatch; + + nr_ice_media_stream_head peer_streams; + int active_streams; + int waiting_pairs; + UCHAR checks_started; + + void *connected_cb_timer; + UCHAR reported_connected; + void *trickle_grace_period_timer; + + STAILQ_ENTRY(nr_ice_peer_ctx_) entry; +}; + +typedef STAILQ_HEAD(nr_ice_peer_ctx_head_, nr_ice_peer_ctx_) nr_ice_peer_ctx_head; + +int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp); +void nr_ice_peer_ctx_destroy(nr_ice_peer_ctx** pctxp); +int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct); +int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream **pstreamp); +int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp); +int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *cand, const char *mdns_addr); + +int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx); +int nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct); +int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx); +int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first); +void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); +void nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx *pctx); +void nr_ice_peer_ctx_disconnect_all_streams(nr_ice_peer_ctx *pctx); +void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx); +void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx); +void nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx, int log_level); +int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx); +void nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx); +int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp); +int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len); +int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id); +int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand); +void nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx *pctx); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_reg.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_reg.h new file mode 100644 index 0000000000..3acc02360a --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_reg.h @@ -0,0 +1,81 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_reg_h +#define _ice_reg_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +#define NR_ICE_REG_PREF_TYPE_HOST "ice.pref.type.host" +#define NR_ICE_REG_PREF_TYPE_RELAYED "ice.pref.type.relayed" +#define NR_ICE_REG_PREF_TYPE_SRV_RFLX "ice.pref.type.srv_rflx" +#define NR_ICE_REG_PREF_TYPE_PEER_RFLX "ice.pref.type.peer_rflx" +#define NR_ICE_REG_PREF_TYPE_HOST_TCP "ice.pref.type.host_tcp" +#define NR_ICE_REG_PREF_TYPE_RELAYED_TCP "ice.pref.type.relayed_tcp" +#define NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP "ice.pref.type.srv_rflx_tcp" +#define NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP "ice.pref.type.peer_rflx_tcp" + +#define NR_ICE_REG_PREF_INTERFACE_PRFX "ice.pref.interface" +#define NR_ICE_REG_SUPPRESS_INTERFACE_PRFX "ice.suppress.interface" + +#define NR_ICE_REG_STUN_SRV_PRFX "ice.stun.server" +#define NR_ICE_REG_STUN_SRV_ADDR "addr" +#define NR_ICE_REG_STUN_SRV_PORT "port" + +#define NR_ICE_REG_TURN_SRV_PRFX "ice.turn.server" +#define NR_ICE_REG_TURN_SRV_ADDR "addr" +#define NR_ICE_REG_TURN_SRV_PORT "port" +#define NR_ICE_REG_TURN_SRV_BANDWIDTH "bandwidth" +#define NR_ICE_REG_TURN_SRV_LIFETIME "lifetime" +#define NR_ICE_REG_TURN_SRV_USERNAME "username" +#define NR_ICE_REG_TURN_SRV_PASSWORD "password" + +#define NR_ICE_REG_ICE_TCP_DISABLE "ice.tcp.disable" +#define NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT "ice.tcp.so_sock_count" +#define NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG "ice.tcp.listen_backlog" + +#define NR_ICE_REG_KEEPALIVE_TIMER "ice.keepalive_timer" + +#define NR_ICE_REG_TRICKLE_GRACE_PERIOD "ice.trickle_grace_period" +#define NR_ICE_REG_PREF_FORCE_INTERFACE_NAME "ice.forced_interface_name" +#define NR_ICE_REG_USE_NR_RESOLVER_FOR_TCP "ice.tcp.use_nr_resolver" +#define NR_ICE_REG_USE_NR_RESOLVER_FOR_UDP "ice.udp.use_nr_resolver" + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.c new file mode 100644 index 0000000000..a6c8513300 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.c @@ -0,0 +1,404 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <assert.h> +#include <string.h> +#include "nr_api.h" +#include "ice_ctx.h" +#include "stun.h" +#include "nr_socket_buffered_stun.h" +#include "nr_socket_multi_tcp.h" + +static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r; + nr_ice_stun_ctx *sc1,*sc2; + nr_ice_socket *sock=cb_arg; + UCHAR buf[9216]; + char string[256]; + nr_transport_addr addr; + int len; + size_t len_s; + int is_stun; + int is_req; + int is_ind; + int processed_indication=0; + + nr_socket *stun_srv_sock=sock->sock; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Socket ready to read",sock->ctx->label); + + if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){ + 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 %d on reliable socket(%p). Abandoning.",sock->ctx->label, r, s); + nr_ice_socket_failed(sock); + return; + } + } + + if (sock->type != NR_ICE_SOCKET_TYPE_STREAM_TCP) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): rearming",sock->ctx->label); + NR_ASYNC_WAIT(s,how,nr_ice_socket_readable_cb,cb_arg); + } + + if (r) { + 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; + + len = (int)len_s; + +#ifdef USE_TURN + re_process: +#endif /* USE_TURN */ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Read %d bytes %sfrom %s",sock->ctx->label,len,(processed_indication ? "relayed " : ""),addr.as_string); + + /* First question: is this STUN or not? */ + is_stun=nr_is_stun_message(buf,len); + + if(is_stun){ + is_req=nr_is_stun_request_message(buf,len); + is_ind=is_req?0:nr_is_stun_indication_message(buf,len); + + snprintf(string, sizeof(string)-1, "ICE(%s): Message is STUN (%s)",sock->ctx->label, + is_req ? "request" : (is_ind ? "indication" : "other")); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)buf, len); + + + /* We need to offer it to all of our stun contexts + to see who bites */ + sc1=TAILQ_FIRST(&sock->stun_ctxs); + while(sc1){ + sc2=TAILQ_NEXT(sc1,entry); + + r=-1; + switch(sc1->type){ + /* This has been deleted, prune... */ + case NR_ICE_STUN_NONE: + TAILQ_REMOVE(&sock->stun_ctxs,sc1,entry); + RFREE(sc1); + break; + + case NR_ICE_STUN_CLIENT: + if(!(is_req||is_ind)){ + r=nr_stun_client_process_response(sc1->u.client,buf,len,&addr); + } + break; + + case NR_ICE_STUN_SERVER: + if(is_req){ + r=nr_stun_server_process_request(sc1->u.server,stun_srv_sock,(char *)buf,len,&addr,NR_STUN_AUTH_RULE_SHORT_TERM); + } + break; +#ifdef USE_TURN + case NR_ICE_TURN_CLIENT: + /* data indications are ok, so don't ignore those */ + /* Check that this is from the right TURN server address. Else + skip */ + if (nr_transport_addr_cmp( + &sc1->u.turn_client.turn_client->turn_server_addr, + &addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + break; + + if(!is_req){ + if(!is_ind) + r=nr_turn_client_process_response(sc1->u.turn_client.turn_client,buf,len,&addr); + else{ + nr_transport_addr n_addr; + size_t n_len; + + if (processed_indication) { + /* Don't allow recursively wrapped indications */ + r_log(LOG_ICE, LOG_WARNING, + "ICE(%s): discarding recursively wrapped indication", + sock->ctx->label); + break; + } + /* This is a bit of a hack. If it's a data indication, strip + off the TURN framing and re-enter. This works because + all STUN processing is on the same physical socket. + We don't care about other kinds of indication */ + r=nr_turn_client_parse_data_indication( + sc1->u.turn_client.turn_client, &addr, + buf, len, buf, &n_len, len, &n_addr); + if(!r){ + r_log(LOG_ICE,LOG_DEBUG,"Unwrapped a data indication."); + len=n_len; + nr_transport_addr_copy(&addr,&n_addr); + stun_srv_sock=sc1->u.turn_client.turn_sock; + processed_indication=1; + goto re_process; + } + } + } + break; +#endif /* USE_TURN */ + + default: + assert(0); /* Can't happen */ + return; + } + if(!r) { + break; + } + + sc1=sc2; + } + if(!sc1){ + if (nr_ice_ctx_is_known_id(sock->ctx,((nr_stun_message_header*)buf)->id.octet)) + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Message is a retransmit",sock->ctx->label); + else + r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): Message does not correspond to any registered stun ctx",sock->ctx->label); + } + } + else{ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Message is not STUN",sock->ctx->label); + + nr_ice_ctx_deliver_packet(sock->ctx, sock->component, &addr, buf, len); + } + + return; + } + +int nr_ice_socket_create(nr_ice_ctx *ctx,nr_ice_component *comp, nr_socket *nsock, int type, nr_ice_socket **sockp) + { + nr_ice_socket *sock=0; + NR_SOCKET fd; + 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 (type == NR_ICE_SOCKET_TYPE_DGRAM) { + assert(addr.protocol == IPPROTO_UDP); + } + else { + assert(addr.protocol == IPPROTO_TCP); + } + sock->type=type; + + TAILQ_INIT(&sock->candidates); + TAILQ_INIT(&sock->stun_ctxs); + + if (sock->type == NR_ICE_SOCKET_TYPE_DGRAM){ + if((r=nr_socket_getfd(nsock,&fd))) + ABORT(r); + NR_ASYNC_WAIT(fd,NR_ASYNC_WAIT_READ,nr_ice_socket_readable_cb,sock); + } + else if (sock->type == NR_ICE_SOCKET_TYPE_STREAM_TURN) { + /* some OS's (e.g. Linux) don't like to see un-connected TCP sockets in + * the poll socket set. */ + nr_socket_buffered_stun_set_readable_cb(nsock,nr_ice_socket_readable_cb,sock); + } + else if (sock->type == NR_ICE_SOCKET_TYPE_STREAM_TCP) { + /* in this case we can't hook up using NR_ASYNC_WAIT, because nr_socket_multi_tcp + consists of multiple nr_sockets and file descriptors. */ + if((r=nr_socket_multi_tcp_set_readable_cb(nsock,nr_ice_socket_readable_cb,sock))) + ABORT(r); + } + + *sockp=sock; + + _status=0; + abort: + if(_status) RFREE(sock); + return(_status); + } + + +int nr_ice_socket_destroy(nr_ice_socket **isockp) + { + nr_ice_stun_ctx *s1,*s2; + nr_ice_socket *isock; + + if(!isockp || !*isockp) + return(0); + + isock=*isockp; + *isockp=0; + + /* Close the socket */ + nr_ice_socket_close(isock); + + /* The STUN server */ + nr_stun_server_ctx_destroy(&isock->stun_server); + + /* Now clean up the STUN ctxs */ + TAILQ_FOREACH_SAFE(s1, &isock->stun_ctxs, entry, s2){ + TAILQ_REMOVE(&isock->stun_ctxs, s1, entry); + RFREE(s1); + } + + RFREE(isock); + + return(0); + } + +int nr_ice_socket_close(nr_ice_socket *isock) + { +#ifdef NR_SOCKET_IS_VOID_PTR + NR_SOCKET fd=NULL; + NR_SOCKET no_socket = NULL; +#else + NR_SOCKET fd=-1; + NR_SOCKET no_socket = -1; +#endif + + if (!isock||!isock->sock) + return(0); + + if (isock->type != NR_ICE_SOCKET_TYPE_STREAM_TCP){ + nr_socket_getfd(isock->sock,&fd); + assert(isock->sock!=0); + if(fd != no_socket){ + NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_READ); + NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_WRITE); + } + } + nr_socket_destroy(&isock->sock); + + return(0); + } + +int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle) + { + nr_ice_stun_ctx *sc=0; + int _status; + + if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx)))) + ABORT(R_NO_MEMORY); + + sc->type=NR_ICE_STUN_CLIENT; + sc->u.client=srv; + + TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry); + + *handle=sc; + + _status=0; + abort: + return(_status); + } + +int nr_ice_socket_register_stun_server(nr_ice_socket *sock, nr_stun_server_ctx *srv,void **handle) + { + nr_ice_stun_ctx *sc=0; + int _status; + + if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx)))) + ABORT(R_NO_MEMORY); + + sc->type=NR_ICE_STUN_SERVER; + sc->u.server=srv; + + TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry); + + *handle=sc; + + _status=0; + abort: + return(_status); + } + +int nr_ice_socket_register_turn_client(nr_ice_socket *sock, nr_turn_client_ctx *srv, + nr_socket *turn_socket, void **handle) + { + nr_ice_stun_ctx *sc=0; + int _status; + + if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx)))) + ABORT(R_NO_MEMORY); + + sc->type=NR_ICE_TURN_CLIENT; + sc->u.turn_client.turn_client=srv; + sc->u.turn_client.turn_sock=turn_socket; + + TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry); + + *handle=sc; + + _status=0; + abort: + return(_status); + } + +/* Just mark it deregistered. Don't delete it now because it's not safe + in the CB, which is where this is likely to be called */ +int nr_ice_socket_deregister(nr_ice_socket *sock, void *handle) + { + nr_ice_stun_ctx *sc=handle; + + if(!sc) + return(0); + + sc->type=NR_ICE_STUN_NONE; + + return(0); + } + +void nr_ice_socket_failed(nr_ice_socket *sock) + { + nr_ice_stun_ctx *s1,*s2; + TAILQ_FOREACH_SAFE(s1, &sock->stun_ctxs, entry, s2){ + switch (s1->type) { + case NR_ICE_STUN_NONE: + break; + case NR_ICE_STUN_CLIENT: + nr_stun_client_failed(s1->u.client); + break; + case NR_ICE_STUN_SERVER: + /* Nothing to do here? */ + break; +#ifdef USE_TURN + case NR_ICE_TURN_CLIENT: + nr_turn_client_failed(s1->u.turn_client.turn_client); + break; +#endif + default: + assert(0); + } + } + } + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.h new file mode 100644 index 0000000000..9b73a2690e --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.h @@ -0,0 +1,98 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_socket_h +#define _ice_socket_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +typedef struct nr_ice_stun_ctx_ { + int type; +#define NR_ICE_STUN_NONE 0 /* Deregistered */ +#define NR_ICE_STUN_CLIENT 1 +#define NR_ICE_STUN_SERVER 2 +#define NR_ICE_TURN_CLIENT 3 + + union { + nr_stun_client_ctx *client; + nr_stun_server_ctx *server; + struct { + nr_turn_client_ctx *turn_client; + nr_socket *turn_sock; /* The nr_socket_turn wrapped around + turn_client */ + } turn_client; + } 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_TURN 2 +#define NR_ICE_SOCKET_TYPE_STREAM_TCP 3 + + 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; + + nr_stun_server_ctx *stun_server; + void *stun_server_handle; + + STAILQ_ENTRY(nr_ice_socket_) entry; +} nr_ice_socket; + +typedef STAILQ_HEAD(nr_ice_socket_head_,nr_ice_socket_) nr_ice_socket_head; + +int nr_ice_socket_create(struct nr_ice_ctx_ *ctx, struct nr_ice_component_ *comp, nr_socket *nsock, int type, nr_ice_socket **sockp); +int nr_ice_socket_destroy(nr_ice_socket **isock); +int nr_ice_socket_close(nr_ice_socket *isock); +int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle); +int nr_ice_socket_register_stun_server(nr_ice_socket *sock, nr_stun_server_ctx *srv,void **handle); +int nr_ice_socket_register_turn_client(nr_ice_socket *sock, nr_turn_client_ctx *srv,nr_socket *turn_socket, void **handle); +int nr_ice_socket_deregister(nr_ice_socket *sock, void *handle); +void nr_ice_socket_failed(nr_ice_socket *sock); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.c new file mode 100644 index 0000000000..a0896d5e9d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.c @@ -0,0 +1,70 @@ +/* +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, Mozilla 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 <string.h> +#include "local_addr.h" + +int nr_local_addr_copy(nr_local_addr *to, nr_local_addr *from) + { + int r,_status; + + if (r=nr_transport_addr_copy(&(to->addr), &(from->addr))) { + ABORT(r); + } + to->interface = from->interface; + to->flags = from->flags; + + _status=0; + abort: + return(_status); + } + +int nr_local_addr_fmt_info_string(nr_local_addr *addr, char *buf, int len) + { + int addr_type = addr->interface.type; + const char *vpn = (addr_type & NR_INTERFACE_TYPE_VPN) ? "VPN on " : ""; + + const char *type = (addr_type & NR_INTERFACE_TYPE_WIRED) ? "wired" : + (addr_type & NR_INTERFACE_TYPE_WIFI) ? "wifi" : + (addr_type & NR_INTERFACE_TYPE_MOBILE) ? "mobile" : + "unknown"; + + snprintf(buf, len, "%s%s, estimated speed: %d kbps %s", + vpn, type, addr->interface.estimated_speed, + (addr->flags & NR_ADDR_FLAG_TEMPORARY ? "temporary" : "")); + buf[len - 1] = '\0'; + return (0); + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.h new file mode 100644 index 0000000000..fb963e1115 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.h @@ -0,0 +1,62 @@ +/* +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, Mozilla 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. +*/ + +#ifndef _local_addr_h +#define _local_addr_h + +#include "transport_addr.h" + +typedef struct nr_interface_ { + int type; +#define NR_INTERFACE_TYPE_UNKNOWN 0 +#define NR_INTERFACE_TYPE_WIRED 1 +#define NR_INTERFACE_TYPE_WIFI 1 << 1 +#define NR_INTERFACE_TYPE_MOBILE 1 << 2 +#define NR_INTERFACE_TYPE_VPN 1 << 3 +#define NR_INTERFACE_TYPE_TEREDO 1 << 4 + int estimated_speed; /* Speed in kbps */ +} nr_interface; + +typedef struct nr_local_addr_ { + nr_transport_addr addr; + nr_interface interface; +#define NR_ADDR_FLAG_TEMPORARY 0x1 + int flags; +} nr_local_addr; + +int nr_local_addr_copy(nr_local_addr *to, nr_local_addr *from); +int nr_local_addr_fmt_info_string(nr_local_addr *addr, char *buf, int len); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.c new file mode 100644 index 0000000000..75e5f95467 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.c @@ -0,0 +1,88 @@ +/* +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, Mozilla 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 "nr_interface_prioritizer.h" +#include "transport_addr.h" + +int nr_interface_prioritizer_create_int(void *obj, + nr_interface_prioritizer_vtbl *vtbl,nr_interface_prioritizer **ifpp) + { + int _status; + nr_interface_prioritizer *ifp=0; + + if(!(ifp=RCALLOC(sizeof(nr_interface_prioritizer)))) + ABORT(R_NO_MEMORY); + + ifp->obj = obj; + ifp->vtbl = vtbl; + + *ifpp = ifp; + + _status=0; + abort: + return(_status); + } + +int nr_interface_prioritizer_destroy(nr_interface_prioritizer **ifpp) + { + nr_interface_prioritizer *ifp; + + if (!ifpp || !*ifpp) + return(0); + + ifp = *ifpp; + *ifpp = 0; + ifp->vtbl->destroy(&ifp->obj); + RFREE(ifp); + return(0); + } + +int nr_interface_prioritizer_add_interface(nr_interface_prioritizer *ifp, + nr_local_addr *addr) + { + return ifp->vtbl->add_interface(ifp->obj, addr); + } + +int nr_interface_prioritizer_get_priority(nr_interface_prioritizer *ifp, + const char *key, UCHAR *interface_preference) + { + return ifp->vtbl->get_priority(ifp->obj,key,interface_preference); + } + +int nr_interface_prioritizer_sort_preference(nr_interface_prioritizer *ifp) + { + return ifp->vtbl->sort_preference(ifp->obj); + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.h new file mode 100644 index 0000000000..c8a36526cc --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.h @@ -0,0 +1,66 @@ +/* +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, Mozilla 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. +*/ + +#ifndef _nr_interface_prioritizer +#define _nr_interface_prioritizer + +#include "transport_addr.h" +#include "local_addr.h" + +typedef struct nr_interface_prioritizer_vtbl_ { + int (*add_interface)(void *obj, nr_local_addr *iface); + int (*get_priority)(void *obj, const char *key, UCHAR *pref); + int (*sort_preference)(void *obj); + int (*destroy)(void **obj); +} nr_interface_prioritizer_vtbl; + +typedef struct nr_interface_prioritizer_ { + void *obj; + nr_interface_prioritizer_vtbl *vtbl; +} nr_interface_prioritizer; + +int nr_interface_prioritizer_create_int(void *obj, nr_interface_prioritizer_vtbl *vtbl, + nr_interface_prioritizer **prioritizer); + +int nr_interface_prioritizer_destroy(nr_interface_prioritizer **prioritizer); + +int nr_interface_prioritizer_add_interface(nr_interface_prioritizer *prioritizer, + nr_local_addr *addr); + +int nr_interface_prioritizer_get_priority(nr_interface_prioritizer *prioritizer, + const char *key, UCHAR *interface_preference); + +int nr_interface_prioritizer_sort_preference(nr_interface_prioritizer *prioritizer); +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.c new file mode 100644 index 0000000000..4dbf1bbe91 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.c @@ -0,0 +1,85 @@ +/* +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, Mozilla 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 "nr_resolver.h" + +int nr_resolver_create_int(void *obj, nr_resolver_vtbl *vtbl, nr_resolver **resolverp) +{ + int _status; + nr_resolver *resolver=0; + + if (!(resolver=RCALLOC(sizeof(nr_resolver)))) + ABORT(R_NO_MEMORY); + + resolver->obj=obj; + resolver->vtbl=vtbl; + + *resolverp=resolver; + _status=0; +abort: + return(_status); +} + +int nr_resolver_destroy(nr_resolver **resolverp) +{ + nr_resolver *resolver; + + if(!resolverp || !*resolverp) + return(0); + + resolver=*resolverp; + *resolverp=0; + + resolver->vtbl->destroy(&resolver->obj); + + RFREE(resolver); + + return(0); +} + +int nr_resolver_resolve(nr_resolver *resolver, + nr_resolver_resource *resource, + int (*cb)(void *cb_arg, nr_transport_addr *addr), + void *cb_arg, + void **handle) +{ + return resolver->vtbl->resolve(resolver->obj, resource, cb, cb_arg, handle); +} + +int nr_resolver_cancel(nr_resolver *resolver, void *handle) +{ + return resolver->vtbl->cancel(resolver->obj, handle); +} diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.h new file mode 100644 index 0000000000..376ba9998b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.h @@ -0,0 +1,96 @@ +/* +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, Mozilla 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. +*/ + +#ifndef _nr_resolver_h +#define _nr_resolver_h + +#include "transport_addr.h" + +#define NR_RESOLVE_PROTOCOL_STUN 1 +#define NR_RESOLVE_PROTOCOL_TURN 2 + +typedef struct nr_resolver_resource_ { + const char *domain_name; + UINT2 port; + int stun_turn; + UCHAR transport_protocol; + UCHAR address_family; +} nr_resolver_resource; + +typedef struct nr_resolver_vtbl_ { + int (*destroy)(void **obj); + int (*resolve)(void *obj, + nr_resolver_resource *resource, + int (*cb)(void *cb_arg, nr_transport_addr *addr), + void *cb_arg, + void **handle); + int (*cancel)(void *obj, void *handle); +} nr_resolver_vtbl; + +typedef struct nr_resolver_ { + void *obj; + nr_resolver_vtbl *vtbl; +} nr_resolver; + + +/* + The convention here is that the provider of this interface + must generate a void *obj, and a vtbl and then call + nr_resolver_create_int() to allocate the generic wrapper + object. + + The vtbl must contain implementations for all the functions + listed. + + The nr_resolver_destroy() function (and hence vtbl->destroy) + will be called when the consumer of the resolver is done + with it. That is the signal that it is safe to clean up + the resources associated with obj. No other function will + be called afterwards. +*/ +int nr_resolver_create_int(void *obj, nr_resolver_vtbl *vtbl, + nr_resolver **resolverp); +int nr_resolver_destroy(nr_resolver **resolverp); + +/* Request resolution of a domain */ +int nr_resolver_resolve(nr_resolver *resolver, + nr_resolver_resource *resource, + int (*cb)(void *cb_arg, nr_transport_addr *addr), + void *cb_arg, + void **handle); + +/* Cancel a requested resolution. No callback will fire. */ +int nr_resolver_cancel(nr_resolver *resolver, void *handle); +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.c new file mode 100644 index 0000000000..c9867610a6 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.c @@ -0,0 +1,187 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <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 && vtbl->version <= 2); + if (vtbl->version < 1 || vtbl->version > 2) + ABORT(R_INTERNAL); + + sock->obj=obj; + sock->vtbl=vtbl; + + *sockp=sock; + + _status=0; + abort: + return(_status); + } + +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, + const 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, const 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); + } + +int nr_socket_listen(nr_socket *sock, int backlog) + { + assert(sock->vtbl->version >=2 ); + CHECK_DEFINED(listen); + return sock->vtbl->listen(sock->obj, backlog); + } + +int nr_socket_accept(nr_socket *sock, nr_transport_addr *addrp, nr_socket **sockp) +{ + assert(sock->vtbl->version >= 2); + CHECK_DEFINED(accept); + return sock->vtbl->accept(sock->obj, addrp, sockp); +} + + +int nr_socket_factory_create_int(void *obj, + nr_socket_factory_vtbl *vtbl, nr_socket_factory **factorypp) + { + int _status; + nr_socket_factory *factoryp=0; + + if(!(factoryp=RCALLOC(sizeof(nr_socket_factory)))) + ABORT(R_NO_MEMORY); + + factoryp->obj = obj; + factoryp->vtbl = vtbl; + + *factorypp = factoryp; + + _status=0; + abort: + return(_status); + } + +int nr_socket_factory_destroy(nr_socket_factory **factorypp) + { + nr_socket_factory *factoryp; + + if (!factorypp || !*factorypp) + return (0); + + factoryp = *factorypp; + *factorypp = NULL; + factoryp->vtbl->destroy(&factoryp->obj); + RFREE(factoryp); + return (0); + } + +int nr_socket_factory_create_socket(nr_socket_factory *factory, nr_transport_addr *addr, nr_socket **sockp) + { + return factory->vtbl->create_socket(factory->obj, addr, sockp); + } + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.h new file mode 100644 index 0000000000..777837f6cc --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.h @@ -0,0 +1,123 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _nr_socket_h +#define _nr_socket_h + +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <sys/socket.h> +#endif + +#include "transport_addr.h" +#include "csi_platform.h" + +#ifdef __cplusplus +#define restrict +#elif defined(WIN32) +/* Undef before defining to avoid a compiler warning */ +#undef restrict +#define restrict __restrict +#endif + +typedef enum { + TCP_TYPE_NONE=0, + TCP_TYPE_ACTIVE, + TCP_TYPE_PASSIVE, + TCP_TYPE_SO, + TCP_TYPE_MAX +} nr_socket_tcp_type; + +typedef struct nr_socket_ nr_socket; + +typedef struct nr_socket_vtbl_ { + UINT4 version; /* Currently 2 */ + int (*destroy)(void **obj); + int (*ssendto)(void *obj,const void *msg, size_t len, int flags, + const 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, const 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); + + /* available since version 2 */ + int (*listen)(void *obj, int backlog); + int (*accept)(void *obj, nr_transport_addr *addrp, nr_socket **sockp); +} nr_socket_vtbl; + + +struct nr_socket_ { + void *obj; + nr_socket_vtbl *vtbl; +}; + +typedef struct nr_socket_factory_vtbl_ { + int (*create_socket)(void *obj, nr_transport_addr *addr, nr_socket **sockp); + int (*destroy)(void **obj); +} nr_socket_factory_vtbl; + +typedef struct nr_socket_factory_ { + void *obj; + nr_socket_factory_vtbl *vtbl; +} nr_socket_factory; + +/* To be called by constructors */ +int nr_socket_create_int(void *obj, nr_socket_vtbl *vtbl, nr_socket **sockp); +int nr_socket_destroy(nr_socket **sockp); +int nr_socket_sendto(nr_socket *sock,const void *msg, size_t len, + int flags, const 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, const 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); +int nr_socket_listen(nr_socket *sock, int backlog); +int nr_socket_accept(nr_socket *sock, nr_transport_addr *addrp, nr_socket **sockp); + +int nr_socket_factory_create_int(void *obj, nr_socket_factory_vtbl *vtbl, nr_socket_factory **factorypp); +int nr_socket_factory_destroy(nr_socket_factory **factoryp); +int nr_socket_factory_create_socket(nr_socket_factory *factory, nr_transport_addr *addr, nr_socket **sockp); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_local.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_local.h new file mode 100644 index 0000000000..a2f813ff66 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_local.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _nr_socket_local_h +#define _nr_socket_local_h + +int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c new file mode 100644 index 0000000000..9b2489b214 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c @@ -0,0 +1,642 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2014, 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 <assert.h> +#include <sys/types.h> + +#include "nr_api.h" +#include "ice_ctx.h" +#include "nr_socket.h" +#include "nr_socket_local.h" +#include "nr_socket_multi_tcp.h" +#include "nr_socket_buffered_stun.h" +#include "async_timer.h" + +typedef struct nr_tcp_socket_ctx_ { + nr_socket * inner; + nr_transport_addr remote_addr; + int is_framed; + + TAILQ_ENTRY(nr_tcp_socket_ctx_) entry; +} nr_tcp_socket_ctx; + +typedef TAILQ_HEAD(nr_tcp_socket_head_,nr_tcp_socket_ctx_) nr_tcp_socket_head; + +static void nr_tcp_socket_readable_cb(NR_SOCKET s, int how, void *arg); + +static int nr_tcp_socket_ctx_destroy(nr_tcp_socket_ctx **objp) + { + nr_tcp_socket_ctx *sock; + + if (!objp || !*objp) + return(0); + + sock=*objp; + *objp=0; + + nr_socket_destroy(&sock->inner); + + RFREE(sock); + + return(0); + } + +/* This takes ownership of nrsock whether it fails or not. */ +static int nr_tcp_socket_ctx_create(nr_socket *nrsock, int is_framed, + int max_pending, nr_tcp_socket_ctx **sockp) + { + int r, _status; + nr_tcp_socket_ctx *sock = 0; + nr_socket *tcpsock; + + if (!(sock = RCALLOC(sizeof(nr_tcp_socket_ctx)))) { + nr_socket_destroy(&nrsock); + ABORT(R_NO_MEMORY); + } + + if ((r=nr_socket_buffered_stun_create(nrsock, max_pending, is_framed ? ICE_TCP_FRAMING : TURN_TCP_FRAMING, &tcpsock))){ + nr_socket_destroy(&nrsock); + ABORT(r); + } + + sock->inner=tcpsock; + sock->is_framed=is_framed; + + if ((r=nr_ip4_port_to_transport_addr(ntohl(INADDR_ANY), 0, IPPROTO_TCP, &sock->remote_addr))) + ABORT(r); + + *sockp=sock; + + _status=0; +abort: + if (_status) { + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s failed with error %d",__FILE__,__LINE__,__FUNCTION__,_status); + nr_tcp_socket_ctx_destroy(&sock); + } + return(_status); + } + +static int nr_tcp_socket_ctx_initialize(nr_tcp_socket_ctx *tcpsock, + const nr_transport_addr *addr, void* cb_arg) + { + int r, _status; + NR_SOCKET fd; + + if ((r=nr_transport_addr_copy(&tcpsock->remote_addr, addr))) + ABORT(r); + if ((r=nr_socket_getfd(tcpsock->inner, &fd))) + ABORT(r); + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, nr_tcp_socket_readable_cb, cb_arg); + + _status=0; + abort: + if (_status) + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + return(_status); + } + +typedef struct nr_socket_multi_tcp_ { + nr_ice_ctx *ctx; + nr_socket *listen_socket; + nr_tcp_socket_head sockets; + nr_socket_tcp_type tcp_type; + nr_transport_addr addr; + NR_async_cb readable_cb; + void *readable_cb_arg; + int max_pending; +} nr_socket_multi_tcp; + +static int nr_socket_multi_tcp_destroy(void **objp); +static int nr_socket_multi_tcp_sendto(void *obj,const void *msg, size_t len, + int flags, const nr_transport_addr *to); +static int nr_socket_multi_tcp_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from); +static int nr_socket_multi_tcp_getaddr(void *obj, nr_transport_addr *addrp); +static int nr_socket_multi_tcp_close(void *obj); +static int nr_socket_multi_tcp_connect(void *sock, const nr_transport_addr *addr); +static int nr_socket_multi_tcp_listen(void *obj, int backlog); + +static nr_socket_vtbl nr_socket_multi_tcp_vtbl={ + 2, + nr_socket_multi_tcp_destroy, + nr_socket_multi_tcp_sendto, + nr_socket_multi_tcp_recvfrom, + 0, + nr_socket_multi_tcp_getaddr, + nr_socket_multi_tcp_connect, + 0, + 0, + nr_socket_multi_tcp_close, + nr_socket_multi_tcp_listen, + 0 +}; + +static int nr_socket_multi_tcp_create_stun_server_socket( + nr_socket_multi_tcp *sock, nr_ice_stun_server * stun_server, + nr_transport_addr *addr, int max_pending) + { + int r, _status; + nr_tcp_socket_ctx *tcp_socket_ctx=0; + nr_socket * nrsock; + + if (stun_server->addr.protocol != IPPROTO_TCP) { + r_log(LOG_ICE, LOG_INFO, + "%s:%d function %s skipping UDP STUN server(addr:%s)", __FILE__, + __LINE__, __FUNCTION__, stun_server->addr.as_string); + ABORT(R_BAD_ARGS); + } + + if (nr_transport_addr_cmp(&stun_server->addr, addr, + NR_TRANSPORT_ADDR_CMP_MODE_VERSION)) { + r_log(LOG_ICE, LOG_INFO, + "%s:%d function %s skipping STUN with different IP version (%u) " + "than local socket (%u),", + __FILE__, __LINE__, __FUNCTION__, stun_server->addr.ip_version, + addr->ip_version); + ABORT(R_BAD_ARGS); + } + + if ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory,addr, &nrsock))) + ABORT(r); + + /* This takes ownership of nrsock whether it fails or not. */ + if ((r=nr_tcp_socket_ctx_create(nrsock, 0, max_pending, &tcp_socket_ctx))) + ABORT(r); + + nr_transport_addr stun_server_addr; + + nr_transport_addr_copy(&stun_server_addr, &stun_server->addr); + r = nr_socket_connect(tcp_socket_ctx->inner, &stun_server_addr); + if (r && r != R_WOULDBLOCK) { + r_log(LOG_ICE, LOG_WARNING, + "%s:%d function %s connect to STUN server(addr:%s) failed with " + "error %d", + __FILE__, __LINE__, __FUNCTION__, stun_server_addr.as_string, r); + ABORT(r); + } + + if ((r = nr_tcp_socket_ctx_initialize(tcp_socket_ctx, &stun_server_addr, + sock))) + ABORT(r); + + TAILQ_INSERT_TAIL(&sock->sockets, tcp_socket_ctx, entry); + + _status=0; + abort: + if (_status) { + nr_tcp_socket_ctx_destroy(&tcp_socket_ctx); + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + } + return(_status); + } + +int nr_socket_multi_tcp_create(struct nr_ice_ctx_ *ctx, + struct nr_ice_component_ *component, + nr_transport_addr *addr, nr_socket_tcp_type tcp_type, + int precreated_so_count, int max_pending, nr_socket **sockp) + { + int i=0; + int r, _status; + nr_socket_multi_tcp *sock=0; + nr_tcp_socket_ctx *tcp_socket_ctx; + nr_socket * nrsock; + + if (!(sock = RCALLOC(sizeof(nr_socket_multi_tcp)))) + ABORT(R_NO_MEMORY); + + TAILQ_INIT(&sock->sockets); + + sock->ctx=ctx; + sock->max_pending=max_pending; + sock->tcp_type=tcp_type; + nr_transport_addr_copy(&sock->addr, addr); + + if((tcp_type==TCP_TYPE_PASSIVE) && + ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory, addr, &sock->listen_socket)))) + ABORT(r); + + if (tcp_type!=TCP_TYPE_ACTIVE) { + nr_ice_stun_server *stun_servers; + nr_ice_turn_server *turn_servers; + int stun_server_ct, turn_server_ct; + if (component) { + stun_servers = component->stream->stun_servers; + turn_servers = component->stream->turn_servers; + stun_server_ct = component->stream->stun_server_ct; + turn_server_ct = component->stream->turn_server_ct; + } else { + /* Mainly for unit-testing */ + stun_servers = ctx->stun_servers_cfg; + turn_servers = ctx->turn_servers_cfg; + stun_server_ct = ctx->stun_server_ct_cfg; + turn_server_ct = ctx->turn_server_ct_cfg; + } + if (stun_servers) { + for (i=0; i<stun_server_ct; ++i) { + if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock, + stun_servers+i, addr, max_pending))) { + if (r!=R_BAD_ARGS) { + r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s failed to connect STUN server from addr:%s with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,r); + } + } + } + } + if (turn_servers) { + for (i=0; i<turn_server_ct; ++i) { + if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock, + &(turn_servers[i]).turn_server, addr, max_pending))) { + if (r!=R_BAD_ARGS) { + r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s failed to connect TURN server from addr:%s with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,r); + } + } + } + } + } + + if ((tcp_type==TCP_TYPE_SO)) { + for (i=0; i<precreated_so_count; ++i) { + + if ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory, addr, &nrsock))) + ABORT(r); + + /* This takes ownership of nrsock whether it fails or not. */ + if ((r=nr_tcp_socket_ctx_create(nrsock, 1, max_pending, &tcp_socket_ctx))){ + ABORT(r); + } + TAILQ_INSERT_TAIL(&sock->sockets, tcp_socket_ctx, entry); + } + } + + if((r=nr_socket_create_int(sock, &nr_socket_multi_tcp_vtbl, sockp))) + ABORT(r); + + _status=0; + abort: + if (_status) { + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + nr_socket_multi_tcp_destroy((void**)&sock); + } + return(_status); + } + +int nr_socket_multi_tcp_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb, void *readable_cb_arg) + { + nr_socket_multi_tcp *mtcp_sock = (nr_socket_multi_tcp *)sock->obj; + + mtcp_sock->readable_cb=readable_cb; + mtcp_sock->readable_cb_arg=readable_cb_arg; + + return 0; + } + +#define PREALLOC_CONNECT_FRAMED 0 +#define PREALLOC_CONNECT_NON_FRAMED 1 +#define PREALLOC_DONT_CONNECT_UNLESS_SO 2 + +static int nr_socket_multi_tcp_get_sock_connected_to(nr_socket_multi_tcp *sock, + const nr_transport_addr *to, int preallocated_connect_mode, nr_socket **ret_sock) + { + int r, _status; + nr_tcp_socket_ctx *tcp_sock_ctx; + nr_socket * nrsock; + + TAILQ_FOREACH(tcp_sock_ctx, &sock->sockets, entry) { + if (!nr_transport_addr_is_wildcard(&tcp_sock_ctx->remote_addr)) { + if (!nr_transport_addr_cmp(to, &tcp_sock_ctx->remote_addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + *ret_sock=tcp_sock_ctx->inner; + return(0); + } + } + } + + tcp_sock_ctx=NULL; + /* not connected yet */ + if (sock->tcp_type != TCP_TYPE_ACTIVE) { + if (preallocated_connect_mode == PREALLOC_DONT_CONNECT_UNLESS_SO && sock->tcp_type != TCP_TYPE_SO) + ABORT(R_FAILED); + + /* find free preallocated socket and connect */ + TAILQ_FOREACH(tcp_sock_ctx, &sock->sockets, entry) { + if (nr_transport_addr_is_wildcard(&tcp_sock_ctx->remote_addr)) { + if (preallocated_connect_mode == PREALLOC_CONNECT_NON_FRAMED && tcp_sock_ctx->is_framed) + continue; + if (preallocated_connect_mode != PREALLOC_CONNECT_NON_FRAMED && !tcp_sock_ctx->is_framed) + continue; + + if ((r=nr_socket_connect(tcp_sock_ctx->inner, to))){ + if (r!=R_WOULDBLOCK) + ABORT(r); + } + + if ((r=nr_tcp_socket_ctx_initialize(tcp_sock_ctx, to, sock))) + ABORT(r); + + *ret_sock=tcp_sock_ctx->inner; + + return(0); + } + } + tcp_sock_ctx=NULL; + ABORT(R_FAILED); + } + + /* if active type - create new socket for each new remote addr */ + assert(sock->tcp_type == TCP_TYPE_ACTIVE); + + if ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory, &sock->addr, &nrsock))) + ABORT(r); + + /* This takes ownership of nrsock whether it fails or not. */ + if ((r=nr_tcp_socket_ctx_create(nrsock, 1, sock->max_pending, &tcp_sock_ctx))){ + ABORT(r); + } + + TAILQ_INSERT_TAIL(&sock->sockets, tcp_sock_ctx, entry); + + if ((r=nr_socket_connect(tcp_sock_ctx->inner, to))){ + if (r!=R_WOULDBLOCK) + ABORT(r); + } + + if ((r=nr_tcp_socket_ctx_initialize(tcp_sock_ctx, to, sock))) + ABORT(r); + + *ret_sock=tcp_sock_ctx->inner; + tcp_sock_ctx=NULL; + + _status=0; + abort: + if (_status) { + if (tcp_sock_ctx) { + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s failed with error %d, tcp_sock_ctx remote_addr: %s",__FILE__,__LINE__,__FUNCTION__,_status, tcp_sock_ctx->remote_addr.as_string); + TAILQ_REMOVE(&sock->sockets, tcp_sock_ctx, entry); + nr_tcp_socket_ctx_destroy(&tcp_sock_ctx); + } else { + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s failed with error %d, tcp_sock_ctx=NULL",__FILE__,__LINE__,__FUNCTION__,_status); + } + } + + return(_status); + } + +int nr_socket_multi_tcp_stun_server_connect(nr_socket *sock, + const nr_transport_addr *addr) + { + int r, _status; + nr_socket_multi_tcp *mtcp_sock = (nr_socket_multi_tcp *)sock->obj; + nr_socket *nrsock; + + assert(mtcp_sock->tcp_type != TCP_TYPE_ACTIVE); + if (mtcp_sock->tcp_type == TCP_TYPE_ACTIVE) + ABORT(R_INTERNAL); + + if ((r=nr_socket_multi_tcp_get_sock_connected_to(mtcp_sock,addr,PREALLOC_CONNECT_NON_FRAMED,&nrsock))) + ABORT(r); + + _status=0; + abort: + if (_status) + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + return(_status); + } + +static int nr_socket_multi_tcp_destroy(void **objp) + { + nr_socket_multi_tcp *sock; + nr_tcp_socket_ctx *tcpsock; + NR_SOCKET fd; + + if (!objp || !*objp) + return 0; + + sock=(nr_socket_multi_tcp *)*objp; + *objp=0; + + /* Cancel waiting on the socket */ + if (sock->listen_socket && !nr_socket_getfd(sock->listen_socket, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + + nr_socket_destroy(&sock->listen_socket); + + while (!TAILQ_EMPTY(&sock->sockets)) { + + tcpsock = TAILQ_FIRST(&sock->sockets); + TAILQ_REMOVE(&sock->sockets, tcpsock, entry); + + if (!nr_socket_getfd(tcpsock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + + nr_tcp_socket_ctx_destroy(&tcpsock); + } + + RFREE(sock); + + return 0; + } + +static int nr_socket_multi_tcp_sendto(void *obj, const void *msg, size_t len, + int flags, const nr_transport_addr *to) + { + int r, _status; + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + nr_socket *nrsock; + + if ((r=nr_socket_multi_tcp_get_sock_connected_to(sock, to, + PREALLOC_DONT_CONNECT_UNLESS_SO, &nrsock))) + ABORT(r); + + if((r=nr_socket_sendto(nrsock, msg, len, flags, to))) + ABORT(r); + + _status=0; + abort: + if (_status) + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(to:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,to->as_string,_status); + + return(_status); +} + +static int nr_socket_multi_tcp_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from) + { + int r, _status = 0; + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + nr_tcp_socket_ctx *tcpsock; + + if (TAILQ_EMPTY(&sock->sockets)) + ABORT(R_FAILED); + + TAILQ_FOREACH(tcpsock, &sock->sockets, entry) { + if (nr_transport_addr_is_wildcard(&tcpsock->remote_addr)) + continue; + r=nr_socket_recvfrom(tcpsock->inner, buf, maxlen, len, flags, from); + if (!r) + return 0; + + if (r!=R_WOULDBLOCK) { + NR_SOCKET fd; + r_log(LOG_ICE,LOG_DEBUG, + "%s:%d function %s(to:%s) failed with error %d",__FILE__, + __LINE__,__FUNCTION__,tcpsock->remote_addr.as_string,r); + if (!nr_socket_getfd(tcpsock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + } + + TAILQ_REMOVE(&sock->sockets, tcpsock, entry); + nr_tcp_socket_ctx_destroy(&tcpsock); + ABORT(r); + } + } + + /* this also gets returned if all tcpsocks have wildcard remote_addr */ + _status=R_WOULDBLOCK; + abort: + + return(_status); + } + +static int nr_socket_multi_tcp_getaddr(void *obj, nr_transport_addr *addrp) + { + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + + return nr_transport_addr_copy(addrp,&sock->addr); + } + +static int nr_socket_multi_tcp_close(void *obj) + { + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + nr_tcp_socket_ctx *tcpsock; + + if(sock->listen_socket) + nr_socket_close(sock->listen_socket); + + TAILQ_FOREACH(tcpsock, &sock->sockets, entry) { + nr_socket_close(tcpsock->inner); //ignore errors + } + + return 0; + } + +static void nr_tcp_socket_readable_cb(NR_SOCKET s, int how, void *arg) + { + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)arg; + + // rearm + NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, nr_tcp_socket_readable_cb, arg); + + if (sock->readable_cb) + sock->readable_cb(s, how, sock->readable_cb_arg); + } + +static int nr_socket_multi_tcp_connect(void *obj, const nr_transport_addr *addr) + { + int r, _status; + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + nr_socket *nrsock; + + if ((r=nr_socket_multi_tcp_get_sock_connected_to(sock,addr,PREALLOC_CONNECT_FRAMED,&nrsock))) + ABORT(r); + + _status=0; +abort: + if (_status) + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + + return(_status); + } + +static void nr_tcp_multi_lsocket_readable_cb(NR_SOCKET s, int how, void *arg) + { + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)arg; + nr_socket *newsock; + nr_transport_addr remote_addr; + nr_tcp_socket_ctx *tcp_sock_ctx; + int r, _status; + + // rearm + NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, nr_tcp_multi_lsocket_readable_cb, arg); + + /* accept */ + if ((r=nr_socket_accept(sock->listen_socket, &remote_addr, &newsock))) + ABORT(r); + + /* This takes ownership of newsock whether it fails or not. */ + if ((r=nr_tcp_socket_ctx_create(newsock, 1, sock->max_pending, &tcp_sock_ctx))) + ABORT(r); + + nr_socket_buffered_set_connected_to(tcp_sock_ctx->inner, &remote_addr); + + if ((r=nr_tcp_socket_ctx_initialize(tcp_sock_ctx, &remote_addr, sock))) { + nr_tcp_socket_ctx_destroy(&tcp_sock_ctx); + ABORT(r); + } + + TAILQ_INSERT_HEAD(&sock->sockets, tcp_sock_ctx, entry); + + _status=0; +abort: + if (_status) { + r_log(LOG_ICE,LOG_WARNING,"%s:%d %s failed to accept new TCP connection: %d",__FILE__,__LINE__,__FUNCTION__,_status); + } else { + r_log(LOG_ICE,LOG_INFO,"%s:%d %s accepted new TCP connection from %s",__FILE__,__LINE__,__FUNCTION__,remote_addr.as_string); + } + } + +static int nr_socket_multi_tcp_listen(void *obj, int backlog) + { + int r, _status; + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + NR_SOCKET fd; + + if(!sock->listen_socket) + ABORT(R_FAILED); + + if ((r=nr_socket_listen(sock->listen_socket, backlog))) + ABORT(r); + + if ((r=nr_socket_getfd(sock->listen_socket, &fd))) + ABORT(r); + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, nr_tcp_multi_lsocket_readable_cb, sock); + + _status=0; + abort: + if (_status) + r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s failed with error %d",__FILE__,__LINE__,__FUNCTION__,_status); + + return(_status); + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.h new file mode 100644 index 0000000000..8413e67293 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.h @@ -0,0 +1,53 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2014, 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. +*/ + +#ifndef _nr_socket_multi_tcp_h +#define _nr_socket_multi_tcp_h + +#include "nr_socket.h" + +/* Argument use_framing is 0 only in call from test code (STUN TCP server + listening socket). For other purposes it should be always set to true */ + +int nr_socket_multi_tcp_create(struct nr_ice_ctx_ *ctx, + struct nr_ice_component_ *component, + nr_transport_addr *addr, nr_socket_tcp_type tcp_type, + int precreated_so_count, int max_pending, nr_socket **sockp); + +int nr_socket_multi_tcp_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb,void *readable_cb_arg); + +int nr_socket_multi_tcp_stun_server_connect(nr_socket *sock, + const nr_transport_addr *addr); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.c new file mode 100644 index 0000000000..0c9ec56744 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.c @@ -0,0 +1,82 @@ +/* +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, Mozilla 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 "nr_socket_wrapper.h" + +int nr_socket_wrapper_factory_create_int(void *obj, nr_socket_wrapper_factory_vtbl *vtbl, + nr_socket_wrapper_factory **wrapperp) +{ + int _status; + nr_socket_wrapper_factory *wrapper=0; + + if (!(wrapper=RCALLOC(sizeof(nr_socket_wrapper_factory)))) + ABORT(R_NO_MEMORY); + + wrapper->obj=obj; + wrapper->vtbl=vtbl; + + *wrapperp=wrapper; + _status=0; +abort: + return(_status); +} + +int nr_socket_wrapper_factory_wrap(nr_socket_wrapper_factory *wrapper, + nr_socket *inner, + nr_socket **socketp) +{ + return wrapper->vtbl->wrap(wrapper->obj, inner, socketp); +} + +int nr_socket_wrapper_factory_destroy(nr_socket_wrapper_factory **wrapperp) +{ + nr_socket_wrapper_factory *wrapper; + + if (!wrapperp || !*wrapperp) + return 0; + + wrapper = *wrapperp; + *wrapperp = 0; + + assert(wrapper->vtbl); + if (wrapper->vtbl) + wrapper->vtbl->destroy(&wrapper->obj); + + RFREE(wrapper); + + return 0; +} + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.h new file mode 100644 index 0000000000..717518e23e --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.h @@ -0,0 +1,63 @@ +/* +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, Mozilla 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. +*/ + +#ifndef _nr_socket_wrapper_h +#define _nr_socket_wrapper_h + +#include "nr_socket.h" + +typedef struct nr_socket_wrapper_factory_vtbl_ { + int (*wrap)(void *obj, + nr_socket *socket, + nr_socket **socketp); + int (*destroy)(void **obj); +} nr_socket_wrapper_factory_vtbl; + +typedef struct nr_socket_wrapper_factory_ { + void *obj; + nr_socket_wrapper_factory_vtbl *vtbl; +} nr_socket_wrapper_factory; + + +int nr_socket_wrapper_factory_create_int(void *obj, nr_socket_wrapper_factory_vtbl *vtbl, + nr_socket_wrapper_factory **wrapperp); + + +int nr_socket_wrapper_factory_wrap(nr_socket_wrapper_factory *wrapper, nr_socket *inner, + nr_socket **socketp); + +int nr_socket_wrapper_factory_destroy(nr_socket_wrapper_factory **wrapperp); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.c new file mode 100644 index 0000000000..efedb3782a --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.c @@ -0,0 +1,559 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <csi_platform.h> +#include <stdio.h> +#include <memory.h> +#include <sys/types.h> +#include <errno.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include <assert.h> +#include "nr_api.h" +#include "util.h" +#include "transport_addr.h" + +int nr_transport_addr_fmt_addr_string(nr_transport_addr *addr) + { + int _status; + /* Max length for normalized IPv6 address string representation is 39 */ + char buffer[40]; + const char *protocol; + + switch(addr->protocol){ + case IPPROTO_TCP: + if (addr->tls) { + protocol = "TLS"; + } else { + protocol = "TCP"; + } + break; + case IPPROTO_UDP: + protocol = "UDP"; + break; + default: + ABORT(R_INTERNAL); + } + + switch(addr->ip_version){ + case NR_IPV4: + if (!inet_ntop(AF_INET, &addr->u.addr4.sin_addr,buffer,sizeof(buffer))) + strcpy(buffer, "[error]"); + snprintf(addr->as_string,sizeof(addr->as_string),"IP4:%s:%d/%s",buffer,(int)ntohs(addr->u.addr4.sin_port),protocol); + break; + case NR_IPV6: + if (!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,buffer,sizeof(buffer))) + strcpy(buffer, "[error]"); + snprintf(addr->as_string,sizeof(addr->as_string),"IP6:[%s]:%d/%s",buffer,(int)ntohs(addr->u.addr6.sin6_port),protocol); + break; + default: + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +int nr_transport_addr_fmt_ifname_addr_string(const nr_transport_addr *addr, char *buf, int len) + { + int _status; + /* leave room for a fully-expanded IPV4-mapped IPV6 address */ + char buffer[46]; + + switch(addr->ip_version){ + case NR_IPV4: + if (!inet_ntop(AF_INET, &addr->u.addr4.sin_addr,buffer,sizeof(buffer))) { + strncpy(buffer, "[error]", sizeof(buffer)); + } + break; + case NR_IPV6: + if (!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,buffer,sizeof(buffer))) { + strncpy(buffer, "[error]", sizeof(buffer)); + } + break; + default: + ABORT(R_INTERNAL); + } + buffer[sizeof(buffer) - 1] = '\0'; + + snprintf(buf,len,"%s:%s",addr->ifname,buffer); + buf[len - 1] = '\0'; + + _status=0; + abort: + return(_status); + } + +int nr_sockaddr_to_transport_addr(struct sockaddr *saddr, int protocol, int keep, nr_transport_addr *addr) + { + int r,_status; + + if(!keep) memset(addr,0,sizeof(nr_transport_addr)); + + switch(protocol){ + case IPPROTO_TCP: + case IPPROTO_UDP: + break; + default: + ABORT(R_BAD_ARGS); + } + + addr->protocol=protocol; + + if(saddr->sa_family==AF_INET){ + addr->ip_version=NR_IPV4; + + memcpy(&addr->u.addr4,saddr,sizeof(struct sockaddr_in)); + } + else if(saddr->sa_family==AF_INET6){ + addr->ip_version=NR_IPV6; + + memcpy(&addr->u.addr6, saddr, sizeof(struct sockaddr_in6)); + } + else + ABORT(R_BAD_ARGS); + + if(r=nr_transport_addr_fmt_addr_string(addr)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + + +int nr_transport_addr_copy(nr_transport_addr *to, const nr_transport_addr *from) + { + memcpy(to,from,sizeof(nr_transport_addr)); + return 0; + } + +int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, const nr_transport_addr *from) + { + int r,_status; + char save_ifname[MAXIFNAME]; + + strncpy(save_ifname, to->ifname, MAXIFNAME); + save_ifname[MAXIFNAME-1]=0; /* Ensure null termination */ + + if (r=nr_transport_addr_copy(to, from)) + ABORT(r); + + strncpy(to->ifname, save_ifname, MAXIFNAME); + + if (r=nr_transport_addr_fmt_addr_string(to)) + ABORT(r); + + _status=0; + abort: + return _status; + } + +int nr_transport_addr_copy_addrport(nr_transport_addr *to, const nr_transport_addr *from) + { + int r,_status; + + switch (from->ip_version) { + case NR_IPV4: + memcpy(&to->u.addr4, &from->u.addr4, sizeof(to->u.addr4)); + break; + case NR_IPV6: + memcpy(&to->u.addr6, &from->u.addr6, sizeof(to->u.addr6)); + break; + default: + ABORT(R_BAD_ARGS); + } + + to->ip_version = from->ip_version; + + if (r=nr_transport_addr_fmt_addr_string(to)) { + ABORT(r); + } + + _status=0; + abort: + return _status; + } + +/* Convenience fxn. Is this the right API?*/ +int nr_ip4_port_to_transport_addr(UINT4 ip4, UINT2 port, int protocol, nr_transport_addr *addr) + { + int r,_status; + + memset(addr, 0, sizeof(nr_transport_addr)); + + addr->ip_version=NR_IPV4; + addr->protocol=protocol; +#ifdef HAVE_SIN_LEN + addr->u.addr4.sin_len=sizeof(struct sockaddr_in); +#endif + addr->u.addr4.sin_family=PF_INET; + addr->u.addr4.sin_port=htons(port); + addr->u.addr4.sin_addr.s_addr=htonl(ip4); + + if(r=nr_transport_addr_fmt_addr_string(addr)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_str_port_to_transport_addr(const char *ip, UINT2 port, int protocol, nr_transport_addr *addr_out) + { + int r,_status; + struct in_addr addr; + struct in6_addr addr6; + + if (inet_pton(AF_INET, ip, &addr) == 1) { + if(r=nr_ip4_port_to_transport_addr(ntohl(addr.s_addr),port,protocol,addr_out)) + ABORT(r); + } else if (inet_pton(AF_INET6, ip, &addr6) == 1) { + if(r=nr_ip6_port_to_transport_addr(&addr6,port,protocol,addr_out)) + ABORT(r); + } else { + ABORT(R_BAD_DATA); + } + + _status=0; + abort: + return(_status); + } + +int nr_ip6_port_to_transport_addr(struct in6_addr* addr6, UINT2 port, int protocol, nr_transport_addr *addr) + { + int r,_status; + + memset(addr, 0, sizeof(nr_transport_addr)); + + addr->ip_version=NR_IPV6; + addr->protocol=protocol; + addr->u.addr6.sin6_family=PF_INET6; + addr->u.addr6.sin6_port=htons(port); + memcpy(addr->u.addr6.sin6_addr.s6_addr, addr6->s6_addr, sizeof(addr6->s6_addr)); + + if(r=nr_transport_addr_fmt_addr_string(addr)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_transport_addr_get_addrstring(const nr_transport_addr *addr, char *str, int maxlen) + { + int _status; + + if (addr->fqdn[0]) { + strncpy(str, addr->fqdn, maxlen); + } else { + const char* res; + switch (addr->ip_version) { + case NR_IPV4: + res = inet_ntop(AF_INET, &addr->u.addr4.sin_addr, str, maxlen); + break; + case NR_IPV6: + res = inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr, str, maxlen); + break; + default: + ABORT(R_INTERNAL); + } + + if (!res) { + if (errno == ENOSPC) { + ABORT(R_BAD_ARGS); + } + ABORT(R_INTERNAL); + } + } + + _status=0; + abort: + return(_status); + } + +int nr_transport_addr_get_port(const nr_transport_addr *addr, int *port) + { + int _status; + + switch(addr->ip_version){ + case NR_IPV4: + *port=ntohs(addr->u.addr4.sin_port); + break; + case NR_IPV6: + *port=ntohs(addr->u.addr6.sin6_port); + break; + default: + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +int nr_transport_addr_set_port(nr_transport_addr *addr, int port) + { + int _status; + + switch(addr->ip_version){ + case NR_IPV4: + addr->u.addr4.sin_port=htons(port); + break; + case NR_IPV6: + addr->u.addr6.sin6_port=htons(port); + break; + default: + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +/* memcmp() may not work if, for instance, the string or interface + haven't been made. Hmmm.. */ +int nr_transport_addr_cmp(const nr_transport_addr *addr1,const nr_transport_addr *addr2,int mode) + { + assert(mode); + + if(addr1->ip_version != addr2->ip_version) + return(1); + + if(mode < NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL) + return(0); + + if(addr1->protocol != addr2->protocol) + return(1); + + if(mode < NR_TRANSPORT_ADDR_CMP_MODE_ADDR) + return(0); + + switch(addr1->ip_version){ + case NR_IPV4: + if(addr1->u.addr4.sin_addr.s_addr != addr2->u.addr4.sin_addr.s_addr) + return(1); + if(mode < NR_TRANSPORT_ADDR_CMP_MODE_ALL) + return(0); + if(addr1->u.addr4.sin_port != addr2->u.addr4.sin_port) + return(1); + break; + case NR_IPV6: + if(memcmp(addr1->u.addr6.sin6_addr.s6_addr,addr2->u.addr6.sin6_addr.s6_addr,sizeof(struct in6_addr))) + return(1); + if(mode < NR_TRANSPORT_ADDR_CMP_MODE_ALL) + return(0); + if(addr1->u.addr6.sin6_port != addr2->u.addr6.sin6_port) + return(1); + break; + default: + abort(); + } + + return(0); + } + +int nr_transport_addr_is_loopback(const nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + switch(addr->u.addr4.sin_family){ + case AF_INET: + if (((ntohl(addr->u.addr4.sin_addr.s_addr)>>24)&0xff)==0x7f) + return 1; + break; + default: + NR_UNIMPLEMENTED; + break; + } + break; + + case NR_IPV6: + if(!memcmp(addr->u.addr6.sin6_addr.s6_addr,in6addr_loopback.s6_addr,sizeof(struct in6_addr))) + return(1); + break; + default: + NR_UNIMPLEMENTED; + } + + return(0); + } + +int nr_transport_addr_is_link_local(const nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + /* RFC3927: 169.254/16 */ + if ((ntohl(addr->u.addr4.sin_addr.s_addr) & 0xFFFF0000) == 0xA9FE0000) + return(1); + break; + case NR_IPV6: + { + UINT4* addrTop = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr); + if ((*addrTop & htonl(0xFFC00000)) == htonl(0xFE800000)) + return(2); + } + break; + default: + NR_UNIMPLEMENTED; + } + + return(0); + } + +int nr_transport_addr_is_mac_based(const nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + // IPv4 has no MAC based self assigned IP addresses + return(0); + case NR_IPV6: + { + // RFC 2373, Appendix A: lower 64bit 0x020000FFFE000000 + // indicates a MAC based IPv6 address + UINT4* macCom = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr + 8); + UINT4* macExt = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr + 12); + if ((*macCom & htonl(0x020000FF)) == htonl(0x020000FF) && + (*macExt & htonl(0xFF000000)) == htonl(0xFE000000)) { + return(1); + } + } + break; + default: + NR_UNIMPLEMENTED; + } + return(0); + } + +int nr_transport_addr_is_teredo(const nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + return(0); + case NR_IPV6: + { + UINT4* addrTop = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr); + if ((*addrTop & htonl(0xFFFFFFFF)) == htonl(0x20010000)) + return(1); + } + break; + default: + NR_UNIMPLEMENTED; + } + + return(0); + } + +int nr_transport_addr_check_compatibility(const nr_transport_addr *addr1, const nr_transport_addr *addr2) + { + // first make sure we're comparing the same ip versions and protocols + if ((addr1->ip_version != addr2->ip_version) || + (addr1->protocol != addr2->protocol)) { + return(1); + } + + if (!addr1->fqdn[0] && !addr2->fqdn[0]) { + // now make sure the link local status matches + if (nr_transport_addr_is_link_local(addr1) != + nr_transport_addr_is_link_local(addr2)) { + return(1); + } + } + return(0); + } + +int nr_transport_addr_is_wildcard(const nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + if(addr->u.addr4.sin_addr.s_addr==INADDR_ANY) + return(1); + if(addr->u.addr4.sin_port==0) + return(1); + break; + case NR_IPV6: + if(!memcmp(addr->u.addr6.sin6_addr.s6_addr,in6addr_any.s6_addr,sizeof(struct in6_addr))) + return(1); + if(addr->u.addr6.sin6_port==0) + return(1); + break; + default: + NR_UNIMPLEMENTED; + } + + return(0); + } + +nr_transport_addr_mask nr_private_ipv4_addrs[] = { + /* RFC1918: 10/8 */ + {0x0A000000, 0xFF000000}, + /* RFC1918: 172.16/12 */ + {0xAC100000, 0xFFF00000}, + /* RFC1918: 192.168/16 */ + {0xC0A80000, 0xFFFF0000}, + /* RFC6598: 100.64/10 */ + {0x64400000, 0xFFC00000} +}; + +int nr_transport_addr_get_private_addr_range(const nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + { + UINT4 ip = ntohl(addr->u.addr4.sin_addr.s_addr); + for (size_t i=0; i<(sizeof(nr_private_ipv4_addrs)/sizeof(nr_transport_addr_mask)); i++) { + if ((ip & nr_private_ipv4_addrs[i].mask) == nr_private_ipv4_addrs[i].addr) + return i + 1; + } + } + break; + case NR_IPV6: + return(0); + default: + NR_UNIMPLEMENTED; + } + + return(0); + } + +int nr_transport_addr_is_reliable_transport(const nr_transport_addr *addr) + { + return addr->protocol == IPPROTO_TCP; + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.h new file mode 100644 index 0000000000..c75c70a1d9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.h @@ -0,0 +1,112 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _transport_addr_h +#define _transport_addr_h + +#include <stdbool.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +#include "r_types.h" + +/* Length of a string hex representation of a MD5 hash */ +#define MAXIFNAME 33 + +/* Generic transport address + + This spans both sockaddr_in and sockaddr_in6 + */ +typedef struct nr_transport_addr_ { + UCHAR ip_version; /* 4 or 6 */ +#define NR_IPV4 4 +#define NR_IPV6 6 + UCHAR protocol; /* IPPROTO_TCP, IPPROTO_UDP */ + union { + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + } u; + char ifname[MAXIFNAME]; + /* A string version. + 56 = 5 ("IP6:[") + 39 (ipv6 address) + 2 ("]:") + 5 (port) + 4 (/UDP) + 1 (null) */ + char as_string[56]; + char fqdn[256]; + bool is_proxied; + bool tls; +} nr_transport_addr; + +typedef struct nr_transport_addr_mask_ { + UINT4 addr; + UINT4 mask; +} nr_transport_addr_mask; + +int nr_sockaddr_to_transport_addr(struct sockaddr *saddr, int protocol, int keep, nr_transport_addr *addr); + +// addresses, ports in local byte order +int nr_ip4_port_to_transport_addr(UINT4 ip4, UINT2 port, int protocol, nr_transport_addr *addr); +int nr_str_port_to_transport_addr(const char *str, UINT2 port, int protocol, nr_transport_addr *addr); +int nr_ip6_port_to_transport_addr(struct in6_addr* addr6, UINT2 port, int protocol, nr_transport_addr *addr); + +int nr_transport_addr_get_addrstring(const nr_transport_addr *addr, char *str, int maxlen); +int nr_transport_addr_get_port(const nr_transport_addr *addr, int *port); +int nr_transport_addr_cmp(const nr_transport_addr *addr1,const nr_transport_addr *addr2,int mode); +#define NR_TRANSPORT_ADDR_CMP_MODE_VERSION 1 +#define NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL 2 +#define NR_TRANSPORT_ADDR_CMP_MODE_ADDR 3 +#define NR_TRANSPORT_ADDR_CMP_MODE_ALL 4 + +int nr_transport_addr_is_wildcard(const nr_transport_addr *addr); +int nr_transport_addr_is_loopback(const nr_transport_addr *addr); +int nr_transport_addr_get_private_addr_range(const nr_transport_addr *addr); +int nr_transport_addr_is_link_local(const nr_transport_addr *addr); +int nr_transport_addr_is_mac_based(const nr_transport_addr *addr); +int nr_transport_addr_is_teredo(const nr_transport_addr *addr); +int nr_transport_addr_check_compatibility(const nr_transport_addr *addr1, const nr_transport_addr *addr2); +int nr_transport_addr_copy(nr_transport_addr *to, const nr_transport_addr *from); +int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, const nr_transport_addr *from); +/* Copies _just_ the address and port (also handles IP version) */ +int nr_transport_addr_copy_addrport(nr_transport_addr *to, const nr_transport_addr *from); +int nr_transport_addr_fmt_addr_string(nr_transport_addr *addr); +int nr_transport_addr_fmt_ifname_addr_string(const nr_transport_addr *addr, char *buf, int len); +int nr_transport_addr_set_port(nr_transport_addr *addr, int port); +int nr_transport_addr_is_reliable_transport(const nr_transport_addr *addr); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.c new file mode 100644 index 0000000000..10f93f1947 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.c @@ -0,0 +1,230 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <csi_platform.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <strings.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include <assert.h> +#include "nr_api.h" +#include "util.h" +#include "transport_addr.h" +#include "transport_addr_reg.h" + +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 /* Value used by linux/BSD */ +#endif + +int +nr_reg_get_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr) +{ + int r,_status; + unsigned int count; + char *address = 0; + UINT2 port = 0; + char *ifname = 0; + char *protocol = 0; + int p; + + if ((r=NR_reg_get_child_count(prefix, &count))) + ABORT(r); + + if (count == 0) + ABORT(R_NOT_FOUND); + + if ((r=NR_reg_alloc2_string(prefix, "address", &address))) { + if (r != R_NOT_FOUND) + ABORT(r); + address = 0; + } + + if ((r=NR_reg_alloc2_string(prefix, "ifname", &ifname))) { + if (r != R_NOT_FOUND) + ABORT(r); + ifname = 0; + } + + if ((r=NR_reg_get2_uint2(prefix, "port", &port))) { + if (r != R_NOT_FOUND) + ABORT(r); + port = 0; + } + + if ((r=NR_reg_alloc2_string(prefix, "protocol", &protocol))) { + if (r != R_NOT_FOUND) + ABORT(r); + p = IPPROTO_UDP; + + protocol = 0; + } + else { + if (!strcasecmp("tcp", protocol)) + p = IPPROTO_TCP; + else if (!strcasecmp("udp", protocol)) + p = IPPROTO_UDP; + else + ABORT(R_BAD_DATA); + } + + if (!keep) memset(addr, 0, sizeof(*addr)); + + if ((r=nr_str_port_to_transport_addr(address?address:"0.0.0.0", port, p, addr))) + ABORT(r); + + if (ifname) { + (void)strlcpy(addr->ifname, ifname, sizeof(addr->ifname)); + } + + _status=0; + abort: + RFREE(protocol); + RFREE(ifname); + RFREE(address); + return(_status); +} + +int +nr_reg_set_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr) +{ + int r,_status; + + if (! keep) { + if ((r=NR_reg_del(prefix))) + ABORT(r); + } + + switch (addr->ip_version) { + case NR_IPV4: + if (!nr_transport_addr_is_wildcard(addr)) { + if ((r=NR_reg_set2_string(prefix, "address", inet_ntoa(addr->u.addr4.sin_addr)))) + ABORT(r); + } + + if (addr->u.addr4.sin_port != 0) { + if ((r=NR_reg_set2_uint2(prefix, "port", ntohs(addr->u.addr4.sin_port)))) + ABORT(r); + } + break; + + case NR_IPV6: + if (!nr_transport_addr_is_wildcard(addr)) { + char address[INET6_ADDRSTRLEN]; + if(!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,address,sizeof(address))) { + ABORT(R_BAD_DATA); + } + + if ((r=NR_reg_set2_string(prefix, "address", address))) { + ABORT(r); + } + } + + if (addr->u.addr6.sin6_port != 0) { + if ((r=NR_reg_set2_uint2(prefix, "port", ntohs(addr->u.addr6.sin6_port)))) + ABORT(r); + } + break; + default: + ABORT(R_INTERNAL); + break; + } + + /* We abort if neither NR_IPV4 or NR_IPV6 above */ + switch (addr->protocol) { + case IPPROTO_TCP: + if ((r=NR_reg_set2_string(prefix, "protocol", "tcp"))) + ABORT(r); + break; + case IPPROTO_UDP: + if ((r=NR_reg_set2_string(prefix, "protocol", "udp"))) + ABORT(r); + break; + default: + NR_UNIMPLEMENTED; + break; + } + + if (strlen(addr->ifname) > 0) { + if ((r=NR_reg_set2_string(prefix, "ifname", addr->ifname))) + ABORT(r); + } + + _status=0; + abort: + if (_status) + NR_reg_del(prefix); + return _status; +} + +int +nr_reg_get_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr) +{ + int r, _status; + NR_registry registry; + + if ((r=NR_reg_make_registry(prefix, name, registry))) + ABORT(r); + + if ((r=nr_reg_get_transport_addr(registry, keep, addr))) + ABORT(r); + + _status = 0; +abort: + return _status; +} + +int +nr_reg_set_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr) +{ + int r, _status; + NR_registry registry; + + if ((r=NR_reg_make_registry(prefix, name, registry))) + ABORT(r); + + if ((r=nr_reg_set_transport_addr(registry, keep, addr))) + ABORT(r); + + _status = 0; +abort: + return _status; +} + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.h new file mode 100644 index 0000000000..761953a9ce --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _transport_addr_reg_h +#define _transport_addr_reg_h + +#include "registry.h" + +int nr_reg_get_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr); +int nr_reg_set_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr); +int nr_reg_get_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr); +int nr_reg_set_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c new file mode 100644 index 0000000000..ec2d084445 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c @@ -0,0 +1,110 @@ +/* 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 defined(BSD) || defined(DARWIN) +#include "addrs-bsd.h" +#include <csi_platform.h> +#include <assert.h> +#include <string.h> +#include "util.h" +#include "stun_util.h" +#include "util.h" +#include <r_macros.h> + +#include <sys/types.h> /* getifaddrs */ +#include <ifaddrs.h> /* getifaddrs */ +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <netinet/in.h> +#include <netinet6/in6_var.h> + +static int +stun_ifaddr_get_v6_flags(struct ifaddrs *ifaddr) +{ + if (ifaddr->ifa_addr->sa_family != AF_INET6) { + return 0; + } + + int flags = 0; + int s = socket(AF_INET6, SOCK_DGRAM, 0); + if (!s) { + r_log(NR_LOG_STUN, LOG_ERR, "socket(AF_INET6, SOCK_DGRAM, 0) failed, errno=%d", errno); + assert(0); + return 0; + } + struct in6_ifreq ifr6; + memset(&ifr6, 0, sizeof(ifr6)); + strncpy(ifr6.ifr_name, ifaddr->ifa_name, sizeof(ifr6.ifr_name)); + /* ifr_addr is a sockaddr_in6, ifa_addr is a sockaddr* */ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ifaddr->ifa_addr; + ifr6.ifr_addr = *sin6; + if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) != -1) { + flags = ifr6.ifr_ifru.ifru_flags6; + } else { + r_log(NR_LOG_STUN, LOG_ERR, "ioctl(SIOCGIFAFLAG_IN6) failed, errno=%d", errno); + assert(0); + } + close(s); + return flags; +} + +static int +stun_ifaddr_is_disallowed_v6(int flags) { + return flags & (IN6_IFF_ANYCAST | IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED); +} + +int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int r,_status,flags; + struct ifaddrs* if_addrs_head=NULL; + struct ifaddrs* if_addr; + + *count = 0; + + if (maxaddrs <= 0) + ABORT(R_BAD_ARGS); + + if (getifaddrs(&if_addrs_head) == -1) { + r_log(NR_LOG_STUN, LOG_ERR, "getifaddrs error e = %d", errno); + ABORT(R_INTERNAL); + } + + if_addr = if_addrs_head; + + while (if_addr && *count < maxaddrs) { + /* This can be null */ + if (if_addr->ifa_addr) { + switch (if_addr->ifa_addr->sa_family) { + case AF_INET: + case AF_INET6: + flags = stun_ifaddr_get_v6_flags(if_addr); + if (!stun_ifaddr_is_disallowed_v6(flags)) { + if (r=nr_sockaddr_to_transport_addr(if_addr->ifa_addr, IPPROTO_UDP, 0, &(addrs[*count].addr))) { + r_log(NR_LOG_STUN, LOG_ERR, "nr_sockaddr_to_transport_addr error r = %d", r); + } else { + if (flags & IN6_IFF_TEMPORARY) { + addrs[*count].flags |= NR_ADDR_FLAG_TEMPORARY; + } + (void)strlcpy(addrs[*count].addr.ifname, if_addr->ifa_name, sizeof(addrs[*count].addr.ifname)); + ++(*count); + } + } + break; + default: + ; + } + } + + if_addr = if_addr->ifa_next; + } + + _status=0; +abort: + if (if_addrs_head) { + freeifaddrs(if_addrs_head); + } + return(_status); +} +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h new file mode 100644 index 0000000000..b575586f48 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h @@ -0,0 +1,13 @@ +/* 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/. */ + +#ifndef STUN_IFADDRS_BSD_H_ +#define STUN_IFADDRS_BSD_H_ + +#include "local_addr.h" + +int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count); + +#endif /* STUN_IFADDRS_BSD_H_ */ + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c new file mode 100644 index 0000000000..73e85c6ccc --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c @@ -0,0 +1,285 @@ +/* +Copyright (c) 2011, The WebRTC project authors. 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 Google 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 +HOLDER 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. +*/ + +#if defined(LINUX) +#include "addrs-netlink.h" +#include <csi_platform.h> +#include <assert.h> +#include <string.h> +#include "util.h" +#include "stun_util.h" +#include "util.h" +#include <r_macros.h> + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/utsname.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <net/if.h> +#include <unistd.h> +#include <errno.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#ifdef ANDROID +/* Work around an Android NDK < r8c bug */ +#undef __unused +#else +#include <linux/if.h> /* struct ifreq, IFF_POINTTOPOINT */ +#include <linux/wireless.h> /* struct iwreq */ +#include <linux/ethtool.h> /* struct ethtool_cmd */ +#include <linux/sockios.h> /* SIOCETHTOOL */ +#endif /* ANDROID */ + + +struct netlinkrequest { + struct nlmsghdr header; + struct ifaddrmsg msg; +}; + +static const int kMaxReadSize = 4096; + +static void set_ifname(nr_local_addr *addr, struct ifaddrmsg* msg) { + assert(sizeof(addr->addr.ifname) > IF_NAMESIZE); + if_indextoname(msg->ifa_index, addr->addr.ifname); +} + +static int get_siocgifflags(nr_local_addr *addr) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + assert(0); + return 0; + } + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, addr->addr.ifname, IFNAMSIZ - 1); + int rc = ioctl(fd, SIOCGIFFLAGS, &ifr); + close(fd); + if (rc == -1) { + assert(0); + return 0; + } + return ifr.ifr_flags; +} + +static int set_sockaddr(nr_local_addr *addr, struct ifaddrmsg* msg, struct rtattr* rta) { + assert(rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL); + void *data = RTA_DATA(rta); + size_t len = RTA_PAYLOAD(rta); + if (msg->ifa_family == AF_INET) { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(struct sockaddr_in)); + sa.sin_family = AF_INET; + memcpy(&sa.sin_addr, data, len); + return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr)); + } else if (msg->ifa_family == AF_INET6) { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(struct sockaddr_in6)); + sa.sin6_family = AF_INET6; + /* We do not set sin6_scope_id to ifa_index, because that is only valid for + * link local addresses, and we don't use those anyway */ + memcpy(&sa.sin6_addr, data, len); + return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr)); + } + + return R_BAD_ARGS; +} + +static int +stun_ifaddr_is_disallowed_v6(int flags) { + return flags & (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | IFA_F_DEPRECATED); +} + +static int +stun_convert_netlink(nr_local_addr *addr, struct ifaddrmsg *address_msg, struct rtattr* rta) +{ + int r = set_sockaddr(addr, address_msg, rta); + if (r) { + r_log(NR_LOG_STUN, LOG_ERR, "set_sockaddr error r = %d", r); + return r; + } + + set_ifname(addr, address_msg); + + if (address_msg->ifa_flags & IFA_F_TEMPORARY) { + addr->flags |= NR_ADDR_FLAG_TEMPORARY; + } + + int flags = get_siocgifflags(addr); + if (flags & IFF_POINTOPOINT) + { + addr->interface.type = NR_INTERFACE_TYPE_UNKNOWN | NR_INTERFACE_TYPE_VPN; + /* TODO (Bug 896913): find backend network type of this VPN */ + } + +#if defined(LINUX) && !defined(ANDROID) + struct ethtool_cmd ecmd; + struct ifreq ifr; + struct iwreq wrq; + int e; + int s = socket(AF_INET, SOCK_DGRAM, 0); + + strncpy(ifr.ifr_name, addr->addr.ifname, sizeof(ifr.ifr_name)); + /* TODO (Bug 896851): interface property for Android */ + /* Getting ethtool for ethernet information. */ + ecmd.cmd = ETHTOOL_GSET; + /* In/out param */ + ifr.ifr_data = (void*)&ecmd; + + e = ioctl(s, SIOCETHTOOL, &ifr); + if (e == 0) + { + /* For wireless network, we won't get ethtool, it's a wired + * connection */ + addr->interface.type = NR_INTERFACE_TYPE_WIRED; +#ifdef DONT_HAVE_ETHTOOL_SPEED_HI + addr->interface.estimated_speed = ecmd.speed; +#else + addr->interface.estimated_speed = ((ecmd.speed_hi << 16) | ecmd.speed) * 1000; +#endif + } + + strncpy(wrq.ifr_name, addr->addr.ifname, sizeof(wrq.ifr_name)); + e = ioctl(s, SIOCGIWRATE, &wrq); + if (e == 0) + { + addr->interface.type = NR_INTERFACE_TYPE_WIFI; + addr->interface.estimated_speed = wrq.u.bitrate.value / 1000; + } + + close(s); + +#else + addr->interface.type = NR_INTERFACE_TYPE_UNKNOWN; + addr->interface.estimated_speed = 0; +#endif + return 0; +} + +int +stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int _status; + int fd = 0; + + /* Scope everything else since we're using ABORT. */ + { + *count = 0; + + if (maxaddrs <= 0) + ABORT(R_BAD_ARGS); + + fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) { + ABORT(R_INTERNAL); + } + + struct netlinkrequest ifaddr_request; + memset(&ifaddr_request, 0, sizeof(ifaddr_request)); + ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST; + ifaddr_request.header.nlmsg_type = RTM_GETADDR; + ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + ssize_t bytes = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0); + if ((size_t)bytes != ifaddr_request.header.nlmsg_len) { + ABORT(R_INTERNAL); + } + + char buf[kMaxReadSize]; + ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0); + while ((amount_read > 0) && (*count != maxaddrs)) { + struct nlmsghdr* header = (struct nlmsghdr*)&buf[0]; + size_t header_size = (size_t)amount_read; + for ( ; NLMSG_OK(header, header_size) && (*count != maxaddrs); + header = NLMSG_NEXT(header, header_size)) { + switch (header->nlmsg_type) { + case NLMSG_DONE: + /* Success. Return. */ + close(fd); + return 0; + case NLMSG_ERROR: + ABORT(R_INTERNAL); + case RTM_NEWADDR: { + struct ifaddrmsg* address_msg = + (struct ifaddrmsg*)NLMSG_DATA(header); + struct rtattr* rta = IFA_RTA(address_msg); + ssize_t payload_len = IFA_PAYLOAD(header); + bool found = false; + while (RTA_OK(rta, payload_len)) { + // This is a bit convoluted. IFA_ADDRESS and IFA_LOCAL are the + // same thing except when using a POINTTOPOINT interface, in + // which case IFA_LOCAL is the local address, and IFA_ADDRESS is + // the remote address. In a reasonable world, that would mean we + // could just use IFA_LOCAL all the time. Sadly, IFA_LOCAL is not + // always set (IPv6 in particular). So, we have to be on the + // lookout for both, and prefer IFA_LOCAL when present. + if (rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL) { + int family = address_msg->ifa_family; + if ((family == AF_INET || family == AF_INET6) && + !stun_ifaddr_is_disallowed_v6(address_msg->ifa_flags) && + !stun_convert_netlink(&addrs[*count], address_msg, rta)) { + found = true; + if (rta->rta_type == IFA_LOCAL) { + // IFA_LOCAL is what we really want; if we find it we're + // done. If this is IFA_ADDRESS instead, we do not proceed + // yet, and allow a subsequent IFA_LOCAL to overwrite what + // we just put in |addrs|. + break; + } + } + } + /* TODO: Use IFA_LABEL instead of if_indextoname? We would need + * to remember how many nr_local_addr we've converted for this + * ifaddrmsg, and set the label on all of them. */ + rta = RTA_NEXT(rta, payload_len); + } + + if (found) { + ++(*count); + } + break; + } + } + } + amount_read = recv(fd, &buf, kMaxReadSize, 0); + } + } + + _status=0; +abort: + close(fd); + return(_status); +} + +#endif /* defined(LINUX) */ diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h new file mode 100644 index 0000000000..b2b07bddc9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h @@ -0,0 +1,45 @@ +/* +Copyright (c) 2011, The WebRTC project authors. 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 Google 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 +HOLDER 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. +*/ + +#ifndef WEBRTC_BASE_IFADDRS_NETLINK_H_ +#define WEBRTC_BASE_IFADDRS_NETLINK_H_ + +#include <stdio.h> +#include <sys/socket.h> +#include "local_addr.h" + +/* for platforms with netlink (android and linux) */ +/* Filters out things like deprecated addresses, and stuff that doesn't pass + * IPv6 duplicate address detection */ +int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count); +#endif /* WEBRTC_BASE_IFADDRS_NETLINK_H_ */ + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c new file mode 100644 index 0000000000..4fdf5e5d44 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c @@ -0,0 +1,210 @@ +/* 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/. */ + +#ifdef WIN32 + +#include "addrs-win32.h" +#include <csi_platform.h> +#include <assert.h> +#include <string.h> +#include "util.h" +#include "stun_util.h" +#include "util.h" +#include <r_macros.h> +#include "nr_crypto.h" + +#include <winsock2.h> +#include <iphlpapi.h> +#include <tchar.h> + +#define WIN32_MAX_NUM_INTERFACES 20 + +#define NR_MD5_HASH_LENGTH 16 + +#define _NR_MAX_KEY_LENGTH 256 +#define _NR_MAX_NAME_LENGTH 512 + +#define _ADAPTERS_BASE_REG "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +static int nr_win32_get_adapter_friendly_name(char *adapter_GUID, char **friendly_name) +{ + int r,_status; + HKEY adapter_reg; + TCHAR adapter_key[_NR_MAX_KEY_LENGTH]; + TCHAR keyval_buf[_NR_MAX_KEY_LENGTH]; + TCHAR adapter_GUID_tchar[_NR_MAX_NAME_LENGTH]; + DWORD keyval_len, key_type; + size_t converted_chars, newlen; + char *my_fn = 0; + +#ifdef _UNICODE + mbstowcs_s(&converted_chars, adapter_GUID_tchar, strlen(adapter_GUID)+1, + adapter_GUID, _TRUNCATE); +#else + strlcpy(adapter_GUID_tchar, adapter_GUID, _NR_MAX_NAME_LENGTH); +#endif + + _tcscpy_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT(_ADAPTERS_BASE_REG)); + _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\")); + _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, adapter_GUID_tchar); + _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\Connection")); + + r = RegOpenKeyEx(HKEY_LOCAL_MACHINE, adapter_key, 0, KEY_READ, &adapter_reg); + + if (r != ERROR_SUCCESS) { + r_log(NR_LOG_STUN, LOG_ERR, "Got error %d opening adapter reg key\n", r); + ABORT(R_INTERNAL); + } + + keyval_len = sizeof(keyval_buf); + r = RegQueryValueEx(adapter_reg, TEXT("Name"), NULL, &key_type, + (BYTE *)keyval_buf, &keyval_len); + + RegCloseKey(adapter_reg); + +#ifdef UNICODE + newlen = wcslen(keyval_buf)+1; + my_fn = (char *) RCALLOC(newlen); + if (!my_fn) { + ABORT(R_NO_MEMORY); + } + wcstombs_s(&converted_chars, my_fn, newlen, keyval_buf, _TRUNCATE); +#else + my_fn = r_strdup(keyval_buf); +#endif + + *friendly_name = my_fn; + _status=0; + +abort: + if (_status) { + if (my_fn) free(my_fn); + } + return(_status); +} + +static int stun_win32_address_disallowed(IP_ADAPTER_UNICAST_ADDRESS *addr) +{ + return (addr->DadState != NldsPreferred) && + (addr->DadState != IpDadStatePreferred); +} + +static int stun_win32_address_temp_v6(IP_ADAPTER_UNICAST_ADDRESS *addr) +{ + return (addr->Address.lpSockaddr->sa_family == AF_INET6) && + (addr->SuffixOrigin == IpSuffixOriginRandom); +} + +int +stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int r, _status; + PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL, tmpAddress = NULL; + // recomended per https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx + static const ULONG initialBufLen = 15000; + ULONG buflen = initialBufLen; + char bin_hashed_ifname[NR_MD5_HASH_LENGTH]; + char hex_hashed_ifname[MAXIFNAME]; + int n = 0; + + *count = 0; + + if (maxaddrs <= 0) + ABORT(R_BAD_ARGS); + + /* According to MSDN (see above) we have try GetAdapterAddresses() multiple times */ + for (n = 0; n < 5; n++) { + AdapterAddresses = (PIP_ADAPTER_ADDRESSES) RMALLOC(buflen); + if (AdapterAddresses == NULL) { + r_log(NR_LOG_STUN, LOG_ERR, "Error allocating buf for GetAdaptersAddresses()"); + ABORT(R_NO_MEMORY); + } + + r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, AdapterAddresses, &buflen); + if (r == NO_ERROR) { + break; + } + r_log(NR_LOG_STUN, LOG_ERR, "GetAdaptersAddresses() returned error (%d)", r); + RFREE(AdapterAddresses); + AdapterAddresses = NULL; + } + + if (n >= 5) { + r_log(NR_LOG_STUN, LOG_ERR, "5 failures calling GetAdaptersAddresses()"); + ABORT(R_INTERNAL); + } + + n = 0; + + /* Loop through the adapters */ + + for (tmpAddress = AdapterAddresses; tmpAddress != NULL; tmpAddress = tmpAddress->Next) { + + if (tmpAddress->OperStatus != IfOperStatusUp) + continue; + + if ((tmpAddress->IfIndex != 0) || (tmpAddress->Ipv6IfIndex != 0)) { + IP_ADAPTER_UNICAST_ADDRESS *u = 0; + + if(r=nr_crypto_md5((UCHAR *)tmpAddress->FriendlyName, + wcslen(tmpAddress->FriendlyName) * sizeof(wchar_t), + bin_hashed_ifname)) + ABORT(r); + if(r=nr_bin2hex(bin_hashed_ifname, sizeof(bin_hashed_ifname), + hex_hashed_ifname)) + ABORT(r); + + for (u = tmpAddress->FirstUnicastAddress; u != 0; u = u->Next) { + SOCKET_ADDRESS *sa_addr = &u->Address; + + if ((sa_addr->lpSockaddr->sa_family != AF_INET) && + (sa_addr->lpSockaddr->sa_family != AF_INET6)) { + r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized sa_family for address on adapter %lu", tmpAddress->IfIndex); + continue; + } + + if (stun_win32_address_disallowed(u)) { + continue; + } + + if ((r=nr_sockaddr_to_transport_addr((struct sockaddr*)sa_addr->lpSockaddr, IPPROTO_UDP, 0, &(addrs[n].addr)))) { + ABORT(r); + } + + strlcpy(addrs[n].addr.ifname, hex_hashed_ifname, sizeof(addrs[n].addr.ifname)); + if (tmpAddress->IfType == IF_TYPE_ETHERNET_CSMACD) { + addrs[n].interface.type = NR_INTERFACE_TYPE_WIRED; + } else if (tmpAddress->IfType == IF_TYPE_IEEE80211) { + /* Note: this only works for >= Win Vista */ + addrs[n].interface.type = NR_INTERFACE_TYPE_WIFI; + } else { + addrs[n].interface.type = NR_INTERFACE_TYPE_UNKNOWN; + } +#if (_WIN32_WINNT >= 0x0600) + /* Note: only >= Vista provide link speed information */ + addrs[n].interface.estimated_speed = tmpAddress->TransmitLinkSpeed / 1000; +#else + addrs[n].interface.estimated_speed = 0; +#endif + if (stun_win32_address_temp_v6(u)) { + addrs[n].flags |= NR_ADDR_FLAG_TEMPORARY; + } + + if (++n >= maxaddrs) + goto done; + } + } + } + + done: + *count = n; + _status = 0; + + abort: + RFREE(AdapterAddresses); + return _status; +} + +#endif //WIN32 + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h new file mode 100644 index 0000000000..a00802192a --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h @@ -0,0 +1,13 @@ +/* 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/. */ + +#ifndef STUN_IFADDRS_WIN32_H_ +#define STUN_IFADDRS_WIN32_H_ + +#include "local_addr.h" + +int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count); + +#endif /* STUN_IFADDRS_WIN32_H_ */ + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c new file mode 100644 index 0000000000..362b7d828e --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c @@ -0,0 +1,176 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <csi_platform.h> +#include <assert.h> +#include <string.h> + +#ifdef WIN32 +#include "addrs-win32.h" +#elif defined(BSD) || defined(DARWIN) +#include "addrs-bsd.h" +#else +#include "addrs-netlink.h" +#endif + +#include "stun.h" +#include "addrs.h" +#include "util.h" + +static int +nr_stun_is_duplicate_addr(nr_local_addr addrs[], int count, nr_local_addr *addr) +{ + int i; + int different; + + for (i = 0; i < count; ++i) { + different = nr_transport_addr_cmp(&addrs[i].addr, &(addr->addr), + NR_TRANSPORT_ADDR_CMP_MODE_ALL); + if (!different) + return 1; /* duplicate */ + } + + return 0; +} + +int +nr_stun_filter_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count) +{ + int r, _status; + nr_local_addr *tmp = 0; + int i; + int n; + /* We prefer temp ipv6 for their privacy properties. If we cannot get + * that, we prefer ipv6 that are not based on mac address. */ + int filter_mac_ipv6 = 0; + int filter_teredo_ipv6 = 0; + int filter_non_temp_ipv6 = 0; + + tmp = RMALLOC(*count * sizeof(*tmp)); + if (!tmp) + ABORT(R_NO_MEMORY); + + for (i = 0; i < *count; ++i) { + if (addrs[i].addr.ip_version == NR_IPV6) { + if (nr_transport_addr_is_teredo(&addrs[i].addr)) { + addrs[i].interface.type |= NR_INTERFACE_TYPE_TEREDO; + /* Prefer teredo over mac-based address. Probably will never see + * both. */ + filter_mac_ipv6 = 1; + } else { + filter_teredo_ipv6 = 1; + } + + if (!nr_transport_addr_is_mac_based(&addrs[i].addr)) { + filter_mac_ipv6 = 1; + } + + if (addrs[i].flags & NR_ADDR_FLAG_TEMPORARY) { + filter_non_temp_ipv6 = 1; + } + } + } + + n = 0; + for (i = 0; i < *count; ++i) { + if (nr_stun_is_duplicate_addr(tmp, n, &addrs[i])) { + /* skip addrs[i], it's a duplicate */ + } + else if (remove_loopback && nr_transport_addr_is_loopback(&addrs[i].addr)) { + /* skip addrs[i], it's a loopback */ + } + else if (remove_link_local && + nr_transport_addr_is_link_local(&addrs[i].addr)) { + /* skip addrs[i], it's a link-local address */ + } + else if (filter_mac_ipv6 && + nr_transport_addr_is_mac_based(&addrs[i].addr)) { + /* skip addrs[i], it's MAC based */ + } + else if (filter_teredo_ipv6 && + nr_transport_addr_is_teredo(&addrs[i].addr)) { + /* skip addrs[i], it's a Teredo address */ + } + else if (filter_non_temp_ipv6 && + (addrs[i].addr.ip_version == NR_IPV6) && + !(addrs[i].flags & NR_ADDR_FLAG_TEMPORARY)) { + /* skip addrs[i], it's a non-temporary ipv6, and we have a temporary */ + } + else { + /* otherwise, copy it to the temporary array */ + if ((r=nr_local_addr_copy(&tmp[n], &addrs[i]))) + ABORT(r); + ++n; + } + } + + *count = n; + + memset(addrs, 0, *count * sizeof(*addrs)); + /* copy temporary array into passed in/out array */ + for (i = 0; i < *count; ++i) { + if ((r=nr_local_addr_copy(&addrs[i], &tmp[i]))) + ABORT(r); + } + + _status = 0; + abort: + RFREE(tmp); + return _status; +} + +#ifndef USE_PLATFORM_NR_STUN_GET_ADDRS + +int +nr_stun_get_addrs(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int _status=0; + int i; + char typestr[100]; + + // Ensure output records are always fully defined. See bug 1589990. + if (maxaddrs > 0) { + memset(addrs, 0, maxaddrs * sizeof(nr_local_addr)); + } + + _status = stun_getaddrs_filtered(addrs, maxaddrs, count); + + for (i = 0; i < *count; ++i) { + nr_local_addr_fmt_info_string(addrs+i,typestr,sizeof(typestr)); + r_log(NR_LOG_STUN, LOG_DEBUG, "Address %d: %s on %s, type: %s\n", + i,addrs[i].addr.as_string,addrs[i].addr.ifname,typestr); + } + + return _status; +} + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h new file mode 100644 index 0000000000..522bd92fd5 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h @@ -0,0 +1,43 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + +#ifndef _addrs_h_ +#define _addrs_h_ + +#include "transport_addr.h" +#include "local_addr.h" + +int nr_stun_get_addrs(nr_local_addr addrs[], int maxaddrs, int *count); +int nr_stun_filter_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c new file mode 100644 index 0000000000..4b5b92fb75 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c @@ -0,0 +1,656 @@ +/* +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 <inttypes.h> + +#include "p_buf.h" +#include "nr_socket.h" +#include "stun.h" +#include "nr_socket_buffered_stun.h" + +#define NR_MAX_FRAME_SIZE 0xFFFF + +typedef struct nr_frame_header_ { + UINT2 frame_length; + char data[0]; +} nr_frame_header; + +typedef struct nr_socket_buffered_stun_ { + nr_socket *inner; + nr_transport_addr remote_addr; + int connected; + + /* 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; + NR_async_cb readable_cb; + void *readable_cb_arg; + + /* Write state */ + nr_p_buf_ctx *p_bufs; + nr_p_buf_head pending_writes; + size_t pending; + size_t max_pending; + nr_framing_type framing_type; +} 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, const 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, const 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 int nr_socket_buffered_stun_listen(void *obj, int backlog); +static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp); + +static nr_socket_vtbl nr_socket_buffered_stun_vtbl={ + 2, + 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, + nr_socket_buffered_stun_listen, + nr_socket_buffered_stun_accept +}; + +void nr_socket_buffered_stun_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb, void *readable_cb_arg) +{ + nr_socket_buffered_stun *buf_sock = (nr_socket_buffered_stun *)sock->obj; + + buf_sock->readable_cb = readable_cb; + buf_sock->readable_cb_arg = readable_cb_arg; +} + +int nr_socket_buffered_set_connected_to(nr_socket *sock, nr_transport_addr *remote_addr) +{ + nr_socket_buffered_stun *buf_sock = (nr_socket_buffered_stun *)sock->obj; + int r, _status; + + if ((r=nr_transport_addr_copy(&buf_sock->remote_addr, remote_addr))) + ABORT(r); + + buf_sock->connected = 1; + + _status=0; +abort: + return(_status); +} + +int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending, + nr_framing_type framing_type, nr_socket **sockp) +{ + int r, _status; + nr_socket_buffered_stun *sock = 0; + size_t frame_size; + + if (!(sock = RCALLOC(sizeof(nr_socket_buffered_stun)))) + ABORT(R_NO_MEMORY); + + sock->inner = inner; + sock->framing_type = framing_type; + + if ((r=nr_ip4_port_to_transport_addr(INADDR_ANY, 0, IPPROTO_TCP, &sock->remote_addr))) + ABORT(r); + + switch (framing_type) { + case ICE_TCP_FRAMING: + frame_size = sizeof(nr_frame_header); + sock->buffer_size = sizeof(nr_frame_header) + NR_MAX_FRAME_SIZE; + sock->bytes_needed = sizeof(nr_frame_header); + break; + case TURN_TCP_FRAMING: + frame_size = 0; + sock->buffer_size = NR_STUN_MAX_MESSAGE_SIZE; + sock->bytes_needed = sizeof(nr_stun_message_header); + break; + default: + assert(0); + ABORT(R_BAD_ARGS); + } + + /* TODO(ekr@rtfm.com): Check this */ + if (!(sock->buffer = RMALLOC(sock->buffer_size))) + ABORT(R_NO_MEMORY); + + sock->read_state = NR_ICE_SOCKET_READ_NONE; + sock->connected = 0; + + STAILQ_INIT(&sock->pending_writes); + if ((r=nr_p_buf_ctx_create(sock->buffer_size, &sock->p_bufs))) + ABORT(r); + sock->max_pending = max_pending + frame_size; + + if ((r=nr_socket_create_int(sock, &nr_socket_buffered_stun_vtbl, sockp))) + ABORT(r); + + _status=0; +abort: + if (_status && sock) { + 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 (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + + 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, const nr_transport_addr *to) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + int r, _status; + size_t written; + nr_frame_header *frame = NULL; + + /* 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 (sock->framing_type == ICE_TCP_FRAMING) { + + assert(len <= NR_MAX_FRAME_SIZE); + if (len > NR_MAX_FRAME_SIZE) + ABORT(R_FAILED); + + if (!(frame = RMALLOC(len + sizeof(nr_frame_header)))) + ABORT(R_NO_MEMORY); + + frame->frame_length = htons(len); + memcpy(frame->data, msg, len); + len += sizeof(nr_frame_header); + msg = frame; + } + + if ((r=nr_socket_buffered_stun_write(obj, msg, len, &written))) + ABORT(r); + + if (len != written) + ABORT(R_IO_ERROR); + + _status=0; +abort: + RFREE(frame); + return _status; +} + +static void nr_socket_buffered_stun_failed(nr_socket_buffered_stun *sock) + { + NR_SOCKET fd; + + sock->read_state = NR_ICE_SOCKET_READ_FAILED; + + /* Cancel waiting on the socket */ + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + } + +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; + nr_frame_header *frame = (nr_frame_header *)sock->buffer; + size_t skip_hdr_size = (sock->framing_type == ICE_TCP_FRAMING) ? sizeof(nr_frame_header) : 0; + + if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) { + ABORT(R_FAILED); + } + + while (sock->bytes_needed) { + /* 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 expected */ + if (sock->read_state == NR_ICE_SOCKET_READ_NONE) { + size_t remaining_length; + if (sock->framing_type == ICE_TCP_FRAMING) { + if (sock->bytes_read < sizeof(nr_frame_header)) + ABORT(R_BAD_DATA); + remaining_length = ntohs(frame->frame_length); + } else { + int tmp_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); + + sock->read_state = NR_ICE_SOCKET_READ_HDR; + /* Set ourselves up to read the rest of the data */ + sock->bytes_needed = remaining_length; + } + } + + assert(skip_hdr_size <= sock->bytes_read); + if (skip_hdr_size > sock->bytes_read) + ABORT(R_BAD_DATA); + sock->bytes_read -= skip_hdr_size; + + if (maxlen < sock->bytes_read) + ABORT(R_BAD_ARGS); + + *len = sock->bytes_read; + memcpy(buf, sock->buffer + skip_hdr_size, sock->bytes_read); + + sock->bytes_read = 0; + sock->read_state = NR_ICE_SOCKET_READ_NONE; + sock->bytes_needed = (sock->framing_type == ICE_TCP_FRAMING) ? sizeof(nr_frame_header) : sizeof(nr_stun_message_header); + + 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; + NR_SOCKET fd; + + /* Cancel waiting on the socket */ + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + } + + return nr_socket_close(sock->inner); +} + +static int nr_socket_buffered_stun_listen(void *obj, int backlog) +{ + int r, _status; + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + + if (!sock->inner) + ABORT(R_FAILED); + + if ((r=nr_socket_listen(sock->inner, backlog))) + ABORT(r); + + _status=0; +abort: + return(_status); +} + + +static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp) +{ + nr_socket_buffered_stun *bsock = (nr_socket_buffered_stun *)obj; + + return nr_socket_accept(bsock->inner, addrp, sockp); +} + +static void nr_socket_buffered_stun_connected_cb(NR_SOCKET s, int how, void *arg) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg; + int r, _status; + NR_SOCKET fd; + + assert(!sock->connected); + + sock->connected = 1; + + if ((r=nr_socket_getfd(sock->inner, &fd))) + ABORT(r); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + + // once connected arm for read + if (sock->readable_cb) { + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, sock->readable_cb, sock->readable_cb_arg); + } + + if (sock->pending) { + r_log(LOG_GENERIC, LOG_INFO, "Invoking writable_cb on connected (%u)", (uint32_t) sock->pending); + nr_socket_buffered_stun_writable_cb(s, how, arg); + } + + _status=0; +abort: + if (_status) { + r_log(LOG_GENERIC, LOG_ERR, "Failure in nr_socket_buffered_stun_connected_cb: %d", _status); + + } +} + +static int nr_socket_buffered_stun_connect(void *obj, const 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))) { + if (r == R_WOULDBLOCK) { + 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_connected_cb, sock); + ABORT(R_WOULDBLOCK); + } + ABORT(r); + } else { + r_log(LOG_GENERIC, LOG_INFO, "Connected without blocking"); + sock->connected = 1; + } + + _status=0; +abort: + return(_status); +} + +static int nr_socket_buffered_stun_arm_writable_cb(nr_socket_buffered_stun *sock) +{ + int r, _status; + 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); + + _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, "Write buffer for %s full (%u + %u > %u) - re-arming @%p", + sock->remote_addr.as_string, (uint32_t)sock->pending, (uint32_t)len, (uint32_t)sock->max_pending, + &(sock->pending)); + ABORT(R_WOULDBLOCK); + } + + + if (sock->connected && !sock->pending) { + r = nr_socket_write(sock->inner, msg, len, &written2, 0); + if (r) { + if (r != R_WOULDBLOCK) { + r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d", + sock->remote_addr.as_string, r); + ABORT(r); + } + r_log(LOG_GENERIC, LOG_INFO, "Write of %" PRIu64 " blocked for %s", + (uint64_t) len, sock->remote_addr.as_string); + + 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))) { + r_log(LOG_GENERIC, LOG_ERR, "Write_to_chain error for %s - %d", + sock->remote_addr.as_string, r); + + ABORT(r); + } + + sock->pending += len; + } + + if (sock->pending) { + if (!already_armed) { + if ((r=nr_socket_buffered_stun_arm_writable_cb(sock))) + ABORT(r); + } + r_log(LOG_GENERIC, LOG_INFO, "Write buffer not empty for %s %u - %s armed (@%p),%s connected", + sock->remote_addr.as_string, (uint32_t)sock->pending, + already_armed ? "already" : "", &sock->pending, + sock->connected ? "" : " not"); + } + + *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; + + if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) { + ABORT(R_FAILED); + } + + /* 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))) { + + r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d", + sock->remote_addr.as_string, r); + ABORT(r); + } + + n1->r_offset += written; + assert(sock->pending >= written); + sock->pending -= written; + + if (n1->r_offset < n1->length) { + /* We wrote something, but not everything */ + r_log(LOG_GENERIC, LOG_INFO, "Write in callback didn't write all (remaining %u of %u) for %s", + n1->length - n1->r_offset, n1->length, + sock->remote_addr.as_string); + 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); + } + + assert(!sock->pending); + _status=0; +abort: + r_log(LOG_GENERIC, LOG_INFO, "Writable_cb %s (%u (%p) pending)", + sock->remote_addr.as_string, (uint32_t)sock->pending, &(sock->pending)); + if (_status && _status != R_WOULDBLOCK) { + r_log(LOG_GENERIC, LOG_ERR, "Failure in writable_cb: %d", _status); + nr_socket_buffered_stun_failed(sock); + /* Report this failure up; the only way to do this is a readable callback. + * Once the user tries to read (using nr_socket_buffered_stun_recvfrom), it + * will notice that there has been a failure. */ + if (sock->readable_cb) { + sock->readable_cb(s, NR_ASYNC_WAIT_READ, sock->readable_cb_arg); + } + } else if (sock->pending) { + nr_socket_buffered_stun_arm_writable_cb(sock); + } +} + +int nr_socket_buffered_stun_reset(nr_socket* sock_arg, nr_socket* new_inner) { + int r, _status; + NR_SOCKET fd; + + nr_socket_buffered_stun* sock = (nr_socket_buffered_stun*)sock_arg->obj; + + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + r_log(LOG_GENERIC, LOG_DEBUG, "In %s, canceling wait on old socket", __FUNCTION__); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + + nr_socket_destroy(&sock->inner); + sock->inner = new_inner; + + sock->read_state = NR_ICE_SOCKET_READ_NONE; + sock->connected = 0; + + sock->bytes_read = 0; + sock->bytes_needed = (sock->framing_type == ICE_TCP_FRAMING) + ? sizeof(nr_frame_header) + : sizeof(nr_stun_message_header); + sock->pending = 0; + + nr_p_buf_free_chain(sock->p_bufs, &sock->pending_writes); + nr_p_buf_ctx_destroy(&sock->p_bufs); + + STAILQ_INIT(&sock->pending_writes); + + if ((r = nr_p_buf_ctx_create(sock->buffer_size, &sock->p_bufs))) { + ABORT(r); + } + + if ((r = nr_ip4_port_to_transport_addr(INADDR_ANY, 0, IPPROTO_TCP, + &sock->remote_addr))) { + ABORT(r); + } + + _status = 0; +abort: + return (_status); +} diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h new file mode 100644 index 0000000000..c635ee393d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h @@ -0,0 +1,66 @@ +/* +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. +*/ + + + +#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 + + 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|. + */ + +typedef enum { + TURN_TCP_FRAMING=0, + ICE_TCP_FRAMING +} nr_framing_type; + +void nr_socket_buffered_stun_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb, void *readable_cb_arg); + +int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending, + nr_framing_type framing_type, nr_socket **sockp); + +int nr_socket_buffered_set_connected_to(nr_socket *sock, + nr_transport_addr *remote_addr); + +int nr_socket_buffered_stun_reset(nr_socket *sock, nr_socket *new_inner); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c new file mode 100644 index 0000000000..1a0162b13e --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c @@ -0,0 +1,195 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + +#ifdef USE_TURN + +#include <csi_platform.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <assert.h> + +#include "stun.h" +#include "turn_client_ctx.h" +#include "nr_socket_turn.h" + + +static char *nr_socket_turn_magic_cookie = "nr_socket_turn"; + +typedef struct nr_socket_turn_ { + char *magic_cookie; + nr_turn_client_ctx *turn; +} nr_socket_turn; + + +static int nr_socket_turn_destroy(void **objp); +static int nr_socket_turn_sendto(void *obj,const void *msg, size_t len, + int flags, const 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={ + 2, + 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, + 0, + 0 +}; + +int nr_socket_turn_create(nr_socket **sockp) + { + int r,_status; + nr_socket_turn *sturn=0; + + if(!(sturn=RCALLOC(sizeof(nr_socket_turn)))) + ABORT(R_NO_MEMORY); + + sturn->magic_cookie = nr_socket_turn_magic_cookie; + + if(r=nr_socket_create_int(sturn, &nr_socket_turn_vtbl, sockp)) + ABORT(r); + + _status=0; + abort: + if(_status){ + nr_socket_turn_destroy((void **)&sturn); + } + return(_status); + } + +static int nr_socket_turn_destroy(void **objp) + { + int _status; + nr_socket_turn *sturn; + + if(!objp || !*objp) + return(0); + + sturn=*objp; + *objp=0; + + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + + /* we don't own the socket, so don't destroy it */ + + RFREE(sturn); + + _status=0; + return(_status); + } + +static int nr_socket_turn_sendto(void *obj,const void *msg, size_t len, + int flags, const nr_transport_addr *addr) + { + int r,_status; + nr_socket_turn *sturn=obj; + + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + assert(sturn->turn); + + if ((r = nr_turn_client_send_indication(sturn->turn, msg, len, flags, + addr))) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +static int nr_socket_turn_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *addr) + { + /* Reading from TURN sockets is done by the indication + processing code in turn_client_ctx. */ + assert(0); + + return(R_INTERNAL); + } + +static int nr_socket_turn_getfd(void *obj, NR_SOCKET *fd) + { + /* You should never directly be touching this fd. */ + assert(0); + + return(R_INTERNAL); + } + +static int nr_socket_turn_getaddr(void *obj, nr_transport_addr *addrp) + { + nr_socket_turn *sturn=obj; + int r, _status; + + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + assert(sturn->turn); + + /* This returns the relayed address */ + if ((r=nr_turn_client_get_relayed_address(sturn->turn, addrp))) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +static int nr_socket_turn_close(void *obj) + { + /* No-op */ +#ifndef NDEBUG + nr_socket_turn *sturn=obj; + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); +#endif + + return 0; + } + +int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx) +{ + nr_socket_turn *sturn=(nr_socket_turn*)sock->obj; + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + assert(!sturn->turn); + + sturn->turn = ctx; + + return 0; +} + +#endif /* USE_TURN */ diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h new file mode 100644 index 0000000000..13506045a8 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h @@ -0,0 +1,48 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#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 **sockp); +int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h new file mode 100644 index 0000000000..a32751d795 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h @@ -0,0 +1,218 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + +#ifndef _STUN_H +#define _STUN_H + +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/param.h> +#include <sys/socket.h> +#ifndef LINUX +#include <net/if.h> +#ifdef DARWIN +#include <net/if_var.h> +#endif +#include <net/if_dl.h> +#include <net/if_types.h> +#else +#include <linux/if.h> +#endif +#ifndef BSD +#include <net/route.h> +#endif +#include <netinet/in.h> +#ifndef LINUX +#include <netinet/in_var.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> +#endif +#include <time.h> + +#include "nr_api.h" +#include "stun_msg.h" +#include "stun_build.h" +#include "stun_codec.h" +#include "stun_hint.h" +#include "stun_util.h" +#include "nr_socket.h" +#include "stun_client_ctx.h" +#include "stun_server_ctx.h" +#include "stun_proc.h" + +#define NR_STUN_VERSION "rfc3489bis-11" +#define NR_STUN_PORT 3478 + +/* STUN attributes */ +#define NR_STUN_ATTR_MAPPED_ADDRESS 0x0001 +#define NR_STUN_ATTR_USERNAME 0x0006 +#define NR_STUN_ATTR_MESSAGE_INTEGRITY 0x0008 +#define NR_STUN_ATTR_ERROR_CODE 0x0009 +#define NR_STUN_ATTR_UNKNOWN_ATTRIBUTES 0x000A +#define NR_STUN_ATTR_REALM 0x0014 +#define NR_STUN_ATTR_NONCE 0x0015 +#define NR_STUN_ATTR_XOR_MAPPED_ADDRESS 0x0020 +#define NR_STUN_ATTR_SERVER 0x8022 +#define NR_STUN_ATTR_ALTERNATE_SERVER 0x8023 +#define NR_STUN_ATTR_FINGERPRINT 0x8028 + +/* for backwards compatibility with obsolete versions of the STUN spec */ +#define NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS 0x8020 + +#ifdef USE_STUND_0_96 +#define NR_STUN_ATTR_OLD_CHANGE_REQUEST 0x0003 +#endif /* USE_STUND_0_96 */ + +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE +/* for backwards compatibility with obsolete versions of the STUN spec */ +#define NR_STUN_ATTR_OLD_PASSWORD 0x0007 +#define NR_STUN_ATTR_OLD_RESPONSE_ADDRESS 0x0002 +#define NR_STUN_ATTR_OLD_SOURCE_ADDRESS 0x0004 +#define NR_STUN_ATTR_OLD_CHANGED_ADDRESS 0x0005 +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + +#ifdef USE_ICE +/* ICE attributes */ +#define NR_STUN_ATTR_PRIORITY 0x0024 +#define NR_STUN_ATTR_USE_CANDIDATE 0x0025 +#define NR_STUN_ATTR_ICE_CONTROLLED 0x8029 +#define NR_STUN_ATTR_ICE_CONTROLLING 0x802A +#endif /* USE_ICE */ + +#ifdef USE_TURN +/* TURN attributes */ +#define NR_STUN_ATTR_LIFETIME 0x000d +/* from an expired draft defined as optional, but in the required range */ +#define NR_STUN_ATTR_BANDWIDTH 0x0010 +#define NR_STUN_ATTR_XOR_PEER_ADDRESS 0x0012 +#define NR_STUN_ATTR_DATA 0x0013 +#define NR_STUN_ATTR_XOR_RELAY_ADDRESS 0x0016 +#define NR_STUN_ATTR_REQUESTED_TRANSPORT 0x0019 + +#define NR_STUN_ATTR_REQUESTED_TRANSPORT_UDP 17 +#endif /* USE_TURN */ + +/* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |M|M|M|M|M|C|M|M|M|C|M|M|M|M| + * |1|1|9|8|7|1|6|5|4|0|3|2|1|0| + * |1|0| | | | | | | | | | | | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Figure 3: Format of STUN Message Type Field + */ +#define NR_STUN_METHOD_TYPE_BITS(m) \ + ((((m) & 0xf80) << 2) | (((m) & 0x070) << 1) | ((m) & 0x00f)) + +#define NR_STUN_CLASS_TYPE_BITS(c) \ + ((((c) & 0x002) << 7) | (((c) & 0x001) << 4)) + +#define NR_STUN_GET_TYPE_METHOD(t) \ + ((((t) >> 2) & 0xf80) | (((t) >> 1) & 0x070) | ((t) & 0x00f)) + +#define NR_STUN_GET_TYPE_CLASS(t) \ + ((((t) >> 7) & 0x002) | (((t) >> 4) & 0x001)) + +#define NR_STUN_TYPE(m,c) (NR_STUN_METHOD_TYPE_BITS((m)) | NR_STUN_CLASS_TYPE_BITS((c))) + +/* building blocks for message types */ +#define NR_METHOD_BINDING 0x001 +#define NR_CLASS_REQUEST 0x0 +#define NR_CLASS_INDICATION 0x1 +#define NR_CLASS_RESPONSE 0x2 +#define NR_CLASS_ERROR_RESPONSE 0x3 + +/* define types for STUN messages */ +#define NR_STUN_MSG_BINDING_REQUEST NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_BINDING_INDICATION NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_INDICATION) +#define NR_STUN_MSG_BINDING_RESPONSE NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_BINDING_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_ERROR_RESPONSE) + +#ifdef USE_TURN +/* building blocks for TURN message types */ +#define NR_METHOD_ALLOCATE 0x003 +#define NR_METHOD_REFRESH 0x004 + +#define NR_METHOD_SEND 0x006 +#define NR_METHOD_DATA 0x007 +#define NR_METHOD_CREATE_PERMISSION 0x008 +#define NR_METHOD_CHANNEL_BIND 0x009 + +/* define types for a TURN message */ +#define NR_STUN_MSG_ALLOCATE_REQUEST NR_STUN_TYPE(NR_METHOD_ALLOCATE, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_ALLOCATE_RESPONSE NR_STUN_TYPE(NR_METHOD_ALLOCATE, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_ALLOCATE, \ + NR_CLASS_ERROR_RESPONSE) +#define NR_STUN_MSG_REFRESH_REQUEST NR_STUN_TYPE(NR_METHOD_REFRESH, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_REFRESH_RESPONSE NR_STUN_TYPE(NR_METHOD_REFRESH, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_REFRESH_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_REFRESH, \ + NR_CLASS_ERROR_RESPONSE) + +#define NR_STUN_MSG_SEND_INDICATION NR_STUN_TYPE(NR_METHOD_SEND, \ + NR_CLASS_INDICATION) +#define NR_STUN_MSG_DATA_INDICATION NR_STUN_TYPE(NR_METHOD_DATA, \ + NR_CLASS_INDICATION) + +#define NR_STUN_MSG_PERMISSION_REQUEST NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_PERMISSION_RESPONSE NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_PERMISSION_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \ + NR_CLASS_ERROR_RESPONSE) + +#define NR_STUN_MSG_CHANNEL_BIND_REQUEST NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_CHANNEL_BIND_RESPONSE NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_CHANNEL_BIND_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \ + NR_CLASS_ERROR_RESPONSE) + + +#endif /* USE_TURN */ + + +#define NR_STUN_AUTH_RULE_OPTIONAL (1<<0) +#define NR_STUN_AUTH_RULE_SHORT_TERM (1<<8) +#define NR_STUN_AUTH_RULE_LONG_TERM (1<<9) + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c new file mode 100644 index 0000000000..001b38e7c4 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c @@ -0,0 +1,611 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <csi_platform.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "nr_api.h" +#include "stun.h" +#include "registry.h" +#include "stun_reg.h" +#include "nr_crypto.h" + + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.1 */ +/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.1 */ +/* note that S 10.1.1 states the message MUST include MESSAGE-INTEGRITY + * and USERNAME, but that's not correct -- for instance ICE keepalive + * messages don't include these (See draft-ietf-mmusic-ice-18.txt S 10: + * "If STUN is being used for keepalives, a STUN Binding Indication is + * used. The Indication MUST NOT utilize any authentication mechanism") + */ +int +nr_stun_form_request_or_indication(int mode, int msg_type, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + assert(NR_STUN_GET_TYPE_CLASS(msg_type) == NR_CLASS_REQUEST + || NR_STUN_GET_TYPE_CLASS(msg_type) == NR_CLASS_INDICATION); + + *msg = 0; + + if ((r=nr_stun_message_create(&req))) + ABORT(r); + + req->header.type = msg_type; + + nr_crypto_random_bytes((UCHAR*)&req->header.id,sizeof(req->header.id)); + + switch (mode) { + default: + if ((r=nr_stun_message_add_fingerprint_attribute(req))) + ABORT(r); + /* fall through */ + case NR_STUN_MODE_STUN_NO_AUTH: + req->header.magic_cookie = NR_STUN_MAGIC_COOKIE; + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MODE_STUND_0_96: + req->header.magic_cookie = NR_STUN_MAGIC_COOKIE2; + + /* actually, stund 0.96 just ignores the fingerprint + * attribute, but don't bother to send it */ + + break; +#endif /* USE_STUND_0_96 */ + + } + + *msg = req; + + _status=0; + abort: + if (_status) RFREE(req); + return _status; +} + +int +nr_stun_build_req_lt_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_username_attribute(req, params->username))) + ABORT(r); + + if (params->realm && params->nonce) { + if ((r=nr_stun_message_add_realm_attribute(req, params->realm))) + ABORT(r); + + if ((r=nr_stun_message_add_nonce_attribute(req, params->nonce))) + ABORT(r); + + if ((r=nr_stun_message_add_message_integrity_attribute(req, params->password))) + ABORT(r); + } + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + +int +nr_stun_build_req_st_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_username_attribute(req, params->username))) + ABORT(r); + + if (params->password) { + if ((r=nr_stun_message_add_message_integrity_attribute(req, params->password))) + ABORT(r); + } + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + +int +nr_stun_build_req_no_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN_NO_AUTH, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + +int +nr_stun_build_keepalive(nr_stun_client_stun_keepalive_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *ind = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_INDICATION, &ind))) + ABORT(r); + + *msg = ind; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&ind); + return _status; +} + +#ifdef USE_STUND_0_96 +int +nr_stun_build_req_stund_0_96(nr_stun_client_stun_binding_request_stund_0_96_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUND_0_96, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_change_request_attribute(req, 0))) + ABORT(r); + + assert(! nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, 0)); + assert(! nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} +#endif /* USE_STUND_0_96 */ + +#ifdef USE_ICE +int +nr_stun_build_use_candidate(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_username_attribute(req, params->username))) + ABORT(r); + + if ((r=nr_stun_message_add_message_integrity_attribute(req, ¶ms->password))) + ABORT(r); + + if ((r=nr_stun_message_add_use_candidate_attribute(req))) + ABORT(r); + + if ((r=nr_stun_message_add_priority_attribute(req, params->priority))) + ABORT(r); + + if ((r=nr_stun_message_add_ice_controlling_attribute(req, params->tiebreaker))) + ABORT(r); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + +int +nr_stun_build_req_ice(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_username_attribute(req, params->username))) + ABORT(r); + + if ((r=nr_stun_message_add_message_integrity_attribute(req, ¶ms->password))) + ABORT(r); + + if ((r=nr_stun_message_add_priority_attribute(req, params->priority))) + ABORT(r); + + switch (params->control) { + case NR_ICE_CONTROLLING: + if ((r=nr_stun_message_add_ice_controlling_attribute(req, params->tiebreaker))) + ABORT(r); + break; + case NR_ICE_CONTROLLED: + if ((r=nr_stun_message_add_ice_controlled_attribute(req, params->tiebreaker))) + ABORT(r); + break; + default: + assert(0); + ABORT(R_INTERNAL); + } + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} +#endif /* USE_ICE */ + +#ifdef USE_TURN + +#ifndef __isascii +#define __isascii(c) (((c) & ~0x7F) == 0) +#endif + +/* Long-term passwords are computed over the key: + + key = MD5(username ":" realm ":" SASLprep(password)) + + Per RFC 5389 S 15.4 +*/ +int +nr_stun_compute_lt_message_integrity_password(const char *username, const char *realm, + Data *password, Data *hmac_key) +{ + char digest_input[1000]; + size_t i; + int r, _status; + size_t len; + + /* First check that the password is ASCII. We are supposed to + SASLprep but we don't support this yet + TODO(ekr@rtfm.com): Add SASLprep for password. + */ + for (i=0; i<password->len; i++) { + if (!__isascii(password->data[i])) + ABORT(R_BAD_DATA); + } + + if (hmac_key->len < 16) + ABORT(R_BAD_ARGS); + + snprintf(digest_input, sizeof(digest_input), "%s:%s:", username, realm); + if ((sizeof(digest_input) - strlen(digest_input)) < password->len) + ABORT(R_BAD_DATA); + + len = strlen(digest_input); + memcpy(digest_input + len, password->data, password->len); + + + if (r=nr_crypto_md5((UCHAR *)digest_input, len + password->len, hmac_key->data)) + ABORT(r); + hmac_key->len=16; + + _status=0; +abort: + return(_status); +} + +static int +nr_stun_build_auth_params(nr_stun_client_auth_params *auth, nr_stun_message *req) +{ + int r, _status; + UCHAR hmac_key_d[16]; + Data hmac_key; + + ATTACH_DATA(hmac_key, hmac_key_d); + + if (!auth->authenticate) + goto done; + + assert(auth->username); + assert(auth->password.len); + assert(auth->realm); + assert(auth->nonce); + + if (r=nr_stun_compute_lt_message_integrity_password(auth->username, + auth->realm, + &auth->password, + &hmac_key)) + ABORT(r); + + if (!auth->username) { + r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no username provided"); + ABORT(R_INTERNAL); + } + + if (!auth->password.len) { + r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no password provided"); + ABORT(R_INTERNAL); + } + + if (!auth->realm) { + r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no realm provided"); + ABORT(R_INTERNAL); + } + + if (!auth->nonce) { + r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no nonce provided"); + ABORT(R_INTERNAL); + } + + if ((r=nr_stun_message_add_username_attribute(req, auth->username))) + ABORT(r); + + if ((r=nr_stun_message_add_realm_attribute(req, auth->realm))) + ABORT(r); + + if ((r=nr_stun_message_add_nonce_attribute(req, auth->nonce))) + ABORT(r); + + if ((r=nr_stun_message_add_message_integrity_attribute(req, &hmac_key))) + ABORT(r); + +done: + _status=0; +abort: + return(_status); +} + +int +nr_stun_build_allocate_request(nr_stun_client_auth_params *auth, nr_stun_client_allocate_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_ALLOCATE_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_requested_transport_attribute(req, NR_STUN_ATTR_REQUESTED_TRANSPORT_UDP))) + ABORT(r); + + if ((r=nr_stun_message_add_lifetime_attribute(req, params->lifetime_secs))) + ABORT(r); + + /* TODO(ekr@rtfm.com): Add the SOFTWARE attribute (Firefox bug 857666) */ + + if ((r=nr_stun_build_auth_params(auth, req))) + ABORT(r); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + + +int nr_stun_build_refresh_request(nr_stun_client_auth_params *auth, nr_stun_client_refresh_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_REFRESH_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_lifetime_attribute(req, params->lifetime_secs))) + ABORT(r); + + + /* TODO(ekr@rtfm.com): Add the SOFTWARE attribute (Firefox bug 857666) */ + + if ((r=nr_stun_build_auth_params(auth, req))) + ABORT(r); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + + +int nr_stun_build_permission_request(nr_stun_client_auth_params *auth, nr_stun_client_permission_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_PERMISSION_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_xor_peer_address_attribute(req, ¶ms->remote_addr))) + ABORT(r); + + if ((r=nr_stun_build_auth_params(auth, req))) + ABORT(r); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + +int +nr_stun_build_send_indication(nr_stun_client_send_indication_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *ind = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_SEND_INDICATION, &ind))) + ABORT(r); + + if ((r=nr_stun_message_add_xor_peer_address_attribute(ind, ¶ms->remote_addr))) + ABORT(r); + + if ((r=nr_stun_message_add_data_attribute(ind, params->data.data, params->data.len))) + ABORT(r); + + *msg = ind; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&ind); + return _status; +} + +int +nr_stun_build_data_indication(nr_stun_client_data_indication_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *ind = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_DATA_INDICATION, &ind))) + ABORT(r); + + if ((r=nr_stun_message_add_xor_peer_address_attribute(ind, ¶ms->remote_addr))) + ABORT(r); + + if ((r=nr_stun_message_add_data_attribute(ind, params->data.data, params->data.len))) + ABORT(r); + + *msg = ind; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&ind); + return _status; +} + +#endif /* USE_TURN */ + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1.1 */ +int +nr_stun_form_success_response(nr_stun_message *req, nr_transport_addr *from, Data *password, nr_stun_message *res) +{ + int r,_status; + int request_method; + char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */ + + /* set up information for default response */ + + request_method = NR_STUN_GET_TYPE_METHOD(req->header.type); + res->header.type = NR_STUN_TYPE(request_method, NR_CLASS_RESPONSE); + res->header.magic_cookie = req->header.magic_cookie; + memcpy(&res->header.id, &req->header.id, sizeof(res->header.id)); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Mapped Address = %s", from->as_string); + + if ((r=nr_stun_message_add_xor_mapped_address_attribute(res, from))) + ABORT(r); + + if (!NR_reg_get_string(NR_STUN_REG_PREF_SERVER_NAME, server_name, sizeof(server_name))) { + if ((r=nr_stun_message_add_server_attribute(res, server_name))) + ABORT(r); + } + + if (res->header.magic_cookie == NR_STUN_MAGIC_COOKIE) { + if (password != 0) { + if ((r=nr_stun_message_add_message_integrity_attribute(res, password))) + ABORT(r); + } + + if ((r=nr_stun_message_add_fingerprint_attribute(res))) + ABORT(r); + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1.1 */ +void +nr_stun_form_error_response(nr_stun_message *req, nr_stun_message* res, int number, char* msg) +{ + char *str; + int request_method; + char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */ + + if (number < 300 || number > 699) + number = 500; + + r_log(NR_LOG_STUN, LOG_INFO, "Responding with error %d: %s", number, msg); + + request_method = NR_STUN_GET_TYPE_METHOD(req->header.type); + res->header.type = NR_STUN_TYPE(request_method, NR_CLASS_ERROR_RESPONSE); + res->header.magic_cookie = req->header.magic_cookie; + memcpy(&res->header.id, &req->header.id, sizeof(res->header.id)); + + /* during development we should never see 500s (hopefully not in deployment either) */ + + str = 0; + switch (number) { + case 300: str = "Try Alternate"; break; + case 400: str = "Bad Request"; break; + case 401: str = "Unauthorized"; break; + case 420: str = "Unknown Attribute"; break; + case 438: str = "Stale Nonce"; break; +#ifdef USE_ICE + case 487: str = "Role Conflict"; break; +#endif + case 500: str = "Server Error"; break; + } + if (str == 0) { + str = "Unknown"; + } + + if (nr_stun_message_add_error_code_attribute(res, number, str)) { + assert(0); /* should never happen */ + } + + if (!NR_reg_get_string(NR_STUN_REG_PREF_SERVER_NAME, server_name, sizeof(server_name))) { + nr_stun_message_add_server_attribute(res, server_name); + } +} + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h new file mode 100644 index 0000000000..c3f91a87b0 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h @@ -0,0 +1,147 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + +#ifndef _stun_build_h +#define _stun_build_h + +#include "stun.h" + +#define NR_STUN_MODE_STUN 1 +#ifdef USE_STUND_0_96 +#define NR_STUN_MODE_STUND_0_96 2 /* backwards compatibility mode */ +#endif /* USE_STUND_0_96 */ +#define NR_STUN_MODE_STUN_NO_AUTH 3 +int nr_stun_form_request_or_indication(int mode, int msg_type, nr_stun_message **msg); + +typedef struct nr_stun_client_stun_binding_request_params_ { + char *username; + Data *password; + char *nonce; + char *realm; +} nr_stun_client_stun_binding_request_params; + +int nr_stun_build_req_lt_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg); +int nr_stun_build_req_st_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg); +int nr_stun_build_req_no_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg); + + +typedef struct nr_stun_client_stun_keepalive_params_ { +#if defined(WIN32) || defined(__clang__) + // VC++ and clang give error and warning respectively if no members + int dummy; +#endif +} nr_stun_client_stun_keepalive_params; + +int nr_stun_build_keepalive(nr_stun_client_stun_keepalive_params *params, nr_stun_message **msg); + + +#ifdef USE_STUND_0_96 +typedef struct nr_stun_client_stun_binding_request_stund_0_96_params_ { +#ifdef WIN32 // silly VC++ gives error if no members + int dummy; +#endif +} nr_stun_client_stun_binding_request_stund_0_96_params; + +int nr_stun_build_req_stund_0_96(nr_stun_client_stun_binding_request_stund_0_96_params *params, nr_stun_message **msg); +#endif /* USE_STUND_0_96 */ + + +#ifdef USE_ICE +typedef struct nr_stun_client_ice_binding_request_params_ { + char *username; + Data password; + UINT4 priority; + int control; +#define NR_ICE_CONTROLLING 1 +#define NR_ICE_CONTROLLED 2 + UINT8 tiebreaker; +} nr_stun_client_ice_binding_request_params; + +int nr_stun_build_use_candidate(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg); + +int nr_stun_build_req_ice(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg); +#endif /* USE_ICE */ + + +typedef struct nr_stun_client_auth_params_ { + char authenticate; + char *username; + char *realm; + char *nonce; + Data password; +} nr_stun_client_auth_params; + +#ifdef USE_TURN +typedef struct nr_stun_client_allocate_request_params_ { + UINT4 lifetime_secs; +} nr_stun_client_allocate_request_params; + +int nr_stun_build_allocate_request(nr_stun_client_auth_params *auth, nr_stun_client_allocate_request_params *params, nr_stun_message **msg); + + +typedef struct nr_stun_client_refresh_request_params_ { + UINT4 lifetime_secs; +} nr_stun_client_refresh_request_params; + +int nr_stun_build_refresh_request(nr_stun_client_auth_params *auth, nr_stun_client_refresh_request_params *params, nr_stun_message **msg); + + + +typedef struct nr_stun_client_permission_request_params_ { + nr_transport_addr remote_addr; +} nr_stun_client_permission_request_params; + +int nr_stun_build_permission_request(nr_stun_client_auth_params *auth, nr_stun_client_permission_request_params *params, nr_stun_message **msg); + + +typedef struct nr_stun_client_send_indication_params_ { + nr_transport_addr remote_addr; + Data data; +} nr_stun_client_send_indication_params; + +int nr_stun_build_send_indication(nr_stun_client_send_indication_params *params, nr_stun_message **msg); + +typedef struct nr_stun_client_data_indication_params_ { + nr_transport_addr remote_addr; + Data data; +} nr_stun_client_data_indication_params; + +int nr_stun_build_data_indication(nr_stun_client_data_indication_params *params, nr_stun_message **msg); +#endif /* USE_TURN */ + +int nr_stun_form_success_response(nr_stun_message *req, nr_transport_addr *from, Data *password, nr_stun_message *res); +void nr_stun_form_error_response(nr_stun_message *request, nr_stun_message* response, int number, char* msg); +int nr_stun_compute_lt_message_integrity_password(const char *username, const char *realm, + Data *password, Data *hmac_key); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c new file mode 100644 index 0000000000..50b9f74a5b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c @@ -0,0 +1,888 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <assert.h> +#include <string.h> +#include <math.h> + +#include <nr_api.h> +#include "stun.h" +#include "async_timer.h" +#include "registry.h" +#include "stun_reg.h" +#include "nr_crypto.h" +#include "r_time.h" + +static int nr_stun_client_send_request(nr_stun_client_ctx *ctx); +static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg); +static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password); + +#define NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD 1 +#define NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK 2 + +int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp) + { + nr_stun_client_ctx *ctx=0; + char allow_loopback; + int r,_status; + + if ((r=nr_stun_startup())) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_stun_client_ctx)))) + ABORT(R_NO_MEMORY); + + ctx->state=NR_STUN_CLIENT_STATE_INITTED; + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + ctx->sock=sock; + + nr_socket_getaddr(sock,&ctx->my_addr); + nr_transport_addr_copy(&ctx->peer_addr,peer); + assert(ctx->my_addr.protocol==ctx->peer_addr.protocol); + + if (RTO != 0) { + ctx->rto_ms = RTO; + } else if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT, &ctx->rto_ms)) { + ctx->rto_ms = 100; + } + + if (NR_reg_get_double(NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF, &ctx->retransmission_backoff_factor)) + 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->maximum_transmits_timeout_ms)) + ctx->maximum_transmits_timeout_ms = 16 * ctx->rto_ms; + + ctx->mapped_addr_check_mask = NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD; + if (NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, &allow_loopback) || + !allow_loopback) { + ctx->mapped_addr_check_mask |= NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK; + } + + if (ctx->my_addr.protocol == IPPROTO_TCP) { + /* Because TCP is reliable there is only one final timeout value. + * We store the timeout value for TCP in here, because timeout_ms gets + * reset to 0 in client_reset() which gets called from client_start() */ + ctx->maximum_transmits_timeout_ms = ctx->rto_ms * + pow(ctx->retransmission_backoff_factor, + ctx->maximum_transmits); + ctx->maximum_transmits = 1; + } + + *ctxp=ctx; + + _status=0; + abort: + if(_status){ + nr_stun_client_ctx_destroy(&ctx); + } + return(_status); + } + +static void nr_stun_client_fire_finished_cb(nr_stun_client_ctx *ctx) + { + if (ctx->finished_cb) { + NR_async_cb finished_cb = ctx->finished_cb; + ctx->finished_cb = 0; /* prevent 2nd call */ + /* finished_cb call must be absolutely last thing in function + * because as a side effect this ctx may be operated on in the + * callback */ + finished_cb(0,0,ctx->cb_arg); + } + } + +int nr_stun_client_start(nr_stun_client_ctx *ctx, int mode, NR_async_cb finished_cb, void *cb_arg) + { + int r,_status; + + if (ctx->state != NR_STUN_CLIENT_STATE_INITTED) + ABORT(R_NOT_PERMITTED); + + ctx->mode=mode; + + ctx->state=NR_STUN_CLIENT_STATE_RUNNING; + ctx->finished_cb=finished_cb; + ctx->cb_arg=cb_arg; + + if(mode!=NR_STUN_CLIENT_MODE_KEEPALIVE){ + if(r=nr_stun_client_send_request(ctx)) + ABORT(r); + } + + _status=0; + abort: + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) { + nr_stun_client_fire_finished_cb(ctx); + } + + return(_status); + } + + int nr_stun_client_restart(nr_stun_client_ctx* ctx, + const nr_transport_addr* peer_addr) { + int r,_status; + int mode; + NR_async_cb finished_cb; + void *cb_arg; + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + mode = ctx->mode; + finished_cb = ctx->finished_cb; + cb_arg = ctx->cb_arg; + + nr_stun_client_reset(ctx); + nr_transport_addr_copy(&ctx->peer_addr, peer_addr); + + if (r=nr_stun_client_start(ctx, mode, finished_cb, cb_arg)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int +nr_stun_client_reset(nr_stun_client_ctx *ctx) +{ + /* Cancel the timer firing */ + if (ctx->timer_handle){ + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + } + + nr_stun_message_destroy(&ctx->request); + ctx->request = 0; + + nr_stun_message_destroy(&ctx->response); + ctx->response = 0; + + memset(&ctx->results, 0, sizeof(ctx->results)); + + ctx->mode = 0; + ctx->finished_cb = 0; + ctx->cb_arg = 0; + ctx->request_ct = 0; + ctx->timeout_ms = 0; + + ctx->state = NR_STUN_CLIENT_STATE_INITTED; + + return 0; +} + +static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg) + { + int _status; + nr_stun_client_ctx *ctx=cb_arg; + struct timeval now; + INT8 ms_waited; + + /* Prevent this timer from being cancelled later */ + ctx->timer_handle=0; + + /* Shouldn't happen */ + if(ctx->state==NR_STUN_CLIENT_STATE_CANCELLED) + ABORT(R_REJECTED); + + gettimeofday(&now, 0); + if (r_timeval_diff_ms(&now, &ctx->timer_set, &ms_waited)) { + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired",ctx->label); + } + else { + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired (after %llu ms)",ctx->label, ms_waited); + } + + if (ctx->request_ct >= ctx->maximum_transmits) { + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Timed out",ctx->label); + ctx->state=NR_STUN_CLIENT_STATE_TIMED_OUT; + ABORT(R_FAILED); + } + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + // track retransmits for ice telemetry + nr_accumulate_count(&(ctx->retransmit_ct), 1); + + /* as a side effect will reset the timer */ + nr_stun_client_send_request(ctx); + + _status = 0; + abort: + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) { + /* Cancel the timer firing */ + if (ctx->timer_handle){ + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + } + + nr_stun_client_fire_finished_cb(ctx); + } + if (_status) { + // cb doesn't return anything, but we should probably log that we aborted + // This also quiets the unused variable warnings. + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired cb abort with status: %d", + ctx->label, _status); + } + return; + } + +int nr_stun_client_force_retransmit(nr_stun_client_ctx *ctx) + { + int r,_status; + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + if (ctx->request_ct > ctx->maximum_transmits) { + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Too many retransmit attempts",ctx->label); + ABORT(R_FAILED); + } + + /* if there is a scheduled retransimt, get rid of the scheduled retransmit + * and retransmit immediately */ + if (ctx->timer_handle) { + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + + if (r=nr_stun_client_send_request(ctx)) + ABORT(r); + } + + _status=0; + abort: + + return(_status); + } + +static int nr_stun_client_send_request(nr_stun_client_ctx *ctx) + { + int r,_status; + char string[256]; + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Sending check request (my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,ctx->peer_addr.as_string); + + if (ctx->request == 0) { + switch (ctx->mode) { + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: + ctx->params.stun_binding_request.nonce = ctx->nonce; + ctx->params.stun_binding_request.realm = ctx->realm; + assert(0); + ABORT(R_INTERNAL); + /* TODO(ekr@rtfm.com): Need to implement long-term auth for binding + requests */ + if ((r=nr_stun_build_req_lt_auth(&ctx->params.stun_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: + if ((r=nr_stun_build_req_st_auth(&ctx->params.stun_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: + if ((r=nr_stun_build_req_no_auth(&ctx->params.stun_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_STUN_CLIENT_MODE_KEEPALIVE: + if ((r=nr_stun_build_keepalive(&ctx->params.stun_keepalive, &ctx->request))) + ABORT(r); + break; +#ifdef USE_STUND_0_96 + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: + if ((r=nr_stun_build_req_stund_0_96(&ctx->params.stun_binding_request_stund_0_96, &ctx->request))) + ABORT(r); + break; +#endif /* USE_STUND_0_96 */ + +#ifdef USE_ICE + case NR_ICE_CLIENT_MODE_USE_CANDIDATE: + if ((r=nr_stun_build_use_candidate(&ctx->params.ice_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_ICE_CLIENT_MODE_BINDING_REQUEST: + if ((r=nr_stun_build_req_ice(&ctx->params.ice_binding_request, &ctx->request))) + ABORT(r); + break; +#endif /* USE_ICE */ + +#ifdef USE_TURN + case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST: + if ((r=nr_stun_build_allocate_request(&ctx->auth_params, &ctx->params.allocate_request, &ctx->request))) + ABORT(r); + break; + case NR_TURN_CLIENT_MODE_REFRESH_REQUEST: + if ((r=nr_stun_build_refresh_request(&ctx->auth_params, &ctx->params.refresh_request, &ctx->request))) + ABORT(r); + break; + case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST: + if ((r=nr_stun_build_permission_request(&ctx->auth_params, &ctx->params.permission_request, &ctx->request))) + ABORT(r); + break; + case NR_TURN_CLIENT_MODE_SEND_INDICATION: + if ((r=nr_stun_build_send_indication(&ctx->params.send_indication, &ctx->request))) + ABORT(r); + break; +#endif /* USE_TURN */ + + default: + assert(0); + ABORT(R_FAILED); + break; + } + } + + if (ctx->request->length == 0) { + if ((r=nr_stun_encode_message(ctx->request))) + ABORT(r); + } + + snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Sending to %s ", ctx->label, ctx->peer_addr.as_string); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)ctx->request->buffer, ctx->request->length); + + assert(ctx->my_addr.protocol==ctx->peer_addr.protocol); + + if(r=nr_socket_sendto(ctx->sock, ctx->request->buffer, ctx->request->length, 0, &ctx->peer_addr)) { + if (r != R_WOULDBLOCK) { + ABORT(r); + } + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_socket_sendto blocked, treating as dropped packet",ctx->label); + } + + ctx->request_ct++; + + if (NR_STUN_GET_TYPE_CLASS(ctx->request->header.type) == NR_CLASS_INDICATION) { + /* no need to set the timer because indications don't receive a + * response */ + } + else { + if (ctx->request_ct >= ctx->maximum_transmits) { + /* Reliable transport only get here once. Unreliable get here for + * their final timeout. */ + ctx->timeout_ms += ctx->maximum_transmits_timeout_ms; + } + else if (ctx->timeout_ms) { + /* exponential backoff */ + ctx->timeout_ms *= ctx->retransmission_backoff_factor; + } + else { + /* initial timeout unreliable transports */ + ctx->timeout_ms = ctx->rto_ms; + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Next timer will fire in %u ms",ctx->label, ctx->timeout_ms); + + gettimeofday(&ctx->timer_set, 0); + + assert(ctx->timeout_ms); + NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle); + } + + _status=0; + abort: + if (_status) { + nr_stun_client_failed(ctx); + } + return(_status); + } + +static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password) +{ + *password = (Data*)arg; + if (!arg) + return(R_NOT_FOUND); + return(0); +} + +int nr_stun_transport_addr_check(nr_transport_addr* addr, UINT4 check) + { + if((check & NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD) && nr_transport_addr_is_wildcard(addr)) + return(R_BAD_DATA); + + if ((check & NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK) && nr_transport_addr_is_loopback(addr)) + return(R_BAD_DATA); + + return(0); + } + +int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr) + { + int r,_status; + char string[256]; + char *username = 0; + Data *password = 0; + int allow_unauthed_redirect = 0; + nr_stun_message_attribute *attr; + nr_transport_addr *mapped_addr = 0; + int fail_on_error = 0; + UCHAR hmac_key_d[16]; + Data hmac_key; + int compute_lt_key=0; + /* TODO(bcampen@mozilla.com): Bug 1023619, refactor this. */ + int response_matched=0; + + ATTACH_DATA(hmac_key, hmac_key_d); + + if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) && + (ctx->state != NR_STUN_CLIENT_STATE_WAITING)) + ABORT(R_REJECTED); + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Inspecting STUN response (my_addr=%s, peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string); + + snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Received ", ctx->label); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len); + + /* determine password */ + switch (ctx->mode) { + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: + /* If the STUN server responds with an error, give up, since we don't + * want to delay the completion of gathering. */ + fail_on_error = 1; + compute_lt_key = 1; + /* Fall through */ + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: + password = ctx->params.stun_binding_request.password; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: + /* If the STUN server responds with an error, give up, since we don't + * want to delay the completion of gathering. */ + fail_on_error = 1; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: + /* If the STUN server responds with an error, give up, since we don't + * want to delay the completion of gathering. */ + fail_on_error = 1; + break; + +#ifdef USE_ICE + case NR_ICE_CLIENT_MODE_BINDING_REQUEST: + /* We do not set fail_on_error here. The error might be transient, and + * retrying isn't going to cause a slowdown. */ + password = &ctx->params.ice_binding_request.password; + break; + case NR_ICE_CLIENT_MODE_USE_CANDIDATE: + /* We do not set fail_on_error here. The error might be transient, and + * retrying isn't going to cause a slowdown. */ + password = &ctx->params.ice_binding_request.password; + break; +#endif /* USE_ICE */ + +#ifdef USE_TURN + case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST: + fail_on_error = 1; + compute_lt_key = 1; + /* Do not require mutual auth on redirect responses to Allocate requests. */ + allow_unauthed_redirect = 1; + username = ctx->auth_params.username; + password = &ctx->auth_params.password; + /* do nothing */ + break; + case NR_TURN_CLIENT_MODE_REFRESH_REQUEST: + fail_on_error = 1; + compute_lt_key = 1; + username = ctx->auth_params.username; + password = &ctx->auth_params.password; + /* do nothing */ + break; + case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST: + fail_on_error = 1; + compute_lt_key = 1; + username = ctx->auth_params.username; + password = &ctx->auth_params.password; + /* do nothing */ + break; + case NR_TURN_CLIENT_MODE_SEND_INDICATION: + /* do nothing -- we just got our DATA-INDICATION */ + break; +#endif /* USE_TURN */ + + default: + assert(0); + ABORT(R_FAILED); + break; + } + + if (compute_lt_key) { + if (!ctx->realm || !username) { + r_log(NR_LOG_STUN, LOG_DEBUG, "Long-term auth required but no realm/username specified. Randomizing key"); + /* Fill the key with random bytes to guarantee non-match */ + if (r=nr_crypto_random_bytes(hmac_key_d, sizeof(hmac_key_d))) + ABORT(r); + } + else { + if (r=nr_stun_compute_lt_message_integrity_password(username, ctx->realm, + password, &hmac_key)) + ABORT(r); + } + password = &hmac_key; + } + + if (ctx->response) { + nr_stun_message_destroy(&ctx->response); + } + + /* TODO(bcampen@mozilla.com): Bug 1023619, refactor this. */ + if ((r=nr_stun_message_create2(&ctx->response, msg, len))) + ABORT(r); + + if ((r=nr_stun_decode_message(ctx->response, nr_stun_client_get_password, password))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): error decoding response",ctx->label); + ABORT(r); + } + + /* This will return an error if request and response don't match, + which is how we reject responses that match other contexts. */ + if ((r=nr_stun_receive_message(ctx->request, ctx->response))) { + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Response is not for us",ctx->label); + ABORT(r); + } + + r_log(NR_LOG_STUN,LOG_INFO, + "STUN-CLIENT(%s): Received response; processing",ctx->label); + response_matched=1; + + if (allow_unauthed_redirect && + nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_ERROR_CODE, + &attr) && + (attr->u.error_code.number / 100 == 3)) { + password = 0; + } + +/* TODO: !nn! currently using password!=0 to mean that auth is required, + * TODO: !nn! but we should probably pass that in explicitly via the + * TODO: !nn! usage (ctx->mode?) */ + if (password) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_NONCE, 0)) { + if ((r=nr_stun_receive_response_long_term_auth(ctx->response, ctx))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): long term auth failed",ctx->label); + ABORT(r); + } + } + else { + if ((r=nr_stun_receive_response_short_term_auth(ctx->response))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): short term auth failed",ctx->label); + ABORT(r); + } + } + } + + if (NR_STUN_GET_TYPE_CLASS(ctx->response->header.type) == NR_CLASS_RESPONSE) { + if ((r=nr_stun_process_success_response(ctx->response))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_stun_process_success_response failed",ctx->label); + ABORT(r); + } + } + else { + if (fail_on_error) { + ctx->state = NR_STUN_CLIENT_STATE_FAILED; + } + /* Note: most times we call process_error_response, we get r != 0. + + However, if the error is to be discarded, we get r == 0, smash + the error code, and just keep going. + */ + if ((r=nr_stun_process_error_response(ctx->response, &ctx->error_code))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_stun_process_error_response failed",ctx->label); + ABORT(r); + } + else { + ctx->error_code = 0xffff; + /* drop the error on the floor */ + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): processed error response",ctx->label); + ABORT(R_FAILED); + } + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Successfully parsed mode=%d",ctx->label,ctx->mode); + +/* TODO: !nn! this should be moved to individual message receive/processing sections */ + switch (ctx->mode) { + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) { + /* Compensate for a bug in Google's STUN servers where they always respond with MAPPED-ADDRESS */ + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS but MAPPED-ADDRESS. Falling back (though server is wrong).", ctx->label); + } + else { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS or MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + } + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, 0) && ! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + + mapped_addr = &ctx->results.stun_binding_response_stund_0_96.mapped_addr; + break; + +#ifdef USE_ICE + case NR_ICE_CLIENT_MODE_BINDING_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; + case NR_ICE_CLIENT_MODE_USE_CANDIDATE: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; +#endif /* USE_ICE */ + +#ifdef USE_TURN + case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + + if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_RELAY_ADDRESS, &attr)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-RELAYED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + + if ((r=nr_stun_transport_addr_check(&attr->u.relay_address.unmasked, + ctx->mapped_addr_check_mask))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_stun_transport_addr_check failed",ctx->label); + ABORT(r); + } + + if ((r=nr_transport_addr_copy( + &ctx->results.allocate_response.relay_addr, + &attr->u.relay_address.unmasked))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_transport_addr_copy failed",ctx->label); + ABORT(r); + } + + if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No LIFETIME",ctx->label); + ABORT(R_BAD_DATA); + } + ctx->results.allocate_response.lifetime_secs=attr->u.lifetime_secs; + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received relay address: %s", ctx->label, ctx->results.allocate_response.relay_addr.as_string); + + mapped_addr = &ctx->results.allocate_response.mapped_addr; + + break; + case NR_TURN_CLIENT_MODE_REFRESH_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No LIFETIME",ctx->label); + ABORT(R_BAD_DATA); + } + ctx->results.refresh_response.lifetime_secs=attr->u.lifetime_secs; + break; + case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + break; +#endif /* USE_TURN */ + + default: + assert(0); + ABORT(R_FAILED); + break; + } + + /* make sure we have the most up-to-date address from this peer */ + if (nr_transport_addr_cmp(&ctx->peer_addr, peer_addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Peer moved from %s to %s", ctx->label, ctx->peer_addr.as_string, peer_addr->as_string); + nr_transport_addr_copy(&ctx->peer_addr, peer_addr); + } + + if (mapped_addr) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, &attr)) { + if ((r=nr_stun_transport_addr_check(&attr->u.xor_mapped_address.unmasked, + ctx->mapped_addr_check_mask))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): XOR-MAPPED-ADDRESS is bogus",ctx->label); + ABORT(r); + } + + if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.xor_mapped_address.unmasked))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_transport_addr_copy failed",ctx->label); + ABORT(r); + } + } + else if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, &attr)) { + if ((r=nr_stun_transport_addr_check(&attr->u.mapped_address, + ctx->mapped_addr_check_mask))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): MAPPED-ADDRESS is bogus",ctx->label); + ABORT(r); + } + + if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.mapped_address))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_transport_addr_copy failed",ctx->label); + ABORT(r); + } + } + else { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No mapped address!",ctx->label); + ABORT(R_BAD_DATA); + } + + // STUN doesn't distinguish protocol in mapped address, therefore + // assign used protocol from peer_addr + if (mapped_addr->protocol!=peer_addr->protocol){ + mapped_addr->protocol=peer_addr->protocol; + nr_transport_addr_fmt_addr_string(mapped_addr); + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received mapped address: %s", ctx->label, mapped_addr->as_string); + } + + ctx->state=NR_STUN_CLIENT_STATE_DONE; + + _status=0; + abort: + if(_status && response_matched){ + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): Error processing response: %s, stun error code %d.", ctx->label, nr_strerror(_status), (int)ctx->error_code); + } + + if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) && + (ctx->state != NR_STUN_CLIENT_STATE_WAITING)) { + /* Cancel the timer firing */ + if (ctx->timer_handle) { + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle = 0; + } + + nr_stun_client_fire_finished_cb(ctx); + } + + return(_status); + } + +int nr_stun_client_ctx_destroy(nr_stun_client_ctx **ctxp) + { + nr_stun_client_ctx *ctx; + + if(!ctxp || !*ctxp) + return(0); + + ctx=*ctxp; + *ctxp=0; + + nr_stun_client_reset(ctx); + + RFREE(ctx->nonce); + RFREE(ctx->realm); + + RFREE(ctx->label); + RFREE(ctx); + + return(0); + } + + +int nr_stun_client_cancel(nr_stun_client_ctx *ctx) + { + /* Cancel the timer firing */ + if (ctx->timer_handle){ + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + } + + /* Mark cancelled so we ignore any returned messsages */ + ctx->state=NR_STUN_CLIENT_STATE_CANCELLED; + return(0); +} + +int nr_stun_client_wait(nr_stun_client_ctx *ctx) + { + nr_stun_client_cancel(ctx); + ctx->state=NR_STUN_CLIENT_STATE_WAITING; + + ctx->request_ct = ctx->maximum_transmits; + ctx->timeout_ms = ctx->maximum_transmits_timeout_ms; + NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle); + + return(0); + } + +int nr_stun_client_failed(nr_stun_client_ctx *ctx) + { + nr_stun_client_cancel(ctx); + ctx->state=NR_STUN_CLIENT_STATE_FAILED; + nr_stun_client_fire_finished_cb(ctx); + return(0); + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h new file mode 100644 index 0000000000..0cc9045e2a --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h @@ -0,0 +1,200 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _stun_client_ctx_h +#define _stun_client_ctx_h + +/* forward declaration */ +typedef struct nr_stun_client_ctx_ nr_stun_client_ctx; + +#include "stun.h" + +/* Checklist for adding new STUN transaction types + + 1. Add new method type in stun.h (NR_METHOD_*) + 2. Add new MSGs in stun.h (NR_STUN_MSG_*) + 3. Add new messages to stun_util.c:nr_stun_msg_type + 4. Add new request type to stun_build.h + 4. Add new message builder to stun_build.c + 5. Add new response type to stun_client_ctx.h + 6. Add new arm to stun_client_ctx.c:nr_stun_client_send_request + 7. Add new arms to nr_stun_client_process_response + 8. Add new arms to stun_hint.c:nr_is_stun_message +*/ + + + + +typedef union nr_stun_client_params_ { + + nr_stun_client_stun_binding_request_params stun_binding_request; + nr_stun_client_stun_keepalive_params stun_keepalive; +#ifdef USE_STUND_0_96 + nr_stun_client_stun_binding_request_stund_0_96_params stun_binding_request_stund_0_96; +#endif /* USE_STUND_0_96 */ + +#ifdef USE_ICE + nr_stun_client_ice_binding_request_params ice_binding_request; +#endif /* USE_ICE */ + +#ifdef USE_TURN + nr_stun_client_allocate_request_params allocate_request; + nr_stun_client_refresh_request_params refresh_request; + nr_stun_client_permission_request_params permission_request; + nr_stun_client_send_indication_params send_indication; +#endif /* USE_TURN */ + +} nr_stun_client_params; + +typedef struct nr_stun_client_stun_binding_response_results_ { + nr_transport_addr mapped_addr; +} nr_stun_client_stun_binding_response_results; + +typedef struct nr_stun_client_stun_binding_response_stund_0_96_results_ { + nr_transport_addr mapped_addr; +} nr_stun_client_stun_binding_response_stund_0_96_results; + +#ifdef USE_ICE +typedef struct nr_stun_client_ice_use_candidate_results_ { +#ifdef WIN32 // silly VC++ gives error if no members + int dummy; +#endif +} nr_stun_client_ice_use_candidate_results; + +typedef struct nr_stun_client_ice_binding_response_results_ { + nr_transport_addr mapped_addr; +} nr_stun_client_ice_binding_response_results; +#endif /* USE_ICE */ + +#ifdef USE_TURN +typedef struct nr_stun_client_allocate_response_results_ { + nr_transport_addr relay_addr; + nr_transport_addr mapped_addr; + UINT4 lifetime_secs; +} nr_stun_client_allocate_response_results; + +typedef struct nr_stun_client_refresh_response_results_ { + UINT4 lifetime_secs; +} nr_stun_client_refresh_response_results; + +typedef struct nr_stun_client_permission_response_results_ { + UINT4 lifetime_secs; +} nr_stun_client_permission_response_results; + +#endif /* USE_TURN */ + +typedef union nr_stun_client_results_ { + nr_stun_client_stun_binding_response_results stun_binding_response; + nr_stun_client_stun_binding_response_stund_0_96_results stun_binding_response_stund_0_96; + +#ifdef USE_ICE + nr_stun_client_ice_use_candidate_results ice_use_candidate; + nr_stun_client_ice_binding_response_results ice_binding_response; +#endif /* USE_ICE */ + +#ifdef USE_TURN + nr_stun_client_allocate_response_results allocate_response; + nr_stun_client_refresh_response_results refresh_response; +#endif /* USE_TURN */ +} nr_stun_client_results; + +struct nr_stun_client_ctx_ { + int state; +#define NR_STUN_CLIENT_STATE_INITTED 0 +#define NR_STUN_CLIENT_STATE_RUNNING 1 +#define NR_STUN_CLIENT_STATE_DONE 2 +#define NR_STUN_CLIENT_STATE_FAILED 3 +#define NR_STUN_CLIENT_STATE_TIMED_OUT 4 +#define NR_STUN_CLIENT_STATE_CANCELLED 5 +#define NR_STUN_CLIENT_STATE_WAITING 6 + + int mode; +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH 1 +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH 2 +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH 3 +#define NR_STUN_CLIENT_MODE_KEEPALIVE 4 +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96 5 +#ifdef USE_ICE +#define NR_ICE_CLIENT_MODE_USE_CANDIDATE 10 +#define NR_ICE_CLIENT_MODE_BINDING_REQUEST 11 +#endif /* USE_ICE */ +#ifdef USE_TURN +#define NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST 20 +#define NR_TURN_CLIENT_MODE_REFRESH_REQUEST 21 +#define NR_TURN_CLIENT_MODE_SEND_INDICATION 22 +#define NR_TURN_CLIENT_MODE_DATA_INDICATION 24 +#define NR_TURN_CLIENT_MODE_PERMISSION_REQUEST 25 +#endif /* USE_TURN */ + + char *label; + nr_transport_addr my_addr; + nr_transport_addr peer_addr; + nr_socket *sock; + nr_stun_client_auth_params auth_params; + nr_stun_client_params params; + nr_stun_client_results results; + char *nonce; + char *realm; + void *timer_handle; + UINT2 request_ct; + UINT2 retransmit_ct; + UINT4 rto_ms; /* retransmission time out */ + double retransmission_backoff_factor; + UINT4 maximum_transmits; + UINT4 maximum_transmits_timeout_ms; + UINT4 mapped_addr_check_mask; /* What checks to run on mapped addresses */ + int timeout_ms; + struct timeval timer_set; + NR_async_cb finished_cb; + void *cb_arg; + nr_stun_message *request; + nr_stun_message *response; + UINT2 error_code; +}; + +int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp); +int nr_stun_client_start(nr_stun_client_ctx *ctx, int mode, NR_async_cb finished_cb, void *cb_arg); +int nr_stun_client_restart(nr_stun_client_ctx* ctx, + const nr_transport_addr* peer_addr); +int nr_stun_client_force_retransmit(nr_stun_client_ctx *ctx); +int nr_stun_client_reset(nr_stun_client_ctx *ctx); +int nr_stun_client_ctx_destroy(nr_stun_client_ctx **ctxp); +int nr_stun_transport_addr_check(nr_transport_addr* addr, UINT4 mask); +int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr); +int nr_stun_client_cancel(nr_stun_client_ctx *ctx); +int nr_stun_client_wait(nr_stun_client_ctx *ctx); +int nr_stun_client_failed(nr_stun_client_ctx *ctx); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c new file mode 100644 index 0000000000..ae748a667b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c @@ -0,0 +1,1550 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> +#include <stddef.h> + +#include "nr_api.h" +#include "stun.h" +#include "byteorder.h" +#include "r_crc32.h" +#include "nr_crypto.h" + +#define NR_STUN_IPV4_FAMILY 0x01 +#define NR_STUN_IPV6_FAMILY 0x02 + +#define SKIP_ATTRIBUTE_DECODE -1 + +static int nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info); + +static int nr_stun_fix_attribute_ordering(nr_stun_message *msg); + +static int nr_stun_encode_htons(UINT2 data, size_t buflen, UCHAR *buf, size_t *offset); +static int nr_stun_encode_htonl(UINT4 data, size_t buflen, UCHAR *buf, size_t *offset); +static int nr_stun_encode_htonll(UINT8 data, size_t buflen, UCHAR *buf, size_t *offset); +static int nr_stun_encode(UCHAR *data, size_t length, size_t buflen, UCHAR *buf, size_t *offset); + +static int nr_stun_decode_htons(UCHAR *buf, size_t buflen, size_t *offset, UINT2 *data); +static int nr_stun_decode_htonl(UCHAR *buf, size_t buflen, size_t *offset, UINT4 *data); +static int nr_stun_decode_htonll(UCHAR *buf, size_t buflen, size_t *offset, UINT8 *data); +static int nr_stun_decode(size_t length, UCHAR *buf, size_t buflen, size_t *offset, UCHAR *data); + +static int nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, size_t len, void *data, size_t max_bytes, size_t max_chars); + +static int nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int +nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data); + + +int +nr_stun_encode_htons(UINT2 data, size_t buflen, UCHAR *buf, size_t *offset) +{ + UINT2 d = htons(data); + + if (*offset + sizeof(d) >= buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd >= %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode_htonl(UINT4 data, size_t buflen, UCHAR *buf, size_t *offset) +{ + UINT4 d = htonl(data); + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode_htonll(UINT8 data, size_t buflen, UCHAR *buf, size_t *offset) +{ + UINT8 d = nr_htonll(data); + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode(UCHAR *data, size_t length, size_t buflen, UCHAR *buf, size_t *offset) +{ + if (*offset + length > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], data, length); + *offset += length; + + return 0; +} + + +int +nr_stun_decode_htons(UCHAR *buf, size_t buflen, size_t *offset, UINT2 *data) +{ + UINT2 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = htons(d); + + return 0; +} + +int +nr_stun_decode_htonl(UCHAR *buf, size_t buflen, size_t *offset, UINT4 *data) +{ + UINT4 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = htonl(d); + + return 0; +} + +int +nr_stun_decode_htonll(UCHAR *buf, size_t buflen, size_t *offset, UINT8 *data) +{ + UINT8 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = nr_htonll(d); + + return 0; +} + +int +nr_stun_decode(size_t length, UCHAR *buf, size_t buflen, size_t *offset, UCHAR *data) +{ + if (*offset + length > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen); + return R_BAD_DATA; + } + + memcpy(data, &buf[*offset], length); + *offset += length; + + return 0; +} + +/** + * The argument must be a non-null pointer to a zero-terminated string. + * + * If the argument is valid UTF-8, returns the number of code points in the + * string excluding the zero-terminator. + * + * If the argument is invalid UTF-8, returns a lower bound for the number of + * code points in the string. (If UTF-8 error handling was performed on the + * string, new REPLACEMENT CHARACTER code points could be introduced in + * a way that would increase the total number of code points compared to + * what this function counts.) + */ +size_t +nr_count_utf8_code_points_without_validation(const char *s) { + size_t nchars = 0; + char c; + while ((c = *s)) { + if ((c & 0xC0) != 0x80) { + ++nchars; + } + ++s; + } + return nchars; +} + +int +nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, size_t len, void *data, size_t max_bytes, size_t max_chars) +{ + int _status; + char *s = data; + size_t nchars; + + if (len > max_bytes) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %d bytes", attr_info->name, len); + ABORT(R_FAILED); + } + + nchars = nr_count_utf8_code_points_without_validation(s); + if (nchars > max_chars) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %zd characters", attr_info->name, nchars); + ABORT(R_FAILED); + } + + _status = 0; + abort: + return _status; +} + +int +nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + int r,_status; + nr_stun_attr_error_code *ec = data; + + if (ec->number < 300 || ec->number > 699) + ABORT(R_FAILED); + + if ((r=nr_stun_attr_string_illegal(attr_info, strlen(ec->reason), ec->reason, NR_STUN_MAX_ERROR_CODE_REASON_BYTES, NR_STUN_MAX_ERROR_CODE_REASON_CHARS))) + ABORT(r); + + _status = 0; + abort: + return _status; +} + +int +nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_NONCE_BYTES, NR_STUN_MAX_NONCE_CHARS); +} + +int +nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_REALM_BYTES, NR_STUN_MAX_REALM_CHARS); +} + +int +nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_SERVER_BYTES, NR_STUN_MAX_SERVER_CHARS); +} + +int +nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_USERNAME_BYTES, -1); +} + +static int +nr_stun_attr_codec_UCHAR_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UCHAR*)data); + return 0; +} + +static int +nr_stun_attr_codec_UCHAR_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + UINT4 tmp = *((UCHAR *)data); + tmp <<= 24; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset) + || nr_stun_encode_htonl(tmp , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UCHAR_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + UINT4 tmp; + + if (attrlen != sizeof(UINT4)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonl(buf, buflen, &offset, &tmp)) + return R_FAILED; + + *((UCHAR *)data) = (tmp >> 24) & 0xff; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UCHAR = { + "UCHAR", + nr_stun_attr_codec_UCHAR_print, + nr_stun_attr_codec_UCHAR_encode, + nr_stun_attr_codec_UCHAR_decode +}; + +static int +nr_stun_attr_codec_UINT4_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UINT4*)data); + return 0; +} + +static int +nr_stun_attr_codec_UINT4_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset) + || nr_stun_encode_htonl(*(UINT4*)data , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UINT4_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + if (attrlen != sizeof(UINT4)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonl(buf, buflen, &offset, (UINT4*)data)) + return R_FAILED; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UINT4 = { + "UINT4", + nr_stun_attr_codec_UINT4_print, + nr_stun_attr_codec_UINT4_encode, + nr_stun_attr_codec_UINT4_decode +}; + +static int +nr_stun_attr_codec_UINT8_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %llu", msg, attr_info->name, *(UINT8*)data); + return 0; +} + +static int +nr_stun_attr_codec_UINT8_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT8) , buflen, buf, &offset) + || nr_stun_encode_htonll(*(UINT8*)data , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UINT8_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + if (attrlen != sizeof(UINT8)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonll(buf, buflen, &offset, (UINT8*)data)) + return R_FAILED; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UINT8 = { + "UINT8", + nr_stun_attr_codec_UINT8_print, + nr_stun_attr_codec_UINT8_encode, + nr_stun_attr_codec_UINT8_decode +}; + +static int +nr_stun_attr_codec_addr_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", msg, attr_info->name, ((nr_transport_addr*)data)->as_string); + return 0; +} + +static int +nr_stun_attr_codec_addr_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int r,_status; + int start = offset; + nr_transport_addr *addr = data; + UCHAR pad = '\0'; + UCHAR family; + + if ((r=nr_stun_encode_htons(attr_info->type, buflen, buf, &offset))) + ABORT(r); + + switch (addr->ip_version) { + case NR_IPV4: + family = NR_STUN_IPV4_FAMILY; + if (nr_stun_encode_htons(8 , buflen, buf, &offset) + || nr_stun_encode(&pad, 1 , buflen, buf, &offset) + || nr_stun_encode(&family, 1 , buflen, buf, &offset) + || nr_stun_encode_htons(ntohs(addr->u.addr4.sin_port), buflen, buf, &offset) + || nr_stun_encode_htonl(ntohl(addr->u.addr4.sin_addr.s_addr), buflen, buf, &offset)) + ABORT(R_FAILED); + break; + + case NR_IPV6: + family = NR_STUN_IPV6_FAMILY; + if (nr_stun_encode_htons(20 , buflen, buf, &offset) + || nr_stun_encode(&pad, 1 , buflen, buf, &offset) + || nr_stun_encode(&family, 1 , buflen, buf, &offset) + || nr_stun_encode_htons(ntohs(addr->u.addr6.sin6_port), buflen, buf, &offset) + || nr_stun_encode(addr->u.addr6.sin6_addr.s6_addr, 16, buflen, buf, &offset)) + ABORT(R_FAILED); + break; + + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + *attrlen = offset - start; + + _status = 0; + abort: + return _status; +} + +static int +nr_stun_attr_codec_addr_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + UCHAR pad; + UCHAR family; + UINT2 port; + UINT4 addr4; + struct in6_addr addr6; + nr_transport_addr *result = data; + + if (nr_stun_decode(1, buf, buflen, &offset, &pad) + || nr_stun_decode(1, buf, buflen, &offset, &family)) + ABORT(R_FAILED); + + switch (family) { + case NR_STUN_IPV4_FAMILY: + if (attrlen != 8) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode_htons(buf, buflen, &offset, &port) + || nr_stun_decode_htonl(buf, buflen, &offset, &addr4)) + ABORT(R_FAILED); + + if (nr_ip4_port_to_transport_addr(addr4, port, IPPROTO_UDP, result)) + ABORT(R_FAILED); + break; + + case NR_STUN_IPV6_FAMILY: + if (attrlen != 20) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode_htons(buf, buflen, &offset, &port) + || nr_stun_decode(16, buf, buflen, &offset, addr6.s6_addr)) + ABORT(R_FAILED); + + if (nr_ip6_port_to_transport_addr(&addr6, port, IPPROTO_UDP, result)) + ABORT(R_FAILED); + break; + + default: + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal address family: %d", family); + ABORT(R_FAILED); + break; + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_addr = { + "addr", + nr_stun_attr_codec_addr_print, + nr_stun_attr_codec_addr_encode, + nr_stun_attr_codec_addr_decode +}; + +static int +nr_stun_attr_codec_data_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_data *d = data; + r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)d->data, d->length); + return 0; +} + +static int +nr_stun_attr_codec_data_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + nr_stun_attr_data *d = data; + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(d->length , buflen, buf, &offset) + || nr_stun_encode(d->data, d->length , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_data_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + nr_stun_attr_data *result = data; + + /* -1 because it is going to be null terminated just to be safe */ + if (attrlen >= (sizeof(result->data) - 1)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too much data: %d bytes", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode(attrlen, buf, buflen, &offset, result->data)) + ABORT(R_FAILED); + + result->length = attrlen; + result->data[attrlen] = '\0'; /* just to be nice */ + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_data = { + "data", + nr_stun_attr_codec_data_print, + nr_stun_attr_codec_data_encode, + nr_stun_attr_codec_data_decode +}; + +static int +nr_stun_attr_codec_error_code_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_error_code *error_code = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %d %s", + msg, attr_info->name, error_code->number, + error_code->reason); + return 0; +} + +static int +nr_stun_attr_codec_error_code_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + nr_stun_attr_error_code *error_code = data; + int start = offset; + int length = strlen(error_code->reason); + UCHAR pad[2] = { 0 }; + UCHAR class = error_code->number / 100; + UCHAR number = error_code->number % 100; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(4 + length , buflen, buf, &offset) + || nr_stun_encode(pad, 2 , buflen, buf, &offset) + || nr_stun_encode(&class, 1 , buflen, buf, &offset) + || nr_stun_encode(&number, 1 , buflen, buf, &offset) + || nr_stun_encode((UCHAR*)error_code->reason, length, buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_error_code_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + nr_stun_attr_error_code *result = data; + UCHAR pad[2]; + UCHAR class; + UCHAR number; + size_t size_reason; + + if (nr_stun_decode(2, buf, buflen, &offset, pad) + || nr_stun_decode(1, buf, buflen, &offset, &class) + || nr_stun_decode(1, buf, buflen, &offset, &number)) + ABORT(R_FAILED); + + result->number = (class * 100) + number; + + size_reason = attrlen - 4; + + /* -1 because the string will be null terminated */ + if (size_reason > (sizeof(result->reason) - 1)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Reason is too large, truncating"); + /* don't fail, but instead truncate the reason */ + size_reason = sizeof(result->reason) - 1; + } + + if (nr_stun_decode(size_reason, buf, buflen, &offset, (UCHAR*)result->reason)) + ABORT(R_FAILED); + result->reason[size_reason] = '\0'; + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_error_code = { + "error_code", + nr_stun_attr_codec_error_code_print, + nr_stun_attr_codec_error_code_encode, + nr_stun_attr_codec_error_code_decode +}; + +static int +nr_stun_attr_codec_fingerprint_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_fingerprint *fingerprint = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %08x", msg, attr_info->name, fingerprint->checksum); + return 0; +} + +static int +nr_stun_attr_codec_fingerprint_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + UINT4 checksum; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + + /* the length must include the FINGERPRINT attribute when computing + * the fingerprint */ + header->length = ntohs(header->length); + header->length += 8; /* Fingerprint */ + header->length = htons(header->length); + + if (r_crc32((char*)buf, offset, &checksum)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint"); + return R_FAILED; + } + + fingerprint->checksum = checksum ^ 0x5354554e; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", fingerprint->checksum); + + fingerprint->valid = 1; + return nr_stun_attr_codec_UINT4.encode(attr_info, &fingerprint->checksum, offset, buflen, buf, attrlen); +} + +static int +nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int r,_status; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + size_t length; + UINT4 checksum; + + if ((r=nr_stun_attr_codec_UINT4.decode(attr_info, attrlen, buf, offset, buflen, &fingerprint->checksum))) + ABORT(r); + + offset -= 4; /* rewind to before the length and type fields */ + + /* the length must include the FINGERPRINT attribute when computing + * the fingerprint */ + length = offset; /* right before FINGERPRINT */ + length -= sizeof(*header); /* remove header length */ + length += 8; /* add length of Fingerprint */ + header->length = htons(length); + + /* make sure FINGERPRINT is final attribute in message */ + if (length + sizeof(*header) != buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Fingerprint is not final attribute in message"); + ABORT(R_FAILED); + } + + if (r_crc32((char*)buf, offset, &checksum)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint"); + ABORT(R_FAILED); + } + + fingerprint->valid = (fingerprint->checksum == (checksum ^ 0x5354554e)); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", (checksum ^ 0x5354554e)); + if (! fingerprint->valid) + r_log(NR_LOG_STUN, LOG_WARNING, "Invalid FINGERPRINT %08x", fingerprint->checksum); + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_fingerprint = { + "fingerprint", + nr_stun_attr_codec_fingerprint_print, + nr_stun_attr_codec_fingerprint_encode, + nr_stun_attr_codec_fingerprint_decode +}; + +static int +nr_stun_attr_codec_flag_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: on", msg, attr_info->name); + return 0; +} + +static int +nr_stun_attr_codec_flag_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(0 , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_flag_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + if (attrlen != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal flag length: %d", attrlen); + return R_FAILED; + } + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_flag = { + "flag", + nr_stun_attr_codec_flag_print, + nr_stun_attr_codec_flag_encode, + nr_stun_attr_codec_flag_decode +}; + +static int +nr_stun_attr_codec_message_integrity_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_message_integrity *integrity = data; + r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)integrity->hash, sizeof(integrity->hash)); + return 0; +} + +static int +nr_stun_compute_message_integrity(UCHAR *buf, int offset, UCHAR *password, int passwordlen, UCHAR *computedHMAC) +{ + int r,_status; + UINT2 hold; + UINT2 length; + nr_stun_message_header *header; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computing MESSAGE-INTEGRITY"); + + header = (nr_stun_message_header*)buf; + hold = header->length; + + /* adjust the length of the message */ + length = offset; + length -= sizeof(*header); + length += 24; /* for MESSAGE-INTEGRITY attribute */ + header->length = htons(length); + + if ((r=nr_crypto_hmac_sha1((UCHAR*)password, passwordlen, + buf, offset, computedHMAC))) + ABORT(r); + + r_dump(NR_LOG_STUN, LOG_DEBUG, "Computed MESSAGE-INTEGRITY ", (char*)computedHMAC, 20); + + _status=0; + abort: + header->length = hold; + return _status; +} + +static int +nr_stun_attr_codec_message_integrity_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + nr_stun_attr_message_integrity *integrity = data; + + if (nr_stun_compute_message_integrity(buf, offset, integrity->password, integrity->passwordlen, integrity->hash)) + return R_FAILED; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(integrity->hash) , buflen, buf, &offset) + || nr_stun_encode(integrity->hash, sizeof(integrity->hash) , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_message_integrity_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + int start; + nr_stun_attr_message_integrity *result = data; + UCHAR computedHMAC[20]; + + result->valid = 0; + + if (attrlen != 20) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s must be 20 bytes, not %d", attr_info->name, attrlen); + ABORT(R_FAILED); + } + + start = offset - 4; /* rewind to before the length and type fields */ + if (start < 0) + ABORT(R_INTERNAL); + + if (nr_stun_decode(attrlen, buf, buflen, &offset, result->hash)) + ABORT(R_FAILED); + + if (result->unknown_user) { + result->valid = 0; + } + else { + if (nr_stun_compute_message_integrity(buf, start, result->password, result->passwordlen, computedHMAC)) + ABORT(R_FAILED); + + assert(sizeof(computedHMAC) == sizeof(result->hash)); + + result->valid = (memcmp(computedHMAC, result->hash, 20) == 0); + } + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_message_integrity = { + "message_integrity", + nr_stun_attr_codec_message_integrity_print, + nr_stun_attr_codec_message_integrity_encode, + nr_stun_attr_codec_message_integrity_decode +}; + +static int +nr_stun_attr_codec_noop_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + return SKIP_ATTRIBUTE_DECODE; +} + +nr_stun_attr_codec nr_stun_attr_codec_noop = { + "NOOP", + 0, /* ignore, never print these attributes */ + 0, /* ignore, never encode these attributes */ + nr_stun_attr_codec_noop_decode +}; + +static int +nr_stun_attr_codec_quoted_string_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", + msg, attr_info->name, (char*)data); + return 0; +} + +static int +nr_stun_attr_codec_quoted_string_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ +//TODO: !nn! syntax check, conversion if not quoted already? +//We'll just restrict this in the API -- EKR + return nr_stun_attr_codec_string.encode(attr_info, data, offset, buflen, buf, attrlen); +} + +static int +nr_stun_attr_codec_quoted_string_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ +//TODO: !nn! I don't see any need to unquote this but we may +//find one later -- EKR + return nr_stun_attr_codec_string.decode(attr_info, attrlen, buf, offset, buflen, data); +} + +nr_stun_attr_codec nr_stun_attr_codec_quoted_string = { + "quoted_string", + nr_stun_attr_codec_quoted_string_print, + nr_stun_attr_codec_quoted_string_encode, + nr_stun_attr_codec_quoted_string_decode +}; + +static int +nr_stun_attr_codec_string_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", + msg, attr_info->name, (char*)data); + return 0; +} + +static int +nr_stun_attr_codec_string_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + char *str = data; + int length = strlen(str); + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(length , buflen, buf, &offset) + || nr_stun_encode((UCHAR*)str, length , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_string_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + char *result = data; + + /* actual enforcement of the specific string size happens elsewhere */ + if (attrlen >= NR_STUN_MAX_STRING_SIZE) { + r_log(NR_LOG_STUN, LOG_WARNING, "String is too large: %d bytes", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode(attrlen, buf, buflen, &offset, (UCHAR*)result)) + ABORT(R_FAILED); + result[attrlen] = '\0'; /* just to be nice */ + + if (strlen(result) != attrlen) { + /* stund 0.96 sends a final null in the Server attribute, so + * only error if the null appears anywhere else in a string */ + if (strlen(result) != attrlen-1) { + r_log(NR_LOG_STUN, LOG_WARNING, "Error in string: %zd/%d", strlen(result), attrlen); + ABORT(R_FAILED); + } + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_string = { + "string", + nr_stun_attr_codec_string_print, + nr_stun_attr_codec_string_encode, + nr_stun_attr_codec_string_decode +}; + +static int +nr_stun_attr_codec_unknown_attributes_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_unknown_attributes *unknown_attributes = data; + char type[9]; + char str[64 + (NR_STUN_MAX_UNKNOWN_ATTRIBUTES * sizeof(type))]; + int i; + + snprintf(str, sizeof(str), "%s %s:", msg, attr_info->name); + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + snprintf(type, sizeof(type), "%s 0x%04x", ((i>0)?",":""), unknown_attributes->attribute[i]); + strlcat(str, type, sizeof(str)); + } + + r_log(NR_LOG_STUN, LOG_DEBUG, "%s", str); + return 0; +} + +static int +nr_stun_attr_codec_unknown_attributes_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int _status; + int start = offset; + nr_stun_attr_unknown_attributes *unknown_attributes = data; + int length = (2 * unknown_attributes->num_attributes); + int i; + + if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes); + ABORT(R_FAILED); + } + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(length , buflen, buf, &offset)) + ABORT(R_FAILED); + + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + if (nr_stun_encode_htons(unknown_attributes->attribute[i], buflen, buf, &offset)) + ABORT(R_FAILED); + } + + *attrlen = offset - start; + + _status = 0; + abort: + return _status; +} + +static int +nr_stun_attr_codec_unknown_attributes_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + nr_stun_attr_unknown_attributes *unknown_attributes = data; + int i; + UINT2 *a; + + if ((attrlen % 4) != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attribute is illegal size: %d", attrlen); + ABORT(R_REJECTED); + } + + unknown_attributes->num_attributes = attrlen / 2; + + if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes); + ABORT(R_REJECTED); + } + + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + a = &(unknown_attributes->attribute[i]); + if (nr_stun_decode_htons(buf, buflen, &offset, a)) + return R_FAILED; + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_unknown_attributes = { + "unknown_attributes", + nr_stun_attr_codec_unknown_attributes_print, + nr_stun_attr_codec_unknown_attributes_encode, + nr_stun_attr_codec_unknown_attributes_decode +}; + +static int +nr_stun_attr_codec_xor_mapped_address_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s (unmasked) %s (masked)", + msg, attr_info->name, + xor_mapped_address->unmasked.as_string, + xor_mapped_address->masked.as_string); + return 0; +} + +static int +nr_stun_attr_codec_xor_mapped_address_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + UINT4 magic_cookie; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string); + + /* this needs to be the magic cookie in the header and not + * the MAGIC_COOKIE constant because if we're talking to + * older servers (that don't have a magic cookie) they use + * message ID for this */ + magic_cookie = ntohl(header->magic_cookie); + + nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->unmasked, &xor_mapped_address->masked); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string); + + if (nr_stun_attr_codec_addr.encode(attr_info, &xor_mapped_address->masked, offset, buflen, buf, attrlen)) + return R_FAILED; + + return 0; +} + +static int +nr_stun_attr_codec_xor_mapped_address_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int r,_status; + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + UINT4 magic_cookie; + + if ((r=nr_stun_attr_codec_addr.decode(attr_info, attrlen, buf, offset, buflen, &xor_mapped_address->masked))) + ABORT(r); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string); + + /* this needs to be the magic cookie in the header and not + * the MAGIC_COOKIE constant because if we're talking to + * older servers (that don't have a magic cookie) they use + * message ID for this */ + magic_cookie = ntohl(header->magic_cookie); + + nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->masked, &xor_mapped_address->unmasked); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string); + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_xor_mapped_address = { + "xor_mapped_address", + nr_stun_attr_codec_xor_mapped_address_print, + nr_stun_attr_codec_xor_mapped_address_encode, + nr_stun_attr_codec_xor_mapped_address_decode +}; + +nr_stun_attr_codec nr_stun_attr_codec_old_xor_mapped_address = { + "xor_mapped_address", + nr_stun_attr_codec_xor_mapped_address_print, + 0, /* never encode this type */ + nr_stun_attr_codec_xor_mapped_address_decode +}; + +nr_stun_attr_codec nr_stun_attr_codec_xor_peer_address = { + "xor_peer_address", + nr_stun_attr_codec_xor_mapped_address_print, + nr_stun_attr_codec_xor_mapped_address_encode, + nr_stun_attr_codec_xor_mapped_address_decode +}; + +#define NR_ADD_STUN_ATTRIBUTE(type, name, codec, illegal) \ + { (type), (name), &(codec), illegal }, + +#define NR_ADD_STUN_ATTRIBUTE_IGNORE(type, name) \ + { (type), (name), &nr_stun_attr_codec_noop, 0 }, + + +static nr_stun_attr_info attrs[] = { + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ALTERNATE_SERVER, "ALTERNATE-SERVER", nr_stun_attr_codec_addr, 0) +#ifdef USE_STUND_0_96 + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_CHANGE_REQUEST, "CHANGE-REQUEST", nr_stun_attr_codec_UINT4, 0) +#endif + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ERROR_CODE, "ERROR-CODE", nr_stun_attr_codec_error_code, nr_stun_attr_error_code_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_FINGERPRINT, "FINGERPRINT", nr_stun_attr_codec_fingerprint, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MAPPED_ADDRESS, "MAPPED-ADDRESS", nr_stun_attr_codec_addr, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MESSAGE_INTEGRITY, "MESSAGE-INTEGRITY", nr_stun_attr_codec_message_integrity, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_NONCE, "NONCE", nr_stun_attr_codec_quoted_string, nr_stun_attr_nonce_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REALM, "REALM", nr_stun_attr_codec_quoted_string, nr_stun_attr_realm_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_SERVER, "SERVER", nr_stun_attr_codec_string, nr_stun_attr_server_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_UNKNOWN_ATTRIBUTES, "UNKNOWN-ATTRIBUTES", nr_stun_attr_codec_unknown_attributes, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USERNAME, "USERNAME", nr_stun_attr_codec_string, nr_stun_attr_username_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_MAPPED_ADDRESS, "XOR-MAPPED-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0) + +#ifdef USE_ICE + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLED, "ICE-CONTROLLED", nr_stun_attr_codec_UINT8, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLING, "ICE-CONTROLLING", nr_stun_attr_codec_UINT8, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_PRIORITY, "PRIORITY", nr_stun_attr_codec_UINT4, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USE_CANDIDATE, "USE-CANDIDATE", nr_stun_attr_codec_flag, 0) +#endif + +#ifdef USE_TURN + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_DATA, "DATA", nr_stun_attr_codec_data, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_LIFETIME, "LIFETIME", nr_stun_attr_codec_UINT4, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_RELAY_ADDRESS, "XOR-RELAY-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_PEER_ADDRESS, "XOR-PEER-ADDRESS", nr_stun_attr_codec_xor_peer_address, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REQUESTED_TRANSPORT, "REQUESTED-TRANSPORT", nr_stun_attr_codec_UCHAR, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_BANDWIDTH, "BANDWIDTH", nr_stun_attr_codec_UINT4, 0) +#endif /* USE_TURN */ + + /* for backwards compatibilty */ + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS, "Old XOR-MAPPED-ADDRESS", nr_stun_attr_codec_old_xor_mapped_address, 0) +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_RESPONSE_ADDRESS, "RESPONSE-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_SOURCE_ADDRESS, "SOURCE-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_CHANGED_ADDRESS, "CHANGED-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_PASSWORD, "PASSWORD") +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ +}; + + +int +nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info) +{ + int _status; + size_t i; + + *info = 0; + for (i = 0; i < sizeof(attrs)/sizeof(*attrs); ++i) { + if (type == attrs[i].type) { + *info = &attrs[i]; + break; + } + } + + if (*info == 0) + ABORT(R_NOT_FOUND); + + _status=0; + abort: + return(_status); +} + +int +nr_stun_fix_attribute_ordering(nr_stun_message *msg) +{ + nr_stun_message_attribute *message_integrity; + nr_stun_message_attribute *fingerprint; + + /* 2nd to the last */ + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &message_integrity)) { + TAILQ_REMOVE(&msg->attributes, message_integrity, entry); + TAILQ_INSERT_TAIL(&msg->attributes, message_integrity, entry); + } + + /* last */ + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &fingerprint)) { + TAILQ_REMOVE(&msg->attributes, fingerprint, entry); + TAILQ_INSERT_TAIL(&msg->attributes, fingerprint, entry); + } + + return 0; +} + +// Since this sanity check is only a collection of assert statements and those +// assert statements are compiled out in non-debug builds, undef SANITY_CHECKS +// so we can avoid the warning that padding_bytes is never used in opt builds. +#ifdef NDEBUG +#undef SANITY_CHECKS +#endif + +#ifdef SANITY_CHECKS +static void sanity_check_encoding_stuff(nr_stun_message *msg) +{ + nr_stun_message_attribute *attr = 0; + int padding_bytes; + int l; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Starting to sanity check encoding"); + + l = 0; + TAILQ_FOREACH(attr, &msg->attributes, entry) { + padding_bytes = 0; + if ((attr->length % 4) != 0) { + padding_bytes = 4 - (attr->length % 4); + } + assert(attr->length == (attr->encoding_length - (4 + padding_bytes))); + assert(((void*)attr->encoding) == (msg->buffer + 20 + l)); + l += attr->encoding_length; + assert((l % 4) == 0); + } + assert(l == msg->header.length); +} +#endif /* SANITY_CHECKS */ + + +int +nr_stun_encode_message(nr_stun_message *msg) +{ + int r,_status; + size_t length_offset; + size_t length_offset_hold; + nr_stun_attr_info *attr_info; + nr_stun_message_attribute *attr; + int padding_bytes; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoding STUN message"); + + nr_stun_fix_attribute_ordering(msg); + + msg->name = nr_stun_msg_type(msg->header.type); + msg->length = 0; + msg->header.length = 0; + + if ((r=nr_stun_encode_htons(msg->header.type, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + if (msg->name) + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: %s", msg->name); + else + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: 0x%03x", msg->header.type); + + /* grab the offset to be used later to re-write the header length field */ + length_offset_hold = msg->length; + + if ((r=nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + + if ((r=nr_stun_encode_htonl(msg->header.magic_cookie, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Cookie: %08x", msg->header.magic_cookie); + + if ((r=nr_stun_encode((UCHAR*)(&msg->header.id), sizeof(msg->header.id), sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + r_dump(NR_LOG_STUN, LOG_DEBUG, "Encoded ID", (void*)&msg->header.id, sizeof(msg->header.id)); + + TAILQ_FOREACH(attr, &msg->attributes, entry) { + if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unrecognized attribute: 0x%04x", attr->type); + ABORT(R_INTERNAL); + } + + attr->name = attr_info->name; + attr->type_name = attr_info->codec->name; + attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[msg->length]; + + if (attr_info->codec->encode != 0) { + if ((r=attr_info->codec->encode(attr_info, &attr->u, msg->length, sizeof(msg->buffer), msg->buffer, &attr->encoding_length))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to encode %s", attr_info->name); + ABORT(r); + } + + msg->length += attr->encoding_length; + attr->length = attr->encoding_length - 4; /* -4 for type and length fields */ + + if (attr_info->illegal) { + if ((r=attr_info->illegal(attr_info, attr->length, &attr->u))) + ABORT(r); + } + + attr_info->codec->print(attr_info, "Encoded", &attr->u); + + if ((attr->length % 4) == 0) { + padding_bytes = 0; + } + else { + padding_bytes = 4 - (attr->length % 4); + nr_stun_encode((UCHAR*)"\0\0\0\0", padding_bytes, sizeof(msg->buffer), msg->buffer, &msg->length); + attr->encoding_length += padding_bytes; + } + + msg->header.length += attr->encoding_length; + length_offset = length_offset_hold; + (void)nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &length_offset); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing encode function for attribute: %s", attr_info->name); + } + } + + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Length: %d", msg->header.length); + + assert(msg->length < NR_STUN_MAX_MESSAGE_SIZE); + +#ifdef SANITY_CHECKS + sanity_check_encoding_stuff(msg); +#endif /* SANITY_CHECKS */ + + _status=0; +abort: + return _status; +} + +int +nr_stun_decode_message(nr_stun_message *msg, int (*get_password)(void *arg, nr_stun_message *msg, Data **password), void *arg) +{ + int r,_status; + int offset; + int size; + int padding_bytes; + nr_stun_message_attribute *attr; + nr_stun_attr_info *attr_info; + Data *password; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsing STUN message of %d bytes", msg->length); + + if (!TAILQ_EMPTY(&msg->attributes)) + ABORT(R_BAD_ARGS); + + if (sizeof(nr_stun_message_header) > msg->length) { + r_log(NR_LOG_STUN, LOG_WARNING, "Message too small"); + ABORT(R_FAILED); + } + + memcpy(&msg->header, msg->buffer, sizeof(msg->header)); + msg->header.type = ntohs(msg->header.type); + msg->header.length = ntohs(msg->header.length); + msg->header.magic_cookie = ntohl(msg->header.magic_cookie); + + msg->name = nr_stun_msg_type(msg->header.type); + + if (msg->name) + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: %s", msg->name); + else + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: 0x%03x", msg->header.type); + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Length: %d", msg->header.length); + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Cookie: %08x", msg->header.magic_cookie); + r_dump(NR_LOG_STUN, LOG_DEBUG, "Parsed ID", (void*)&msg->header.id, sizeof(msg->header.id)); + + if (msg->header.length + sizeof(msg->header) != msg->length) { + r_log(NR_LOG_STUN, LOG_WARNING, "Inconsistent message header length: %d/%d", + msg->header.length, msg->length); + ABORT(R_FAILED); + } + + size = msg->header.length; + + if ((size % 4) != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message size: %d", msg->header.length); + ABORT(R_FAILED); + } + + offset = sizeof(msg->header); + + while (size > 0) { + r_log(NR_LOG_STUN, LOG_DEBUG, "size = %d", size); + + if (size < 4) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message length: %d", size); + ABORT(R_FAILED); + } + + if ((r=nr_stun_message_attribute_create(msg, &attr))) + ABORT(R_NO_MEMORY); + + attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[offset]; + attr->type = ntohs(attr->encoding->type); + attr->length = ntohs(attr->encoding->length); + attr->encoding_length = attr->length + 4; + + if ((attr->length % 4) != 0) { + padding_bytes = 4 - (attr->length % 4); + attr->encoding_length += padding_bytes; + } + + if ((attr->encoding_length) > (size_t)size) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attribute length larger than remaining message size: %d/%d", attr->encoding_length, size); + ABORT(R_FAILED); + } + + if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) { + if (attr->type <= 0x7FFF) + ++msg->comprehension_required_unknown_attributes; + else + ++msg->comprehension_optional_unknown_attributes; + r_log(NR_LOG_STUN, LOG_INFO, "Unrecognized attribute: 0x%04x", attr->type); + } + else { + attr_info->name = attr_info->name; + attr->type_name = attr_info->codec->name; + + if (attr->type == NR_STUN_ATTR_MESSAGE_INTEGRITY) { + if (get_password && get_password(arg, msg, &password) == 0) { + if (password->len > sizeof(attr->u.message_integrity.password)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Password too long: %d bytes", password->len); + ABORT(R_FAILED); + } + + memcpy(attr->u.message_integrity.password, password->data, password->len); + attr->u.message_integrity.passwordlen = password->len; + } + else { + /* set to user "not found" */ + attr->u.message_integrity.unknown_user = 1; + } + } + else if (attr->type == NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS) { + attr->type = NR_STUN_ATTR_XOR_MAPPED_ADDRESS; + r_log(NR_LOG_STUN, LOG_INFO, "Translating obsolete XOR-MAPPED-ADDRESS type"); + } + + if ((r=attr_info->codec->decode(attr_info, attr->length, msg->buffer, offset+4, msg->length, &attr->u))) { + if (r == SKIP_ATTRIBUTE_DECODE) { + r_log(NR_LOG_STUN, LOG_INFO, "Skipping %s", attr_info->name); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to parse %s", attr_info->name); + } + + attr->invalid = 1; + } + else { + attr_info->codec->print(attr_info, "Parsed", &attr->u); + +#ifdef USE_STUN_PEDANTIC + r_log(NR_LOG_STUN, LOG_DEBUG, "Before pedantic attr_info checks"); + if (attr_info->illegal) { + if ((r=attr_info->illegal(attr_info, attr->length, &attr->u))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Failed pedantic attr_info checks"); + ABORT(r); + } + } + r_log(NR_LOG_STUN, LOG_DEBUG, "After pedantic attr_info checks"); +#endif /* USE_STUN_PEDANTIC */ + } + } + + offset += attr->encoding_length; + size -= attr->encoding_length; + } + +#ifdef SANITY_CHECKS + sanity_check_encoding_stuff(msg); +#endif /* SANITY_CHECKS */ + + _status=0; + abort: + return _status; +} + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h new file mode 100644 index 0000000000..4e4ff60e0c --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h @@ -0,0 +1,78 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _stun_codec_h +#define _stun_codec_h + +#include "stun_msg.h" + +typedef struct nr_stun_attr_info_ nr_stun_attr_info; + +typedef struct nr_stun_attr_codec_ { + char *name; + int (*print)(nr_stun_attr_info *attr_info, char *msg, void *data); + int (*encode)(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen); + int (*decode)(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data); +} nr_stun_attr_codec; + +struct nr_stun_attr_info_ { + UINT2 type; + char *name; + nr_stun_attr_codec *codec; + int (*illegal)(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +}; + +extern nr_stun_attr_codec nr_stun_attr_codec_UINT4; +extern nr_stun_attr_codec nr_stun_attr_codec_UINT8; +extern nr_stun_attr_codec nr_stun_attr_codec_addr; +extern nr_stun_attr_codec nr_stun_attr_codec_bytes; +extern nr_stun_attr_codec nr_stun_attr_codec_data; +extern nr_stun_attr_codec nr_stun_attr_codec_error_code; +extern nr_stun_attr_codec nr_stun_attr_codec_fingerprint; +extern nr_stun_attr_codec nr_stun_attr_codec_flag; +extern nr_stun_attr_codec nr_stun_attr_codec_message_integrity; +extern nr_stun_attr_codec nr_stun_attr_codec_noop; +extern nr_stun_attr_codec nr_stun_attr_codec_quoted_string; +extern nr_stun_attr_codec nr_stun_attr_codec_string; +extern nr_stun_attr_codec nr_stun_attr_codec_unknown_attributes; +extern nr_stun_attr_codec nr_stun_attr_codec_xor_mapped_address; +extern nr_stun_attr_codec nr_stun_attr_codec_xor_peer_address; +extern nr_stun_attr_codec nr_stun_attr_codec_old_xor_mapped_address; + +size_t nr_count_utf8_code_points_without_validation(const char *s); +int nr_stun_encode_message(nr_stun_message *msg); +int nr_stun_decode_message(nr_stun_message *msg, int (*get_password)(void *arg, nr_stun_message *msg, Data **password), void *arg); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c new file mode 100644 index 0000000000..8f118e8942 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c @@ -0,0 +1,245 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> + +#include "stun.h" + + +/* returns 0 if it's not a STUN message + * 1 if it's likely to be a STUN message + * 2 if it's super likely to be a STUN message + * 3 if it really is a STUN message */ +int +nr_is_stun_message(UCHAR *buf, size_t len) +{ + const UINT4 cookie = htonl(NR_STUN_MAGIC_COOKIE); + const UINT4 cookie2 = htonl(NR_STUN_MAGIC_COOKIE2); +#if 0 + nr_stun_message msg; +#endif + UINT2 type; + nr_stun_encoded_attribute* attr; + unsigned int attrLen; + int atrType; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if ((buf[0] & (0x80|0x40)) != 0) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + switch (type) { + case NR_STUN_MSG_BINDING_REQUEST: + case NR_STUN_MSG_BINDING_INDICATION: + case NR_STUN_MSG_BINDING_RESPONSE: + case NR_STUN_MSG_BINDING_ERROR_RESPONSE: + +#ifdef USE_TURN + case NR_STUN_MSG_ALLOCATE_REQUEST: + case NR_STUN_MSG_ALLOCATE_RESPONSE: + case NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE: + case NR_STUN_MSG_REFRESH_REQUEST: + case NR_STUN_MSG_REFRESH_RESPONSE: + case NR_STUN_MSG_REFRESH_ERROR_RESPONSE: + case NR_STUN_MSG_PERMISSION_REQUEST: + case NR_STUN_MSG_PERMISSION_RESPONSE: + case NR_STUN_MSG_PERMISSION_ERROR_RESPONSE: + case NR_STUN_MSG_CHANNEL_BIND_REQUEST: + case NR_STUN_MSG_CHANNEL_BIND_RESPONSE: + case NR_STUN_MSG_CHANNEL_BIND_ERROR_RESPONSE: + case NR_STUN_MSG_SEND_INDICATION: + case NR_STUN_MSG_DATA_INDICATION: +#ifdef NR_STUN_MSG_CONNECT_REQUEST + case NR_STUN_MSG_CONNECT_REQUEST: +#endif +#ifdef NR_STUN_MSG_CONNECT_RESPONSE + case NR_STUN_MSG_CONNECT_RESPONSE: +#endif +#ifdef NR_STUN_MSG_CONNECT_ERROR_RESPONSE + case NR_STUN_MSG_CONNECT_ERROR_RESPONSE: +#endif +#ifdef NR_STUN_MSG_CONNECT_STATUS_INDICATION + case NR_STUN_MSG_CONNECT_STATUS_INDICATION: +#endif +#endif /* USE_TURN */ + + /* ok so far, continue */ + break; + default: + return 0; + break; + } + + if (!memcmp(&cookie2, &buf[4], sizeof(UINT4))) { + /* return here because if it's an old-style message then there will + * not be a fingerprint in the message */ + return 1; + } + + if (memcmp(&cookie, &buf[4], sizeof(UINT4))) + return 0; + + /* the magic cookie was right, so it's pretty darn likely that what we've + * got here is a STUN message */ + + attr = (nr_stun_encoded_attribute*)(buf + (len - 8)); + attrLen = ntohs(attr->length); + atrType = ntohs(attr->type); + + if (atrType != NR_STUN_ATTR_FINGERPRINT || attrLen != 4) + return 1; + + /* the fingerprint is in the right place and looks sane, so we can be quite + * sure we've got a STUN message */ + +#if 0 +/* nevermind this check ... there's a reasonable chance that a NAT has modified + * the message (and thus the fingerprint check will fail), but it's still an + * otherwise-perfectly-good STUN message, so skip the check since we're going + * to return "true" whether the check succeeds or fails */ + + if (nr_stun_parse_attr_UINT4(buf + (len - 4), attrLen, &msg.fingerprint)) + return 2; + + + if (nr_stun_compute_fingerprint(buf, len - 8, &computedFingerprint)) + return 2; + + if (msg.fingerprint.number != computedFingerprint) + return 2; + + /* and the fingerprint is good, so it's gotta be a STUN message */ +#endif + + return 3; +} + +int +nr_is_stun_request_message(UCHAR *buf, size_t len) +{ + UINT2 type; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (!nr_is_stun_message(buf, len)) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_REQUEST; +} + +int +nr_is_stun_indication_message(UCHAR *buf, size_t len) +{ + UINT2 type; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (!nr_is_stun_message(buf, len)) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_INDICATION; +} + +int +nr_is_stun_response_message(UCHAR *buf, size_t len) +{ + UINT2 type; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (!nr_is_stun_message(buf, len)) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_RESPONSE + || NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_ERROR_RESPONSE; +} + +int +nr_has_stun_cookie(UCHAR *buf, size_t len) +{ + static UINT4 cookie; + + cookie = htonl(NR_STUN_MAGIC_COOKIE); + + if (sizeof(nr_stun_message_header) > 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); +} + + + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h new file mode 100644 index 0000000000..c2badc1d2b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h @@ -0,0 +1,44 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + +#ifndef _stun_hint_h +#define _stun_hint_h + +int nr_is_stun_message(UCHAR *buf, size_t len); +int nr_is_stun_request_message(UCHAR *buf, size_t len); +int nr_is_stun_response_message(UCHAR *buf, size_t len); +int nr_is_stun_indication_message(UCHAR *buf, size_t len); +int nr_has_stun_cookie(UCHAR *buf, size_t len); +int nr_stun_message_length(UCHAR *buf, int len, int *length); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c new file mode 100644 index 0000000000..7e01686109 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c @@ -0,0 +1,364 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> + +#include "stun.h" + + +int +nr_stun_message_create(nr_stun_message **msg) +{ + int _status; + nr_stun_message *m = 0; + + m = RCALLOC(sizeof(*m)); + if (!m) + ABORT(R_NO_MEMORY); + + TAILQ_INIT(&m->attributes); + + *msg = m; + + _status=0; + abort: + return(_status); +} + +int +nr_stun_message_create2(nr_stun_message **msg, UCHAR *buffer, size_t length) +{ + int r,_status; + nr_stun_message *m = 0; + + if (length > sizeof(m->buffer)) { + ABORT(R_BAD_DATA); + } + + if ((r=nr_stun_message_create(&m))) + ABORT(r); + + memcpy(m->buffer, buffer, length); + m->length = length; + + *msg = m; + + _status=0; + abort: + return(_status); +} + +int +nr_stun_message_destroy(nr_stun_message **msg) +{ + int _status; + nr_stun_message_attribute_head *attrs; + nr_stun_message_attribute *attr; + + if (msg && *msg) { + attrs = &(*msg)->attributes; + while (!TAILQ_EMPTY(attrs)) { + attr = TAILQ_FIRST(attrs); + nr_stun_message_attribute_destroy(*msg, &attr); + } + + RFREE(*msg); + + *msg = 0; + } + + _status=0; +/* abort: */ + return(_status); +} + +int +nr_stun_message_attribute_create(nr_stun_message *msg, nr_stun_message_attribute **attr) +{ + int _status; + nr_stun_message_attribute *a = 0; + + a = RCALLOC(sizeof(*a)); + if (!a) + ABORT(R_NO_MEMORY); + + TAILQ_INSERT_TAIL(&msg->attributes, a, entry); + + *attr = a; + + _status=0; + abort: + return(_status); +} + +int +nr_stun_message_attribute_destroy(nr_stun_message *msg, nr_stun_message_attribute **attr) +{ + int _status; + nr_stun_message_attribute *a = 0; + + if (attr && *attr) { + a = *attr; + TAILQ_REMOVE(&msg->attributes, a, entry); + + RFREE(a); + + *attr = 0; + } + + _status=0; +/* abort: */ + return(_status); +} + +int +nr_stun_message_has_attribute(nr_stun_message *msg, UINT2 type, nr_stun_message_attribute **attribute) +{ + nr_stun_message_attribute *attr = 0; + nr_stun_message_get_attribute(msg, type, 0, &attr); + + if (attribute) + *attribute = attr; + + return attr ? 1 : 0; +} + +int +nr_stun_message_get_attribute(nr_stun_message *msg, UINT2 type, UINT2 index, nr_stun_message_attribute **attribute) +{ + nr_stun_message_attribute *attr; + TAILQ_FOREACH(attr, &msg->attributes, entry) { + if (attr->type == type && !attr->invalid) { + if (!index) { + *attribute = attr; + return 0; + } + --index; + } + } + *attribute = 0; + return R_NOT_FOUND; +} + +#define NR_STUN_MESSAGE_ADD_ATTRIBUTE(__type, __code) \ + { \ + int r,_status; \ + nr_stun_message_attribute *attr = 0; \ + if ((r=nr_stun_message_attribute_create(msg, &attr))) \ + ABORT(r); \ + attr->type = (__type); \ + { __code } \ + _status=0; \ + abort: \ + if (_status){ \ + nr_stun_message_attribute_destroy(msg, &attr); \ + } \ + return(_status); \ + } + + +int +nr_stun_message_add_alternate_server_attribute(nr_stun_message *msg, nr_transport_addr *alternate_server) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ALTERNATE_SERVER, + { + if ((r=nr_transport_addr_copy(&attr->u.alternate_server, alternate_server))) + ABORT(r); + } +) + +int +nr_stun_message_add_error_code_attribute(nr_stun_message *msg, UINT2 number, char *reason) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ERROR_CODE, + { + attr->u.error_code.number = number; + (void)strlcpy(attr->u.error_code.reason, reason, sizeof(attr->u.error_code.reason)); + } +) + +int +nr_stun_message_add_fingerprint_attribute(nr_stun_message *msg) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_FINGERPRINT, + {} +) + +int +nr_stun_message_add_message_integrity_attribute(nr_stun_message *msg, Data *password) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_MESSAGE_INTEGRITY, + { + if (sizeof(attr->u.message_integrity.password) < password->len) + ABORT(R_BAD_DATA); + + memcpy(attr->u.message_integrity.password, password->data, password->len); + attr->u.message_integrity.passwordlen = password->len; + } +) + +int +nr_stun_message_add_nonce_attribute(nr_stun_message *msg, char *nonce) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_NONCE, + { (void)strlcpy(attr->u.nonce, nonce, sizeof(attr->u.nonce)); } +) + +int +nr_stun_message_add_realm_attribute(nr_stun_message *msg, char *realm) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_REALM, + { (void)strlcpy(attr->u.realm, realm, sizeof(attr->u.realm)); } +) + +int +nr_stun_message_add_server_attribute(nr_stun_message *msg, char *server_name) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_SERVER, + { (void)strlcpy(attr->u.server_name, server_name, sizeof(attr->u.server_name)); } +) + +int +nr_stun_message_add_unknown_attributes_attribute(nr_stun_message *msg, nr_stun_attr_unknown_attributes *unknown_attributes) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_UNKNOWN_ATTRIBUTES, + { memcpy(&attr->u.unknown_attributes, unknown_attributes, sizeof(attr->u.unknown_attributes)); } +) + +int +nr_stun_message_add_username_attribute(nr_stun_message *msg, char *username) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_USERNAME, + { (void)strlcpy(attr->u.username, username, sizeof(attr->u.username)); } +) + +int +nr_stun_message_add_requested_transport_attribute(nr_stun_message *msg, UCHAR protocol) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_REQUESTED_TRANSPORT, + { attr->u.requested_transport = protocol; } +) + +int +nr_stun_message_add_xor_mapped_address_attribute(nr_stun_message *msg, nr_transport_addr *mapped_address) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_XOR_MAPPED_ADDRESS, + { + if ((r=nr_transport_addr_copy(&attr->u.xor_mapped_address.unmasked, mapped_address))) + ABORT(r); + } +) + +int +nr_stun_message_add_xor_peer_address_attribute(nr_stun_message *msg, nr_transport_addr *peer_address) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_XOR_PEER_ADDRESS, + { + if ((r=nr_transport_addr_copy(&attr->u.xor_mapped_address.unmasked, peer_address))) + ABORT(r); + } +) + +#ifdef USE_ICE +int +nr_stun_message_add_ice_controlled_attribute(nr_stun_message *msg, UINT8 ice_controlled) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ICE_CONTROLLED, + { attr->u.ice_controlled = ice_controlled; } +) + +int +nr_stun_message_add_ice_controlling_attribute(nr_stun_message *msg, UINT8 ice_controlling) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ICE_CONTROLLING, + { attr->u.ice_controlling = ice_controlling; } +) + +int +nr_stun_message_add_priority_attribute(nr_stun_message *msg, UINT4 priority) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_PRIORITY, + { attr->u.priority = priority; } +) + +int +nr_stun_message_add_use_candidate_attribute(nr_stun_message *msg) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_USE_CANDIDATE, + {} +) +#endif /* USE_ICE */ + +#ifdef USE_TURN +int +nr_stun_message_add_data_attribute(nr_stun_message *msg, UCHAR *data, int length) + +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_DATA, + { + if (length > NR_STUN_MAX_MESSAGE_SIZE) + ABORT(R_BAD_ARGS); + + memcpy(attr->u.data.data, data, length); + attr->u.data.length=length; + } +) + +int +nr_stun_message_add_lifetime_attribute(nr_stun_message *msg, UINT4 lifetime_secs) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_LIFETIME, + { attr->u.lifetime_secs = lifetime_secs; } +) + +#endif /* USE_TURN */ + +#ifdef USE_STUND_0_96 +int +nr_stun_message_add_change_request_attribute(nr_stun_message *msg, UINT4 change_request) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_OLD_CHANGE_REQUEST, + { attr->u.change_request = change_request; } +) +#endif /* USE_STUND_0_96 */ + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h new file mode 100644 index 0000000000..ffd68d3eee --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h @@ -0,0 +1,208 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _stun_msg_h +#define _stun_msg_h + +#include "csi_platform.h" +#include "nr_api.h" +#include "transport_addr.h" + +#define NR_STUN_MAX_USERNAME_BYTES 513 +#define NR_STUN_MAX_ERROR_CODE_REASON_BYTES 763 +#define NR_STUN_MAX_ERROR_CODE_REASON_CHARS 128 +#define NR_STUN_MAX_REALM_BYTES 763 +#define NR_STUN_MAX_REALM_CHARS 128 +#define NR_STUN_MAX_NONCE_BYTES 763 +#define NR_STUN_MAX_NONCE_CHARS 128 +#define NR_STUN_MAX_SERVER_BYTES 763 +#define NR_STUN_MAX_SERVER_CHARS 128 +#define NR_STUN_MAX_STRING_SIZE 763 /* any possible string */ +#define NR_STUN_MAX_UNKNOWN_ATTRIBUTES 16 +#define NR_STUN_MAX_MESSAGE_SIZE 2048 + +#define NR_STUN_MAGIC_COOKIE 0x2112A442 +#define NR_STUN_MAGIC_COOKIE2 0xc5cb4e1d /* used recognize old stun messages */ + +typedef struct { UCHAR octet[12]; } UINT12; + +typedef struct nr_stun_attr_error_code_ { + UINT2 number; + char reason[NR_STUN_MAX_ERROR_CODE_REASON_BYTES+1]; /* +1 for \0 */ +} nr_stun_attr_error_code; + +typedef struct nr_stun_attr_fingerprint_ { + UINT4 checksum; + int valid; +} nr_stun_attr_fingerprint; + +typedef struct nr_stun_attr_message_integrity_ { + UCHAR hash[20]; + int unknown_user; + UCHAR password[1024]; + int passwordlen; + int valid; +} nr_stun_attr_message_integrity; + +typedef struct nr_stun_attr_unknown_attributes_ { + UINT2 attribute[NR_STUN_MAX_UNKNOWN_ATTRIBUTES]; + int num_attributes; +} nr_stun_attr_unknown_attributes; + +typedef struct nr_stun_attr_xor_mapped_address_ { + nr_transport_addr masked; + nr_transport_addr unmasked; +} nr_stun_attr_xor_mapped_address; + +typedef struct nr_stun_attr_data_ { + UCHAR data[NR_STUN_MAX_MESSAGE_SIZE]; + size_t length; +} nr_stun_attr_data; + + +typedef struct nr_stun_encoded_attribute_ { + UINT2 type; + UINT2 length; + UCHAR value[NR_STUN_MAX_MESSAGE_SIZE]; +} nr_stun_encoded_attribute; + +typedef struct nr_stun_message_attribute_ { + UINT2 type; + UINT2 length; + union { + nr_transport_addr address; + nr_transport_addr alternate_server; + nr_stun_attr_error_code error_code; + nr_stun_attr_fingerprint fingerprint; + nr_transport_addr mapped_address; + nr_stun_attr_message_integrity message_integrity; + char nonce[NR_STUN_MAX_NONCE_BYTES+1]; /* +1 for \0 */ + char realm[NR_STUN_MAX_REALM_BYTES+1]; /* +1 for \0 */ + nr_stun_attr_xor_mapped_address relay_address; + char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */ + nr_stun_attr_unknown_attributes unknown_attributes; + char username[NR_STUN_MAX_USERNAME_BYTES+1]; /* +1 for \0 */ + nr_stun_attr_xor_mapped_address xor_mapped_address; + +#ifdef USE_ICE + UINT4 priority; + UINT8 ice_controlled; + UINT8 ice_controlling; +#endif /* USE_ICE */ + +#ifdef USE_TURN + UINT4 lifetime_secs; + nr_transport_addr remote_address; + UCHAR requested_transport; + nr_stun_attr_data data; +#endif /* USE_TURN */ + +#ifdef USE_STUND_0_96 + UINT4 change_request; +#endif /* USE_STUND_0_96 */ + + /* make sure there's enough room here to place any possible + * attribute */ + UCHAR largest_possible_attribute[NR_STUN_MAX_MESSAGE_SIZE]; + } u; + nr_stun_encoded_attribute *encoding; + size_t encoding_length; + char *name; + char *type_name; + int invalid; + TAILQ_ENTRY(nr_stun_message_attribute_) entry; +} nr_stun_message_attribute; + +typedef TAILQ_HEAD(nr_stun_message_attribute_head_,nr_stun_message_attribute_) nr_stun_message_attribute_head; + +typedef struct nr_stun_message_header_ { + UINT2 type; + UINT2 length; + UINT4 magic_cookie; + UINT12 id; +} nr_stun_message_header; + +typedef struct nr_stun_message_ { + char *name; + UCHAR buffer[NR_STUN_MAX_MESSAGE_SIZE]; + size_t length; + nr_stun_message_header header; + int comprehension_required_unknown_attributes; + int comprehension_optional_unknown_attributes; + nr_stun_message_attribute_head attributes; +} nr_stun_message; + +int nr_stun_message_create(nr_stun_message **msg); +int nr_stun_message_create2(nr_stun_message **msg, UCHAR *buffer, size_t length); +int nr_stun_message_destroy(nr_stun_message **msg); + +int nr_stun_message_attribute_create(nr_stun_message *msg, nr_stun_message_attribute **attr); +int nr_stun_message_attribute_destroy(nr_stun_message *msg, nr_stun_message_attribute **attr); + +int nr_stun_message_has_attribute(nr_stun_message *msg, UINT2 type, nr_stun_message_attribute **attribute); + +int nr_stun_message_get_attribute(nr_stun_message *msg, UINT2 type, UINT2 index, nr_stun_message_attribute **attribute); + +int nr_stun_message_add_alternate_server_attribute(nr_stun_message *msg, nr_transport_addr *alternate_server); +int nr_stun_message_add_error_code_attribute(nr_stun_message *msg, UINT2 number, char *reason); +int nr_stun_message_add_fingerprint_attribute(nr_stun_message *msg); +int nr_stun_message_add_message_integrity_attribute(nr_stun_message *msg, Data *password); +int nr_stun_message_add_nonce_attribute(nr_stun_message *msg, char *nonce); +int nr_stun_message_add_realm_attribute(nr_stun_message *msg, char *realm); +int nr_stun_message_add_server_attribute(nr_stun_message *msg, char *server_name); +int nr_stun_message_add_unknown_attributes_attribute(nr_stun_message *msg, nr_stun_attr_unknown_attributes *unknown_attributes); +int nr_stun_message_add_username_attribute(nr_stun_message *msg, char *username); +int nr_stun_message_add_xor_mapped_address_attribute(nr_stun_message *msg, nr_transport_addr *mapped_address); + +#ifdef USE_ICE +int nr_stun_message_add_ice_controlled_attribute(nr_stun_message *msg, UINT8 ice_controlled); +int nr_stun_message_add_ice_controlling_attribute(nr_stun_message *msg, UINT8 ice_controlling); +int nr_stun_message_add_priority_attribute(nr_stun_message *msg, UINT4 priority); +int nr_stun_message_add_use_candidate_attribute(nr_stun_message *msg); +#endif /* USE_ICE */ + +#ifdef USE_TURN +int nr_stun_message_add_data_attribute(nr_stun_message *msg, UCHAR *data, int length); +int nr_stun_message_add_lifetime_attribute(nr_stun_message *msg, UINT4 lifetime_secs); +int nr_stun_message_add_requested_transport_attribute(nr_stun_message *msg, UCHAR transport); +int +nr_stun_message_add_xor_peer_address_attribute(nr_stun_message *msg, nr_transport_addr *peer_address); +#endif /* USE_TURN */ + +#ifdef USE_STUND_0_96 +int nr_stun_message_add_change_request_attribute(nr_stun_message *msg, UINT4 change_request); +#endif /* USE_STUND_0_96 */ + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c new file mode 100644 index 0000000000..13366e265d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c @@ -0,0 +1,554 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> + +#include "stun.h" +#include "stun_reg.h" +#include "registry.h" + +static int +nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res); + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3 */ +int +nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg) +{ + int _status; + nr_stun_message_attribute *attr; + +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + /* if this message was generated by an RFC 3489 impementation, + * the call to nr_is_stun_message will fail, so skip that + * check and puke elsewhere if the message can't be decoded */ + if (msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE + || msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE2) { +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + if (!nr_is_stun_message(msg->buffer, msg->length)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Not a STUN message"); + ABORT(R_REJECTED); + } +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + } +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + + if (req == 0) { + if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_REQUEST) { + r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message type: %03x", msg->header.type); + ABORT(R_REJECTED); + } + } + else { + if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_RESPONSE + && NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_ERROR_RESPONSE) { + r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message class: %03x", msg->header.type); + ABORT(R_REJECTED); + } + + if (NR_STUN_GET_TYPE_METHOD(req->header.type) != NR_STUN_GET_TYPE_METHOD(msg->header.type)) { + r_log(NR_LOG_STUN,LOG_WARNING,"Inconsistent message method: %03x expected %03x", msg->header.type, req->header.type); + ABORT(R_REJECTED); + } + + if (nr_stun_different_transaction(msg->buffer, msg->length, req)) { + r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized STUN transaction"); + ABORT(R_REJECTED); + } + } + + switch (msg->header.magic_cookie) { + case NR_STUN_MAGIC_COOKIE: + /* basically draft-ietf-behave-rfc3489bis-10.txt S 6 rules */ + + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &attr) + && !attr->u.fingerprint.valid) { + r_log(NR_LOG_STUN, LOG_WARNING, "Invalid fingerprint"); + ABORT(R_REJECTED); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + + default: +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ +#else +#ifdef NDEBUG + /* in deployment builds we should always see a recognized magic cookie */ + r_log(NR_LOG_STUN, LOG_WARNING, "Missing Magic Cookie"); + ABORT(R_REJECTED); +#else + /* ignore this condition because sometimes we like to pretend we're + * a server talking to old clients and their messages don't contain + * a magic cookie at all but rather the magic cookie field is part + * of their ID and therefore random */ +#endif /* NDEBUG */ +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + break; + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1 */ +int +nr_stun_process_request(nr_stun_message *req, nr_stun_message *res) +{ + int _status; +#ifdef USE_STUN_PEDANTIC + int r; + nr_stun_attr_unknown_attributes unknown_attributes = { { 0 } }; + nr_stun_message_attribute *attr; + + if (req->comprehension_required_unknown_attributes > 0) { + nr_stun_form_error_response(req, res, 420, "Unknown Attributes"); + r_log(NR_LOG_STUN, LOG_WARNING, "Request contains comprehension required but unknown attributes"); + + TAILQ_FOREACH(attr, &req->attributes, entry) { + if (attr->name == 0) { + /* unrecognized attribute */ + + /* should never happen, but truncate if it ever were to occur */ + if (unknown_attributes.num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) + break; + + unknown_attributes.attribute[unknown_attributes.num_attributes++] = attr->type; + } + } + + assert(req->comprehension_required_unknown_attributes + req->comprehension_optional_unknown_attributes == unknown_attributes.num_attributes); + + if ((r=nr_stun_message_add_unknown_attributes_attribute(res, &unknown_attributes))) + ABORT(R_ALREADY); + + ABORT(R_ALREADY); + } +#endif /* USE_STUN_PEDANTIC */ + + _status=0; +#ifdef USE_STUN_PEDANTIC + abort: +#endif /* USE_STUN_PEDANTIC */ + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.2 */ +int +nr_stun_process_indication(nr_stun_message *ind) +{ + int _status; +#ifdef USE_STUN_PEDANTIC + + if (ind->comprehension_required_unknown_attributes > 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Indication contains comprehension required but unknown attributes"); + ABORT(R_REJECTED); + } +#endif /* USE_STUN_PEDANTIC */ + + _status=0; +#ifdef USE_STUN_PEDANTIC + abort: +#endif /* USE_STUN_PEDANTIC */ + return _status; +} + +/* RFC5389 S 7.3.3, except that we *also* allow a MAPPED_ADDRESS + to compensate for a bug in Google's STUN server where it + always returns MAPPED_ADDRESS. + + Mozilla bug: 888274. + */ +int +nr_stun_process_success_response(nr_stun_message *res) +{ + int _status; + +#ifdef USE_STUN_PEDANTIC + if (res->comprehension_required_unknown_attributes > 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Response contains comprehension required but unknown attributes"); + ABORT(R_REJECTED); + } +#endif /* USE_STUN_PEDANTIC */ + + if (NR_STUN_GET_TYPE_METHOD(res->header.type) == NR_METHOD_BINDING) { + if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0) && + ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing XOR-MAPPED-ADDRESS and MAPPED_ADDRESS"); + ABORT(R_REJECTED); + } + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.4 */ +int +nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code) +{ + int _status; + nr_stun_message_attribute *attr; + + if (res->comprehension_required_unknown_attributes > 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Error response contains comprehension required but unknown attributes"); + ABORT(R_REJECTED); + } + + if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, &attr)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing ERROR-CODE"); + ABORT(R_REJECTED); + } + + *error_code = attr->u.error_code.number; + + switch (attr->u.error_code.number / 100) { + case 3: + /* We do not treat STUN/300 as retryable. The TURN Allocate handling + * code will reset the ctx if appropriate. */ + ABORT(R_REJECTED); + break; + + case 4: + /* If the error code is 400 through 499, the client declares the + * transaction failed; in the case of 420 (Unknown Attribute), the + * response should contain a UNKNOWN-ATTRIBUTES attribute that gives + * additional information. */ + if (attr->u.error_code.number == 420) + ABORT(R_REJECTED); + + /* it may be possible to restart given the info that was received in + * this response, so retry */ + ABORT(R_RETRY); + break; + + case 5: + /* If the error code is 500 through 599, the client MAY resend the + * request; clients that do so MUST limit the number of times they do + * this. */ + /* let the retransmit mechanism handle resending the request */ + break; + + default: + ABORT(R_REJECTED); + break; + } + + /* the spec says: "The client then does any processing specified by the authentication + * mechanism (see Section 10). This may result in a new transaction + * attempt." -- but this is handled already elsewhere, so needn't be repeated + * in this function */ + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.2 */ +int +nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg, + nr_stun_message *res) +{ + int _status; + nr_stun_message_attribute *attr; + + switch (msg->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) { + nr_stun_form_error_response(msg, res, 400, "Missing MESSAGE-INTEGRITY"); + ABORT(R_ALREADY); + } + + if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, 0)) { + nr_stun_form_error_response(msg, res, 400, "Missing USERNAME"); + ABORT(R_ALREADY); + } + + if (attr->u.message_integrity.unknown_user) { + nr_stun_form_error_response(msg, res, 401, "Unrecognized USERNAME"); + ABORT(R_ALREADY); + } + + if (!attr->u.message_integrity.valid) { + nr_stun_form_error_response(msg, res, 401, "Bad MESSAGE-INTEGRITY"); + ABORT(R_ALREADY); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.3 */ +int +nr_stun_receive_response_short_term_auth(nr_stun_message *res) +{ + int _status; + nr_stun_message_attribute *attr; + + switch (res->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + + if (!attr->u.message_integrity.valid) { + r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; +} + +static int +nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res) +{ + int r,_status; + char *realm = 0; + char *nonce; + UINT2 size; + + if ((r=NR_reg_alloc_string(NR_STUN_REG_PREF_SERVER_REALM, &realm))) + ABORT(r); + + if ((r=nr_stun_message_add_realm_attribute(res, realm))) + ABORT(r); + + if (clnt) { + if (strlen(clnt->nonce) < 1) + new_nonce = 1; + + if (new_nonce) { + if (NR_reg_get_uint2(NR_STUN_REG_PREF_SERVER_NONCE_SIZE, &size)) + size = 48; + + if (size > (sizeof(clnt->nonce) - 1)) + size = sizeof(clnt->nonce) - 1; + + nr_random_alphanum(clnt->nonce, size); + clnt->nonce[size] = '\0'; + } + + nonce = clnt->nonce; + } + else { + /* user is not known, so use a bogus nonce since there's no way to + * store a good nonce with the client-specific data -- this nonce + * will be recognized as stale if the client attempts another + * request */ + nonce = "STALE"; + } + + if ((r=nr_stun_message_add_nonce_attribute(res, nonce))) + ABORT(r); + + _status=0; + abort: +#ifdef USE_TURN +assert(_status == 0); /* TODO: !nn! cleanup after I reimplmement TURN */ +#endif + RFREE(realm); + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.2.1 - 10.2.2 */ +int +nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res) +{ + int r,_status; + nr_stun_message_attribute *mi; + nr_stun_message_attribute *n; + nr_stun_server_client *clnt = 0; + + switch (req->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, 0)) { + nr_stun_form_error_response(req, res, 400, "Missing USERNAME"); + nr_stun_add_realm_and_nonce(0, 0, res); + ABORT(R_ALREADY); + } + + if ((r=nr_stun_get_message_client(ctx, req, &clnt))) { + nr_stun_form_error_response(req, res, 401, "Unrecognized USERNAME"); + nr_stun_add_realm_and_nonce(0, 0, res); + ABORT(R_ALREADY); + } + + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, &mi)) { + nr_stun_form_error_response(req, res, 401, "Missing MESSAGE-INTEGRITY"); + nr_stun_add_realm_and_nonce(0, clnt, res); + ABORT(R_ALREADY); + } + + assert(!mi->u.message_integrity.unknown_user); + + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_REALM, 0)) { + nr_stun_form_error_response(req, res, 400, "Missing REALM"); + ABORT(R_ALREADY); + } + + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_NONCE, &n)) { + nr_stun_form_error_response(req, res, 400, "Missing NONCE"); + ABORT(R_ALREADY); + } + + assert(sizeof(clnt->nonce) == sizeof(n->u.nonce)); + if (strncmp(clnt->nonce, n->u.nonce, sizeof(n->u.nonce))) { + nr_stun_form_error_response(req, res, 438, "Stale NONCE"); + nr_stun_add_realm_and_nonce(1, clnt, res); + ABORT(R_ALREADY); + } + + if (!mi->u.message_integrity.valid) { + nr_stun_form_error_response(req, res, 401, "Bad MESSAGE-INTEGRITY"); + nr_stun_add_realm_and_nonce(0, clnt, res); + ABORT(R_ALREADY); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to do in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.2.3 */ +int +nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx) +{ + int _status; + nr_stun_message_attribute *attr; + + switch (res->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_REALM, &attr)) { + RFREE(ctx->realm); + ctx->realm = r_strdup(attr->u.realm); + if (!ctx->realm) + ABORT(R_NO_MEMORY); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing REALM"); + ABORT(R_REJECTED); + } + + if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_NONCE, &attr)) { + RFREE(ctx->nonce); + ctx->nonce = r_strdup(attr->u.nonce); + if (!ctx->nonce) + ABORT(R_NO_MEMORY); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing NONCE"); + ABORT(R_REJECTED); + } + + if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) { + if (!attr->u.message_integrity.valid) { + r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; +} + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h new file mode 100644 index 0000000000..5975670779 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h @@ -0,0 +1,53 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _stun_proc_h +#define _stun_proc_h + +#include "stun.h" + +int nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg); +int nr_stun_process_request(nr_stun_message *req, nr_stun_message *res); +int nr_stun_process_indication(nr_stun_message *ind); +int nr_stun_process_success_response(nr_stun_message *res); +int nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code); + +int nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg, nr_stun_message *res); +int nr_stun_receive_response_short_term_auth(nr_stun_message *res); + +int nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res); +int nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h new file mode 100644 index 0000000000..2167fcd91b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h @@ -0,0 +1,58 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _stun_reg_h +#define _stun_reg_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +#define NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT "stun.client.retransmission_timeout" +#define NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF "stun.client.retransmission_backoff_factor" +#define NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS "stun.client.maximum_transmits" +#define NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF "stun.client.final_retransmit_backoff" + +#define NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS "stun.allow_loopback" +#define NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS "stun.allow_link_local" +#define NR_STUN_REG_PREF_ADDRESS_PRFX "stun.address" +#define NR_STUN_REG_PREF_SERVER_NAME "stun.server.name" +#define NR_STUN_REG_PREF_SERVER_NONCE_SIZE "stun.server.nonce_size" +#define NR_STUN_REG_PREF_SERVER_REALM "stun.server.realm" + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c new file mode 100644 index 0000000000..b92b6b5ab6 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c @@ -0,0 +1,468 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <string.h> +#include <assert.h> + +#include "nr_api.h" +#include "stun.h" + +static int nr_stun_server_destroy_client(nr_stun_server_client *clnt); +static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt); +static int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res); + + +int nr_stun_server_ctx_create(char *label, nr_stun_server_ctx **ctxp) + { + int r,_status; + nr_stun_server_ctx *ctx=0; + + if ((r=nr_stun_startup())) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_stun_server_ctx)))) + ABORT(R_NO_MEMORY); + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + STAILQ_INIT(&ctx->clients); + + *ctxp=ctx; + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp) + { + nr_stun_server_ctx *ctx; + nr_stun_server_client *clnt1,*clnt2; + + if(!ctxp || !*ctxp) + return(0); + + ctx=*ctxp; + + STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) { + nr_stun_server_destroy_client(clnt1); + } + + nr_stun_server_destroy_client(ctx->default_client); + + RFREE(ctx->label); + RFREE(ctx); + + return(0); + } + +static int nr_stun_server_client_create(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg, nr_stun_server_client **clntp) + { + nr_stun_server_client *clnt=0; + int r,_status; + + if(!(clnt=RCALLOC(sizeof(nr_stun_server_client)))) + ABORT(R_NO_MEMORY); + + if(!(clnt->label=r_strdup(client_label))) + ABORT(R_NO_MEMORY); + + if(!(clnt->username=r_strdup(user))) + ABORT(R_NO_MEMORY); + + if(r=r_data_copy(&clnt->password,pass)) + ABORT(r); + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s)/CLIENT(%s): Adding client for %s",ctx->label, client_label, user); + clnt->stun_server_cb=cb; + clnt->cb_arg=cb_arg; + + *clntp = clnt; + _status=0; + abort: + if(_status){ + nr_stun_server_destroy_client(clnt); + } + return(_status); + } + +int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg) + { + int r,_status; + nr_stun_server_client *clnt; + + if (r=nr_stun_server_client_create(ctx, client_label, user, pass, cb, cb_arg, &clnt)) + ABORT(r); + + STAILQ_INSERT_TAIL(&ctx->clients,clnt,entry); + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg) + { + int r,_status; + nr_stun_server_client *clnt; + + assert(!ctx->default_client); + if (ctx->default_client) + ABORT(R_INTERNAL); + + if (r=nr_stun_server_client_create(ctx, "default_client", ufrag, pass, cb, cb_arg, &clnt)) + ABORT(r); + + ctx->default_client = clnt; + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg) + { + nr_stun_server_client *clnt1,*clnt2; + int found = 0; + + STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) { + if(clnt1->cb_arg == cb_arg) { + STAILQ_REMOVE(&ctx->clients, clnt1, nr_stun_server_client_, entry); + nr_stun_server_destroy_client(clnt1); + found++; + } + } + + if (!found) + ERETURN(R_NOT_FOUND); + + return 0; + } + +static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password) + { + int _status; + nr_stun_server_ctx *ctx = (nr_stun_server_ctx*)arg; + nr_stun_server_client *clnt = 0; + nr_stun_message_attribute *username_attribute; + + if ((nr_stun_get_message_client(ctx, msg, &clnt))) { + if (! nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, &username_attribute)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label); + ABORT(R_NOT_FOUND); + } + + /* Although this is an exceptional condition, we'll already have seen a + * NOTICE-level log message about the unknown user, so additional log + * messages at any level higher than DEBUG are unnecessary. */ + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Unable to find password for unknown user: %s",ctx->label,username_attribute->u.username); + ABORT(R_NOT_FOUND); + } + + *password = &clnt->password; + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res) + { + int r,_status; + + if (nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0) + || !(auth_rule & NR_STUN_AUTH_RULE_OPTIONAL)) { + /* favor long term credentials over short term, if both are supported */ + + if (auth_rule & NR_STUN_AUTH_RULE_LONG_TERM) { + if ((r=nr_stun_receive_request_long_term_auth(req, ctx, res))) + ABORT(r); + } + else if (auth_rule & NR_STUN_AUTH_RULE_SHORT_TERM) { + if ((r=nr_stun_receive_request_or_indication_short_term_auth(req, res))) + ABORT(r); + } + } + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule) + { + int r,_status; + char string[256]; + nr_stun_message *req = 0; + nr_stun_message *res = 0; + nr_stun_server_client *clnt = 0; + nr_stun_server_request info; + int error; + int dont_free = 0; + nr_transport_addr my_addr; + + if ((r=nr_socket_getaddr(sock, &my_addr))) { + ABORT(r); + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Received(my_addr=%s,peer_addr=%s)",ctx->label,my_addr.as_string,peer_addr->as_string); + + snprintf(string, sizeof(string)-1, "STUN-SERVER(%s): Received ", ctx->label); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len); + + memset(&info,0,sizeof(info)); + + if ((r=nr_stun_message_create2(&req, (UCHAR*)msg, len))) + ABORT(r); + + if ((r=nr_stun_message_create(&res))) + ABORT(r); + + if ((r=nr_stun_decode_message(req, nr_stun_server_get_password, ctx))) { + /* RFC5389 S 7.3 says "If any errors are detected, the message is + * silently discarded." */ +#ifndef USE_STUN_PEDANTIC + /* ... but that seems like a bad idea, at least return a 400 so + * that the server isn't a black hole to the client */ + nr_stun_form_error_response(req, res, 400, "Bad Request - Failed to decode request"); + ABORT(R_ALREADY); +#endif /* USE_STUN_PEDANTIC */ + ABORT(R_REJECTED); + } + + if ((r=nr_stun_receive_message(0, req))) { + /* RFC5389 S 7.3 says "If any errors are detected, the message is + * silently discarded." */ +#ifndef USE_STUN_PEDANTIC + /* ... but that seems like a bad idea, at least return a 400 so + * that the server isn't a black hole to the client */ + nr_stun_form_error_response(req, res, 400, "Bad Request - Section 7.3 check failed"); + ABORT(R_ALREADY); +#endif /* USE_STUN_PEDANTIC */ + ABORT(R_REJECTED); + } + + if (NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_REQUEST + && NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_INDICATION) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Illegal message type: %04x",ctx->label,req->header.type); + /* RFC5389 S 7.3 says "If any errors are detected, the message is + * silently discarded." */ +#ifndef USE_STUN_PEDANTIC + /* ... but that seems like a bad idea, at least return a 400 so + * that the server isn't a black hole to the client */ + nr_stun_form_error_response(req, res, 400, "Bad Request - Unsupported message type"); + ABORT(R_ALREADY); +#endif /* USE_STUN_PEDANTIC */ + ABORT(R_REJECTED); + } + + /* "The STUN agent then does any checks that are required by a + * authentication mechanism that the usage has specified" */ + if ((r=nr_stun_server_process_request_auth_checks(ctx, req, auth_rule, res))) + ABORT(r); + + if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION) { + if ((r=nr_stun_process_indication(req))) + ABORT(r); + } + else { + if ((r=nr_stun_process_request(req, res))) + ABORT(r); + } + + assert(res->header.type == 0); + + clnt = 0; + if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_REQUEST) { + if ((nr_stun_get_message_client(ctx, req, &clnt))) { + if ((r=nr_stun_form_success_response(req, peer_addr, 0, res))) + ABORT(r); + } + else { + if ((r=nr_stun_form_success_response(req, peer_addr, &clnt->password, res))) + ABORT(r); + } + } + + if(clnt && clnt->stun_server_cb){ + r_log(NR_LOG_STUN,LOG_DEBUG,"Entering STUN server callback"); + + /* Set up the info */ + if(r=nr_transport_addr_copy(&info.src_addr,peer_addr)) + ABORT(r); + + info.request = req; + info.response = res; + + error = 0; + dont_free = 0; + if (clnt->stun_server_cb(clnt->cb_arg,ctx,sock,&info,&dont_free,&error)) { + if (error == 0) + error = 500; + + nr_stun_form_error_response(req, res, error, "ICE Failure"); + ABORT(R_ALREADY); + } + } + + _status=0; + abort: + if (!res) + goto skip_response; + + if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION) + goto skip_response; + + /* Now respond */ + + if (_status != 0 && ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0)) + nr_stun_form_error_response(req, res, 500, "Failed to specify error"); + + if ((r=nr_stun_server_send_response(ctx, sock, peer_addr, res, clnt))) { + r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Failed sending response (my_addr=%s,peer_addr=%s)",ctx->label,my_addr.as_string,peer_addr->as_string); + _status = R_FAILED; + } + +#if 0 + /* EKR: suppressed these checks because if you have an error when + you are sending an error, things go wonky */ +#ifdef SANITY_CHECKS + if (_status == R_ALREADY) { + assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_ERROR_RESPONSE); + assert(nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0)); + } + else { + assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_RESPONSE); + assert(!nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0)); + } +#endif /* SANITY_CHECKS */ +#endif + + if (0) { + skip_response: + _status = 0; + } + + if (!dont_free) { + nr_stun_message_destroy(&res); + nr_stun_message_destroy(&req); + } + + return(_status); + } + +static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt) + { + int r,_status; + char string[256]; + nr_transport_addr my_addr; + + if ((r=nr_socket_getaddr(sock, &my_addr))) { + ABORT(r); + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(label=%s): Sending(my_addr=%s,peer_addr=%s)",ctx->label,my_addr.as_string,peer_addr->as_string); + + if ((r=nr_stun_encode_message(res))) { + /* should never happen */ + r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Unable to encode message", ctx->label); + } + else { + snprintf(string, sizeof(string)-1, "STUN(%s): Sending to %s ", ctx->label, peer_addr->as_string); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)res->buffer, res->length); + + if(r=nr_socket_sendto(sock,res->buffer,res->length,0,peer_addr)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +static int nr_stun_server_destroy_client(nr_stun_server_client *clnt) + { + if (!clnt) + return 0; + + RFREE(clnt->label); + RFREE(clnt->username); + r_data_zfree(&clnt->password); + + RFREE(clnt); + return(0); + } + +int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **out) + { + int _status; + nr_stun_message_attribute *attr; + nr_stun_server_client *clnt=0; + + if (! nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, &attr)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label); + ABORT(R_NOT_FOUND); + } + + STAILQ_FOREACH(clnt, &ctx->clients, entry) { + if (!strncmp(clnt->username, attr->u.username, + sizeof(attr->u.username))) + break; + } + + if (!clnt && ctx->default_client) { + /* If we can't find a specific client see if this matches the default, + which means that the username starts with our ufrag. + */ + char *colon = strchr(attr->u.username, ':'); + if (colon && !strncmp(ctx->default_client->username, + attr->u.username, + colon - attr->u.username)) { + clnt = ctx->default_client; + r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Falling back to default client, username=: %s",ctx->label,attr->u.username); + } + } + + if (!clnt) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Request from unknown user: %s",ctx->label,attr->u.username); + ABORT(R_NOT_FOUND); + } + + *out = clnt; + + _status=0; + abort: + return(_status); + } + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h new file mode 100644 index 0000000000..328675ee22 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h @@ -0,0 +1,80 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _stun_server_ctx_h +#define _stun_server_ctx_h + +typedef struct nr_stun_server_ctx_ nr_stun_server_ctx; +typedef struct nr_stun_server_client_ nr_stun_server_client; + +#include "stun_util.h" + + +typedef struct nr_stun_server_request_{ + nr_transport_addr src_addr; + nr_stun_message *request; + nr_stun_message *response; +} nr_stun_server_request; + +typedef int (*nr_stun_server_cb)(void *cb_arg, struct nr_stun_server_ctx_ *ctx,nr_socket *sock,nr_stun_server_request *req, int *dont_free, int *error); + +struct nr_stun_server_client_ { + char *label; + char *username; + Data password; + nr_stun_server_cb stun_server_cb; + void *cb_arg; + char nonce[NR_STUN_MAX_NONCE_BYTES+1]; /* +1 for \0 */ + STAILQ_ENTRY(nr_stun_server_client_) entry; +}; + +typedef STAILQ_HEAD(nr_stun_server_client_head_, nr_stun_server_client_) nr_stun_server_client_head; + +struct nr_stun_server_ctx_ { + char *label; + nr_stun_server_client_head clients; + nr_stun_server_client *default_client; +}; + + +int nr_stun_server_ctx_create(char *label, nr_stun_server_ctx **ctxp); +int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp); +int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg); +int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg); +int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg); +int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule); +int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **clnt); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c new file mode 100644 index 0000000000..cbb0128924 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c @@ -0,0 +1,352 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> + +#include "stun.h" +#include "stun_reg.h" +#include "registry.h" +#include "addrs.h" +#include "transport_addr_reg.h" +#include "nr_crypto.h" +#include "hex.h" + + +int NR_LOG_STUN = 0; + +int +nr_stun_startup(void) +{ + int r,_status; + + if ((r=r_log_register("stun", &NR_LOG_STUN))) + ABORT(r); + + _status=0; + abort: + return _status; +} + +int +nr_stun_xor_mapped_address(UINT4 magicCookie, UINT12 transactionId, nr_transport_addr *from, nr_transport_addr *to) +{ + int _status; + + switch (from->ip_version) { + case NR_IPV4: + nr_ip4_port_to_transport_addr( + (ntohl(from->u.addr4.sin_addr.s_addr) ^ magicCookie), + (ntohs(from->u.addr4.sin_port) ^ (magicCookie>>16)), + from->protocol, to); + break; + case NR_IPV6: + { + union { + unsigned char addr[16]; + UINT4 addr32[4]; + } maskedAddr; + + maskedAddr.addr32[0] = htonl(magicCookie); /* Passed in host byte order */ + memcpy(&maskedAddr.addr32[1], transactionId.octet, sizeof(transactionId)); + + /* We now have the mask in network byte order */ + /* Xor the address in network byte order */ + for (size_t i = 0; i < sizeof(maskedAddr); ++i) { + maskedAddr.addr[i] ^= from->u.addr6.sin6_addr.s6_addr[i]; + } + + nr_ip6_port_to_transport_addr( + (struct in6_addr*)&maskedAddr, + (ntohs(from->u.addr6.sin6_port) ^ (magicCookie>>16)), + from->protocol, to); + } + break; + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + _status = 0; + abort: + return _status; +} + +int +nr_stun_filter_local_addresses(nr_local_addr addrs[], int *count) +{ + int r,_status; + char allow_loopback = 0; + char allow_link_local = 0; + + if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, + &allow_loopback))) { + if (r != R_NOT_FOUND) { + ABORT(r); + } + } + + if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, + &allow_link_local))) { + if (r != R_NOT_FOUND) { + ABORT(r); + } + } + + if ((r=nr_stun_filter_addrs(addrs, + !allow_loopback, + !allow_link_local, + count))) { + ABORT(r); + } + + _status=0; + abort: + return _status; +} + +int +nr_stun_find_local_addresses(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int r,_status; + //NR_registry *children = 0; + + *count = 0; + +#if 0 + // this really goes with the code commented out below. (mjf) + if ((r=NR_reg_get_child_count(NR_STUN_REG_PREF_ADDRESS_PRFX, (unsigned int*)count))) + if (r != R_NOT_FOUND) + ABORT(r); +#endif + + if (*count == 0) { + if ((r=nr_stun_get_addrs(addrs, maxaddrs, count))) + ABORT(r); + + goto done; + } + + if (*count >= maxaddrs) { + r_log(NR_LOG_STUN, LOG_INFO, "Address list truncated from %d to %d", *count, maxaddrs); + *count = maxaddrs; + } + +#if 0 + if (*count > 0) { + /* TODO(ekr@rtfm.com): Commented out 2012-07-26. + + This code is currently not used in Firefox and needs to be + ported to 64-bit */ + children = RCALLOC((*count + 10) * sizeof(*children)); + if (!children) + ABORT(R_NO_MEMORY); + + assert(sizeof(size_t) == sizeof(*count)); + + if ((r=NR_reg_get_children(NR_STUN_REG_PREF_ADDRESS_PRFX, children, (size_t)(*count + 10), (size_t*)count))) + ABORT(r); + + for (i = 0; i < *count; ++i) { + if ((r=nr_reg_get_transport_addr(children[i], 0, &addrs[i].addr))) + ABORT(r); + } + } +#endif + + done: + + _status=0; + abort: + //RFREE(children); + return _status; +} + +int +nr_stun_different_transaction(UCHAR *msg, size_t len, nr_stun_message *req) +{ + int _status; + nr_stun_message_header header; + char reqid[44]; + char msgid[44]; + size_t unused; + + if (sizeof(header) > len) + ABORT(R_FAILED); + + assert(sizeof(header.id) == sizeof(UINT12)); + + memcpy(&header, msg, sizeof(header)); + + if (memcmp(&req->header.id, &header.id, sizeof(header.id))) { + nr_nbin2hex((UCHAR*)&req->header.id, sizeof(req->header.id), reqid, sizeof(reqid), &unused); + nr_nbin2hex((UCHAR*)&header.id, sizeof(header.id), msgid, sizeof(msgid), &unused); + r_log(NR_LOG_STUN, LOG_DEBUG, "Mismatched message IDs %s/%s", reqid, msgid); + ABORT(R_NOT_FOUND); + } + + _status=0; + abort: + return _status; +} + +char* +nr_stun_msg_type(int type) +{ + char *ret = 0; + + switch (type) { + case NR_STUN_MSG_BINDING_REQUEST: + ret = "BINDING-REQUEST"; + break; + case NR_STUN_MSG_BINDING_INDICATION: + ret = "BINDING-INDICATION"; + break; + case NR_STUN_MSG_BINDING_RESPONSE: + ret = "BINDING-RESPONSE"; + break; + case NR_STUN_MSG_BINDING_ERROR_RESPONSE: + ret = "BINDING-ERROR-RESPONSE"; + break; + +#ifdef USE_TURN + case NR_STUN_MSG_ALLOCATE_REQUEST: + ret = "ALLOCATE-REQUEST"; + break; + case NR_STUN_MSG_ALLOCATE_RESPONSE: + ret = "ALLOCATE-RESPONSE"; + break; + case NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE: + ret = "ALLOCATE-ERROR-RESPONSE"; + break; + case NR_STUN_MSG_REFRESH_REQUEST: + ret = "REFRESH-REQUEST"; + break; + case NR_STUN_MSG_REFRESH_RESPONSE: + ret = "REFRESH-RESPONSE"; + break; + case NR_STUN_MSG_REFRESH_ERROR_RESPONSE: + ret = "REFRESH-ERROR-RESPONSE"; + break; + case NR_STUN_MSG_SEND_INDICATION: + ret = "SEND-INDICATION"; + break; + case NR_STUN_MSG_DATA_INDICATION: + ret = "DATA-INDICATION"; + break; + case NR_STUN_MSG_PERMISSION_REQUEST: + ret = "PERMISSION-REQUEST"; + break; + case NR_STUN_MSG_PERMISSION_RESPONSE: + ret = "PERMISSION-RESPONSE"; + break; + case NR_STUN_MSG_PERMISSION_ERROR_RESPONSE: + ret = "PERMISSION-ERROR-RESPONSE"; + break; +#endif /* USE_TURN */ + + default: + /* ret remains 0 */ + break; + } + + return ret; +} + +int +nr_random_alphanum(char *alphanum, int size) +{ + static char alphanums[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; + int i; + + nr_crypto_random_bytes((UCHAR*)alphanum, size); + + /* now convert from binary to alphanumeric */ + for (i = 0; i < size; ++i) + alphanum[i] = alphanums[(UCHAR)alphanum[i]]; + + return 0; +} + +#ifndef UINT2_MAX +#define UINT2_MAX ((UINT2)(65535U)) +#endif + +void nr_accumulate_count(UINT2* orig_count, UINT2 add_count) + { + if (UINT2_MAX - add_count < *orig_count) { + // don't rollover, just stop accumulating at MAX value + *orig_count = UINT2_MAX; + } else { + *orig_count += add_count; + } + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h new file mode 100644 index 0000000000..379b4fdd3d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h @@ -0,0 +1,62 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _stun_util_h +#define _stun_util_h + +#include "stun.h" +#include "local_addr.h" + +extern int NR_LOG_STUN; + +int nr_stun_startup(void); + +int nr_stun_xor_mapped_address(UINT4 magicCookie, UINT12 transactionId, nr_transport_addr *from, nr_transport_addr *to); + +// removes duplicates, and based on prefs, loopback and link_local addresses +int nr_stun_filter_local_addresses(nr_local_addr addrs[], int *count); + +int nr_stun_find_local_addresses(nr_local_addr addrs[], int maxaddrs, int *count); + +int nr_stun_different_transaction(UCHAR *msg, size_t len, nr_stun_message *req); + +char* nr_stun_msg_type(int type); + +int nr_random_alphanum(char *alphanum, int size); + +// accumulate a count without worrying about rollover +void nr_accumulate_count(UINT2* orig_count, UINT2 add_count); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c new file mode 100644 index 0000000000..707d43a4a9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c @@ -0,0 +1,1277 @@ +/* + 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, Mozilla 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. +*/ + +#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" +#include "ice_ctx.h" + +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 */ + +// Set to enable a temporary fix that will run the TURN reservation keep-alive +// logic when data is received via a TURN relayed path: a DATA_INDICATION packet is received. +// TODO(pkerr@mozilla.com) This should be replace/removed when bug 935806 is implemented. +#define REFRESH_RESERVATION_ON_RECV 1 + +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_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_start_refresh_timer(nr_turn_client_ctx *ctx, + nr_turn_stun_ctx *sctx, + UINT4 lifetime); +static int nr_turn_permission_create(nr_turn_client_ctx *ctx, + const nr_transport_addr *addr, + nr_turn_permission **permp); +static int nr_turn_permission_find(nr_turn_client_ctx *ctx, + const nr_transport_addr *addr, + nr_turn_permission **permp); +static int nr_turn_permission_destroy(nr_turn_permission **permp); +static void nr_turn_client_refresh_cb(NR_SOCKET s, int how, void *arg); +static void nr_turn_client_permissions_cb(NR_SOCKET s, int how, void *cb); +static int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx, + nr_stun_message *req, + int flags); + +int nr_transport_addr_listnode_create(const nr_transport_addr *addr, nr_transport_addr_listnode **listnodep) +{ + nr_transport_addr_listnode *listnode = 0; + int r,_status; + + if (!(listnode=RCALLOC(sizeof(nr_transport_addr_listnode)))) { + ABORT(R_NO_MEMORY); + } + + if ((r = nr_transport_addr_copy(&listnode->value, addr))) { + ABORT(r); + } + + *listnodep = listnode; + listnode = 0; + _status = 0; + +abort: + nr_transport_addr_listnode_destroy(&listnode); + return(_status); +} + +void nr_transport_addr_listnode_destroy(nr_transport_addr_listnode **listnode) +{ + RFREE(*listnode); + *listnode = 0; +} + +/* nr_turn_stun_ctx functions */ +static int nr_turn_stun_ctx_create(nr_turn_client_ctx *tctx, int mode, + NR_async_cb success_cb, + NR_async_cb error_cb, + nr_turn_stun_ctx **ctxp) +{ + nr_turn_stun_ctx *sctx = 0; + int r,_status; + char label[256]; + + if (!(sctx=RCALLOC(sizeof(nr_turn_stun_ctx)))) + ABORT(R_NO_MEMORY); + + /* TODO(ekr@rtfm.com): label by phase */ + snprintf(label, sizeof(label), "%s:%s", tctx->label, ":TURN"); + + if ((r=nr_stun_client_ctx_create(label, tctx->sock, &tctx->turn_server_addr, + TURN_RTO, &sctx->stun))) { + ABORT(r); + } + + /* Set the STUN auth parameters, but don't set authentication on. + For that we need the nonce, set in nr_turn_stun_set_auth_params. + */ + sctx->stun->auth_params.username=tctx->username; + INIT_DATA(sctx->stun->auth_params.password, + tctx->password->data, tctx->password->len); + + sctx->tctx=tctx; + sctx->success_cb=success_cb; + sctx->error_cb=error_cb; + sctx->mode=mode; + sctx->last_error_code=0; + STAILQ_INIT(&sctx->addresses_tried); + + /* Add ourselves to the tctx's list */ + STAILQ_INSERT_TAIL(&tctx->stun_ctxs, sctx, entry); + *ctxp=sctx; + + _status=0; +abort: + if (_status) { + nr_turn_stun_ctx_destroy(&sctx); + } + return(_status); +} + +/* Note: this function does not pull us off the tctx's list. */ +static int nr_turn_stun_ctx_destroy(nr_turn_stun_ctx **ctxp) +{ + nr_turn_stun_ctx *ctx; + + if (!ctxp || !*ctxp) + return 0; + + ctx = *ctxp; + *ctxp = 0; + + nr_stun_client_ctx_destroy(&ctx->stun); + RFREE(ctx->realm); + RFREE(ctx->nonce); + + while (!STAILQ_EMPTY(&ctx->addresses_tried)) { + nr_transport_addr_listnode *listnode = STAILQ_FIRST(&ctx->addresses_tried); + STAILQ_REMOVE_HEAD(&ctx->addresses_tried, entry); + nr_transport_addr_listnode_destroy(&listnode); + } + + RFREE(ctx); + + return 0; +} + +static int nr_turn_stun_set_auth_params(nr_turn_stun_ctx *ctx, + char *realm, char *nonce) +{ + int _status; + + RFREE(ctx->realm); + RFREE(ctx->nonce); + + assert(realm); + if (!realm) + ABORT(R_BAD_ARGS); + ctx->realm=r_strdup(realm); + if (!ctx->realm) + ABORT(R_NO_MEMORY); + + assert(nonce); + if (!nonce) + ABORT(R_BAD_ARGS); + ctx->nonce=r_strdup(nonce); + if (!ctx->nonce) + ABORT(R_NO_MEMORY); + + RFREE(ctx->stun->realm); + ctx->stun->realm = r_strdup(ctx->realm); + if (!ctx->stun->realm) + ABORT(R_NO_MEMORY); + + ctx->stun->auth_params.realm = ctx->realm; + ctx->stun->auth_params.nonce = ctx->nonce; + ctx->stun->auth_params.authenticate = 1; /* May already be 1 */ + + _status=0; +abort: + return(_status); +} + +static int nr_turn_stun_ctx_start(nr_turn_stun_ctx *ctx) +{ + int r, _status; + nr_turn_client_ctx *tctx = ctx->tctx; + nr_transport_addr_listnode *address_tried = 0; + + if ((r=nr_stun_client_reset(ctx->stun))) { + r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't reset STUN", + tctx->label); + ABORT(r); + } + + if ((r=nr_stun_client_start(ctx->stun, ctx->mode, nr_turn_stun_ctx_cb, ctx))) { + r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't start STUN", + tctx->label); + ABORT(r); + } + + if ((r=nr_transport_addr_listnode_create(&ctx->stun->peer_addr, &address_tried))) { + ABORT(r); + } + + STAILQ_INSERT_TAIL(&ctx->addresses_tried, address_tried, entry); + + _status=0; +abort: + return _status; +} + +static int nr_turn_stun_ctx_handle_redirect(nr_turn_stun_ctx *ctx) +{ + int r, _status; + nr_turn_client_ctx *tctx = ctx->tctx; + nr_stun_message_attribute *ec; + nr_stun_message_attribute *attr; + nr_transport_addr *alternate_addr = 0; + int index = 0; + + if (!tctx->ctx) { + /* If we were to require TCP nr_sockets to allow multiple connect calls by + * disconnecting and re-connecting, we could avoid this requirement. */ + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect is not supported when " + "there is no ICE ctx, and by extension no socket factory, since we " + "need that to create new sockets in the TCP case", + tctx->label); + ABORT(R_BAD_ARGS); + } + + if (ctx->stun->response->header.type != NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect called for something " + "other than an Allocate error response (type=%d)", + tctx->label, ctx->stun->response->header.type); + ABORT(R_BAD_ARGS); + } + + if (!nr_stun_message_has_attribute(ctx->stun->response, + NR_STUN_ATTR_ERROR_CODE, &ec) || + (ec->u.error_code.number != 300)) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect called without a " + "300 response", + tctx->label); + ABORT(R_BAD_ARGS); + } + + while (!alternate_addr && !nr_stun_message_get_attribute( + ctx->stun->response, NR_STUN_ATTR_ALTERNATE_SERVER, index++, &attr)) { + alternate_addr = &attr->u.alternate_server; + + // TODO: Someday we may need to handle IP version switching, but it is + // unclear how that is supposed to work with ICE when the IP version of + // the candidate's base address is fixed... + if (alternate_addr->ip_version != tctx->turn_server_addr.ip_version) { + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): nr_turn_stun_ctx_handle_redirect not trying %s, since it is a different IP version", + tctx->label, alternate_addr->as_string); + alternate_addr = 0; + continue; + } + + /* Check if we've already tried this, and ignore if we have */ + nr_transport_addr_listnode *address_tried = 0; + STAILQ_FOREACH(address_tried, &ctx->addresses_tried, entry) { + /* Ignore protocol */ + alternate_addr->protocol = address_tried->value.protocol; + if (!nr_transport_addr_cmp(alternate_addr, &address_tried->value, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): nr_turn_stun_ctx_handle_redirect already tried %s, ignoring", + tctx->label, alternate_addr->as_string); + alternate_addr = 0; + break; + } + } + } + + if (!alternate_addr) { + /* Should we use a different error code depending on why we didn't find + * one? (eg; no ALTERNATE-SERVERS at all, none that we have not tried + * already, none that are of a compatible IP version?) */ + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect did not find a viable " + "ALTERNATE-SERVER", + tctx->label); + ABORT(R_FAILED); + } + + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): nr_turn_stun_ctx_handle_redirect trying %s", + tctx->label, alternate_addr->as_string); + + /* This also handles the call to nr_transport_addr_fmt_addr_string */ + if ((r = nr_transport_addr_copy_addrport(&tctx->turn_server_addr, + alternate_addr))) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect copying ALTERNATE-SERVER " + "failed(%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + /* TURN server address is now updated. Restart the STUN Allocate ctx. Note + * that we do not attempt to update the local address; if the TURN server + * redirects to something that is not best reached from the already-selected + * local address, oh well. */ + + if (tctx->turn_server_addr.protocol == IPPROTO_TCP) { + /* For TCP, we need to replace the underlying nr_socket, since we cannot + * un-connect it from the old server. */ + /* If we were to require TCP nr_sockets to allow multiple connect calls by + * disconnecting and re-connecting, we could avoid this stuff, and just + * call nr_socket_connect. */ + nr_transport_addr old_local_addr; + nr_socket* new_socket; + if ((r = nr_socket_getaddr(tctx->sock, &old_local_addr))) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect " + "failed to get old local address (%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + if ((r = nr_socket_factory_create_socket(tctx->ctx->socket_factory, + &old_local_addr, &new_socket))) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect " + "failed to create new raw TCP socket for redirect (%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + if ((r = nr_socket_buffered_stun_reset(tctx->sock, new_socket))) { + /* nr_socket_buffered_stun_reset always takes ownership of |new_socket| */ + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect " + "failed to update raw TCP socket (%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + if ((r = nr_socket_connect(tctx->sock, &tctx->turn_server_addr))) { + if (r != R_WOULDBLOCK) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect nr_socket_connect " + "failed(%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + } + } + + nr_transport_addr_copy(&ctx->stun->peer_addr, &tctx->turn_server_addr); + + if ((r = nr_turn_stun_ctx_start(ctx))) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect nr_turn_stun_ctx_start " + "failed(%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + _status = 0; +abort: + return _status; +} + +static void nr_turn_stun_ctx_cb(NR_SOCKET s, int how, void *arg) +{ + int r, _status; + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + + ctx->last_error_code = ctx->stun->error_code; + + switch (ctx->stun->state) { + case NR_STUN_CLIENT_STATE_DONE: + /* Save the realm and nonce */ + if (ctx->stun->realm && (!ctx->tctx->realm || strcmp(ctx->stun->realm, + ctx->tctx->realm))) { + RFREE(ctx->tctx->realm); + ctx->tctx->realm = r_strdup(ctx->stun->realm); + if (!ctx->tctx->realm) + ABORT(R_NO_MEMORY); + } + if (ctx->stun->nonce && (!ctx->tctx->nonce || strcmp(ctx->stun->nonce, + ctx->tctx->nonce))) { + RFREE(ctx->tctx->nonce); + ctx->tctx->nonce = r_strdup(ctx->stun->nonce); + if (!ctx->tctx->nonce) + ABORT(R_NO_MEMORY); + } + + ctx->retry_ct=0; + ctx->success_cb(0, 0, ctx); + break; + + case NR_STUN_CLIENT_STATE_FAILED: + /* Special case: if this is an authentication error, + we retry once. This allows the 401/438 nonce retry + paradigm. After that, we fail */ + /* TODO(ekr@rtfm.com): 401 needs a #define */ + /* TODO(ekr@rtfm.com): Add alternate-server (Mozilla bug 857688) */ + if (ctx->stun->error_code == 438) { + // track 438s for ice telemetry + nr_accumulate_count(&(ctx->tctx->cnt_438s), 1); + } + if (ctx->stun->error_code == 401 || ctx->stun->error_code == 438) { + if (ctx->retry_ct > 0) { + if (ctx->stun->error_code == 401) { + // track 401s for ice telemetry + nr_accumulate_count(&(ctx->tctx->cnt_401s), 1); + } + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Exceeded the number of retries", ctx->tctx->label); + ABORT(R_FAILED); + } + + if (!ctx->stun->nonce) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no nonce", ctx->tctx->label); + ABORT(R_FAILED); + } + if (!ctx->stun->realm) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no realm", ctx->tctx->label); + ABORT(R_FAILED); + } + + /* Try to retry */ + if ((r=nr_turn_stun_set_auth_params(ctx, ctx->stun->realm, + ctx->stun->nonce))) + ABORT(r); + + ctx->stun->error_code = 0; /* Reset to avoid inf-looping */ + + if ((r=nr_turn_stun_ctx_start(ctx))) { + r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't start STUN", ctx->tctx->label); + ABORT(r); + } + + ctx->retry_ct++; + } else if (ctx->stun->error_code == 300) { + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): Redirect received, restarting TURN", ctx->tctx->label); + /* We don't limit this with a retry counter, we limit redirects by + * checking whether we've tried the ALTERNATE-SERVER yet. */ + ctx->retry_ct = 0; + if ((r = nr_turn_stun_ctx_handle_redirect(ctx))) { + ABORT(r); + } + } else { + ABORT(R_FAILED); + } + break; + + case NR_STUN_CLIENT_STATE_TIMED_OUT: + ABORT(R_FAILED); + break; + + case NR_STUN_CLIENT_STATE_CANCELLED: + assert(0); /* Shouldn't happen */ + return; + break; + + default: + assert(0); /* Shouldn't happen */ + return; + } + + _status=0; +abort: + if (_status) { + ctx->error_cb(0, 0, ctx); + } +} + +/* nr_turn_client_ctx functions */ +int nr_turn_client_ctx_create(const char* label, nr_socket* sock, + const char* username, Data* password, + nr_transport_addr* addr, nr_ice_ctx* ice_ctx, + nr_turn_client_ctx** ctxp) { + nr_turn_client_ctx *ctx=0; + int r,_status; + + 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); + + 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); + + ctx->state = NR_TURN_CLIENT_STATE_INITTED; + if (addr->protocol == IPPROTO_TCP) { + if ((r=nr_socket_connect(ctx->sock, &ctx->turn_server_addr))) { + if (r != R_WOULDBLOCK) + ABORT(r); + } + } + + ctx->ctx = ice_ctx; + + *ctxp=ctx; + + _status=0; +abort: + if(_status){ + nr_turn_client_ctx_destroy(&ctx); + } + return(_status); +} + +int +nr_turn_client_ctx_destroy(nr_turn_client_ctx **ctxp) +{ + 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); + + nr_turn_client_deallocate(ctx); + + /* Cancel frees the rest of our data */ + RFREE(ctx->label); + ctx->label = 0; + + nr_turn_client_cancel(ctx); + + RFREE(ctx->username); + ctx->username = 0; + r_data_destroy(&ctx->password); + RFREE(ctx->nonce); + ctx->nonce = 0; + RFREE(ctx->realm); + ctx->realm = 0; + + /* Destroy the STUN client ctxs */ + while (!STAILQ_EMPTY(&ctx->stun_ctxs)) { + nr_turn_stun_ctx *stun = STAILQ_FIRST(&ctx->stun_ctxs); + STAILQ_REMOVE_HEAD(&ctx->stun_ctxs, entry); + nr_turn_stun_ctx_destroy(&stun); + } + + /* 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); + } + + RFREE(ctx); + + return(0); +} + +int nr_turn_client_cancel(nr_turn_client_ctx *ctx) +{ + nr_turn_stun_ctx *stun = 0; + + 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); + + /* Cancel the STUN client ctxs */ + stun = STAILQ_FIRST(&ctx->stun_ctxs); + while (stun) { + nr_stun_client_cancel(stun->stun); + stun = STAILQ_NEXT(stun, entry); + } + + /* 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); +} + +int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx, + nr_stun_message *req, + int flags) +{ + int r,_status; + + if ((r=nr_stun_encode_message(req))) + ABORT(r); + + if ((r=nr_socket_sendto(ctx->sock, + req->buffer, req->length, flags, + &ctx->turn_server_addr))) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Failed sending request", + ctx->label); + ABORT(r); + } + + _status=0; +abort: + return(_status); +} + +int nr_turn_client_deallocate(nr_turn_client_ctx *ctx) +{ + int r,_status; + nr_stun_message *aloc = 0; + nr_stun_client_auth_params auth; + nr_stun_client_refresh_request_params refresh; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + return(0); + + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): deallocating", ctx->label); + + refresh.lifetime_secs = 0; + + auth.username = ctx->username; + INIT_DATA(auth.password, ctx->password->data, ctx->password->len); + + auth.realm = ctx->realm; + auth.nonce = ctx->nonce; + + auth.authenticate = 1; + + if ((r=nr_stun_build_refresh_request(&auth, &refresh, &aloc))) + ABORT(r); + + // We are only sending a single request here because we are in the process of + // shutting everything down. Theoretically we should probably start a seperate + // STUN transaction which outlives the TURN context. + if ((r=nr_turn_client_send_stun_request(ctx, aloc, 0))) + ABORT(r); + + ctx->state = NR_TURN_CLIENT_STATE_DEALLOCATING; + + _status=0; +abort: + nr_stun_message_destroy(&aloc); + return(_status); +} + +static void nr_turn_client_fire_finished_cb(nr_turn_client_ctx *ctx) + { + if (ctx->finished_cb) { + NR_async_cb finished_cb=ctx->finished_cb; + ctx->finished_cb=0; + finished_cb(0, 0, ctx->cb_arg); + } + } + +int nr_turn_client_failed(nr_turn_client_ctx *ctx) +{ + if (ctx->state == NR_TURN_CLIENT_STATE_FAILED || + ctx->state == NR_TURN_CLIENT_STATE_CANCELLED) + return(0); + + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s) failed", ctx->label); + nr_turn_client_cancel(ctx); + ctx->state = NR_TURN_CLIENT_STATE_FAILED; + nr_turn_client_fire_finished_cb(ctx); + + return(0); +} + +int nr_turn_client_get_relayed_address(nr_turn_client_ctx *ctx, + nr_transport_addr *relayed_address) +{ + int r, _status; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_FAILED); + + if (r=nr_transport_addr_copy(relayed_address, &ctx->relay_addr)) + ABORT(r); + + _status=0; +abort: + return(_status); +} + +int nr_turn_client_get_mapped_address(nr_turn_client_ctx *ctx, + nr_transport_addr *mapped_address) +{ + int r, _status; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_FAILED); + + if (r=nr_transport_addr_copy(mapped_address, &ctx->mapped_addr)) + ABORT(r); + + _status=0; +abort: + return(_status); +} + +static void nr_turn_client_allocate_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + nr_turn_stun_ctx *refresh_ctx; + int r,_status; + + ctx->tctx->state = NR_TURN_CLIENT_STATE_ALLOCATED; + + if ((r=nr_transport_addr_copy( + &ctx->tctx->relay_addr, + &ctx->stun->results.allocate_response.relay_addr))) + ABORT(r); + + if ((r=nr_transport_addr_copy( + &ctx->tctx->mapped_addr, + &ctx->stun->results.allocate_response.mapped_addr))) + ABORT(r); + + if ((r=nr_turn_client_refresh_setup(ctx->tctx, &refresh_ctx))) + ABORT(r); + + if ((r=nr_turn_client_start_refresh_timer( + ctx->tctx, refresh_ctx, + ctx->stun->results.allocate_response.lifetime_secs))) + ABORT(r); + + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): Succesfully allocated addr %s lifetime=%u", + ctx->tctx->label, + ctx->tctx->relay_addr.as_string, + ctx->stun->results.allocate_response.lifetime_secs); + + nr_turn_client_fire_finished_cb(ctx->tctx); + _status=0; +abort: + if (_status) { + nr_turn_client_failed(ctx->tctx); + } +} + +static void nr_turn_client_error_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): mode %d, %s", + ctx->tctx->label, ctx->mode, __FUNCTION__); + + nr_turn_client_failed(ctx->tctx); +} + +static void nr_turn_client_permission_error_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + + if (ctx->last_error_code == 403) { + // track 403s for ice telemetry + nr_accumulate_count(&(ctx->tctx->cnt_403s), 1); + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): mode %d, permission denied", + ctx->tctx->label, ctx->mode); + + } else{ + nr_turn_client_error_cb(0, 0, ctx); + } +} + +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; + + if(ctx->state == NR_TURN_CLIENT_STATE_FAILED || + ctx->state == NR_TURN_CLIENT_STATE_CANCELLED){ + /* TURN TCP contexts can fail before we ever try to form an allocation, + * since the TCP connection can fail. It is also conceivable that a TURN + * TCP context could be cancelled before we are done forming all + * allocations (although we do not do this at the time this code was + * written) */ + assert(ctx->turn_server_addr.protocol == IPPROTO_TCP); + ABORT(R_NOT_FOUND); + } + + assert(ctx->state == NR_TURN_CLIENT_STATE_INITTED); + + 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; + + if (ctx->state == NR_TURN_CLIENT_STATE_INITTED) { + if ((r=nr_turn_stun_ctx_start(stun))) + ABORT(r); + ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING; + } else { + ABORT(R_ALREADY); + } + + _status=0; +abort: + if (_status) { + nr_turn_client_failed(ctx); + } + + return(_status); +} + +int nr_turn_client_process_response(nr_turn_client_ctx *ctx, + UCHAR *msg, int len, + nr_transport_addr *turn_server_addr) +{ + int r, _status; + nr_turn_stun_ctx *sc1; + + switch (ctx->state) { + case NR_TURN_CLIENT_STATE_ALLOCATING: + case NR_TURN_CLIENT_STATE_ALLOCATED: + break; + default: + ABORT(R_FAILED); + } + + sc1 = STAILQ_FIRST(&ctx->stun_ctxs); + while (sc1) { + r = nr_stun_client_process_response(sc1->stun, msg, len, turn_server_addr); + if (!r) + break; + if (r==R_RETRY) /* Likely a 401 and we will retry */ + break; + if (r != R_REJECTED) + ABORT(r); + sc1 = STAILQ_NEXT(sc1, entry); + } + if (!sc1) + ABORT(R_REJECTED); + + _status=0; +abort: + return(_status); +} + +static int nr_turn_client_refresh_setup(nr_turn_client_ctx *ctx, + nr_turn_stun_ctx **sctx) +{ + nr_turn_stun_ctx *stun = 0; + int r,_status; + + assert(ctx->state == NR_TURN_CLIENT_STATE_ALLOCATED); + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_NOT_PERMITTED); + + if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_REFRESH_REQUEST, + nr_turn_client_refresh_cb, + nr_turn_client_error_cb, + &stun))) + ABORT(r); + + if ((r=nr_turn_stun_set_auth_params(stun, ctx->realm, ctx->nonce))) + ABORT(r); + + stun->stun->params.refresh_request.lifetime_secs = + TURN_LIFETIME_REQUEST_SECONDS; + + + *sctx=stun; + + _status=0; +abort: + return(_status); +} + +static int nr_turn_client_start_refresh_timer(nr_turn_client_ctx *tctx, + nr_turn_stun_ctx *sctx, + UINT4 lifetime) +{ + int _status; + + assert(!tctx->refresh_timer_handle); + + if (lifetime <= TURN_REFRESH_SLACK_SECONDS) { + r_log(NR_LOG_TURN, LOG_ERR, "Too short lifetime specified for turn %u", lifetime); + ABORT(R_BAD_DATA); + } + + if (lifetime > 3600) + lifetime = 3600; + + lifetime -= TURN_REFRESH_SLACK_SECONDS; + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Setting refresh timer for %u seconds", + tctx->label, lifetime); + NR_ASYNC_TIMER_SET(lifetime * 1000, nr_turn_client_refresh_timer_cb, sctx, + &tctx->refresh_timer_handle); + + _status=0; +abort: + if (_status) { + nr_turn_client_failed(tctx); + } + return _status; +} + +static void nr_turn_client_refresh_timer_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + int r,_status; + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Refresh timer fired", + ctx->tctx->label); + + ctx->tctx->refresh_timer_handle=0; + if ((r=nr_turn_stun_ctx_start(ctx))) { + ABORT(r); + } + + _status=0; +abort: + if (_status) { + nr_turn_client_failed(ctx->tctx); + } + return; +} + +static void nr_turn_client_refresh_cb(NR_SOCKET s, int how, void *arg) +{ + int r, _status; + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + /* Save lifetime from the reset */ + UINT4 lifetime = ctx->stun->results.refresh_response.lifetime_secs; + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Refresh succeeded. lifetime=%u", + ctx->tctx->label, lifetime); + + if ((r=nr_turn_client_start_refresh_timer( + ctx->tctx, ctx, lifetime))) + ABORT(r); + + _status=0; + +abort: + if (_status) { + nr_turn_client_failed(ctx->tctx); + } +} + +/* TODO(ekr@rtfm.com): We currently don't support channels. + We might in the future. Mozilla bug 857736 */ +int nr_turn_client_send_indication(nr_turn_client_ctx *ctx, + const UCHAR *msg, size_t len, + int flags, const nr_transport_addr *remote_addr) +{ + int r,_status; + nr_stun_client_send_indication_params params = { { 0 } }; + nr_stun_message *ind = 0; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_FAILED); + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Send indication len=%zu", + ctx->label, len); + + if ((r=nr_turn_client_ensure_perm(ctx, remote_addr))) + ABORT(r); + + if ((r=nr_transport_addr_copy(¶ms.remote_addr, remote_addr))) + ABORT(r); + + params.data.data = (UCHAR*)msg; + params.data.len = len; + + if ((r=nr_stun_build_send_indication(¶ms, &ind))) + ABORT(r); + + if ((r=nr_turn_client_send_stun_request(ctx, ind, flags))) + ABORT(r); + + _status=0; +abort: + nr_stun_message_destroy(&ind); + return(_status); +} + +int nr_turn_client_parse_data_indication(nr_turn_client_ctx *ctx, + nr_transport_addr *source_addr, + UCHAR *msg, size_t len, + UCHAR *newmsg, size_t *newlen, + size_t newsize, + nr_transport_addr *remote_addr) +{ + int r,_status; + nr_stun_message *ind=0; + nr_stun_message_attribute *attr; + nr_turn_permission *perm; + + if (nr_transport_addr_cmp(&ctx->turn_server_addr, source_addr, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(NR_LOG_TURN, LOG_WARNING, + "TURN(%s): Indication from unexpected source addr %s (expected %s)", + ctx->label, source_addr->as_string, ctx->turn_server_addr.as_string); + ABORT(R_REJECTED); + } + + if ((r=nr_stun_message_create2(&ind, msg, len))) + ABORT(r); + if ((r=nr_stun_decode_message(ind, 0, 0))) + ABORT(r); + + if (ind->header.type != NR_STUN_MSG_DATA_INDICATION) + ABORT(R_BAD_ARGS); + + if (!nr_stun_message_has_attribute(ind, NR_STUN_ATTR_XOR_PEER_ADDRESS, &attr)) + ABORT(R_BAD_ARGS); + + if ((r=nr_turn_permission_find(ctx, &attr->u.xor_mapped_address.unmasked, + &perm))) { + if (r == R_NOT_FOUND) { + r_log(NR_LOG_TURN, LOG_WARNING, + "TURN(%s): Indication from peer addr %s with no permission", + ctx->label, attr->u.xor_mapped_address.unmasked.as_string); + } + ABORT(r); + } + + if ((r=nr_transport_addr_copy(remote_addr, + &attr->u.xor_mapped_address.unmasked))) + ABORT(r); + +#if REFRESH_RESERVATION_ON_RECV + if ((r=nr_turn_client_ensure_perm(ctx, remote_addr))) { + ABORT(r); + } +#endif + + if (!nr_stun_message_has_attribute(ind, NR_STUN_ATTR_DATA, &attr)) { + ABORT(R_BAD_DATA); + } + + assert(newsize >= attr->u.data.length); + if (newsize < attr->u.data.length) + ABORT(R_BAD_ARGS); + + memcpy(newmsg, attr->u.data.data, attr->u.data.length); + *newlen = attr->u.data.length; + + _status=0; +abort: + nr_stun_message_destroy(&ind); + return(_status); +} + + + +/* The permissions model is as follows: + + - We keep a list of all the permissions we have ever requested + along with when they were last established. + - Whenever someone sends a packet, we automatically create/ + refresh the permission. + + This means that permissions automatically time out if + unused. + +*/ +int nr_turn_client_ensure_perm(nr_turn_client_ctx *ctx, const nr_transport_addr *addr) +{ + int r, _status; + nr_turn_permission *perm = 0; + UINT8 now; + UINT8 turn_permission_refresh = (TURN_PERMISSION_LIFETIME_SECONDS - + TURN_REFRESH_SLACK_SECONDS) * TURN_USECS_PER_S; + + if ((r=nr_turn_permission_find(ctx, addr, &perm))) { + if (r == R_NOT_FOUND) { + if ((r=nr_turn_permission_create(ctx, addr, &perm))) + ABORT(r); + } + else { + ABORT(r); + } + } + + assert(perm); + + /* Now check that the permission is up-to-date */ + now = r_gettimeint(); + + if ((now - perm->last_used) > turn_permission_refresh) { + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Permission for %s requires refresh", + ctx->label, perm->addr.as_string); + + if ((r=nr_turn_stun_ctx_start(perm->stun))) + ABORT(r); + + perm->last_used = now; /* Update the time now so we don't retry on + next packet */ + } + + _status=0; +abort: + return(_status); +} + +static int nr_turn_permission_create(nr_turn_client_ctx *ctx, const nr_transport_addr *addr, + nr_turn_permission **permp) +{ + int r, _status; + nr_turn_permission *perm = 0; + + assert(ctx->state == NR_TURN_CLIENT_STATE_ALLOCATED); + + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): Creating permission for %s", + ctx->label, addr->as_string); + + if (!(perm = RCALLOC(sizeof(nr_turn_permission)))) + ABORT(R_NO_MEMORY); + + if ((r=nr_transport_addr_copy(&perm->addr, addr))) + ABORT(r); + + perm->last_used = 0; + + if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_PERMISSION_REQUEST, + nr_turn_client_permissions_cb, + nr_turn_client_permission_error_cb, + &perm->stun))) + ABORT(r); + + /* We want to authenticate on the first packet */ + if ((r=nr_turn_stun_set_auth_params(perm->stun, ctx->realm, ctx->nonce))) + ABORT(r); + + if ((r=nr_transport_addr_copy( + &perm->stun->stun->params.permission_request.remote_addr, addr))) + ABORT(r); + STAILQ_INSERT_TAIL(&ctx->permissions, perm, entry); + + *permp = perm; + + _status=0; +abort: + if (_status) { + nr_turn_permission_destroy(&perm); + } + return(_status); +} + + +static int nr_turn_permission_find(nr_turn_client_ctx *ctx, const nr_transport_addr *addr, + nr_turn_permission **permp) +{ + nr_turn_permission *perm; + int _status; + + perm = STAILQ_FIRST(&ctx->permissions); + while (perm) { + if (!nr_transport_addr_cmp(&perm->addr, addr, + NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) + break; + + perm = STAILQ_NEXT(perm, entry); + } + + if (!perm) { + ABORT(R_NOT_FOUND); + } + if (perm->stun->last_error_code == 403) { + ABORT(R_NOT_PERMITTED); + } + *permp = perm; + + _status=0; +abort: + return(_status); +} + +static void nr_turn_client_permissions_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Successfully refreshed permission", + ctx->tctx->label); +} + +/* Note that we don't destroy the nr_turn_stun_ctx. That is owned by the + nr_turn_client_ctx. */ +static int nr_turn_permission_destroy(nr_turn_permission **permp) +{ + nr_turn_permission *perm; + + if (!permp || !*permp) + return(0); + + perm = *permp; + *permp = 0; + + RFREE(perm); + + return(0); +} + +#endif /* USE_TURN */ diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h new file mode 100644 index 0000000000..2049f1bdfb --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h @@ -0,0 +1,161 @@ +/* +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, Mozilla 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. +*/ + + + +#ifndef _turn_client_ctx_h +#define _turn_client_ctx_h + +struct nr_ice_ctx_; + +typedef struct nr_transport_addr_listnode_ { + nr_transport_addr value; + STAILQ_ENTRY(nr_transport_addr_listnode_) entry; +} nr_transport_addr_listnode; +typedef STAILQ_HEAD(nr_transport_addr_listnode_head_, nr_transport_addr_listnode_) nr_transport_addr_listnode_head; + +/* + Represents a single set of STUN transactions, i.e., + Allocate, Refresh, Permission. It automatically handles + permissions and restarts. + */ +typedef struct nr_turn_stun_ctx_ { + struct nr_turn_client_ctx_ *tctx; + int mode; /* From stun_client_ctx.h, NR_TURN_CLIENT_MODE_* */ + int retry_ct; + nr_stun_client_ctx *stun; + char *nonce; + char *realm; + NR_async_cb success_cb; + NR_async_cb error_cb; + int last_error_code; + + nr_transport_addr_listnode_head addresses_tried; + + STAILQ_ENTRY(nr_turn_stun_ctx_) entry; +} nr_turn_stun_ctx; +typedef STAILQ_HEAD(nr_turn_stun_ctx_head_, nr_turn_stun_ctx_) + nr_turn_stun_ctx_head; + +/* Represents a single TURN permission */ +typedef struct nr_turn_permission_ { + nr_transport_addr addr; + 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_FAILED 4 +#define NR_TURN_CLIENT_STATE_CANCELLED 5 +#define NR_TURN_CLIENT_STATE_DEALLOCATING 6 + + 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; + + /* We need access to the socket factory to create new TCP sockets for handling + * STUN/300 responses. */ + /* If we were to require TCP nr_sockets to allow multiple connect calls by + * disconnecting and re-connecting, we could avoid this requirement. */ + struct nr_ice_ctx_* ctx; + + NR_async_cb finished_cb; + void *cb_arg; + + void *connected_timer_handle; + void *refresh_timer_handle; + + // ice telemetry + UINT2 cnt_401s; + UINT2 cnt_403s; + UINT2 cnt_438s; +} nr_turn_client_ctx; + +extern int NR_LOG_TURN; + +int nr_transport_addr_listnode_create(const nr_transport_addr *addr, nr_transport_addr_listnode **listnodep); +void nr_transport_addr_listnode_destroy(nr_transport_addr_listnode **listnode); +int nr_turn_client_ctx_create(const char* label, nr_socket* sock, + const char* username, Data* password, + nr_transport_addr* addr, + struct nr_ice_ctx_* ice_ctx, + nr_turn_client_ctx** ctxp); +int nr_turn_client_ctx_destroy(nr_turn_client_ctx **ctxp); +int nr_turn_client_allocate(nr_turn_client_ctx *ctx, + NR_async_cb finished_cb, void *cb_arg); +int nr_turn_client_get_relayed_address(nr_turn_client_ctx *ctx, + nr_transport_addr *relayed_address); +int nr_turn_client_get_mapped_address(nr_turn_client_ctx *ctx, + nr_transport_addr *mapped_address); +int nr_turn_client_process_response(nr_turn_client_ctx *ctx, + UCHAR *msg, int len, + nr_transport_addr *turn_server_addr); +int nr_turn_client_cancel(nr_turn_client_ctx *ctx); +int nr_turn_client_failed(nr_turn_client_ctx *ctx); +int nr_turn_client_deallocate(nr_turn_client_ctx *ctx); +int nr_turn_client_send_indication(nr_turn_client_ctx *ctx, + const UCHAR *msg, size_t len, + int flags, const nr_transport_addr *remote_addr); +int nr_turn_client_parse_data_indication(nr_turn_client_ctx *ctx, + nr_transport_addr *source_addr, + UCHAR *msg, size_t len, + UCHAR *newmsg, size_t *newlen, + size_t newsize, + nr_transport_addr *remote_addr); +int nr_turn_client_ensure_perm(nr_turn_client_ctx *ctx, + const nr_transport_addr *addr); +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.c b/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.c new file mode 100644 index 0000000000..81f7731d74 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.c @@ -0,0 +1,57 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <stdarg.h> +#include "nr_api.h" +#include "cb_args.h" + +void **nr_pack_cb_args(int ct,...) + { + void **vlist; + va_list ap; + int i; + + va_start(ap,ct); + if(!(vlist=RCALLOC(sizeof(void *)*ct+1))) + abort(); + + for(i=0;i<ct;i++){ + vlist[i]=va_arg(ap, void *); + } + + va_end(ap); + + return(vlist); + } + + + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.h b/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.h new file mode 100644 index 0000000000..83f7831ccc --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _cb_args_h +#define _cb_args_h + +void **nr_pack_cb_args(int ct,...); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.c b/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.c new file mode 100644 index 0000000000..249cfe350f --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.c @@ -0,0 +1,71 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <stdarg.h> +#include <string.h> +#include "nr_api.h" +#include "ice_util.h" + +int nr_concat_strings(char **outp,...) + { + va_list ap; + char *s,*out=0; + int len=0; + int _status; + + va_start(ap,outp); + while(s=va_arg(ap,char *)){ + len+=strlen(s); + } + va_end(ap); + + + if(!(out=RMALLOC(len+1))) + ABORT(R_NO_MEMORY); + + *outp=out; + + va_start(ap,outp); + while(s=va_arg(ap,char *)){ + len=strlen(s); + memcpy(out,s,len); + out+=len; + } + va_end(ap); + + *out=0; + + _status=0; + abort: + return(_status); + } + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.h b/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.h new file mode 100644 index 0000000000..44751edaf8 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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. +*/ + + + +#ifndef _ice_util_h +#define _ice_util_h + +int nr_concat_strings(char **outp,...); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/upstream.diff b/dom/media/webrtc/transport/third_party/nICEr/upstream.diff new file mode 100644 index 0000000000..a664fd9d21 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/upstream.diff @@ -0,0 +1,2449 @@ +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.c src/ice/ice_candidate.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_candidate.c 2012-10-06 08:30:22.000000000 -0700 +@@ -54,36 +54,38 @@ + + #include "stun_client_ctx.h" + #include "stun_server_ctx.h" + #include "turn_client_ctx.h" + #include "ice_ctx.h" + #include "ice_candidate.h" + #include "ice_reg.h" + #include "ice_util.h" + #include "nr_socket_turn.h" + ++static int next_automatic_preference = 224; ++ + static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand); + static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg); +-static void nr_ice_srvrflx_stun_finished_cb(int sock, int how, void *cb_arg); ++static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg); + #ifdef USE_TURN + static int nr_ice_start_relay_turn(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg); +-static void nr_ice_turn_allocated_cb(int sock, int how, void *cb_arg); ++static void nr_ice_turn_allocated_cb(NR_SOCKET sock, int how, void *cb_arg); + #endif /* USE_TURN */ + + char *nr_ice_candidate_type_names[]={0,"host","srflx","prflx","relay",0}; + + int nr_ice_candidate_create(nr_ice_ctx *ctx,char *label,nr_ice_component *comp,nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp) + { + nr_ice_candidate *cand=0; + nr_ice_candidate *tmp=0; + int r,_status; +- ++ + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + if(!(cand->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + cand->state=NR_ICE_CAND_STATE_CREATED; + cand->ctx=ctx; + cand->isock=isock; + cand->osock=osock; + cand->type=ctype; + cand->stun_server=stun_server; +@@ -189,21 +191,21 @@ + if(cand->delay_timer) + NR_async_timer_cancel(cand->delay_timer); + + RFREE(cand->foundation); + RFREE(cand->label); + RFREE(cand); + + return(0); + } + +-void nr_ice_candidate_destroy_cb(int s, int h, void *cb_arg) ++void nr_ice_candidate_destroy_cb(NR_SOCKET s, int h, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + nr_ice_candidate_destroy(&cand); + } + + /* This algorithm is not super-fast, but I don't think we need a hash + table just yet and it produces a small foundation string */ + static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand) + { + nr_ice_foundation *foundation; +@@ -276,22 +278,38 @@ + break; + default: + ABORT(R_INTERNAL); + } + + if(type_preference > 126) + r_log(LOG_ICE,LOG_ERR,"Illegal type preference %d",type_preference); + + + if(r=NR_reg_get2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname, +- &interface_preference)) +- ABORT(r); ++ &interface_preference)) { ++ if (r==R_NOT_FOUND) { ++ if (next_automatic_preference == 1) { ++ r_log(LOG_ICE,LOG_DEBUG,"Out of preference values. Can't assign one for interface %s",cand->base.ifname); ++ ABORT(R_NOT_FOUND); ++ } ++ r_log(LOG_ICE,LOG_DEBUG,"Automatically assigning preference for interface %s->%d",cand->base.ifname, ++ next_automatic_preference); ++ if (r=NR_reg_set2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,next_automatic_preference)){ ++ ABORT(r); ++ } ++ interface_preference=next_automatic_preference; ++ next_automatic_preference--; ++ } ++ else { ++ ABORT(r); ++ } ++ } + + cand->priority= + (type_preference << 24) | + (interface_preference << 16) | + (stun_priority << 8) | + (256 - cand->component_id); + + /* S 4.1.2 */ + assert(cand->priority>=1&&cand->priority<=2147483647); + +@@ -306,21 +324,22 @@ + + 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; + cand->state=NR_ICE_CAND_STATE_INITIALIZED; +- ready_cb(0,0,cb_arg); ++ // Post this so that it doesn't happen in-line ++ NR_ASYNC_SCHEDULE(ready_cb,cb_arg); + break; + #ifdef USE_TURN + case RELAYED: + if(r=nr_ice_start_relay_turn(cand,ready_cb,cb_arg)) + ABORT(r); + ABORT(R_WOULDBLOCK); + break; + #endif /* USE_TURN */ + case SERVER_REFLEXIVE: + /* Need to start stun */ +@@ -333,21 +352,21 @@ + ABORT(R_INTERNAL); + } + + _status=0; + abort: + if(_status && _status!=R_WOULDBLOCK) + cand->state=NR_ICE_CAND_STATE_FAILED; + return(_status); + } + +-static void nr_ice_srvrflx_start_stun_timer_cb(int s, int how, void *cb_arg) ++static void nr_ice_srvrflx_start_stun_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->delay_timer=0; + + /* TODO: if the response is a BINDING-ERROR-RESPONSE, then restart + * TODO: using NR_STUN_CLIENT_MODE_BINDING_REQUEST because the + * TODO: server may not have understood the 0.96-style request */ + if(r=nr_stun_client_start(cand->u.srvrflx.stun, NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96, nr_ice_srvrflx_stun_finished_cb, cand)) +@@ -387,21 +406,21 @@ + + _status=0; + abort: + if(_status){ + cand->state=NR_ICE_CAND_STATE_FAILED; + } + return(_status); + } + + #ifdef USE_TURN +-static void nr_ice_start_relay_turn_timer_cb(int s, int how, void *cb_arg) ++static void nr_ice_start_relay_turn_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + int i; + + cand->delay_timer=0; + + if(r=nr_turn_client_allocate(cand->u.relayed.turn, cand->u.relayed.server->username, cand->u.relayed.server->password, cand->u.relayed.server->bandwidth_kbps, cand->u.relayed.server->lifetime_secs, nr_ice_turn_allocated_cb, cand)) + ABORT(r); + +@@ -443,21 +462,21 @@ + + _status=0; + abort: + if(_status){ + cand->state=NR_ICE_CAND_STATE_FAILED; + } + return(_status); + } + #endif /* USE_TURN */ + +-static void nr_ice_srvrflx_stun_finished_cb(int sock, int how, void *cb_arg) ++static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg) + { + int _status; + nr_ice_candidate *cand=cb_arg; + + /* Deregister to suppress duplicates */ + if(cand->u.srvrflx.stun_handle){ /* This test because we might have failed before CB registered */ + nr_ice_socket_deregister(cand->isock,cand->u.srvrflx.stun_handle); + cand->u.srvrflx.stun_handle=0; + } + +@@ -481,40 +500,40 @@ + } + _status = 0; + abort: + if(_status){ + cand->state=NR_ICE_CAND_STATE_FAILED; + cand->done_cb(0,0,cand->cb_arg); + } + } + + #ifdef USE_TURN +-static void nr_ice_turn_allocated_cb(int s, int how, void *cb_arg) ++static void nr_ice_turn_allocated_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_ice_candidate *cand=cb_arg; + nr_turn_client_ctx *turn=cand->u.relayed.turn; + int i; + char *label; + + /* Deregister to suppress duplicates */ + if(cand->u.relayed.turn_handle){ /* This test because we might have failed before CB registered */ + nr_ice_socket_deregister(cand->isock,cand->u.relayed.turn_handle); + cand->u.relayed.turn_handle=0; + } + + switch(turn->state){ + /* OK, we should have a mapped address */ + case NR_TURN_CLIENT_STATE_ALLOCATED: + /* switch candidate from TURN mode to STUN mode */ + +- if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|",turn->relay_addr.as_string,")",0)) ++ if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|",turn->relay_addr.as_string,")",NULL)) + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Switching from TURN (%s) to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label); + + /* Copy out mapped address and relay address */ + nr_transport_addr_copy(&turn->relay_addr, &cand->u.relayed.turn->stun_ctx[NR_TURN_CLIENT_PHASE_ALLOCATE_REQUEST2]->results.allocate_response2.relay_addr); + nr_transport_addr_copy(&cand->addr, &turn->relay_addr); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-CANDIDATE(%s): base=%s, candidate=%s", cand->label, cand->base.as_string, cand->addr.as_string); + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.h src/ice/ice_candidate.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_candidate.h 2012-10-06 08:30:22.000000000 -0700 +@@ -41,21 +41,22 @@ + + typedef enum {HOST=1, SERVER_REFLEXIVE, PEER_REFLEXIVE, RELAYED} nr_ice_candidate_type; + + struct nr_ice_candidate_ { + char *label; + int state; + #define NR_ICE_CAND_STATE_CREATED 1 + #define NR_ICE_CAND_STATE_INITIALIZING 2 + #define NR_ICE_CAND_STATE_INITIALIZED 3 + #define NR_ICE_CAND_STATE_FAILED 4 +-#define NR_ICE_CAND_PEER_CANDIDATE 10 ++#define NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED 9 ++#define NR_ICE_CAND_PEER_CANDIDATE_PAIRED 10 + struct nr_ice_ctx_ *ctx; + nr_ice_socket *isock; /* The socket to read from + (it contains all other candidates + on this socket) */ + nr_socket *osock; /* The socket to write to */ + nr_ice_media_stream *stream; /* The media stream this is associated with */ + nr_ice_component *component; /* The component this is associated with */ + nr_ice_candidate_type type; /* The type of the candidate (S 4.1.1) */ + UCHAR component_id; /* The component id (S 4.1.2.1) */ + nr_transport_addr addr; /* The advertised address; +@@ -89,21 +90,21 @@ + TAILQ_ENTRY(nr_ice_candidate_) entry_comp; + }; + + extern char *nr_ice_candidate_type_names[]; + + + int nr_ice_candidate_create(struct nr_ice_ctx_ *ctx,char *label, nr_ice_component *component, nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp); + int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg); + int nr_ice_candidate_process_stun(nr_ice_candidate *cand, UCHAR *msg, int len, nr_transport_addr *faddr); + int nr_ice_candidate_destroy(nr_ice_candidate **candp); +-void nr_ice_candidate_destroy_cb(int s, int h, void *cb_arg); ++void nr_ice_candidate_destroy_cb(NR_SOCKET s, int h, void *cb_arg); + int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen); + int nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *attr,nr_ice_media_stream *stream,nr_ice_candidate **candp); + int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp); + int nr_ice_candidate_compute_priority(nr_ice_candidate *cand); + + #ifdef __cplusplus + } + #endif /* __cplusplus */ + #endif + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.c src/ice/ice_candidate_pair.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_candidate_pair.c 2012-10-06 08:30:22.000000000 -0700 +@@ -37,21 +37,21 @@ + #include <assert.h> + #include <string.h> + #include <nr_api.h> + #include "ice_ctx.h" + #include "ice_util.h" + #include "ice_codeword.h" + #include "stun.h" + + static char *nr_ice_cand_pair_states[]={"UNKNOWN","FROZEN","WAITING","IN_PROGRESS","FAILED","SUCCEEDED","CANCELLED"}; + +-static void nr_ice_candidate_pair_restart_stun_controlled_cb(int s, int how, void *cb_arg); ++static void nr_ice_candidate_pair_restart_stun_controlled_cb(NR_SOCKET s, int how, void *cb_arg); + static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair, + nr_ice_candidate *lcand, nr_ice_candidate *rcand); + + int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp) + { + nr_ice_cand_pair *pair=0; + UINT8 o_priority, a_priority; + char *lufrag,*rufrag; + char *lpwd,*rpwd; + char *l2ruser=0,*r2lpass=0; +@@ -61,21 +61,21 @@ + UINT8 t_priority; + + if(!(pair=RCALLOC(sizeof(nr_ice_cand_pair)))) + ABORT(R_NO_MEMORY); + + pair->pctx=pctx; + + nr_ice_candidate_pair_compute_codeword(pair,lcand,rcand); + + if(r=nr_concat_strings(&pair->as_string,pair->codeword,"|",lcand->addr.as_string,"|", +- rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")",0)) ++ rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")", NULL)) + ABORT(r); + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_FROZEN); + pair->local=lcand; + pair->remote=rcand; + + /* Priority computation S 5.7.2 */ + if(pctx->ctx->flags & NR_ICE_CTX_FLAGS_OFFERER) + { + assert(!(pctx->ctx->flags & NR_ICE_CTX_FLAGS_ANSWERER)); +@@ -87,21 +87,21 @@ + o_priority=rcand->priority; + a_priority=lcand->priority; + } + pair->priority=(MIN(o_priority, a_priority))<<32 | + (MAX(o_priority, a_priority))<<1 | (o_priority > a_priority?0:1); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Pairing candidate %s (%x):%s (%x) priority=%llu (%llx) codeword=%s",pctx->ctx->label,lcand->addr.as_string,lcand->priority,rcand->addr.as_string,rcand->priority,pair->priority,pair->priority,pair->codeword); + + /* Foundation */ + if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|", +- rcand->foundation,0)) ++ rcand->foundation,NULL)) + ABORT(r); + + + /* OK, now the STUN data */ + lufrag=lcand->stream->ufrag?lcand->stream->ufrag:pctx->ctx->ufrag; + lpwd=lcand->stream->pwd?lcand->stream->pwd:pctx->ctx->pwd; + rufrag=rcand->stream->ufrag?rcand->stream->ufrag:pctx->peer_ufrag; + rpwd=rcand->stream->pwd?rcand->stream->pwd:pctx->peer_pwd; + + +@@ -110,39 +110,39 @@ + + /* Make a bogus candidate to compute a theoretical peer reflexive + * priority per S 7.1.1.1 */ + memcpy(&tmpcand, lcand, sizeof(tmpcand)); + tmpcand.type = PEER_REFLEXIVE; + if (r=nr_ice_candidate_compute_priority(&tmpcand)) + ABORT(r); + t_priority = tmpcand.priority; + + /* Our sending context */ +- if(r=nr_concat_strings(&l2ruser,lufrag,":",rufrag,0)) ++ if(r=nr_concat_strings(&l2ruser,lufrag,":",rufrag,NULL)) + ABORT(r); + if(r=nr_stun_client_ctx_create(pair->as_string, + lcand->osock, + &rcand->addr,RTO,&pair->stun_client)) + ABORT(r); + if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(l2ruser))) + ABORT(R_NO_MEMORY); + if(r=r_data_make(&pair->stun_client->params.ice_binding_request.password,(UCHAR *)lpwd,strlen(lpwd))) + ABORT(r); + pair->stun_client->params.ice_binding_request.priority=t_priority; + pair->stun_client->params.ice_binding_request.control = pctx->controlling? + NR_ICE_CONTROLLING:NR_ICE_CONTROLLED; + + pair->stun_client->params.ice_binding_request.tiebreaker=pctx->tiebreaker; + + /* Our receiving username/passwords. Stash these for later + injection into the stun server ctx*/ +- if(r=nr_concat_strings(&pair->r2l_user,rufrag,":",lufrag,0)) ++ if(r=nr_concat_strings(&pair->r2l_user,rufrag,":",lufrag,NULL)) + ABORT(r); + if(!(r2lpass=r_strdup(rpwd))) + ABORT(R_NO_MEMORY); + INIT_DATA(pair->r2l_pwd,(UCHAR *)r2lpass,strlen(r2lpass)); + + *pairp=pair; + + _status=0; + abort: + RFREE(l2ruser); +@@ -178,21 +178,21 @@ + + int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + assert(pair->state==NR_ICE_PAIR_STATE_FROZEN); + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING); + + return(0); + } + +-static void nr_ice_candidate_pair_stun_cb(int s, int how, void *cb_arg) ++static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_ice_cand_pair *pair=cb_arg,*orig_pair; + nr_ice_candidate *cand=0; + nr_stun_message *sres; + nr_transport_addr *request_src; + nr_transport_addr *request_dst; + nr_transport_addr *response_src; + nr_transport_addr response_dst; + nr_stun_message_attribute *attr; +@@ -457,32 +457,47 @@ + abort: + return(_status); + } + + int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state) + { + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): setting pair %s to %s", + pctx->label,pair->as_string,nr_ice_cand_pair_states[state]); +- pair->state=state; + +- if(pctx->state!=NR_ICE_PAIR_STATE_WAITING){ ++ /* NOTE: This function used to reference pctx->state instead of ++ pair->state and the assignment to pair->state was at the top ++ of this function. Because pctx->state was never changed, this seems to have ++ been a typo. The natural logic is "if the state changed ++ decrement the counter" so this implies we should be checking ++ the pair state rather than the pctx->state. ++ ++ This didn't cause big problems because waiting_pairs was only ++ used for pacing, so the pacing just was kind of broken. ++ ++ This note is here as a reminder until we do more testing ++ and make sure that in fact this was a typo. ++ */ ++ if(pair->state!=NR_ICE_PAIR_STATE_WAITING){ + if(state==NR_ICE_PAIR_STATE_WAITING) + pctx->waiting_pairs++; + } + else{ + if(state!=NR_ICE_PAIR_STATE_WAITING) + pctx->waiting_pairs--; + + assert(pctx->waiting_pairs>=0); + } ++ pair->state=state; ++ ++ + if(pair->state==NR_ICE_PAIR_STATE_FAILED){ + if(r=nr_ice_component_failed_pair(pair->remote->component, pair)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +@@ -505,42 +520,42 @@ + break; + } + + c1=TAILQ_NEXT(c1,entry); + } + if(!c1) TAILQ_INSERT_TAIL(head,pair,entry); + + return(0); + } + +-void nr_ice_candidate_pair_restart_stun_nominated_cb(int s, int how, void *cb_arg) ++void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_cand_pair *pair=cb_arg; + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s):%d: Restarting pair %s as nominated",pair->pctx->label,pair->local->stream->label,pair->remote->component->component_id,pair->as_string); + + nr_stun_client_reset(pair->stun_client); + pair->stun_client->params.ice_binding_request.control=NR_ICE_CONTROLLING; + + if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair)) + ABORT(r); + + if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request)) + ABORT(r); + + _status=0; + abort: + return; + } + +-static void nr_ice_candidate_pair_restart_stun_controlled_cb(int s, int how, void *cb_arg) ++static void nr_ice_candidate_pair_restart_stun_controlled_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_cand_pair *pair=cb_arg; + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s):%d: Restarting pair %s as CONTROLLED",pair->pctx->label,pair->local->stream->label,pair->remote->component->component_id,pair->as_string); + + nr_stun_client_reset(pair->stun_client); + pair->stun_client->params.ice_binding_request.control=NR_ICE_CONTROLLED; + + if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_BINDING_REQUEST,nr_ice_candidate_pair_stun_cb,pair)) +@@ -556,21 +571,21 @@ + + + + static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair, + nr_ice_candidate *lcand, nr_ice_candidate *rcand) + { + int r,_status; + char *as_string=0; + + if(r=nr_concat_strings(&as_string,lcand->addr.as_string,"|", +- rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")",0)) ++ rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")",NULL)) + ABORT(r); + + nr_ice_compute_codeword(as_string,strlen(as_string),pair->codeword); + + _status=0; + abort: + RFREE(as_string); + return; + } + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.h src/ice/ice_candidate_pair.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_candidate_pair.h 2012-10-06 08:30:22.000000000 -0700 +@@ -72,18 +72,18 @@ + + int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp); + int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state); + int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out); + int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair); +-void nr_ice_candidate_pair_restart_stun_nominated_cb(int s, int how, void *cb_arg); ++void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg); + int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp); + + #ifdef __cplusplus + } + #endif /* __cplusplus */ + #endif + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_component.c src/ice/ice_component.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_component.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_component.c 2012-10-06 08:30:22.000000000 -0700 +@@ -451,21 +451,21 @@ + if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){ + r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label); + *error=487; + ABORT(R_BAD_DATA); + } + pcand->priority=attr->u.priority; +- pcand->state=NR_ICE_CAND_PEER_CANDIDATE; ++ pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED;; + TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp); + + if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand, + &pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN); + + if(r=nr_ice_candidate_pair_insert(&comp->stream->check_list,pair)) { +@@ -563,30 +563,38 @@ + break; + } + + /* PAIR with each peer*/ + if(TAILQ_EMPTY(&pcomp->candidates)) { + /* can happen if our peer proposes no (or all bogus) candidates */ + goto next_cand; + } + pcand=TAILQ_FIRST(&pcomp->candidates); + while(pcand){ +- nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword); +- r_log(LOG_ICE,LOG_DEBUG,"Examining peer candidate %s:%s",codeword,pcand->label); +- +- if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair)) +- ABORT(r); +- +- if(r=nr_ice_candidate_pair_insert(&pcomp->stream->check_list, +- pair)) +- ABORT(r); ++ /* Only pair peer candidates which have not yet been paired. ++ This allows "trickle ICE". (Not yet standardized, but ++ part of WebRTC). ++ ++ TODO(ekr@rtfm.com): Add refernece to the spec when there ++ is one. ++ */ ++ if (pcand->state = NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED) { ++ nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword); ++ r_log(LOG_ICE,LOG_DEBUG,"Examining peer candidate %s:%s",codeword,pcand->label); ++ ++ if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair)) ++ ABORT(r); + ++ if(r=nr_ice_candidate_pair_insert(&pcomp->stream->check_list, ++ pair)) ++ ABORT(r); ++ } + pcand=TAILQ_NEXT(pcand,entry_comp); + } + + if(!pair) + ABORT(R_INTERNAL); + + /* Add the stun username/password pair from the last pair (any + would do) to the stun contexts */ + isock=STAILQ_FIRST(&lcomp->sockets); + while(isock){ +@@ -594,20 +602,28 @@ + pair->r2l_user,&pair->r2l_pwd,nr_ice_component_stun_server_cb,pcomp)) + ABORT(r); + + isock=STAILQ_NEXT(isock,entry); + } + + next_cand: + lcand=TAILQ_NEXT(lcand,entry_comp); + } + ++ /* Mark all peer candidates as paired */ ++ pcand=TAILQ_FIRST(&pcomp->candidates); ++ while(pcand){ ++ pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED; ++ ++ pcand=TAILQ_NEXT(pcand,entry_comp); ++ } ++ + _status=0; + abort: + return(_status); + } + + int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) + { + int r,_status; + int fire_cb=0; + nr_ice_cand_pair *p2; +@@ -616,32 +632,32 @@ + fire_cb=1; + + /* Are we changing what the nominated pair is? */ + if(comp->nominated){ + if(comp->nominated->priority > pair->priority) + return(0); + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): replacing pair %s with pair %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->as_string,pair->as_string); + } + + /* Set the new nominated pair */ +- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): nominated pair is %s (0x%x)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,(int)pair); ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): nominated pair is %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,pair); + comp->state=NR_ICE_COMPONENT_NOMINATED; + comp->nominated=pair; + comp->active=pair; + +- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling all pairs but %s (0x%x)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,(int)pair); ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling all pairs but %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,pair); + + /* OK, we need to cancel off everything on this component */ + p2=TAILQ_FIRST(&comp->stream->check_list); + while(p2){ + if((p2 != pair) && (p2->remote->component->component_id == comp->component_id)){ +- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling pair %s (0x%x)",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->as_string,(int)p2); ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling pair %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->as_string,p2); + + if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2)) + ABORT(r); + } + + p2=TAILQ_NEXT(p2,entry); + } + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id); + + if(r=nr_ice_media_stream_component_nominated(comp->stream,comp)) +@@ -734,21 +750,21 @@ + ABORT(r); + } + + _status=0; + abort: + RFREE(pairs); + return(_status); + } + + +-static void nr_ice_component_keepalive_cb(int s, int how, void *cb_arg) ++static void nr_ice_component_keepalive_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_component *comp=cb_arg; + UINT4 keepalive_timeout; + + assert(comp->keepalive_ctx); + + if(NR_reg_get_uint4(NR_ICE_REG_KEEPALIVE_TIMER,&keepalive_timeout)){ + keepalive_timeout=15000; /* Default */ + } + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.c src/ice/ice_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -56,21 +56,21 @@ + #include "util.h" + + + int LOG_ICE = 0; + + static int nr_ice_random_string(char *str, int len); + static int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out); + #ifdef USE_TURN + static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out); + #endif /* USE_TURN */ +-static void nr_ice_ctx_destroy_cb(int s, int how, void *cb_arg); ++static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg); + + int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out) + { + int r,_status; + nr_ice_stun_server *servers = 0; + int i; + NR_registry child; + char *addr=0; + UINT2 port; + in_addr_t addr_int; +@@ -271,21 +271,21 @@ + *ctxp=ctx; + + _status=0; + abort: + if(_status) + nr_ice_ctx_destroy_cb(0,0,ctx); + + return(_status); + } + +-static void nr_ice_ctx_destroy_cb(int s, int how, void *cb_arg) ++static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_ctx *ctx=cb_arg; + nr_ice_foundation *f1,*f2; + nr_ice_media_stream *s1,*s2; + int i; + nr_ice_stun_id *id1,*id2; + + RFREE(ctx->label); + + RFREE(ctx->stun_servers); +@@ -323,21 +323,21 @@ + if(!ctxp || !*ctxp) + return(0); + + NR_ASYNC_SCHEDULE(nr_ice_ctx_destroy_cb,*ctxp); + + *ctxp=0; + + return(0); + } + +-void nr_ice_initialize_finished_cb(int s, int h, void *cb_arg) ++void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg) + { + nr_ice_ctx *ctx=cb_arg; + + /* r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Candidate %s %s",ctx->label, + cand->label, cand->state==NR_ICE_CAND_STATE_INITIALIZED?"INITIALIZED":"FAILED"); + */ + ctx->uninitialized_candidates--; + + if(ctx->uninitialized_candidates==0){ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): All candidates initialized",ctx->label); +@@ -368,21 +368,22 @@ + stream=STAILQ_FIRST(&ctx->streams); + while(stream){ + if(r=nr_ice_media_stream_initialize(ctx,stream)) + ABORT(r); + + stream=STAILQ_NEXT(stream,entry); + } + + if(ctx->uninitialized_candidates) + ABORT(R_WOULDBLOCK); +- ++ ++ + _status=0; + abort: + return(_status); + } + + int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp) + { + int r,_status; + + if(r=nr_ice_media_stream_create(ctx,label,components,streamp)) +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.h src/ice/ice_ctx.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_ctx.h 2012-10-06 08:30:22.000000000 -0700 +@@ -92,23 +92,23 @@ + UCHAR id[12]; + + STAILQ_ENTRY(nr_ice_stun_id_) entry; + } nr_ice_stun_id; + + typedef STAILQ_HEAD(nr_ice_stun_id_head_,nr_ice_stun_id_) nr_ice_stun_id_head; + + struct nr_ice_ctx_ { + UINT4 flags; + int state; +-#define NR_ICE_STATE_CREATED 1 +-#define NR_ICE_STATE_INITIALIZING 2 +-#define NR_ICE_STATE_INITIALIZED 3 ++#define NR_ICE_STATE_CREATED 1 ++#define NR_ICE_STATE_INITIALIZING 2 ++#define NR_ICE_STATE_INITIALIZED 3 + char *label; + + char *ufrag; + char *pwd; + + UINT4 Ta; + + nr_ice_stun_server *stun_servers; /* The list of stun servers */ + int stun_server_ct; + nr_ice_turn_server *turn_servers; /* The list of turn servers */ +@@ -133,21 +133,21 @@ + + int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp); + #define NR_ICE_CTX_FLAGS_OFFERER 1 + #define NR_ICE_CTX_FLAGS_ANSWERER (1<<1) + #define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION (1<<2) + #define NR_ICE_CTX_FLAGS_LITE (1<<3) + + int nr_ice_ctx_destroy(nr_ice_ctx **ctxp); + int nr_ice_initialize(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg); + int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand); +-void nr_ice_initialize_finished_cb(int s, int h, void *cb_arg); ++void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg); + int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp); + int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp); + int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len); + int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]); + int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg); + int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx); + + extern int LOG_ICE; + + #ifdef __cplusplus +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.c src/ice/ice_media_stream.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_media_stream.c 2012-10-06 08:30:22.000000000 -0700 +@@ -35,21 +35,21 @@ + static char *RCSSTRING __UNUSED__="$Id: ice_media_stream.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + + #include <string.h> + #include <assert.h> + #include <nr_api.h> + #include <r_assoc.h> + #include <async_timer.h> + #include "ice_ctx.h" + + static char *nr_ice_media_stream_states[]={"INVALID", +- "FROZEN","ACTIVE","COMPLETED","FAILED" ++ "UNPAIRED","FROZEN","ACTIVE","COMPLETED","FAILED" + }; + + int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state); + + int nr_ice_media_stream_create(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp) + { + int r,_status; + nr_ice_media_stream *stream=0; + nr_ice_component *comp=0; + int i; +@@ -66,29 +66,29 @@ + for(i=0;i<components;i++){ + /* component-id must be > 0, so increment by 1 */ + if(r=nr_ice_component_create(stream, i+1, &comp)) + ABORT(r); + + } + + TAILQ_INIT(&stream->check_list); + + stream->component_ct=components; +- ++ stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED; + *streamp=stream; + + _status=0; + abort: + if(_status){ + nr_ice_media_stream_destroy(&stream); + } +- return(_status); ++ return(_status); + } + + int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp) + { + nr_ice_media_stream *stream; + nr_ice_component *c1,*c2; + nr_ice_cand_pair *p1,*p2; + if(!streamp || !*streamp) + return(0); + +@@ -200,85 +200,148 @@ + if(attrs){ + for(index=0;index<attrct;index++){ + RFREE(attrs[index]); + } + RFREE(attrs); + } + } + return(_status); + } + ++ ++/* Get a default candidate per 4.1.4 */ ++int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp) ++ { ++ int _status; ++ nr_ice_component *comp; ++ nr_ice_candidate *cand; ++ nr_ice_candidate *best_cand = NULL; ++ ++ comp=STAILQ_FIRST(&stream->components); ++ while(comp){ ++ if (comp->component_id == component) ++ break; ++ ++ comp=STAILQ_NEXT(comp,entry); ++ } ++ ++ if (!comp) ++ ABORT(R_NOT_FOUND); ++ ++ /* We have the component. Now find the "best" candidate, making ++ use of the fact that more "reliable" candidate types have ++ higher numbers. So, we sort by type and then priority within ++ type ++ */ ++ cand=TAILQ_FIRST(&comp->candidates); ++ while(cand){ ++ if (!best_cand) { ++ best_cand = cand; ++ } ++ else { ++ if (best_cand->type < cand->type) { ++ best_cand = cand; ++ } else if (best_cand->type == cand->type) { ++ if (best_cand->priority < cand->priority) ++ best_cand = cand; ++ } ++ } ++ ++ cand=TAILQ_NEXT(cand,entry_comp); ++ } ++ ++ /* No candidates */ ++ if (!best_cand) ++ ABORT(R_NOT_FOUND); ++ ++ *candp = best_cand; ++ ++ _status=0; ++ abort: ++ return(_status); ++ } ++ ++ + int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream) + { + int r,_status; + nr_ice_component *pcomp,*lcomp; + + pcomp=STAILQ_FIRST(&pstream->components); + lcomp=STAILQ_FIRST(&lstream->components); + while(pcomp){ + if(r=nr_ice_component_pair_candidates(pctx,lcomp,pcomp)) + ABORT(r); +- ++ + lcomp=STAILQ_NEXT(lcomp,entry); + pcomp=STAILQ_NEXT(pcomp,entry); + }; + ++ if (pstream->ice_state == NR_ICE_MEDIA_STREAM_UNPAIRED) { ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): unfreezing stream %s",pstream->pctx->label,pstream->label); ++ pstream->ice_state = NR_ICE_MEDIA_STREAM_CHECKS_FROZEN; ++ } ++ + _status=0; + abort: + return(_status); + } + + /* S 5.8 -- run the highest priority WAITING pair or if not available + FROZEN pair */ +-static void nr_ice_media_stream_check_timer_cb(int s, int h, void *cb_arg) ++static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg) + { + int r,_status; + nr_ice_media_stream *stream=cb_arg; + nr_ice_cand_pair *pair; + int timer_val; + + assert(stream->pctx->active_streams!=0); + + timer_val=stream->pctx->ctx->Ta*stream->pctx->active_streams; + ++ if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) { ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): bogus state for stream %s",stream->pctx->label,stream->label); ++ } + assert(stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer expired for media stream %s",stream->pctx->label,stream->label); + stream->timer=0; + +- + /* Find the highest priority WAITING check and move it to RUNNING */ + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_WAITING) + break; + pair=TAILQ_NEXT(pair,entry); + } + + /* Hmmm... No WAITING. Let's look for FROZEN */ + if(!pair){ + pair=TAILQ_FIRST(&stream->check_list); +- ++ + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_FROZEN){ + if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair)) + ABORT(r); + break; + } + pair=TAILQ_NEXT(pair,entry); + } + } + + if(pair){ + nr_ice_candidate_pair_start(pair->pctx,pair); /* Ignore failures */ + NR_ASYNC_TIMER_SET(timer_val,nr_ice_media_stream_check_timer_cb,cb_arg,&stream->timer); + } ++ /* TODO(ekr@rtfm.com): Report on the special case where there are no checks to ++ run at all */ + _status=0; + abort: + return; + } + + + /* Start checks for this media stream (aka check list) */ + int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + assert(stream->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FROZEN); +@@ -476,21 +539,23 @@ + /* All done... */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): all components have nominated candidate pairs",stream->pctx->label,stream->label); + nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED); + + /* Cancel our timer */ + if(stream->timer){ + NR_async_timer_cancel(stream->timer); + stream->timer=0; + } + +- stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream); ++ if (stream->pctx->handler) { ++ stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream); ++ } + + /* Now tell the peer_ctx that we're done */ + if(r=nr_ice_peer_ctx_stream_done(stream->pctx,stream)) + ABORT(r); + + done: + _status=0; + abort: + return(_status); + } +@@ -515,21 +580,23 @@ + + p2=TAILQ_NEXT(p2,entry); + } + + /* Cancel our timer */ + if(stream->timer){ + NR_async_timer_cancel(stream->timer); + stream->timer=0; + } + +- stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream); ++ if (stream->pctx->handler) { ++ stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream); ++ } + + /* Now tell the peer_ctx that we're done */ + if(r=nr_ice_peer_ctx_stream_done(stream->pctx,stream)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.h src/ice/ice_media_stream.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_media_stream.h 2012-10-06 08:30:22.000000000 -0700 +@@ -45,40 +45,43 @@ + struct nr_ice_peer_ctx_ *pctx; + + struct nr_ice_media_stream_ *local_stream; /* used when this is a peer */ + int component_ct; + nr_ice_component_head components; + + char *ufrag; /* ICE username */ + char *pwd; /* ICE password */ + + int ice_state; +-#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 1 +-#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 2 +-#define NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED 3 +-#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 4 ++ ++#define NR_ICE_MEDIA_STREAM_UNPAIRED 1 ++#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 2 ++#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 3 ++#define NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED 4 ++#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 5 + + nr_ice_cand_pair_head check_list; + void *timer; /* Check list periodic timer */ + + // nr_ice_cand_pair_head valid_list; +- ++ + STAILQ_ENTRY(nr_ice_media_stream_) entry; + }; + + typedef STAILQ_HEAD(nr_ice_media_stream_head_,nr_ice_media_stream_) nr_ice_media_stream_head; + + int nr_ice_media_stream_create(struct nr_ice_ctx_ *ctx,char *label, int components, nr_ice_media_stream **streamp); + int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp); + int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr); + int nr_ice_media_stream_initialize(struct nr_ice_ctx_ *ctx, nr_ice_media_stream *stream); + int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp,int *attrctp); ++int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp); + int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream); + int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); + int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); + int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation); + int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out); + int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component); + int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component); + int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state); + int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp); + int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx,nr_ice_media_stream *str, int component, UCHAR *data, int len); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_parser.c src/ice/ice_parser.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_parser.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_parser.c 2012-10-06 08:30:22.000000000 -0700 +@@ -35,20 +35,21 @@ + static char *RCSSTRING __UNUSED__="$Id: ice_parser.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + + #include <csi_platform.h> + #include <sys/types.h> + #ifdef WIN32 + #include <winsock2.h> + #else + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> ++#include <strings.h> + #endif + #include <string.h> + #include <assert.h> + #include <ctype.h> + #include "nr_api.h" + #include "ice_ctx.h" + #include "ice_candidate.h" + #include "ice_reg.h" + + static void +@@ -125,21 +126,21 @@ + char *rel_addr=0; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + + if(!(cand->label=r_strdup(orig))) + ABORT(R_NO_MEMORY); + + cand->ctx=ctx; + cand->isock=0; +- cand->state=NR_ICE_CAND_PEER_CANDIDATE; ++ cand->state=NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED; + cand->stream=stream; + skip_whitespace(&str); + + /* Candidate attr */ + if (strncasecmp(str, "candidate:", 10)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 10); + if (*str == '\0') + ABORT(R_BAD_DATA); +@@ -311,26 +312,31 @@ + /* it's expected to be at EOD at this point */ + + break; + default: + ABORT(R_INTERNAL); + break; + } + + skip_whitespace(&str); + +- assert(strlen(str) == 0); +- ++ /* This used to be an assert, but we don't want to exit on invalid ++ remote data */ ++ if (strlen(str) != 0) { ++ ABORT(R_BAD_DATA); ++ } ++ + *candp=cand; + + _status=0; + abort: ++ /* TODO(ekr@rtfm.com): Fix memory leak if we have a parse error */ + if (_status) + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Error parsing attribute: %s",ctx->label,orig); + + RFREE(connection_address); + RFREE(rel_addr); + return(_status); + } + + + int +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.c src/ice/ice_peer_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_peer_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -35,33 +35,35 @@ + static char *RCSSTRING __UNUSED__="$Id: ice_peer_ctx.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + + #include <string.h> + #include <assert.h> + #include <nr_api.h> + #include "ice_ctx.h" + #include "ice_peer_ctx.h" + #include "nr_crypto.h" + #include "async_timer.h" + +-static void nr_ice_peer_ctx_destroy_cb(int s, int how, void *cb_arg); ++static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg); ++static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct); ++static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate); + + int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp) + { + int r,_status; + nr_ice_peer_ctx *pctx=0; + + if(!(pctx=RCALLOC(sizeof(nr_ice_peer_ctx)))) + ABORT(R_NO_MEMORY); + + if(!(pctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); +- ++ + pctx->ctx=ctx; + pctx->handler=handler; + + /* Decide controlling vs. controlled */ + if(ctx->flags & NR_ICE_CTX_FLAGS_LITE){ + if(pctx->peer_lite){ + r_log(LOG_ICE,LOG_ERR,"Both sides are ICE-Lite"); + ABORT(R_BAD_DATA); + } + +@@ -88,85 +90,177 @@ + nr_ice_peer_ctx_destroy_cb(0,0,pctx); + } + return(_status); + } + + + + int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct) + { + nr_ice_media_stream *pstream=0; +- nr_ice_candidate *cand=0; + nr_ice_component *comp,*comp2; + int r,_status; +- int i,j; + +- /* Note: use component_ct from our own stream since components other +- than this offered by the other side are unusable */ ++ /* ++ Note: use component_ct from our own stream since components other ++ than this offered by the other side are unusable */ + if(r=nr_ice_media_stream_create(pctx->ctx,stream->label,stream->component_ct,&pstream)) + ABORT(r); +- +- /* Match up the local and remote components */ ++ ++ /* Match up the local and remote components */ + comp=STAILQ_FIRST(&stream->components); + comp2=STAILQ_FIRST(&pstream->components); + while(comp){ + comp2->local_component=comp; + + comp=STAILQ_NEXT(comp,entry); + comp2=STAILQ_NEXT(comp2,entry); + } +- + +- pstream->ice_state=NR_ICE_MEDIA_STREAM_CHECKS_FROZEN; + pstream->local_stream=stream; + pstream->pctx=pctx; + ++ if (r=nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct)) ++ ABORT(r); ++ ++ ++ STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry); ++ ++ _status=0; ++ abort: ++ return(_status); ++ } ++ ++static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct) ++ { ++ int r; ++ int i; ++ + for(i=0;i<attr_ct;i++){ + if(!strncmp(attrs[i],"ice-",4)){ +- if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i])) ++ if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i])) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified bogus ICE attribute",pctx->ctx->label,pctx->label); + continue; +- continue; ++ } + } +- +- if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,attrs[i],pstream,&cand)) +- continue; +- if(cand->component_id-1>=pstream->component_ct){ +- r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified too many components",pctx->ctx->label,pctx->label); +- continue; ++ else if (!strncmp(attrs[i],"candidate",9)){ ++ if(r=nr_ice_ctx_parse_candidate(pctx,pstream,attrs[i])) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified bogus candidate",pctx->ctx->label,pctx->label); ++ continue; ++ } ++ } ++ else { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified bogus attribute",pctx->ctx->label,pctx->label); + } ++ } + +- /* Not the fastest way to find a component, but it's what we got */ +- j=1; +- for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){ +- if(j==cand->component_id) +- break; ++ /* Doesn't fail because we just skip errors */ ++ return(0); ++ } + +- j++; +- } +- +- if(!comp){ +- r_log(LOG_ICE,LOG_ERR,"Peer answered with more components than we offered"); +- ABORT(R_BAD_DATA); +- } +- +- cand->component=comp; ++static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate) ++ { ++ nr_ice_candidate *cand=0; ++ nr_ice_component *comp; ++ int j; ++ int r, _status; + +- TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp); ++ if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,candidate,pstream,&cand)) ++ ABORT(r); ++ if(cand->component_id-1>=pstream->component_ct){ ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified too many components",pctx->ctx->label,pctx->label); ++ ABORT(R_BAD_DATA); + } + +- STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry); ++ /* Not the fastest way to find a component, but it's what we got */ ++ j=1; ++ for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){ ++ if(j==cand->component_id) ++ break; ++ ++ j++; ++ } ++ ++ if(!comp){ ++ r_log(LOG_ICE,LOG_ERR,"Peer answered with more components than we offered"); ++ ABORT(R_BAD_DATA); ++ } ++ ++ cand->component=comp; ++ ++ TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp); + + _status=0; +- abort: ++ abort: ++ if (_status) { ++ nr_ice_candidate_destroy(&cand); ++ } + return(_status); + } + ++ ++ ++int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *candidate) ++ { ++ /* First need to find the stream. Because we don't have forward pointers, ++ iterate through all the peer streams to find one that matches us */ ++ nr_ice_media_stream *pstream; ++ int r,_status; ++ int needs_pairing = 0; ++ ++ pstream=STAILQ_FIRST(&pctx->peer_streams); ++ while(pstream) { ++ if (pstream->local_stream == stream) ++ break; ++ ++ pstream = STAILQ_NEXT(pstream, entry); ++ } ++ if (!pstream) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) has no stream matching stream %s",pctx->ctx->label,pctx->label,stream->label); ++ ABORT(R_NOT_FOUND); ++ } ++ ++ switch(pstream->ice_state) { ++ case NR_ICE_MEDIA_STREAM_UNPAIRED: ++ break; ++ case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN: ++ case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE: ++ needs_pairing = 1; ++ break; ++ default: ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) tried to trickle ICE in inappropriate state %d",pctx->ctx->label,pctx->label,stream->label,pstream->ice_state); ++ ABORT(R_ALREADY); ++ break; ++ } ++ ++ if(r=nr_ice_ctx_parse_candidate(pctx,pstream,candidate)){ ++ ABORT(r); ++ } ++ ++ /* If ICE is running (i.e., we are in FROZEN or ACTIVE states) ++ then we need to pair this new candidate. For now we ++ just re-pair the stream which is inefficient but still ++ fine because we suppress duplicate pairing */ ++ if (needs_pairing) { ++ if(r=nr_ice_media_stream_pair_candidates(pctx, stream, pstream)) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to pair trickle ICE candidates",pctx->ctx->label,pctx->label,stream->label); ++ ABORT(r); ++ } ++ } ++ ++ _status =0; ++ abort: ++ return(_status); ++ ++ } ++ ++ + int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx) + { + nr_ice_media_stream *stream; + int r,_status; + + if(STAILQ_EMPTY(&pctx->peer_streams)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) received no media stream attribributes",pctx->ctx->label,pctx->label); + ABORT(R_FAILED); + } + +@@ -177,21 +271,21 @@ + ABORT(r); + + stream=STAILQ_NEXT(stream,entry); + } + + _status=0; + abort: + return(_status); + } + +-static void nr_ice_peer_ctx_destroy_cb(int s, int how, void *cb_arg) ++static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_peer_ctx *pctx=cb_arg; + nr_ice_media_stream *str1,*str2; + + RFREE(pctx->label); + RFREE(pctx->peer_ufrag); + RFREE(pctx->peer_pwd); + + STAILQ_FOREACH_SAFE(str1, &pctx->peer_streams, entry, str2){ + STAILQ_REMOVE(&pctx->peer_streams,str1,nr_ice_media_stream_,entry); +@@ -199,44 +293,79 @@ + } + + RFREE(pctx); + } + + int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp) + { + + if(!pctxp || !*pctxp) + return(0); +- ++ ++ /* Stop calling the handler */ ++ (*pctxp)->handler = 0; ++ + NR_ASYNC_SCHEDULE(nr_ice_peer_ctx_destroy_cb,*pctxp); + + *pctxp=0; + + return(0); + } + ++ + /* Start the checks for the first media stream (S 5.7) + The rest remain FROZEN */ + int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx) + { ++ return nr_ice_peer_ctx_start_checks2(pctx, 0); ++ } ++ ++/* Start checks for some media stream. ++ ++ If allow_non_first == 0, then we only look at the first stream, ++ which is 5245-complaint. ++ ++ If allow_non_first == 1 then we find the first non-empty stream ++ This is not compliant with RFC 5245 but is necessary to make trickle ICE ++ work plausibly ++*/ ++int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first) ++ { + int r,_status; + nr_ice_media_stream *stream; + + stream=STAILQ_FIRST(&pctx->peer_streams); + if(!stream) + ABORT(R_FAILED); + ++ while (stream) { ++ if(!TAILQ_EMPTY(&stream->check_list)) ++ break; ++ ++ if(!allow_non_first){ ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label); ++ ABORT(R_FAILED); ++ } ++ ++ stream=STAILQ_NEXT(stream, entry); ++ } ++ ++ if (!stream) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) no streams with non-empty check lists",pctx->ctx->label,pctx->label); ++ ABORT(R_NOT_FOUND); ++ } ++ + if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) { +- if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream)) +- ABORT(r); +- if(r=nr_ice_media_stream_start_checks(pctx,stream)) +- ABORT(r); ++ if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream)) ++ ABORT(r); ++ if(r=nr_ice_media_stream_start_checks(pctx,stream)) ++ ABORT(r); + } + + _status=0; + abort: + return(_status); + } + + #ifndef NDEBUG + int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out) + { +@@ -253,26 +382,28 @@ + stream=STAILQ_NEXT(stream,entry); + } + fprintf(out,"==========================================\n"); + + _status=0; + abort: + return(_status); + } + #endif + +-static void nr_ice_peer_ctx_fire_done(int s, int how, void *cb_arg) ++static void nr_ice_peer_ctx_fire_done(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_peer_ctx *pctx=cb_arg; + + /* Fire the handler callback to say we're done */ +- pctx->handler->vtbl->ice_completed(pctx->handler->obj, pctx); ++ if (pctx->handler) { ++ pctx->handler->vtbl->ice_completed(pctx->handler->obj, pctx); ++ } + } + + + /* OK, a stream just went ready. Examine all the streams to see if we're + maybe miraculously done */ + int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + int _status; + nr_ice_media_stream *str; + int failed=0; +@@ -365,21 +496,24 @@ + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + break; + + cand=TAILQ_NEXT(cand,entry_comp); + } + + if(!cand) + ABORT(R_REJECTED); + + /* OK, there's a match. Call the handler */ +- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label); + +- pctx->handler->vtbl->msg_recvd(pctx->handler->obj, +- pctx,comp->stream,comp->component_id,data,len); ++ if (pctx->handler) { ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label); ++ ++ pctx->handler->vtbl->msg_recvd(pctx->handler->obj, ++ pctx,comp->stream,comp->component_id,data,len); ++ } + + _status=0; + abort: + return(_status); + } + + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.h src/ice/ice_peer_ctx.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_peer_ctx.h 2012-10-06 08:30:22.000000000 -0700 +@@ -33,23 +33,21 @@ + + + #ifndef _ice_peer_ctx_h + #define _ice_peer_ctx_h + #ifdef __cplusplus + using namespace std; + extern "C" { + #endif /* __cplusplus */ + + struct nr_ice_peer_ctx_ { +- int state; + char *label; +- + nr_ice_ctx *ctx; + nr_ice_handler *handler; + + UCHAR controlling; /* 1 for controlling, 0 for controlled */ + UINT8 tiebreaker; + + char *peer_ufrag; + char *peer_pwd; + int peer_lite; + int peer_ice_mismatch; +@@ -59,23 +57,26 @@ + int waiting_pairs; + + STAILQ_ENTRY(nr_ice_peer_ctx_) entry; + }; + + typedef STAILQ_HEAD(nr_ice_peer_ctx_head_, nr_ice_peer_ctx_) nr_ice_peer_ctx_head; + + int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp); + int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp); + int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct); ++int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *cand); ++ + int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx); + int nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct); + int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx); ++int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first); + int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out); + int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx); + int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); + int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp); + int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len); + #ifdef __cplusplus + } + #endif /* __cplusplus */ + #endif + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_socket.c src/ice/ice_socket.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_socket.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_socket.c 2012-10-06 08:30:22.000000000 -0700 +@@ -216,28 +216,34 @@ + RFREE(s1); + } + + RFREE(isock); + + return(0); + } + + int nr_ice_socket_close(nr_ice_socket *isock) + { ++#ifdef NR_SOCKET_IS_VOID_PTR ++ NR_SOCKET fd=NULL; ++ NR_SOCKET no_socket = NULL; ++#else + NR_SOCKET fd=-1; ++ NR_SOCKET no_socket = -1; ++#endif + + if (!isock||!isock->sock) + return(0); + + nr_socket_getfd(isock->sock,&fd); + assert(isock->sock!=0); +- if(fd!=-1){ ++ if(fd != no_socket){ + NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_READ); + NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_WRITE); + nr_socket_destroy(&isock->sock); + } + + return(0); + } + + int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle) + { +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/net/nr_socket.h src/net/nr_socket.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/net/nr_socket.h 2012-09-16 16:26:09.000000000 -0700 ++++ src/net/nr_socket.h 2012-10-06 08:30:22.000000000 -0700 +@@ -38,21 +38,23 @@ + #include <sys/types.h> + #ifdef WIN32 + #include <winsock2.h> + #include <ws2tcpip.h> + #else + #include <sys/socket.h> + #endif + + #include "transport_addr.h" + +-#ifdef WIN32 ++#ifdef __cplusplus ++#define restrict ++#elif defined(WIN32) + #define restrict __restrict + #endif + + typedef struct nr_socket_vtbl_ { + 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); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/net/transport_addr_reg.c src/net/transport_addr_reg.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/net/transport_addr_reg.c 2012-09-16 16:26:09.000000000 -0700 ++++ src/net/transport_addr_reg.c 2012-10-06 08:30:22.000000000 -0700 +@@ -29,25 +29,27 @@ + (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: transport_addr_reg.c,v 1.2 2008/04/28 17:59:03 ekr Exp $"; + + #include <csi_platform.h> + #include <stdio.h> ++#include <string.h> + #include <memory.h> + #include <sys/types.h> + #ifdef WIN32 + #include <winsock2.h> + #else ++#include <strings.h> + #include <unistd.h> + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> + #endif + #include <assert.h> + #include "nr_api.h" + #include "transport_addr.h" + #include "transport_addr_reg.h" + +@@ -83,20 +85,22 @@ + + if ((r=NR_reg_get2_uint2(prefix, "port", &port))) { + if (r != R_NOT_FOUND) + ABORT(r); + port = 0; + } + + if ((r=NR_reg_alloc2_string(prefix, "protocol", &protocol))) { + if (r != R_NOT_FOUND) + ABORT(r); ++ p = IPPROTO_UDP; ++ + protocol = 0; + } + else { + if (!strcasecmp("tcp", protocol)) + p = IPPROTO_TCP; + else if (!strcasecmp("udp", protocol)) + p = IPPROTO_UDP; + else + ABORT(R_BAD_DATA); + } +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/addrs.c src/stun/addrs.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/addrs.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/addrs.c 2012-10-06 09:42:43.000000000 -0700 +@@ -46,20 +46,22 @@ + #include <sys/sysctl.h> + #include <sys/param.h> + #include <sys/socket.h> + #include <sys/syslog.h> + #include <net/if.h> + #ifndef LINUX + #include <net/if_var.h> + #include <net/if_dl.h> + #include <net/if_types.h> + #include <sys/sockio.h> ++#else ++#include <linux/if.h> + #endif + #include <net/route.h> + + /* IP */ + #include <netinet/in.h> + #ifdef LINUX + #include "sys/ioctl.h" + #else + #include <netinet/in_var.h> + #endif +@@ -105,20 +107,23 @@ + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + #include <err.h> + + static void stun_rt_xaddrs(caddr_t, caddr_t, struct rt_addrinfo *); + static int stun_grab_addrs(char *name, int addrcount, + struct ifa_msghdr *ifam, + nr_transport_addr addrs[], int maxaddrs, int *count); ++static int ++nr_stun_is_duplicate_addr(nr_transport_addr addrs[], int count, nr_transport_addr *addr); ++ + + /* + * Expand the compacted form of addresses as returned via the + * configuration read via sysctl(). + */ + #define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + + static void +@@ -135,21 +140,21 @@ + continue; + rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; + ADVANCE(cp, sa); + } + } + + static int + stun_grab_addrs(char *name, int addrcount, struct ifa_msghdr *ifam, nr_transport_addr addrs[], int maxaddrs, int *count) + { + int r,_status; +- NR_SOCKET s = -1; ++ int s = -1; + struct ifreq ifr; + struct rt_addrinfo info; + struct sockaddr_in *sin; + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + + if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "unable to obtain addresses from socket"); + ABORT(R_FAILED); +@@ -180,21 +185,20 @@ + addrcount--; + + if (*count >= maxaddrs) { + r_log(NR_LOG_STUN, LOG_WARNING, "Address list truncated at %d out of entries", maxaddrs, maxaddrs+addrcount); + break; + } + + ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen); + } + +- + _status = 0; + abort: + if (s != -1) close(s); + return _status; + } + + static int + stun_get_mib_addrs(nr_transport_addr addrs[], int maxaddrs, int *count) + { + int _status; +@@ -551,44 +555,48 @@ + #else + + static int + stun_get_siocgifconf_addrs(nr_transport_addr addrs[], int maxaddrs, int *count) + { + struct ifconf ifc; + int _status; + int s = socket( AF_INET, SOCK_DGRAM, 0 ); + int len = 100 * sizeof(struct ifreq); + int r; ++ int e; ++ char *ptr; ++ int tl; ++ int n; ++ struct ifreq ifr2; + + char buf[ len ]; + + ifc.ifc_len = len; + ifc.ifc_buf = buf; + +- int e = ioctl(s,SIOCGIFCONF,&ifc); +- char *ptr = buf; +- int tl = ifc.ifc_len; +- int n=0; ++ e = ioctl(s,SIOCGIFCONF,&ifc); ++ ptr = buf; ++ tl = ifc.ifc_len; ++ n=0; + + while ( (tl > 0) && ( n < maxaddrs) ) + { + struct ifreq* ifr = (struct ifreq *)ptr; + + #ifdef LINUX +- int si = sizeof(ifr->ifr_name) + sizeof(ifr->ifr_addr); ++ int si = sizeof(struct ifreq); + #else + int si = sizeof(ifr->ifr_name) + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)); + #endif + tl -= si; + ptr += si; + +- struct ifreq ifr2; + ifr2 = *ifr; + + e = ioctl(s,SIOCGIFADDR,&ifr2); + if ( e == -1 ) + { + continue; + } + + //r_log(NR_LOG_STUN, LOG_ERR, "ioctl addr e = %d",e); + +@@ -603,21 +611,21 @@ + + close(s); + + *count = n; + + _status = 0; + return _status; + } + #endif + +-int ++static int + nr_stun_is_duplicate_addr(nr_transport_addr addrs[], int count, nr_transport_addr *addr) + { + int i; + int different; + + for (i = 0; i < count; ++i) { + different = nr_transport_addr_cmp(&addrs[i], addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL); + if (!different) + return 1; /* duplicate */ + } +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/nr_socket_turn.c src/stun/nr_socket_turn.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/nr_socket_turn.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/nr_socket_turn.c 2012-10-06 08:30:22.000000000 -0700 +@@ -246,17 +246,19 @@ + default: + assert(0); + break; + } + + return R_FAILED; + } + + static int nr_socket_turn_close(void *obj) + { ++#ifndef NDEBUG + nr_socket_turn *sturn=obj; + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); ++#endif + + return 0; + } + + #endif /* USE_TURN */ +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_client_ctx.c src/stun/stun_client_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_client_ctx.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_client_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -38,21 +38,22 @@ + #include <string.h> + + #include <nr_api.h> + #include "stun.h" + #include "async_timer.h" + #include "registry.h" + #include "stun_reg.h" + #include "r_time.h" + + static int nr_stun_client_send_request(nr_stun_client_ctx *ctx); +-static void nr_stun_client_timer_expired_cb(int a, int b, void *cb_arg); ++static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg); ++static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password); + + int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp) + { + nr_stun_client_ctx *ctx=0; + int r,_status; + + if ((r=nr_stun_startup())) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_stun_client_ctx)))) +@@ -185,21 +186,21 @@ + ctx->finished_cb = 0; + ctx->cb_arg = 0; + ctx->request_ct = 0; + ctx->timeout_ms = 0; + + ctx->state = NR_STUN_CLIENT_STATE_INITTED; + + return 0; + } + +-static void nr_stun_client_timer_expired_cb(int a, int b, void *cb_arg) ++static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg) + { + int _status; + nr_stun_client_ctx *ctx=cb_arg; + struct timeval now; + INT8 ms_waited; + + /* Prevent this timer from being cancelled later */ + ctx->timer_handle=0; + + /* Shouldn't happen */ +@@ -387,21 +388,21 @@ + } + + _status=0; + abort: + if (_status) { + ctx->state=NR_STUN_CLIENT_STATE_FAILED; + } + return(_status); + } + +-int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password) ++static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password) + { + *password = (Data*)arg; + if (!arg) + return(R_NOT_FOUND); + return(0); + } + + int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr) + { + int r,_status; +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_codec.c src/stun/stun_codec.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_codec.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_codec.c 2012-10-06 08:30:22.000000000 -0700 +@@ -73,20 +73,22 @@ + static int nr_stun_decode_htonll(UCHAR *buf, int buflen, int *offset, UINT8 *data); + static int nr_stun_decode(int length, UCHAR *buf, int buflen, int *offset, UCHAR *data); + + static int nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, int len, void *data, int max_bytes, int max_chars); + + static int nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); + static int nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); + static int nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); + static int nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); + static int nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); ++static int ++nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data); + + + int + nr_stun_encode_htons(UINT2 data, int buflen, UCHAR *buf, int *offset) + { + UINT2 d = htons(data); + + if (*offset + sizeof(d) >= buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d >= %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; +@@ -632,21 +634,21 @@ + } + + fingerprint->checksum = checksum ^ 0x5354554e; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", fingerprint->checksum); + + fingerprint->valid = 1; + return nr_stun_attr_codec_UINT4.encode(attr_info, &fingerprint->checksum, offset, buflen, buf, attrlen); + } + +-int ++static int + nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) + { + int r,_status; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + int length; + UINT4 checksum; + + if ((r=nr_stun_attr_codec_UINT4.decode(attr_info, attrlen, buf, offset, buflen, &fingerprint->checksum))) + ABORT(r); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_proc.c src/stun/stun_proc.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_proc.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_proc.c 2012-10-06 08:30:22.000000000 -0700 +@@ -43,20 +43,22 @@ + #include <time.h> + #else /* UNIX */ + #include <string.h> + #endif /* end UNIX */ + #include <assert.h> + + #include "stun.h" + #include "stun_reg.h" + #include "registry.h" + ++static int ++nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res); + + /* draft-ietf-behave-rfc3489bis-10.txt S 7.3 */ + int + nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg) + { + int _status; + nr_stun_message_attribute *attr; + + #ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + /* if this message was generated by an RFC 3489 impementation, +@@ -371,21 +373,21 @@ + /* nothing to check in this case */ + break; + #endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; + } + +-int ++static int + nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res) + { + int r,_status; + char *realm = 0; + char *nonce; + UINT2 size; + + if ((r=NR_reg_alloc_string(NR_STUN_REG_PREF_SERVER_REALM, &realm))) + ABORT(r); + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_server_ctx.c src/stun/stun_server_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_server_ctx.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_server_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -114,21 +114,21 @@ + STAILQ_INSERT_TAIL(&ctx->clients,clnt,entry); + + _status=0; + abort: + if(_status){ + nr_stun_server_destroy_client(clnt); + } + return(_status); + } + +-int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password) ++static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password) + { + int _status; + nr_stun_server_ctx *ctx = (nr_stun_server_ctx*)arg; + nr_stun_server_client *clnt = 0; + nr_stun_message_attribute *username_attribute; + + if ((nr_stun_get_message_client(ctx, msg, &clnt))) { + if (! nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, &username_attribute)) { + r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Missing Username",ctx->label); + ABORT(R_NOT_FOUND); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_util.c src/stun/stun_util.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_util.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_util.c 2012-10-06 08:30:22.000000000 -0700 +@@ -94,21 +94,20 @@ + _status = 0; + abort: + return _status; + } + + int + nr_stun_find_local_addresses(nr_transport_addr addrs[], int maxaddrs, int *count) + { + int r,_status; + NR_registry *children = 0; +- int i; + + if ((r=NR_reg_get_child_count(NR_STUN_REG_PREF_ADDRESS_PRFX, (unsigned int*)count))) + if (r == R_NOT_FOUND) + *count = 0; + else + ABORT(r); + + if (*count == 0) { + if ((r=nr_stun_get_addrs(addrs, maxaddrs, 1, count))) + ABORT(r); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/turn_client_ctx.c src/stun/turn_client_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/turn_client_ctx.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/turn_client_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -55,21 +55,24 @@ + }; + + static int TURN_PHASE_MODE[NUMBER_OF_STUN_CTX] = { + NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST1, + NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST2, + NR_TURN_CLIENT_MODE_SET_ACTIVE_DEST_REQUEST + }; + + + static int nr_turn_client_next_action(nr_turn_client_ctx *ctx, int stun_ctx_state); +-static void nr_turn_client_cb(int s, int how, void *cb_arg); ++static void nr_turn_client_cb(NR_SOCKET s, int how, void *cb_arg); ++static int ++nr_turn_client_prepare_start(nr_turn_client_ctx *ctx, char *username, Data *password, UINT4 bandwidth_kbps, UINT4 lifetime_secs, NR_async_cb finished_cb, void *cb_arg); ++ + + int + nr_turn_client_next_action(nr_turn_client_ctx *ctx, int stun_ctx_state) + { + int r,_status; + + assert(ctx->phase >= -1 && ctx->phase < NUMBER_OF_STUN_CTX); + + switch (ctx->state) { + //case NR_TURN_CLIENT_STATE_ALLOCATING: +@@ -147,21 +150,21 @@ + * because as a side effect this ctx may be operated on in the + * callback */ + finished_cb(0,0,ctx->cb_arg); + } + } + + return(_status); + } + + void +-nr_turn_client_cb(int s, int how, void *cb_arg) ++nr_turn_client_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_turn_client_ctx *ctx = (nr_turn_client_ctx*)cb_arg; + nr_stun_client_ctx *stun_ctx = ctx->stun_ctx[ctx->phase]; + + assert(ctx->phase >= 0); + + if ((r=nr_turn_client_next_action(ctx, stun_ctx->state))) + ABORT(r); + +@@ -234,21 +237,21 @@ + + RFREE(ctx->username); + r_data_destroy(&ctx->password); + + RFREE(ctx->label); + RFREE(ctx); + + return(0); + } + +-int ++static int + nr_turn_client_prepare_start(nr_turn_client_ctx *ctx, char *username, Data *password, UINT4 bandwidth_kbps, UINT4 lifetime_secs, NR_async_cb finished_cb, void *cb_arg) + { + int r,_status; + nr_stun_client_allocate_request1_params *allocate_request1 = 0; + nr_stun_client_allocate_request2_params *allocate_request2 = 0; + nr_stun_client_allocate_response1_results *allocate_response1 = 0; + // nr_stun_client_allocate_response2_results *allocate_response2; + + if (ctx->state != NR_TURN_CLIENT_STATE_INITTED) + ABORT(R_NOT_PERMITTED); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/util/ice_util.c src/util/ice_util.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/util/ice_util.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/util/ice_util.c 2012-10-06 08:30:22.000000000 -0700 +@@ -31,20 +31,21 @@ + */ + + #include <stdarg.h> + + + static char *RCSSTRING __UNUSED__="$Id: ice_util.c,v 1.2 2008/04/28 17:59:05 ekr Exp $"; + + #include <stdarg.h> + #include <string.h> + #include "nr_api.h" ++#include "ice_util.h" + + int nr_concat_strings(char **outp,...) + { + va_list ap; + char *s,*out=0; + int len=0; + int _status; + + va_start(ap,outp); + while(s=va_arg(ap,char *)){ +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/util/mbslen.c src/util/mbslen.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/util/mbslen.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/util/mbslen.c 2012-10-06 08:31:01.000000000 -0700 +@@ -56,21 +56,21 @@ + { + #ifdef DARWIN + static locale_t loc = 0; + static int initialized = 0; + #endif /* DARWIN */ + #ifdef WIN32 + char *my_locale=0; + unsigned int i; + #endif /* WIN32 */ + int _status; +- int nbytes; ++ size_t nbytes; + int nchars; + mbstate_t mbs; + + #ifdef DARWIN + if (! initialized) { + initialized = 1; + loc = newlocale(LC_CTYPE_MASK, "UTF-8", LC_GLOBAL_LOCALE); + } + + if (loc == 0) { +@@ -102,25 +102,28 @@ + + memset(&mbs, 0, sizeof(mbs)); + nchars = 0; + + #ifdef DARWIN + while (*s != '\0' && (nbytes = mbrlen_l(s, strlen(s), &mbs, loc)) != 0) + #else + while (*s != '\0' && (nbytes = mbrlen(s, strlen(s), &mbs)) != 0) + #endif /* DARWIN */ + { +- assert(nbytes >= 0); +- if (nbytes == (size_t)-1) /* should never happen */ ++ if (nbytes == (size_t)-1) /* should never happen */ { ++ assert(0); + ABORT(R_INTERNAL); +- if (nbytes == (size_t)-2) /* encoding error */ ++ } ++ if (nbytes == (size_t)-2) /* encoding error */ { ++ assert(0); + ABORT(R_BAD_DATA); ++ } + + s += nbytes; + ++nchars; + } + + *ncharsp = nchars; + + _status = 0; + abort: + #ifdef WIN32 diff --git a/dom/media/webrtc/transport/third_party/nrappkit/COPYRIGHT b/dom/media/webrtc/transport/third_party/nrappkit/COPYRIGHT new file mode 100644 index 0000000000..b0bb25595e --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/COPYRIGHT @@ -0,0 +1,159 @@ + +Copyright (C) 2006, Network Resonance, Inc. +All Rights Reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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. +3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + +This distribution also contains material from ssldump, tcpdump, and +FreeBSD. The licenses are on the individual source files but follow +here as well. + +SSLDUMP LICENSE +Copyright (C) 1999-2001 RTFM, Inc. +All Rights Reserved + +This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla +<ekr@rtfm.com> and licensed by RTFM, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + +4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE ERIC RESCORLA AND RTFM ``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 REGENTS 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. + +TCPDUMP LICENSE +The manual page for this software is partially excerpted from +the tcpdump manual page, which is subject to the following license: +Copyright (c) 1987, 1988, 1989, 1990, 1991, 1992, 1994, 1995, 1996, 1997 + The Regents of the University of California. All rights reserved. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that: (1) source code distributions +retain the above copyright notice and this paragraph in its entirety, (2) +distributions including binary code include the above copyright notice and +this paragraph in its entirety in the documentation or other materials +provided with the distribution, and (3) all advertising materials mentioning +features or use of this software display the following acknowledgement: +``This product includes software developed by the University of California, +Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + +The compilation of software known as FreeBSD is distributed under the +following terms: + +Copyright (C) 1992-2004 The FreeBSD Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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. + +THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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. + +The 4.4BSD and 4.4BSD-Lite software is distributed under the following +terms: + +All of the documentation and software included in the 4.4BSD and 4.4BSD-Lite +Releases is copyrighted by The Regents of the University of California. + +Copyright 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 + The Regents of the University of California. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: +This product includes software developed by the University of +California, Berkeley and its contributors. +4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/IMPORT_FILES b/dom/media/webrtc/transport/third_party/nrappkit/IMPORT_FILES new file mode 100644 index 0000000000..0f80a52e33 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/IMPORT_FILES @@ -0,0 +1,87 @@ + # API + src/plugin/nr_plugin.h + src/share/nr_api.h + src/share/nr_common.h + src/share/nr_reg_keys.h + + # libekr + src/util/libekr/assoc.h + src/util/libekr/debug.c + src/util/libekr/debug.h + src/util/libekr/r_assoc.c + src/util/libekr/r_assoc.h + src/util/libekr/r_common.h + src/util/libekr/r_crc32.c + src/util/libekr/r_crc32.h + src/util/libekr/r_data.c + src/util/libekr/r_data.h + src/util/libekr/r_defaults.h + src/util/libekr/r_errors.c + src/util/libekr/r_errors.h + src/util/libekr/r_includes.h + src/util/libekr/r_list.c + src/util/libekr/r_list.h + src/util/libekr/r_macros.h + src/util/libekr/r_memory.c + src/util/libekr/r_memory.h + src/util/libekr/r_replace.c + src/util/libekr/r_thread.h + src/util/libekr/r_time.c + src/util/libekr/r_time.h + src/util/libekr/r_types.h + src/util/libekr/debug.c + src/util/libekr/debug.h + + # Utilities + src/util/byteorder.c + src/util/byteorder.h + src/util/hex.c + src/util/hex.h + src/util/p_buf.c + src/util/p_buf.h + src/util/util.c + src/util/util.h + + # Events + src/event/async_timer.h + src/event/async_wait.h + src/event/async_wait_int.h + + # Logging + src/log/r_log.c + src/log/r_log.h + + # Registry + src/registry/c2ru.c + src/registry/c2ru.h + src/registry/registry.c + src/registry/registry.h + src/registry/registry_int.h + src/registry/registry_local.c + src/registry/registry_vtbl.h + src/registry/registrycb.c + + src/port/darwin/include/csi_platform.h + src/port/win32/include/csi_platform.h + src/port/linux/include/csi_platform.h + src/port/linux/include/linux_funcs.h + src/port/linux/include/sys/ttycom.h + src/port/linux/port-impl.mk + # sys/queue.h manually copied into src/port/generic + + # Stats + src/stats/nrstats.h + + # Meta-files + COPYRIGHT + VERSION + README + + + + + + + + + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/README b/dom/media/webrtc/transport/third_party/nrappkit/README new file mode 100644 index 0000000000..9cf43319b3 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/README @@ -0,0 +1,133 @@ +$Id: README,v 1.3 2007/11/21 00:09:10 adamcain Exp $ + +nrappkit 1.0b2 +Copyright (C) 2006 Network Resonance, Inc. + + +nrappkit is a toolkit for building standalone applications and +appliances. It provides: + +- registry-based configuration (with change callbacks) +- extensible command and configuration shell +- extensible statistics system +- configurable logging system +- event and timer handling +- generic plugin system +- launcher daemon + +The contents of nrappkit were extracted from Network Resonance's +product on the theory that they were generally useful for +application developers. + +THIS PACKAGE DOES NOT GRANT A LICENSE OR RIGHT TO ANY OTHER NETWORK +RESONANCE TECHNOLOGY OR SOFTWARE. + + + +BUILDING + +Builds are done semi-manually with port directories for each +platform. There are pre-existing ports to FreeBSD, Linux (Ubuntu +and Fedora Core), and Darwin (MacOSX). To build the system: + + cd src/make/<platform> + gmake + +Some of the platforms come in several variants. Most notably, +if a platform exists in "regular" and "-appliance" variant, +this means that the regular variant just builds binaries intended +to be run out of the make directory (for development) and the +appliance variant is intended to be installed in a real system. + +By default we want to install things owned as user "pcecap". +Either make this user or edit the Makefile to be a user you +like (e.g., nobody). + +If you want to include the 'nrsh' command-line configuration +tool in your build, you will need to make sure the line + BUILD_NRSH=yes +appears (uncommented-out) in your platform Makefile. You will +also need to to build OpenSSL and libedit and point your nrappkit +Makefile to the correct paths. You can obtain these packages at: + openssl-0.9.7l + http://www.openssl.org/source/openssl-0.9.7l.tar.gz + + libedit-20060829-2.9 + http://freshmeat.net/redir/editline/53029/url_tgz/libedit-20060829-2.9.tar.gz + + +INSTALLING +If you're doing an appliance as opposed to a development build, +you'll want to install it. This is easy: + + su + gmake install + +Most binaries and libraries ends up in /usr/local/pcecap while +data files are in /var/pcecap. However, you can tweak +this in the Makefile. By default it's all owned by pcecap. + +To ensure that dynamic libraries are loaded correctly at runtime, +you'd want to make sure the right directory is included in your +LD_LIBRARY_PATH or via ldconfig. + + +QUICK TOUR +The build makes the following binaries that you may find useful: + +- captured -- the launcher (the name is historical) +- registryd -- the registry daemon +- nrregctl -- a registry control program +- nrsh -- the command shell (when included in build) +- nrstatsctl -- the stats control program + +Using the nrcapctl script is the easiest way to interact with +the applications. It is run as "nrcapctl <command>" with the +following commands recognized: + + startup -- fires up captured, which in turn runs and + initializes the registry + + shutdown -- kills captured and its child processes + + status -- prints the running status of captured in + human-readable form + + stat -- prints the running status of captured in + a form easily parsed by scripts + + enable -- alters the mode.txt file so that captured + starts + + disable -- alters the mode.txt file so that captured + does not start + + clear-statistics -- equivalent to "nrstatsctl -z" (requires + that captured be running) + +Note: the "start" and "stop" nrcapctl commands do nothing as they +use components not included in nrappkit. However the associated +script logic in nrcapctl demonstrates how additional applications +might be launched using nrcapctl and particular registry settings. + + +EXTENDING +When things come up, they're pretty dumb. You'll probably want to +write your own applications, otherwise it's not clear why you're doing +this. The general idea is that you write your application using the +facilities that nrappkit provides and then write plugins to the +nrappkit components as necessary. So, for example, say you want +to write a network daemon. You would: + + - configure the launcher to launch your daemon (using the registry, + naturally). + - make calls to the registry to get configuration data + - make calls to the logging system to log data + - implement a stats module to record statistics + - write a plugin to nrsh to let people configure your parameters + +Examples of some of this stuff can be found in examples/demo_plugin. +Otherwise, read the source. More documentation will be on the way, +hopefully. + + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/README_MOZILLA b/dom/media/webrtc/transport/third_party/nrappkit/README_MOZILLA new file mode 100644 index 0000000000..92102c83c6 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/README_MOZILLA @@ -0,0 +1,22 @@ +This import of nrappkit is a subset of the distribution at: + + nrappkit.sourceforge.net + + +The last revision included in this import was on Nov 25, 2008. +This import includes a small nunber of portability fixes that +have been submitted to the upstream repository and are included +in upstream.diff. + + +Out of the list in the README, we use: + +- registry-based configuration (with change callbacks) + [but without the registry daemon] +- configurable logging system +- event and timer handling + [though partly reimplemented] + +Also, we use a bunch of the generic utilities such as string handling, +generic hash tables in C, yet another concrete type mapping, etc. + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/VERSION b/dom/media/webrtc/transport/third_party/nrappkit/VERSION new file mode 100644 index 0000000000..3ef69b306c --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/VERSION @@ -0,0 +1 @@ +1.0b2 diff --git a/dom/media/webrtc/transport/third_party/nrappkit/nrappkit.gyp b/dom/media/webrtc/transport/third_party/nrappkit/nrappkit.gyp new file mode 100644 index 0000000000..c3f88af4bc --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/nrappkit.gyp @@ -0,0 +1,251 @@ +# 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/. +# +# nrappkit.gyp +# +# +{ + 'targets' : [ + { + 'target_name' : 'nrappkit', + 'type' : 'static_library', + + 'include_dirs' : [ + # EXTERNAL + # INTERNAL + 'src/event', + 'src/log', + 'src/port/generic/include', + 'src/registry', + 'src/share', + 'src/stats', + 'src/util', + 'src/util/libekr', + ], + + 'sources' : [ + # Shared +# './src/share/nr_api.h', + './src/share/nr_common.h', +# './src/share/nr_dynlib.h', + './src/share/nr_reg_keys.h', +# './src/share/nr_startup.c', +# './src/share/nr_startup.h', +# './src/share/nrappkit_static_plugins.c', + './src/port/generic/include' + + # libekr + './src/util/libekr/assoc.h', +# './src/util/libekr/debug.c', +# './src/util/libekr/debug.h', + './src/util/libekr/r_assoc.c', + './src/util/libekr/r_assoc.h', +# './src/util/libekr/r_assoc_test.c', + './src/util/libekr/r_common.h', + './src/util/libekr/r_crc32.c', + './src/util/libekr/r_crc32.h', + './src/util/libekr/r_data.c', + './src/util/libekr/r_data.h', + './src/util/libekr/r_defaults.h', + './src/util/libekr/r_errors.c', + './src/util/libekr/r_errors.h', + './src/util/libekr/r_includes.h', +# './src/util/libekr/r_list.c', +# './src/util/libekr/r_list.h', + './src/util/libekr/r_macros.h', + './src/util/libekr/r_memory.c', + './src/util/libekr/r_memory.h', + './src/util/libekr/r_replace.c', + './src/util/libekr/r_thread.h', + './src/util/libekr/r_time.c', + './src/util/libekr/r_time.h', + './src/util/libekr/r_types.h', + + # Utilities + './src/util/byteorder.c', + './src/util/byteorder.h', + #'./src/util/escape.c', + #'./src/util/escape.h', + #'./src/util/filename.c', + #'./src/util/getopt.c', + #'./src/util/getopt.h', + './src/util/hex.c', + './src/util/hex.h', + #'./src/util/mem_util.c', + #'./src/util/mem_util.h', + #'./src/util/mutex.c', + #'./src/util/mutex.h', + './src/util/p_buf.c', + './src/util/p_buf.h', + #'./src/util/ssl_util.c', + #'./src/util/ssl_util.h', + './src/util/util.c', + './src/util/util.h', + #'./src/util/util_db.c', + #'./src/util/util_db.h', + + # Events +# './src/event/async_timer.c', + './src/event/async_timer.h', +# './src/event/async_wait.c', + './src/event/async_wait.h', + './src/event/async_wait_int.h', + + # Logging + './src/log/r_log.c', + './src/log/r_log.h', + #'./src/log/r_log_plugin.c', + + # Registry + './src/registry/c2ru.c', + './src/registry/c2ru.h', + #'./src/registry/mod_registry/mod_registry.c', + #'./src/registry/nrregctl.c', + #'./src/registry/nrregistryctl.c', + './src/registry/registry.c', + './src/registry/registry.h', + './src/registry/registry_int.h', + './src/registry/registry_local.c', + #'./src/registry/registry_plugin.c', + './src/registry/registry_vtbl.h', + './src/registry/registrycb.c', + #'./src/registry/registryd.c', + #'./src/registry/regrpc.h', + #'./src/registry/regrpc_client.c', + #'./src/registry/regrpc_client.h', + #'./src/registry/regrpc_client_cb.c', + #'./src/registry/regrpc_clnt.c', + #'./src/registry/regrpc_server.c', + #'./src/registry/regrpc_svc.c', + #'./src/registry/regrpc_xdr.c', + + # Statistics + #'./src/stats/nrstats.c', + #'./src/stats/nrstats.h', + #'./src/stats/nrstats_app.c', + #'./src/stats/nrstats_int.h', + #'./src/stats/nrstats_memory.c', + ], + + 'defines' : [ + 'SANITY_CHECKS', + 'R_PLATFORM_INT_TYPES=<stdint.h>', + 'R_DEFINED_INT2=int16_t', + 'R_DEFINED_UINT2=uint16_t', + 'R_DEFINED_INT4=int32_t', + 'R_DEFINED_UINT4=uint32_t', + 'R_DEFINED_INT8=int64_t', + 'R_DEFINED_UINT8=uint64_t', + ], + + 'conditions' : [ + ## Mac and BSDs + [ 'OS == "mac"', { + 'defines' : [ + 'DARWIN', + ], + }], + [ 'os_bsd == 1', { + 'defines' : [ + 'BSD', + ], + }], + [ 'OS == "mac" or OS == "ios" or os_bsd == 1', { + 'cflags_mozilla': [ + '-Wall', + '-Wno-parentheses', + '-Wno-strict-prototypes', + '-Wmissing-prototypes', + '-Wno-format', + '-Wno-format-security', + ], + 'defines' : [ + 'HAVE_LIBM=1', + 'HAVE_STRDUP=1', + 'HAVE_STRLCPY=1', + 'HAVE_SYS_TIME_H=1', + 'HAVE_VFPRINTF=1', + 'NEW_STDIO' + 'RETSIGTYPE=void', + 'TIME_WITH_SYS_TIME_H=1', + '__UNUSED__=__attribute__((unused))', + ], + + 'include_dirs': [ + 'src/port/darwin/include' + ], + + 'sources': [ + './src/port/darwin/include/csi_platform.h', + ], + }], + + ## Win + [ 'OS == "win"', { + 'defines' : [ + 'WIN', + '__UNUSED__=', + 'HAVE_STRDUP=1', + 'NO_REG_RPC' + ], + + 'include_dirs': [ + 'src/port/win32/include' + ], + + 'sources': [ + './src/port/win32/include/csi_platform.h', + ], + }], + + # Windows, clang-cl build + [ 'clang_cl == 1', { + 'cflags_mozilla': [ + '-Xclang', + '-Wall', + '-Xclang', + '-Wno-parentheses', + '-Wno-strict-prototypes', + '-Wmissing-prototypes', + '-Wno-format', + '-Wno-format-security', + ], + }], + + ## Linux/Android + [ '(OS == "linux") or (OS == "android")', { + 'cflags_mozilla': [ + '-Wall', + '-Wno-parentheses', + '-Wno-strict-prototypes', + '-Wmissing-prototypes', + '-Wno-format', + '-Wno-format-security', + ], + 'defines' : [ + 'LINUX', + 'HAVE_LIBM=1', + 'HAVE_STRDUP=1', + 'HAVE_STRLCPY=1', + 'HAVE_SYS_TIME_H=1', + 'HAVE_VFPRINTF=1', + 'NEW_STDIO' + 'RETSIGTYPE=void', + 'TIME_WITH_SYS_TIME_H=1', + 'NO_REG_RPC=1', + '__UNUSED__=__attribute__((unused))', + ], + + 'include_dirs': [ + 'src/port/linux/include' + ], + 'sources': [ + './src/port/linux/include/csi_platform.h', + ], + }] + ] + }] +} + + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_timer.h b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_timer.h new file mode 100644 index 0000000000..544a3d44fd --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_timer.h @@ -0,0 +1,54 @@ +/** + async_timer.h + + + Copyright (C) 2004, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Sun Feb 22 19:35:24 2004 + */ + + +#ifndef _async_timer_h +#define _async_timer_h + + +int NR_async_timer_init(void); +int NR_async_timer_set(int delay_ms,NR_async_cb cb,void *cb_arg,char *function,int line,void **handle); +int NR_async_timer_cancel(void *handle); +int NR_async_timer_update_time(struct timeval *tv); +int NR_async_timer_next_timeout(int *delay_ms); +int NR_async_timer_sanity_check_for_cb_deleted(NR_async_cb cb,void *cb_arg); + +#define NR_ASYNC_TIMER_SET(d,c,a,hp) NR_async_timer_set(d,c,a,(char *)__FUNCTION__,__LINE__,hp) + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h new file mode 100644 index 0000000000..58bcd435eb --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h @@ -0,0 +1,83 @@ +/** + async_wait.h + + + Copyright (C) 2001-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Thu Dec 20 20:14:49 2001 + */ + + +#ifndef _async_wait_h +#define _async_wait_h + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <csi_platform.h> + +typedef void (*NR_async_cb)(NR_SOCKET resource,int how,void *arg); + +#define NR_ASYNC_WAIT_READ 0 +#define NR_ASYNC_WAIT_WRITE 1 + + +int NR_async_wait_init(void); +int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb,void *cb_arg, + char *function,int line); +int NR_async_cancel(NR_SOCKET sock,int how); +int NR_async_schedule(NR_async_cb cb,void *arg,char *function,int line); +int NR_async_event_wait(int *eventsp); +int NR_async_event_wait2(int *eventsp,struct timeval *tv); + + +#ifdef NR_DEBUG_ASYNC +#define NR_ASYNC_WAIT(s,h,cb,arg) do { \ +fprintf(stderr,"NR_async_wait(%d,%s,%s) at %s(%d)\n",s,#h,#cb,__FUNCTION__,__LINE__); \ + NR_async_wait(s,h,cb,arg,(char *)__FUNCTION__,__LINE__); \ +} while (0) +#define NR_ASYNC_SCHEDULE(cb,arg) do { \ +fprintf(stderr,"NR_async_schedule(%s) at %s(%d)\n",#cb,__FUNCTION__,__LINE__);\ + NR_async_schedule(cb,arg,(char *)__FUNCTION__,__LINE__); \ +} while (0) +#define NR_ASYNC_CANCEL(s,h) do { \ + fprintf(stderr,"NR_async_cancel(%d,%s) at %s(%d)\n",s,#h,(char *)__FUNCTION__,__LINE__); \ +NR_async_cancel(s,h); \ +} while (0) +#else +#define NR_ASYNC_WAIT(a,b,c,d) NR_async_wait(a,b,c,d,(char *)__FUNCTION__,__LINE__) +#define NR_ASYNC_SCHEDULE(a,b) NR_async_schedule(a,b,(char *)__FUNCTION__,__LINE__) +#define NR_ASYNC_CANCEL NR_async_cancel +#endif + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait_int.h b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait_int.h new file mode 100644 index 0000000000..d17372d183 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait_int.h @@ -0,0 +1,62 @@ +/** + async_wait_int.h + + + Copyright (C) 2004, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Sun Feb 22 19:16:01 2004 + */ + + +#ifndef _async_wait_int_h +#define _async_wait_int_h + + +typedef struct callback_ { + NR_async_cb cb; + void *arg; + int how; + int resource; + void *backptr; + + char *func; + int free_func; + int line; + TAILQ_ENTRY(callback_) entry; +} callback; + +int nr_async_create_cb(NR_async_cb cb,void *arg,int how, + int resource,char *func,int line,callback **cbp); +int nr_async_destroy_cb(callback **cbp); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c new file mode 100644 index 0000000000..09bb24749f --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c @@ -0,0 +1,696 @@ +/** + r_log.c + + + Copyright (C) 2001, RTFM, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Mon Dec 3 15:24:38 2001 + */ + +#ifdef LINUX +#define _BSD_SOURCE +#define _DEFAULT_SOURCE +#endif + +#include "r_log.h" +#include "hex.h" + +#include <string.h> +#include <errno.h> +#ifndef _MSC_VER +#include <strings.h> +#include <syslog.h> +#endif +#include <registry.h> +#include <time.h> + + +#include "nr_common.h" +#include "nr_reg_keys.h" + + +#define LOGGING_DEFAULT_LEVEL 5 + +int NR_LOG_LOGGING = 0; + +static char *log_level_strings[]={ + "EMERG", + "ALERT", + "CRIT", + "ERR", + "WARNING", + "NOTICE", + "INFO", + "DEBUG" +}; + +static char *log_level_reg_strings[]={ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug" +}; + +#define LOGGING_REG_PREFIX "logging" + +#define MAX_ERROR_STRING_SIZE 512 + +#define R_LOG_INITTED1 1 +#define R_LOG_INITTED2 2 + +#define LOG_NUM_DESTINATIONS 3 + +typedef struct log_type_ { + char *facility_name; + int level[LOG_NUM_DESTINATIONS]; + NR_registry dest_facility_key[LOG_NUM_DESTINATIONS]; +} log_type; + +#define MAX_LOG_TYPES 16 + +static log_type log_types[MAX_LOG_TYPES]; +static int log_type_ct; + + +typedef struct log_destination_ { + char *dest_name; + int enabled; + int default_level; + r_dest_vlog *dest_vlog; +} log_destination; + + +#define LOG_LEVEL_UNDEFINED -1 +#define LOG_LEVEL_NONE -2 +#define LOG_LEVEL_USE_DEST_DEFAULT -3 + +static int stderr_vlog(int facility,int level,const char *format,va_list ap); +static int syslog_vlog(int facility,int level,const char *format,va_list ap); +static int noop_vlog(int facility,int level,const char *format,va_list ap); + +static log_destination log_destinations[LOG_NUM_DESTINATIONS]={ + { + "stderr", + 0, + LOGGING_DEFAULT_LEVEL, + stderr_vlog, + }, + { + "syslog", +#ifndef WIN32 + 1, +#else + 0, +#endif + LOGGING_DEFAULT_LEVEL, + syslog_vlog, + }, + { + "extra", + 0, + LOGGING_DEFAULT_LEVEL, + noop_vlog, + }, +}; + +static int r_log_level=LOGGING_DEFAULT_LEVEL; +static int r_log_level_environment=0; +static int r_log_initted=0; +static int r_log_env_verbose=0; + +static void r_log_facility_change_cb(void *cb_arg, char action, NR_registry name); +static void r_log_facility_delete_cb(void *cb_arg, char action, NR_registry name); +static void r_log_destination_change_cb(void *cb_arg, char action, NR_registry name); +static void r_log_default_level_change_cb(void *cb_arg, char action, NR_registry name); +static int r_log_get_default_level(void); +static int r_log_get_destinations(int usereg); +static int r_logging_dest(int dest_index, int facility, int level); +static int _r_log_init(int usereg); +static int r_log_get_reg_level(NR_registry name, int *level); + +int r_log_register(char *facility_name,int *log_facility) + { + int i,j; + int level; + int r,_status; + char *buf=0; + NR_registry dest_prefix, dest_facility_prefix; + + for(i=0;i<log_type_ct;i++){ + if(!strcmp(facility_name,log_types[i].facility_name)){ + *log_facility=i; + return(0); + } + } + + if(log_type_ct==MAX_LOG_TYPES){ + ABORT(R_INTERNAL); + } + + i=log_type_ct; + + /* Initial registration completed, increment log_type_ct */ + log_types[i].facility_name=r_strdup(facility_name); + *log_facility=log_type_ct; + log_type_ct++; + + for(j=0; j<LOG_NUM_DESTINATIONS; j++){ + log_types[i].level[j]=LOG_LEVEL_UNDEFINED; + + if(NR_reg_initted()){ + + if((size_t)snprintf(dest_prefix,sizeof(NR_registry), + "logging.%s.facility",log_destinations[j].dest_name)>=sizeof(NR_registry)) + ABORT(R_INTERNAL); + + if (r=NR_reg_make_registry(dest_prefix,facility_name,dest_facility_prefix)) + ABORT(r); + + if((size_t)snprintf(log_types[i].dest_facility_key[j],sizeof(NR_registry), + "%s.level",dest_facility_prefix)>=sizeof(NR_registry)) + ABORT(R_INTERNAL); + + if(!r_log_get_reg_level(log_types[i].dest_facility_key[j],&level)){ + log_types[i].level[j]=level; + } + + /* Set a callback for the facility's level */ + if(r=NR_reg_register_callback(log_types[i].dest_facility_key[j], + NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE, + r_log_facility_change_cb,(void *)&(log_types[i].level[j]))) + ABORT(r); + if(r=NR_reg_register_callback(log_types[i].dest_facility_key[j], + NR_REG_CB_ACTION_DELETE, + r_log_facility_delete_cb,(void *)&(log_types[i].level[j]))) + ABORT(r); + + } + } + + _status=0; + abort: + if(_status) + RFREE(buf); + return(_status); + } + +int r_log_facility(int facility,char **typename) + { + if(facility >= 0 && facility < log_type_ct){ + *typename=log_types[facility].facility_name; + return(0); + } + return(R_NOT_FOUND); + } + +static int r_log_get_reg_level(NR_registry name, int *out) + { + char level[32]; + int r,_status; + int i; + + if(r=NR_reg_get_string(name,level,sizeof(level))) + ABORT(r); + + if(!strcasecmp(level,"none")){ + *out=LOG_LEVEL_NONE; + return(0); + } + + for(i=0;i<=LOG_DEBUG;i++){ + if(!strcasecmp(level,log_level_reg_strings[i])){ + *out=(int)i; + return(0); + } + } + + if(i>LOG_DEBUG){ + *out=LOG_LEVEL_UNDEFINED; + } + + _status=0; + abort: + return(_status); + } + +/* Handle the case where a value changes */ +static void r_log_facility_change_cb(void *cb_arg, char action, NR_registry name) + { + int *lt_level=(int *)cb_arg; + int level; + int r,_status; + + if(r=r_log_get_reg_level(name,&level)) + ABORT(r); + + *lt_level=level; + + _status=0; + abort: + (void)_status; // to avoid unused variable warning and still conform to + // pattern of using ABORT + return; + } + +/* Handle the case where a value is deleted */ +static void r_log_facility_delete_cb(void *cb_arg, char action, NR_registry name) + { + int *lt_level=(int *)cb_arg; + + *lt_level=LOG_LEVEL_UNDEFINED; + } + +int r_log(int facility,int level,const char *format,...) + { + va_list ap; + + va_start(ap,format); + + r_vlog(facility,level,format,ap); + va_end(ap); + + return(0); + } + +int r_dump(int facility,int level,char *name,char *data,int len) + { + char *hex = 0; + size_t unused; + + if(!r_logging(facility,level)) + return(0); + + hex=RMALLOC((len*2)+1); + if (!hex) + return(R_FAILED); + + if (nr_nbin2hex((UCHAR*)data, len, hex, len*2+1, &unused)) + strcpy(hex, "?"); + + if(name) + r_log(facility,level,"%s[%d]=%s",name,len,hex); + else + r_log(facility,level,"%s",hex); + + RFREE(hex); + return(0); + } + +// Some platforms (notably WIN32) do not have this +#ifndef va_copy + #ifdef WIN32 + #define va_copy(dest, src) ( (dest) = (src) ) + #else // WIN32 + #error va_copy undefined, and semantics of assignment on va_list unknown + #endif //WIN32 +#endif //va_copy + +int r_vlog(int facility,int level,const char *format,va_list ap) + { + char log_fmt_buf[MAX_ERROR_STRING_SIZE]; + char *level_str="unknown"; + char *facility_str="unknown"; + char *fmt_str=(char *)format; + int i; + + if(r_log_env_verbose){ + if((level>=LOG_EMERG) && (level<=LOG_DEBUG)) + level_str=log_level_strings[level]; + + if(facility >= 0 && facility < log_type_ct) + facility_str=log_types[facility].facility_name; + + snprintf(log_fmt_buf, MAX_ERROR_STRING_SIZE, "(%s/%s) %s", + facility_str,level_str,format); + + log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0; + fmt_str=log_fmt_buf; + } + + for(i=0; i<LOG_NUM_DESTINATIONS; i++){ + if(r_logging_dest(i,facility,level)){ + // Some platforms do not react well when you use a va_list more than + // once + va_list copy; + va_copy(copy, ap); + log_destinations[i].dest_vlog(facility,level,fmt_str,copy); + va_end(copy); + } + } + return(0); + } + +int stderr_vlog(int facility,int level,const char *format,va_list ap) + { +#if 0 /* remove time stamping, for now */ + char cbuf[30]; + time_t tt; + + tt=time(0); + + ctime_r(&tt,cbuf); + cbuf[strlen(cbuf)-1]=0; + + fprintf(stderr,"%s: ",cbuf); +#endif + + vfprintf(stderr,format,ap); + fprintf(stderr,"\n"); + return(0); + } + +int syslog_vlog(int facility,int level,const char *format,va_list ap) + { +#ifndef WIN32 + vsyslog(level|LOG_LOCAL0,format,ap); +#endif + return(0); + } + +int noop_vlog(int facility,int level,const char *format,va_list ap) + { + return(0); + } + +int r_log_e(int facility,int level,const char *format,...) + { + va_list ap; + + va_start(ap,format); + r_vlog_e(facility,level,format,ap); + va_end(ap); + + return(0); + } + +int r_vlog_e(int facility,int level,const char *format,va_list ap) + { + char log_fmt_buf[MAX_ERROR_STRING_SIZE]; + if(r_logging(facility,level)) { + int formatlen = strlen(format); + + if(formatlen+2 > MAX_ERROR_STRING_SIZE) + return(1); + + strncpy(log_fmt_buf, format, formatlen); + strcpy(&log_fmt_buf[formatlen], ": "); + snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s", +#ifdef WIN32 + strerror(WSAGetLastError())); +#else + strerror(errno)); +#endif + log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0; + + r_vlog(facility,level,log_fmt_buf,ap); + } + return(0); + } + +int r_log_nr(int facility,int level,int r,const char *format,...) + { + va_list ap; + + va_start(ap,format); + r_vlog_nr(facility,level,r,format,ap); + va_end(ap); + + return(0); + } + +int r_vlog_nr(int facility,int level,int r,const char *format,va_list ap) + { + char log_fmt_buf[MAX_ERROR_STRING_SIZE]; + if(r_logging(facility,level)) { + int formatlen = strlen(format); + + if(formatlen+2 > MAX_ERROR_STRING_SIZE) + return(1); + strncpy(log_fmt_buf, format, formatlen); + strcpy(&log_fmt_buf[formatlen], ": "); + snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s", + nr_strerror(r)); + + log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0; + + r_vlog(facility,level,log_fmt_buf,ap); + } + return(0); + } + +static int r_logging_dest(int dest_index, int facility, int level) + { + int thresh; + + _r_log_init(0); + + if(!log_destinations[dest_index].enabled) + return(0); + + if(level <= r_log_level_environment) + return(1); + + if(r_log_initted<R_LOG_INITTED2) + return(level<=r_log_level); + + if(facility < 0 || facility > log_type_ct) + thresh=r_log_level; + else{ + if(log_types[facility].level[dest_index]==LOG_LEVEL_NONE) + return(0); + + if(log_types[facility].level[dest_index]>=0) + thresh=log_types[facility].level[dest_index]; + else if(log_destinations[dest_index].default_level!=LOG_LEVEL_UNDEFINED) + thresh=log_destinations[dest_index].default_level; + else + thresh=r_log_level; + } + + if(level<=thresh) + return(1); + + return(0); + } + +int r_logging(int facility, int level) + { + int i; + + _r_log_init(0); + + /* return 1 if logging is on for any dest */ + + for(i=0; i<LOG_NUM_DESTINATIONS; i++){ + if(r_logging_dest(i,facility,level)) + return(1); + } + + return(0); + } + + +static int r_log_get_default_level(void) + { + char *log; + int _status; + + log=getenv("R_LOG_LEVEL"); + + if(log){ + r_log_level=atoi(log); + r_log_level_environment=atoi(log); + } + else{ + r_log_level=LOGGING_DEFAULT_LEVEL; + } + + _status=0; + //abort: + return(_status); + } + + +static int r_log_get_destinations(int usereg) + { + char *log; + int i; + int r,_status; + + log=getenv("R_LOG_DESTINATION"); + if(log){ + for(i=0; i<LOG_NUM_DESTINATIONS; i++) + log_destinations[i].enabled=!strcmp(log,log_destinations[i].dest_name); + } + else if(usereg){ + NR_registry reg_key; + int i; + int value; + char c; + + /* Get the data out of the registry */ + for(i=0; i<LOG_NUM_DESTINATIONS; i++){ + /* set callback for default level */ + if((size_t)snprintf(reg_key,sizeof(reg_key),"%s.%s.level",LOGGING_REG_PREFIX, + log_destinations[i].dest_name)>=sizeof(reg_key)) + ABORT(R_INTERNAL); + + NR_reg_register_callback(reg_key, + NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE|NR_REG_CB_ACTION_DELETE, + r_log_default_level_change_cb,0); + + if(r=r_log_get_reg_level(reg_key,&value)){ + if(r==R_NOT_FOUND) + log_destinations[i].default_level=LOG_LEVEL_UNDEFINED; + else + ABORT(R_INTERNAL); + } + else + log_destinations[i].default_level=value; + + /* set callback for the enabled key for this logging dest */ + if((size_t)snprintf(reg_key,sizeof(reg_key),"%s.%s.enabled",LOGGING_REG_PREFIX, + log_destinations[i].dest_name)>=sizeof(reg_key)) + ABORT(R_INTERNAL); + + NR_reg_register_callback(reg_key, + NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE|NR_REG_CB_ACTION_DELETE, + r_log_destination_change_cb,0); + + if(r=NR_reg_get_char(reg_key,&c)){ + if(r==R_NOT_FOUND) + log_destinations[i].enabled=0; + else + ABORT(r); + } + else + log_destinations[i].enabled=c; + } + } + + _status=0; + abort: + return(_status); + } + +static void r_log_destination_change_cb(void *cb_arg, char action, NR_registry name) + { + r_log_get_destinations(1); + } + +static void r_log_default_level_change_cb(void *cb_arg, char action, NR_registry name) + { + r_log_get_destinations(1); + } + + +int r_log_init() + { + _r_log_init(1); + + return 0; + } + +int _r_log_init(int use_reg) + { +#ifndef WIN32 + char *log; +#endif + + if(r_log_initted==0) { +#ifdef WIN32 + r_log_env_verbose=1; +#else + log=getenv("R_LOG_VERBOSE"); + if(log) + r_log_env_verbose=atoi(log); +#endif + + } + + if(!use_reg){ + if(r_log_initted<R_LOG_INITTED1){ + r_log_get_default_level(); + r_log_get_destinations(0); + + r_log_initted=R_LOG_INITTED1; + } + } + else{ + if(r_log_initted<R_LOG_INITTED2){ + int facility; + + r_log_get_default_level(); + r_log_get_destinations(1); + + r_log_register("generic",&facility); + r_log_register("logging",&NR_LOG_LOGGING); + + r_log_initted=R_LOG_INITTED2; + } + } + + return(0); + } + +int r_log_set_extra_destination(int default_level, r_dest_vlog *dest_vlog) + { + int i; + log_destination *dest = 0; + + for(i=0; i<LOG_NUM_DESTINATIONS; i++){ + if(!strcmp("extra",log_destinations[i].dest_name)){ + dest=&log_destinations[i]; + break; + } + } + + if(!dest) + return(R_INTERNAL); + + if (dest_vlog==0){ + dest->enabled=0; + dest->dest_vlog=noop_vlog; + } + else{ + dest->enabled=1; + dest->default_level=default_level; + dest->dest_vlog=dest_vlog; + } + + return(0); + } + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.h b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.h new file mode 100644 index 0000000000..a72dfa0667 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.h @@ -0,0 +1,85 @@ +/** + r_log.h + + + Copyright (C) 2001, RTFM, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Mon Dec 3 15:14:45 2001 + */ + + +#ifndef _r_log_h +#define _r_log_h + +#ifndef WIN32 +#include <syslog.h> +#endif +#include <stdarg.h> +#include <r_common.h> + +int r_log(int facility,int level,const char *fmt,...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 3, 4))) +#endif +; + +int r_vlog(int facility,int level,const char *fmt,va_list ap); +int r_dump(int facility,int level,char *name,char *data,int len); + +int r_log_e(int facility,int level,const char *fmt,...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 3, 4))) +#endif +; + +int r_vlog_e(int facility,int level,const char *fmt,va_list ap); +int r_log_nr(int facility,int level,int r,const char *fmt,...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 4, 5))) +#endif +; + +int r_vlog_nr(int facility,int level,int r,const char *fmt,va_list ap); + +int r_log_register(char *tipename,int *facility); +int r_log_facility(int facility,char **tipename); +int r_logging(int facility, int level); +int r_log_init(void); + +#define LOG_GENERIC 0 +#define LOG_COMMON 0 + +typedef int r_dest_vlog(int facility,int level,const char *format,va_list ap); +int r_log_set_extra_destination(int default_level, r_dest_vlog *dest_vlog); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/plugin/nr_plugin.h b/dom/media/webrtc/transport/third_party/nrappkit/src/plugin/nr_plugin.h new file mode 100644 index 0000000000..56da0624a6 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/plugin/nr_plugin.h @@ -0,0 +1,57 @@ +/** + nr_plugin.h + + + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@networkresonance.com Mon Jun 19 18:18:54 2006 + */ + + +#ifndef _nr_plugin_h +#define _nr_plugin_h + +typedef int (NR_plugin_hook)(void); + +typedef struct NR_plugin_hook_def_ { + char *type; + NR_plugin_hook *func; +} NR_plugin_hook_def; + +typedef struct NR_plugin_def_ { + int api_version; // Should be 1 + char *name; + char *version; + NR_plugin_hook_def *hooks; +} NR_plugin_def; + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/android_funcs.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/android_funcs.h new file mode 100644 index 0000000000..d48bbe1373 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/android_funcs.h @@ -0,0 +1,62 @@ +/** + linux_funcs.h + + + Copyright (C) 2004, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Mon Dec 13 16:28:22 2004 + */ + + +#ifndef _android_funcs_h +#define _android_funcs_h + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <unistd.h> + +#define ETHERTYPE_VLAN 0x8100 + +#define STDIO_BYTES_BUFFERED(fp) (fp->_IO_read_end - fp->_IO_read_ptr) + +size_t strlcat(char *dst, const char *src, size_t siz); +#ifndef strlcpy +#define strlcpy(a,b,c) \ + (strncpy((a),(b),(c)), \ + ((c)<= 0 ? 0 : ((a)[(c)-1]='\0')), \ + strlen((b))) +#endif + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/csi_platform.h new file mode 100644 index 0000000000..bcfb2bce77 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/csi_platform.h @@ -0,0 +1,55 @@ +/** + platform.h + + + Copyright (C) 2004, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Mon Dec 13 17:26:51 2004 + */ + + +#ifndef _platform_h +#define _platform_h + +#include <android_funcs.h> + +#ifdef NR_SOCKET_IS_VOID_PTR +typedef void* NR_SOCKET; +#else +typedef int NR_SOCKET; +#define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count)) +#define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count)) +#define NR_SOCKET_CLOSE(sock) close(sock) +#endif + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/sys/ttycom.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/sys/ttycom.h new file mode 100644 index 0000000000..852bf9103b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/sys/ttycom.h @@ -0,0 +1,38 @@ +/* + * + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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. + * + */ +#ifndef __ANDROID_TTYCOM_H +#define __ANDROID_TTYCOM_H + +#include <asm/ioctls.h> + +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/port-impl.mk b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/port-impl.mk new file mode 100644 index 0000000000..6704cfedf9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/port-impl.mk @@ -0,0 +1,31 @@ +# +# Copyright (C) 2006, Network Resonance, Inc. +# All Rights Reserved +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. Neither the name of Network Resonance, Inc. nor the name of any +# contributors to this software 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. +# +#EXTATTR_IMPL=xattr diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h new file mode 100644 index 0000000000..41f1a5b097 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h @@ -0,0 +1,57 @@ +/** + platform.h + + + Copyright (C) 2005, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + briank@network-resonance.com Tue Mar 15 14:28:12 PST 2005 + */ + + +#ifndef _platform_h +#define _platform_h + +#include <unistd.h> + +#define STDIO_BYTES_BUFFERED(fp) (fp->_r) + +#ifdef NR_SOCKET_IS_VOID_PTR +typedef void* NR_SOCKET; +#else +typedef int NR_SOCKET; +#define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count)) +#define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count)) +#define NR_SOCKET_CLOSE(sock) close(sock) +#endif + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include/sys/queue.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include/sys/queue.h new file mode 100644 index 0000000000..4d7b998a34 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include/sys/queue.h @@ -0,0 +1,562 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: src/sys/sys/queue.h,v 1.58 2004/04/07 04:19:49 imp Exp $ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +#include <stddef.h> + +#ifndef offsetof +#define offsetof(type, field) ((size_t)(&((type *)0)->field)) +#endif + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - - - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * + */ +#define QUEUE_MACRO_DEBUG 0 +#if QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + char * lastfile; + int lastline; + char * prevfile; + int prevline; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define TRACEBUF +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_NEXT(curelm, field) = \ + SLIST_NEXT(SLIST_NEXT(curelm, field), field); \ + } \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + if ((STAILQ_NEXT(curelm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((curelm), field);\ + } \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \ + if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT((elm)->field.tqe_next); \ + TRASHIT((elm)->field.tqe_prev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + + +#ifdef _KERNEL + +/* + * XXX insque() and remque() are an old way of handling certain queues. + * They bogusly assumes that all queue heads look alike. + */ + +struct quehead { + struct quehead *qh_link; + struct quehead *qh_rlink; +}; + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) + +static __inline void +insque(void *a, void *b) +{ + struct quehead *element = (struct quehead *)a, + *head = (struct quehead *)b; + + element->qh_link = head->qh_link; + element->qh_rlink = head; + head->qh_link = element; + element->qh_link->qh_rlink = element; +} + +static __inline void +remque(void *a) +{ + struct quehead *element = (struct quehead *)a; + + element->qh_link->qh_rlink = element->qh_rlink; + element->qh_rlink->qh_link = element->qh_link; + element->qh_rlink = 0; +} + +#else /* !(__GNUC__ || __INTEL_COMPILER) */ + +void insque(void *a, void *b); +void remque(void *a); + +#endif /* __GNUC__ || __INTEL_COMPILER */ + +#endif /* _KERNEL */ + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h new file mode 100644 index 0000000000..8aefaf9249 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h @@ -0,0 +1,55 @@ +/** + platform.h + + + Copyright (C) 2004, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Mon Dec 13 17:26:51 2004 + */ + + +#ifndef _platform_h +#define _platform_h + +#include <linux_funcs.h> + +#ifdef NR_SOCKET_IS_VOID_PTR +typedef void* NR_SOCKET; +#else +typedef int NR_SOCKET; +#define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count)) +#define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count)) +#define NR_SOCKET_CLOSE(sock) close(sock) +#endif + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/linux_funcs.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/linux_funcs.h new file mode 100644 index 0000000000..1619136306 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/linux_funcs.h @@ -0,0 +1,62 @@ +/** + linux_funcs.h + + + Copyright (C) 2004, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Mon Dec 13 16:28:22 2004 + */ + + +#ifndef _linux_funcs_h +#define _linux_funcs_h + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <unistd.h> + +#define ETHERTYPE_VLAN 0x8100 + +#define STDIO_BYTES_BUFFERED(fp) (fp->_IO_read_end - fp->_IO_read_ptr) + +size_t strlcat(char *dst, const char *src, size_t siz); +#ifndef strlcpy +#define strlcpy(a,b,c) \ + (strncpy((a),(b),(c)), \ + ((c)<= 0 ? 0 : ((a)[(c)-1]='\0')), \ + strlen((b))) +#endif + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h new file mode 100644 index 0000000000..6f8a3066d6 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h @@ -0,0 +1,38 @@ +/* + * + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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. + * + */ +#ifndef __LINUX_TTYCOM_H +#define __LINUX_TTYCOM_H + +#include <asm/ioctls.h> + +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/port-impl.mk b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/port-impl.mk new file mode 100644 index 0000000000..6704cfedf9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/port-impl.mk @@ -0,0 +1,31 @@ +# +# Copyright (C) 2006, Network Resonance, Inc. +# All Rights Reserved +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. Neither the name of Network Resonance, Inc. nor the name of any +# contributors to this software 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. +# +#EXTATTR_IMPL=xattr diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h new file mode 100644 index 0000000000..27c64c53d9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h @@ -0,0 +1,107 @@ +/** + platform.h + + + Copyright (C) 2005, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + briank@network-resonance.com Tue Mar 15 14:28:12 PST 2005 + */ + + +#ifndef _csi_platform_h +#define _csi_platform_h + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 // This prevents weird "'TryEnterCriticalSection': identifier not found" + // compiler errors when poco/win32_mutex.h is included +#endif + +#define UINT8 UBLAH_IGNORE_ME_PLEASE +#define INT8 BLAH_IGNORE_ME_PLEASE +#include <winsock2.h> +#undef UINT8 +#undef INT8 +#include <r_types.h> +#include <errno.h> + +#define strcasecmp _stricmp +#define strncasecmp _strnicmp + +#define strcasestr stristr + +/* Hack version of strlcpy (in util/util.c) */ +size_t strlcat(char *dst, const char *src, size_t siz); + +/* Hack version of getopt() (in util/getopt.c) */ +int getopt(int argc, char *argv[], char *opstring); +extern char *optarg; +extern int optind; +extern int opterr; + +/* Hack version of gettimeofday() (in util/util.c) */ +int gettimeofday(struct timeval *tv, void *tz); + +#ifdef NR_SOCKET_IS_VOID_PTR +typedef void* NR_SOCKET; +#else +typedef SOCKET NR_SOCKET; +#define NR_SOCKET_READ(sock,buf,count) recv((sock),(buf),(count),0) +#define NR_SOCKET_WRITE(sock,buf,count) send((sock),(buf),(count),0) +#define NR_SOCKET_CLOSE(sock) closesocket(sock) +#endif + +#ifndef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#endif + +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +// Until we refine the Windows port.... + +#define in_addr_t UINT4 + +#ifndef strlcpy +#define strlcpy(a,b,c) \ + (strncpy((a),(b),(c)), \ + ((c)<= 0 ? 0 : ((a)[(c)-1]='\0')), \ + strlen((b))) +#endif + +#endif /* _csi_platform_h */ + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.c new file mode 100644 index 0000000000..3e71ca37d9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.c @@ -0,0 +1,320 @@ +/* + * + * c2ru.c + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/c2ru.c,v $ + * $Revision: 1.3 $ + * $Date: 2007/06/26 22:37:50 $ + * + * c2r utility methods + * + * + * Copyright (C) 2005, Network Resonance, Inc. + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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 <sys/queue.h> +#include <string.h> +#include <registry.h> +#include "nr_common.h" +#include <r_errors.h> +#include <r_macros.h> +#include <ctype.h> +#include "c2ru.h" + + +#define NRGET(func, type, get) \ +int \ +func(NR_registry parent, char *child, type **out) \ +{ \ + int r, _status; \ + NR_registry registry; \ + type tmp; \ + \ + if ((r = nr_c2ru_make_registry(parent, child, registry))) \ + ABORT(r); \ + \ + if ((r = get(registry, &tmp))) { \ + if (r != R_NOT_FOUND) \ + ABORT(r); \ + *out = 0; \ + } \ + else { \ + *out = RCALLOC(sizeof(tmp)); \ + if (*out == 0) \ + ABORT(R_NO_MEMORY); \ + **out = tmp; \ + } \ + \ + _status = 0; \ +abort: \ + return (_status); \ +} + +int +nr_c2ru_get_char(NR_registry parent, char *child, char **out) +{ + int r, _status; + NR_registry registry; + char tmp; + + if ((r = nr_c2ru_make_registry(parent, child, registry))) + ABORT(r); + + if ((r = NR_reg_get_char(registry, &tmp))) { + if (r != R_NOT_FOUND) + ABORT(r); + *out = 0; + } + else { + *out = RCALLOC(sizeof(tmp)); + if (*out == 0) + ABORT(R_NO_MEMORY); + **out = tmp; + } + + _status = 0; +abort: + return (_status); +} +NRGET(nr_c2ru_get_uchar, UCHAR, NR_reg_get_uchar) +NRGET(nr_c2ru_get_int2, INT2, NR_reg_get_int2) +NRGET(nr_c2ru_get_uint2, UINT2, NR_reg_get_uint2) +NRGET(nr_c2ru_get_int4, INT4, NR_reg_get_int4) +NRGET(nr_c2ru_get_uint4, UINT4, NR_reg_get_uint4) +NRGET(nr_c2ru_get_int8, INT8, NR_reg_get_int8) +NRGET(nr_c2ru_get_uint8, UINT8, NR_reg_get_uint8) +NRGET(nr_c2ru_get_double, double, NR_reg_get_double) +NRGET(nr_c2ru_get_string, char*, NR_reg_alloc_string) +NRGET(nr_c2ru_get_data, Data, NR_reg_alloc_data) + + +#define NRSET(func, type, set) \ +int \ +func(NR_registry parent, char *child, type *in) \ +{ \ + int r, _status; \ + NR_registry registry; \ + \ + if (in == 0) \ + return 0; \ + \ + if ((r = nr_c2ru_make_registry(parent, child, registry))) \ + ABORT(r); \ + \ + if ((r = set(registry, *in))) \ + ABORT(r); \ + \ + _status = 0; \ +abort: \ + return (_status); \ +} + +NRSET(nr_c2ru_set_char, char, NR_reg_set_char) +NRSET(nr_c2ru_set_uchar, UCHAR, NR_reg_set_uchar) +NRSET(nr_c2ru_set_int2, INT2, NR_reg_set_int2) +NRSET(nr_c2ru_set_uint2, UINT2, NR_reg_set_uint2) +NRSET(nr_c2ru_set_int4, INT4, NR_reg_set_int4) +NRSET(nr_c2ru_set_uint4, UINT4, NR_reg_set_uint4) +NRSET(nr_c2ru_set_int8, INT8, NR_reg_set_int8) +NRSET(nr_c2ru_set_uint8, UINT8, NR_reg_set_uint8) +NRSET(nr_c2ru_set_double, double, NR_reg_set_double) +NRSET(nr_c2ru_set_string, char*, NR_reg_set_string) + +int +nr_c2ru_set_data(NR_registry parent, char *child, Data *in) +{ + int r, _status; + NR_registry registry; + + if (in == 0) + return 0; + + if ((r = nr_c2ru_make_registry(parent, child, registry))) + ABORT(r); + + if ((r = NR_reg_set_bytes(registry, in->data, in->len))) + ABORT(r); + + _status = 0; +abort: + return (_status); +} + +#define NRFREE(func, type) \ +int \ +func(type *in) \ +{ \ + if (in) \ + RFREE(in); \ + return 0; \ +} + +NRFREE(nr_c2ru_free_char, char) +NRFREE(nr_c2ru_free_uchar, UCHAR) +NRFREE(nr_c2ru_free_int2, INT2) +NRFREE(nr_c2ru_free_uint2, UINT2) +NRFREE(nr_c2ru_free_int4, INT4) +NRFREE(nr_c2ru_free_uint4, UINT4) +NRFREE(nr_c2ru_free_int8, INT8) +NRFREE(nr_c2ru_free_uint8, UINT8) +NRFREE(nr_c2ru_free_double, double) + + +int +nr_c2ru_free_string(char **in) +{ + if (*in) + RFREE(*in); + if (in) + RFREE(in); + return 0; +} + +int +nr_c2ru_free_data(Data *in) +{ + int r, _status; + + if (in) { + if ((r=r_data_destroy(&in))) + ABORT(r); + } + + _status = 0; +abort: + return (_status); +} + +int +nr_c2ru_get_children(NR_registry parent, char *child, void *ptr, size_t size, int (*get)(NR_registry, void*)) +{ + int r, _status; + NR_registry registry; + unsigned int count; + unsigned int i; + NR_registry name; + struct entry { TAILQ_ENTRY(entry) entries; } *entry; + TAILQ_HEAD(, entry) *tailq = (void*)ptr; + + TAILQ_INIT(tailq); + + if ((r=nr_c2ru_make_registry(parent, child, registry))) + ABORT(r); + + if ((r=NR_reg_get_child_count(registry, &count))) { + if (r != R_NOT_FOUND) + ABORT(r); + } + else { + for (i = 0; i < count; ++i) { + if ((r=NR_reg_get_child_registry(registry, i, name))) { + /* ignore R_NOT_FOUND errors */ + if (r == R_NOT_FOUND) + continue; + else + ABORT(r); + } + + if ((r=get(name, &entry))) { + /* ignore R_NOT_FOUND errors */ + if (r == R_NOT_FOUND) + continue; + else + ABORT(r); + } + + TAILQ_INSERT_TAIL(tailq, entry, entries); + } + } + + _status = 0; +abort: + return (_status); +} + +int +nr_c2ru_set_children(NR_registry parent, char *child, void *ptr, int (*set)(NR_registry, void*), int (*label)(NR_registry, void*, char[NR_REG_MAX_NR_REGISTRY_LEN])) +{ + int r, _status; + NR_registry registry; + int i; + NR_registry name; + char buffer[NR_REG_MAX_NR_REGISTRY_LEN]; + struct entry { TAILQ_ENTRY(entry) entries; } *entry; + TAILQ_HEAD(, entry) *tailq = (void*)ptr; + + if ((r=nr_c2ru_make_registry(parent, child, registry))) + ABORT(r); + + (void)NR_reg_del(registry); + + i = 0; + TAILQ_FOREACH(entry, tailq, entries) { + if (label == 0 || (r=label(registry, entry, buffer))) { + snprintf(buffer, sizeof(buffer), "%d", i); + } + if ((r=nr_c2ru_make_registry(registry, buffer, name))) + ABORT(r); + + if ((r=set(name, entry))) + ABORT(r); + + ++i; + } + + _status = 0; +abort: + return (_status); +} + +int +nr_c2ru_free_children(void *ptr, int (*free)(void*)) +{ + struct entry { TAILQ_ENTRY(entry) entries; } *entry; + TAILQ_HEAD(, entry) *tailq = (void*)ptr; + + while (! TAILQ_EMPTY(tailq)) { + entry = TAILQ_FIRST(tailq); + TAILQ_REMOVE(tailq, entry, entries); + (void)free(entry); + } + + return 0; +} + +/* requires parent already in legal form */ +int +nr_c2ru_make_registry(NR_registry parent, char *child, NR_registry out) +{ + return NR_reg_make_registry(parent, child, out); +} diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.h b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.h new file mode 100644 index 0000000000..0f8c38ecc2 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.h @@ -0,0 +1,96 @@ +/* + * + * c2ru.h + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/c2ru.h,v $ + * $Revision: 1.2 $ + * $Date: 2006/08/16 19:39:13 $ + * + * c2r utility methods + * + * + * Copyright (C) 2005, Network Resonance, Inc. + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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. + * + * + */ + +#ifndef __C2RU_H__ +#define __C2RU_H__ + +#include <sys/types.h> +#include <r_types.h> +#include <r_data.h> +#include "registry_int.h" + +int nr_c2ru_get_char(NR_registry parent, char *child, char **out); +int nr_c2ru_get_uchar(NR_registry parent, char *child, UCHAR **out); +int nr_c2ru_get_int2(NR_registry parent, char *child, INT2 **out); +int nr_c2ru_get_uint2(NR_registry parent, char *child, UINT2 **out); +int nr_c2ru_get_int4(NR_registry parent, char *child, INT4 **out); +int nr_c2ru_get_uint4(NR_registry parent, char *child, UINT4 **out); +int nr_c2ru_get_int8(NR_registry parent, char *child, INT8 **out); +int nr_c2ru_get_uint8(NR_registry parent, char *child, UINT8 **out); +int nr_c2ru_get_double(NR_registry parent, char *child, double **out); +int nr_c2ru_get_data(NR_registry parent, char *child, Data **out); +int nr_c2ru_get_string(NR_registry parent, char *child, char ***out); + +int nr_c2ru_set_char(NR_registry parent, char *child, char *data); +int nr_c2ru_set_uchar(NR_registry parent, char *child, UCHAR *data); +int nr_c2ru_set_int2(NR_registry parent, char *child, INT2 *data); +int nr_c2ru_set_uint2(NR_registry parent, char *child, UINT2 *data); +int nr_c2ru_set_int4(NR_registry parent, char *child, INT4 *data); +int nr_c2ru_set_uint4(NR_registry parent, char *child, UINT4 *data); +int nr_c2ru_set_int8(NR_registry parent, char *child, INT8 *data); +int nr_c2ru_set_uint8(NR_registry parent, char *child, UINT8 *data); +int nr_c2ru_set_double(NR_registry parent, char *child, double *data); +int nr_c2ru_set_registry(NR_registry parent, char *child); +int nr_c2ru_set_data(NR_registry parent, char *child, Data *data); +int nr_c2ru_set_string(NR_registry parent, char *child, char **data); + +int nr_c2ru_free_char(char *data); +int nr_c2ru_free_uchar(UCHAR *data); +int nr_c2ru_free_int2(INT2 *data); +int nr_c2ru_free_uint2(UINT2 *data); +int nr_c2ru_free_int4(INT4 *data); +int nr_c2ru_free_uint4(UINT4 *data); +int nr_c2ru_free_int8(INT8 *data); +int nr_c2ru_free_uint8(UINT8 *data); +int nr_c2ru_free_double(double *data); +int nr_c2ru_free_data(Data *data); +int nr_c2ru_free_string(char **data); + +int nr_c2ru_get_children(NR_registry parent, char *child, void *ptr, size_t size, int (*get)(NR_registry, void*)); +int nr_c2ru_set_children(NR_registry parent, char *child, void *ptr, int (*set)(NR_registry, void*), int (*label)(NR_registry, void*, char[NR_REG_MAX_NR_REGISTRY_LEN])); +int nr_c2ru_free_children(void *ptr, int (*free)(void*)); + +int nr_c2ru_make_registry(NR_registry parent, char *child, NR_registry out); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c new file mode 100644 index 0000000000..709b1c3fb7 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c @@ -0,0 +1,604 @@ +/* + * + * registry.c + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry.c,v $ + * $Revision: 1.6 $ + * $Date: 2007/11/21 00:09:12 $ + * + * Datastore for tracking configuration and related info. + * + * + * Copyright (C) 2005, Network Resonance, Inc. + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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 <assert.h> +#include <string.h> +#ifndef _MSC_VER +#include <strings.h> +#include <sys/param.h> +#include <netinet/in.h> +#endif +#ifdef OPENSSL +#include <openssl/ssl.h> +#endif +#include <ctype.h> +#include "registry.h" +#include "registry_int.h" +#include "registry_vtbl.h" +#include "r_assoc.h" +#include "nr_common.h" +#include "r_log.h" +#include "r_errors.h" +#include "r_macros.h" +#include "c2ru.h" + +/* vtbl used to switch hit between local and remote invocations */ +static nr_registry_module *reg_vtbl = 0; + +/* must be in the order the types are numbered */ +static char *typenames[] = { "char", "UCHAR", "INT2", "UINT2", "INT4", "UINT4", "INT8", "UINT8", "double", "Data", "string", "registry" }; + +int NR_LOG_REGISTRY=0; + +NR_registry NR_TOP_LEVEL_REGISTRY = ""; + +int +NR_reg_init(void *mode) +{ + int r, _status; + nr_registry_module *module = (nr_registry_module*)mode; +#ifdef SANITY_CHECKS + NR_registry registry; +#endif + + if (reg_vtbl) { + if (reg_vtbl != module) { + r_log(LOG_GENERIC,LOG_ERR,"Can't reinitialize registry in different mode"); + ABORT(R_INTERNAL); + } + + return(0); + } + + reg_vtbl = module; + + if ((r=reg_vtbl->vtbl->init(mode))) + ABORT(r); + +#ifdef SANITY_CHECKS + if ((r=NR_reg_get_registry(NR_TOP_LEVEL_REGISTRY, registry))) + ABORT(r); + assert(strcmp(registry, NR_TOP_LEVEL_REGISTRY) == 0); +#endif + + r_log_init(); + r_log_register("registry",&NR_LOG_REGISTRY); + + _status=0; + abort: + r_log(NR_LOG_REGISTRY, + (_status ? LOG_ERR : LOG_INFO), + (_status ? "Couldn't initialize registry" : "Initialized registry")); + return(_status); +} + +int +NR_reg_initted(void) +{ + return reg_vtbl!=0; +} + +#define NRREGGET(func, method, type) \ +int \ +func(NR_registry name, type *out) \ +{ \ + return reg_vtbl->vtbl->method(name, out); \ +} + +NRREGGET(NR_reg_get_char, get_char, char) +NRREGGET(NR_reg_get_uchar, get_uchar, UCHAR) +NRREGGET(NR_reg_get_int2, get_int2, INT2) +NRREGGET(NR_reg_get_uint2, get_uint2, UINT2) +NRREGGET(NR_reg_get_int4, get_int4, INT4) +NRREGGET(NR_reg_get_uint4, get_uint4, UINT4) +NRREGGET(NR_reg_get_int8, get_int8, INT8) +NRREGGET(NR_reg_get_uint8, get_uint8, UINT8) +NRREGGET(NR_reg_get_double, get_double, double) + +int +NR_reg_get_registry(NR_registry name, NR_registry out) +{ + return reg_vtbl->vtbl->get_registry(name, out); +} + +int +NR_reg_get_bytes(NR_registry name, UCHAR *out, size_t size, size_t *length) +{ + return reg_vtbl->vtbl->get_bytes(name, out, size, length); +} + +int +NR_reg_get_string(NR_registry name, char *out, size_t size) +{ + return reg_vtbl->vtbl->get_string(name, out, size); +} + +int +NR_reg_get_length(NR_registry name, size_t *length) +{ + return reg_vtbl->vtbl->get_length(name, length); +} + +int +NR_reg_get_type(NR_registry name, NR_registry_type type) +{ + return reg_vtbl->vtbl->get_type(name, type); +} + +#define NRREGSET(func, method, type) \ +int \ +func(NR_registry name, type data) \ +{ \ + return reg_vtbl->vtbl->method(name, data); \ +} + +NRREGSET(NR_reg_set_char, set_char, char) +NRREGSET(NR_reg_set_uchar, set_uchar, UCHAR) +NRREGSET(NR_reg_set_int2, set_int2, INT2) +NRREGSET(NR_reg_set_uint2, set_uint2, UINT2) +NRREGSET(NR_reg_set_int4, set_int4, INT4) +NRREGSET(NR_reg_set_uint4, set_uint4, UINT4) +NRREGSET(NR_reg_set_int8, set_int8, INT8) +NRREGSET(NR_reg_set_uint8, set_uint8, UINT8) +NRREGSET(NR_reg_set_double, set_double, double) +NRREGSET(NR_reg_set_string, set_string, char*) + +int +NR_reg_set_registry(NR_registry name) +{ + return reg_vtbl->vtbl->set_registry(name); +} + +int +NR_reg_set_bytes(NR_registry name, unsigned char *data, size_t length) +{ + return reg_vtbl->vtbl->set_bytes(name, data, length); +} + + +int +NR_reg_del(NR_registry name) +{ + return reg_vtbl->vtbl->del(name); +} + +int +NR_reg_fin(NR_registry name) +{ + return reg_vtbl->vtbl->fin(name); +} + +int +NR_reg_get_child_count(NR_registry parent, unsigned int *count) +{ + assert(sizeof(count) == sizeof(size_t)); + return reg_vtbl->vtbl->get_child_count(parent, (size_t*)count); +} + +int +NR_reg_get_child_registry(NR_registry parent, unsigned int i, NR_registry child) +{ + int r, _status; + size_t count; + NR_registry *children=0; + + if ((r=reg_vtbl->vtbl->get_child_count(parent, &count))) + ABORT(r); + + if (i >= count) + ABORT(R_NOT_FOUND); + else { + count++; + children = (NR_registry *)RCALLOC(count * sizeof(NR_registry)); + if (!children) + ABORT(R_NO_MEMORY); + + if ((r=reg_vtbl->vtbl->get_children(parent, children, count, &count))) + ABORT(r); + + if (i >= count) + ABORT(R_NOT_FOUND); + + strncpy(child, children[i], sizeof(NR_registry)); + } + + _status=0; + abort: + RFREE(children); + return(_status); +} + +int +NR_reg_get_children(NR_registry parent, NR_registry *children, size_t size, size_t *length) +{ + return reg_vtbl->vtbl->get_children(parent, children, size, length); +} + +int +NR_reg_dump() +{ + int r, _status; + + if ((r=reg_vtbl->vtbl->dump(0))) + ABORT(r); + + _status=0; + abort: + return(_status); +} + +// convenience methods, call RFREE on the returned data +int +NR_reg_alloc_data(NR_registry name, Data *data) +{ + int r, _status; + size_t length; + UCHAR *tmp = 0; + size_t sanity_check; + + if ((r=NR_reg_get_length(name, &length))) + ABORT(r); + + if (!(tmp = (void*)RMALLOC(length))) + ABORT(R_NO_MEMORY); + + if ((r=NR_reg_get_bytes(name, tmp, length, &sanity_check))) + ABORT(r); + + assert(length == sanity_check); + + data->len = length; + data->data = tmp; + + _status=0; + abort: + if (_status) { + if (tmp) RFREE(tmp); + } + return(_status); +} + +int +NR_reg_alloc_string(NR_registry name, char **data) +{ + int r, _status; + size_t length; + char *tmp = 0; + + if ((r=NR_reg_get_length(name, &length))) + ABORT(r); + + if (!(tmp = (void*)RMALLOC(length+1))) + ABORT(R_NO_MEMORY); + + if ((r=NR_reg_get_string(name, tmp, length+1))) + ABORT(r); + + assert(length == strlen(tmp)); + + *data = tmp; + + _status=0; + abort: + if (_status) { + if (tmp) RFREE(tmp); + } + return(_status); +} + + +char * +nr_reg_type_name(int type) +{ + if ((type < NR_REG_TYPE_CHAR) || (type > NR_REG_TYPE_REGISTRY)) + return(NULL); + + return(typenames[type]); +} + +int +nr_reg_compute_type(char *typename, int *type) +{ + int _status; + size_t i; + +#ifdef SANITY_CHECKS + assert(!strcasecmp(typenames[NR_REG_TYPE_CHAR], "char")); + assert(!strcasecmp(typenames[NR_REG_TYPE_UCHAR], "UCHAR")); + assert(!strcasecmp(typenames[NR_REG_TYPE_INT2], "INT2")); + assert(!strcasecmp(typenames[NR_REG_TYPE_UINT2], "UINT2")); + assert(!strcasecmp(typenames[NR_REG_TYPE_INT4], "INT4")); + assert(!strcasecmp(typenames[NR_REG_TYPE_UINT4], "UINT4")); + assert(!strcasecmp(typenames[NR_REG_TYPE_INT8], "INT8")); + assert(!strcasecmp(typenames[NR_REG_TYPE_UINT8], "UINT8")); + assert(!strcasecmp(typenames[NR_REG_TYPE_DOUBLE], "double")); + assert(!strcasecmp(typenames[NR_REG_TYPE_BYTES], "Data")); + assert(!strcasecmp(typenames[NR_REG_TYPE_STRING], "string")); + assert(!strcasecmp(typenames[NR_REG_TYPE_REGISTRY], "registry")); + assert(sizeof(typenames)/sizeof(*typenames) == (NR_REG_TYPE_REGISTRY+1)); +#endif + + for (i = 0; i < sizeof(typenames)/sizeof(*typenames); ++i) { + if (!strcasecmp(typenames[i], typename)) { + *type = i; + return 0; + } + } + ABORT(R_BAD_ARGS); + + _status=0; + abort: + return(_status); +} + +/* More convenience functions: the same as their parents but they + take a prefix and a suffix */ +#define NRGET2(func, type, get) \ +int \ +func(NR_registry parent, char *child, type *out) \ +{ \ + int r, _status; \ + NR_registry registry; \ + \ + if ((r = NR_reg_make_registry(parent, child, registry))) \ + ABORT(r); \ + \ + if ((r = get(registry, out))) { \ + ABORT(r); \ + } \ + \ + _status = 0; \ +abort: \ + return (_status); \ +} + +NRGET2(NR_reg_get2_char, char, NR_reg_get_char) +NRGET2(NR_reg_get2_uchar, UCHAR, NR_reg_get_uchar) +NRGET2(NR_reg_get2_int2, INT2, NR_reg_get_int2) +NRGET2(NR_reg_get2_uint2, UINT2, NR_reg_get_uint2) +NRGET2(NR_reg_get2_int4, INT4, NR_reg_get_int4) +NRGET2(NR_reg_get2_uint4, UINT4, NR_reg_get_uint4) +NRGET2(NR_reg_get2_int8, INT8, NR_reg_get_int8) +NRGET2(NR_reg_get2_uint8, UINT8, NR_reg_get_uint8) +NRGET2(NR_reg_get2_double, double, NR_reg_get_double) +NRGET2(NR_reg_alloc2_string, char*, NR_reg_alloc_string) +NRGET2(NR_reg_alloc2_data, Data, NR_reg_alloc_data) + +int +NR_reg_get2_bytes(NR_registry parent, char *child, UCHAR *out, size_t size, size_t *length) +{ + int r, _status; + NR_registry registry; + + if ((r=NR_reg_make_registry(parent, child, registry))) + ABORT(r); + + if ((r=NR_reg_get_bytes(registry, out, size, length))) + ABORT(r); + + _status = 0; +abort: + return (_status); +} + +int +NR_reg_get2_string(NR_registry parent, char *child, char *out, size_t size) +{ + int r, _status; + NR_registry registry; + + if ((r=NR_reg_make_registry(parent, child, registry))) + ABORT(r); + + if ((r=NR_reg_get_string(registry, out, size))) + ABORT(r); + + _status = 0; +abort: + return (_status); +} + +/* More convenience functions: the same as their parents but they + take a prefix and a suffix */ +#define NRSET2(func, type, set) \ +int \ +func(NR_registry parent, char *child, type in) \ +{ \ + int r, _status; \ + NR_registry registry; \ + \ + if ((r = NR_reg_make_registry(parent, child, registry))) \ + ABORT(r); \ + \ + if ((r = set(registry, in))) { \ + ABORT(r); \ + } \ + \ + _status = 0; \ +abort: \ + return (_status); \ +} + +NRSET2(NR_reg_set2_char, char, NR_reg_set_char) +NRSET2(NR_reg_set2_uchar, UCHAR, NR_reg_set_uchar) +NRSET2(NR_reg_set2_int2, INT2, NR_reg_set_int2) +NRSET2(NR_reg_set2_uint2, UINT2, NR_reg_set_uint2) +NRSET2(NR_reg_set2_int4, INT4, NR_reg_set_int4) +NRSET2(NR_reg_set2_uint4, UINT4, NR_reg_set_uint4) +NRSET2(NR_reg_set2_int8, INT8, NR_reg_set_int8) +NRSET2(NR_reg_set2_uint8, UINT8, NR_reg_set_uint8) +NRSET2(NR_reg_set2_double, double, NR_reg_set_double) +NRSET2(NR_reg_set2_string, char*, NR_reg_set_string) + +int +NR_reg_set2_bytes(NR_registry prefix, char *name, UCHAR *data, size_t length) +{ + int r, _status; + NR_registry registry; + + if ((r = NR_reg_make_registry(prefix, name, registry))) + ABORT(r); + + if ((r = NR_reg_set_bytes(registry, data, length))) + ABORT(r); + + _status = 0; +abort: + return (_status); +} + + +int +NR_reg_make_child_registry(NR_registry parent, NR_registry descendant, unsigned int generation, NR_registry child) +{ + int _status; + size_t length; + + length = strlen(parent); + + if (strncasecmp(parent, descendant, length)) + ABORT(R_BAD_ARGS); + + while (descendant[length] != '\0') { + if (descendant[length] == '.') { + if (generation == 0) + break; + + --generation; + } + + ++length; + if (length >= sizeof(NR_registry)) + ABORT(R_BAD_ARGS); + } + + strncpy(child, descendant, length); + child[length] = '\0'; + + _status=0; + abort: + return(_status); +} + +int +NR_reg_get2_child_count(NR_registry base, NR_registry name, unsigned int *count) + { + int r, _status; + NR_registry registry; + + if ((r=nr_c2ru_make_registry(base, name, registry))) + ABORT(r); + + if (r=NR_reg_get_child_count(registry,count)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int +NR_reg_get2_child_registry(NR_registry base, NR_registry name, unsigned int i, NR_registry child) + { + int r, _status; + NR_registry registry; + + if ((r=nr_c2ru_make_registry(base, name, registry))) + ABORT(r); + + if (r=NR_reg_get_child_registry(registry, i, child)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + + +/* requires parent already in legal form */ +int +NR_reg_make_registry(NR_registry parent, char *child, NR_registry out) +{ + int r, _status; + size_t plen; + size_t clen; + char *c; + size_t i; + + if ((r=nr_reg_is_valid(parent))) + ABORT(r); + + if (*child == '.') + ABORT(R_BAD_ARGS); + + clen = strlen(child); + if (!clen) + ABORT(R_BAD_ARGS); + plen = strlen(parent); + if ((plen + clen + 2) > sizeof(NR_registry)) + ABORT(R_BAD_ARGS); + + if (out != parent) + strcpy(out, parent); + + c = &(out[plen]); + + if (parent[0] != '\0') { + *c = '.'; + ++c; + } + + for (i = 0; i < clen; ++i, ++c) { + *c = child[i]; + if (isspace(*c) || *c == '.' || *c == '/' || ! isprint(*c)) + *c = '_'; + } + + *c = '\0'; + + _status = 0; +abort: + return _status; +} + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.h b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.h new file mode 100644 index 0000000000..b48893ba72 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.h @@ -0,0 +1,154 @@ +/* + * + * registry.h + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry.h,v $ + * $Revision: 1.3 $ + * $Date: 2007/07/17 17:58:16 $ + * + * Datastore for tracking configuration and related info. + * + * + * Copyright (C) 2005, Network Resonance, Inc. + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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. + * + * + */ + +#ifndef __REGISTRY_H__ +#define __REGISTRY_H__ + +#include <stdio.h> +#include <sys/types.h> +#include <r_types.h> +#include <r_data.h> + +#define NR_REG_MAX_NR_REGISTRY_LEN 128 +#define NR_REG_MAX_TYPE_LEN 32 + +typedef char NR_registry[NR_REG_MAX_NR_REGISTRY_LEN]; +typedef char NR_registry_type[NR_REG_MAX_TYPE_LEN]; + +extern NR_registry NR_TOP_LEVEL_REGISTRY; + +extern void *NR_REG_MODE_LOCAL; +extern void *NR_REG_MODE_REMOTE; + +int NR_reg_init(void *mode); + +int NR_reg_initted(void); + +int NR_reg_get_char(NR_registry name, char *out); +int NR_reg_get_uchar(NR_registry name, UCHAR *out); +int NR_reg_get_int2(NR_registry name, INT2 *out); +int NR_reg_get_uint2(NR_registry name, UINT2 *out); +int NR_reg_get_int4(NR_registry name, INT4 *out); +int NR_reg_get_uint4(NR_registry name, UINT4 *out); +int NR_reg_get_int8(NR_registry name, INT8 *out); +int NR_reg_get_uint8(NR_registry name, UINT8 *out); +int NR_reg_get_double(NR_registry name, double *out); +int NR_reg_get_registry(NR_registry name, NR_registry out); + +int NR_reg_get_bytes(NR_registry name, UCHAR *out, size_t size, size_t *length); +int NR_reg_get_string(NR_registry name, char *out, size_t size); +int NR_reg_get_length(NR_registry name, size_t *length); +int NR_reg_get_type(NR_registry name, NR_registry_type type); + + +int NR_reg_get2_char(NR_registry prefix, char *name, char *); +int NR_reg_get2_uchar(NR_registry prefix, char *name, UCHAR *); +int NR_reg_get2_int2(NR_registry prefix, char *name, INT2 *); +int NR_reg_get2_uint2(NR_registry prefix, char *name, UINT2 *); +int NR_reg_get2_int4(NR_registry prefix, char *name, INT4 *); +int NR_reg_get2_uint4(NR_registry prefix, char *name, UINT4 *); +int NR_reg_get2_int8(NR_registry prefix, char *name, INT8 *); +int NR_reg_get2_uint8(NR_registry prefix, char *name, UINT8 *); +int NR_reg_get2_double(NR_registry prefix, char *name, double *); +int NR_reg_get2_bytes(NR_registry prefix, char *name, UCHAR *out, size_t size, size_t *length); +int NR_reg_get2_string(NR_registry prefix, char *name, char *out, size_t size); + +int NR_reg_alloc2_string(NR_registry prefix, char *name, char **); +int NR_reg_alloc2_data(NR_registry prefix, char *name, Data *); + +int NR_reg_set_char(NR_registry name, char data); +int NR_reg_set_uchar(NR_registry name, UCHAR data); +int NR_reg_set_int2(NR_registry name, INT2 data); +int NR_reg_set_uint2(NR_registry name, UINT2 data); +int NR_reg_set_int4(NR_registry name, INT4 data); +int NR_reg_set_uint4(NR_registry name, UINT4 data); +int NR_reg_set_int8(NR_registry name, INT8 data); +int NR_reg_set_uint8(NR_registry name, UINT8 data); +int NR_reg_set_double(NR_registry name, double data); + +int NR_reg_set_registry(NR_registry name); + +int NR_reg_set_bytes(NR_registry name, UCHAR *data, size_t length); +int NR_reg_set_string(NR_registry name, char *data); + +int NR_reg_set2_char(NR_registry prefix, char *name, char data); +int NR_reg_set2_uchar(NR_registry prefix, char *name, UCHAR data); +int NR_reg_set2_int2(NR_registry prefix, char *name, INT2 data); +int NR_reg_set2_uint2(NR_registry prefix, char *name, UINT2 data); +int NR_reg_set2_int4(NR_registry prefix, char *name, INT4 data); +int NR_reg_set2_uint4(NR_registry prefix, char *name, UINT4 data); +int NR_reg_set2_int8(NR_registry prefix, char *name, INT8 data); +int NR_reg_set2_uint8(NR_registry prefix, char *name, UINT8 data); +int NR_reg_set2_double(NR_registry prefix, char *name, double data); + +int NR_reg_set2_bytes(NR_registry prefix, char *name, UCHAR *data, size_t length); +int NR_reg_set2_string(NR_registry prefix, char *name, char *data); + +int NR_reg_del(NR_registry name); + +int NR_reg_fin(NR_registry name); + +int NR_reg_get_child_count(NR_registry parent, unsigned int *count); +int NR_reg_get_child_registry(NR_registry parent, unsigned int i, NR_registry child); +int NR_reg_get2_child_count(NR_registry base, NR_registry name, unsigned int *count); +int NR_reg_get2_child_registry(NR_registry base, NR_registry name, unsigned int i, NR_registry child); +int NR_reg_get_children(NR_registry parent, NR_registry children[], size_t size, size_t *length); + +int NR_reg_dump(void); + +/* convenience methods, call RFREE on the returned data */ +int NR_reg_alloc_data(NR_registry name, Data *data); +int NR_reg_alloc_string(NR_registry name, char **data); + +#define NR_REG_CB_ACTION_ADD (1<<0) +#define NR_REG_CB_ACTION_CHANGE (1<<1) +#define NR_REG_CB_ACTION_DELETE (1<<2) +#define NR_REG_CB_ACTION_FINAL (1<<6) +int NR_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg); +int NR_reg_unregister_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name)); + +int NR_reg_make_registry(NR_registry parent, char *child, NR_registry out); +int NR_reg_make_child_registry(NR_registry parent, NR_registry descendant, unsigned int generation, NR_registry child); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_int.h b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_int.h new file mode 100644 index 0000000000..d6d412c543 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_int.h @@ -0,0 +1,97 @@ +#if 0 +#define NR_LOG_REGISTRY BLAHBLAH() +#define LOG_REGISTRY BLAHBLAH() +static int BLAHBLAH() { +int blahblah; +r_log_register("registry",&blahblah); +return blahblah; +} +#endif + +/* + * + * registry_int.h + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry_int.h,v $ + * $Revision: 1.3 $ + * $Date: 2007/06/26 22:37:51 $ + * + * Callback-related functions + * + * + * Copyright (C) 2005, Network Resonance, Inc. + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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. + * + * + */ + +#ifndef __REGISTRY_INT_H__ +#define __REGISTRY_INT_H__ + +#include <sys/types.h> +#include <r_types.h> +#ifndef NO_REG_RPC +#include <rpc/rpc.h> +#endif + +extern int NR_LOG_REGISTRY; + +int nr_reg_is_valid(NR_registry name); + +#define NR_REG_TYPE_CHAR 0 +#define NR_REG_TYPE_UCHAR 1 +#define NR_REG_TYPE_INT2 2 +#define NR_REG_TYPE_UINT2 3 +#define NR_REG_TYPE_INT4 4 +#define NR_REG_TYPE_UINT4 5 +#define NR_REG_TYPE_INT8 6 +#define NR_REG_TYPE_UINT8 7 +#define NR_REG_TYPE_DOUBLE 8 +#define NR_REG_TYPE_BYTES 9 +#define NR_REG_TYPE_STRING 10 +#define NR_REG_TYPE_REGISTRY 11 +char *nr_reg_type_name(int type); +int nr_reg_compute_type(char *type_name, int *type); + +char *nr_reg_action_name(int action); + +int nr_reg_cb_init(void); +int nr_reg_client_cb_init(void); +int nr_reg_register_for_callbacks(int fd, int connect_to_port); +int nr_reg_raise_event(NR_registry name, int action); +#ifndef NO_REG_RPC +int nr_reg_get_client(CLIENT **client); +#endif + +#define CALLBACK_SERVER_ADDR "127.0.0.1" +#define CALLBACK_SERVER_PORT 8082 +#define CALLBACK_SERVER_BACKLOG 32 + +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_local.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_local.c new file mode 100644 index 0000000000..ed6e19aaa0 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_local.c @@ -0,0 +1,1168 @@ +/* + * + * registry.c + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry_local.c,v $ + * $Revision: 1.4 $ + * $Date: 2007/11/21 00:09:13 $ + * + * Datastore for tracking configuration and related info. + * + * + * Copyright (C) 2005, Network Resonance, Inc. + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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 <assert.h> +#include <string.h> +#ifndef WIN32 +#include <strings.h> +#include <sys/param.h> +#include <netinet/in.h> +#endif +#ifdef OPENSSL +#include <openssl/ssl.h> +#endif +#include <ctype.h> +#include "registry.h" +#include "registry_int.h" +#include "registry_vtbl.h" +#include "r_assoc.h" +#include "nr_common.h" +#include "r_log.h" +#include "r_errors.h" +#include "r_macros.h" + +/* if C were an object-oriented language, nr_scalar_registry_node and + * nr_array_registry_node would subclass nr_registry_node, but it isn't + * object-oriented language, so this is used in cases where the pointer + * could be of either type */ +typedef struct nr_registry_node_ { + unsigned char type; +} nr_registry_node; + +typedef struct nr_scalar_registry_node_ { + unsigned char type; + union { + char _char; + UCHAR _uchar; + INT2 _nr_int2; + UINT2 _nr_uint2; + INT4 _nr_int4; + UINT4 _nr_uint4; + INT8 _nr_int8; + UINT8 _nr_uint8; + double _double; + } scalar; +} nr_scalar_registry_node; + +/* string, bytes */ +typedef struct nr_array_registry_node_ { + unsigned char type; + struct { + unsigned int length; + unsigned char data[1]; + } array; +} nr_array_registry_node; + +static int nr_reg_local_init(nr_registry_module *me); +static int nr_reg_local_get_char(NR_registry name, char *data); +static int nr_reg_local_get_uchar(NR_registry name, UCHAR *data); +static int nr_reg_local_get_int2(NR_registry name, INT2 *data); +static int nr_reg_local_get_uint2(NR_registry name, UINT2 *data); +static int nr_reg_local_get_int4(NR_registry name, INT4 *data); +static int nr_reg_local_get_uint4(NR_registry name, UINT4 *data); +static int nr_reg_local_get_int8(NR_registry name, INT8 *data); +static int nr_reg_local_get_uint8(NR_registry name, UINT8 *data); +static int nr_reg_local_get_double(NR_registry name, double *data); +static int nr_reg_local_get_registry(NR_registry name, NR_registry data); +static int nr_reg_local_get_bytes(NR_registry name, UCHAR *data, size_t size, size_t *length); +static int nr_reg_local_get_string(NR_registry name, char *data, size_t size); +static int nr_reg_local_get_length(NR_registry name, size_t *len); +static int nr_reg_local_get_type(NR_registry name, NR_registry_type type); +static int nr_reg_local_set_char(NR_registry name, char data); +static int nr_reg_local_set_uchar(NR_registry name, UCHAR data); +static int nr_reg_local_set_int2(NR_registry name, INT2 data); +static int nr_reg_local_set_uint2(NR_registry name, UINT2 data); +static int nr_reg_local_set_int4(NR_registry name, INT4 data); +static int nr_reg_local_set_uint4(NR_registry name, UINT4 data); +static int nr_reg_local_set_int8(NR_registry name, INT8 data); +static int nr_reg_local_set_uint8(NR_registry name, UINT8 data); +static int nr_reg_local_set_double(NR_registry name, double data); +static int nr_reg_local_set_registry(NR_registry name); +static int nr_reg_local_set_bytes(NR_registry name, UCHAR *data, size_t length); +static int nr_reg_local_set_string(NR_registry name, char *data); +static int nr_reg_local_del(NR_registry name); +static int nr_reg_local_get_child_count(NR_registry parent, size_t *count); +static int nr_reg_local_get_children(NR_registry parent, NR_registry *data, size_t size, size_t *length); +static int nr_reg_local_fin(NR_registry name); +static int nr_reg_local_dump(int sorted); +static int nr_reg_insert_node(char *name, void *node); +static int nr_reg_change_node(char *name, void *node, void *old); +static int nr_reg_get(char *name, int type, void *out); +static int nr_reg_get_data(NR_registry name, nr_scalar_registry_node *node, void *out); +static int nr_reg_get_array(char *name, unsigned char type, UCHAR *out, size_t size, size_t *length); +static int nr_reg_set(char *name, int type, void *data); +static int nr_reg_set_array(char *name, unsigned char type, UCHAR *data, size_t length); +static int nr_reg_set_parent_registries(char *name); + +/* make these static OLD_REGISTRY */ +#if 0 +static int nr_reg_fetch_node(char *name, unsigned char type, nr_registry_node **node, int *free_node); +static char *nr_reg_alloc_node_data(char *name, nr_registry_node *node, int *freeit); +#else +int nr_reg_fetch_node(char *name, unsigned char type, nr_registry_node **node, int *free_node); +char *nr_reg_alloc_node_data(char *name, nr_registry_node *node, int *freeit); +#endif +static int nr_reg_rfree(void *ptr); +#if 0 /* Unused currently */ +static int nr_reg_noop(void *ptr); +#endif +static int nr_reg_compute_length(char *name, nr_registry_node *node, size_t *length); +char *nr_reg_action_name(int action); + +/* the registry, containing mappings like "foo.bar.baz" to registry + * nodes, which are either of type nr_scalar_registry_node or + * nr_array_registry_node */ +static r_assoc *nr_registry = 0; + +#if 0 /* Unused currently */ +static nr_array_registry_node nr_top_level_node; +#endif + +typedef struct nr_reg_find_children_arg_ { + size_t size; + NR_registry *children; + size_t length; +} nr_reg_find_children_arg; + +static int nr_reg_local_iter(NR_registry prefix, int (*action)(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node), void *ptr); +static int nr_reg_local_iter_delete(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node); +static int nr_reg_local_find_children(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node); +static int nr_reg_local_count_children(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node); +static int nr_reg_local_dump_print(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node); + + + +int +nr_reg_local_iter(NR_registry prefix, int (*action)(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node), void *ptr) +{ + int r, _status; + r_assoc_iterator iter; + char *name; + int namel; + nr_registry_node *node; + int prefixl; + + if (prefix == 0) + ABORT(R_INTERNAL); + + if ((r=r_assoc_init_iter(nr_registry, &iter))) + ABORT(r); + + prefixl = strlen(prefix); + + for (;;) { + if ((r=r_assoc_iter(&iter, (void*)&name, &namel, (void*)&node))) { + if (r == R_EOD) + break; + else + ABORT(r); + } + + /* subtract to remove the '\0' character from the string length */ + --namel; + + /* sanity check that the name is null-terminated */ + assert(namel >= 0); + assert(name[namel] == '\0'); + + if (namel < 0 || name[namel] != '\0' || node == 0) + break; + + /* 3 cases where action will be called: + * 1) prefix == "" + * 2) prefix == name + * 3) name == prefix + '.' + */ + if (prefixl == 0 + || ((namel == prefixl || (namel > prefixl && name[prefixl] == '.')) + && !strncmp(prefix, name, prefixl))) { + if ((r=action(ptr, &iter, prefix, name, node))) + ABORT(r); + } + } + + _status=0; + abort: + + return(_status); +} + +int +nr_reg_local_iter_delete(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node) +{ + int r, _status; + + if ((r=r_assoc_iter_delete(iter))) + ABORT(r); + + _status=0; + abort: + return(_status); +} + +int +nr_reg_local_find_children(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node) +{ + int _status; + int prefixl = strlen(prefix); + char *dot; + nr_reg_find_children_arg *arg = (void*)ptr; + + assert(sizeof(*(arg->children)) == sizeof(NR_registry)); + + /* only grovel through immediate children */ + if (prefixl == 0 || name[prefixl] == '.') { + if (name[prefixl] != '\0') { + dot = strchr(&name[prefixl+1], '.'); + if (dot == 0) { + strncpy(arg->children[arg->length], name, sizeof(NR_registry)-1); + ++arg->length; + + /* only grab as many as there are room for */ + if (arg->length >= arg->size) + ABORT(R_INTERRUPTED); + } + } + } + + _status = 0; + abort: + return _status; +} + +int +nr_reg_local_count_children(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node) +{ + int prefixl = strlen(prefix); + char *dot; + + /* only count children */ + if (name[prefixl] == '.') { + dot = strchr(&name[prefixl+1], '.'); + if (dot == 0) + ++(*(unsigned int *)ptr); + } + else if (name[0] != '\0') { + if (prefixl == 0) + ++(*(unsigned int *)ptr); + } + + return 0; +} + +int +nr_reg_local_dump_print(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node) +{ + int _status; + int freeit = 0; + char *data; + + /* only print leaf nodes */ + if (node->type != NR_REG_TYPE_REGISTRY) { + data = nr_reg_alloc_node_data(name, node, &freeit); + if (ptr) + fprintf((FILE*)ptr, "%s: %s\n", name, data); + else + r_log(NR_LOG_REGISTRY, LOG_INFO, "%s: %s", name, data); + if (freeit) + RFREE(data); + } + + _status=0; + //abort: + return(_status); +} + + +#if 0 /* Unused currently */ +int +nr_reg_noop(void *ptr) +{ + return 0; +} +#endif + +int +nr_reg_rfree(void *ptr) +{ + RFREE(ptr); + return 0; +} + +int +nr_reg_fetch_node(char *name, unsigned char type, nr_registry_node **node, int *free_node) +{ + int r, _status; + + *node = 0; + *free_node = 0; + + if ((r=nr_reg_is_valid(name))) + ABORT(r); + + if ((r=r_assoc_fetch(nr_registry, name, strlen(name)+1, (void*)node))) + ABORT(r); + + if ((*node)->type != type) + ABORT(R_FAILED); + + _status=0; + abort: + if (_status) { + if (*node) + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Couldn't fetch node '%s' ('%s'), found '%s' instead", + name, nr_reg_type_name(type), nr_reg_type_name((*node)->type)); + else + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Couldn't fetch node '%s' ('%s')", + name, nr_reg_type_name(type)); + } + else { + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Fetched node '%s' ('%s')", + name, nr_reg_type_name(type)); + } + return(_status); +} + +int +nr_reg_insert_node(char *name, void *node) +{ + int r, _status; + + if ((r=nr_reg_is_valid(name))) + ABORT(r); + + /* since the registry application is not multi-threaded, a node being + * inserted should always be a new node because the registry app must + * have looked for a node with this key but not found it, so it is + * being created/inserted now using R_ASSOC_NEW */ + if ((r=r_assoc_insert(nr_registry, name, strlen(name)+1, node, 0, nr_reg_rfree, R_ASSOC_NEW))) + ABORT(r); + + if ((r=nr_reg_set_parent_registries(name))) + ABORT(r); + + if ((r=nr_reg_raise_event(name, NR_REG_CB_ACTION_ADD))) + ABORT(r); + + _status=0; + abort: + if (r_logging(NR_LOG_REGISTRY, LOG_INFO)) { + int freeit; + char *data = nr_reg_alloc_node_data(name, (void*)node, &freeit); + r_log(NR_LOG_REGISTRY, LOG_INFO, + "insert '%s' (%s) %s: %s", name, + nr_reg_type_name(((nr_registry_node*)node)->type), + (_status ? "FAILED" : "succeeded"), data); + if (freeit) + RFREE(data); + } + return(_status); +} + +int +nr_reg_change_node(char *name, void *node, void *old) +{ + int r, _status; + + if ((r=nr_reg_is_valid(name))) + ABORT(r); + + if (old != node) { + if ((r=r_assoc_insert(nr_registry, name, strlen(name)+1, node, 0, nr_reg_rfree, R_ASSOC_REPLACE))) + ABORT(r); + } + + if ((r=nr_reg_raise_event(name, NR_REG_CB_ACTION_CHANGE))) + ABORT(r); + + _status=0; + abort: + if (r_logging(NR_LOG_REGISTRY, LOG_INFO)) { + int freeit; + char *data = nr_reg_alloc_node_data(name, (void*)node, &freeit); + r_log(NR_LOG_REGISTRY, LOG_INFO, + "change '%s' (%s) %s: %s", name, + nr_reg_type_name(((nr_registry_node*)node)->type), + (_status ? "FAILED" : "succeeded"), data); + if (freeit) + RFREE(data); + } + return(_status); +} + +char * +nr_reg_alloc_node_data(char *name, nr_registry_node *node, int *freeit) +{ + char *s = 0; + int len; + int alloc = 0; + unsigned int i; + + *freeit = 0; + + switch (node->type) { + default: + alloc = 100; /* plenty of room for any of the scalar types */ + break; + case NR_REG_TYPE_REGISTRY: + alloc = strlen(name) + 1; + break; + case NR_REG_TYPE_BYTES: + alloc = (2 * ((nr_array_registry_node*)node)->array.length) + 1; + break; + case NR_REG_TYPE_STRING: + alloc = 0; + break; + } + + if (alloc > 0) { + s = (void*)RMALLOC(alloc); + if (!s) + return ""; + + *freeit = 1; + } + + len = alloc; + + switch (node->type) { + case NR_REG_TYPE_CHAR: + i = ((nr_scalar_registry_node*)node)->scalar._char; + if (isprint(i) && ! isspace(i)) + snprintf(s, len, "%c", (char)i); + else + snprintf(s, len, "\\%03o", (char)i); + break; + case NR_REG_TYPE_UCHAR: + snprintf(s, len, "0x%02x", ((nr_scalar_registry_node*)node)->scalar._uchar); + break; + case NR_REG_TYPE_INT2: + snprintf(s, len, "%d", ((nr_scalar_registry_node*)node)->scalar._nr_int2); + break; + case NR_REG_TYPE_UINT2: + snprintf(s, len, "%u", ((nr_scalar_registry_node*)node)->scalar._nr_uint2); + break; + case NR_REG_TYPE_INT4: + snprintf(s, len, "%d", ((nr_scalar_registry_node*)node)->scalar._nr_int4); + break; + case NR_REG_TYPE_UINT4: + snprintf(s, len, "%u", ((nr_scalar_registry_node*)node)->scalar._nr_uint4); + break; + case NR_REG_TYPE_INT8: + snprintf(s, len, "%lld", ((nr_scalar_registry_node*)node)->scalar._nr_int8); + break; + case NR_REG_TYPE_UINT8: + snprintf(s, len, "%llu", ((nr_scalar_registry_node*)node)->scalar._nr_uint8); + break; + case NR_REG_TYPE_DOUBLE: + snprintf(s, len, "%#f", ((nr_scalar_registry_node*)node)->scalar._double); + break; + case NR_REG_TYPE_REGISTRY: + snprintf(s, len, "%s", name); + break; + case NR_REG_TYPE_BYTES: + for (i = 0; i < ((nr_array_registry_node*)node)->array.length; ++i) { + sprintf(&s[2*i], "%02x", ((nr_array_registry_node*)node)->array.data[i]); + } + break; + case NR_REG_TYPE_STRING: + s = (char*)((nr_array_registry_node*)node)->array.data; + break; + default: + assert(0); /* bad value */ + *freeit = 0; + s = ""; + break; + } + + return s; +} + +int +nr_reg_get(char *name, int type, void *out) +{ + int r, _status; + nr_scalar_registry_node *node = 0; + int free_node = 0; + + if ((r=nr_reg_fetch_node(name, type, (void*)&node, &free_node))) + ABORT(r); + + if ((r=nr_reg_get_data(name, node, out))) + ABORT(r); + + _status=0; + abort: + if (free_node) RFREE(node); + return(_status); +} + +int +nr_reg_get_data(NR_registry name, nr_scalar_registry_node *node, void *out) +{ + int _status; + + switch (node->type) { + case NR_REG_TYPE_CHAR: + *(char*)out = node->scalar._char; + break; + case NR_REG_TYPE_UCHAR: + *(UCHAR*)out = node->scalar._uchar; + break; + case NR_REG_TYPE_INT2: + *(INT2*)out = node->scalar._nr_int2; + break; + case NR_REG_TYPE_UINT2: + *(UINT2*)out = node->scalar._nr_uint2; + break; + case NR_REG_TYPE_INT4: + *(INT4*)out = node->scalar._nr_int4; + break; + case NR_REG_TYPE_UINT4: + *(UINT4*)out = node->scalar._nr_uint4; + break; + case NR_REG_TYPE_INT8: + *(INT8*)out = node->scalar._nr_int8; + break; + case NR_REG_TYPE_UINT8: + *(UINT8*)out = node->scalar._nr_uint8; + break; + case NR_REG_TYPE_DOUBLE: + *(double*)out = node->scalar._double; + break; + default: + ABORT(R_INTERNAL); + break; + } + + _status=0; + abort: + return(_status); +} + +int +nr_reg_get_array(char *name, unsigned char type, unsigned char *out, size_t size, size_t *length) +{ + int r, _status; + nr_array_registry_node *node = 0; + int free_node = 0; + + if ((r=nr_reg_fetch_node(name, type, (void*)&node, &free_node))) + ABORT(r); + + if (size < node->array.length) + ABORT(R_BAD_ARGS); + + if (out != 0) + memcpy(out, node->array.data, node->array.length); + if (length != 0) + *length = node->array.length; + + _status=0; + abort: + if (node && free_node) RFREE(node); + return(_status); +} + +int +nr_reg_set(char *name, int type, void *data) +{ + int r, _status; + nr_scalar_registry_node *node = 0; + int create_node = 0; + int changed = 0; + int free_node = 0; + + if ((r=nr_reg_fetch_node(name, type, (void*)&node, &free_node))) + if (r == R_NOT_FOUND) { + create_node = 1; + free_node = 1; + } + else + ABORT(r); + + if (create_node) { + if (!(node=(void*)RCALLOC(sizeof(nr_scalar_registry_node)))) + ABORT(R_NO_MEMORY); + + node->type = type; + } + else { + if (node->type != type) + ABORT(R_BAD_ARGS); + } + + switch (type) { +#define CASE(TYPE, _name, type) \ + case TYPE: \ + if (node->scalar._name != *(type*)data) { \ + node->scalar._name = *(type*)data; \ + if (! create_node) \ + changed = 1; \ + } \ + break; + CASE(NR_REG_TYPE_CHAR, _char, char) + CASE(NR_REG_TYPE_UCHAR, _uchar, UCHAR) + CASE(NR_REG_TYPE_INT2, _nr_int2, INT2) + CASE(NR_REG_TYPE_UINT2, _nr_uint2, UINT2) + CASE(NR_REG_TYPE_INT4, _nr_int4, INT4) + CASE(NR_REG_TYPE_UINT4, _nr_uint4, UINT4) + CASE(NR_REG_TYPE_INT8, _nr_int8, INT8) + CASE(NR_REG_TYPE_UINT8, _nr_uint8, UINT8) + CASE(NR_REG_TYPE_DOUBLE, _double, double) +#undef CASE + + case NR_REG_TYPE_REGISTRY: + /* do nothing */ + break; + + default: + ABORT(R_INTERNAL); + break; + } + + if (create_node) { + if ((r=nr_reg_insert_node(name, node))) + ABORT(r); + free_node = 0; + } + else { + if (changed) { + if ((r=nr_reg_change_node(name, node, node))) + ABORT(r); + free_node = 0; + } + } + + _status=0; + abort: + if (_status) { + if (node && free_node) RFREE(node); + } + return(_status); +} + +int +nr_reg_set_array(char *name, unsigned char type, UCHAR *data, size_t length) +{ + int r, _status; + nr_array_registry_node *old = 0; + nr_array_registry_node *node = 0; + int free_node = 0; + int added = 0; + int changed = 0; + + if ((r=nr_reg_fetch_node(name, type, (void*)&old, &free_node))) { + if (r != R_NOT_FOUND) + ABORT(r); + } + else { + assert(free_node == 0); + } + + if (old) { + if (old->type != type) + ABORT(R_BAD_ARGS); + + if (old->array.length != length + || memcmp(old->array.data, data, length)) { + changed = 1; + + if (old->array.length < length) { + if (!(node=(void*)RCALLOC(sizeof(nr_array_registry_node)+length))) + ABORT(R_NO_MEMORY); + } + else { + node = old; + } + } + } + else { + if (!(node=(void*)RCALLOC(sizeof(nr_array_registry_node)+length))) + ABORT(R_NO_MEMORY); + + added = 1; + } + + if (added || changed) { + node->type = type; + node->array.length = length; + memcpy(node->array.data, data, length); + } + + if (added) { + if ((r=nr_reg_insert_node(name, node))) + ABORT(r); + } + else if (changed) { + if ((r=nr_reg_change_node(name, node, old))) + ABORT(r); + } + + _status=0; + abort: + return(_status); +} + +int +nr_reg_set_parent_registries(char *name) +{ + int r, _status; + char *parent = 0; + char *dot; + + if ((parent = r_strdup(name)) == 0) + ABORT(R_NO_MEMORY); + + if ((dot = strrchr(parent, '.')) != 0) { + *dot = '\0'; + if ((r=NR_reg_set_registry(parent))) + ABORT(r); + } + + _status=0; + abort: + if (parent) RFREE(parent); + return(_status); +} + + + + + +/* NON-STATIC METHODS */ + +int +nr_reg_is_valid(NR_registry name) +{ + int _status; + unsigned int length; + unsigned int i; + + if (name == 0) + ABORT(R_BAD_ARGS); + + /* make sure the key is null-terminated */ + if (memchr(name, '\0', sizeof(NR_registry)) == 0) + ABORT(R_BAD_ARGS); + + length = strlen(name); + + /* cannot begin or end with a period */ + if (name[0] == '.') + ABORT(R_BAD_ARGS); + if (strlen(name) > 0 && name[length-1] == '.') + ABORT(R_BAD_ARGS); + + /* all characters cannot be space, and must be printable and not / */ + for (i = 0; i < length; ++i) { + if (isspace(name[i]) || !isprint(name[i]) || name[i] == '/') + ABORT(R_BAD_ARGS); + } + + _status=0; + abort: + if (_status) { + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "invalid name '%s'", name); + } + return(_status); +} + + +int +nr_reg_compute_length(char *name, nr_registry_node *in, size_t *length) +{ + int _status; + nr_array_registry_node *node = (nr_array_registry_node*)in; + + switch (node->type) { + case NR_REG_TYPE_STRING: + *length = node->array.length - 1; + break; + case NR_REG_TYPE_BYTES: + *length = node->array.length; + break; + case NR_REG_TYPE_CHAR: + *length = sizeof(char); + break; + case NR_REG_TYPE_UCHAR: + *length = sizeof(UCHAR); + break; + case NR_REG_TYPE_INT2: + case NR_REG_TYPE_UINT2: + *length = 2; + break; + case NR_REG_TYPE_INT4: + case NR_REG_TYPE_UINT4: + *length = 4; + break; + case NR_REG_TYPE_INT8: + case NR_REG_TYPE_UINT8: + *length = 8; + break; + case NR_REG_TYPE_DOUBLE: + *length = sizeof(double); + break; + case NR_REG_TYPE_REGISTRY: + *length = strlen(name); + break; + default: + ABORT(R_INTERNAL); + break; + } + + _status=0; + abort: + return(_status); +} + + +/* VTBL METHODS */ + +int +nr_reg_local_init(nr_registry_module *me) +{ + int r, _status; + + if (nr_registry == 0) { + if ((r=r_assoc_create(&nr_registry, r_assoc_crc32_hash_compute, 12))) + ABORT(r); + + if ((r=nr_reg_cb_init())) + ABORT(r); + + /* make sure NR_TOP_LEVEL_REGISTRY always exists */ + if ((r=nr_reg_local_set_registry(NR_TOP_LEVEL_REGISTRY))) + ABORT(r); + } + + _status=0; + abort: + return(_status); +} + +#define NRREGLOCALGET(func, TYPE, type) \ +int \ +func(NR_registry name, type *out) \ +{ \ + return nr_reg_get(name, TYPE, out); \ +} + +NRREGLOCALGET(nr_reg_local_get_char, NR_REG_TYPE_CHAR, char) +NRREGLOCALGET(nr_reg_local_get_uchar, NR_REG_TYPE_UCHAR, UCHAR) +NRREGLOCALGET(nr_reg_local_get_int2, NR_REG_TYPE_INT2, INT2) +NRREGLOCALGET(nr_reg_local_get_uint2, NR_REG_TYPE_UINT2, UINT2) +NRREGLOCALGET(nr_reg_local_get_int4, NR_REG_TYPE_INT4, INT4) +NRREGLOCALGET(nr_reg_local_get_uint4, NR_REG_TYPE_UINT4, UINT4) +NRREGLOCALGET(nr_reg_local_get_int8, NR_REG_TYPE_INT8, INT8) +NRREGLOCALGET(nr_reg_local_get_uint8, NR_REG_TYPE_UINT8, UINT8) +NRREGLOCALGET(nr_reg_local_get_double, NR_REG_TYPE_DOUBLE, double) + +int +nr_reg_local_get_registry(NR_registry name, NR_registry out) +{ + int r, _status; + nr_scalar_registry_node *node = 0; + int free_node = 0; + + if ((r=nr_reg_fetch_node(name, NR_REG_TYPE_REGISTRY, (void*)&node, &free_node))) + ABORT(r); + + strncpy(out, name, sizeof(NR_registry)); + + _status=0; + abort: + if (free_node) RFREE(node); + return(_status); + +} + +int +nr_reg_local_get_bytes(NR_registry name, UCHAR *out, size_t size, size_t *length) +{ + return nr_reg_get_array(name, NR_REG_TYPE_BYTES, out, size, length); +} + +int +nr_reg_local_get_string(NR_registry name, char *out, size_t size) +{ + return nr_reg_get_array(name, NR_REG_TYPE_STRING, (UCHAR*)out, size, 0); +} + +int +nr_reg_local_get_length(NR_registry name, size_t *length) +{ + int r, _status; + nr_registry_node *node = 0; + + if ((r=nr_reg_is_valid(name))) + ABORT(r); + + if ((r=r_assoc_fetch(nr_registry, name, strlen(name)+1, (void*)&node))) + ABORT(r); + + if ((r=nr_reg_compute_length(name, node, length))) + ABORT(r); + + _status=0; + abort: + return(_status); +} + +int +nr_reg_local_get_type(NR_registry name, NR_registry_type type) +{ + int r, _status; + nr_registry_node *node = 0; + char *str; + + if ((r=nr_reg_is_valid(name))) + ABORT(r); + + if ((r=r_assoc_fetch(nr_registry, name, strlen(name)+1, (void*)&node))) + ABORT(r); + + str = nr_reg_type_name(node->type); + if (! str) + ABORT(R_BAD_ARGS); + + strncpy(type, str, sizeof(NR_registry_type)); + + _status=0; + abort: + return(_status); +} + + +#define NRREGLOCALSET(func, TYPE, type) \ +int \ +func(NR_registry name, type data) \ +{ \ + return nr_reg_set(name, TYPE, &data); \ +} + +NRREGLOCALSET(nr_reg_local_set_char, NR_REG_TYPE_CHAR, char) +NRREGLOCALSET(nr_reg_local_set_uchar, NR_REG_TYPE_UCHAR, UCHAR) +NRREGLOCALSET(nr_reg_local_set_int2, NR_REG_TYPE_INT2, INT2) +NRREGLOCALSET(nr_reg_local_set_uint2, NR_REG_TYPE_UINT2, UINT2) +NRREGLOCALSET(nr_reg_local_set_int4, NR_REG_TYPE_INT4, INT4) +NRREGLOCALSET(nr_reg_local_set_uint4, NR_REG_TYPE_UINT4, UINT4) +NRREGLOCALSET(nr_reg_local_set_int8, NR_REG_TYPE_INT8, INT8) +NRREGLOCALSET(nr_reg_local_set_uint8, NR_REG_TYPE_UINT8, UINT8) +NRREGLOCALSET(nr_reg_local_set_double, NR_REG_TYPE_DOUBLE, double) + +int +nr_reg_local_set_registry(NR_registry name) +{ + return nr_reg_set(name, NR_REG_TYPE_REGISTRY, 0); +} + +int +nr_reg_local_set_bytes(NR_registry name, unsigned char *data, size_t length) +{ + return nr_reg_set_array(name, NR_REG_TYPE_BYTES, data, length); +} + +int +nr_reg_local_set_string(NR_registry name, char *data) +{ + return nr_reg_set_array(name, NR_REG_TYPE_STRING, (UCHAR*)data, strlen(data)+1); +} + +int +nr_reg_local_del(NR_registry name) +{ + int r, _status; + + if ((r=nr_reg_is_valid(name))) + ABORT(r); + + /* delete from NR_registry */ + if ((r=nr_reg_local_iter(name, nr_reg_local_iter_delete, 0))) + ABORT(r); + + if ((r=nr_reg_raise_event(name, NR_REG_CB_ACTION_DELETE))) + ABORT(r); + + /* if deleting from the root, re-insert the root */ + if (! strcasecmp(name, NR_TOP_LEVEL_REGISTRY)) { + if ((r=nr_reg_local_set_registry(NR_TOP_LEVEL_REGISTRY))) + ABORT(r); + } + + _status=0; + abort: + r_log(NR_LOG_REGISTRY, + (_status ? LOG_INFO : LOG_INFO), + "delete of '%s' %s", name, + (_status ? "FAILED" : "succeeded")); + return(_status); +} + +int +nr_reg_local_get_child_count(NR_registry parent, size_t *count) +{ + int r, _status; + nr_registry_node *ignore1; + int ignore2; + + + if ((r=nr_reg_is_valid(parent))) + ABORT(r); + + /* test to see whether it is present */ + if ((r=nr_reg_fetch_node(parent, NR_REG_TYPE_REGISTRY, &ignore1, &ignore2))) + ABORT(r); + + /* sanity check that there isn't any memory to free */ + assert(ignore2 == 0); + + *count = 0; + + if ((r=nr_reg_local_iter(parent, nr_reg_local_count_children, count))) + ABORT(r); + + _status=0; + abort: + return(_status); +} + +int +nr_reg_local_get_children(NR_registry parent, NR_registry *data, size_t size, size_t *length) +{ + int r, _status; + nr_reg_find_children_arg arg; + + if ((r=nr_reg_is_valid(parent))) + ABORT(r); + + arg.children = data; + arg.size = size; + arg.length = 0; + + if ((r=nr_reg_local_iter(parent, nr_reg_local_find_children, (void*)&arg))) { + if (r == R_INTERRUPTED) + ABORT(R_BAD_ARGS); + else + ABORT(r); + } + + assert(sizeof(*arg.children) == sizeof(NR_registry)); + qsort(arg.children, arg.length, sizeof(*arg.children), (void*)strcasecmp); + + *length = arg.length; + + _status = 0; + abort: + return(_status); +} + +int +nr_reg_local_fin(NR_registry name) +{ + int r, _status; + + if ((r=nr_reg_raise_event(name, NR_REG_CB_ACTION_FINAL))) + ABORT(r); + + _status=0; + abort: + return(_status); +} + +int +nr_reg_local_dump(int sorted) +{ + int r, _status; + + if ((r=nr_reg_local_iter(NR_TOP_LEVEL_REGISTRY, nr_reg_local_dump_print, 0))) + ABORT(r); + + _status=0; + abort: + return(_status); +} + + + +static nr_registry_module_vtbl nr_reg_local_vtbl = { + nr_reg_local_init, + nr_reg_local_get_char, + nr_reg_local_get_uchar, + nr_reg_local_get_int2, + nr_reg_local_get_uint2, + nr_reg_local_get_int4, + nr_reg_local_get_uint4, + nr_reg_local_get_int8, + nr_reg_local_get_uint8, + nr_reg_local_get_double, + nr_reg_local_get_registry, + nr_reg_local_get_bytes, + nr_reg_local_get_string, + nr_reg_local_get_length, + nr_reg_local_get_type, + nr_reg_local_set_char, + nr_reg_local_set_uchar, + nr_reg_local_set_int2, + nr_reg_local_set_uint2, + nr_reg_local_set_int4, + nr_reg_local_set_uint4, + nr_reg_local_set_int8, + nr_reg_local_set_uint8, + nr_reg_local_set_double, + nr_reg_local_set_registry, + nr_reg_local_set_bytes, + nr_reg_local_set_string, + nr_reg_local_del, + nr_reg_local_get_child_count, + nr_reg_local_get_children, + nr_reg_local_fin, + nr_reg_local_dump +}; + +static nr_registry_module nr_reg_local_module = { 0, &nr_reg_local_vtbl }; + +void *NR_REG_MODE_LOCAL = &nr_reg_local_module; + + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_vtbl.h b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_vtbl.h new file mode 100644 index 0000000000..0f75a03b95 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_vtbl.h @@ -0,0 +1,96 @@ +/* + * + * registry_vtbl.h + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry_vtbl.h,v $ + * $Revision: 1.2 $ + * $Date: 2006/08/16 19:39:14 $ + * + * + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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. + * + * + */ + +#ifndef __REGISTRY_VTBL_H__ +#define __REGISTRY_VTBL_H__ + +typedef struct nr_registry_module_ nr_registry_module; + +typedef struct nr_registry_module_vtbl_ { + int (*init)(nr_registry_module*); + + int (*get_char)(NR_registry name, char *out); + int (*get_uchar)(NR_registry name, UCHAR *out); + int (*get_int2)(NR_registry name, INT2 *out); + int (*get_uint2)(NR_registry name, UINT2 *out); + int (*get_int4)(NR_registry name, INT4 *out); + int (*get_uint4)(NR_registry name, UINT4 *out); + int (*get_int8)(NR_registry name, INT8 *out); + int (*get_uint8)(NR_registry name, UINT8 *out); + int (*get_double)(NR_registry name, double *out); + int (*get_registry)(NR_registry name, NR_registry out); + + int (*get_bytes)(NR_registry name, UCHAR *out, size_t size, size_t *length); + int (*get_string)(NR_registry name, char *out, size_t size); + int (*get_length)(NR_registry name, size_t *length); + int (*get_type)(NR_registry name, NR_registry_type type); + + int (*set_char)(NR_registry name, char data); + int (*set_uchar)(NR_registry name, UCHAR data); + int (*set_int2)(NR_registry name, INT2 data); + int (*set_uint2)(NR_registry name, UINT2 data); + int (*set_int4)(NR_registry name, INT4 data); + int (*set_uint4)(NR_registry name, UINT4 data); + int (*set_int8)(NR_registry name, INT8 data); + int (*set_uint8)(NR_registry name, UINT8 data); + int (*set_double)(NR_registry name, double data); + int (*set_registry)(NR_registry name); + + int (*set_bytes)(NR_registry name, UCHAR *data, size_t length); + int (*set_string)(NR_registry name, char *data); + + int (*del)(NR_registry name); + + int (*get_child_count)(NR_registry parent, size_t *count); + int (*get_children)(NR_registry parent, NR_registry *data, size_t size, size_t *length); + + int (*fin)(NR_registry name); + + int (*dump)(int sorted); +} nr_registry_module_vtbl; + +struct nr_registry_module_ { + void *handle; + nr_registry_module_vtbl *vtbl; +}; + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registrycb.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registrycb.c new file mode 100644 index 0000000000..4b326a1ee2 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registrycb.c @@ -0,0 +1,440 @@ +/* + * + * registrycb.c + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registrycb.c,v $ + * $Revision: 1.3 $ + * $Date: 2007/06/26 22:37:51 $ + * + * Callback-related functions + * + * + * Copyright (C) 2005, Network Resonance, Inc. + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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 <assert.h> +#include <string.h> +#include "registry.h" +#include "registry_int.h" +#include "r_assoc.h" +#include "r_errors.h" +#include "nr_common.h" +#include "r_log.h" +#include "r_macros.h" + +static char CB_ACTIONS[] = { NR_REG_CB_ACTION_ADD, + NR_REG_CB_ACTION_DELETE, + NR_REG_CB_ACTION_CHANGE, + NR_REG_CB_ACTION_FINAL }; + +typedef struct nr_reg_cb_info_ { + char action; + void (*cb)(void *cb_arg, char action, NR_registry name); + void *cb_arg; + NR_registry name; +} nr_reg_cb_info; + +/* callbacks that are registered, a mapping from names like "foo.bar.baz" + * to an r_assoc which contains possibly several nr_reg_cb_info*'s */ +static r_assoc *nr_registry_callbacks = 0; + +//static size_t SIZEOF_CB_ID = (sizeof(void (*)()) + 1); +#define SIZEOF_CB_ID (sizeof(void (*)()) + 1) + +static int nr_reg_validate_action(char action); +static int nr_reg_assoc_destroy(void *ptr); +static int compute_cb_id(void *cb, char action, unsigned char cb_id[SIZEOF_CB_ID]); +static int nr_reg_info_free(void *ptr); +static int nr_reg_raise_event_recurse(char *name, char *tmp, int action); +static int nr_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg); +static int nr_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name)); + +int +nr_reg_cb_init() +{ + int r, _status; + + if (nr_registry_callbacks == 0) { + if ((r=r_assoc_create(&nr_registry_callbacks, r_assoc_crc32_hash_compute, 12))) + ABORT(r); + } + + _status=0; + abort: + if (_status) { + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Couldn't init notifications: %s", nr_strerror(_status)); + } + return(_status); +} + +int +nr_reg_validate_action(char action) +{ + int _status; + size_t i; + + for (i = 0; i < sizeof(CB_ACTIONS); ++i) { + if (action == CB_ACTIONS[i]) + return 0; + } + ABORT(R_BAD_ARGS); + + _status=0; + abort: + return(_status); +} + +int +nr_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg) +{ + int r, _status; + r_assoc *assoc; + int create_assoc = 0; + nr_reg_cb_info *info; + int create_info = 0; + unsigned char cb_id[SIZEOF_CB_ID]; + + if (name == 0 || cb == 0) + ABORT(R_BAD_ARGS); + + if (nr_registry_callbacks == 0) + ABORT(R_FAILED); + + if ((r=nr_reg_is_valid(name))) + ABORT(r); + + if ((r=nr_reg_validate_action(action))) + ABORT(r); + + if ((r=r_assoc_fetch(nr_registry_callbacks, name, strlen(name)+1, (void*)&assoc))) { + if (r == R_NOT_FOUND) + create_assoc = 1; + else + ABORT(r); + } + + if (create_assoc) { + if ((r=r_assoc_create(&assoc, r_assoc_crc32_hash_compute, 5))) + ABORT(r); + + if ((r=r_assoc_insert(nr_registry_callbacks, name, strlen(name)+1, assoc, 0, nr_reg_assoc_destroy, R_ASSOC_NEW))) + ABORT(r); + } + + if ((r=compute_cb_id(cb, action, cb_id))) + ABORT(r); + + if ((r=r_assoc_fetch(assoc, (char*)cb_id, SIZEOF_CB_ID, (void*)&info))) { + if (r == R_NOT_FOUND) + create_info = 1; + else + ABORT(r); + } + + if (create_info) { + if (!(info=(void*)RCALLOC(sizeof(*info)))) + ABORT(R_NO_MEMORY); + } + + strncpy(info->name, name, sizeof(info->name)); + info->action = action; + info->cb = cb; + info->cb_arg = cb_arg; + + if (create_info) { + if ((r=r_assoc_insert(assoc, (char*)cb_id, SIZEOF_CB_ID, info, 0, nr_reg_info_free, R_ASSOC_NEW))) + ABORT(r); + } + + _status=0; + abort: + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "register callback %p on '%s' for '%s' %s", cb, name, nr_reg_action_name(action), (_status ? "FAILED" : "succeeded")); + + if (_status) { + if (create_info && info) RFREE(info); + if (create_assoc && assoc) nr_reg_assoc_destroy(&assoc); + } + return(_status); +} + +int +nr_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name)) +{ + int r, _status; + r_assoc *assoc; + int size; + unsigned char cb_id[SIZEOF_CB_ID]; + + if (name == 0 || cb == 0) + ABORT(R_BAD_ARGS); + + if (nr_registry_callbacks == 0) + ABORT(R_FAILED); + + if ((r=nr_reg_is_valid(name))) + ABORT(r); + + if ((r=nr_reg_validate_action(action))) + ABORT(r); + + if ((r=r_assoc_fetch(nr_registry_callbacks, name, strlen(name)+1, (void*)&assoc))) { + if (r != R_NOT_FOUND) + ABORT(r); + } + else { + if ((r=compute_cb_id(cb, action, cb_id))) + ABORT(r); + + if ((r=r_assoc_delete(assoc, (char*)cb_id, SIZEOF_CB_ID))) { + if (r != R_NOT_FOUND) + ABORT(r); + } + + if ((r=r_assoc_num_elements(assoc, &size))) + ABORT(r); + + if (size == 0) { + if ((r=r_assoc_delete(nr_registry_callbacks, name, strlen(name)+1))) + ABORT(r); + } + } + + _status=0; + abort: + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "unregister callback %p on '%s' for '%s' %s", cb, name, nr_reg_action_name(action), (_status ? "FAILED" : "succeeded")); + + return(_status); +} + +int +compute_cb_id(void *cb, char action, unsigned char cb_id[SIZEOF_CB_ID]) +{ + /* callbacks are identified by the pointer to the cb function plus + * the action being watched */ + assert(sizeof(cb) == sizeof(void (*)())); + assert(sizeof(cb) == (SIZEOF_CB_ID - 1)); + + memcpy(cb_id, &(cb), sizeof(cb)); + cb_id[SIZEOF_CB_ID-1] = action; + + return 0; +} + +char * +nr_reg_action_name(int action) +{ + char *name = "*Unknown*"; + + switch (action) { + case NR_REG_CB_ACTION_ADD: name = "add"; break; + case NR_REG_CB_ACTION_DELETE: name = "delete"; break; + case NR_REG_CB_ACTION_CHANGE: name = "change"; break; + case NR_REG_CB_ACTION_FINAL: name = "final"; break; + } + + return name; +} + +int +nr_reg_assoc_destroy(void *ptr) +{ + return r_assoc_destroy((r_assoc**)&ptr); +} + +int +nr_reg_info_free(void *ptr) +{ + RFREE(ptr); + return 0; +} + +/* call with tmp=0 */ +int +nr_reg_raise_event_recurse(char *name, char *tmp, int action) +{ + int r, _status; + r_assoc *assoc; + nr_reg_cb_info *info; + r_assoc_iterator iter; + char *key; + int keyl; + char *c; + int free_tmp = 0; + int count; + + if (tmp == 0) { + if (!(tmp = (char*)r_strdup(name))) + ABORT(R_NO_MEMORY); + free_tmp = 1; + } + + if ((r=r_assoc_fetch(nr_registry_callbacks, tmp, strlen(tmp)+1, (void*)&assoc))) { + if (r != R_NOT_FOUND) + ABORT(r); + + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "No callbacks found on '%s'", tmp); + } + else { + if (!r_assoc_num_elements(assoc, &count)) { + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "%d callback%s found on '%s'", + count, ((count == 1) ? "" : "s"), tmp); + } + + if ((r=r_assoc_init_iter(assoc, &iter))) + ABORT(r); + + for (;;) { + if ((r=r_assoc_iter(&iter, (void*)&key, &keyl, (void*)&info))) { + if (r == R_EOD) + break; + else + ABORT(r); + } + + if (info->action == action) { + r_log(NR_LOG_REGISTRY, LOG_DEBUG, + "Invoking callback %p for '%s'", + info->cb, + nr_reg_action_name(info->action)); + + (void)info->cb(info->cb_arg, action, name); + } + else { + r_log(NR_LOG_REGISTRY, LOG_DEBUG, + "Skipping callback %p for '%s'", + info->cb, + nr_reg_action_name(info->action)); + } + } + } + + if (strlen(tmp) > 0) { + c = strrchr(tmp, '.'); + if (c != 0) + *c = '\0'; + else + tmp[0] = '\0'; + + if ((r=nr_reg_raise_event_recurse(name, tmp, action))) + ABORT(r); + } + + _status=0; + abort: + if (free_tmp && tmp != 0) RFREE(tmp); + return(_status); +} + + +/* NON-STATIC METHODS */ + +int +nr_reg_raise_event(NR_registry name, int action) +{ + int r, _status; + int count; + char *event = nr_reg_action_name(action); + + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "raising event '%s' on '%s'", event, name); + + if (name == 0) + ABORT(R_BAD_ARGS); + + if ((r=nr_reg_validate_action(action))) + ABORT(r); + + if ((r=r_assoc_num_elements(nr_registry_callbacks, &count))) + ABORT(r); + + if (count > 0) { + if ((r=nr_reg_raise_event_recurse(name, 0, action))) + ABORT(r); + } + else { + r_log(NR_LOG_REGISTRY, LOG_DEBUG, "No callbacks found"); + return 0; + } + + _status=0; + abort: + return(_status); +} + + +/* PUBLIC METHODS */ + +int +NR_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg) +{ + int r, _status; + size_t i; + + for (i = 0; i < sizeof(CB_ACTIONS); ++i) { + if (action & CB_ACTIONS[i]) { + if ((r=nr_reg_register_callback(name, CB_ACTIONS[i], cb, cb_arg))) + ABORT(r); + + action &= ~(CB_ACTIONS[i]); + } + } + + if (action) + ABORT(R_BAD_ARGS); + + _status=0; + abort: + return(_status); +} + +int +NR_reg_unregister_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name)) +{ + int r, _status; + size_t i; + + for (i = 0; i < sizeof(CB_ACTIONS); ++i) { + if (action & CB_ACTIONS[i]) { + if ((r=nr_reg_unregister_callback(name, CB_ACTIONS[i], cb))) + ABORT(r); + + action &= ~(CB_ACTIONS[i]); + } + } + + if (action) + ABORT(R_BAD_ARGS); + + _status=0; + abort: + return(_status); +} diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_api.h b/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_api.h new file mode 100644 index 0000000000..ce6055f0d9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_api.h @@ -0,0 +1,51 @@ +/** + nr_pce.h + + + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@networkresonance.com Wed Jul 19 13:18:39 2006 + */ + + +#ifndef _nr_pce_h +#define _nr_pce_h + +#include <sys/queue.h> +#include <csi_platform.h> +#include <r_common.h> +#include <r_log.h> +#include <nrstats.h> +#include <nr_plugin.h> +#include <async_wait.h> + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_common.h b/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_common.h new file mode 100644 index 0000000000..ac3ff44004 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_common.h @@ -0,0 +1,108 @@ +/** + nr_common.h + + + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + */ + + +#ifndef _nr_common_h +#define _nr_common_h + +#include <csi_platform.h> + +#ifdef USE_MPATROL +#define USEDEBUG 1 +#include <mpatrol.h> +#endif + +#ifdef USE_DMALLOC +#include <dmalloc.h> +#endif + +#include <string.h> +#include <time.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <errno.h> +#else +#include <sys/errno.h> +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#include <sys/types.h> +#include <sys/queue.h> +#include <r_log.h> + +extern int NR_LOG_REASSD; + +#include "registry.h" +#include "nrstats.h" + +typedef struct nr_captured_packet_ { + UCHAR cap_interface; /* 1 for primary, 2 for secondary */ + struct timeval ts; /* The time this packet was captured */ + UINT4 len; /* The length of the packet */ + UINT8 packet_number; /* The listener's packet index */ +} nr_captured_packet; + +#ifndef NR_ROOT_PATH +#define NR_ROOT_PATH "/usr/local/ctc/" +#endif + +#define NR_ARCHIVE_DIR NR_ROOT_PATH "archive/" +#define NR_TEMP_DIR NR_ROOT_PATH "tmp/" +#define NR_ARCHIVE_STATEFILE NR_ROOT_PATH "archive/state" +#define NR_CAPTURED_PID_FILENAME NR_ROOT_PATH "captured.pid" +#define NR_REASSD_PID_FILENAME NR_ROOT_PATH "reassd.pid" +#define NR_MODE_FILENAME NR_ROOT_PATH "mode.txt" + +char *nr_revision_number(void); + + + + +/* Memory buckets for CTC memory types */ +#define NR_MEM_TCP 1 +#define NR_MEM_HTTP 2 +#define NR_MEM_DELIVERY 3 +#define NR_MEM_OUT_HM 4 +#define NR_MEM_OUT_SSL 5 +#define NR_MEM_SSL 7 +#define NR_MEM_COMMON 8 +#define NR_MEM_CODEC 9 + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_reg_keys.h b/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_reg_keys.h new file mode 100644 index 0000000000..b051f51d4a --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_reg_keys.h @@ -0,0 +1,167 @@ +/* + * + * nr_reg_keys.h + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/share/nr_reg_keys.h,v $ + * $Revision: 1.3 $ + * $Date: 2008/01/29 00:34:00 $ + * + * + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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. + * + * + */ + +#ifndef __NR_REG_KEYS_H__ +#define __NR_REG_KEYS_H__ + +#include <stdio.h> + +#define NR_REG_NAME_LENGTH_MIN 1 +#define NR_REG_NAME_LENGTH_MAX 32 + +#define NR_REG_HOSTNAME "hostname" +#define NR_REG_ADDRESS "address" +#define NR_REG_NETMASKSIZE "netmasksize" +#define NR_REG_ADDRESS_NETMASKSIZE "address/netmasksize" /* use only in clic files */ +#define NR_REG_PORT "port" + +#define NR_REG_LOGGING_SYSLOG_ENABLED "logging.syslog.enabled" +#define NR_REG_LOGGING_SYSLOG_SERVERS "logging.syslog.servers" +#define NR_REG_LOGGING_SYSLOG_FACILITY "logging.syslog.facility" +#define NR_REG_LOGGING_SYSLOG_LEVEL "logging.syslog.level" + +#define NR_REG_CAPTURED_DAEMONS "captured.daemons" + +#define NR_REG_LISTEND_ENABLED "listend.enabled" + +#define NR_REG_LISTEND_MAX_INPUT_BUFFER_SIZE "listend.max_input_buffer_size" +#define NR_REG_LISTEND_MAX_INPUT_BUFFER_SIZE_MIN 1 // 1 byte? +#define NR_REG_LISTEND_MAX_INPUT_BUFFER_SIZE_MAX (10ULL*1024*1024*1024) // 10 GB + +#define NR_REG_LISTEND_INTERFACE "listend.interface" +#define NR_REG_LISTEND_INTERFACE_PRIMARY "listend.interface.primary" +#define NR_REG_LISTEND_INTERFACE_SECONDARY "listend.interface.secondary" +#define NR_REG_LISTEND_LISTEN_ON_BOTH_INTERFACES "listend.interface.listen_on_both_interfaces" +#define NR_REG_LISTEND_ENABLE_VLAN "listend.enable_vlan" + +#define NR_REG_LISTEND_LISTEN_TO "listend.listen_to" +#define NR_REG_LISTEND_IGNORE "listend.ignore" + +#define NR_REG_LISTEND_PORT_MIN 0 +#define NR_REG_LISTEND_PORT_MAX 65535 + +#define NR_REG_REASSD_MAX_MEMORY_CONSUMPTION "reassd.max_memory_consumption" +#define NR_REG_REASSD_MAX_MEMORY_CONSUMPTION_MIN (10*1024) // 100 KB +#define NR_REG_REASSD_MAX_MEMORY_CONSUMPTION_MAX (10ULL*1024*1024*1024) // 10 GB + +#define NR_REG_REASSD_DECODER_TCP_IGNORE_CHECKSUMS "reassd.decoder.tcp.ignore_checksums" + +#define NR_REG_REASSD_DECODER_TCP_MAX_CONNECTIONS_IN_SYN_STATE "reassd.decoder.tcp.max_connections_in_syn_state" +#define NR_REG_REASSD_DECODER_TCP_MAX_CONNECTIONS_IN_SYN_STATE_MIN 1 +#define NR_REG_REASSD_DECODER_TCP_MAX_CONNECTIONS_IN_SYN_STATE_MAX 500000 + +#define NR_REG_REASSD_DECODER_TCP_MAX_SIMULTANEOUS_CONNECTIONS "reassd.decoder.tcp.max_simultaneous_connections" +#define NR_REG_REASSD_DECODER_TCP_MAX_SIMULTANEOUS_CONNECTIONS_MIN 1 +#define NR_REG_REASSD_DECODER_TCP_MAX_SIMULTANEOUS_CONNECTIONS_MAX 1000000 // 1 million + +#define NR_REG_REASSD_DECODER_SSL_MAX_SESSION_CACHE_SIZE "reassd.decoder.ssl.max_session_cache_size" +#define NR_REG_REASSD_DECODER_SSL_MAX_SESSION_CACHE_SIZE_MIN 0 +#define NR_REG_REASSD_DECODER_SSL_MAX_SESSION_CACHE_SIZE_MAX (1ULL*1024*1024*1024) // 1GB + +#define NR_REG_REASSD_DECODER_SSL_REVEAL_LOCAL_KEYS "reassd.decoder.ssl.reveal.local.keys" + +#define NR_REG_REASSD_DECODER_HTTP_HANGING_RESPONSE_TIMEOUT "reassd.decoder.http.hanging_response_timeout" +#define NR_REG_REASSD_DECODER_HTTP_HANGING_RESPONSE_TIMEOUT_MIN 1 +#define NR_REG_REASSD_DECODER_HTTP_HANGING_RESPONSE_TIMEOUT_MAX 1023 + +#define NR_REG_REASSD_DECODER_HTTP_HANGING_TRANSMISSION_TIMEOUT "reassd.decoder.http.hanging_transmission_timeout" +#define NR_REG_REASSD_DECODER_HTTP_HANGING_TRANSMISSION_TIMEOUT_MIN 1 +#define NR_REG_REASSD_DECODER_HTTP_HANGING_TRANSMISSION_TIMEOUT_MAX 1023 + +#define NR_REG_REASSD_DECODER_HTTP_MAX_HTTP_MESSAGE_SIZE "reassd.decoder.http.max_http_message_size" +#define NR_REG_REASSD_DECODER_HTTP_MAX_HTTP_MESSAGE_SIZE_MIN 0 +#define NR_REG_REASSD_DECODER_HTTP_MAX_HTTP_MESSAGE_SIZE_MAX (10ULL*1024*1024*1024) + +/* PCE-only: */ +#define NR_REG_LISTEND_ARCHIIVE "listend.archive" +#define NR_REG_LISTEND_ARCHIVE_MAX_SIZE "listend.archive.max_size" +#define NR_REG_LISTEND_ARCHIVE_MAX_SIZE_MIN 0 +#define NR_REG_LISTEND_ARCHIVE_MAX_SIZE_MAX (10ULL*1024*1024*1024*1024) // 10 TB + +#define NR_REG_LISTEND_ARCHIVE_RECORDING_ENABLED "listend.archive.recording_enabled" + +#define NR_REG_REASSD_DECODER_NET_DELIVER_BATCH_INTERVAL "reassd.decoder.net_deliver.batch_interval" +#define NR_REG_REASSD_DECODER_NET_DELIVER_BATCH_INTERVAL_MIN 0 +#define NR_REG_REASSD_DECODER_NET_DELIVER_BATCH_INTERVAL_MAX 1023 + +#define NR_REG_REASSD_DECODER_NET_DELIVER_MAX_QUEUE_DEPTH "reassd.decoder.net_deliver.max_queue_depth" +#define NR_REG_REASSD_DECODER_NET_DELIVER_MAX_QUEUE_DEPTH_MIN 0 +#define NR_REG_REASSD_DECODER_NET_DELIVER_MAX_QUEUE_DEPTH_MAX (1ULL*1024*1024*1024) // 1 GB + +#define NR_REG_REASSD_DECODER_NET_DELIVER_MY_KEY_CERTIFICATE "reassd.decoder.net_deliver.my_key.certificate" +#define NR_REG_REASSD_DECODER_NET_DELIVER_MY_KEY_PRIVATE_KEY "reassd.decoder.net_deliver.my_key.private_key" +#define NR_REG_REASSD_DECODER_NET_DELIVER_PEER "reassd.decoder.net_deliver.peer" +#define NR_REG_REASSD_DECODER_NET_DELIVER_PEER_PORT_MIN 0 +#define NR_REG_REASSD_DECODER_NET_DELIVER_PEER_PORT_MAX 65535 + +#define NR_REG_REASSD_DECODER_NET_DELIVER_POLLING_INTERVAL "reassd.decoder.net_deliver.polling_interval" +#define NR_REG_REASSD_DECODER_NET_DELIVER_POLLING_INTERVAL_MIN 1 +#define NR_REG_REASSD_DECODER_NET_DELIVER_POLLING_INTERVAL_MAX 1023 + +#define NR_REG_REASSD_DECODER_NET_DELIVER_STATELESS "reassd.decoder.net_deliver.stateless" +#define NR_REG_REASSD_DECODER_NET_DELIVER_WATCHDOG_TIMER "reassd.decoder.net_deliver.watchdog_timer" +#define NR_REG_REASSD_DECODER_NET_DELIVER_WATCHDOG_TIMER_MIN 0 +#define NR_REG_REASSD_DECODER_NET_DELIVER_WATCHDOG_TIMER_MAX 666 + +/* ASA-only: */ +#define NR_REG_MIGRATE_ENABLED "migrate.enabled" + +#define NR_REG_MIGRATE_INACTIVITY_TIMEOUT "migrate.inactivity_timeout" +#define NR_REG_MIGRATE_INACTIVITY_TIMEOUT_MIN 0 +#define NR_REG_MIGRATE_INACTIVITY_TIMEOUT_MAX 1023 + +#define NR_REG_MIGRATE_MIN_LOCAL_SIZE "migrate.min_local_size" +#define NR_REG_MIGRATE_MIN_OVERLAP_SIZE "migrate.min_overlap_size" +#define NR_REG_MIGRATE_RETRANSMIT_FREQUENCY "migrate.retransmit_frequency" +#define NR_REG_MIGRATE_RETRIES "migrate.retries" +#define NR_MIGRATE_LOCATION_NUMBER_MIN 0 +#define NR_MIGRATE_LOCATION_NUMBER_MAX 255 + +#define NR_REG_REVELATION_ENABLE "revelation.enabled" +#define NR_REG_REVELATION_MAX_PER_HOUR "revelation.max_per_hour" +#define NR_REG_REVELATION_MAX_PER_HOUR_PER_PORTAL "revelation.max_per_hour_per_portal" + +/* Appliance-only: */ +#define NR_REG_SNMP_ENABLED "snmp.enabled" +#define NR_REG_CLOCK_TIMEZONE "clock.timezone" + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/stats/nrstats.h b/dom/media/webrtc/transport/third_party/nrappkit/src/stats/nrstats.h new file mode 100644 index 0000000000..0b6c2bc3c3 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/stats/nrstats.h @@ -0,0 +1,118 @@ +/* + * + * nrstats.h + * + * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/stats/nrstats.h,v $ + * $Revision: 1.4 $ + * $Date: 2007/06/26 22:37:55 $ + * + * API for keeping and sharing statistics + * + * + * Copyright (C) 2003-2005, Network Resonance, Inc. + * Copyright (C) 2006, Network Resonance, Inc. + * All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Network Resonance, Inc. nor the name of any + * contributors to this software 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. + * + * + */ + +#ifndef __NRSTATS_H__ +#define __NRSTATS_H__ + +#include <sys/types.h> +#ifdef WIN32 +#include <time.h> +#else +#include <sys/time.h> +#endif +#include <r_types.h> + +#ifndef CAPTURE_USER +#define CAPTURE_USER "pcecap" +#endif + +#define NR_MAX_STATS_TYPES 256 /* max number of stats objects */ +#define NR_MAX_STATS_TYPE_NAME 26 + +typedef struct NR_stats_type_ { + char name[NR_MAX_STATS_TYPE_NAME]; + int (*reset)(void *stats); + int (*print)(void *stats, char *stat_namespace, void (*output)(void *handle, const char *fmt, ...), void *handle); + int (*get_lib_name)(char **libname); + unsigned int size; +} NR_stats_type; + +typedef struct NR_stats_app_ { + time_t last_counter_reset; + time_t last_restart; + UINT8 total_restarts; + char version[64]; +} NR_stats_app; + +extern NR_stats_type *NR_stats_type_app; + +/* everything measured in bytes */ +typedef struct NR_stats_memory_ { + UINT8 current_size; + UINT8 max_size; + UINT8 in_use; + UINT8 in_use_max; +} NR_stats_memory; + +extern NR_stats_type *NR_stats_type_memory; + +/* all functions below return 0 on success, else they return an + * error code */ + +/* if errprintf is null, warnings and errors will be sent to /dev/null */ +extern int NR_stats_startup(char *app_name, char *user_name, void (*errprintf)(void *handle, const char *fmt, ...), void *errhandle); +extern int NR_stats_shutdown(void); +#define NR_STATS_CREATE (1<<0) +extern int NR_stats_get(char *module_name, NR_stats_type *type, int flag, void **stats); +extern int NR_stats_clear(void *stats); /* zeroizes */ +extern int NR_stats_reset(void *stats); /* zeros "speedometers" */ +extern int NR_stats_register(NR_stats_type *type); +extern int NR_stats_acquire_mutex(void *stats); +extern int NR_stats_release_mutex(void *stats); +// TODO: should _get_names take an app_name argument (0==all)???? +extern int NR_stats_get_names(unsigned int *nnames, char ***names); +extern int NR_stats_get_by_name(char *name, NR_stats_type **type, void **stats); +extern int NR_stats_get_lib_name(void *stats, char **lib_name); +extern int NR_stats_rmids(void); + +extern char *NR_prefix_to_stats_module(char *prefix); + +#define NR_INCREMENT_STAT(stat) do { \ + stat++; if(stat>stat##_max) stat##_max=stat; \ + } while (0) +#define NR_UPDATE_STAT(stat,newval) do { \ + stat=newval; if(stat>stat##_max) stat##_max=stat; \ + } while (0) + +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.c new file mode 100644 index 0000000000..64689accfa --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.c @@ -0,0 +1,73 @@ +/** + byteorder.c + + + Copyright (C) 2007, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + briank@networkresonance.com Wed May 16 16:46:00 PDT 2007 + */ + +#include "nr_common.h" +#ifndef WIN32 +#include <arpa/inet.h> +#endif +#include "r_types.h" +#include "byteorder.h" + +#define IS_BIG_ENDIAN (htonl(0x1) == 0x1) + +#define BYTE(n,i) (((UCHAR*)&(n))[(i)]) +#define SWAP(n,x,y) tmp=BYTE((n),(x)), \ + BYTE((n),(x))=BYTE((n),(y)), \ + BYTE((n),(y))=tmp + +UINT8 +nr_htonll(UINT8 hostlonglong) +{ + UINT8 netlonglong = hostlonglong; + UCHAR tmp; + + if (!IS_BIG_ENDIAN) { + SWAP(netlonglong, 0, 7); + SWAP(netlonglong, 1, 6); + SWAP(netlonglong, 2, 5); + SWAP(netlonglong, 3, 4); + } + + return netlonglong; +} + +UINT8 +nr_ntohll(UINT8 netlonglong) +{ + return nr_htonll(netlonglong); +} + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.h new file mode 100644 index 0000000000..8df0589a63 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.h @@ -0,0 +1,47 @@ +/** + byteorder.h + + + Copyright (C) 2007, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + briank@networkresonance.com Wed May 16 16:46:00 PDT 2007 + */ + + +#ifndef _byteorder_h +#define _byteorder_h + +UINT8 nr_htonll(UINT8 hostlonglong); + +UINT8 nr_ntohll(UINT8 netlonglong); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.c new file mode 100644 index 0000000000..8212988eda --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.c @@ -0,0 +1,109 @@ +/** + hex.c + + + Copyright (C) 2001-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + briank@network-resonance.com + */ + +#include <sys/types.h> +#include <string.h> +#include <ctype.h> +#include "r_common.h" +#include "hex.h" +#include "r_log.h" + +static char bin2hex_map[][3] = { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff" }; + +static int hex2bin_map[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0 /* '0' */, 1 /* '1' */, 2 /* '2' */, 3 /* '3' */, 4 /* '4' */, 5 /* '5' */, 6 /* '6' */, 7 /* '7' */, 8 /* '8' */, 9 /* '9' */, -1, -1, -1, -1, -1, -1, -1, 10 /* 'A' */, 11 /* 'B' */, 12 /* 'C' */, 13 /* 'D' */, 14 /* 'E' */, 15 /* 'F' */, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10 /* 'a' */, 11 /* 'b' */, 12 /* 'c' */, 13 /* 'd' */, 14 /* 'e' */, 15 /* 'f' */, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +int +nr_nbin2hex(UCHAR *bin, size_t binlen, char hex[], size_t size, size_t *len) +{ + int _status; + size_t i; + size_t hexlen; + + hexlen = 2*binlen; + if (size < hexlen) + ABORT(R_BAD_ARGS); + + for (i = 0; i < binlen; ++i) { + *hex++ = bin2hex_map[bin[i]][0]; + *hex++ = bin2hex_map[bin[i]][1]; + } + + if (size >= hexlen+1) + *hex = '\0'; + + *len = hexlen; + + _status=0; + abort: + return(_status); +} + + +int +nr_nhex2bin(char *hex, size_t hexlen, UCHAR bin[], size_t size, size_t *len) +{ + int _status; + size_t binlen; + int h1; + int h2; + size_t i; + + if (hexlen % 2) + ABORT(R_BAD_ARGS); + + binlen = hexlen/2; + + if (size < binlen) + ABORT(R_BAD_ARGS); + + for (i = 0; i < binlen; ++i) { + h1 = hex2bin_map[(int)*hex++]; + h2 = hex2bin_map[(int)*hex++]; + + if (h1 == -1 || h2 == -1) + ABORT(R_BAD_ARGS); + + bin[i] = (h1 << 4) | h2; + } + + *len = binlen; + + _status=0; + abort: + return(_status); +} diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.h new file mode 100644 index 0000000000..8c493b533b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.h @@ -0,0 +1,47 @@ +/** + hex.h + + + Copyright (C) 2001-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + briank@network-resonance.com + */ + + +#ifndef _hex_h +#define _hex_h + +int nr_nbin2hex(UCHAR *bin, size_t binlen, char hex[], size_t size, size_t *len); +int nr_nhex2bin(char *hex, size_t hexlen, UCHAR bin[], size_t size, size_t *len); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/assoc.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/assoc.h new file mode 100644 index 0000000000..013e788685 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/assoc.h @@ -0,0 +1,90 @@ +/** + assoc.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + assoc.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: assoc.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $ + + + ekr@rtfm.com Sun Jan 17 17:56:35 1999 + */ + + +#ifndef _assoc_h +#define _assoc_h + +typedef struct assoc_ assoc; + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.c new file mode 100644 index 0000000000..fa796217ea --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.c @@ -0,0 +1,127 @@ +/** + debug.c + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + debug.c + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: debug.c,v 1.3 2007/06/26 22:37:57 adamcain Exp $ + + + ekr@rtfm.com Wed Jan 6 17:08:58 1999 + */ + +#include <stdarg.h> +#include <stdio.h> +#include "r_common.h" +#include "debug.h" + +int nr_debug(int class,char *format,...) + { + va_list ap; + + va_start(ap,format); +#ifdef WIN32 + vprintf(format,ap); + printf("\n"); +#else + vfprintf(stderr,format,ap); + fprintf(stderr,"\n"); +#endif + return(0); + } + +int nr_xdump(name,data,len) + char *name; + UCHAR *data; + int len; + { + int i; + + if(name){ + printf("%s[%d]=\n",name,len); + } + for(i=0;i<len;i++){ + + if((len>8) && i && !(i%12)){ + printf("\n"); + } + printf("%.2x ",data[i]&255); + } + if(i%12) + printf("\n"); + return(0); + } + + + + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.h new file mode 100644 index 0000000000..34f7b2fb54 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.h @@ -0,0 +1,94 @@ +/** + debug.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + debug.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: debug.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $ + + + ekr@rtfm.com Wed Jan 6 17:13:00 1999 + */ + + +#ifndef _debug_h +#define _debug_h + +// Remove debugging mess from ssldump +// #define DBG(a) + +int nr_debug(int class,char *format,...); +int nr_xdump(char *name,UCHAR *data, int len); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.c new file mode 100644 index 0000000000..25b3827d50 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.c @@ -0,0 +1,539 @@ +/** + r_assoc.c + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_assoc.c + + This is an associative array implementation, using an open-chained + hash bucket technique. + + Note that this implementation permits each data entry to have + separate copy constructors and destructors. This currently wastes + space, but could be implemented while saving space by using + the high order bit of the length value or somesuch. + + The major problem with this code is it's not resizable, though it + could be made so. + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_assoc.c,v 1.4 2007/06/08 17:41:49 adamcain Exp $ + + ekr@rtfm.com Sun Jan 17 17:57:15 1999 + */ + +#include <r_common.h> +#include <string.h> +#include "r_assoc.h" + +typedef struct r_assoc_el_ { + char *key; + int key_len; + void *data; + struct r_assoc_el_ *prev; + struct r_assoc_el_ *next; + int (*copy)(void **n,void *old); + int (*destroy)(void *ptr); +} r_assoc_el; + +struct r_assoc_ { + int size; + int bits; + int (*hash_func)(char *key,int len,int size); + r_assoc_el **chains; + UINT4 num_elements; +}; + +#define DEFAULT_TABLE_BITS 5 + +static int destroy_assoc_chain(r_assoc_el *chain); +static int r_assoc_fetch_bucket(r_assoc *assoc, + char *key,int len,r_assoc_el **bucketp); +static int copy_assoc_chain(r_assoc_el **knewp, r_assoc_el *old); + +int r_assoc_create(assocp,hash_func,bits) + r_assoc **assocp; + int (*hash_func)(char *key,int len,int size); + int bits; + { + r_assoc *assoc=0; + int _status; + + if(!(assoc=(r_assoc *)RCALLOC(sizeof(r_assoc)))) + ABORT(R_NO_MEMORY); + assoc->size=(1<<bits); + assoc->bits=bits; + assoc->hash_func=hash_func; + + if(!(assoc->chains=(r_assoc_el **)RCALLOC(sizeof(r_assoc_el *)* + assoc->size))) + ABORT(R_NO_MEMORY); + + *assocp=assoc; + + _status=0; + abort: + if(_status){ + r_assoc_destroy(&assoc); + } + return(_status); + } + +int r_assoc_destroy(assocp) + r_assoc **assocp; + { + r_assoc *assoc; + int i; + + if(!assocp || !*assocp) + return(0); + + assoc=*assocp; + for(i=0;i<assoc->size;i++) + destroy_assoc_chain(assoc->chains[i]); + + RFREE(assoc->chains); + RFREE(*assocp); + + return(0); + } + +static int destroy_assoc_chain(chain) + r_assoc_el *chain; + { + r_assoc_el *nxt; + + while(chain){ + nxt=chain->next; + + if(chain->destroy) + chain->destroy(chain->data); + + RFREE(chain->key); + + RFREE(chain); + chain=nxt; + } + + return(0); + } + +static int copy_assoc_chain(knewp,old) + r_assoc_el **knewp; + r_assoc_el *old; + { + r_assoc_el *knew=0,*ptr,*tmp; + int r,_status; + + ptr=0; /* Pacify GCC's uninitialized warning. + It's not correct */ + if(!old) { + *knewp=0; + return(0); + } + for(;old;old=old->next){ + if(!(tmp=(r_assoc_el *)RCALLOC(sizeof(r_assoc_el)))) + ABORT(R_NO_MEMORY); + + if(!knew){ + knew=tmp; + ptr=knew; + } + else{ + ptr->next=tmp; + tmp->prev=ptr; + ptr=tmp; + } + + ptr->destroy=old->destroy; + ptr->copy=old->copy; + + if(old->copy){ + if(r=old->copy(&ptr->data,old->data)) + ABORT(r); + } + else + ptr->data=old->data; + + if(!(ptr->key=(char *)RMALLOC(old->key_len))) + ABORT(R_NO_MEMORY); + memcpy(ptr->key,old->key,ptr->key_len=old->key_len); + } + + *knewp=knew; + + _status=0; + abort: + if(_status){ + destroy_assoc_chain(knew); + } + return(_status); + } + +static int r_assoc_fetch_bucket(assoc,key,len,bucketp) + r_assoc *assoc; + char *key; + int len; + r_assoc_el **bucketp; + { + UINT4 hash_value; + r_assoc_el *bucket; + + hash_value=assoc->hash_func(key,len,assoc->bits); + + for(bucket=assoc->chains[hash_value];bucket;bucket=bucket->next){ + if(bucket->key_len == len && !memcmp(bucket->key,key,len)){ + *bucketp=bucket; + return(0); + } + } + + return(R_NOT_FOUND); + } + +int r_assoc_fetch(assoc,key,len,datap) + r_assoc *assoc; + char *key; + int len; + void **datap; + { + r_assoc_el *bucket; + int r; + + if(r=r_assoc_fetch_bucket(assoc,key,len,&bucket)){ + if(r!=R_NOT_FOUND) + ERETURN(r); + return(r); + } + + *datap=bucket->data; + return(0); + } + +int r_assoc_insert(assoc,key,len,data,copy,destroy,how) + r_assoc *assoc; + char *key; + int len; + void *data; + int (*copy)(void **knew,void *old); + int (*destroy)(void *ptr); + int how; + { + r_assoc_el *bucket,*new_bucket=0; + int r,_status; + + if(r=r_assoc_fetch_bucket(assoc,key,len,&bucket)){ + /*Note that we compute the hash value twice*/ + UINT4 hash_value; + + if(r!=R_NOT_FOUND) + ABORT(r); + hash_value=assoc->hash_func(key,len,assoc->bits); + + if(!(new_bucket=(r_assoc_el *)RCALLOC(sizeof(r_assoc_el)))) + ABORT(R_NO_MEMORY); + if(!(new_bucket->key=(char *)RMALLOC(len))) + ABORT(R_NO_MEMORY); + memcpy(new_bucket->key,key,len); + new_bucket->key_len=len; + + /*Insert at the list head. Is FIFO a good algorithm?*/ + if(assoc->chains[hash_value]) + assoc->chains[hash_value]->prev=new_bucket; + new_bucket->next=assoc->chains[hash_value]; + assoc->chains[hash_value]=new_bucket; + bucket=new_bucket; + } + else{ + if(!(how&R_ASSOC_REPLACE)) + ABORT(R_ALREADY); + + if(bucket->destroy) + bucket->destroy(bucket->data); + } + + bucket->data=data; + bucket->copy=copy; + bucket->destroy=destroy; + assoc->num_elements++; + + _status=0; + abort: + if(_status && new_bucket){ + RFREE(new_bucket->key); + RFREE(new_bucket); + } + return(_status); + } + +int r_assoc_delete(assoc,key,len) + r_assoc *assoc; + char *key; + int len; + { + int r; + r_assoc_el *bucket; + UINT4 hash_value; + + if(r=r_assoc_fetch_bucket(assoc,key,len,&bucket)){ + if(r!=R_NOT_FOUND) + ERETURN(r); + return(r); + } + + /* Now remove the element from the hash chain */ + if(bucket->prev){ + bucket->prev->next=bucket->next; + } + else { + hash_value=assoc->hash_func(key,len,assoc->bits); + assoc->chains[hash_value]=bucket->next; + } + + if(bucket->next) + bucket->next->prev=bucket->prev; + + /* Remove the data */ + if(bucket->destroy) + bucket->destroy(bucket->data); + + RFREE(bucket->key); + RFREE(bucket); + assoc->num_elements--; + + return(0); + } + +int r_assoc_copy(knewp,old) + r_assoc **knewp; + r_assoc *old; + { + int r,_status,i; + r_assoc *knew; + + if(!(knew=(r_assoc *)RCALLOC(sizeof(r_assoc)))) + ABORT(R_NO_MEMORY); + knew->size=old->size; + knew->bits=old->bits; + knew->hash_func=old->hash_func; + + if(!(knew->chains=(r_assoc_el **)RCALLOC(sizeof(r_assoc_el)*old->size))) + ABORT(R_NO_MEMORY); + for(i=0;i<knew->size;i++){ + if(r=copy_assoc_chain(knew->chains+i,old->chains[i])) + ABORT(r); + } + knew->num_elements=old->num_elements; + + *knewp=knew; + + _status=0; + abort: + if(_status){ + r_assoc_destroy(&knew); + } + return(_status); + } + +int r_assoc_num_elements(r_assoc *assoc,int *sizep) + { + *sizep=assoc->num_elements; + + return(0); + } + +int r_assoc_init_iter(assoc,iter) + r_assoc *assoc; + r_assoc_iterator *iter; + { + int i; + + iter->assoc=assoc; + iter->prev_chain=-1; + iter->prev=0; + + iter->next_chain=assoc->size; + iter->next=0; + + for(i=0;i<assoc->size;i++){ + if(assoc->chains[i]!=0){ + iter->next_chain=i; + iter->next=assoc->chains[i]; + break; + } + } + + return(0); + } + +int r_assoc_iter(iter,key,keyl,val) + r_assoc_iterator *iter; + void **key; + int *keyl; + void **val; + { + int i; + r_assoc_el *ret; + + if(!iter->next) + return(R_EOD); + ret=iter->next; + + *key=ret->key; + *keyl=ret->key_len; + *val=ret->data; + + /* Now increment */ + iter->prev_chain=iter->next_chain; + iter->prev=iter->next; + + /* More on this chain */ + if(iter->next->next){ + iter->next=iter->next->next; + } + else{ + iter->next=0; + + /* FInd the next occupied chain*/ + for(i=iter->next_chain+1;i<iter->assoc->size;i++){ + if(iter->assoc->chains[i]){ + iter->next_chain=i; + iter->next=iter->assoc->chains[i]; + break; + } + } + } + + return(0); + } + +/* Delete the last returned value*/ +int r_assoc_iter_delete(iter) + r_assoc_iterator *iter; + { + /* First unhook it from the list*/ + if(!iter->prev->prev){ + /* First element*/ + iter->assoc->chains[iter->prev_chain]=iter->prev->next; + } + else{ + iter->prev->prev->next=iter->prev->next; + } + + if(iter->prev->next){ + iter->prev->next->prev=iter->prev->prev; + } + + if (iter->prev->destroy) + iter->prev->destroy(iter->prev->data); + + iter->assoc->num_elements--; + RFREE(iter->prev->key); + RFREE(iter->prev); + return(0); + } + + +/*This is a hack from AMS. Supposedly, it's pretty good for strings, even + though it doesn't take into account all the data*/ +int r_assoc_simple_hash_compute(key,len,bits) + char *key; + int len; + int bits; + { + UINT4 h=0; + + h=key[0] +(key[len-1] * len); + + h &= (1<<bits) - 1; + + return(h); + } + + +int r_crc32(char *data,int len,UINT4 *crcval); + +int r_assoc_crc32_hash_compute(data,len,bits) + char *data; + int len; + int bits; + { + UINT4 res; + UINT4 mask; + + /* First compute the CRC value */ + if(r_crc32(data,len,&res)) + ERETURN(R_INTERNAL); + + mask=~(0xffffffff<<bits); + + return(res & mask); + } diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.h new file mode 100644 index 0000000000..5311542c9d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.h @@ -0,0 +1,126 @@ +/** + r_assoc.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_assoc.h + + Associative array code. This code has the advantage that different + elements can have different create and destroy operators. Unfortunately, + this can waste space. + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_assoc.h,v 1.3 2007/06/08 22:16:08 adamcain Exp $ + + + ekr@rtfm.com Sun Jan 17 17:57:18 1999 + */ + + +#ifndef _r_assoc_h +#define _r_assoc_h + +typedef struct r_assoc_ r_assoc; + +int r_assoc_create(r_assoc **assocp, + int (*hash_func)(char *,int,int), + int bits); +int r_assoc_insert(r_assoc *assoc,char *key,int len, + void *value,int (*copy)(void **knew,void *old), + int (*destroy)(void *ptr),int how); +#define R_ASSOC_REPLACE 0x1 +#define R_ASSOC_NEW 0x2 + +int r_assoc_fetch(r_assoc *assoc,char *key, int len, void **value); +int r_assoc_delete(r_assoc *assoc,char *key, int len); + +int r_assoc_copy(r_assoc **knew,r_assoc *old); +int r_assoc_destroy(r_assoc **assocp); +int r_assoc_simple_hash_compute(char *key, int len,int bits); +int r_assoc_crc32_hash_compute(char *key, int len,int bits); + +/*We need iterators, but I haven't written them yet*/ +typedef struct r_assoc_iterator_ { + r_assoc *assoc; + int prev_chain; + struct r_assoc_el_ *prev; + int next_chain; + struct r_assoc_el_ *next; +} r_assoc_iterator; + +int r_assoc_init_iter(r_assoc *assoc,r_assoc_iterator *); +int r_assoc_iter(r_assoc_iterator *iter,void **key,int *keyl, void **val); +int r_assoc_iter_delete(r_assoc_iterator *); + +int r_assoc_num_elements(r_assoc *assoc,int *sizep); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_common.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_common.h new file mode 100644 index 0000000000..c11bb3d0fd --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_common.h @@ -0,0 +1,100 @@ +/** + r_common.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_common.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_common.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $ + + + ekr@rtfm.com Tue Dec 22 10:40:07 1998 + */ + + +#ifndef _r_common_h +#define _r_common_h + +#include "r_defaults.h" +#include "r_includes.h" +#include "r_types.h" +#include "r_macros.h" +#include "r_errors.h" +#include "r_data.h" + +/* defines for possibly replaced functions */ +#ifndef HAVE_STRDUP +char *strdup(char *in); +#endif + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c new file mode 100644 index 0000000000..38d3e4da38 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c @@ -0,0 +1,175 @@ +/** + r_crc32.c + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + crc32.c + + Copyright (C) 2003, RTFM, Inc. + All Rights Reserved. + + ekr@rtfm.com Fri Jan 31 13:57:43 2003 + + THE FOLLOWING CODE WAS EXTRACTED FROM FreeBSD + The comment below was the original header + + The main function was modified to process a buffer + rather than a file + */ + +/* + * This code implements the AUTODIN II polynomial used by Ethernet, + * and can be used to calculate multicast address hash indices. + * It assumes that the low order bits will be transmitted first, + * and consequently the low byte should be sent first when + * the crc computation is finished. The crc should be complemented + * before transmission. + * The variable corresponding to the macro argument "crc" should + * be an unsigned long and should be preset to all ones for Ethernet + * use. An error-free packet will leave 0xDEBB20E3 in the crc. + * Spencer Garrett <srg@quick.com> + */ + + +#include <sys/types.h> +#include <r_types.h> + +#ifdef WIN32 +#define u_int32_t UINT4 +#endif + +#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) + +/* generated using the AUTODIN II polynomial + * x^32 + x^26 + x^23 + x^22 + x^16 + + * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 + */ +static const u_int32_t crctab[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +#include <stdio.h> +#include <sys/types.h> + + +int r_crc32 (char *buf,int dlen,u_int32_t *cval); + +int +r_crc32(buf, dlen, cval) + char *buf; + int dlen; + u_int32_t *cval; +{ + u_int32_t crc = ~0; + char *p ; + int i; + u_int32_t crc32_total = 0 ; + + p=buf; + + for(i=0;i<dlen;i++){ + CRC(crc, *p) ; + CRC(crc32_total, *p) ; + p++; + } + + *cval = ~crc ; + + return 0 ; +} diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.h new file mode 100644 index 0000000000..c11be4e04d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.h @@ -0,0 +1,14 @@ +/** + r_crc32.h + + Copyright (C) 2002-2003, Network Resonance, Inc. + All Rights Reserved. + + */ + +#ifndef _r_crc32_ +#define _r_crc32_ + +int r_crc32 (char *buf,int dlen, UINT4 *cval); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.c new file mode 100644 index 0000000000..dfb7af2d5c --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.c @@ -0,0 +1,248 @@ +/** + r_data.c + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_data.c + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_data.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $ + + ekr@rtfm.com Tue Aug 17 15:39:50 1999 + */ + +#include <string.h> +#include <r_common.h> +#include <r_data.h> +#include <string.h> + +int r_data_create(dp,d,l) + Data **dp; + const UCHAR *d; + size_t l; + { + Data *d_=0; + int _status; + + if(!(d_=(Data *)RCALLOC(sizeof(Data)))) + ABORT(R_NO_MEMORY); + if(!(d_->data=(UCHAR *)RMALLOC(l))) + ABORT(R_NO_MEMORY); + + if (d) { + memcpy(d_->data,d,l); + } + d_->len=l; + + *dp=d_; + + _status=0; + abort: + if(_status) + r_data_destroy(&d_); + + return(_status); + } + + +int r_data_alloc_mem(d,l) + Data *d; + size_t l; + { + int _status; + + if(!(d->data=(UCHAR *)RMALLOC(l))) + ABORT(R_NO_MEMORY); + d->len=l; + + _status=0; + abort: + return(_status); + } + +int r_data_alloc(dp,l) + Data **dp; + size_t l; + { + Data *d_=0; + int _status; + + if(!(d_=(Data *)RCALLOC(sizeof(Data)))) + ABORT(R_NO_MEMORY); + if(!(d_->data=(UCHAR *)RCALLOC(l))) + ABORT(R_NO_MEMORY); + + d_->len=l; + + *dp=d_; + _status=0; + abort: + if(_status) + r_data_destroy(&d_); + + return(_status); + } + +int r_data_make(dp,d,l) + Data *dp; + const UCHAR *d; + size_t l; + { + if(!(dp->data=(UCHAR *)RMALLOC(l))) + ERETURN(R_NO_MEMORY); + + memcpy(dp->data,d,l); + dp->len=l; + + return(0); + } + +int r_data_destroy(dp) + Data **dp; + { + if(!dp || !*dp) + return(0); + + if((*dp)->data) + RFREE((*dp)->data); + + RFREE(*dp); + *dp=0; + + return(0); + } + +int r_data_destroy_v(v) + void *v; + { + Data *d; + + if(!v) + return(0); + + d=(Data *)v; + r_data_zfree(d); + + RFREE(d); + + return(0); + } + +int r_data_destroy_vp(v) + void **v; + { + Data *d; + + if(!v || !*v) + return(0); + + d=(Data *)*v; + r_data_zfree(d); + + *v=0; + RFREE(d); + + return(0); + } + +int r_data_copy(dst,src) + Data *dst; + Data *src; + { + if(!(dst->data=(UCHAR *)RMALLOC(src->len))) + ERETURN(R_NO_MEMORY); + memcpy(dst->data,src->data,dst->len=src->len); + return(0); + } + +int r_data_zfree(d) + Data *d; + { + if(!d) + return(0); + if(!d->data) + return(0); + memset(d->data,0,d->len); + RFREE(d->data); + return(0); + } + +int r_data_compare(d1,d2) + Data *d1; + Data *d2; + { + if(d1->len<d2->len) + return(-1); + if(d2->len<d1->len) + return(-1); + return(memcmp(d1->data,d2->data,d1->len)); + } + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.h new file mode 100644 index 0000000000..3edf7b287a --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.h @@ -0,0 +1,108 @@ +/** + r_data.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_data.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_data.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $ + + + ekr@rtfm.com Wed Feb 10 14:18:19 1999 + */ + + +#ifndef _r_data_h +#define _r_data_h + +typedef struct Data_ { + UCHAR *data; + size_t len; +} Data; + +int r_data_create(Data **dp,const UCHAR *d,size_t l); +int r_data_alloc(Data **dp, size_t l); +int r_data_make(Data *dp, const UCHAR *d,size_t l); +int r_data_alloc_mem(Data *d,size_t l); +int r_data_destroy(Data **dp); +int r_data_destroy_v(void *v); +int r_data_destroy_vp(void **vp); +int r_data_copy(Data *dst,Data *src); +int r_data_zfree(Data *d); +int r_data_compare(Data *d1,Data *d2); + +#define INIT_DATA(a,b,c) (a).data=b; (a).len=c +#define ATTACH_DATA(a,b) (a).data=b; (a).len=sizeof(b) +#define ZERO_DATA(a) (a).data=0; (a).len=0 + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_defaults.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_defaults.h new file mode 100644 index 0000000000..0ec91a0331 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_defaults.h @@ -0,0 +1,91 @@ +/** + r_defaults.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_defaults.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_defaults.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $ + + + ekr@rtfm.com Tue Dec 22 10:39:14 1998 + */ + + +#ifndef _r_defaults_h +#define _r_defaults_h + +/*The needs defines don't belong here*/ +#define R_NEEDS_STDLIB_H + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.c new file mode 100644 index 0000000000..e770e02438 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.c @@ -0,0 +1,136 @@ +/** + r_errors.c + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_errors.c + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_errors.c,v 1.5 2008/11/26 03:22:02 adamcain Exp $ + + + ekr@rtfm.com Tue Feb 16 16:37:05 1999 + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "r_common.h" +#include "r_errors.h" + +static struct { + int errnum; + char *str; +} errors[] = NR_ERROR_MAPPING; + +int nr_verr_exit(char *fmt,...) + { + va_list ap; + + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + + if (fmt[0] != '\0' && fmt[strlen(fmt)-1] != '\n') + fprintf(stderr,"\n"); + + exit(1); + } + +char * +nr_strerror(int errnum) +{ + static char unknown_error[256]; + size_t i; + char *error = 0; + + for (i = 0; i < sizeof(errors)/sizeof(*errors); ++i) { + if (errnum == errors[i].errnum) { + error = errors[i].str; + break; + } + } + + if (! error) { + snprintf(unknown_error, sizeof(unknown_error), "Unknown error: %d", errnum); + error = unknown_error; + } + + return error; +} + +int +nr_strerror_r(int errnum, char *strerrbuf, size_t buflen) +{ + char *error = nr_strerror(errnum); + snprintf(strerrbuf, buflen, "%s", error); + return 0; +} + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.h new file mode 100644 index 0000000000..f52375b98d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.h @@ -0,0 +1,127 @@ +/** + r_errors.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_errors.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_errors.h,v 1.4 2007/10/12 20:53:24 adamcain Exp $ + + + ekr@rtfm.com Tue Dec 22 10:59:49 1998 + */ + + +#ifndef _r_errors_h +#define _r_errors_h + +#define R_NO_MEMORY 1 /*out of memory*/ +#define R_NOT_FOUND 2 /*Item not found*/ +#define R_INTERNAL 3 /*Unspecified internal error*/ +#define R_ALREADY 4 /*Action already done*/ +#define R_EOD 5 /*end of data*/ +#define R_BAD_ARGS 6 /*Bad arguments*/ +#define R_BAD_DATA 7 /*Bad data*/ +#define R_WOULDBLOCK 8 /*Operation would block */ +#define R_QUEUED 9 /*Operation was queued */ +#define R_FAILED 10 /*Operation failed */ +#define R_REJECTED 11 /* We don't care about this */ +#define R_INTERRUPTED 12 /* Operation interrupted */ +#define R_IO_ERROR 13 /* I/O Error */ +#define R_NOT_PERMITTED 14 /* Permission denied */ +#define R_RETRY 15 /* Retry possible */ + +#define NR_ERROR_MAPPING {\ + { R_NO_MEMORY, "Cannot allocate memory" },\ + { R_NOT_FOUND, "Item not found" },\ + { R_INTERNAL, "Internal failure" },\ + { R_ALREADY, "Action already performed" },\ + { R_EOD, "End of data" },\ + { R_BAD_ARGS, "Invalid argument" },\ + { R_BAD_DATA, "Invalid data" },\ + { R_WOULDBLOCK, "Operation would block" },\ + { R_QUEUED, "Operation queued" },\ + { R_FAILED, "Operation failed" },\ + { R_REJECTED, "Operation rejected" },\ + { R_INTERRUPTED, "Operation interrupted" },\ + { R_IO_ERROR, "I/O error" },\ + { R_NOT_PERMITTED, "Permission Denied" },\ + { R_RETRY, "Retry may be possible" },\ + } + +int nr_verr_exit(char *fmt,...); + +char *nr_strerror(int errnum); +int nr_strerror_r(int errnum, char *strerrbuf, size_t buflen); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_includes.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_includes.h new file mode 100644 index 0000000000..4fb7af5643 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_includes.h @@ -0,0 +1,98 @@ +/** + r_includes.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_includes.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_includes.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $ + + + ekr@rtfm.com Tue Dec 22 11:38:50 1998 + */ + + +#ifndef _r_includes_h +#define _r_includes_h + +#ifdef R_NEEDS_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef R_NEEDS_MEMORY_H +#include <memory.h> +#endif + +#include <stdio.h> + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.c new file mode 100644 index 0000000000..4e71d67030 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.c @@ -0,0 +1,273 @@ +/** + r_list.c + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_list.c + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_list.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $ + + + ekr@rtfm.com Tue Jan 19 08:36:39 1999 + */ + +#include <r_common.h> +#include "r_list.h" + +typedef struct r_list_el_ { + void *data; + struct r_list_el_ *next; + struct r_list_el_ *prev; + int (*copy)(void **new,void *old); + int (*destroy)(void **ptr); +} r_list_el; + +struct r_list_ { + struct r_list_el_ *first; + struct r_list_el_ *last; +}; + +int r_list_create(listp) + r_list **listp; + { + r_list *list=0; + int _status; + + if(!(list=(r_list *)RCALLOC(sizeof(r_list)))) + ABORT(R_NO_MEMORY); + + list->first=0; + list->last=0; + *listp=list; + + _status=0; + abort: + return(_status); + } + +int r_list_destroy(listp) + r_list **listp; + { + r_list *list; + r_list_el *el; + + if(!listp || !*listp) + return(0); + list=*listp; + + el=list->first; + + while(el){ + r_list_el *el_t; + + if(el->destroy && el->data) + el->destroy(&el->data); + el_t=el; + el=el->next; + RFREE(el_t); + } + + RFREE(list); + *listp=0; + + return(0); + } + +int r_list_copy(outp,in) + r_list**outp; + r_list *in; + { + r_list *out=0; + r_list_el *el,*el2,*last=0; + int r, _status; + + if(!in){ + *outp=0; + return(0); + } + + if(r=r_list_create(&out)) + ABORT(r); + + for(el=in->first;el;el=el->next){ + if(!(el2=(r_list_el *)RCALLOC(sizeof(r_list_el)))) + ABORT(R_NO_MEMORY); + + if(el->copy && el->data){ + if(r=el->copy(&el2->data,el->data)) + ABORT(r); + } + + el2->copy=el->copy; + el2->destroy=el->destroy; + + if(!(out->first)) + out->first=el2; + + el2->prev=last; + if(last) last->next=el2; + last=el2; + } + + out->last=last; + + *outp=out; + + _status=0; + abort: + if(_status) + r_list_destroy(&out); + return(_status); + } + +int r_list_insert(list,value,copy,destroy) + r_list *list; + void *value; + int (*copy)(void **out, void *in); + int (*destroy)(void **val); + { + r_list_el *el=0; + int _status; + + if(!(el=(r_list_el *)RCALLOC(sizeof(r_list_el)))) + ABORT(R_NO_MEMORY); + el->data=value; + el->copy=copy; + el->destroy=destroy; + + el->prev=0; + el->next=list->first; + if(list->first){ + list->first->prev=el; + } + list->first=el; + + _status=0; + abort: + return(_status); + } + +int r_list_append(list,value,copy,destroy) + r_list *list; + void *value; + int (*copy)(void **out, void *in); + int (*destroy)(void **val); + { + r_list_el *el=0; + int _status; + + if(!(el=(r_list_el *)RCALLOC(sizeof(r_list_el)))) + ABORT(R_NO_MEMORY); + el->data=value; + el->copy=copy; + el->destroy=destroy; + + el->prev=list->last; + el->next=0; + + if(list->last) list->last->next=el; + else list->first=el; + + list->last=el; + + _status=0; + abort: + return(_status); + } + +int r_list_init_iter(list,iter) + r_list *list; + r_list_iterator *iter; + { + iter->list=list; + iter->ptr=list->first; + + return(0); + } + +int r_list_iter(iter,val) + r_list_iterator *iter; + void **val; + { + if(!iter->ptr) + return(R_EOD); + + *val=iter->ptr->data; + iter->ptr=iter->ptr->next; + + return(0); + } + + + + + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.h new file mode 100644 index 0000000000..bbefc6c39f --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.h @@ -0,0 +1,106 @@ +/** + r_list.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_list.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_list.h,v 1.3 2007/06/08 17:41:49 adamcain Exp $ + + + ekr@rtfm.com Tue Jan 19 08:36:48 1999 + */ + + +#ifndef _r_list_h +#define _r_list_h + +typedef struct r_list_ r_list; + +typedef struct r_list_iterator_ { + r_list *list; + struct r_list_el_ *ptr; +} r_list_iterator; + +int r_list_create(r_list **listp); +int r_list_destroy(r_list **listp); +int r_list_copy(r_list **out,r_list *in); +int r_list_insert(r_list *list,void *value, + int (*copy)(void **knew,void *old), + int (*destroy)(void **ptr)); +int r_list_append(r_list *list,void *value, + int (*copy)(void **knew,void *old), + int (*destroy)(void **ptr)); +int r_list_init_iter(r_list *list,r_list_iterator *iter); +int r_list_iter(r_list_iterator *iter,void **val); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h new file mode 100644 index 0000000000..ddfedd3c36 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h @@ -0,0 +1,137 @@ +/** + r_macros.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_macros.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_macros.h,v 1.3 2007/06/26 22:37:57 adamcain Exp $ + + ekr@rtfm.com Tue Dec 22 10:37:32 1998 + */ + + +#ifndef _r_macros_h +#define _r_macros_h + +/* Haven't removed all PROTO_LIST defs yet */ +#define PROTO_LIST(a) a + +#ifndef WIN32 +#ifndef __GNUC__ +#define __FUNCTION__ "unknown" +#endif +#endif + +#ifdef R_TRACE_ERRORS +#ifdef WIN32 +#define REPORT_ERROR_(caller,a) printf("%s: error %d at %s:%d (function %s)\n", \ + caller,a,__FILE__,__LINE__,__FUNCTION__) +#else +#define REPORT_ERROR_(caller,a) fprintf(stderr,"%s: error %d at %s:%d (function %s)\n", \ + caller,a,__FILE__,__LINE__,__FUNCTION__) +#endif +#else +#define REPORT_ERROR_(caller,a) +#endif + +#ifndef ERETURN +#define ERETURN(a) do {int _r=a; if(!_r) _r=-1; REPORT_ERROR_("ERETURN",_r); return(_r);} while(0) +#endif + +#ifndef ABORT +#define ABORT(a) do { int _r=a; if(!_r) _r=-1; REPORT_ERROR_("ABORT",_r); _status=_r; goto abort;} while(0) +#endif + +#ifndef FREE +#define FREE(a) if(a) free(a) +#endif +#ifndef MIN +#define MIN(a,b) ((a)>(b))?(b):(a) +#endif + +#ifndef MAX +#define MAX(a,b) ((b)>(a))?(b):(a) +#endif + +#ifdef DEBUG +#define DBG(a) debug a +int debug(int cls, char *format,...); +#else +#define DBG(a) +#endif + +#define NR_UNIMPLEMENTED do { fprintf(stderr,"%s:%d Function %s unimplemented\n",__FILE__,__LINE__,__FUNCTION__); abort(); } while(0) + +#include "r_memory.h" + +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.c new file mode 100644 index 0000000000..53846fc019 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.c @@ -0,0 +1,198 @@ +/** + r_memory.c + + + Copyright (C) 2004, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Thu Apr 22 20:40:45 2004 + */ + +#include <string.h> +#include <stddef.h> +#include <assert.h> +#include "r_common.h" +#include "r_memory.h" + +typedef struct r_malloc_chunk_ { +#ifdef SANITY_CHECKS + UINT4 hdr; +#endif + UCHAR type; + UINT4 size; + UCHAR memory[1]; +} r_malloc_chunk; + +#define CHUNK_MEMORY_OFFSET offsetof(struct r_malloc_chunk_, memory) +#define GET_CHUNK_ADDR_FROM_MEM_ADDR(memp) \ + ((struct r_malloc_chunk *)(((unsigned char*)(memp))-CHUNK_MEMORY_OFFSET)) +#define CHUNK_SIZE(size) (size+sizeof(r_malloc_chunk)) + +#define HDR_FLAG 0x464c4147 + +static UINT4 mem_usage; /* Includes our header */ +static UINT4 mem_stats[256]; /* Does not include our header */ + +void *r_malloc(type,size) + int type; + size_t size; + { + size_t total; + r_malloc_chunk *chunk; + + total=size+sizeof(r_malloc_chunk); + + if(!(chunk=malloc(total))) + return(0); + +#ifdef SANITY_CHECKS + chunk->hdr=HDR_FLAG; +#endif + chunk->type=type; + chunk->size=size; + + mem_usage+=CHUNK_SIZE(size); + mem_stats[type]+=size; + + return(chunk->memory); + } + +void *r_calloc(type,number,size) + int type; + size_t number; + size_t size; + { + void *ret; + size_t total; + + total=number*size; + + if(!(ret=r_malloc(type,total))) + return(0); + + memset(ret,0,size); + + return(ret); + } + +void r_free(ptr) + void *ptr; + { + r_malloc_chunk *chunk; + + if(!ptr) return; + + chunk=(r_malloc_chunk *)GET_CHUNK_ADDR_FROM_MEM_ADDR(ptr); +#ifdef SANITY_CHECKS + assert(chunk->hdr==HDR_FLAG); +#endif + + mem_usage-=CHUNK_SIZE(chunk->size); + mem_stats[chunk->type]-=chunk->size; + + free(chunk); + } + +void *r_realloc(ptr,size) + void *ptr; + size_t size; + { + r_malloc_chunk *chunk,*nchunk; + size_t total; + + if(!ptr) return(r_malloc(255,size)); + + chunk=(r_malloc_chunk *)GET_CHUNK_ADDR_FROM_MEM_ADDR(ptr); +#ifdef SANITY_CHECKS + assert(chunk->hdr==HDR_FLAG); +#endif + + total=size + sizeof(r_malloc_chunk); + + if(!(nchunk=realloc(chunk,total))) + return(0); + + mem_usage-=CHUNK_SIZE(nchunk->size); + mem_stats[nchunk->type]-=nchunk->size; + + nchunk->size=size; + mem_usage+=CHUNK_SIZE(nchunk->size); + mem_stats[nchunk->type]+=nchunk->size; + + return(nchunk->memory); + } + +char *r_strdup(str) + const char *str; + { + int len; + char *nstr; + + if(!str) + return(0); + + len=strlen(str)+1; + + if(!(nstr=r_malloc(0,len))) + return(0); + + memcpy(nstr,str,len); + + return(nstr); + } + +int r_mem_get_usage(usagep) + UINT4 *usagep; + { + *usagep=mem_usage; + + return(0); + } + +int r_memory_dump_stats() + { + int i; + + printf("Total memory usage: %d\n",mem_usage); + printf("Memory usage by bucket\n"); + for(i=0;i<256;i++){ + if(mem_stats[i]){ + printf("%d\t%d\n",i,mem_stats[i]); + } + } + return(0); + } + +void *r_malloc_compat(size) + size_t size; + { + return(r_malloc(255,size)); + } diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.h new file mode 100644 index 0000000000..4357070767 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.h @@ -0,0 +1,101 @@ +/** + r_memory.h + + + Copyright (C) 2004, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Sat Apr 24 08:30:00 2004 + */ + + +#ifndef _r_memory_h +#define _r_memory_h + +#define R_MALLOC_X 2 + +#include "r_types.h" + +void *r_malloc(int type, size_t size); +void *r_malloc_compat(size_t size); +void *r_calloc(int type,size_t number,size_t size); +void r_free (void *ptr); +void *r_realloc(void *ptr,size_t size); +char *r_strdup(const char *str); +int r_mem_get_usage(UINT4 *usage); +int r_memory_dump_stats(void); + +#ifdef NO_MALLOC_REPLACE + +#ifndef RMALLOC +#define RMALLOC(a) malloc(a) +#endif + +#ifndef RCALLOC +#define RCALLOC(a) calloc(1,a) +#endif + +#ifndef RFREE +#define RFREE(a) if(a) free(a) +#endif + +#ifndef RREALLOC +#define RREALLOC(a,b) realloc(a,b) +#endif + +#else + + +#ifndef R_MALLOC_TYPE +#define R_MALLOC_TYPE 0 +#endif + +#ifndef RMALLOC +#define RMALLOC(a) r_malloc(R_MALLOC_TYPE,a) +#endif + +#ifndef RCALLOC +#define RCALLOC(a) r_calloc(R_MALLOC_TYPE,1,a) +#endif + +#ifndef RFREE +#define RFREE(a) if(a) r_free(a) +#endif + +#ifndef RREALLOC +#define RREALLOC(a,b) r_realloc(a,b) +#endif + +#endif + + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_replace.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_replace.c new file mode 100644 index 0000000000..8916b884cc --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_replace.c @@ -0,0 +1,107 @@ +/** + r_replace.c + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_replace.c + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_replace.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $ + + + ekr@rtfm.com Sun Oct 1 11:18:49 2000 + */ + +#include "r_common.h" + +#ifndef HAVE_STRDUP + +char *strdup(str) + char *str; + { + int len=strlen(str); + char *n; + + if(!(n=(char *)malloc(len+1))) + return(0); + + memcpy(n,str,len+1); + + return(n); + } +#endif + + +#ifdef SUPPLY_ATEXIT +int atexit(void (*func)(void)){ + ; +} +#endif diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_thread.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_thread.h new file mode 100644 index 0000000000..212900bcc4 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_thread.h @@ -0,0 +1,68 @@ +/** + r_thread.h + + + Copyright (C) 1999, RTFM, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Tue Feb 23 14:58:36 1999 + */ + + +#ifndef _r_thread_h +#define _r_thread_h + +typedef void *r_thread; +typedef void *r_rwlock; +typedef void * r_cond; + +int r_thread_fork (void (*func)(void *),void *arg, + r_thread *tid); +int r_thread_destroy (r_thread tid); +int r_thread_yield (void); +int r_thread_exit (void); +int r_thread_wait_last (void); +int r_thread_self (void); + +int r_rwlock_create (r_rwlock **lockp); +int r_rwlock_destroy (r_rwlock **lock); +int r_rwlock_lock (r_rwlock *lock,int action); + +int r_cond_init (r_cond *cond); +int r_cond_wait (r_cond cond); +int r_cond_signal (r_cond cond); + +#define R_RWLOCK_UNLOCK 0 +#define R_RWLOCK_RLOCK 1 +#define R_RWLOCK_WLOCK 2 + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.c new file mode 100644 index 0000000000..f873585c4b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.c @@ -0,0 +1,235 @@ +/** + r_time.c + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_time.c + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_time.c,v 1.5 2008/11/26 03:22:02 adamcain Exp $ + + ekr@rtfm.com Thu Mar 4 08:43:46 1999 + */ + +#include <r_common.h> +#include <r_time.h> + +/*Note that t1 must be > t0 */ +int r_timeval_diff(t1,t0,diff) + struct timeval *t1; + struct timeval *t0; + struct timeval *diff; + { + long d; + + if(t0->tv_sec > t1->tv_sec) + ERETURN(R_BAD_ARGS); + if((t0->tv_sec == t1->tv_sec) && (t0->tv_usec > t1->tv_usec)) + ERETURN(R_BAD_ARGS); + + /*Easy case*/ + if(t0->tv_usec <= t1->tv_usec){ + diff->tv_sec=t1->tv_sec - t0->tv_sec; + diff->tv_usec=t1->tv_usec - t0->tv_usec; + return(0); + } + + /*Hard case*/ + d=t0->tv_usec - t1->tv_usec; + if(t1->tv_sec < (t0->tv_sec + 1)) + ERETURN(R_BAD_ARGS); + diff->tv_sec=t1->tv_sec - (t0->tv_sec + 1); + diff->tv_usec=1000000 - d; + + return(0); + } + +int r_timeval_add(t1,t2,sum) + struct timeval *t1; + struct timeval *t2; + struct timeval *sum; + { + long tv_sec,tv_usec,d; + + tv_sec=t1->tv_sec + t2->tv_sec; + + d=t1->tv_usec + t2->tv_usec; + if(d>1000000){ + tv_sec++; + tv_usec=d-1000000; + } + else{ + tv_usec=d; + } + + sum->tv_sec=tv_sec; + sum->tv_usec=tv_usec; + + return(0); + } + +int r_timeval_cmp(t1,t2) + struct timeval *t1; + struct timeval *t2; + { + if(t1->tv_sec>t2->tv_sec) + return(1); + if(t1->tv_sec<t2->tv_sec) + return(-1); + if(t1->tv_usec>t2->tv_usec) + return(1); + if(t1->tv_usec<t2->tv_usec) + return(-1); + return(0); + } + + +UINT8 r_timeval2int(tv) + struct timeval *tv; + { + UINT8 r=0; + + r=(tv->tv_sec); + r*=1000000; + r+=tv->tv_usec; + + return r; + } + +int r_int2timeval(UINT8 t,struct timeval *tv) + { + tv->tv_sec=t/1000000; + tv->tv_usec=t%1000000; + + return(0); + } + +UINT8 r_gettimeint() + { + struct timeval tv; + + gettimeofday(&tv,0); + + return r_timeval2int(&tv); + } + +/* t1-t0 in microseconds */ +int r_timeval_diff_usec(struct timeval *t1, struct timeval *t0, INT8 *diff) + { + int r,_status; + int sign; + struct timeval tmp; + + sign = 1; + if (r=r_timeval_diff(t1, t0, &tmp)) { + if (r == R_BAD_ARGS) { + sign = -1; + if (r=r_timeval_diff(t0, t1, &tmp)) + ABORT(r); + } + } + + /* 1 second = 1000 milliseconds + * 1 milliseconds = 1000 microseconds */ + + *diff = ((tmp.tv_sec * (1000*1000)) + tmp.tv_usec) * sign; + + _status = 0; + abort: + return(_status); + } + +/* t1-t0 in milliseconds */ +int r_timeval_diff_ms(struct timeval *t1, struct timeval *t0, INT8 *diff) + { + int r,_status; + int sign; + struct timeval tmp; + + sign = 1; + if (r=r_timeval_diff(t1, t0, &tmp)) { + if (r == R_BAD_ARGS) { + sign = -1; + if (r=r_timeval_diff(t0, t1, &tmp)) + ABORT(r); + } + } + + /* 1 second = 1000 milliseconds + * 1 milliseconds = 1000 microseconds */ + + *diff = ((tmp.tv_sec * 1000) + (tmp.tv_usec / 1000)) * sign; + + _status = 0; + abort: + return(_status); + } + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h new file mode 100644 index 0000000000..1ee1e778fe --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h @@ -0,0 +1,109 @@ +/** + r_time.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_time.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_time.h,v 1.4 2007/06/26 22:37:57 adamcain Exp $ + + + ekr@rtfm.com Thu Mar 4 08:45:41 1999 + */ + + +#ifndef _r_time_h +#define _r_time_h + +#include <csi_platform.h> + +#ifndef WIN32 +#include <sys/time.h> +#include <time.h> +#endif + +int r_timeval_diff(struct timeval *t1,struct timeval *t0, struct timeval *diff); +int r_timeval_add(struct timeval *t1,struct timeval *t2, struct timeval *sum); +int r_timeval_cmp(struct timeval *t1,struct timeval *t2); + +UINT8 r_timeval2int(struct timeval *tv); +int r_int2timeval(UINT8 t,struct timeval *tv); +UINT8 r_gettimeint(void); + +/* t1-t0 in microseconds */ +int r_timeval_diff_usec(struct timeval *t1, struct timeval *t0, INT8 *diff); + +/* t1-t0 in milliseconds */ +int r_timeval_diff_ms(struct timeval *t1, struct timeval *t0, INT8 *diff); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_types.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_types.h new file mode 100644 index 0000000000..d00810a7a2 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_types.h @@ -0,0 +1,213 @@ +/** + r_types.h + + + Copyright (C) 2002-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + */ + +/** + r_types.h + + + Copyright (C) 1999-2000 RTFM, Inc. + All Rights Reserved + + This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla + <ekr@rtfm.com> and licensed by RTFM, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed by Eric Rescorla for + RTFM, Inc. + + 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``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 REGENTS 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 SUCH DAMAGE. + + $Id: r_types.h,v 1.2 2006/08/16 19:39:18 adamcain Exp $ + + + ekr@rtfm.com Tue Dec 22 10:36:02 1998 + */ + + +#ifndef _r_types_h +#define _r_types_h + +/* Either define R_PLATFORM_INT_TYPES or be on a platform that + has stdint.h */ +#ifdef R_PLATFORM_INT_TYPES +#include R_PLATFORM_INT_TYPES +#else +#include <stdint.h> +#endif + +#ifndef R_DEFINED_INT2 +#ifndef SIZEOF_INT +typedef short INT2; +#else +# if (SIZEOF_INT==2) +typedef int INT2; +# elif (SIZEOF_SHORT==2) +typedef short INT2; +# elif (SIZEOF_LONG==2) +typedef long INT2; +# else +# error no type for INT2 +# endif +#endif +#else +typedef R_DEFINED_INT2 INT2; +#endif + +#ifndef R_DEFINED_UINT2 +#ifndef SIZEOF_UNSIGNED_INT +typedef unsigned short UINT2; +#else +# if (SIZEOF_UNSIGNED_INT==2) +typedef unsigned int UINT2; +# elif (SIZEOF_UNSIGNED_SHORT==2) +typedef unsigned short UINT2; +# elif (SIZEOF_UNSIGNED_LONG==2) +typedef unsigned long UINT2; +# else +# error no type for UINT2 +# endif +#endif +#else +typedef R_DEFINED_UINT2 UINT2; +#endif + +#ifndef R_DEFINED_INT4 +#ifndef SIZEOF_INT +typedef int INT4; +#else +# if (SIZEOF_INT==4) +typedef int INT4; +# elif (SIZEOF_SHORT==4) +typedef short INT4; +# elif (SIZEOF_LONG==4) +typedef long INT4; +# else +# error no type for INT4 +# endif +#endif +#else +typedef R_DEFINED_INT4 INT4; +#endif + +#ifndef R_DEFINED_UINT4 +#ifndef SIZEOF_UNSIGNED_INT +typedef unsigned int UINT4; +#else +# if (SIZEOF_UNSIGNED_INT==4) +typedef unsigned int UINT4; +# elif (SIZEOF_UNSIGNED_SHORT==4) +typedef unsigned short UINT4; +# elif (SIZEOF_UNSIGNED_LONG==4) +typedef unsigned long UINT4; +# else +# error no type for UINT4 +# endif +#endif +#else +typedef R_DEFINED_UINT4 UINT4; +#endif + +#ifndef R_DEFINED_INT8 +#ifndef SIZEOF_INT +typedef long long INT8; +#else +# if (SIZEOF_INT==8) +typedef int INT8; +# elif (SIZEOF_SHORT==8) +typedef short INT8; +# elif (SIZEOF_LONG==8) +typedef long INT8; +# elif (SIZEOF_LONG_LONG==8) +typedef long long INT8; +# else +# error no type for INT8 +# endif +#endif +#else +typedef R_DEFINED_INT8 INT8; +#endif + +#ifndef R_DEFINED_UINT8 +#ifndef SIZEOF_UNSIGNED_INT +typedef unsigned long long UINT8; +#else +# if (SIZEOF_UNSIGNED_INT==8) +typedef unsigned int UINT8; +# elif (SIZEOF_UNSIGNED_SHORT==8) +typedef unsigned short UINT8; +# elif (SIZEOF_UNSIGNED_LONG==8) +typedef unsigned long UINT8; +# elif (SIZEOF_UNSIGNED_LONG_LONG==8) +typedef unsigned long long UINT8; +# else +# error no type for UINT8 +# endif +#endif +#else +typedef R_DEFINED_UINT8 UINT8; +#endif + +#ifndef R_DEFINED_UCHAR +typedef unsigned char UCHAR; +#else +typedef R_DEFINED_UCHAR UCHAR; +#endif +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.c new file mode 100644 index 0000000000..459baecdda --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.c @@ -0,0 +1,215 @@ +/** + p_buf.c + + + Copyright (C) 2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + All Rights Reserved. + + ekr@rtfm.com Tue Nov 25 16:33:08 2003 + */ + +#include <string.h> +#include <stddef.h> +#include "nr_common.h" +#include "p_buf.h" + + +static int nr_p_buf_destroy_chain(nr_p_buf_head *head); +static int nr_p_buf_destroy(nr_p_buf *buf); + +int nr_p_buf_ctx_create(size,ctxp) + int size; + nr_p_buf_ctx **ctxp; + { + int _status; + nr_p_buf_ctx *ctx=0; + + if(!(ctx=(nr_p_buf_ctx *)RCALLOC(sizeof(nr_p_buf_ctx)))) + ABORT(R_NO_MEMORY); + + ctx->buf_size=size; + STAILQ_INIT(&ctx->free_list); + + *ctxp=ctx; + _status=0; + abort: + if(_status){ + nr_p_buf_ctx_destroy(&ctx); + } + return(_status); + } + +int nr_p_buf_ctx_destroy(ctxp) + nr_p_buf_ctx **ctxp; + { + nr_p_buf_ctx *ctx; + + if(!ctxp || !*ctxp) + return(0); + + ctx=*ctxp; + + nr_p_buf_destroy_chain(&ctx->free_list); + + RFREE(ctx); + *ctxp=0; + + return(0); + } + +int nr_p_buf_alloc(ctx,bufp) + nr_p_buf_ctx *ctx; + nr_p_buf **bufp; + { + int _status; + nr_p_buf *buf=0; + + if(!STAILQ_EMPTY(&ctx->free_list)){ + buf=STAILQ_FIRST(&ctx->free_list); + STAILQ_REMOVE_HEAD(&ctx->free_list,entry); + goto ok; + } + else { + if(!(buf=(nr_p_buf *)RCALLOC(sizeof(nr_p_buf)))) + ABORT(R_NO_MEMORY); + if(!(buf->data=(UCHAR *)RMALLOC(ctx->buf_size))) + ABORT(R_NO_MEMORY); + buf->size=ctx->buf_size; + } + + ok: + buf->r_offset=0; + buf->length=0; + + *bufp=buf; + _status=0; + abort: + if(_status){ + nr_p_buf_destroy(buf); + } + return(_status); + } + +int nr_p_buf_free(ctx,buf) + nr_p_buf_ctx *ctx; + nr_p_buf *buf; + { + STAILQ_INSERT_TAIL(&ctx->free_list,buf,entry); + + return(0); + } + +int nr_p_buf_free_chain(ctx,head) + nr_p_buf_ctx *ctx; + nr_p_buf_head *head; + { + nr_p_buf *n1,*n2; + + n1=STAILQ_FIRST(head); + while(n1){ + n2=STAILQ_NEXT(n1,entry); + + nr_p_buf_free(ctx,n1); + + n1=n2; + } + + return(0); + } + + +int nr_p_buf_write_to_chain(ctx,chain,data,len) + nr_p_buf_ctx *ctx; + nr_p_buf_head *chain; + UCHAR *data; + UINT4 len; + { + int r,_status; + nr_p_buf *buf; + + buf=STAILQ_LAST(chain,nr_p_buf_,entry); + while(len){ + int towrite; + + if(!buf){ + if(r=nr_p_buf_alloc(ctx,&buf)) + ABORT(r); + STAILQ_INSERT_TAIL(chain,buf,entry); + } + + towrite=MIN(len,(buf->size-(buf->length+buf->r_offset))); + + memcpy(buf->data+buf->length+buf->r_offset,data,towrite); + len-=towrite; + data+=towrite; + buf->length+=towrite; + + r_log(LOG_COMMON,LOG_DEBUG,"Wrote %d bytes to buffer %p",towrite,buf); + buf=0; + } + + _status=0; + abort: + return(_status); + } + +static int nr_p_buf_destroy_chain(head) + nr_p_buf_head *head; + { + nr_p_buf *n1,*n2; + + n1=STAILQ_FIRST(head); + while(n1){ + n2=STAILQ_NEXT(n1,entry); + + nr_p_buf_destroy(n1); + + n1=n2; + } + + return(0); + } + +static int nr_p_buf_destroy(buf) + nr_p_buf *buf; + { + if(!buf) + return(0); + + RFREE(buf->data); + RFREE(buf); + + return(0); + } + + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.h new file mode 100644 index 0000000000..29881960c6 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.h @@ -0,0 +1,72 @@ +/** + p_buf.h + + + Copyright (C) 2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Tue Nov 25 15:58:37 2003 + */ + + +#ifndef _p_buf_h +#define _p_buf_h + +typedef struct nr_p_buf_ { + UCHAR *data; /*The pointer to the buffer where the data lives */ + UINT4 size; /*The size of the buffer */ + UINT4 r_offset; /*The offset into the buffer where the data starts + when reading */ + UINT4 length; /*The length of the data portion */ + + STAILQ_ENTRY(nr_p_buf_) entry; +} nr_p_buf; + +typedef STAILQ_HEAD(nr_p_buf_head_,nr_p_buf_) nr_p_buf_head; + + +typedef struct nr_p_buf_ctx_ { + int buf_size; + + nr_p_buf_head free_list; +} nr_p_buf_ctx; + +int nr_p_buf_ctx_create(int size,nr_p_buf_ctx **ctxp); +int nr_p_buf_ctx_destroy(nr_p_buf_ctx **ctxp); +int nr_p_buf_alloc(nr_p_buf_ctx *ctx,nr_p_buf **bufp); +int nr_p_buf_free(nr_p_buf_ctx *ctx,nr_p_buf *buf); +int nr_p_buf_free_chain(nr_p_buf_ctx *ctx,nr_p_buf_head *chain); +int nr_p_buf_write_to_chain(nr_p_buf_ctx *ctx, + nr_p_buf_head *chain, + UCHAR *data,UINT4 len); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c new file mode 100644 index 0000000000..17d49639fd --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c @@ -0,0 +1,775 @@ +/** + util.c + + + Copyright (C) 2001-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Wed Dec 26 17:19:36 2001 + */ + +#ifndef WIN32 +#include <sys/uio.h> +#include <pwd.h> +#include <dirent.h> +#endif +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <sys/stat.h> +#ifdef OPENSSL +#include <openssl/evp.h> +#endif +#include "nr_common.h" +#include "r_common.h" +#include "registry.h" +#include "util.h" +#include "r_log.h" + +int nr_util_default_log_facility=LOG_COMMON; + +int nr_get_filename(base,name,namep) + char *base; + char *name; + char **namep; + { + int len=strlen(base)+strlen(name)+2; + char *ret=0; + int _status; + + if(!(ret=(char *)RMALLOC(len))) + ABORT(R_NO_MEMORY); + if(base[strlen(base)-1]!='/'){ + sprintf(ret,"%s/%s",base,name); + } + else{ + sprintf(ret,"%s%s",base,name); + } + *namep=ret; + _status=0; + abort: + return(_status); + } + +#if 0 +int read_RSA_private_key(base,name,keyp) + char *base; + char *name; + RSA **keyp; + { + char *keyfile=0; + BIO *bio=0; + FILE *fp=0; + RSA *rsa=0; + int r,_status; + + /* Load the keyfile */ + if(r=get_filename(base,name,&keyfile)) + ABORT(r); + if(!(fp=fopen(keyfile,"r"))) + ABORT(R_NOT_FOUND); + if(!(bio=BIO_new(BIO_s_file()))) + ABORT(R_NO_MEMORY); + BIO_set_fp(bio,fp,BIO_NOCLOSE); + + if(!(rsa=PEM_read_bio_RSAPrivateKey(bio,0,0,0))) + ABORT(R_NOT_FOUND); + + *keyp=rsa; + _status=0; + abort: + return(_status); + } +#endif + + +void nr_errprintf_log(const char *format,...) + { + va_list ap; + + va_start(ap,format); + + r_vlog(nr_util_default_log_facility,LOG_ERR,format,ap); + + va_end(ap); + } + +void nr_errprintf_log2(void *ignore, const char *format,...) + { + va_list ap; + + va_start(ap,format); + + r_vlog(nr_util_default_log_facility,LOG_ERR,format,ap); + + va_end(ap); + } + + +int nr_fwrite_all(FILE *fp,UCHAR *buf,int len) + { + int r,_status; + + while(len){ + r=fwrite(buf,1,len,fp); + if(r==0) + ABORT(R_IO_ERROR); + + len-=r; + buf+=r; + } + + _status=0; + abort: + return(_status); + } + +int nr_read_data(fd,buf,len) + int fd; + char *buf; + int len; + { + int r,_status; + + while(len){ + r=NR_SOCKET_READ(fd,buf,len); + if(r<=0) + ABORT(R_EOD); + + buf+=r; + len-=r; + } + + + _status=0; + abort: + return(_status); + } + +#ifdef WIN32 + // TODO +#else +int nr_drop_privileges(char *username) + { + int _status; + + /* Drop privileges */ + if ((getuid() == 0) || geteuid()==0) { + struct passwd *passwd; + + if ((passwd = getpwnam(CAPTURE_USER)) == 0){ + r_log(LOG_GENERIC,LOG_EMERG,"Couldn't get user %s",CAPTURE_USER); + ABORT(R_INTERNAL); + } + + if(setuid(passwd->pw_uid)!=0){ + r_log(LOG_GENERIC,LOG_EMERG,"Couldn't drop privileges"); + ABORT(R_INTERNAL); + } + } + + _status=0; + abort: + return(_status); + } +#endif + +int nr_bin2hex(UCHAR *in,int len,UCHAR *out) + { + while(len){ + sprintf((char*)out,"%.2x",in[0] & 0xff); + + in+=1; + out+=2; + + len--; + } + + return(0); + } + +int nr_hex_ascii_dump(Data *data) + { + UCHAR *ptr=data->data; + int len=data->len; + + while(len){ + int i; + int bytes=MIN(len,16); + + for(i=0;i<bytes;i++) + printf("%.2x ",ptr[i]&255); + /* Fill */ + for(i=0;i<(16-bytes);i++) + printf(" "); + printf(" "); + + for(i=0;i<bytes;i++){ + if(isprint(ptr[i])) + printf("%c",ptr[i]); + else + printf("."); + } + printf("\n"); + + len-=bytes; + ptr+=bytes; + } + return(0); + } + +#ifdef OPENSSL +int nr_sha1_file(char *filename,UCHAR *out) + { + EVP_MD_CTX md_ctx; + FILE *fp=0; + int r,_status; + UCHAR buf[1024]; + int out_len; + + EVP_MD_CTX_init(&md_ctx); + + if(!(fp=fopen(filename,"r"))){ + r_log(LOG_COMMON,LOG_ERR,"Couldn't open file %s",filename); + ABORT(R_NOT_FOUND); + } + + EVP_DigestInit_ex(&md_ctx,EVP_sha1(),0); + + while(1){ + r=fread(buf,1,sizeof(buf),fp); + + if(r<0){ + r_log(LOG_COMMON,LOG_ERR,"Error reading from %s",filename); + ABORT(R_INTERNAL); + } + + if(!r) + break; + + EVP_DigestUpdate(&md_ctx,buf,r); + } + + EVP_DigestFinal(&md_ctx,out,(unsigned int*)&out_len); + if(out_len!=20) + ABORT(R_INTERNAL); + + _status=0; + abort: + EVP_MD_CTX_cleanup(&md_ctx); + if(fp) fclose(fp); + + return(_status); + } + +#endif + +#ifdef WIN32 + // TODO +#else + +#if 0 + +#include <fts.h> + +int nr_rm_tree(char *path) + { + FTS *fts=0; + FTSENT *p; + int failed=0; + int _status; + char *argv[2]; + + argv[0]=path; + argv[1]=0; + + if(!(fts=fts_open(argv,0,NULL))){ + r_log_e(LOG_COMMON,LOG_ERR,"Couldn't open directory %s",path); + ABORT(R_FAILED); + } + + while(p=fts_read(fts)){ + switch(p->fts_info){ + case FTS_D: + break; + case FTS_DOT: + break; + case FTS_ERR: + r_log_e(LOG_COMMON,LOG_ERR,"Problem reading %s",p->fts_path); + break; + default: + r_log(LOG_COMMON,LOG_DEBUG,"Removing %s",p->fts_path); + errno=0; + if(remove(p->fts_path)){ + r_log_e(LOG_COMMON,LOG_ERR,"Problem removing %s",p->fts_path); + failed=1; + } + } + } + + if(failed) + ABORT(R_FAILED); + + _status=0; + abort: + if(fts) fts_close(fts); + return(_status); + } +#endif + +int nr_write_pid_file(char *pid_filename) + { + FILE *fp; + int _status; + + if(!pid_filename) + ABORT(R_BAD_ARGS); + + unlink(pid_filename); + + if(!(fp=fopen(pid_filename,"w"))){ + r_log(LOG_GENERIC,LOG_CRIT,"Couldn't open PID file: %s",strerror(errno)); + ABORT(R_NOT_FOUND); + } + + fprintf(fp,"%d\n",getpid()); + + fclose(fp); + + chmod(pid_filename,S_IRUSR | S_IRGRP | S_IROTH); + + _status=0; + abort: + return(_status); + } +#endif + +int nr_reg_uint4_fetch_and_check(NR_registry key, UINT4 min, UINT4 max, int log_fac, int die, UINT4 *val) + { + int r,_status; + UINT4 my_val; + + if(r=NR_reg_get_uint4(key,&my_val)){ + r_log(log_fac,LOG_ERR,"Couldn't get key '%s', error %d",key,r); + ABORT(r); + } + + if((min>0) && (my_val<min)){ + r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%lu, (min = %lu)",key,(unsigned long)my_val,(unsigned long)min); + ABORT(R_BAD_DATA); + } + + if(my_val>max){ + r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%lu, (max = %lu)",key,(unsigned long)my_val,(unsigned long)max); + ABORT(R_BAD_DATA); + } + + *val=my_val; + _status=0; + + abort: + if(die && _status){ + r_log(log_fac,LOG_CRIT,"Exiting due to invalid configuration (key '%s')",key); + exit(1); + } + return(_status); + } + +int nr_reg_uint8_fetch_and_check(NR_registry key, UINT8 min, UINT8 max, int log_fac, int die, UINT8 *val) + { + int r,_status; + UINT8 my_val; + + if(r=NR_reg_get_uint8(key,&my_val)){ + r_log(log_fac,LOG_ERR,"Couldn't get key '%s', error %d",key,r); + ABORT(r); + } + + if(my_val<min){ + r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%llu, (min = %llu)",key,my_val,min); + ABORT(R_BAD_DATA); + } + + if(my_val>max){ + r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%llu, (max = %llu)",key,my_val,max); + ABORT(R_BAD_DATA); + } + + *val=my_val; + _status=0; + + abort: + if(die && _status){ + r_log(log_fac,LOG_CRIT,"Exiting due to invalid configuration (key '%s')",key); + exit(1); + } + return(_status); + } + +#if defined(LINUX) || defined(WIN32) +/*- + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. + */ + + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +#endif /* LINUX or WIN32 */ + +#if defined(USE_OWN_INET_NTOP) || (defined(WIN32) && WINVER < 0x0600) +#include <errno.h> +#ifdef WIN32 +#include <Ws2ipdef.h> +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif +#else +#include <sys/socket.h> +#endif +#define INET6 + +/* inet_ntop implementation from NetBSD */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(NS_INADDRSZ) +# define NS_INADDRSZ 4 +#endif +#if !defined(NS_IN6ADDRSZ) +# define NS_IN6ADDRSZ 16 +#endif +#if !defined(NS_INT16SZ) +# define NS_INT16SZ 2 +#endif + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static const char *inet_ntop4(const unsigned char *src, char *dst, size_t size); +#ifdef INET6 +static const char *inet_ntop6(const unsigned char *src, char *dst, size_t size); +#endif /* INET6 */ + +/* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ +const char * +inet_ntop(int af, const void *src, char *dst, size_t size) +{ + + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, size)); +#ifdef INET6 + case AF_INET6: + return (inet_ntop6(src, dst, size)); +#endif /* INET6 */ + default: + errno = EAFNOSUPPORT; + return (NULL); + } + /* NOTREACHED */ +} + +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address, more or less like inet_ntoa() + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a unsigned char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4(const unsigned char *src, char *dst, size_t size) +{ + char tmp[sizeof "255.255.255.255"]; + int l; + + l = snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u", + src[0], src[1], src[2], src[3]); + if (l <= 0 || (size_t) l >= size) { + errno = ENOSPC; + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); +} + +#ifdef INET6 +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop6(const unsigned char *src, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; + char *tp, *ep; + struct { int base, len; } best, cur; + unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + int advance; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + best.len = -1; /* XXX gcc */ + cur.len = -1; /* XXX gcc */ + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + ep = tmp + sizeof(tmp); + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, (size_t)(ep - tp))) + return (NULL); + tp += strlen(tp); + break; + } + advance = snprintf(tp, (size_t)(ep - tp), "%x", words[i]); + if (advance <= 0 || advance >= ep - tp) + return (NULL); + tp += advance; + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + if (tp + 1 >= ep) + return (NULL); + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); +} +#endif /* INET6 */ + +#ifdef WIN32 +/* Not exactly, will forgive stuff like <addr>:<port> */ +int inet_pton(int af, const char *src, void *dst) +{ + struct sockaddr_storage ss; + int addrlen = sizeof(ss); + + if (af != AF_INET && af != AF_INET6) { + return -1; + } + + if (!WSAStringToAddressA(src, af, NULL, (struct sockaddr*)&ss, &addrlen)) { + if (af == AF_INET) { + struct sockaddr_in *in = (struct sockaddr_in*)&ss; + memcpy(dst, &in->sin_addr, sizeof(struct in_addr)); + } else { + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&ss; + memcpy(dst, &in6->sin6_addr, sizeof(struct in6_addr)); + } + return 1; + } + return 0; +} +#endif /* WIN32 */ + +#endif + +#ifdef WIN32 +#include <time.h> +/* this is only millisecond-accurate, but that should be OK */ + +int gettimeofday(struct timeval *tv, void *tz) + { + SYSTEMTIME st; + FILETIME ft; + ULARGE_INTEGER u; + + GetLocalTime (&st); + + /* strangely, the FILETIME is the number of 100 nanosecond (0.1 us) intervals + * since the Epoch */ + SystemTimeToFileTime(&st, &ft); + u.HighPart = ft.dwHighDateTime; + u.LowPart = ft.dwLowDateTime; + + tv->tv_sec = (long) (u.QuadPart / 10000000L); + tv->tv_usec = (long) (st.wMilliseconds * 1000);; + + return 0; + } +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h new file mode 100644 index 0000000000..975baa4aa2 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h @@ -0,0 +1,73 @@ +/** + util.h + + + Copyright (C) 2001-2003, Network Resonance, Inc. + Copyright (C) 2006, Network Resonance, Inc. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of Network Resonance, Inc. nor the name of any + contributors to this software 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. + + + ekr@rtfm.com Wed Dec 26 17:20:23 2001 + */ + + +#ifndef _util_h +#define _util_h + +#include "registry.h" + +int nr_get_filename(char *base,char *name, char **namep); +#if 0 +#include <openssl/ssl.h> + +int read_RSA_private_key(char *base, char *name,RSA **keyp); +#endif +void nr_errprintf_log(const char *fmt,...); +void nr_errprintf_log2(void *ignore, const char *fmt,...); +extern int nr_util_default_log_facility; + +int nr_read_data(int fd,char *buf,int len); +int nr_drop_privileges(char *username); +int nr_hex_ascii_dump(Data *data); +int nr_fwrite_all(FILE *fp,UCHAR *buf,int len); +int nr_sha1_file(char *filename,UCHAR *out); +int nr_bin2hex(UCHAR *in,int len,UCHAR *out); +int nr_rm_tree(char *path); +int nr_write_pid_file(char *pid_filename); + +int nr_reg_uint4_fetch_and_check(NR_registry key, UINT4 min, UINT4 max, int log_fac, int die, UINT4 *val); +int nr_reg_uint8_fetch_and_check(NR_registry key, UINT8 min, UINT8 max, int log_fac, int die, UINT8 *val); + +#if defined(WIN32) && WINVER < 0x0600 +const char *inet_ntop(int af, const void *src, char *dst, size_t size); +int inet_pton(int af, const char *src, void *dst); +#endif + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nrappkit/upstream.diff b/dom/media/webrtc/transport/third_party/nrappkit/upstream.diff new file mode 100644 index 0000000000..50b88961dc --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nrappkit/upstream.diff @@ -0,0 +1,391 @@ +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h +@@ -38,21 +38,21 @@ + + + #ifndef _async_wait_h + #define _async_wait_h + + #ifdef HAVE_SYS_TIME_H + #include <sys/time.h> + #endif + #include <csi_platform.h> + +-typedef void (*NR_async_cb)(int resource,int how,void *arg); ++typedef void (*NR_async_cb)(NR_SOCKET resource,int how,void *arg); + + #define NR_ASYNC_WAIT_READ 0 + #define NR_ASYNC_WAIT_WRITE 1 + + + int NR_async_wait_init(void); + int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb,void *cb_arg, + char *function,int line); + int NR_async_cancel(NR_SOCKET sock,int how); + int NR_async_schedule(NR_async_cb cb,void *arg,char *function,int line); +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c +@@ -33,28 +33,41 @@ + POSSIBILITY OF SUCH DAMAGE. + + + ekr@rtfm.com Mon Dec 3 15:24:38 2001 + */ + + + static char *RCSSTRING __UNUSED__ ="$Id: r_log.c,v 1.10 2008/11/25 22:25:18 adamcain Exp $"; + + ++#ifdef LINUX ++#define _BSD_SOURCE ++#endif ++ + #include "r_log.h" + #include "hex.h" ++ + #include <string.h> ++#include <errno.h> ++#ifndef _MSC_VER ++#include <strings.h> ++#include <syslog.h> ++#endif + #include <registry.h> + #include <time.h> ++ ++ + #include "nr_common.h" + #include "nr_reg_keys.h" + ++ + #define LOGGING_DEFAULT_LEVEL 5 + + int NR_LOG_LOGGING = 0; + + static char *log_level_strings[]={ + "EMERG", + "ALERT", + "CRIT", + "ERR", + "WARNING", +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h +@@ -37,17 +37,21 @@ + */ + + + #ifndef _platform_h + #define _platform_h + + #include <unistd.h> + + #define STDIO_BYTES_BUFFERED(fp) (fp->_r) + ++#ifdef NR_SOCKET_IS_VOID_PTR ++typedef void* NR_SOCKET; ++#else + typedef int NR_SOCKET; + #define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count)) + #define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count)) + #define NR_SOCKET_CLOSE(sock) close(sock) ++#endif + + #endif + +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h +@@ -35,17 +35,21 @@ + + ekr@rtfm.com Mon Dec 13 17:26:51 2004 + */ + + + #ifndef _platform_h + #define _platform_h + + #include <linux_funcs.h> + ++#ifdef NR_SOCKET_IS_VOID_PTR ++typedef void* NR_SOCKET; ++#else + typedef int NR_SOCKET; + #define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count)) + #define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count)) + #define NR_SOCKET_CLOSE(sock) close(sock) ++#endif + + #endif + +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h +@@ -44,45 +44,51 @@ + #define _WIN32_WINNT 0x0400 // This prevents weird "'TryEnterCriticalSection': identifier not found" + // compiler errors when poco/win32_mutex.h is included + #endif + + #define UINT8 UBLAH_IGNORE_ME_PLEASE + #define INT8 BLAH_IGNORE_ME_PLEASE + #include <winsock2.h> + #undef UINT8 + #undef INT8 + #include <r_types.h> ++#include <errno.h> + + #define strcasecmp _stricmp + #define strncasecmp _strnicmp +-#define snprintf _snprintf + + #define strcasestr stristr + + /* Hack version of strlcpy (in util/util.c) */ + size_t strlcat(char *dst, const char *src, size_t siz); + + /* Hack version of getopt() (in util/getopt.c) */ + int getopt(int argc, char *argv[], char *opstring); + extern char *optarg; + extern int optind; + extern int opterr; + + /* Hack version of gettimeofday() (in util/util.c) */ + int gettimeofday(struct timeval *tv, void *tz); + ++#ifdef NR_SOCKET_IS_VOID_PTR ++typedef void* NR_SOCKET; ++#else + typedef SOCKET NR_SOCKET; + #define NR_SOCKET_READ(sock,buf,count) recv((sock),(buf),(count),0) + #define NR_SOCKET_WRITE(sock,buf,count) send((sock),(buf),(count),0) + #define NR_SOCKET_CLOSE(sock) closesocket(sock) ++#endif + ++#ifndef EHOSTUNREACH + #define EHOSTUNREACH WSAEHOSTUNREACH ++#endif + + #define LOG_EMERG 0 + #define LOG_ALERT 1 + #define LOG_CRIT 2 + #define LOG_ERR 3 + #define LOG_WARNING 4 + #define LOG_NOTICE 5 + #define LOG_INFO 6 + #define LOG_DEBUG 7 + +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c +@@ -37,21 +37,22 @@ + * 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 <assert.h> + #include <string.h> +-#ifndef WIN32 ++#ifndef _MSC_VER ++#include <strings.h> + #include <sys/param.h> + #include <netinet/in.h> + #endif + #ifdef OPENSSL + #include <openssl/ssl.h> + #endif + #include <ctype.h> + #include "registry.h" + #include "registry_int.h" + #include "registry_vtbl.h" +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c +@@ -58,20 +58,21 @@ + * the crc computation is finished. The crc should be complemented + * before transmission. + * The variable corresponding to the macro argument "crc" should + * be an unsigned long and should be preset to all ones for Ethernet + * use. An error-free packet will leave 0xDEBB20E3 in the crc. + * Spencer Garrett <srg@quick.com> + */ + + + #include <sys/types.h> ++#include <r_types.h> + + static char *RCSSTRING __UNUSED__ ="$Id: r_crc32.c,v 1.4 2008/11/26 03:22:02 adamcain Exp $"; + + #ifdef WIN32 + #define u_int32_t UINT4 + #endif + + #define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) + + /* generated using the AUTODIN II polynomial +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h +@@ -118,20 +118,20 @@ + #ifndef MIN + #define MIN(a,b) ((a)>(b))?(b):(a) + #endif + + #ifndef MAX + #define MAX(a,b) ((b)>(a))?(b):(a) + #endif + + #ifdef DEBUG + #define DBG(a) debug a +-int debug(int class,char *format,...); ++int debug(int cls, char *format,...); + #else + #define DBG(a) + #endif + + #define UNIMPLEMENTED do { fprintf(stderr,"%s:%d Function %s unimplemented\n",__FILE__,__LINE__,__FUNCTION__); abort(); } while(0) + + #include "r_memory.h" + + #endif +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h +@@ -77,35 +77,25 @@ + $Id: r_time.h,v 1.4 2007/06/26 22:37:57 adamcain Exp $ + + + ekr@rtfm.com Thu Mar 4 08:45:41 1999 + */ + + + #ifndef _r_time_h + #define _r_time_h + ++#include <csi_platform.h> + +-#ifdef WIN32 +-# include <time.h> +-#else +-/* Cribbed from the autoconf doc */ +-# if TIME_WITH_SYS_TIME +-# include <sys/time.h> +-# include <time.h> +-# else +-# if HAVE_SYS_TIME_H +-# include <sys/time.h> +-# else +-# include <time.h> +-# endif +-# endif ++#ifndef WIN32 ++#include <sys/time.h> ++#include <time.h> + #endif + + int r_timeval_diff(struct timeval *t1,struct timeval *t0, struct timeval *diff); + int r_timeval_add(struct timeval *t1,struct timeval *t2, struct timeval *sum); + int r_timeval_cmp(struct timeval *t1,struct timeval *t2); + + UINT8 r_timeval2int(struct timeval *tv); + int r_int2timeval(UINT8 t,struct timeval *tv); + UINT8 r_gettimeint(void); + +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c +@@ -292,20 +292,22 @@ int nr_sha1_file(char *filename,UCHAR *o + + return(_status); + } + + #endif + + #ifdef WIN32 + // TODO + #else + ++#if 0 ++ + #include <fts.h> + + int nr_rm_tree(char *path) + { + FTS *fts=0; + FTSENT *p; + int failed=0; + int _status; + char *argv[2]; + +@@ -337,20 +339,21 @@ int nr_rm_tree(char *path) + } + + if(failed) + ABORT(R_FAILED); + + _status=0; + abort: + if(fts) fts_close(fts); + return(_status); + } ++#endif + + int nr_write_pid_file(char *pid_filename) + { + FILE *fp; + int _status; + + if(!pid_filename) + ABORT(R_BAD_ARGS); + + unlink(pid_filename); +@@ -739,12 +742,24 @@ int gettimeofday(struct timeval *tv, voi + * since the Epoch */ + SystemTimeToFileTime(&st, &ft); + u.HighPart = ft.dwHighDateTime; + u.LowPart = ft.dwLowDateTime; + + tv->tv_sec = (long) (u.QuadPart / 10000000L); + tv->tv_usec = (long) (st.wMilliseconds * 1000);; + + return 0; + } ++ ++int snprintf(char *buffer, size_t n, const char *format, ...) ++{ ++ va_list argp; ++ int ret; ++ va_start(argp, format); ++ ret = _vscprintf(format, argp); ++ vsnprintf_s(buffer, n, _TRUNCATE, format, argp); ++ va_end(argp); ++ return ret; ++} ++ + #endif + +diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h +--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h ++++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h +@@ -57,12 +57,17 @@ int nr_drop_privileges(char *username); + int nr_hex_ascii_dump(Data *data); + int nr_fwrite_all(FILE *fp,UCHAR *buf,int len); + int nr_sha1_file(char *filename,UCHAR *out); + int nr_bin2hex(UCHAR *in,int len,UCHAR *out); + int nr_rm_tree(char *path); + int nr_write_pid_file(char *pid_filename); + + int nr_reg_uint4_fetch_and_check(NR_registry key, UINT4 min, UINT4 max, int log_fac, int die, UINT4 *val); + int nr_reg_uint8_fetch_and_check(NR_registry key, UINT8 min, UINT8 max, int log_fac, int die, UINT8 *val); + ++#ifdef WIN32 ++int snprintf(char *buffer, size_t n, const char *format, ...); ++const char *inet_ntop(int af, const void *src, char *dst, size_t size); + #endif + ++#endif ++ diff --git a/dom/media/webrtc/transport/transportflow.cpp b/dom/media/webrtc/transport/transportflow.cpp new file mode 100644 index 0000000000..8b263c8cfe --- /dev/null +++ b/dom/media/webrtc/transport/transportflow.cpp @@ -0,0 +1,74 @@ +/* -*- 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 <deque> + +#include "transportflow.h" +#include "transportlayer.h" + +namespace mozilla { + +NS_IMPL_ISUPPORTS0(TransportFlow) + +// There are some hacks here to allow destruction off of +// the main thread. +TransportFlow::~TransportFlow() { + // Push the destruction onto the STS thread. Note that there + // is still some possibility that someone is accessing this + // object simultaneously, but as long as smart pointer discipline + // is maintained, it shouldn't be possible to access and + // destroy it simultaneously. The conversion to a UniquePtr + // ensures automatic destruction of the queue at exit of + // DestroyFinal. + CheckThread(); + ClearLayers(layers_.get()); +} + +void TransportFlow::DestroyFinal( + UniquePtr<std::deque<TransportLayer*>> layers) { + ClearLayers(layers.get()); +} + +void TransportFlow::ClearLayers(std::deque<TransportLayer*>* layers) { + while (!layers->empty()) { + delete layers->front(); + layers->pop_front(); + } +} + +void TransportFlow::PushLayer(TransportLayer* layer) { + CheckThread(); + layers_->push_front(layer); + EnsureSameThread(layer); + layer->SetFlowId(id_); +} + +TransportLayer* TransportFlow::GetLayer(const std::string& id) const { + CheckThread(); + + if (layers_) { + for (TransportLayer* layer : *layers_) { + if (layer->id() == id) return layer; + } + } + + return nullptr; +} + +void TransportFlow::EnsureSameThread(TransportLayer* layer) { + // Enforce that if any of the layers have a thread binding, + // they all have the same binding. + if (target_) { + const nsCOMPtr<nsIEventTarget>& lthread = layer->GetThread(); + + if (lthread && (lthread != target_)) MOZ_CRASH(); + } else { + target_ = layer->GetThread(); + } +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/transportflow.h b/dom/media/webrtc/transport/transportflow.h new file mode 100644 index 0000000000..43253a260b --- /dev/null +++ b/dom/media/webrtc/transport/transportflow.h @@ -0,0 +1,105 @@ +/* -*- 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 + +#ifndef transportflow_h__ +#define transportflow_h__ + +#include <deque> +#include <string> + +#include "nscore.h" +#include "nsISupportsImpl.h" +#include "mozilla/UniquePtr.h" +#include "transportlayer.h" +#include "m_cpp_utils.h" + +// A stack of transport layers acts as a flow. +// Generally, one reads and writes to the top layer. + +// This code has a confusing hybrid threading model which +// probably needs some eventual refactoring. +// TODO(ekr@rtfm.com): Bug 844891 +// +// TransportFlows are not inherently bound to a thread *but* +// TransportLayers can be. If any layer in a flow is bound +// to a given thread, then all layers in the flow MUST be +// bound to that thread and you can only manipulate the +// flow (push layers, write, etc.) on that thread. +// +// The sole official exception to this is that you are +// allowed to *destroy* a flow off the bound thread provided +// that there are no listeners on its signals. This exception +// is designed to allow idioms where you create the flow +// and then something goes wrong and you destroy it and +// you don't want to bother with a thread dispatch. +// +// Eventually we hope to relax the "no listeners" +// restriction by thread-locking the signals, but previous +// attempts have caused deadlocks. +// +// Most of these invariants are enforced by hard asserts +// (i.e., those which fire even in production builds). + +namespace mozilla { + +class TransportFlow final : public nsISupports { + public: + TransportFlow() + : id_("(anonymous)"), layers_(new std::deque<TransportLayer*>) {} + explicit TransportFlow(const std::string id) + : id_(id), layers_(new std::deque<TransportLayer*>) {} + + const std::string& id() const { return id_; } + + // Layer management. Note PushLayer() is not thread protected, so + // either: + // (a) Do it in the thread handling the I/O + // (b) Do it before you activate the I/O system + // + // The flow takes ownership of the layers after a successful + // push. + void PushLayer(TransportLayer* layer); + + TransportLayer* GetLayer(const std::string& id) const; + + NS_DECL_THREADSAFE_ISUPPORTS + + private: + ~TransportFlow(); + + DISALLOW_COPY_ASSIGN(TransportFlow); + + // Check if we are on the right thread + void CheckThread() const { + if (!CheckThreadInt()) MOZ_CRASH(); + } + + bool CheckThreadInt() const { + bool on; + + if (!target_) // OK if no thread set. + return true; + if (NS_FAILED(target_->IsOnCurrentThread(&on))) return false; + + return on; + } + + void EnsureSameThread(TransportLayer* layer); + + static void DestroyFinal(UniquePtr<std::deque<TransportLayer*>> layers); + + // Overload needed because we use deque internally and queue externally. + static void ClearLayers(std::deque<TransportLayer*>* layers); + + std::string id_; + UniquePtr<std::deque<TransportLayer*>> layers_; + nsCOMPtr<nsIEventTarget> target_; +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/transportlayer.cpp b/dom/media/webrtc/transport/transportlayer.cpp new file mode 100644 index 0000000000..8816f24754 --- /dev/null +++ b/dom/media/webrtc/transport/transportlayer.cpp @@ -0,0 +1,49 @@ +/* -*- 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 "logging.h" +#include "transportlayer.h" + +// Logging context +namespace mozilla { + +MOZ_MTLOG_MODULE("mtransport") + +nsresult TransportLayer::Init() { + if (state_ != TS_NONE) return state_ == TS_ERROR ? NS_ERROR_FAILURE : NS_OK; + + nsresult rv = InitInternal(); + + if (!NS_SUCCEEDED(rv)) { + state_ = TS_ERROR; + return rv; + } + state_ = TS_INIT; + + return NS_OK; +} + +void TransportLayer::Chain(TransportLayer* downward) { + downward_ = downward; + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Inserted: downward='" + << (downward ? downward->id() : "none") + << "'"); + + WasInserted(); +} + +void TransportLayer::SetState(State state, const char* file, unsigned line) { + if (state != state_) { + MOZ_MTLOG(state == TS_ERROR ? ML_ERROR : ML_DEBUG, + file << ":" << line << ": " << LAYER_INFO << "state " << state_ + << "->" << state); + state_ = state; + SignalStateChange(this, state); + } +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/transportlayer.h b/dom/media/webrtc/transport/transportlayer.h new file mode 100644 index 0000000000..47287ab4e0 --- /dev/null +++ b/dom/media/webrtc/transport/transportlayer.h @@ -0,0 +1,108 @@ +/* -*- 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 + +#ifndef transportlayer_h__ +#define transportlayer_h__ + +#include "sigslot.h" + +#include "nsCOMPtr.h" +#include "nsIEventTarget.h" + +#include "m_cpp_utils.h" +#include "mediapacket.h" + +namespace mozilla { + +class TransportFlow; + +typedef int TransportResult; + +enum { TE_WOULDBLOCK = -1, TE_ERROR = -2, TE_INTERNAL = -3 }; + +#define TRANSPORT_LAYER_ID(name) \ + const std::string id() const override { return name; } \ + static std::string ID() { return name; } + +// Abstract base class for network transport layers. +class TransportLayer : public sigslot::has_slots<> { + public: + // The state of the transport flow + // We can't use "ERROR" because Windows has a macro named "ERROR" + enum State { TS_NONE, TS_INIT, TS_CONNECTING, TS_OPEN, TS_CLOSED, TS_ERROR }; + + // Is this a stream or datagram flow + TransportLayer() : state_(TS_NONE), flow_id_(), downward_(nullptr) {} + + virtual ~TransportLayer() = default; + + // Called to initialize + nsresult Init(); // Called by Insert() to set up -- do not override + virtual nsresult InitInternal() { return NS_OK; } // Called by Init + + void SetFlowId(const std::string& flow_id) { flow_id_ = flow_id; } + + virtual void Chain(TransportLayer* downward); + + // Downward interface + TransportLayer* downward() { return downward_; } + + // Get the state + State state() const { return state_; } + // Must be implemented by derived classes + virtual TransportResult SendPacket(MediaPacket& packet) = 0; + + // Get the thread. + const nsCOMPtr<nsIEventTarget> GetThread() const { return target_; } + + // Event definitions that one can register for + // State has changed + sigslot::signal2<TransportLayer*, State> SignalStateChange; + // Data received on the flow + sigslot::signal2<TransportLayer*, MediaPacket&> SignalPacketReceived; + + // Return the layer id for this layer + virtual const std::string id() const = 0; + + // The id of the flow + const std::string& flow_id() const { return flow_id_; } + + protected: + virtual void WasInserted() {} + virtual void SetState(State state, const char* file, unsigned line); + // Check if we are on the right thread + void CheckThread() const { MOZ_ASSERT(CheckThreadInt(), "Wrong thread"); } + + State state_; + std::string flow_id_; + TransportLayer* downward_; // The next layer in the stack + nsCOMPtr<nsIEventTarget> target_; + + private: + DISALLOW_COPY_ASSIGN(TransportLayer); + + bool CheckThreadInt() const { + bool on; + + if (!target_) // OK if no thread set. + return true; + + NS_ENSURE_SUCCESS(target_->IsOnCurrentThread(&on), false); + NS_ENSURE_TRUE(on, false); + + return true; + } +}; + +#define LAYER_INFO \ + "Flow[" << flow_id() << "(none)" \ + << "]; Layer[" << id() << "]: " +#define TL_SET_STATE(x) SetState((x), __FILE__, __LINE__) + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/transportlayerdtls.cpp b/dom/media/webrtc/transport/transportlayerdtls.cpp new file mode 100644 index 0000000000..4ab8aaa029 --- /dev/null +++ b/dom/media/webrtc/transport/transportlayerdtls.cpp @@ -0,0 +1,1558 @@ +/* -*- 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 "transportlayerdtls.h" + +#include <algorithm> +#include <queue> +#include <sstream> + +#include "dtlsidentity.h" +#include "keyhi.h" +#include "logging.h" +#include "mozilla/Telemetry.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "nsCOMPtr.h" +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" +#include "sslexp.h" +#include "sslproto.h" + +namespace mozilla { + +MOZ_MTLOG_MODULE("mtransport") + +static PRDescIdentity transport_layer_identity = PR_INVALID_IO_LAYER; + +// TODO: Implement a mode for this where +// the channel is not ready until confirmed externally +// (e.g., after cert check). + +#define UNIMPLEMENTED \ + MOZ_MTLOG(ML_ERROR, "Call to unimplemented function " << __FUNCTION__); \ + MOZ_ASSERT(false); \ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0) + +#define MAX_ALPN_LENGTH 255 + +// We need to adapt the NSPR/libssl model to the TransportFlow model. +// The former wants pull semantics and TransportFlow wants push. +// +// - A TransportLayerDtls assumes it is sitting on top of another +// TransportLayer, which means that events come in asynchronously. +// - NSS (libssl) wants to sit on top of a PRFileDesc and poll. +// - The TransportLayerNSPRAdapter is a PRFileDesc containing a +// FIFO. +// - When TransportLayerDtls.PacketReceived() is called, we insert +// the packets in the FIFO and then do a PR_Recv() on the NSS +// PRFileDesc, which eventually reads off the FIFO. +// +// All of this stuff is assumed to happen solely in a single thread +// (generally the SocketTransportService thread) + +void TransportLayerNSPRAdapter::PacketReceived(MediaPacket& packet) { + if (enabled_) { + input_.push(new MediaPacket(std::move(packet))); + } +} + +int32_t TransportLayerNSPRAdapter::Recv(void* buf, int32_t buflen) { + if (input_.empty()) { + PR_SetError(PR_WOULD_BLOCK_ERROR, 0); + return -1; + } + + MediaPacket* front = input_.front(); + int32_t count = static_cast<int32_t>(front->len()); + + if (buflen < count) { + MOZ_ASSERT(false, "Not enough buffer space to receive into"); + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); + return -1; + } + + memcpy(buf, front->data(), count); + + input_.pop(); + delete front; + + return count; +} + +int32_t TransportLayerNSPRAdapter::Write(const void* buf, int32_t length) { + if (!enabled_) { + MOZ_MTLOG(ML_WARNING, "Writing to disabled transport layer"); + return -1; + } + + MediaPacket packet; + // Copies. Oh well. + packet.Copy(static_cast<const uint8_t*>(buf), static_cast<size_t>(length)); + packet.SetType(MediaPacket::DTLS); + + TransportResult r = output_->SendPacket(packet); + if (r >= 0) { + return r; + } + + if (r == TE_WOULDBLOCK) { + PR_SetError(PR_WOULD_BLOCK_ERROR, 0); + } else { + PR_SetError(PR_IO_ERROR, 0); + } + + return -1; +} + +// Implementation of NSPR methods +static PRStatus TransportLayerClose(PRFileDesc* f) { + f->dtor(f); + return PR_SUCCESS; +} + +static int32_t TransportLayerRead(PRFileDesc* f, void* buf, int32_t length) { + UNIMPLEMENTED; + return -1; +} + +static int32_t TransportLayerWrite(PRFileDesc* f, const void* buf, + int32_t length) { + TransportLayerNSPRAdapter* io = + reinterpret_cast<TransportLayerNSPRAdapter*>(f->secret); + return io->Write(buf, length); +} + +static int32_t TransportLayerAvailable(PRFileDesc* f) { + UNIMPLEMENTED; + return -1; +} + +int64_t TransportLayerAvailable64(PRFileDesc* f) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus TransportLayerSync(PRFileDesc* f) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static int32_t TransportLayerSeek(PRFileDesc* f, int32_t offset, + PRSeekWhence how) { + UNIMPLEMENTED; + return -1; +} + +static int64_t TransportLayerSeek64(PRFileDesc* f, int64_t offset, + PRSeekWhence how) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus TransportLayerFileInfo(PRFileDesc* f, PRFileInfo* info) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRStatus TransportLayerFileInfo64(PRFileDesc* f, PRFileInfo64* info) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static int32_t TransportLayerWritev(PRFileDesc* f, const PRIOVec* iov, + int32_t iov_size, PRIntervalTime to) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus TransportLayerConnect(PRFileDesc* f, const PRNetAddr* addr, + PRIntervalTime to) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRFileDesc* TransportLayerAccept(PRFileDesc* sd, PRNetAddr* addr, + PRIntervalTime to) { + UNIMPLEMENTED; + return nullptr; +} + +static PRStatus TransportLayerBind(PRFileDesc* f, const PRNetAddr* addr) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRStatus TransportLayerListen(PRFileDesc* f, int32_t depth) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRStatus TransportLayerShutdown(PRFileDesc* f, int32_t how) { + // This is only called from NSS when we are the server and the client refuses + // to provide a certificate. In this case, the handshake is destined for + // failure, so we will just let this pass. + TransportLayerNSPRAdapter* io = + reinterpret_cast<TransportLayerNSPRAdapter*>(f->secret); + io->SetEnabled(false); + return PR_SUCCESS; +} + +// This function does not support peek, or waiting until `to` +static int32_t TransportLayerRecv(PRFileDesc* f, void* buf, int32_t buflen, + int32_t flags, PRIntervalTime to) { + MOZ_ASSERT(flags == 0); + if (flags != 0) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return -1; + } + + TransportLayerNSPRAdapter* io = + reinterpret_cast<TransportLayerNSPRAdapter*>(f->secret); + return io->Recv(buf, buflen); +} + +// Note: this is always nonblocking and assumes a zero timeout. +static int32_t TransportLayerSend(PRFileDesc* f, const void* buf, + int32_t amount, int32_t flags, + PRIntervalTime to) { + int32_t written = TransportLayerWrite(f, buf, amount); + return written; +} + +static int32_t TransportLayerRecvfrom(PRFileDesc* f, void* buf, int32_t amount, + int32_t flags, PRNetAddr* addr, + PRIntervalTime to) { + UNIMPLEMENTED; + return -1; +} + +static int32_t TransportLayerSendto(PRFileDesc* f, const void* buf, + int32_t amount, int32_t flags, + const PRNetAddr* addr, PRIntervalTime to) { + UNIMPLEMENTED; + return -1; +} + +static int16_t TransportLayerPoll(PRFileDesc* f, int16_t in_flags, + int16_t* out_flags) { + UNIMPLEMENTED; + return -1; +} + +static int32_t TransportLayerAcceptRead(PRFileDesc* sd, PRFileDesc** nd, + PRNetAddr** raddr, void* buf, + int32_t amount, PRIntervalTime t) { + UNIMPLEMENTED; + return -1; +} + +static int32_t TransportLayerTransmitFile(PRFileDesc* sd, PRFileDesc* f, + const void* headers, int32_t hlen, + PRTransmitFileFlags flags, + PRIntervalTime t) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus TransportLayerGetpeername(PRFileDesc* f, PRNetAddr* addr) { + // TODO: Modify to return unique names for each channel + // somehow, as opposed to always the same static address. The current + // implementation messes up the session cache, which is why it's off + // elsewhere + addr->inet.family = PR_AF_INET; + addr->inet.port = 0; + addr->inet.ip = 0; + + return PR_SUCCESS; +} + +static PRStatus TransportLayerGetsockname(PRFileDesc* f, PRNetAddr* addr) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static PRStatus TransportLayerGetsockoption(PRFileDesc* f, + PRSocketOptionData* opt) { + switch (opt->option) { + case PR_SockOpt_Nonblocking: + opt->value.non_blocking = PR_TRUE; + return PR_SUCCESS; + default: + UNIMPLEMENTED; + break; + } + + return PR_FAILURE; +} + +// Imitate setting socket options. These are mostly noops. +static PRStatus TransportLayerSetsockoption(PRFileDesc* f, + const PRSocketOptionData* opt) { + switch (opt->option) { + case PR_SockOpt_Nonblocking: + return PR_SUCCESS; + case PR_SockOpt_NoDelay: + return PR_SUCCESS; + default: + UNIMPLEMENTED; + break; + } + + return PR_FAILURE; +} + +static int32_t TransportLayerSendfile(PRFileDesc* out, PRSendFileData* in, + PRTransmitFileFlags flags, + PRIntervalTime to) { + UNIMPLEMENTED; + return -1; +} + +static PRStatus TransportLayerConnectContinue(PRFileDesc* f, int16_t flags) { + UNIMPLEMENTED; + return PR_FAILURE; +} + +static int32_t TransportLayerReserved(PRFileDesc* f) { + UNIMPLEMENTED; + return -1; +} + +static const struct PRIOMethods TransportLayerMethods = { + PR_DESC_LAYERED, + TransportLayerClose, + TransportLayerRead, + TransportLayerWrite, + TransportLayerAvailable, + TransportLayerAvailable64, + TransportLayerSync, + TransportLayerSeek, + TransportLayerSeek64, + TransportLayerFileInfo, + TransportLayerFileInfo64, + TransportLayerWritev, + TransportLayerConnect, + TransportLayerAccept, + TransportLayerBind, + TransportLayerListen, + TransportLayerShutdown, + TransportLayerRecv, + TransportLayerSend, + TransportLayerRecvfrom, + TransportLayerSendto, + TransportLayerPoll, + TransportLayerAcceptRead, + TransportLayerTransmitFile, + TransportLayerGetsockname, + TransportLayerGetpeername, + TransportLayerReserved, + TransportLayerReserved, + TransportLayerGetsockoption, + TransportLayerSetsockoption, + TransportLayerSendfile, + TransportLayerConnectContinue, + TransportLayerReserved, + TransportLayerReserved, + TransportLayerReserved, + TransportLayerReserved}; + +TransportLayerDtls::~TransportLayerDtls() { + // Destroy the NSS instance first so it can still send out an alert before + // we disable the nspr_io_adapter_. + ssl_fd_ = nullptr; + nspr_io_adapter_->SetEnabled(false); + if (timer_) { + timer_->Cancel(); + } +} + +nsresult TransportLayerDtls::InitInternal() { + // Get the transport service as an event target + nsresult rv; + target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + + if (NS_FAILED(rv)) { + MOZ_MTLOG(ML_ERROR, "Couldn't get socket transport service"); + return rv; + } + + timer_ = NS_NewTimer(); + if (!timer_) { + MOZ_MTLOG(ML_ERROR, "Couldn't get timer"); + return rv; + } + + return NS_OK; +} + +void TransportLayerDtls::WasInserted() { + // Connect to the lower layers + if (!Setup()) { + TL_SET_STATE(TS_ERROR); + } +} + +// Set the permitted and default ALPN identifiers. +// The default is here to allow for peers that don't want to negotiate ALPN +// in that case, the default string will be reported from GetNegotiatedAlpn(). +// Setting the default to the empty string causes the transport layer to fail +// if ALPN is not negotiated. +// Note: we only support Unicode strings here, which are encoded into UTF-8, +// even though ALPN ostensibly allows arbitrary octet sequences. +nsresult TransportLayerDtls::SetAlpn(const std::set<std::string>& alpn_allowed, + const std::string& alpn_default) { + alpn_allowed_ = alpn_allowed; + alpn_default_ = alpn_default; + + return NS_OK; +} + +nsresult TransportLayerDtls::SetVerificationAllowAll() { + // Defensive programming + if (verification_mode_ != VERIFY_UNSET) return NS_ERROR_ALREADY_INITIALIZED; + + verification_mode_ = VERIFY_ALLOW_ALL; + + return NS_OK; +} + +nsresult TransportLayerDtls::SetVerificationDigest(const DtlsDigest& digest) { + // Defensive programming + if (verification_mode_ != VERIFY_UNSET && + verification_mode_ != VERIFY_DIGEST) { + return NS_ERROR_ALREADY_INITIALIZED; + } + + digests_.push_back(digest); + verification_mode_ = VERIFY_DIGEST; + return NS_OK; +} + +void TransportLayerDtls::SetMinMaxVersion(Version min_version, + Version max_version) { + if (min_version < Version::DTLS_1_0 || min_version > Version::DTLS_1_3 || + max_version < Version::DTLS_1_0 || max_version > Version::DTLS_1_3 || + min_version > max_version || max_version < min_version) { + return; + } + minVersion_ = min_version; + maxVersion_ = max_version; +} + +// These are the named groups that we will allow. +static const SSLNamedGroup NamedGroupPreferences[] = { + ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, + ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072}; + +// TODO: make sure this is called from STS. Otherwise +// we have thread safety issues +bool TransportLayerDtls::Setup() { + CheckThread(); + SECStatus rv; + + if (!downward_) { + MOZ_MTLOG(ML_ERROR, "DTLS layer with nothing below. This is useless"); + return false; + } + nspr_io_adapter_ = MakeUnique<TransportLayerNSPRAdapter>(downward_); + + if (!identity_) { + MOZ_MTLOG(ML_ERROR, "Can't start DTLS without an identity"); + return false; + } + + if (verification_mode_ == VERIFY_UNSET) { + MOZ_MTLOG(ML_ERROR, + "Can't start DTLS without specifying a verification mode"); + return false; + } + + if (transport_layer_identity == PR_INVALID_IO_LAYER) { + transport_layer_identity = PR_GetUniqueIdentity("nssstreamadapter"); + } + + UniquePRFileDesc pr_fd( + PR_CreateIOLayerStub(transport_layer_identity, &TransportLayerMethods)); + MOZ_ASSERT(pr_fd != nullptr); + if (!pr_fd) return false; + pr_fd->secret = reinterpret_cast<PRFilePrivate*>(nspr_io_adapter_.get()); + + UniquePRFileDesc ssl_fd(DTLS_ImportFD(nullptr, pr_fd.get())); + MOZ_ASSERT(ssl_fd != nullptr); // This should never happen + if (!ssl_fd) { + return false; + } + + Unused << pr_fd.release(); // ownership transfered to ssl_fd; + + if (role_ == CLIENT) { + MOZ_MTLOG(ML_INFO, "Setting up DTLS as client"); + rv = SSL_GetClientAuthDataHook(ssl_fd.get(), GetClientAuthDataHook, this); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); + return false; + } + + if (maxVersion_ >= Version::DTLS_1_3) { + MOZ_MTLOG(ML_INFO, "Setting DTLS1.3 supported_versions workaround"); + rv = SSL_SetDtls13VersionWorkaround(ssl_fd.get(), PR_TRUE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't set DTLS1.3 workaround"); + return false; + } + } + } else { + MOZ_MTLOG(ML_INFO, "Setting up DTLS as server"); + // Server side + rv = SSL_ConfigSecureServer(ssl_fd.get(), identity_->cert().get(), + identity_->privkey().get(), + identity_->auth_type()); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); + return false; + } + + UniqueCERTCertList zero_certs(CERT_NewCertList()); + rv = SSL_SetTrustAnchors(ssl_fd.get(), zero_certs.get()); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't set trust anchors"); + return false; + } + + // Insist on a certificate from the client + rv = SSL_OptionSet(ssl_fd.get(), SSL_REQUEST_CERTIFICATE, PR_TRUE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't request certificate"); + return false; + } + + rv = SSL_OptionSet(ssl_fd.get(), SSL_REQUIRE_CERTIFICATE, PR_TRUE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't require certificate"); + return false; + } + } + + SSLVersionRange version_range = {static_cast<PRUint16>(minVersion_), + static_cast<PRUint16>(maxVersion_)}; + + rv = SSL_VersionRangeSet(ssl_fd.get(), &version_range); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Can't disable SSLv3"); + return false; + } + + rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_SESSION_TICKETS, PR_FALSE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't disable session tickets"); + return false; + } + + rv = SSL_OptionSet(ssl_fd.get(), SSL_NO_CACHE, PR_TRUE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't disable session caching"); + return false; + } + + rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_DEFLATE, PR_FALSE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't disable deflate"); + return false; + } + + rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_RENEGOTIATION, + SSL_RENEGOTIATE_NEVER); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't disable renegotiation"); + return false; + } + + rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_FALSE_START, PR_FALSE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't disable false start"); + return false; + } + + rv = SSL_OptionSet(ssl_fd.get(), SSL_NO_LOCKS, PR_TRUE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't disable locks"); + return false; + } + + rv = SSL_OptionSet(ssl_fd.get(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't disable ECDHE key reuse"); + return false; + } + + if (!SetupCipherSuites(ssl_fd)) { + return false; + } + + rv = SSL_NamedGroupConfig(ssl_fd.get(), NamedGroupPreferences, + mozilla::ArrayLength(NamedGroupPreferences)); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't set named groups"); + return false; + } + + // Certificate validation + rv = SSL_AuthCertificateHook(ssl_fd.get(), AuthCertificateHook, + reinterpret_cast<void*>(this)); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't set certificate validation hook"); + return false; + } + + if (!SetupAlpn(ssl_fd)) { + return false; + } + + // Now start the handshake + rv = SSL_ResetHandshake(ssl_fd.get(), role_ == SERVER ? PR_TRUE : PR_FALSE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't reset handshake"); + return false; + } + ssl_fd_ = std::move(ssl_fd); + + // Finally, get ready to receive data + downward_->SignalStateChange.connect(this, &TransportLayerDtls::StateChange); + downward_->SignalPacketReceived.connect(this, + &TransportLayerDtls::PacketReceived); + + if (downward_->state() == TS_OPEN) { + TL_SET_STATE(TS_CONNECTING); + Handshake(); + } + + return true; +} + +bool TransportLayerDtls::SetupAlpn(UniquePRFileDesc& ssl_fd) const { + if (alpn_allowed_.empty()) { + return true; + } + + SECStatus rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_NPN, PR_FALSE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't disable NPN"); + return false; + } + + rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_ALPN, PR_TRUE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't enable ALPN"); + return false; + } + + unsigned char buf[MAX_ALPN_LENGTH]; + size_t offset = 0; + for (const auto& tag : alpn_allowed_) { + if ((offset + 1 + tag.length()) >= sizeof(buf)) { + MOZ_MTLOG(ML_ERROR, "ALPN too long"); + return false; + } + buf[offset++] = tag.length(); + memcpy(buf + offset, tag.c_str(), tag.length()); + offset += tag.length(); + } + rv = SSL_SetNextProtoNego(ssl_fd.get(), buf, offset); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't set ALPN string"); + return false; + } + return true; +} + +// Ciphers we need to enable. These are on by default in standard firefox +// builds, but can be disabled with prefs and they aren't on in our unit tests +// since that uses NSS default configuration. +// +// Only override prefs to comply with MUST statements in the security-arch doc. +// Anything outside this list is governed by the usual combination of policy +// and user preferences. +static const uint32_t EnabledCiphers[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}; + +// Disable all NSS suites modes without PFS or with old and rusty ciphersuites. +// Anything outside this list is governed by the usual combination of policy +// and user preferences. +static const uint32_t DisabledCiphers[] = { + // Bug 1310061: disable all SHA384 ciphers until fixed + TLS_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, + + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + TLS_ECDHE_RSA_WITH_RC4_128_SHA, + + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_DSS_WITH_RC4_128_SHA, + + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + TLS_ECDH_RSA_WITH_RC4_128_SHA, + + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, + TLS_RSA_WITH_SEED_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_RC4_128_MD5, + + TLS_DHE_RSA_WITH_DES_CBC_SHA, + TLS_DHE_DSS_WITH_DES_CBC_SHA, + TLS_RSA_WITH_DES_CBC_SHA, + + TLS_ECDHE_ECDSA_WITH_NULL_SHA, + TLS_ECDHE_RSA_WITH_NULL_SHA, + TLS_ECDH_ECDSA_WITH_NULL_SHA, + TLS_ECDH_RSA_WITH_NULL_SHA, + TLS_RSA_WITH_NULL_SHA, + TLS_RSA_WITH_NULL_SHA256, + TLS_RSA_WITH_NULL_MD5, +}; + +bool TransportLayerDtls::SetupCipherSuites(UniquePRFileDesc& ssl_fd) { + SECStatus rv; + + // Set the SRTP ciphers + if (!enabled_srtp_ciphers_.empty()) { + rv = SSL_InstallExtensionHooks(ssl_fd.get(), ssl_use_srtp_xtn, + TransportLayerDtls::WriteSrtpXtn, this, + TransportLayerDtls::HandleSrtpXtn, this); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "unable to set SRTP extension handler"); + return false; + } + } + + for (const auto& cipher : EnabledCiphers) { + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Enabling: " << cipher); + rv = SSL_CipherPrefSet(ssl_fd.get(), cipher, PR_TRUE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Unable to enable suite: " << cipher); + return false; + } + } + + for (const auto& cipher : DisabledCiphers) { + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Disabling: " << cipher); + + PRBool enabled = false; + rv = SSL_CipherPrefGet(ssl_fd.get(), cipher, &enabled); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Unable to check if suite is enabled: " + << cipher); + return false; + } + if (enabled) { + rv = SSL_CipherPrefSet(ssl_fd.get(), cipher, PR_FALSE); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_NOTICE, + LAYER_INFO << "Unable to disable suite: " << cipher); + return false; + } + } + } + + return true; +} + +nsresult TransportLayerDtls::GetCipherSuite(uint16_t* cipherSuite) const { + CheckThread(); + if (!cipherSuite) { + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "GetCipherSuite passed a nullptr"); + return NS_ERROR_NULL_POINTER; + } + if (state_ != TS_OPEN) { + return NS_ERROR_NOT_AVAILABLE; + } + SSLChannelInfo info; + SECStatus rv = SSL_GetChannelInfo(ssl_fd_.get(), &info, sizeof(info)); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "GetCipherSuite can't get channel info"); + return NS_ERROR_FAILURE; + } + *cipherSuite = info.cipherSuite; + return NS_OK; +} + +std::vector<uint16_t> TransportLayerDtls::GetDefaultSrtpCiphers() { + std::vector<uint16_t> ciphers; + + ciphers.push_back(kDtlsSrtpAeadAes128Gcm); + // Since we don't support DTLS 1.3 or SHA384 ciphers (see bug 1312976) + // we don't really enough entropy to prefer this over 128 bit + ciphers.push_back(kDtlsSrtpAeadAes256Gcm); + ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_80); +#ifndef NIGHTLY_BUILD + // To support bug 1491583 lets try to find out if we get bug reports if we no + // longer offer this in Nightly builds. + ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_32); +#endif + + return ciphers; +} + +void TransportLayerDtls::StateChange(TransportLayer* layer, State state) { + switch (state) { + case TS_NONE: + MOZ_ASSERT(false); // Can't happen + break; + + case TS_INIT: + MOZ_MTLOG(ML_ERROR, + LAYER_INFO << "State change of lower layer to INIT forbidden"); + TL_SET_STATE(TS_ERROR); + break; + + case TS_CONNECTING: + MOZ_MTLOG(ML_INFO, LAYER_INFO << "Lower layer is connecting."); + break; + + case TS_OPEN: + if (timer_) { + MOZ_MTLOG(ML_INFO, + LAYER_INFO << "Lower layer is now open; starting TLS"); + timer_->Cancel(); + timer_->SetTarget(target_); + // Async, since the ICE layer might need to send a STUN response, and we + // don't want the handshake to start until that is sent. + timer_->InitWithNamedFuncCallback(TimerCallback, this, 0, + nsITimer::TYPE_ONE_SHOT, + "TransportLayerDtls::TimerCallback"); + TL_SET_STATE(TS_CONNECTING); + } else { + // We have already completed DTLS. Can happen if the ICE layer failed + // due to a loss of network, and then recovered. + TL_SET_STATE(TS_OPEN); + } + break; + + case TS_CLOSED: + MOZ_MTLOG(ML_INFO, LAYER_INFO << "Lower layer is now closed"); + TL_SET_STATE(TS_CLOSED); + break; + + case TS_ERROR: + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer experienced an error"); + TL_SET_STATE(TS_ERROR); + break; + } +} + +void TransportLayerDtls::Handshake() { + if (!timer_) { + // We are done with DTLS, regardless of the state changes of lower layers + return; + } + + // Clear the retransmit timer + timer_->Cancel(); + + MOZ_ASSERT(state_ == TS_CONNECTING); + + SECStatus rv = SSL_ForceHandshake(ssl_fd_.get()); + + if (rv == SECSuccess) { + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "****** SSL handshake completed ******"); + if (!cert_ok_) { + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Certificate check never occurred"); + TL_SET_STATE(TS_ERROR); + return; + } + if (!CheckAlpn()) { + // Despite connecting, the connection doesn't have a valid ALPN label. + // Forcibly close the connection so that the peer isn't left hanging + // (assuming the close_notify isn't dropped). + ssl_fd_ = nullptr; + TL_SET_STATE(TS_ERROR); + return; + } + + TL_SET_STATE(TS_OPEN); + + RecordTlsTelemetry(); + timer_ = nullptr; + } else { + int32_t err = PR_GetError(); + switch (err) { + case SSL_ERROR_RX_MALFORMED_HANDSHAKE: + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Malformed DTLS message; ignoring"); + // If this were TLS (and not DTLS), this would be fatal, but + // here we're required to ignore bad messages, so fall through + [[fallthrough]]; + case PR_WOULD_BLOCK_ERROR: + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Handshake would have blocked"); + PRIntervalTime timeout; + rv = DTLS_GetHandshakeTimeout(ssl_fd_.get(), &timeout); + if (rv == SECSuccess) { + uint32_t timeout_ms = PR_IntervalToMilliseconds(timeout); + + MOZ_MTLOG(ML_DEBUG, + LAYER_INFO << "Setting DTLS timeout to " << timeout_ms); + timer_->SetTarget(target_); + timer_->InitWithNamedFuncCallback( + TimerCallback, this, timeout_ms, nsITimer::TYPE_ONE_SHOT, + "TransportLayerDtls::TimerCallback"); + } + break; + default: + const char* err_msg = PR_ErrorToName(err); + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "DTLS handshake error " << err << " (" + << err_msg << ")"); + TL_SET_STATE(TS_ERROR); + break; + } + } +} + +// Checks if ALPN was negotiated correctly and returns false if it wasn't. +// After this returns successfully, alpn_ will be set to the negotiated +// protocol. +bool TransportLayerDtls::CheckAlpn() { + if (alpn_allowed_.empty()) { + return true; + } + + SSLNextProtoState alpnState; + char chosenAlpn[MAX_ALPN_LENGTH]; + unsigned int chosenAlpnLen; + SECStatus rv = SSL_GetNextProto(ssl_fd_.get(), &alpnState, + reinterpret_cast<unsigned char*>(chosenAlpn), + &chosenAlpnLen, sizeof(chosenAlpn)); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "ALPN error"); + return false; + } + switch (alpnState) { + case SSL_NEXT_PROTO_SELECTED: + case SSL_NEXT_PROTO_NEGOTIATED: + break; // OK + + case SSL_NEXT_PROTO_NO_SUPPORT: + MOZ_MTLOG(ML_NOTICE, + LAYER_INFO << "ALPN not negotiated, " + << (alpn_default_.empty() ? "failing" + : "selecting default")); + alpn_ = alpn_default_; + return !alpn_.empty(); + + case SSL_NEXT_PROTO_NO_OVERLAP: + // This only happens if there is a custom NPN/ALPN callback installed and + // that callback doesn't properly handle ALPN. + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "error in ALPN selection callback"); + return false; + + case SSL_NEXT_PROTO_EARLY_VALUE: + MOZ_CRASH("Unexpected 0-RTT ALPN value"); + return false; + } + + // Warning: NSS won't null terminate the ALPN string for us. + std::string chosen(chosenAlpn, chosenAlpnLen); + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Selected ALPN string: " << chosen); + if (alpn_allowed_.find(chosen) == alpn_allowed_.end()) { + // Maybe our peer chose a protocol we didn't offer (when we are client), or + // something is seriously wrong. + std::ostringstream ss; + for (auto i = alpn_allowed_.begin(); i != alpn_allowed_.end(); ++i) { + ss << (i == alpn_allowed_.begin() ? " '" : ", '") << *i << "'"; + } + MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Bad ALPN string: '" << chosen + << "'; permitted:" << ss.str()); + return false; + } + alpn_ = chosen; + return true; +} + +void TransportLayerDtls::PacketReceived(TransportLayer* layer, + MediaPacket& packet) { + CheckThread(); + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << packet.len() << ")"); + + if (state_ != TS_CONNECTING && state_ != TS_OPEN) { + MOZ_MTLOG(ML_DEBUG, + LAYER_INFO << "Discarding packet in inappropriate state"); + return; + } + + if (!packet.data()) { + // Something ate this, probably the SRTP layer + return; + } + + if (packet.type() != MediaPacket::DTLS) { + return; + } + + nspr_io_adapter_->PacketReceived(packet); + GetDecryptedPackets(); +} + +void TransportLayerDtls::GetDecryptedPackets() { + // If we're still connecting, try to handshake + if (state_ == TS_CONNECTING) { + Handshake(); + } + + // Now try a recv if we're open, since there might be data left + if (state_ == TS_OPEN) { + int32_t rv; + // One packet might contain several DTLS packets + do { + // nICEr uses a 9216 bytes buffer to allow support for jumbo frames + // Can we peek to get a better idea of the actual size? + static const size_t kBufferSize = 9216; + auto buffer = MakeUnique<uint8_t[]>(kBufferSize); + rv = PR_Recv(ssl_fd_.get(), buffer.get(), kBufferSize, 0, + PR_INTERVAL_NO_WAIT); + if (rv > 0) { + // We have data + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS"); + MediaPacket packet; + packet.SetType(MediaPacket::SCTP); + packet.Take(std::move(buffer), static_cast<size_t>(rv)); + SignalPacketReceived(this, packet); + } else if (rv == 0) { + TL_SET_STATE(TS_CLOSED); + } else { + int32_t err = PR_GetError(); + + if (err == PR_WOULD_BLOCK_ERROR) { + // This gets ignored + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Receive would have blocked"); + } else { + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); + TL_SET_STATE(TS_ERROR); + } + } + } while (rv > 0); + } +} + +void TransportLayerDtls::SetState(State state, const char* file, + unsigned line) { + if (timer_) { + switch (state) { + case TS_NONE: + case TS_INIT: + MOZ_ASSERT(false); + break; + case TS_CONNECTING: + break; + case TS_OPEN: + case TS_CLOSED: + case TS_ERROR: + timer_->Cancel(); + break; + } + } + + TransportLayer::SetState(state, file, line); +} + +TransportResult TransportLayerDtls::SendPacket(MediaPacket& packet) { + CheckThread(); + if (state_ != TS_OPEN) { + MOZ_MTLOG(ML_ERROR, + LAYER_INFO << "Can't call SendPacket() in state " << state_); + return TE_ERROR; + } + + int32_t rv = PR_Send(ssl_fd_.get(), packet.data(), packet.len(), 0, + PR_INTERVAL_NO_WAIT); + + if (rv > 0) { + // We have data + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Wrote " << rv << " bytes to SSL Layer"); + return rv; + } + + if (rv == 0) { + TL_SET_STATE(TS_CLOSED); + return 0; + } + + int32_t err = PR_GetError(); + + if (err == PR_WOULD_BLOCK_ERROR) { + // This gets ignored + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Send would have blocked"); + return TE_WOULDBLOCK; + } + + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); + TL_SET_STATE(TS_ERROR); + return TE_ERROR; +} + +SECStatus TransportLayerDtls::GetClientAuthDataHook( + void* arg, PRFileDesc* fd, CERTDistNames* caNames, + CERTCertificate** pRetCert, SECKEYPrivateKey** pRetKey) { + MOZ_MTLOG(ML_DEBUG, "Server requested client auth"); + + TransportLayerDtls* stream = reinterpret_cast<TransportLayerDtls*>(arg); + stream->CheckThread(); + + if (!stream->identity_) { + MOZ_MTLOG(ML_ERROR, "No identity available"); + PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0); + return SECFailure; + } + + *pRetCert = CERT_DupCertificate(stream->identity_->cert().get()); + if (!*pRetCert) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return SECFailure; + } + + *pRetKey = SECKEY_CopyPrivateKey(stream->identity_->privkey().get()); + if (!*pRetKey) { + CERT_DestroyCertificate(*pRetCert); + *pRetCert = nullptr; + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return SECFailure; + } + + return SECSuccess; +} + +nsresult TransportLayerDtls::SetSrtpCiphers( + const std::vector<uint16_t>& ciphers) { + enabled_srtp_ciphers_ = std::move(ciphers); + return NS_OK; +} + +nsresult TransportLayerDtls::GetSrtpCipher(uint16_t* cipher) const { + CheckThread(); + if (srtp_cipher_ == 0) { + return NS_ERROR_NOT_AVAILABLE; + } + *cipher = srtp_cipher_; + return NS_OK; +} + +static uint8_t* WriteUint16(uint8_t* cursor, uint16_t v) { + *cursor++ = v >> 8; + *cursor++ = v & 0xff; + return cursor; +} + +static SSLHandshakeType SrtpXtnServerMessage(PRFileDesc* fd) { + SSLPreliminaryChannelInfo preinfo; + SECStatus rv = SSL_GetPreliminaryChannelInfo(fd, &preinfo, sizeof(preinfo)); + if (rv != SECSuccess) { + MOZ_ASSERT(false, "Can't get version info"); + return ssl_hs_client_hello; + } + return (preinfo.protocolVersion >= SSL_LIBRARY_VERSION_TLS_1_3) + ? ssl_hs_encrypted_extensions + : ssl_hs_server_hello; +} + +/* static */ +PRBool TransportLayerDtls::WriteSrtpXtn(PRFileDesc* fd, + SSLHandshakeType message, uint8_t* data, + unsigned int* len, unsigned int max_len, + void* arg) { + auto self = reinterpret_cast<TransportLayerDtls*>(arg); + + // ClientHello: send all supported versions. + if (message == ssl_hs_client_hello) { + MOZ_ASSERT(self->role_ == CLIENT); + MOZ_ASSERT(self->enabled_srtp_ciphers_.size(), "Haven't enabled SRTP"); + // We will take 2 octets for each cipher, plus a 2 octet length and 1 octet + // for the length of the empty MKI. + if (max_len < self->enabled_srtp_ciphers_.size() * 2 + 3) { + MOZ_ASSERT(false, "Not enough space to send SRTP extension"); + return false; + } + uint8_t* cursor = WriteUint16(data, self->enabled_srtp_ciphers_.size() * 2); + for (auto cs : self->enabled_srtp_ciphers_) { + cursor = WriteUint16(cursor, cs); + } + *cursor++ = 0; // MKI is empty + *len = cursor - data; + return true; + } + + if (message == SrtpXtnServerMessage(fd)) { + MOZ_ASSERT(self->role_ == SERVER); + if (!self->srtp_cipher_) { + // Not negotiated. Definitely bad, but the connection can fail later. + return false; + } + if (max_len < 5) { + MOZ_ASSERT(false, "Not enough space to send SRTP extension"); + return false; + } + + uint8_t* cursor = WriteUint16(data, 2); // Length = 2. + cursor = WriteUint16(cursor, self->srtp_cipher_); + *cursor++ = 0; // No MKI + *len = cursor - data; + return true; + } + + return false; +} + +class TlsParser { + public: + TlsParser(const uint8_t* data, size_t len) : cursor_(data), remaining_(len) {} + + bool error() const { return error_; } + size_t remaining() const { return remaining_; } + + template <typename T, + class = typename std::enable_if<std::is_unsigned<T>::value>::type> + void Read(T* v, size_t sz = sizeof(T)) { + MOZ_ASSERT(sz <= sizeof(T), + "Type is too small to hold the value requested"); + if (remaining_ < sz) { + error_ = true; + return; + } + + T result = 0; + for (size_t i = 0; i < sz; ++i) { + result = (result << 8) | *cursor_++; + remaining_--; + } + *v = result; + } + + template <typename T, + class = typename std::enable_if<std::is_unsigned<T>::value>::type> + void ReadVector(std::vector<T>* v, size_t w) { + MOZ_ASSERT(v->empty(), "vector needs to be empty"); + + uint32_t len; + Read(&len, w); + if (error_ || len % sizeof(T) != 0 || len > remaining_) { + error_ = true; + return; + } + + size_t count = len / sizeof(T); + v->reserve(count); + for (T i = 0; !error_ && i < count; ++i) { + T item; + Read(&item); + if (!error_) { + v->push_back(item); + } + } + } + + void Skip(size_t n) { + if (remaining_ < n) { + error_ = true; + } else { + cursor_ += n; + remaining_ -= n; + } + } + + size_t SkipVector(size_t w) { + uint32_t len = 0; + Read(&len, w); + Skip(len); + return len; + } + + private: + const uint8_t* cursor_; + size_t remaining_; + bool error_ = false; +}; + +/* static */ +SECStatus TransportLayerDtls::HandleSrtpXtn( + PRFileDesc* fd, SSLHandshakeType message, const uint8_t* data, + unsigned int len, SSLAlertDescription* alert, void* arg) { + static const uint8_t kTlsAlertHandshakeFailure = 40; + static const uint8_t kTlsAlertIllegalParameter = 47; + static const uint8_t kTlsAlertDecodeError = 50; + static const uint8_t kTlsAlertUnsupportedExtension = 110; + + auto self = reinterpret_cast<TransportLayerDtls*>(arg); + + // Parse the extension. + TlsParser parser(data, len); + std::vector<uint16_t> advertised; + parser.ReadVector(&advertised, 2); + size_t mki_len = parser.SkipVector(1); + if (parser.error() || parser.remaining() > 0) { + *alert = kTlsAlertDecodeError; + return SECFailure; + } + + if (message == ssl_hs_client_hello) { + MOZ_ASSERT(self->role_ == SERVER); + if (self->enabled_srtp_ciphers_.empty()) { + // We don't have SRTP enabled, which is probably bad, but no sense in + // having the handshake fail at this point, let the client decide if this + // is a problem. + return SECSuccess; + } + + for (auto supported : self->enabled_srtp_ciphers_) { + auto it = std::find(advertised.begin(), advertised.end(), supported); + if (it != advertised.end()) { + self->srtp_cipher_ = supported; + return SECSuccess; + } + } + + // No common cipher. + *alert = kTlsAlertHandshakeFailure; + return SECFailure; + } + + if (message == SrtpXtnServerMessage(fd)) { + MOZ_ASSERT(self->role_ == CLIENT); + if (advertised.size() != 1 || mki_len > 0) { + *alert = kTlsAlertIllegalParameter; + return SECFailure; + } + self->srtp_cipher_ = advertised[0]; + return SECSuccess; + } + + *alert = kTlsAlertUnsupportedExtension; + return SECFailure; +} + +nsresult TransportLayerDtls::ExportKeyingMaterial(const std::string& label, + bool use_context, + const std::string& context, + unsigned char* out, + unsigned int outlen) { + CheckThread(); + if (state_ != TS_OPEN) { + MOZ_ASSERT(false, "Transport must be open for ExportKeyingMaterial"); + return NS_ERROR_NOT_AVAILABLE; + } + SECStatus rv = SSL_ExportKeyingMaterial( + ssl_fd_.get(), label.c_str(), label.size(), use_context, + reinterpret_cast<const unsigned char*>(context.c_str()), context.size(), + out, outlen); + if (rv != SECSuccess) { + MOZ_MTLOG(ML_ERROR, "Couldn't export SSL keying material"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +SECStatus TransportLayerDtls::AuthCertificateHook(void* arg, PRFileDesc* fd, + PRBool checksig, + PRBool isServer) { + TransportLayerDtls* stream = reinterpret_cast<TransportLayerDtls*>(arg); + stream->CheckThread(); + return stream->AuthCertificateHook(fd, checksig, isServer); +} + +SECStatus TransportLayerDtls::CheckDigest( + const DtlsDigest& digest, UniqueCERTCertificate& peer_cert) const { + DtlsDigest computed_digest(digest.algorithm_); + + MOZ_MTLOG(ML_DEBUG, + LAYER_INFO << "Checking digest, algorithm=" << digest.algorithm_); + nsresult res = DtlsIdentity::ComputeFingerprint(peer_cert, &computed_digest); + if (NS_FAILED(res)) { + MOZ_MTLOG(ML_ERROR, "Could not compute peer fingerprint for digest " + << digest.algorithm_); + // Go to end + PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); + return SECFailure; + } + + if (computed_digest != digest) { + MOZ_MTLOG(ML_ERROR, "Digest does not match"); + PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); + return SECFailure; + } + + return SECSuccess; +} + +SECStatus TransportLayerDtls::AuthCertificateHook(PRFileDesc* fd, + PRBool checksig, + PRBool isServer) { + CheckThread(); + UniqueCERTCertificate peer_cert(SSL_PeerCertificate(fd)); + + // We are not set up to take this being called multiple + // times. Change this if we ever add renegotiation. + MOZ_ASSERT(!auth_hook_called_); + if (auth_hook_called_) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return SECFailure; + } + auth_hook_called_ = true; + + MOZ_ASSERT(verification_mode_ != VERIFY_UNSET); + + switch (verification_mode_) { + case VERIFY_UNSET: + // Break out to error exit + PR_SetError(PR_UNKNOWN_ERROR, 0); + break; + + case VERIFY_ALLOW_ALL: + cert_ok_ = true; + return SECSuccess; + + case VERIFY_DIGEST: { + MOZ_ASSERT(!digests_.empty()); + // Check all the provided digests + + // Checking functions call PR_SetError() + SECStatus rv = SECFailure; + for (auto digest : digests_) { + rv = CheckDigest(digest, peer_cert); + + // Matches a digest, we are good to go + if (rv == SECSuccess) { + cert_ok_ = true; + return SECSuccess; + } + } + } break; + default: + MOZ_CRASH(); // Can't happen + } + + return SECFailure; +} + +void TransportLayerDtls::TimerCallback(nsITimer* timer, void* arg) { + TransportLayerDtls* dtls = reinterpret_cast<TransportLayerDtls*>(arg); + + MOZ_MTLOG(ML_DEBUG, "DTLS timer expired"); + + dtls->Handshake(); +} + +void TransportLayerDtls::RecordTlsTelemetry() { + MOZ_ASSERT(state_ == TS_OPEN); + SSLChannelInfo info; + SECStatus ss = SSL_GetChannelInfo(ssl_fd_.get(), &info, sizeof(info)); + if (ss != SECSuccess) { + MOZ_MTLOG(ML_NOTICE, + LAYER_INFO << "RecordTlsTelemetry failed to get channel info"); + return; + } + + uint16_t telemetry_cipher = 0; + + switch (info.cipherSuite) { + /* Old DHE ciphers: candidates for removal, see bug 1227519 */ + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + telemetry_cipher = 1; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + telemetry_cipher = 2; + break; + /* Current ciphers */ + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + telemetry_cipher = 3; + break; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + telemetry_cipher = 4; + break; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + telemetry_cipher = 5; + break; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + telemetry_cipher = 6; + break; + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + telemetry_cipher = 7; + break; + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + telemetry_cipher = 8; + break; + case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + telemetry_cipher = 9; + break; + case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + telemetry_cipher = 10; + break; + /* TLS 1.3 ciphers */ + case TLS_AES_128_GCM_SHA256: + telemetry_cipher = 11; + break; + case TLS_CHACHA20_POLY1305_SHA256: + telemetry_cipher = 12; + break; + case TLS_AES_256_GCM_SHA384: + telemetry_cipher = 13; + break; + } + + Telemetry::Accumulate(Telemetry::WEBRTC_DTLS_CIPHER, telemetry_cipher); + + uint16_t cipher; + nsresult rv = GetSrtpCipher(&cipher); + + if (NS_FAILED(rv)) { + MOZ_MTLOG(ML_DEBUG, "No SRTP cipher suite"); + return; + } + + auto cipher_label = mozilla::Telemetry::LABELS_WEBRTC_SRTP_CIPHER::Unknown; + + switch (cipher) { + case kDtlsSrtpAes128CmHmacSha1_80: + cipher_label = Telemetry::LABELS_WEBRTC_SRTP_CIPHER::Aes128CmHmacSha1_80; + break; + case kDtlsSrtpAes128CmHmacSha1_32: + cipher_label = Telemetry::LABELS_WEBRTC_SRTP_CIPHER::Aes128CmHmacSha1_32; + break; + case kDtlsSrtpAeadAes128Gcm: + cipher_label = Telemetry::LABELS_WEBRTC_SRTP_CIPHER::AeadAes128Gcm; + break; + case kDtlsSrtpAeadAes256Gcm: + cipher_label = Telemetry::LABELS_WEBRTC_SRTP_CIPHER::AeadAes256Gcm; + break; + } + + Telemetry::AccumulateCategorical(cipher_label); +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/transportlayerdtls.h b/dom/media/webrtc/transport/transportlayerdtls.h new file mode 100644 index 0000000000..078b266e8b --- /dev/null +++ b/dom/media/webrtc/transport/transportlayerdtls.h @@ -0,0 +1,187 @@ +/* -*- 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 + +#ifndef transportlayerdtls_h__ +#define transportlayerdtls_h__ + +#include <queue> +#include <set> + +#ifdef XP_MACOSX +// ensure that Apple Security kit enum goes before "sslproto.h" +# include <CoreFoundation/CFAvailability.h> +# include <Security/CipherSuite.h> +#endif + +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "nsCOMPtr.h" +#include "nsITimer.h" +#include "ScopedNSSTypes.h" +#include "m_cpp_utils.h" +#include "dtlsidentity.h" +#include "transportlayer.h" +#include "ssl.h" +#include "sslproto.h" + +namespace mozilla { + +// RFC 5764 (we don't support the NULL cipher) +static const uint16_t kDtlsSrtpAes128CmHmacSha1_80 = 0x0001; +static const uint16_t kDtlsSrtpAes128CmHmacSha1_32 = 0x0002; +// RFC 7714 +static const uint16_t kDtlsSrtpAeadAes128Gcm = 0x0007; +static const uint16_t kDtlsSrtpAeadAes256Gcm = 0x0008; + +struct Packet; + +class TransportLayerNSPRAdapter { + public: + explicit TransportLayerNSPRAdapter(TransportLayer* output) + : output_(output), input_(), enabled_(true) {} + + void PacketReceived(MediaPacket& packet); + int32_t Recv(void* buf, int32_t buflen); + int32_t Write(const void* buf, int32_t length); + void SetEnabled(bool enabled) { enabled_ = enabled; } + + private: + DISALLOW_COPY_ASSIGN(TransportLayerNSPRAdapter); + + TransportLayer* output_; + std::queue<MediaPacket*> input_; + bool enabled_; +}; + +class TransportLayerDtls final : public TransportLayer { + public: + TransportLayerDtls() = default; + + virtual ~TransportLayerDtls(); + + enum Role { CLIENT, SERVER }; + enum Verification { VERIFY_UNSET, VERIFY_ALLOW_ALL, VERIFY_DIGEST }; + + // DTLS-specific operations + void SetRole(Role role) { role_ = role; } + Role role() { return role_; } + + enum class Version : uint16_t { + DTLS_1_0 = SSL_LIBRARY_VERSION_DTLS_1_0, + DTLS_1_2 = SSL_LIBRARY_VERSION_DTLS_1_2, + DTLS_1_3 = SSL_LIBRARY_VERSION_DTLS_1_3 + }; + void SetMinMaxVersion(Version min_version, Version max_version); + + void SetIdentity(const RefPtr<DtlsIdentity>& identity) { + identity_ = identity; + } + nsresult SetAlpn(const std::set<std::string>& allowedAlpn, + const std::string& alpnDefault); + const std::string& GetNegotiatedAlpn() const { return alpn_; } + + nsresult SetVerificationAllowAll(); + + nsresult SetVerificationDigest(const DtlsDigest& digest); + + nsresult GetCipherSuite(uint16_t* cipherSuite) const; + + nsresult SetSrtpCiphers(const std::vector<uint16_t>& ciphers); + nsresult GetSrtpCipher(uint16_t* cipher) const; + static std::vector<uint16_t> GetDefaultSrtpCiphers(); + + nsresult ExportKeyingMaterial(const std::string& label, bool use_context, + const std::string& context, unsigned char* out, + unsigned int outlen); + + // Transport layer overrides. + nsresult InitInternal() override; + void WasInserted() override; + TransportResult SendPacket(MediaPacket& packet) override; + + // Signals + void StateChange(TransportLayer* layer, State state); + void PacketReceived(TransportLayer* layer, MediaPacket& packet); + + // For testing use only. Returns the fd. + PRFileDesc* internal_fd() { + CheckThread(); + return ssl_fd_.get(); + } + + TRANSPORT_LAYER_ID("dtls") + + protected: + void SetState(State state, const char* file, unsigned line) override; + + private: + DISALLOW_COPY_ASSIGN(TransportLayerDtls); + + bool Setup(); + bool SetupCipherSuites(UniquePRFileDesc& ssl_fd); + bool SetupAlpn(UniquePRFileDesc& ssl_fd) const; + void GetDecryptedPackets(); + void Handshake(); + + bool CheckAlpn(); + + static SECStatus GetClientAuthDataHook(void* arg, PRFileDesc* fd, + CERTDistNames* caNames, + CERTCertificate** pRetCert, + SECKEYPrivateKey** pRetKey); + static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd, + PRBool checksig, PRBool isServer); + SECStatus AuthCertificateHook(PRFileDesc* fd, PRBool checksig, + PRBool isServer); + + static void TimerCallback(nsITimer* timer, void* arg); + + SECStatus CheckDigest(const DtlsDigest& digest, + UniqueCERTCertificate& cert) const; + + void RecordHandshakeCompletionTelemetry(TransportLayer::State endState); + void RecordTlsTelemetry(); + + static PRBool WriteSrtpXtn(PRFileDesc* fd, SSLHandshakeType message, + uint8_t* data, unsigned int* len, + unsigned int max_len, void* arg); + + static SECStatus HandleSrtpXtn(PRFileDesc* fd, SSLHandshakeType message, + const uint8_t* data, unsigned int len, + SSLAlertDescription* alert, void* arg); + + RefPtr<DtlsIdentity> identity_; + // What ALPN identifiers are permitted. + std::set<std::string> alpn_allowed_; + // What ALPN identifier is used if ALPN is not supported. + // The empty string indicates that ALPN is required. + std::string alpn_default_; + // What ALPN string was negotiated. + std::string alpn_; + std::vector<uint16_t> enabled_srtp_ciphers_; + uint16_t srtp_cipher_ = 0; + + Role role_ = CLIENT; + Verification verification_mode_ = VERIFY_UNSET; + std::vector<DtlsDigest> digests_; + + Version minVersion_ = Version::DTLS_1_0; + Version maxVersion_ = Version::DTLS_1_2; + + // Must delete nspr_io_adapter after ssl_fd_ b/c ssl_fd_ causes an alert + // (ssl_fd_ contains an un-owning pointer to nspr_io_adapter_) + UniquePtr<TransportLayerNSPRAdapter> nspr_io_adapter_ = nullptr; + UniquePRFileDesc ssl_fd_ = nullptr; + + nsCOMPtr<nsITimer> timer_ = nullptr; + bool auth_hook_called_ = false; + bool cert_ok_ = false; +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/transportlayerice.cpp b/dom/media/webrtc/transport/transportlayerice.cpp new file mode 100644 index 0000000000..03d8131ead --- /dev/null +++ b/dom/media/webrtc/transport/transportlayerice.cpp @@ -0,0 +1,168 @@ +/* -*- 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 + +// Some of this code is cut-and-pasted from nICEr. Copyright is: + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +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 <string> + +#include "nsComponentManagerUtils.h" +#include "nsError.h" +#include "nsNetCID.h" + +// nICEr includes +extern "C" { +#include "nr_api.h" +#include "ice_util.h" +#include "transport_addr.h" +} + +// Local includes +#include "logging.h" +#include "nricemediastream.h" +#include "transportlayerice.h" + +namespace mozilla { + +#ifdef ERROR +# undef ERROR +#endif + +MOZ_MTLOG_MODULE("mtransport") + +TransportLayerIce::TransportLayerIce() : stream_(nullptr), component_(0) { + // setup happens later +} + +TransportLayerIce::~TransportLayerIce() { + // No need to do anything here, since we use smart pointers +} + +void TransportLayerIce::SetParameters(RefPtr<NrIceMediaStream> stream, + int component) { + // Stream could be null in the case of some badly written js that causes + // us to be in an ICE restart case, but not have valid streams due to + // not calling PeerConnectionImpl::EnsureTransports if + // PeerConnectionImpl::SetSignalingState_m thinks the conditions were + // not correct. We also solved a case where an incoming answer was + // incorrectly beginning an ICE restart when the offer did not indicate one. + if (!stream) { + MOZ_ASSERT(false); + return; + } + + stream_ = stream; + component_ = component; + + PostSetup(); +} + +void TransportLayerIce::PostSetup() { + stream_->SignalReady.connect(this, &TransportLayerIce::IceReady); + stream_->SignalFailed.connect(this, &TransportLayerIce::IceFailed); + stream_->SignalPacketReceived.connect(this, + &TransportLayerIce::IcePacketReceived); + if (stream_->state() == NrIceMediaStream::ICE_OPEN) { + TL_SET_STATE(TS_OPEN); + } +} + +TransportResult TransportLayerIce::SendPacket(MediaPacket& packet) { + CheckThread(); + SignalPacketSending(this, packet); + nsresult res = stream_->SendPacket(component_, packet.data(), packet.len()); + + if (!NS_SUCCEEDED(res)) { + return (res == NS_BASE_STREAM_WOULD_BLOCK) ? TE_WOULDBLOCK : TE_ERROR; + } + + MOZ_MTLOG(ML_DEBUG, + LAYER_INFO << " SendPacket(" << packet.len() << ") succeeded"); + + return packet.len(); +} + +void TransportLayerIce::IceCandidate(NrIceMediaStream* stream, + const std::string&) { + // NO-OP for now +} + +void TransportLayerIce::IceReady(NrIceMediaStream* stream) { + CheckThread(); + // only handle the current stream (not the old stream during restart) + if (stream != stream_) { + return; + } + MOZ_MTLOG(ML_INFO, LAYER_INFO << "ICE Ready(" << stream->name() << "," + << component_ << ")"); + TL_SET_STATE(TS_OPEN); +} + +void TransportLayerIce::IceFailed(NrIceMediaStream* stream) { + CheckThread(); + // only handle the current stream (not the old stream during restart) + if (stream != stream_) { + return; + } + MOZ_MTLOG(ML_INFO, LAYER_INFO << "ICE Failed(" << stream->name() << "," + << component_ << ")"); + TL_SET_STATE(TS_ERROR); +} + +void TransportLayerIce::IcePacketReceived(NrIceMediaStream* stream, + int component, + const unsigned char* data, int len) { + CheckThread(); + // We get packets for both components, so ignore the ones that aren't + // for us. + if (component_ != component) return; + + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << stream->name() << "," + << component << "," << len << ")"); + // Might be useful to allow MediaPacket to borrow a buffer (ie; not take + // ownership, but copy it if the MediaPacket is moved). This could be a + // footgun though with MediaPackets that end up on the heap. + MediaPacket packet; + packet.Copy(data, len); + packet.Categorize(); + + SignalPacketReceived(this, packet); +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/transportlayerice.h b/dom/media/webrtc/transport/transportlayerice.h new file mode 100644 index 0000000000..1c55b71ce8 --- /dev/null +++ b/dom/media/webrtc/transport/transportlayerice.h @@ -0,0 +1,60 @@ +/* -*- 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 + +// This is a wrapper around the nICEr ICE stack +#ifndef transportlayerice_h__ +#define transportlayerice_h__ + +#include "sigslot.h" + +#include "mozilla/RefPtr.h" + +#include "m_cpp_utils.h" + +#include "nricemediastream.h" +#include "transportlayer.h" + +// An ICE transport layer -- corresponds to a single ICE +namespace mozilla { + +class TransportLayerIce : public TransportLayer { + public: + TransportLayerIce(); + + virtual ~TransportLayerIce(); + + void SetParameters(RefPtr<NrIceMediaStream> stream, int component); + + void ResetOldStream(); // called after successful ice restart + void RestoreOldStream(); // called after unsuccessful ice restart + + // Transport layer overrides. + TransportResult SendPacket(MediaPacket& packet) override; + + // Slots for ICE + void IceCandidate(NrIceMediaStream* stream, const std::string&); + void IceReady(NrIceMediaStream* stream); + void IceFailed(NrIceMediaStream* stream); + void IcePacketReceived(NrIceMediaStream* stream, int component, + const unsigned char* data, int len); + + // Useful for capturing encrypted packets + sigslot::signal2<TransportLayer*, MediaPacket&> SignalPacketSending; + + TRANSPORT_LAYER_ID("ice") + + private: + DISALLOW_COPY_ASSIGN(TransportLayerIce); + void PostSetup(); + + RefPtr<NrIceMediaStream> stream_; + int component_; +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/transportlayerlog.cpp b/dom/media/webrtc/transport/transportlayerlog.cpp new file mode 100644 index 0000000000..d78e722dca --- /dev/null +++ b/dom/media/webrtc/transport/transportlayerlog.cpp @@ -0,0 +1,48 @@ +/* -*- 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 "logging.h" +#include "transportlayerlog.h" + +namespace mozilla { + +MOZ_MTLOG_MODULE("mtransport") + +void TransportLayerLogging::WasInserted() { + if (downward_) { + downward_->SignalStateChange.connect(this, + &TransportLayerLogging::StateChange); + downward_->SignalPacketReceived.connect( + this, &TransportLayerLogging::PacketReceived); + TL_SET_STATE(downward_->state()); + } +} + +TransportResult TransportLayerLogging::SendPacket(MediaPacket& packet) { + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << packet.len() << ")"); + + if (downward_) { + return downward_->SendPacket(packet); + } + return static_cast<TransportResult>(packet.len()); +} + +void TransportLayerLogging::StateChange(TransportLayer* layer, State state) { + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Received StateChange to " << state); + + TL_SET_STATE(state); +} + +void TransportLayerLogging::PacketReceived(TransportLayer* layer, + MediaPacket& packet) { + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << packet.len() << ")"); + + SignalPacketReceived(this, packet); +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/transportlayerlog.h b/dom/media/webrtc/transport/transportlayerlog.h new file mode 100644 index 0000000000..4d0b3fd930 --- /dev/null +++ b/dom/media/webrtc/transport/transportlayerlog.h @@ -0,0 +1,38 @@ +/* -*- 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 + +#ifndef transportlayerlog_h__ +#define transportlayerlog_h__ + +#include "m_cpp_utils.h" +#include "transportlayer.h" + +namespace mozilla { + +class TransportLayerLogging : public TransportLayer { + public: + TransportLayerLogging() = default; + + // Overrides for TransportLayer + TransportResult SendPacket(MediaPacket& packet) override; + + // Signals (forwarded to upper layer) + void StateChange(TransportLayer* layer, State state); + void PacketReceived(TransportLayer* layer, MediaPacket& packet); + + TRANSPORT_LAYER_ID("log") + + protected: + void WasInserted() override; + + private: + DISALLOW_COPY_ASSIGN(TransportLayerLogging); +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/transportlayerloopback.cpp b/dom/media/webrtc/transport/transportlayerloopback.cpp new file mode 100644 index 0000000000..ab2a52c17e --- /dev/null +++ b/dom/media/webrtc/transport/transportlayerloopback.cpp @@ -0,0 +1,119 @@ +/* -*- 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 "logging.h" +#include "prlock.h" + +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" +#include "nsString.h" + +#include "transportlayerloopback.h" + +namespace mozilla { + +MOZ_MTLOG_MODULE("mtransport") + +nsresult TransportLayerLoopback::Init() { + nsresult rv; + target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + if (!NS_SUCCEEDED(rv)) return rv; + + timer_ = NS_NewTimer(target_); + MOZ_ASSERT(timer_); + if (!timer_) return NS_ERROR_FAILURE; + + packets_lock_ = PR_NewLock(); + MOZ_ASSERT(packets_lock_); + if (!packets_lock_) return NS_ERROR_FAILURE; + + deliverer_ = new Deliverer(this); + + timer_->InitWithCallback(deliverer_, 100, nsITimer::TYPE_REPEATING_SLACK); + + return NS_OK; +} + +// Connect to the other side +void TransportLayerLoopback::Connect(TransportLayerLoopback* peer) { + peer_ = peer; + + TL_SET_STATE(TS_OPEN); +} + +TransportResult TransportLayerLoopback::SendPacket(MediaPacket& packet) { + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << packet.len() << ")"); + + if (!peer_) { + MOZ_MTLOG(ML_ERROR, "Discarding packet because peer not attached"); + return TE_ERROR; + } + + size_t len = packet.len(); + nsresult res = peer_->QueuePacket(packet); + if (!NS_SUCCEEDED(res)) return TE_ERROR; + + return static_cast<TransportResult>(len); +} + +nsresult TransportLayerLoopback::QueuePacket(MediaPacket& packet) { + MOZ_ASSERT(packets_lock_); + + PR_Lock(packets_lock_); + + if (combinePackets_ && !packets_.empty()) { + MediaPacket* prevPacket = packets_.front(); + + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing combined packets of length " + << prevPacket->len() << " and " + << packet.len()); + auto combined = MakeUnique<uint8_t[]>(prevPacket->len() + packet.len()); + memcpy(combined.get(), prevPacket->data(), prevPacket->len()); + memcpy(combined.get() + prevPacket->len(), packet.data(), packet.len()); + prevPacket->Take(std::move(combined), prevPacket->len() + packet.len()); + } else { + MOZ_MTLOG(ML_DEBUG, + LAYER_INFO << " Enqueuing packet of length " << packet.len()); + packets_.push(new MediaPacket(std::move(packet))); + } + + PRStatus r = PR_Unlock(packets_lock_); + MOZ_ASSERT(r == PR_SUCCESS); + if (r != PR_SUCCESS) return NS_ERROR_FAILURE; + + return NS_OK; +} + +void TransportLayerLoopback::DeliverPackets() { + while (!packets_.empty()) { + UniquePtr<MediaPacket> packet(packets_.front()); + packets_.pop(); + + MOZ_MTLOG(ML_DEBUG, + LAYER_INFO << " Delivering packet of length " << packet->len()); + SignalPacketReceived(this, *packet); + } +} + +NS_IMPL_ISUPPORTS(TransportLayerLoopback::Deliverer, nsITimerCallback, nsINamed) + +NS_IMETHODIMP TransportLayerLoopback::Deliverer::Notify(nsITimer* timer) { + if (!layer_) return NS_OK; + + layer_->DeliverPackets(); + + return NS_OK; +} + +NS_IMETHODIMP TransportLayerLoopback::Deliverer::GetName(nsACString& aName) { + aName.AssignLiteral("TransportLayerLoopback::Deliverer"); + return NS_OK; +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/transportlayerloopback.h b/dom/media/webrtc/transport/transportlayerloopback.h new file mode 100644 index 0000000000..6801af8189 --- /dev/null +++ b/dom/media/webrtc/transport/transportlayerloopback.h @@ -0,0 +1,109 @@ +/* -*- 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 + +#ifndef transportlayerloopback_h__ +#define transportlayerloopback_h__ + +#include "prlock.h" + +#include <queue> + +#include "nsCOMPtr.h" +#include "nsINamed.h" +#include "nsITimer.h" + +#include "m_cpp_utils.h" +#include "transportlayer.h" + +// A simple loopback transport layer that is used for testing. +namespace mozilla { + +class TransportLayerLoopback : public TransportLayer { + public: + TransportLayerLoopback() + : peer_(nullptr), + timer_(nullptr), + packets_(), + packets_lock_(nullptr), + deliverer_(nullptr), + combinePackets_(false) {} + + ~TransportLayerLoopback() { + while (!packets_.empty()) { + MediaPacket* packet = packets_.front(); + packets_.pop(); + delete packet; + } + if (packets_lock_) { + PR_DestroyLock(packets_lock_); + } + timer_->Cancel(); + deliverer_->Detach(); + } + + // Init + nsresult Init(); + + // Connect to the other side + void Connect(TransportLayerLoopback* peer); + + // Disconnect + void Disconnect() { + TransportLayerLoopback* peer = peer_; + + peer_ = nullptr; + if (peer) { + peer->Disconnect(); + } + } + + void CombinePackets(bool combine) { combinePackets_ = combine; } + + // Overrides for TransportLayer + TransportResult SendPacket(MediaPacket& packet) override; + + // Deliver queued packets + void DeliverPackets(); + + TRANSPORT_LAYER_ID("loopback") + + private: + DISALLOW_COPY_ASSIGN(TransportLayerLoopback); + + // A timer to deliver packets if some are available + // Fires every 100 ms + class Deliverer : public nsITimerCallback, public nsINamed { + public: + explicit Deliverer(TransportLayerLoopback* layer) : layer_(layer) {} + void Detach() { layer_ = nullptr; } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITIMERCALLBACK + NS_DECL_NSINAMED + + private: + virtual ~Deliverer() = default; + + DISALLOW_COPY_ASSIGN(Deliverer); + + TransportLayerLoopback* layer_; + }; + + // Queue a packet for delivery + nsresult QueuePacket(MediaPacket& packet); + + TransportLayerLoopback* peer_; + nsCOMPtr<nsITimer> timer_; + std::queue<MediaPacket*> packets_; + PRLock* packets_lock_; + RefPtr<Deliverer> deliverer_; + bool combinePackets_; +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transport/transportlayersrtp.cpp b/dom/media/webrtc/transport/transportlayersrtp.cpp new file mode 100644 index 0000000000..25830a2dde --- /dev/null +++ b/dom/media/webrtc/transport/transportlayersrtp.cpp @@ -0,0 +1,222 @@ +/* -*- 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 "transportlayersrtp.h" +#include "transportlayerdtls.h" + +#include "logging.h" +#include "nsError.h" +#include "mozilla/Assertions.h" + +namespace mozilla { + +MOZ_MTLOG_MODULE("mtransport") + +static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp"; + +TransportLayerSrtp::TransportLayerSrtp(TransportLayerDtls& dtls) { + // We need to connect to the dtls layer, not the ice layer, because even + // though the packets that DTLS decrypts don't flow through us, we do base our + // keying information on the keying information established by the DTLS layer. + dtls.SignalStateChange.connect(this, &TransportLayerSrtp::StateChange); + + TL_SET_STATE(dtls.state()); +} + +void TransportLayerSrtp::WasInserted() { + // Connect to the lower layers + if (!Setup()) { + TL_SET_STATE(TS_ERROR); + } +} + +bool TransportLayerSrtp::Setup() { + CheckThread(); + if (!downward_) { + MOZ_MTLOG(ML_ERROR, "SRTP layer with nothing below. This is useless"); + return false; + } + + // downward_ is the TransportLayerIce + downward_->SignalPacketReceived.connect(this, + &TransportLayerSrtp::PacketReceived); + + return true; +} + +TransportResult TransportLayerSrtp::SendPacket(MediaPacket& packet) { + if (state() != TS_OPEN) { + return TE_ERROR; + } + + if (packet.len() < 4) { + MOZ_ASSERT(false); + return TE_ERROR; + } + + MOZ_ASSERT(packet.capacity() - packet.len() >= SRTP_MAX_EXPANSION); + + int out_len; + nsresult res; + switch (packet.type()) { + case MediaPacket::RTP: + res = mSendSrtp->ProtectRtp(packet.data(), packet.len(), + packet.capacity(), &out_len); + packet.SetType(MediaPacket::SRTP); + break; + case MediaPacket::RTCP: + res = mSendSrtp->ProtectRtcp(packet.data(), packet.len(), + packet.capacity(), &out_len); + packet.SetType(MediaPacket::SRTCP); + break; + default: + MOZ_CRASH("SRTP layer asked to send packet that is neither RTP or RTCP"); + } + + if (NS_FAILED(res)) { + MOZ_MTLOG(ML_ERROR, + "Error protecting " + << (packet.type() == MediaPacket::RTP ? "RTP" : "RTCP") + << " len=" << packet.len() << "[" << std::hex + << packet.data()[0] << " " << packet.data()[1] << " " + << packet.data()[2] << " " << packet.data()[3] << "]"); + return TE_ERROR; + } + + size_t unencrypted_len = packet.len(); + packet.SetLength(out_len); + + TransportResult bytes = downward_->SendPacket(packet); + if (bytes == out_len) { + // Whole packet was written, but the encrypted length might be different. + // Don't confuse the caller. + return unencrypted_len; + } + + if (bytes == TE_WOULDBLOCK) { + return TE_WOULDBLOCK; + } + + return TE_ERROR; +} + +void TransportLayerSrtp::StateChange(TransportLayer* layer, State state) { + if (state == TS_OPEN && !mSendSrtp) { + TransportLayerDtls* dtls = static_cast<TransportLayerDtls*>(layer); + MOZ_ASSERT(dtls); // DTLS is mandatory + + uint16_t cipher_suite; + nsresult res = dtls->GetSrtpCipher(&cipher_suite); + if (NS_FAILED(res)) { + MOZ_MTLOG(ML_DEBUG, "DTLS-SRTP disabled"); + TL_SET_STATE(TS_ERROR); + return; + } + + unsigned int key_size = SrtpFlow::KeySize(cipher_suite); + unsigned int salt_size = SrtpFlow::SaltSize(cipher_suite); + unsigned int master_key_size = key_size + salt_size; + MOZ_ASSERT(master_key_size <= SRTP_MAX_KEY_LENGTH); + + // SRTP Key Exporter as per RFC 5764 S 4.2 + unsigned char srtp_block[SRTP_MAX_KEY_LENGTH * 2]; + res = dtls->ExportKeyingMaterial(kDTLSExporterLabel, false, "", srtp_block, + sizeof(srtp_block)); + if (NS_FAILED(res)) { + MOZ_MTLOG(ML_ERROR, "Failed to compute DTLS-SRTP keys. This is an error"); + TL_SET_STATE(TS_ERROR); + return; + } + + // Slice and dice as per RFC 5764 S 4.2 + unsigned char client_write_key[SRTP_MAX_KEY_LENGTH]; + unsigned char server_write_key[SRTP_MAX_KEY_LENGTH]; + unsigned int offset = 0; + memcpy(client_write_key, srtp_block + offset, key_size); + offset += key_size; + memcpy(server_write_key, srtp_block + offset, key_size); + offset += key_size; + memcpy(client_write_key + key_size, srtp_block + offset, salt_size); + offset += salt_size; + memcpy(server_write_key + key_size, srtp_block + offset, salt_size); + MOZ_ASSERT((offset + salt_size) == (2 * master_key_size)); + + unsigned char* write_key; + unsigned char* read_key; + + if (dtls->role() == TransportLayerDtls::CLIENT) { + write_key = client_write_key; + read_key = server_write_key; + } else { + write_key = server_write_key; + read_key = client_write_key; + } + + MOZ_ASSERT(!mSendSrtp && !mRecvSrtp); + mSendSrtp = + SrtpFlow::Create(cipher_suite, false, write_key, master_key_size); + mRecvSrtp = SrtpFlow::Create(cipher_suite, true, read_key, master_key_size); + if (!mSendSrtp || !mRecvSrtp) { + MOZ_MTLOG(ML_ERROR, "Couldn't create SRTP flow."); + TL_SET_STATE(TS_ERROR); + return; + } + + MOZ_MTLOG(ML_INFO, "Created SRTP flow!"); + } + + TL_SET_STATE(state); +} + +void TransportLayerSrtp::PacketReceived(TransportLayer* layer, + MediaPacket& packet) { + if (state() != TS_OPEN) { + return; + } + + if (!packet.data()) { + // Something ate this, probably the DTLS layer + return; + } + + if (packet.type() != MediaPacket::SRTP && + packet.type() != MediaPacket::SRTCP) { + return; + } + + // We want to keep the encrypted packet around for packet dumping + packet.CopyDataToEncrypted(); + int outLen; + nsresult res; + + if (packet.type() == MediaPacket::SRTP) { + packet.SetType(MediaPacket::RTP); + res = mRecvSrtp->UnprotectRtp(packet.data(), packet.len(), packet.len(), + &outLen); + } else { + packet.SetType(MediaPacket::RTCP); + res = mRecvSrtp->UnprotectRtcp(packet.data(), packet.len(), packet.len(), + &outLen); + } + + if (NS_SUCCEEDED(res)) { + packet.SetLength(outLen); + SignalPacketReceived(this, packet); + } else { + // TODO: What do we do wrt packet dumping here? Maybe signal an empty + // packet? Signal the still-encrypted packet? + MOZ_MTLOG(ML_ERROR, + "Error unprotecting " + << (packet.type() == MediaPacket::RTP ? "RTP" : "RTCP") + << " len=" << packet.len() << "[" << std::hex + << packet.data()[0] << " " << packet.data()[1] << " " + << packet.data()[2] << " " << packet.data()[3] << "]"); + } +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transport/transportlayersrtp.h b/dom/media/webrtc/transport/transportlayersrtp.h new file mode 100644 index 0000000000..a5ba035930 --- /dev/null +++ b/dom/media/webrtc/transport/transportlayersrtp.h @@ -0,0 +1,43 @@ +/* -*- 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/. */ + +#ifndef transportlayersrtp_h__ +#define transportlayersrtp_h__ + +#include <string> + +#include "transportlayer.h" +#include "mozilla/RefPtr.h" +#include "SrtpFlow.h" + +namespace mozilla { + +class TransportLayerDtls; + +class TransportLayerSrtp final : public TransportLayer { + public: + explicit TransportLayerSrtp(TransportLayerDtls& dtls); + virtual ~TransportLayerSrtp() = default; + + // Transport layer overrides. + void WasInserted() override; + TransportResult SendPacket(MediaPacket& packet) override; + + // Signals + void StateChange(TransportLayer* layer, State state); + void PacketReceived(TransportLayer* layer, MediaPacket& packet); + + TRANSPORT_LAYER_ID("srtp") + + private: + bool Setup(); + DISALLOW_COPY_ASSIGN(TransportLayerSrtp); + RefPtr<SrtpFlow> mSendSrtp; + RefPtr<SrtpFlow> mRecvSrtp; +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transportbridge/MediaPipeline.cpp b/dom/media/webrtc/transportbridge/MediaPipeline.cpp new file mode 100644 index 0000000000..6cb0afc36e --- /dev/null +++ b/dom/media/webrtc/transportbridge/MediaPipeline.cpp @@ -0,0 +1,1682 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "MediaPipeline.h" + +#include <inttypes.h> +#include <math.h> +#include <sstream> + +#include "AudioSegment.h" +#include "AudioConverter.h" +#include "DOMMediaStream.h" +#include "ImageContainer.h" +#include "ImageTypes.h" +#include "MediaEngine.h" +#include "MediaSegment.h" +#include "MediaTrackGraphImpl.h" +#include "MediaTrackListener.h" +#include "MediaStreamTrack.h" +#include "jsapi/RemoteTrackSource.h" +#include "RtpLogger.h" +#include "VideoFrameConverter.h" +#include "VideoSegment.h" +#include "VideoStreamTrack.h" +#include "VideoUtils.h" +#include "mozilla/Logging.h" +#include "mozilla/NullPrincipal.h" +#include "mozilla/PeerIdentity.h" +#include "mozilla/Preferences.h" +#include "mozilla/SharedThreadPool.h" +#include "mozilla/Sprintf.h" +#include "mozilla/StaticPrefs_media.h" +#include "mozilla/TaskQueue.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/UniquePtrExtensions.h" +#include "mozilla/dom/RTCStatsReportBinding.h" +#include "mozilla/dom/Document.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/gfx/Types.h" +#include "nsError.h" +#include "nsThreadUtils.h" +#include "transport/runnable_utils.h" +#include "jsapi/MediaTransportHandler.h" +#include "jsapi/PeerConnectionImpl.h" +#include "Tracing.h" +#include "libwebrtcglue/WebrtcImageBuffer.h" +#include "common_video/include/video_frame_buffer.h" +#include "modules/rtp_rtcp/include/rtp_rtcp.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" + +// Max size given stereo is 480*2*2 = 1920 (10ms of 16-bits stereo audio at +// 48KHz) +#define AUDIO_SAMPLE_BUFFER_MAX_BYTES (480 * 2 * 2) +static_assert((WEBRTC_MAX_SAMPLE_RATE / 100) * sizeof(uint16_t) * 2 <= + AUDIO_SAMPLE_BUFFER_MAX_BYTES, + "AUDIO_SAMPLE_BUFFER_MAX_BYTES is not large enough"); + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::gfx; +using namespace mozilla::layers; + +mozilla::LazyLogModule gMediaPipelineLog("MediaPipeline"); + +namespace mozilla { + +// An async inserter for audio data, to avoid running audio codec encoders +// on the MTG/input audio thread. Basically just bounces all the audio +// data to a single audio processing/input queue. We could if we wanted to +// use multiple threads and a TaskQueue. +class AudioProxyThread { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioProxyThread) + + explicit AudioProxyThread(RefPtr<AudioSessionConduit> aConduit) + : mConduit(std::move(aConduit)), + mTaskQueue(TaskQueue::Create( + GetMediaThreadPool(MediaThreadType::WEBRTC_WORKER), "AudioProxy")), + mAudioConverter(nullptr) { + MOZ_ASSERT(mConduit); + MOZ_COUNT_CTOR(AudioProxyThread); + } + + // This function is the identity if aInputRate is supported. + // Else, it returns a rate that is supported, that ensure no loss in audio + // quality: the sampling rate returned is always greater to the inputed + // sampling-rate, if they differ.. + uint32_t AppropriateSendingRateForInputRate(uint32_t aInputRate) { + AudioSessionConduit* conduit = + static_cast<AudioSessionConduit*>(mConduit.get()); + if (conduit->IsSamplingFreqSupported(aInputRate)) { + return aInputRate; + } + if (aInputRate < 16000) { + return 16000; + } + if (aInputRate < 32000) { + return 32000; + } + if (aInputRate < 44100) { + return 44100; + } + return 48000; + } + + // From an arbitrary AudioChunk at sampling-rate aRate, process the audio into + // something the conduit can work with (or send silence if the track is not + // enabled), and send the audio in 10ms chunks to the conduit. + void InternalProcessAudioChunk(TrackRate aRate, const AudioChunk& aChunk, + bool aEnabled) { + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + + // Convert to interleaved 16-bits integer audio, with a maximum of two + // channels (since the WebRTC.org code below makes the assumption that the + // input audio is either mono or stereo), with a sample-rate rate that is + // 16, 32, 44.1, or 48kHz. + uint32_t outputChannels = aChunk.ChannelCount() == 1 ? 1 : 2; + int32_t transmissionRate = AppropriateSendingRateForInputRate(aRate); + + // We take advantage of the fact that the common case (microphone directly + // to PeerConnection, that is, a normal call), the samples are already + // 16-bits mono, so the representation in interleaved and planar is the + // same, and we can just use that. + if (aEnabled && outputChannels == 1 && + aChunk.mBufferFormat == AUDIO_FORMAT_S16 && transmissionRate == aRate) { + const int16_t* samples = aChunk.ChannelData<int16_t>().Elements()[0]; + PacketizeAndSend(samples, transmissionRate, outputChannels, + aChunk.mDuration); + return; + } + + uint32_t sampleCount = aChunk.mDuration * outputChannels; + if (mInterleavedAudio.Length() < sampleCount) { + mInterleavedAudio.SetLength(sampleCount); + } + + if (!aEnabled || aChunk.mBufferFormat == AUDIO_FORMAT_SILENCE) { + PodZero(mInterleavedAudio.Elements(), sampleCount); + } else if (aChunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) { + DownmixAndInterleave(aChunk.ChannelData<float>(), aChunk.mDuration, + aChunk.mVolume, outputChannels, + mInterleavedAudio.Elements()); + } else if (aChunk.mBufferFormat == AUDIO_FORMAT_S16) { + DownmixAndInterleave(aChunk.ChannelData<int16_t>(), aChunk.mDuration, + aChunk.mVolume, outputChannels, + mInterleavedAudio.Elements()); + } + int16_t* inputAudio = mInterleavedAudio.Elements(); + size_t inputAudioFrameCount = aChunk.mDuration; + + AudioConfig inputConfig(AudioConfig::ChannelLayout(outputChannels), aRate, + AudioConfig::FORMAT_S16); + AudioConfig outputConfig(AudioConfig::ChannelLayout(outputChannels), + transmissionRate, AudioConfig::FORMAT_S16); + // Resample to an acceptable sample-rate for the sending side + if (!mAudioConverter || mAudioConverter->InputConfig() != inputConfig || + mAudioConverter->OutputConfig() != outputConfig) { + mAudioConverter = MakeUnique<AudioConverter>(inputConfig, outputConfig); + } + + int16_t* processedAudio = nullptr; + size_t framesProcessed = + mAudioConverter->Process(inputAudio, inputAudioFrameCount); + + if (framesProcessed == 0) { + // In place conversion not possible, use a buffer. + framesProcessed = mAudioConverter->Process(mOutputAudio, inputAudio, + inputAudioFrameCount); + processedAudio = mOutputAudio.Data(); + } else { + processedAudio = inputAudio; + } + + PacketizeAndSend(processedAudio, transmissionRate, outputChannels, + framesProcessed); + } + + // This packetizes aAudioData in 10ms chunks and sends it. + // aAudioData is interleaved audio data at a rate and with a channel count + // that is appropriate to send with the conduit. + void PacketizeAndSend(const int16_t* aAudioData, uint32_t aRate, + uint32_t aChannels, uint32_t aFrameCount) { + MOZ_ASSERT(AppropriateSendingRateForInputRate(aRate) == aRate); + MOZ_ASSERT(aChannels == 1 || aChannels == 2); + MOZ_ASSERT(aAudioData); + + uint32_t audio_10ms = aRate / 100; + + if (!mPacketizer || mPacketizer->mPacketSize != audio_10ms || + mPacketizer->mChannels != aChannels) { + // It's the right thing to drop the bit of audio still in the packetizer: + // we don't want to send to the conduit audio that has two different + // rates while telling it that it has a constante rate. + mPacketizer = + MakeUnique<AudioPacketizer<int16_t, int16_t>>(audio_10ms, aChannels); + mPacket = MakeUnique<int16_t[]>(audio_10ms * aChannels); + } + + mPacketizer->Input(aAudioData, aFrameCount); + + while (mPacketizer->PacketsAvailable()) { + mPacketizer->Output(mPacket.get()); + auto frame = std::make_unique<webrtc::AudioFrame>(); + // UpdateFrame makes a copy of the audio data. + frame->UpdateFrame(frame->timestamp_, mPacket.get(), + mPacketizer->mPacketSize, aRate, frame->speech_type_, + frame->vad_activity_, mPacketizer->mChannels); + mConduit->SendAudioFrame(std::move(frame)); + } + } + + void QueueAudioChunk(TrackRate aRate, const AudioChunk& aChunk, + bool aEnabled) { + RefPtr<AudioProxyThread> self = this; + nsresult rv = mTaskQueue->Dispatch(NS_NewRunnableFunction( + "AudioProxyThread::QueueAudioChunk", [self, aRate, aChunk, aEnabled]() { + self->InternalProcessAudioChunk(aRate, aChunk, aEnabled); + })); + MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); + Unused << rv; + } + + protected: + virtual ~AudioProxyThread() { MOZ_COUNT_DTOR(AudioProxyThread); } + + const RefPtr<AudioSessionConduit> mConduit; + const RefPtr<TaskQueue> mTaskQueue; + // Only accessed on mTaskQueue + UniquePtr<AudioPacketizer<int16_t, int16_t>> mPacketizer; + // A buffer to hold a single packet of audio. + UniquePtr<int16_t[]> mPacket; + nsTArray<int16_t> mInterleavedAudio; + AlignedShortBuffer mOutputAudio; + UniquePtr<AudioConverter> mAudioConverter; +}; + +MediaPipeline::MediaPipeline(const std::string& aPc, + RefPtr<MediaTransportHandler> aTransportHandler, + DirectionType aDirection, + RefPtr<AbstractThread> aCallThread, + RefPtr<nsISerialEventTarget> aStsThread, + RefPtr<MediaSessionConduit> aConduit) + : mConduit(std::move(aConduit)), + mDirection(aDirection), + mCallThread(std::move(aCallThread)), + mStsThread(aStsThread), + mActive(false, "MediaPipeline::mActive"), + mLevel(0), + mTransportHandler(std::move(aTransportHandler)), + mRtpPacketsSent(0), + mRtcpPacketsSent(0), + mRtpPacketsReceived(0), + mRtcpPacketsReceived(0), + mRtpBytesSent(0), + mRtpBytesReceived(0), + mPc(aPc), + mFilter(), + mRtpHeaderExtensionMap(new webrtc::RtpHeaderExtensionMap()), + mPacketDumper(PacketDumper::GetPacketDumper(mPc)) {} + +MediaPipeline::~MediaPipeline() { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("Destroying MediaPipeline: %s", mDescription.c_str())); +} + +void MediaPipeline::Start() { + MOZ_ASSERT(NS_IsMainThread()); + mActive = true; +} + +void MediaPipeline::Stop() { + MOZ_ASSERT(NS_IsMainThread()); + mActive = false; +} + +void MediaPipeline::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + Stop(); + + RUN_ON_THREAD(mStsThread, + WrapRunnable(RefPtr<MediaPipeline>(this), + &MediaPipeline::DetachTransport_s), + NS_DISPATCH_NORMAL); +} + +void MediaPipeline::DetachTransport_s() { + ASSERT_ON_THREAD(mStsThread); + + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("%s in %s", mDescription.c_str(), __FUNCTION__)); + + disconnect_all(); + mRtpState = TransportLayer::TS_NONE; + mRtcpState = TransportLayer::TS_NONE; + mTransportId.clear(); + mConduit->SetTransportActive(false); + mRtpSendEventListener.DisconnectIfExists(); + mSenderRtcpSendEventListener.DisconnectIfExists(); + mReceiverRtcpSendEventListener.DisconnectIfExists(); +} + +void MediaPipeline::UpdateTransport_m( + const std::string& aTransportId, UniquePtr<MediaPipelineFilter>&& aFilter) { + mStsThread->Dispatch(NS_NewRunnableFunction( + __func__, [aTransportId, filter = std::move(aFilter), + self = RefPtr<MediaPipeline>(this)]() mutable { + self->UpdateTransport_s(aTransportId, std::move(filter)); + })); +} + +void MediaPipeline::UpdateTransport_s( + const std::string& aTransportId, UniquePtr<MediaPipelineFilter>&& aFilter) { + ASSERT_ON_THREAD(mStsThread); + if (!mSignalsConnected) { + mTransportHandler->SignalStateChange.connect( + this, &MediaPipeline::RtpStateChange); + mTransportHandler->SignalRtcpStateChange.connect( + this, &MediaPipeline::RtcpStateChange); + mTransportHandler->SignalEncryptedSending.connect( + this, &MediaPipeline::EncryptedPacketSending); + mTransportHandler->SignalPacketReceived.connect( + this, &MediaPipeline::PacketReceived); + mTransportHandler->SignalAlpnNegotiated.connect( + this, &MediaPipeline::AlpnNegotiated); + mSignalsConnected = true; + } + + if (aTransportId != mTransportId) { + mTransportId = aTransportId; + mRtpState = mTransportHandler->GetState(mTransportId, false); + mRtcpState = mTransportHandler->GetState(mTransportId, true); + CheckTransportStates(); + } + + if (mFilter) { + for (const auto& extension : mFilter->GetExtmap()) { + mRtpHeaderExtensionMap->Deregister(extension.uri); + } + } + if (mFilter && aFilter) { + // Use the new filter, but don't forget any remote SSRCs that we've learned + // by receiving traffic. + mFilter->Update(*aFilter); + } else { + mFilter = std::move(aFilter); + } + if (mFilter) { + for (const auto& extension : mFilter->GetExtmap()) { + mRtpHeaderExtensionMap->RegisterByUri(extension.id, extension.uri); + } + } +} + +void MediaPipeline::GetContributingSourceStats( + const nsString& aInboundRtpStreamId, + FallibleTArray<dom::RTCRTPContributingSourceStats>& aArr) const { + ASSERT_ON_THREAD(mStsThread); + // Get the expiry from now + DOMHighResTimeStamp expiry = + RtpCSRCStats::GetExpiryFromTime(GetTimestampMaker().GetNow()); + for (auto info : mCsrcStats) { + if (!info.second.Expired(expiry)) { + RTCRTPContributingSourceStats stats; + info.second.GetWebidlInstance(stats, aInboundRtpStreamId); + if (!aArr.AppendElement(stats, fallible)) { + mozalloc_handle_oom(0); + } + } + } +} + +void MediaPipeline::RtpStateChange(const std::string& aTransportId, + TransportLayer::State aState) { + if (mTransportId != aTransportId) { + return; + } + mRtpState = aState; + CheckTransportStates(); +} + +void MediaPipeline::RtcpStateChange(const std::string& aTransportId, + TransportLayer::State aState) { + if (mTransportId != aTransportId) { + return; + } + mRtcpState = aState; + CheckTransportStates(); +} + +void MediaPipeline::CheckTransportStates() { + ASSERT_ON_THREAD(mStsThread); + + if (mRtpState == TransportLayer::TS_CLOSED || + mRtpState == TransportLayer::TS_ERROR || + mRtcpState == TransportLayer::TS_CLOSED || + mRtcpState == TransportLayer::TS_ERROR) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Warning, + ("RTP Transport failed for pipeline %p flow %s", this, + mDescription.c_str())); + + 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 + // have experienced an error. + mConduit->SetTransportActive(false); + mRtpSendEventListener.DisconnectIfExists(); + mSenderRtcpSendEventListener.DisconnectIfExists(); + mReceiverRtcpSendEventListener.DisconnectIfExists(); + return; + } + + if (mRtpState == TransportLayer::TS_OPEN) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("RTP Transport ready for pipeline %p flow %s", this, + mDescription.c_str())); + } + + if (mRtcpState == TransportLayer::TS_OPEN) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("RTCP Transport ready for pipeline %p flow %s", this, + mDescription.c_str())); + } + + if (mRtpState == TransportLayer::TS_OPEN && mRtcpState == mRtpState) { + if (mDirection == DirectionType::TRANSMIT) { + mConduit->ConnectSenderRtcpEvent(mSenderRtcpReceiveEvent); + mRtpSendEventListener = mConduit->SenderRtpSendEvent().Connect( + mStsThread, this, &MediaPipeline::SendPacket); + mSenderRtcpSendEventListener = mConduit->SenderRtcpSendEvent().Connect( + mStsThread, this, &MediaPipeline::SendPacket); + } else { + mConduit->ConnectReceiverRtcpEvent(mReceiverRtcpReceiveEvent); + mConduit->ConnectReceiverRtpEvent(mRtpReceiveEvent); + mReceiverRtcpSendEventListener = + mConduit->ReceiverRtcpSendEvent().Connect(mStsThread, this, + &MediaPipeline::SendPacket); + } + mConduit->SetTransportActive(true); + TransportReady_s(); + } +} + +void MediaPipeline::SendPacket(MediaPacket&& aPacket) { + ASSERT_ON_THREAD(mStsThread); + + const bool isRtp = aPacket.type() == MediaPacket::RTP; + + if (isRtp && mRtpState != TransportLayer::TS_OPEN) { + return; + } + + if (!isRtp && mRtcpState != TransportLayer::TS_OPEN) { + return; + } + + aPacket.sdp_level() = Some(Level()); + + if (RtpLogger::IsPacketLoggingOn()) { + RtpLogger::LogPacket(aPacket, false, mDescription); + } + + if (isRtp) { + mPacketDumper->Dump(Level(), dom::mozPacketDumpType::Rtp, true, + aPacket.data(), aPacket.len()); + IncrementRtpPacketsSent(aPacket); + } else { + mPacketDumper->Dump(Level(), dom::mozPacketDumpType::Rtcp, true, + aPacket.data(), aPacket.len()); + IncrementRtcpPacketsSent(); + } + + MOZ_LOG( + gMediaPipelineLog, LogLevel::Debug, + ("%s sending %s packet", mDescription.c_str(), (isRtp ? "RTP" : "RTCP"))); + + mTransportHandler->SendPacket(mTransportId, std::move(aPacket)); +} + +void MediaPipeline::IncrementRtpPacketsSent(const MediaPacket& aPacket) { + ASSERT_ON_THREAD(mStsThread); + ++mRtpPacketsSent; + mRtpBytesSent += aPacket.len(); + + if (!(mRtpPacketsSent % 100)) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("RTP sent packet count for %s Pipeline %p: %u (%" PRId64 " bytes)", + mDescription.c_str(), this, mRtpPacketsSent, mRtpBytesSent)); + } +} + +void MediaPipeline::IncrementRtcpPacketsSent() { + ASSERT_ON_THREAD(mStsThread); + ++mRtcpPacketsSent; + if (!(mRtcpPacketsSent % 100)) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("RTCP sent packet count for %s Pipeline %p: %u", + mDescription.c_str(), this, mRtcpPacketsSent)); + } +} + +void MediaPipeline::IncrementRtpPacketsReceived(int32_t aBytes) { + ASSERT_ON_THREAD(mStsThread); + ++mRtpPacketsReceived; + mRtpBytesReceived += aBytes; + if (!(mRtpPacketsReceived % 100)) { + MOZ_LOG( + gMediaPipelineLog, LogLevel::Info, + ("RTP received packet count for %s Pipeline %p: %u (%" PRId64 " bytes)", + mDescription.c_str(), this, mRtpPacketsReceived, mRtpBytesReceived)); + } +} + +void MediaPipeline::IncrementRtcpPacketsReceived() { + ASSERT_ON_THREAD(mStsThread); + ++mRtcpPacketsReceived; + if (!(mRtcpPacketsReceived % 100)) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("RTCP received packet count for %s Pipeline %p: %u", + mDescription.c_str(), this, mRtcpPacketsReceived)); + } +} + +void MediaPipeline::RtpPacketReceived(const MediaPacket& packet) { + ASSERT_ON_THREAD(mStsThread); + + if (mDirection == DirectionType::TRANSMIT) { + return; + } + + if (!packet.len()) { + return; + } + + webrtc::RTPHeader header; + rtc::CopyOnWriteBuffer packet_buffer(packet.data(), packet.len()); + webrtc::RtpPacketReceived pktHeader(mRtpHeaderExtensionMap.get()); + if (!pktHeader.Parse(packet_buffer)) { + return; + } + pktHeader.GetHeader(&header); + + if (mFilter && !mFilter->Filter(header)) { + return; + } + + // Make sure to only get the time once, and only if we need it by + // using getTimestamp() for access + DOMHighResTimeStamp now = 0.0; + bool hasTime = false; + + // Remove expired RtpCSRCStats + if (!mCsrcStats.empty()) { + if (!hasTime) { + now = GetTimestampMaker().GetNow(); + hasTime = true; + } + auto expiry = RtpCSRCStats::GetExpiryFromTime(now); + for (auto p = mCsrcStats.begin(); p != mCsrcStats.end();) { + if (p->second.Expired(expiry)) { + p = mCsrcStats.erase(p); + continue; + } + p++; + } + } + + // Add new RtpCSRCStats + if (header.numCSRCs) { + for (auto i = 0; i < header.numCSRCs; i++) { + if (!hasTime) { + now = GetTimestampMaker().GetNow(); + hasTime = true; + } + auto csrcInfo = mCsrcStats.find(header.arrOfCSRCs[i]); + if (csrcInfo == mCsrcStats.end()) { + mCsrcStats.insert(std::make_pair( + header.arrOfCSRCs[i], RtpCSRCStats(header.arrOfCSRCs[i], now))); + } else { + csrcInfo->second.SetTimestamp(now); + } + } + } + + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("%s received RTP packet.", mDescription.c_str())); + IncrementRtpPacketsReceived(packet.len()); + OnRtpPacketReceived(); + + RtpLogger::LogPacket(packet, true, mDescription); + + // Might be nice to pass ownership of the buffer in this case, but it is a + // small optimization in a rare case. + mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Srtp, false, + packet.encrypted_data(), packet.encrypted_len()); + + mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Rtp, false, packet.data(), + packet.len()); + + mRtpReceiveEvent.Notify(packet.Clone(), header); +} + +void MediaPipeline::RtcpPacketReceived(const MediaPacket& packet) { + ASSERT_ON_THREAD(mStsThread); + + if (!packet.len()) { + return; + } + + // We do not filter RTCP. This is because a compound RTCP packet can contain + // any collection of RTCP packets, and webrtc.org already knows how to filter + // out what it is interested in, and what it is not. Maybe someday we should + // have a TransportLayer that breaks up compound RTCP so we can filter them + // individually, but I doubt that will matter much. + + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("%s received RTCP packet.", mDescription.c_str())); + IncrementRtcpPacketsReceived(); + + RtpLogger::LogPacket(packet, true, mDescription); + + // Might be nice to pass ownership of the buffer in this case, but it is a + // small optimization in a rare case. + mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Srtcp, false, + packet.encrypted_data(), packet.encrypted_len()); + + mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Rtcp, false, + packet.data(), packet.len()); + + if (StaticPrefs::media_webrtc_net_force_disable_rtcp_reception()) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("%s RTCP packet forced to be dropped", mDescription.c_str())); + return; + } + + if (mDirection == DirectionType::TRANSMIT) { + mSenderRtcpReceiveEvent.Notify(packet.Clone()); + } else { + mReceiverRtcpReceiveEvent.Notify(packet.Clone()); + } +} + +void MediaPipeline::PacketReceived(const std::string& aTransportId, + const MediaPacket& packet) { + ASSERT_ON_THREAD(mStsThread); + + if (mTransportId != aTransportId) { + return; + } + + MOZ_ASSERT(mRtpState == TransportLayer::TS_OPEN); + MOZ_ASSERT(mRtcpState == mRtpState); + + switch (packet.type()) { + case MediaPacket::RTP: + RtpPacketReceived(packet); + break; + case MediaPacket::RTCP: + RtcpPacketReceived(packet); + break; + default:; + } +} + +void MediaPipeline::AlpnNegotiated(const std::string& aAlpn, + bool aPrivacyRequested) { + ASSERT_ON_THREAD(mStsThread); + + if (aPrivacyRequested) { + MakePrincipalPrivate_s(); + } +} + +void MediaPipeline::EncryptedPacketSending(const std::string& aTransportId, + const MediaPacket& aPacket) { + ASSERT_ON_THREAD(mStsThread); + + if (mTransportId == aTransportId) { + dom::mozPacketDumpType type; + if (aPacket.type() == MediaPacket::SRTP) { + type = dom::mozPacketDumpType::Srtp; + } else if (aPacket.type() == MediaPacket::SRTCP) { + type = dom::mozPacketDumpType::Srtcp; + } else if (aPacket.type() == MediaPacket::DTLS) { + // TODO(bug 1497936): Implement packet dump for DTLS + return; + } else { + MOZ_ASSERT(false); + return; + } + mPacketDumper->Dump(Level(), type, true, aPacket.data(), aPacket.len()); + } +} + +class MediaPipelineTransmit::PipelineListener + : public DirectMediaTrackListener { + friend class MediaPipelineTransmit; + + public: + explicit PipelineListener(RefPtr<MediaSessionConduit> aConduit) + : mConduit(std::move(aConduit)), + mActive(false), + mEnabled(false), + mDirectConnect(false) {} + + ~PipelineListener() { + if (mConverter) { + mConverter->Shutdown(); + } + } + + void SetActive(bool aActive) { + mActive = aActive; + if (mConverter) { + mConverter->SetActive(aActive); + } + } + void SetEnabled(bool aEnabled) { mEnabled = aEnabled; } + + // These are needed since nested classes don't have access to any particular + // instance of the parent + void SetAudioProxy(RefPtr<AudioProxyThread> aProxy) { + mAudioProcessing = std::move(aProxy); + } + + void SetVideoFrameConverter(RefPtr<VideoFrameConverter> aConverter) { + mConverter = std::move(aConverter); + } + + void OnVideoFrameConverted(webrtc::VideoFrame aVideoFrame) { + MOZ_RELEASE_ASSERT(mConduit->type() == MediaSessionConduit::VIDEO); + static_cast<VideoSessionConduit*>(mConduit.get()) + ->SendVideoFrame(std::move(aVideoFrame)); + } + + // Implement MediaTrackListener + void NotifyQueuedChanges(MediaTrackGraph* aGraph, TrackTime aOffset, + const MediaSegment& aQueuedMedia) override; + void NotifyEnabledStateChanged(MediaTrackGraph* aGraph, + bool aEnabled) override; + + // Implement DirectMediaTrackListener + void NotifyRealtimeTrackData(MediaTrackGraph* aGraph, TrackTime aOffset, + const MediaSegment& aMedia) override; + void NotifyDirectListenerInstalled(InstallationResult aResult) override; + void NotifyDirectListenerUninstalled() override; + + private: + void NewData(const MediaSegment& aMedia, TrackRate aRate = 0); + + const RefPtr<MediaSessionConduit> mConduit; + RefPtr<AudioProxyThread> mAudioProcessing; + RefPtr<VideoFrameConverter> mConverter; + + // active is true if there is a transport to send on + mozilla::Atomic<bool> mActive; + // enabled is true if the media access control permits sending + // actual content; when false you get black/silence + mozilla::Atomic<bool> mEnabled; + + // Written and read on the MediaTrackGraph thread + bool mDirectConnect; +}; + +MediaPipelineTransmit::MediaPipelineTransmit( + const std::string& aPc, RefPtr<MediaTransportHandler> aTransportHandler, + RefPtr<AbstractThread> aCallThread, RefPtr<nsISerialEventTarget> aStsThread, + bool aIsVideo, RefPtr<MediaSessionConduit> aConduit) + : MediaPipeline(aPc, std::move(aTransportHandler), DirectionType::TRANSMIT, + std::move(aCallThread), std::move(aStsThread), + std::move(aConduit)), + mWatchManager(this, AbstractThread::MainThread()), + mIsVideo(aIsVideo), + mListener(new PipelineListener(mConduit)), + mDomTrack(nullptr, "MediaPipelineTransmit::mDomTrack"), + mSendTrackOverride(nullptr, "MediaPipelineTransmit::mSendTrackOverride") { + if (!IsVideo()) { + mAudioProcessing = + MakeAndAddRef<AudioProxyThread>(*mConduit->AsAudioSessionConduit()); + mListener->SetAudioProxy(mAudioProcessing); + } else { // Video + mConverter = MakeAndAddRef<VideoFrameConverter>(GetTimestampMaker()); + mFrameListener = mConverter->VideoFrameConvertedEvent().Connect( + mConverter->mTaskQueue, + [listener = mListener](webrtc::VideoFrame aFrame) { + listener->OnVideoFrameConverted(std::move(aFrame)); + }); + mListener->SetVideoFrameConverter(mConverter); + } + + mWatchManager.Watch(mActive, &MediaPipelineTransmit::UpdateSendState); + mWatchManager.Watch(mDomTrack, &MediaPipelineTransmit::UpdateSendState); + mWatchManager.Watch(mSendTrackOverride, + &MediaPipelineTransmit::UpdateSendState); + + mDescription = GenerateDescription(); +} + +MediaPipelineTransmit::~MediaPipelineTransmit() { + mFrameListener.DisconnectIfExists(); + + MOZ_ASSERT(!mTransmitting); + MOZ_ASSERT(!mDomTrack.Ref()); +} + +void MediaPipelineTransmit::Shutdown() { + MediaPipeline::Shutdown(); + MOZ_ASSERT(!mActive); + mWatchManager.Shutdown(); + if (mDomTrack.Ref()) { + mDomTrack.Ref()->RemovePrincipalChangeObserver(this); + mDomTrack = nullptr; + } + mUnsettingSendTrack = false; + UpdateSendState(); + MOZ_ASSERT(!mTransmitting); +} + +void MediaPipeline::SetDescription_s(const std::string& description) { + ASSERT_ON_THREAD(mStsThread); + mDescription = description; +} + +std::string MediaPipelineTransmit::GenerateDescription() const { + MOZ_ASSERT(NS_IsMainThread()); + + std::stringstream description; + description << mPc << "| "; + description << (mIsVideo ? "Transmit video[" : "Transmit audio["); + + if (mDomTrack.Ref()) { + nsString nsTrackId; + mDomTrack.Ref()->GetId(nsTrackId); + description << NS_ConvertUTF16toUTF8(nsTrackId).get(); + } else if (mSendTrackOverride.Ref()) { + description << "override " << mSendTrackOverride.Ref().get(); + } else { + description << "no track"; + } + + description << "]"; + + return description.str(); +} + +void MediaPipelineTransmit::UpdateSendState() { + MOZ_ASSERT(NS_IsMainThread()); + + // This runs because either mActive, mDomTrack or mSendTrackOverride changed, + // or because mSendTrack was unset async. Based on these inputs this method + // is responsible for hooking up mSendTrack to mListener in order to feed data + // to the conduit. + // + // If we are inactive, or if the send track does not match what we want to + // send (mDomTrack or mSendTrackOverride), we must stop feeding data to the + // conduit. NB that removing the listener from mSendTrack is async, and we + // must wait for it to resolve before adding mListener to another track. + // mUnsettingSendTrack gates us until the listener has been removed from + // mSendTrack. + // + // If we are active and the send track does match what we want to send, we + // make sure mListener is added to the send track. Either now, or if we're + // still waiting for another send track to be removed, during a future call to + // this method. + + if (mUnsettingSendTrack) { + // We must wait for the send track to be unset before we can set it again, + // to avoid races. Once unset this function is triggered again. + return; + } + + const bool wasTransmitting = mTransmitting; + + const bool haveLiveSendTrack = mSendTrack && !mSendTrack->IsDestroyed(); + const bool haveLiveDomTrack = mDomTrack.Ref() && !mDomTrack.Ref()->Ended(); + const bool haveLiveOverrideTrack = + mSendTrackOverride.Ref() && !mSendTrackOverride.Ref()->IsDestroyed(); + const bool mustRemoveSendTrack = + haveLiveSendTrack && !mSendTrackOverride.Ref() && + (!haveLiveDomTrack || mDomTrack.Ref()->GetTrack() != mSendPortSource); + + mTransmitting = mActive && (haveLiveDomTrack || haveLiveOverrideTrack) && + !mustRemoveSendTrack; + + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("MediaPipeline %p UpdateSendState wasTransmitting=%d, active=%d, " + "sendTrack=%p (%s), domTrack=%p (%s), " + "sendTrackOverride=%p (%s), mustRemove=%d, mTransmitting=%d", + this, wasTransmitting, mActive.Ref(), mSendTrack.get(), + haveLiveSendTrack ? "live" : "ended", mDomTrack.Ref().get(), + haveLiveDomTrack ? "live" : "ended", mSendTrackOverride.Ref().get(), + haveLiveOverrideTrack ? "live" : "ended", mustRemoveSendTrack, + mTransmitting)); + + if (!wasTransmitting && mTransmitting) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("Attaching pipeline %p to track %p conduit type=%s", this, + mDomTrack.Ref().get(), mIsVideo ? "video" : "audio")); + if (mDescriptionInvalidated) { + // Only update the description when we attach to a track, as detaching is + // always a longer async step than updating the description. Updating on + // detach would cause the wrong track id to be attributed in logs. + RUN_ON_THREAD(mStsThread, + WrapRunnable(RefPtr<MediaPipeline>(this), + &MediaPipelineTransmit::SetDescription_s, + GenerateDescription()), + NS_DISPATCH_NORMAL); + mDescriptionInvalidated = false; + } + if (mSendTrackOverride.Ref()) { + // Special path that allows unittests to avoid mDomTrack and the graph by + // manually calling SetSendTrack. + mSendTrack = mSendTrackOverride.Ref(); + } else { + mSendTrack = mDomTrack.Ref()->Graph()->CreateForwardedInputTrack( + mDomTrack.Ref()->GetTrack()->mType); + mSendPortSource = mDomTrack.Ref()->GetTrack(); + mSendPort = mSendTrack->AllocateInputPort(mSendPortSource.get()); + } + if (mIsVideo) { + mConverter->SetTrackingId(mDomTrack.Ref()->GetSource().mTrackingId); + } + mSendTrack->QueueSetAutoend(false); + if (mIsVideo) { + mSendTrack->AddDirectListener(mListener); + } + mSendTrack->AddListener(mListener); + } + + if (wasTransmitting && !mTransmitting) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("Detaching pipeline %p from track %p conduit type=%s", this, + mDomTrack.Ref().get(), mIsVideo ? "video" : "audio")); + mUnsettingSendTrack = true; + if (mIsVideo) { + mSendTrack->RemoveDirectListener(mListener); + } + mSendTrack->RemoveListener(mListener)->Then( + GetMainThreadSerialEventTarget(), __func__, + [this, self = RefPtr<MediaPipelineTransmit>(this)] { + mUnsettingSendTrack = false; + mSendTrack = nullptr; + if (!mWatchManager.IsShutdown()) { + mWatchManager.ManualNotify(&MediaPipelineTransmit::UpdateSendState); + } + }); + if (!mSendTrackOverride.Ref()) { + // If an override is set it may be re-used. + mSendTrack->Destroy(); + mSendPort->Destroy(); + mSendPort = nullptr; + mSendPortSource = nullptr; + } + } +} + +bool MediaPipelineTransmit::Transmitting() const { + MOZ_ASSERT(NS_IsMainThread()); + + return mActive; +} + +bool MediaPipelineTransmit::IsVideo() const { return mIsVideo; } + +void MediaPipelineTransmit::PrincipalChanged(dom::MediaStreamTrack* aTrack) { + MOZ_ASSERT(aTrack && aTrack == mDomTrack.Ref()); + + PeerConnectionWrapper pcw(mPc); + if (pcw.impl()) { + Document* doc = pcw.impl()->GetParentObject()->GetExtantDoc(); + if (doc) { + UpdateSinkIdentity(doc->NodePrincipal(), pcw.impl()->GetPeerIdentity()); + } else { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("Can't update sink principal; document gone")); + } + } +} + +void MediaPipelineTransmit::UpdateSinkIdentity( + nsIPrincipal* aPrincipal, const PeerIdentity* aSinkIdentity) { + MOZ_ASSERT(NS_IsMainThread()); + + if (!mDomTrack.Ref()) { + // Nothing to do here + return; + } + + bool enableTrack = aPrincipal->Subsumes(mDomTrack.Ref()->GetPrincipal()); + if (!enableTrack) { + // first try didn't work, but there's a chance that this is still available + // if our track is bound to a peerIdentity, and the peer connection (our + // sink) is bound to the same identity, then we can enable the track. + const PeerIdentity* trackIdentity = mDomTrack.Ref()->GetPeerIdentity(); + if (aSinkIdentity && trackIdentity) { + enableTrack = (*aSinkIdentity == *trackIdentity); + } + } + + mListener->SetEnabled(enableTrack); +} + +void MediaPipelineTransmit::TransportReady_s() { + ASSERT_ON_THREAD(mStsThread); + // Call base ready function. + MediaPipeline::TransportReady_s(); + mListener->SetActive(true); +} + +nsresult MediaPipelineTransmit::SetTrack(RefPtr<MediaStreamTrack> aDomTrack) { + MOZ_ASSERT(NS_IsMainThread()); + if (mDomTrack.Ref()) { + mDomTrack.Ref()->RemovePrincipalChangeObserver(this); + } + + if (aDomTrack) { + nsString nsTrackId; + aDomTrack->GetId(nsTrackId); + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("Reattaching pipeline to track %p track %s conduit type: %s", + aDomTrack.get(), NS_ConvertUTF16toUTF8(nsTrackId).get(), + mIsVideo ? "video" : "audio")); + } + + mDescriptionInvalidated = true; + mDomTrack = std::move(aDomTrack); + if (mDomTrack.Ref()) { + mDomTrack.Ref()->AddPrincipalChangeObserver(this); + PrincipalChanged(mDomTrack.Ref()); + } + + return NS_OK; +} + +RefPtr<dom::MediaStreamTrack> MediaPipelineTransmit::GetTrack() const { + MOZ_ASSERT(NS_IsMainThread()); + return mDomTrack; +} + +void MediaPipelineTransmit::SetSendTrackOverride( + RefPtr<ProcessedMediaTrack> aSendTrack) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(!mSendTrack); + MOZ_RELEASE_ASSERT(!mSendPort); + MOZ_RELEASE_ASSERT(!mSendTrackOverride.Ref()); + mDescriptionInvalidated = true; + mSendTrackOverride = std::move(aSendTrack); +} + +// Called if we're attached with AddDirectListener() +void MediaPipelineTransmit::PipelineListener::NotifyRealtimeTrackData( + MediaTrackGraph* aGraph, TrackTime aOffset, const MediaSegment& aMedia) { + MOZ_LOG( + gMediaPipelineLog, LogLevel::Debug, + ("MediaPipeline::NotifyRealtimeTrackData() listener=%p, offset=%" PRId64 + ", duration=%" PRId64, + this, aOffset, aMedia.GetDuration())); + TRACE_COMMENT( + "MediaPipelineTransmit::PipelineListener::NotifyRealtimeTrackData", "%s", + aMedia.GetType() == MediaSegment::VIDEO ? "Video" : "Audio"); + NewData(aMedia, aGraph->GraphRate()); +} + +void MediaPipelineTransmit::PipelineListener::NotifyQueuedChanges( + MediaTrackGraph* aGraph, TrackTime aOffset, + const MediaSegment& aQueuedMedia) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("MediaPipeline::NotifyQueuedChanges()")); + + if (aQueuedMedia.GetType() == MediaSegment::VIDEO) { + // We always get video from the direct listener. + return; + } + + TRACE("MediaPipelineTransmit::PipelineListener::NotifyQueuedChanges (Audio)"); + + if (mDirectConnect) { + // ignore non-direct data if we're also getting direct data + return; + } + + size_t rate; + if (aGraph) { + rate = aGraph->GraphRate(); + } else { + // When running tests, graph may be null. In that case use a default. + rate = 16000; + } + NewData(aQueuedMedia, rate); +} + +void MediaPipelineTransmit::PipelineListener::NotifyEnabledStateChanged( + MediaTrackGraph* aGraph, bool aEnabled) { + if (mConduit->type() != MediaSessionConduit::VIDEO) { + return; + } + MOZ_ASSERT(mConverter); + mConverter->SetTrackEnabled(aEnabled); +} + +void MediaPipelineTransmit::PipelineListener::NotifyDirectListenerInstalled( + InstallationResult aResult) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("MediaPipeline::NotifyDirectListenerInstalled() listener=%p," + " result=%d", + this, static_cast<int32_t>(aResult))); + + mDirectConnect = InstallationResult::SUCCESS == aResult; +} + +void MediaPipelineTransmit::PipelineListener:: + NotifyDirectListenerUninstalled() { + MOZ_LOG( + gMediaPipelineLog, LogLevel::Info, + ("MediaPipeline::NotifyDirectListenerUninstalled() listener=%p", this)); + + if (mConduit->type() == MediaSessionConduit::VIDEO) { + // Reset the converter's track-enabled state. If re-added to a new track + // later and that track is disabled, we will be signaled explicitly. + MOZ_ASSERT(mConverter); + mConverter->SetTrackEnabled(true); + } + + mDirectConnect = false; +} + +void MediaPipelineTransmit::PipelineListener::NewData( + const MediaSegment& aMedia, TrackRate aRate /* = 0 */) { + if (mConduit->type() != (aMedia.GetType() == MediaSegment::AUDIO + ? MediaSessionConduit::AUDIO + : MediaSessionConduit::VIDEO)) { + MOZ_ASSERT(false, + "The media type should always be correct since the " + "listener is locked to a specific track"); + return; + } + + // TODO(ekr@rtfm.com): For now assume that we have only one + // track type and it's destined for us + // See bug 784517 + if (aMedia.GetType() == MediaSegment::AUDIO) { + MOZ_RELEASE_ASSERT(aRate > 0); + + if (!mActive) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("Discarding audio packets because transport not ready")); + return; + } + + const AudioSegment* audio = static_cast<const AudioSegment*>(&aMedia); + for (AudioSegment::ConstChunkIterator iter(*audio); !iter.IsEnded(); + iter.Next()) { + mAudioProcessing->QueueAudioChunk(aRate, *iter, mEnabled); + } + } else { + const VideoSegment* video = static_cast<const VideoSegment*>(&aMedia); + + for (VideoSegment::ConstChunkIterator iter(*video); !iter.IsEnded(); + iter.Next()) { + mConverter->QueueVideoChunk(*iter, !mEnabled); + } + } +} + +class GenericReceiveListener : public MediaTrackListener { + public: + explicit GenericReceiveListener(const RefPtr<dom::MediaStreamTrack>& aTrack) + : mTrackSource(new nsMainThreadPtrHolder<RemoteTrackSource>( + "GenericReceiveListener::mTrackSource", + &static_cast<RemoteTrackSource&>(aTrack->GetSource()))), + mSource(mTrackSource->mStream), + mTrackingId(mTrackSource->mTrackingId), + mIsAudio(aTrack->AsAudioStreamTrack()), + mEnabled(false), + mMaybeTrackNeedsUnmute(true) { + MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); + MOZ_DIAGNOSTIC_ASSERT(mSource, "Must be used with a SourceMediaTrack"); + } + + virtual ~GenericReceiveListener() = default; + + void SetEnabled(bool aEnabled) { + if (mEnabled == aEnabled) { + return; + } + mEnabled = aEnabled; + if (aEnabled) { + mMaybeTrackNeedsUnmute = true; + } + if (mIsAudio && !mSource->IsDestroyed()) { + mSource->SetPullingEnabled(mEnabled); + } + } + + void OnRtpReceived() { + if (mMaybeTrackNeedsUnmute) { + mMaybeTrackNeedsUnmute = false; + GetMainThreadEventTarget()->Dispatch( + NewRunnableMethod("GenericReceiveListener::OnRtpReceived_m", this, + &GenericReceiveListener::OnRtpReceived_m)); + } + } + + void OnRtpReceived_m() { + if (mEnabled) { + mTrackSource->SetMuted(false); + } + } + + void EndTrack() { + MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, + ("GenericReceiveListener ending track")); + + if (!mSource->IsDestroyed()) { + // This breaks the cycle with the SourceMediaTrack + mSource->RemoveListener(this); + mSource->End(); + mSource->Destroy(); + } + + GetMainThreadEventTarget()->Dispatch( + NewRunnableMethod("RemoteTrackSource::ForceEnded", mTrackSource.get(), + &RemoteTrackSource::ForceEnded)); + } + + protected: + const nsMainThreadPtrHandle<RemoteTrackSource> mTrackSource; + const RefPtr<SourceMediaTrack> mSource; + const TrackingId mTrackingId; + const bool mIsAudio; + // Main thread only. + bool mEnabled; + // Any thread. + Atomic<bool> mMaybeTrackNeedsUnmute; +}; + +MediaPipelineReceive::MediaPipelineReceive( + const std::string& aPc, RefPtr<MediaTransportHandler> aTransportHandler, + RefPtr<AbstractThread> aCallThread, RefPtr<nsISerialEventTarget> aStsThread, + RefPtr<MediaSessionConduit> aConduit) + : MediaPipeline(aPc, std::move(aTransportHandler), DirectionType::RECEIVE, + std::move(aCallThread), std::move(aStsThread), + std::move(aConduit)) {} + +MediaPipelineReceive::~MediaPipelineReceive() = default; + +class MediaPipelineReceiveAudio::PipelineListener + : public GenericReceiveListener { + public: + PipelineListener(const RefPtr<dom::MediaStreamTrack>& aTrack, + RefPtr<MediaSessionConduit> aConduit, + const PrincipalHandle& aPrincipalHandle) + : GenericReceiveListener(aTrack), + mConduit(std::move(aConduit)), + // AudioSession conduit only supports 16, 32, 44.1 and 48kHz + // This is an artificial limitation, it would however require more + // changes to support any rates. If the sampling rate is not-supported, + // we will use 48kHz instead. + mRate(static_cast<AudioSessionConduit*>(mConduit.get()) + ->IsSamplingFreqSupported(mSource->Graph()->GraphRate()) + ? mSource->Graph()->GraphRate() + : WEBRTC_MAX_SAMPLE_RATE), + mTaskQueue(TaskQueue::Create( + GetMediaThreadPool(MediaThreadType::WEBRTC_WORKER), + "AudioPipelineListener")), + mPlayedTicks(0), + mAudioFrame(std::make_unique<webrtc::AudioFrame>()), + mPrincipalHandle(aPrincipalHandle), + mForceSilence(false) {} + + void Init() { + mSource->SetAppendDataSourceRate(mRate); + mSource->AddListener(this); + } + + // Implement MediaTrackListener + void NotifyPull(MediaTrackGraph* aGraph, TrackTime aEndOfAppendedData, + TrackTime aDesiredTime) override { + NotifyPullImpl(aDesiredTime); + } + + void MakePrincipalPrivate_s() { + mForceSilence = true; + + GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction( + "MediaPipelineReceiveAudio::PipelineListener::MakePrincipalPrivate_s", + [self = RefPtr<PipelineListener>(this), this] { + class Message : public ControlMessage { + public: + Message(RefPtr<PipelineListener> aListener, + const PrincipalHandle& aPrivatePrincipal) + : ControlMessage(nullptr), + mListener(std::move(aListener)), + mPrivatePrincipal(aPrivatePrincipal) {} + + void Run() override { + mListener->mPrincipalHandle = mPrivatePrincipal; + mListener->mForceSilence = false; + } + + const RefPtr<PipelineListener> mListener; + PrincipalHandle mPrivatePrincipal; + }; + + RefPtr<nsIPrincipal> privatePrincipal = + NullPrincipal::CreateWithInheritedAttributes( + mTrackSource->GetPrincipal()); + mTrackSource->SetPrincipal(privatePrincipal); + + if (mSource->IsDestroyed()) { + return; + } + + mSource->GraphImpl()->AppendMessage( + MakeUnique<Message>(this, MakePrincipalHandle(privatePrincipal))); + })); + } + + private: + ~PipelineListener() = default; + + void NotifyPullImpl(TrackTime aDesiredTime) { + TRACE_COMMENT("PiplineListener::NotifyPullImpl", "PipelineListener %p", + this); + uint32_t samplesPer10ms = mRate / 100; + + // mSource's rate is not necessarily the same as the graph rate, since there + // are sample-rate constraints on the inbound audio: only 16, 32, 44.1 and + // 48kHz are supported. The audio frames we get here is going to be + // resampled when inserted into the graph. aDesiredTime and mPlayedTicks are + // in the graph rate. + + while (mPlayedTicks < aDesiredTime) { + // This fetches 10ms of data, either mono or stereo + MediaConduitErrorCode err = + static_cast<AudioSessionConduit*>(mConduit.get()) + ->GetAudioFrame(mRate, mAudioFrame.get()); + + if (err != kMediaConduitNoError) { + // Insert silence on conduit/GIPS failure (extremely unlikely) + MOZ_LOG(gMediaPipelineLog, LogLevel::Error, + ("Audio conduit failed (%d) to return data @ %" PRId64 + " (desired %" PRId64 " -> %f)", + err, mPlayedTicks, aDesiredTime, + mSource->TrackTimeToSeconds(aDesiredTime))); + constexpr size_t mono = 1; + mAudioFrame->UpdateFrame( + mAudioFrame->timestamp_, nullptr, samplesPer10ms, mRate, + mAudioFrame->speech_type_, mAudioFrame->vad_activity_, + std::max(mono, mAudioFrame->num_channels())); + } + + MOZ_LOG( + gMediaPipelineLog, LogLevel::Debug, + ("Audio conduit returned buffer for %zu channels, %zu frames", + mAudioFrame->num_channels(), mAudioFrame->samples_per_channel())); + + AudioSegment segment; + if (mForceSilence || mAudioFrame->muted()) { + segment.AppendNullData(mAudioFrame->samples_per_channel()); + } else { + CheckedInt<size_t> bufferSize(sizeof(uint16_t)); + bufferSize *= mAudioFrame->samples_per_channel(); + bufferSize *= mAudioFrame->num_channels(); + RefPtr<SharedBuffer> samples = SharedBuffer::Create(bufferSize); + int16_t* samplesData = static_cast<int16_t*>(samples->Data()); + AutoTArray<int16_t*, 2> channels; + AutoTArray<const int16_t*, 2> outputChannels; + + channels.SetLength(mAudioFrame->num_channels()); + + size_t offset = 0; + for (size_t i = 0; i < mAudioFrame->num_channels(); i++) { + channels[i] = samplesData + offset; + offset += mAudioFrame->samples_per_channel(); + } + + DeinterleaveAndConvertBuffer( + mAudioFrame->data(), mAudioFrame->samples_per_channel(), + mAudioFrame->num_channels(), channels.Elements()); + + outputChannels.AppendElements(channels); + + segment.AppendFrames(samples.forget(), outputChannels, + mAudioFrame->samples_per_channel(), + mPrincipalHandle); + } + + // Handle track not actually added yet or removed/finished + if (TrackTime appended = mSource->AppendData(&segment)) { + mPlayedTicks += appended; + } else { + MOZ_LOG(gMediaPipelineLog, LogLevel::Error, ("AppendData failed")); + // we can't un-read the data, but that's ok since we don't want to + // buffer - but don't i-loop! + break; + } + } + } + + const RefPtr<MediaSessionConduit> mConduit; + // This conduit's sampling rate. This is either 16, 32, 44.1 or 48kHz, and + // tries to be the same as the graph rate. If the graph rate is higher than + // 48kHz, mRate is capped to 48kHz. If mRate does not match the graph rate, + // audio is resampled to the graph rate. + const TrackRate mRate; + const RefPtr<TaskQueue> mTaskQueue; + // Number of frames of data that has been added to the SourceMediaTrack in + // the graph's rate. Graph thread only. + TrackTicks mPlayedTicks; + // Allocation of an audio frame used as a scratch buffer when reading data out + // of libwebrtc for forwarding into the graph. Graph thread only. + std::unique_ptr<webrtc::AudioFrame> mAudioFrame; + // Principal handle used when appending data to the SourceMediaTrack. Graph + // thread only. + PrincipalHandle mPrincipalHandle; + // Set to true on the sts thread if privacy is requested when ALPN was + // negotiated. Set to false again when mPrincipalHandle is private. + Atomic<bool> mForceSilence; +}; + +MediaPipelineReceiveAudio::MediaPipelineReceiveAudio( + const std::string& aPc, RefPtr<MediaTransportHandler> aTransportHandler, + RefPtr<AbstractThread> aCallThread, RefPtr<nsISerialEventTarget> aStsThread, + RefPtr<AudioSessionConduit> aConduit, + const RefPtr<dom::MediaStreamTrack>& aTrack, + const PrincipalHandle& aPrincipalHandle) + : MediaPipelineReceive(aPc, std::move(aTransportHandler), + std::move(aCallThread), std::move(aStsThread), + std::move(aConduit)), + mListener(aTrack + ? new PipelineListener(aTrack, mConduit, aPrincipalHandle) + : nullptr), + mWatchManager(this, AbstractThread::MainThread()) { + mDescription = mPc + "| Receive audio"; + if (mListener) { + mListener->Init(); + } + mWatchManager.Watch(mActive, &MediaPipelineReceiveAudio::UpdateListener); +} + +void MediaPipelineReceiveAudio::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + MediaPipeline::Shutdown(); + mWatchManager.Shutdown(); + if (mListener) { + mListener->EndTrack(); + } +} + +void MediaPipelineReceiveAudio::MakePrincipalPrivate_s() { + ASSERT_ON_THREAD(mStsThread); + if (mListener) { + mListener->MakePrincipalPrivate_s(); + } +} + +void MediaPipelineReceiveAudio::OnRtpPacketReceived() { + ASSERT_ON_THREAD(mStsThread); + if (mListener) { + mListener->OnRtpReceived(); + } +} + +void MediaPipelineReceiveAudio::UpdateListener() { + MOZ_ASSERT(NS_IsMainThread()); + if (mListener) { + mListener->SetEnabled(mActive.Ref()); + } +} + +class MediaPipelineReceiveVideo::PipelineListener + : public GenericReceiveListener { + public: + PipelineListener(const RefPtr<dom::MediaStreamTrack>& aTrack, + const PrincipalHandle& aPrincipalHandle) + : GenericReceiveListener(aTrack), + mImageContainer( + MakeAndAddRef<ImageContainer>(ImageContainer::ASYNCHRONOUS)), + mMutex("MediaPipelineReceiveVideo::PipelineListener::mMutex"), + mPrincipalHandle(aPrincipalHandle) {} + + void Init() { mSource->AddListener(this); } + + void MakePrincipalPrivate_s() { + { + MutexAutoLock lock(mMutex); + mForceDropFrames = true; + } + + GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction( + __func__, [self = RefPtr<PipelineListener>(this), this] { + RefPtr<nsIPrincipal> privatePrincipal = + NullPrincipal::CreateWithInheritedAttributes( + mTrackSource->GetPrincipal()); + mTrackSource->SetPrincipal(privatePrincipal); + + MutexAutoLock lock(mMutex); + mPrincipalHandle = MakePrincipalHandle(privatePrincipal); + mForceDropFrames = false; + })); + } + + void RenderVideoFrame(const webrtc::VideoFrameBuffer& aBuffer, + uint32_t aTimeStamp, int64_t aRenderTime) { + PrincipalHandle principal; + { + MutexAutoLock lock(mMutex); + if (mForceDropFrames) { + return; + } + principal = mPrincipalHandle; + } + RefPtr<Image> image; + if (aBuffer.type() == webrtc::VideoFrameBuffer::Type::kNative) { + // We assume that only native handles are used with the + // WebrtcMediaDataCodec decoder. + const ImageBuffer* imageBuffer = + static_cast<const ImageBuffer*>(&aBuffer); + image = imageBuffer->GetNativeImage(); + } else { + MOZ_ASSERT(aBuffer.type() == webrtc::VideoFrameBuffer::Type::kI420); + rtc::scoped_refptr<const webrtc::I420BufferInterface> i420( + aBuffer.GetI420()); + + MOZ_ASSERT(i420->DataY()); + // Create a video frame using |buffer|. + PerformanceRecorder<CopyVideoStage> rec( + "MediaPipelineReceiveVideo::CopyToImage"_ns, mTrackingId, + i420->width(), i420->height()); + + RefPtr<PlanarYCbCrImage> yuvImage = + mImageContainer->CreatePlanarYCbCrImage(); + + PlanarYCbCrData yuvData; + yuvData.mYChannel = const_cast<uint8_t*>(i420->DataY()); + yuvData.mYStride = i420->StrideY(); + MOZ_ASSERT(i420->StrideU() == i420->StrideV()); + yuvData.mCbCrStride = i420->StrideU(); + yuvData.mCbChannel = const_cast<uint8_t*>(i420->DataU()); + yuvData.mCrChannel = const_cast<uint8_t*>(i420->DataV()); + yuvData.mPictureRect = IntRect(0, 0, i420->width(), i420->height()); + yuvData.mStereoMode = StereoMode::MONO; + // This isn't the best default. + yuvData.mYUVColorSpace = gfx::YUVColorSpace::BT601; + yuvData.mChromaSubsampling = + gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT; + + if (!yuvImage->CopyData(yuvData)) { + MOZ_ASSERT(false); + return; + } + rec.Record(); + + image = std::move(yuvImage); + } + + VideoSegment segment; + auto size = image->GetSize(); + segment.AppendFrame(image.forget(), size, principal); + mSource->AppendData(&segment); + } + + private: + RefPtr<layers::ImageContainer> mImageContainer; + Mutex mMutex MOZ_UNANNOTATED; // Protects the below members. + PrincipalHandle mPrincipalHandle; + // Set to true on the sts thread if privacy is requested when ALPN was + // negotiated. Set to false again when mPrincipalHandle is private. + bool mForceDropFrames = false; +}; + +class MediaPipelineReceiveVideo::PipelineRenderer + : public mozilla::VideoRenderer { + public: + explicit PipelineRenderer(MediaPipelineReceiveVideo* aPipeline) + : mPipeline(aPipeline) {} + + void Detach() { mPipeline = nullptr; } + + // Implement VideoRenderer + void FrameSizeChange(unsigned int aWidth, unsigned int aHeight) override {} + void RenderVideoFrame(const webrtc::VideoFrameBuffer& aBuffer, + uint32_t aTimeStamp, int64_t aRenderTime) override { + mPipeline->mListener->RenderVideoFrame(aBuffer, aTimeStamp, aRenderTime); + } + + private: + MediaPipelineReceiveVideo* mPipeline; // Raw pointer to avoid cycles +}; + +MediaPipelineReceiveVideo::MediaPipelineReceiveVideo( + const std::string& aPc, RefPtr<MediaTransportHandler> aTransportHandler, + RefPtr<AbstractThread> aCallThread, RefPtr<nsISerialEventTarget> aStsThread, + RefPtr<VideoSessionConduit> aConduit, + const RefPtr<dom::MediaStreamTrack>& aTrack, + const PrincipalHandle& aPrincipalHandle) + : MediaPipelineReceive(aPc, std::move(aTransportHandler), + std::move(aCallThread), std::move(aStsThread), + std::move(aConduit)), + mRenderer(new PipelineRenderer(this)), + mListener(aTrack ? new PipelineListener(aTrack, aPrincipalHandle) + : nullptr), + mWatchManager(this, AbstractThread::MainThread()) { + mDescription = mPc + "| Receive video"; + if (mListener) { + mListener->Init(); + } + static_cast<VideoSessionConduit*>(mConduit.get())->AttachRenderer(mRenderer); + mWatchManager.Watch(mActive, &MediaPipelineReceiveVideo::UpdateListener); +} + +void MediaPipelineReceiveVideo::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + MediaPipeline::Shutdown(); + mWatchManager.Shutdown(); + + // stop generating video and thus stop invoking the PipelineRenderer + // and PipelineListener - the renderer has a raw ptr to the Pipeline to + // avoid cycles, and the render callbacks are invoked from a different + // thread so simple null-checks would cause TSAN bugs without locks. + static_cast<VideoSessionConduit*>(mConduit.get())->DetachRenderer(); + if (mListener) { + mListener->EndTrack(); + } +} + +void MediaPipelineReceiveVideo::MakePrincipalPrivate_s() { + ASSERT_ON_THREAD(mStsThread); + if (mListener) { + mListener->MakePrincipalPrivate_s(); + } +} + +void MediaPipelineReceiveVideo::OnRtpPacketReceived() { + ASSERT_ON_THREAD(mStsThread); + if (mListener) { + mListener->OnRtpReceived(); + } +} + +void MediaPipelineReceiveVideo::UpdateListener() { + MOZ_ASSERT(NS_IsMainThread()); + if (mListener) { + mListener->SetEnabled(mActive.Ref()); + } +} + +const dom::RTCStatsTimestampMaker& MediaPipeline::GetTimestampMaker() const { + return mConduit->GetTimestampMaker(); +} + +DOMHighResTimeStamp MediaPipeline::RtpCSRCStats::GetExpiryFromTime( + const DOMHighResTimeStamp aTime) { + // DOMHighResTimeStamp is a unit measured in ms + return aTime + EXPIRY_TIME_MILLISECONDS; +} + +MediaPipeline::RtpCSRCStats::RtpCSRCStats(const uint32_t aCsrc, + const DOMHighResTimeStamp aTime) + : mCsrc(aCsrc), mTimestamp(aTime) {} + +void MediaPipeline::RtpCSRCStats::GetWebidlInstance( + dom::RTCRTPContributingSourceStats& aWebidlObj, + const nsString& aInboundRtpStreamId) const { + nsString statId = u"csrc_"_ns + aInboundRtpStreamId; + statId.AppendLiteral("_"); + statId.AppendInt(mCsrc); + aWebidlObj.mId.Construct(statId); + aWebidlObj.mType.Construct(RTCStatsType::Csrc); + aWebidlObj.mTimestamp.Construct(mTimestamp); + aWebidlObj.mContributorSsrc.Construct(mCsrc); + aWebidlObj.mInboundRtpStreamId.Construct(aInboundRtpStreamId); +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transportbridge/MediaPipeline.h b/dom/media/webrtc/transportbridge/MediaPipeline.h new file mode 100644 index 0000000000..6fe3f5f287 --- /dev/null +++ b/dom/media/webrtc/transportbridge/MediaPipeline.h @@ -0,0 +1,425 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 + +#ifndef mediapipeline_h__ +#define mediapipeline_h__ + +#include <map> + +#include "transport/sigslot.h" +#include "transport/transportlayer.h" // For TransportLayer::State + +#include "libwebrtcglue/MediaConduitInterface.h" +#include "mozilla/ReentrantMonitor.h" +#include "mozilla/Atomics.h" +#include "mozilla/StateMirroring.h" +#include "transport/mediapacket.h" +#include "transport/runnable_utils.h" +#include "AudioPacketizer.h" +#include "MediaEventSource.h" +#include "MediaPipelineFilter.h" +#include "MediaSegment.h" +#include "PrincipalChangeObserver.h" +#include "jsapi/PacketDumper.h" + +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" + +// Should come from MediaEngine.h, but that's a pain to include here +// because of the MOZILLA_EXTERNAL_LINKAGE stuff. +#define WEBRTC_MAX_SAMPLE_RATE 48000 + +class nsIPrincipal; + +namespace mozilla { +class AudioProxyThread; +class MediaInputPort; +class MediaPipelineFilter; +class MediaTransportHandler; +class PeerIdentity; +class ProcessedMediaTrack; +class SourceMediaTrack; +class VideoFrameConverter; + +namespace dom { +class MediaStreamTrack; +struct RTCRTPContributingSourceStats; +} // namespace dom + +// A class that represents the pipeline of audio and video +// The dataflow looks like: +// +// TRANSMIT +// CaptureDevice -> stream -> [us] -> conduit -> [us] -> transport -> network +// +// RECEIVE +// network -> transport -> [us] -> conduit -> [us] -> stream -> Playout +// +// The boxes labeled [us] are just bridge logic implemented in this class +// +// We have to deal with a number of threads: +// +// GSM: +// * Assembles the pipeline +// SocketTransportService +// * Receives notification that ICE and DTLS have completed +// * Processes incoming network data and passes it to the conduit +// * Processes outgoing RTP and RTCP +// MediaTrackGraph +// * Receives outgoing data from the MediaTrackGraph +// * Receives pull requests for more data from the +// MediaTrackGraph +// One or another GIPS threads +// * Receives RTCP messages to send to the other side +// * Processes video frames GIPS wants to render +// +// For a transmitting conduit, "output" is RTP and "input" is RTCP. +// For a receiving conduit, "input" is RTP and "output" is RTCP. +// + +class MediaPipeline : public sigslot::has_slots<> { + public: + enum class DirectionType { TRANSMIT, RECEIVE }; + MediaPipeline(const std::string& aPc, + RefPtr<MediaTransportHandler> aTransportHandler, + DirectionType aDirection, RefPtr<AbstractThread> aCallThread, + RefPtr<nsISerialEventTarget> aStsThread, + RefPtr<MediaSessionConduit> aConduit); + + void Start(); + void Stop(); + + void SetLevel(size_t aLevel) { mLevel = aLevel; } + + // Main thread shutdown. + virtual void Shutdown(); + + void UpdateTransport_m(const std::string& aTransportId, + UniquePtr<MediaPipelineFilter>&& aFilter); + + void UpdateTransport_s(const std::string& aTransportId, + UniquePtr<MediaPipelineFilter>&& aFilter); + + virtual DirectionType Direction() const { return mDirection; } + size_t Level() const { return mLevel; } + virtual bool IsVideo() const = 0; + + class RtpCSRCStats { + public: + // Gets an expiration time for CRC info given a reference time, + // this reference time would normally be the time of calling. + // This value can then be used to check if a RtpCSRCStats + // has expired via Expired(...) + static DOMHighResTimeStamp GetExpiryFromTime( + const DOMHighResTimeStamp aTime); + + RtpCSRCStats(const uint32_t aCsrc, const DOMHighResTimeStamp aTime); + ~RtpCSRCStats() = default; + // Initialize a webidl representation suitable for adding to a report. + // This assumes that the webidl object is empty. + // @param aWebidlObj the webidl binding object to popluate + // @param aInboundRtpStreamId the associated RTCInboundRTPStreamStats.id + void GetWebidlInstance(dom::RTCRTPContributingSourceStats& aWebidlObj, + const nsString& aInboundRtpStreamId) const; + void SetTimestamp(const DOMHighResTimeStamp aTime) { mTimestamp = aTime; } + // Check if the RtpCSRCStats has expired, checks against a + // given expiration time. + bool Expired(const DOMHighResTimeStamp aExpiry) const { + return mTimestamp < aExpiry; + } + + private: + static const double constexpr EXPIRY_TIME_MILLISECONDS = 10 * 1000; + const uint32_t mCsrc; + DOMHighResTimeStamp mTimestamp; + }; + + // Gets the gathered contributing source stats for the last expiration period. + // @param aId the stream id to use for populating inboundRtpStreamId field + // @param aArr the array to append the stats objects to + void GetContributingSourceStats( + const nsString& aInboundRtpStreamId, + FallibleTArray<dom::RTCRTPContributingSourceStats>& aArr) const; + + int32_t RtpPacketsSent() const { return mRtpPacketsSent; } + int64_t RtpBytesSent() const { return mRtpBytesSent; } + int32_t RtcpPacketsSent() const { return mRtcpPacketsSent; } + int32_t RtpPacketsReceived() const { return mRtpPacketsReceived; } + int64_t RtpBytesReceived() const { return mRtpBytesReceived; } + int32_t RtcpPacketsReceived() const { return mRtcpPacketsReceived; } + + const dom::RTCStatsTimestampMaker& GetTimestampMaker() const; + + // Thread counting + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline) + + protected: + virtual ~MediaPipeline(); + + // The transport is ready + virtual void TransportReady_s() {} + + void IncrementRtpPacketsSent(const MediaPacket& aPacket); + void IncrementRtcpPacketsSent(); + void IncrementRtpPacketsReceived(int aBytes); + virtual void OnRtpPacketReceived() {} + void IncrementRtcpPacketsReceived(); + + virtual void SendPacket(MediaPacket&& packet); + + // Process slots on transports + void RtpStateChange(const std::string& aTransportId, TransportLayer::State); + void RtcpStateChange(const std::string& aTransportId, TransportLayer::State); + virtual void CheckTransportStates(); + void PacketReceived(const std::string& aTransportId, + const MediaPacket& packet); + void AlpnNegotiated(const std::string& aAlpn, bool aPrivacyRequested); + + void RtpPacketReceived(const MediaPacket& packet); + void RtcpPacketReceived(const MediaPacket& packet); + + void EncryptedPacketSending(const std::string& aTransportId, + const MediaPacket& aPacket); + + void SetDescription_s(const std::string& description); + + // Called when ALPN is negotiated and is requesting privacy, so receive + // pipelines do not enter data into the graph under a content principal. + virtual void MakePrincipalPrivate_s() {} + + public: + const RefPtr<MediaSessionConduit> mConduit; + const DirectionType mDirection; + + // Pointers to the threads we need. Initialized at creation + // and used all over the place. + const RefPtr<AbstractThread> mCallThread; + const RefPtr<nsISerialEventTarget> mStsThread; + + protected: + // True if we should be actively transmitting or receiving data. Main thread + // only. + Watchable<bool> mActive; + Atomic<size_t> mLevel; + std::string mTransportId; + const RefPtr<MediaTransportHandler> mTransportHandler; + + TransportLayer::State mRtpState = TransportLayer::TS_NONE; + TransportLayer::State mRtcpState = TransportLayer::TS_NONE; + bool mSignalsConnected = false; + + // Only safe to access from STS thread. + int32_t mRtpPacketsSent; + int32_t mRtcpPacketsSent; + int32_t mRtpPacketsReceived; + int32_t mRtcpPacketsReceived; + int64_t mRtpBytesSent; + int64_t mRtpBytesReceived; + + // Only safe to access from STS thread. + std::map<uint32_t, RtpCSRCStats> mCsrcStats; + + // Written in c'tor. Read on STS and main thread. + const std::string mPc; + + // String describing this MediaPipeline for logging purposes. Only safe to + // access from STS thread. + std::string mDescription; + + // Written in c'tor, all following accesses are on the STS thread. + UniquePtr<MediaPipelineFilter> mFilter; + const UniquePtr<webrtc::RtpHeaderExtensionMap> mRtpHeaderExtensionMap; + + RefPtr<PacketDumper> mPacketDumper; + + MediaEventProducerExc<MediaPacket, webrtc::RTPHeader> mRtpReceiveEvent; + MediaEventProducerExc<MediaPacket> mSenderRtcpReceiveEvent; + MediaEventProducerExc<MediaPacket> mReceiverRtcpReceiveEvent; + + MediaEventListener mRtpSendEventListener; + MediaEventListener mSenderRtcpSendEventListener; + MediaEventListener mReceiverRtcpSendEventListener; + + private: + bool IsRtp(const unsigned char* aData, size_t aLen) const; + // Must be called on the STS thread. Must be called after Shutdown(). + void DetachTransport_s(); +}; + +// A specialization of pipeline for reading from an input device +// and transmitting to the network. +class MediaPipelineTransmit + : public MediaPipeline, + public dom::PrincipalChangeObserver<dom::MediaStreamTrack> { + public: + // Set aRtcpTransport to nullptr to use rtcp-mux + MediaPipelineTransmit(const std::string& aPc, + RefPtr<MediaTransportHandler> aTransportHandler, + RefPtr<AbstractThread> aCallThread, + RefPtr<nsISerialEventTarget> aStsThread, bool aIsVideo, + RefPtr<MediaSessionConduit> aConduit); + + void Shutdown() override; + + bool Transmitting() const; + + // written and used from MainThread + bool IsVideo() const override; + + // When the principal of the domtrack changes, it calls through to here + // so that we can determine whether to enable track transmission. + // In cases where the peer isn't yet identified, we disable the pipeline (not + // the stream, that would potentially affect others), so that it sends + // black/silence. Once the peer is identified, re-enable those streams. + virtual void UpdateSinkIdentity(nsIPrincipal* aPrincipal, + const PeerIdentity* aSinkIdentity); + + // for monitoring changes in track ownership + void PrincipalChanged(dom::MediaStreamTrack* aTrack) override; + + // Override MediaPipeline::TransportReady_s. + void TransportReady_s() override; + + // Replace a track with a different one. + nsresult SetTrack(RefPtr<dom::MediaStreamTrack> aDomTrack); + + // Used to correlate stats + RefPtr<dom::MediaStreamTrack> GetTrack() const; + + // For test use only. This allows a send track to be set without a + // corresponding dom track. + void SetSendTrackOverride(RefPtr<ProcessedMediaTrack> aSendTrack); + + // Separate classes to allow ref counting + class PipelineListener; + class VideoFrameFeeder; + + protected: + ~MediaPipelineTransmit(); + + // Updates mDescription (async) with information about the track we are + // transmitting. + std::string GenerateDescription() const; + + // Sets up mSendPort and mSendTrack to feed mConduit if we are transmitting + // and have a dom track but no send track. Main thread only. + void UpdateSendState(); + + private: + WatchManager<MediaPipelineTransmit> mWatchManager; + const bool mIsVideo; + const RefPtr<PipelineListener> mListener; + RefPtr<AudioProxyThread> mAudioProcessing; + RefPtr<VideoFrameConverter> mConverter; + MediaEventListener mFrameListener; + Watchable<RefPtr<dom::MediaStreamTrack>> mDomTrack; + // Input port connecting mDomTrack's MediaTrack to mSendTrack. + RefPtr<MediaInputPort> mSendPort; + // The source track of the mSendTrack. Main thread only. + RefPtr<ProcessedMediaTrack> mSendPortSource; + // True if a parameter affecting mDescription has changed. To avoid updating + // the description unnecessarily. Main thread only. + bool mDescriptionInvalidated = true; + // Set true once we trigger the async removal of mSendTrack. Set false once + // the async removal is done. Main thread only. + bool mUnsettingSendTrack = false; + // MediaTrack that we send over the network. This allows changing mDomTrack. + // Because changing mSendTrack is async and can be racy (when changing from a + // track in one graph to a track in another graph), it is set very strictly. + // If mSendTrack is null it can be set by UpdateSendState(). + // If it is non-null it can only be set to null, and only by the + // RemoveListener MozPromise handler, as seen in UpdateSendState. + RefPtr<ProcessedMediaTrack> mSendTrack; + // When this is set and we are active, this track will be used as mSendTrack. + // Allows unittests to insert a send track without requiring a dom track or a + // graph. Main thread only. + Watchable<RefPtr<ProcessedMediaTrack>> mSendTrackOverride; + // True when mSendTrack is set, not destroyed and mActive is true. mListener + // is attached to mSendTrack when this is true. Main thread only. + bool mTransmitting = false; +}; + +// A specialization of pipeline for reading from the network and +// rendering media. +class MediaPipelineReceive : public MediaPipeline { + public: + // Set aRtcpTransport to nullptr to use rtcp-mux + MediaPipelineReceive(const std::string& aPc, + RefPtr<MediaTransportHandler> aTransportHandler, + RefPtr<AbstractThread> aCallThread, + RefPtr<nsISerialEventTarget> aStsThread, + RefPtr<MediaSessionConduit> aConduit); + + protected: + ~MediaPipelineReceive(); +}; + +// A specialization of pipeline for reading from the network and +// rendering audio. +class MediaPipelineReceiveAudio : public MediaPipelineReceive { + public: + MediaPipelineReceiveAudio(const std::string& aPc, + RefPtr<MediaTransportHandler> aTransportHandler, + RefPtr<AbstractThread> aCallThread, + RefPtr<nsISerialEventTarget> aStsThread, + RefPtr<AudioSessionConduit> aConduit, + const RefPtr<dom::MediaStreamTrack>& aTrack, + const PrincipalHandle& aPrincipalHandle); + + void Shutdown() override; + + bool IsVideo() const override { return false; } + + void MakePrincipalPrivate_s() override; + + void OnRtpPacketReceived() override; + + private: + void UpdateListener(); + + // Separate class to allow ref counting + class PipelineListener; + + const RefPtr<PipelineListener> mListener; + WatchManager<MediaPipelineReceiveAudio> mWatchManager; +}; + +// A specialization of pipeline for reading from the network and +// rendering video. +class MediaPipelineReceiveVideo : public MediaPipelineReceive { + public: + MediaPipelineReceiveVideo(const std::string& aPc, + RefPtr<MediaTransportHandler> aTransportHandler, + RefPtr<AbstractThread> aCallThread, + RefPtr<nsISerialEventTarget> aStsThread, + RefPtr<VideoSessionConduit> aConduit, + const RefPtr<dom::MediaStreamTrack>& aTrack, + const PrincipalHandle& aPrincipalHandle); + + void Shutdown() override; + + bool IsVideo() const override { return true; } + + void MakePrincipalPrivate_s() override; + + void OnRtpPacketReceived() override; + + private: + void UpdateListener(); + + class PipelineRenderer; + friend class PipelineRenderer; + + // Separate class to allow ref counting + class PipelineListener; + + const RefPtr<PipelineRenderer> mRenderer; + const RefPtr<PipelineListener> mListener; + WatchManager<MediaPipelineReceiveVideo> mWatchManager; +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp b/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp new file mode 100644 index 0000000000..1acb73e9f2 --- /dev/null +++ b/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: softtabstop=2:shiftwidth=2:expandtab + * */ +/* 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: bcampen@mozilla.com + +#include "MediaPipelineFilter.h" + +#include "api/rtp_headers.h" +#include "api/rtp_parameters.h" +#include "mozilla/Logging.h" + +// defined in MediaPipeline.cpp +extern mozilla::LazyLogModule gMediaPipelineLog; + +#define DEBUG_LOG(x) MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, x) + +namespace mozilla { +MediaPipelineFilter::MediaPipelineFilter( + const std::vector<webrtc::RtpExtension>& aExtMap) + : mExtMap(aExtMap) {} + +void MediaPipelineFilter::SetRemoteMediaStreamId( + const Maybe<std::string>& aMid) { + if (aMid != mRemoteMid) { + DEBUG_LOG(("MediaPipelineFilter added new remote RTP MID: '%s'.", + aMid.valueOr("").c_str())); + mRemoteMid = aMid; + mRemoteMidBindings.clear(); + } +} + +bool MediaPipelineFilter::Filter(const webrtc::RTPHeader& header) { + DEBUG_LOG(("MediaPipelineFilter inspecting seq# %u SSRC: %u", + header.sequenceNumber, header.ssrc)); + + auto fromStreamId = [](const std::string& aId) { + return Maybe<std::string>(aId.empty() ? Nothing() : Some(aId)); + }; + + // + // MID Based Filtering + // + + const auto mid = fromStreamId(header.extension.mid); + + // Check to see if a bound SSRC is moved to a new MID + if (mRemoteMidBindings.count(header.ssrc) == 1 && mid && mRemoteMid != mid) { + mRemoteMidBindings.erase(header.ssrc); + } + // Bind an SSRC if a matching MID is found + if (mid && mRemoteMid == mid) { + DEBUG_LOG(("MediaPipelineFilter learned SSRC: %u for MID: '%s'", + header.ssrc, mRemoteMid.value().c_str())); + mRemoteMidBindings.insert(header.ssrc); + } + // Check for matching MID + if (!mRemoteMidBindings.empty()) { + MOZ_ASSERT(mRemoteMid != Nothing()); + if (mRemoteMidBindings.count(header.ssrc) == 1) { + DEBUG_LOG( + ("MediaPipelineFilter SSRC: %u matched for MID: '%s'." + " passing packet", + header.ssrc, mRemoteMid.value().c_str())); + return true; + } + DEBUG_LOG( + ("MediaPipelineFilter SSRC: %u did not match bound SSRC(s) for" + " MID: '%s'. ignoring packet", + header.ssrc, mRemoteMid.value().c_str())); + for (const uint32_t ssrc : mRemoteMidBindings) { + DEBUG_LOG(("MID %s is associated with SSRC: %u", + mRemoteMid.value().c_str(), ssrc)); + } + return false; + } + + // + // RTP-STREAM-ID based filtering (for tests only) + // + + // + // Remote SSRC based filtering + // + + if (remote_ssrc_set_.count(header.ssrc)) { + DEBUG_LOG( + ("MediaPipelineFilter SSRC: %u matched remote SSRC set." + " passing packet", + header.ssrc)); + return true; + } + DEBUG_LOG( + ("MediaPipelineFilter SSRC: %u did not match any of %zu" + " remote SSRCS.", + header.ssrc, remote_ssrc_set_.size())); + + // + // PT, payload type, last ditch effort filtering + // + + if (payload_type_set_.count(header.payloadType)) { + DEBUG_LOG( + ("MediaPipelineFilter payload-type: %u matched %zu" + " unique payload type. learning ssrc. passing packet", + header.ssrc, remote_ssrc_set_.size())); + // Actual match. We need to update the ssrc map so we can route rtcp + // sender reports correctly (these use a different payload-type field) + AddRemoteSSRC(header.ssrc); + return true; + } + DEBUG_LOG( + ("MediaPipelineFilter payload-type: %u did not match any of %zu" + " unique payload-types.", + header.payloadType, payload_type_set_.size())); + DEBUG_LOG( + ("MediaPipelineFilter packet failed to match any criteria." + " ignoring packet")); + return false; +} + +void MediaPipelineFilter::AddRemoteSSRC(uint32_t ssrc) { + remote_ssrc_set_.insert(ssrc); +} + +void MediaPipelineFilter::AddUniquePT(uint8_t payload_type) { + payload_type_set_.insert(payload_type); +} + +void MediaPipelineFilter::Update(const MediaPipelineFilter& filter_update) { + // We will not stomp the remote_ssrc_set_ if the update has no ssrcs, + // because we don't want to unlearn any remote ssrcs unless the other end + // has explicitly given us a new set. + if (!filter_update.remote_ssrc_set_.empty()) { + remote_ssrc_set_ = filter_update.remote_ssrc_set_; + } + // We don't want to overwrite the learned binding unless we have changed MIDs + // or the update contains a MID binding. + if (!filter_update.mRemoteMidBindings.empty() || + (filter_update.mRemoteMid && filter_update.mRemoteMid != mRemoteMid)) { + mRemoteMid = filter_update.mRemoteMid; + mRemoteMidBindings = filter_update.mRemoteMidBindings; + } + payload_type_set_ = filter_update.payload_type_set_; + + // Use extmapping from new filter + mExtMap = filter_update.mExtMap; +} + +} // end namespace mozilla diff --git a/dom/media/webrtc/transportbridge/MediaPipelineFilter.h b/dom/media/webrtc/transportbridge/MediaPipelineFilter.h new file mode 100644 index 0000000000..9b40bceda8 --- /dev/null +++ b/dom/media/webrtc/transportbridge/MediaPipelineFilter.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: softtabstop=2:shiftwidth=2:expandtab + * */ +/* 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: bcampen@mozilla.com + +#ifndef mediapipelinefilter_h__ +#define mediapipelinefilter_h__ + +#include <cstddef> +#include <stdint.h> +#include <string> + +#include <set> +#include <vector> + +#include "mozilla/Maybe.h" + +namespace webrtc { +struct RTPHeader; +struct RtpExtension; +} // namespace webrtc + +namespace mozilla { + +// TODO @@NG update documentation after initial review + +// A class that handles the work of filtering RTP packets that arrive at a +// MediaPipeline. This is primarily important for the use of BUNDLE (ie; +// multiple m-lines share the same RTP stream). There are three ways that this +// can work; +// +// 1) In our SDP, we include a media-level extmap parameter with a unique +// integer of our choosing, with the hope that the other side will include +// this value in a header in the first few RTP packets it sends us. This +// allows us to perform correlation in cases where the other side has not +// informed us of the ssrcs it will be sending (either because it did not +// include them in its SDP, or their SDP has not arrived yet) +// and also gives us the opportunity to learn SSRCs from packets so adorned. +// +// 2) If the remote endpoint includes SSRC media-level attributes in its SDP, +// we can simply use this information to populate the filter. The only +// shortcoming here is when RTP packets arrive before the answer does. See +// above. +// +// 3) As a fallback, we can try to use payload type IDs to perform correlation, +// but only when the type id is unique to this media section. +// This too allows us to learn about SSRCs (mostly useful for filtering +// sender reports later). +class MediaPipelineFilter { + public: + MediaPipelineFilter() = default; + explicit MediaPipelineFilter( + const std::vector<webrtc::RtpExtension>& aExtMap); + + // Checks whether this packet passes the filter, possibly updating the filter + // in the process (if the MID or payload types are used, they can teach + // the filter about ssrcs) + bool Filter(const webrtc::RTPHeader& header); + + void AddRemoteSSRC(uint32_t ssrc); + + void SetRemoteMediaStreamId(const Maybe<std::string>& aMid); + + // When a payload type id is unique to our media section, add it here. + void AddUniquePT(uint8_t payload_type); + + void Update(const MediaPipelineFilter& filter_update); + + std::vector<webrtc::RtpExtension> GetExtmap() const { return mExtMap; } + + private: + // The number of filters we manage here is quite small, so I am optimizing + // for readability. + std::set<uint32_t> remote_ssrc_set_; + std::set<uint8_t> payload_type_set_; + Maybe<std::string> mRemoteMid; + std::set<uint32_t> mRemoteMidBindings; + // RID extension can be set by tests and is sticky, the rest of + // the mapping is not. + std::vector<webrtc::RtpExtension> mExtMap; +}; + +} // end namespace mozilla + +#endif // mediapipelinefilter_h__ diff --git a/dom/media/webrtc/transportbridge/RtpLogger.cpp b/dom/media/webrtc/transportbridge/RtpLogger.cpp new file mode 100644 index 0000000000..aac1fad197 --- /dev/null +++ b/dom/media/webrtc/transportbridge/RtpLogger.cpp @@ -0,0 +1,67 @@ +/* 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: nohlmeier@mozilla.com + +#include "RtpLogger.h" +#include "mozilla/Logging.h" + +#include <ctime> +#include <iomanip> +#include <sstream> +#ifdef _WIN32 +# include <time.h> +# include <sys/timeb.h> +#else +# include <sys/time.h> +#endif + +// Logging context +using namespace mozilla; + +mozilla::LazyLogModule gRtpLoggerLog("RtpLogger"); + +namespace mozilla { + +bool RtpLogger::IsPacketLoggingOn() { + return MOZ_LOG_TEST(gRtpLoggerLog, LogLevel::Debug); +} + +void RtpLogger::LogPacket(const MediaPacket& packet, bool input, + std::string desc) { + if (MOZ_LOG_TEST(gRtpLoggerLog, LogLevel::Debug)) { + bool isRtp = (packet.type() == MediaPacket::RTP); + std::stringstream ss; + /* This creates text2pcap compatible format, e.g.: + * RTCP_PACKET O 10:36:26.864934 000000 80 c8 00 06 6d ... + */ + ss << (input ? "I " : "O "); + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + char buf[9]; + if (0 < strftime(buf, sizeof(buf), "%H:%M:%S", &tm)) { + ss << buf; + } + ss << std::setfill('0'); +#ifdef _WIN32 + struct timeb tb; + ftime(&tb); + ss << "." << (tb.millitm) << " "; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + ss << "." << (tv.tv_usec) << " "; +#endif + ss << " 000000"; + ss << std::hex << std::setfill('0'); + for (size_t i = 0; i < packet.len(); ++i) { + ss << " " << std::setw(2) << (int)packet.data()[i]; + } + MOZ_LOG(gRtpLoggerLog, LogLevel::Debug, + ("%s%s%s", desc.c_str(), (isRtp ? " RTP_PACKET " : " RTCP_PACKET "), + ss.str().c_str())); + } +} + +} // namespace mozilla diff --git a/dom/media/webrtc/transportbridge/RtpLogger.h b/dom/media/webrtc/transportbridge/RtpLogger.h new file mode 100644 index 0000000000..fcfaede6e2 --- /dev/null +++ b/dom/media/webrtc/transportbridge/RtpLogger.h @@ -0,0 +1,28 @@ +/* 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: nohlmeier@mozilla.com + +#ifndef rtplogger_h__ +#define rtplogger_h__ + +#include "transport/mediapacket.h" + +namespace mozilla { + +/* This class logs RTP and RTCP packets in hex in a format compatible to + * text2pcap. + * Example to convert the MOZ log file into a PCAP file: + * egrep '(RTP_PACKET|RTCP_PACKET)' moz.log | \ + * text2pcap -D -n -l 1 -i 17 -u 1234,1235 -t '%H:%M:%S.' - rtp.pcap + */ +class RtpLogger { + public: + static bool IsPacketLoggingOn(); + static void LogPacket(const MediaPacket& packet, bool input, + std::string desc); +}; + +} // namespace mozilla +#endif diff --git a/dom/media/webrtc/transportbridge/moz.build b/dom/media/webrtc/transportbridge/moz.build new file mode 100644 index 0000000000..290dd26a0a --- /dev/null +++ b/dom/media/webrtc/transportbridge/moz.build @@ -0,0 +1,27 @@ +# -*- Mode: python; 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/. +include("/dom/media/webrtc/third_party_build/webrtc.mozbuild") + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/dom/media", + "/dom/media/webrtc", + "/ipc/chromium/src", + "/media/libyuv/libyuv/include", + "/media/webrtc", + "/third_party/libsrtp/src/crypto/include", + "/third_party/libsrtp/src/include", + "/third_party/libwebrtc", + "/third_party/libwebrtc/third_party/abseil-cpp", +] + +UNIFIED_SOURCES += [ + "MediaPipeline.cpp", + "MediaPipelineFilter.cpp", + "RtpLogger.cpp", +] + +FINAL_LIBRARY = "xul" |