import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

59
media/mtransport/README Normal file
View file

@ -0,0 +1,59 @@
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
(TransportLayerPrsock, 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.
* Prsock -- a wrapper around NSPR sockets
* Loopback -- a loopback IO mechanism
* Logging -- a passthrough that just logs its data
The last three are primarily for debugging.

View file

@ -0,0 +1,36 @@
# -*- 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.mtransport += [
'../dtlsidentity.h',
'../m_cpp_utils.h',
'../nricectx.h',
'../nricemediastream.h',
'../nriceresolverfake.h',
'../rlogconnector.h',
'../runnable_utils.h',
'../sigslot.h',
'../simpletokenbucket.h',
'../stun_socket_filter.h',
'../transportflow.h',
'../transportlayer.h',
'../transportlayerdtls.h',
'../transportlayerice.h',
'../transportlayerlog.h',
'../transportlayerloopback.h',
'../transportlayerprsock.h',
]
include('../common.build')
# 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.
SOURCES += mtransport_cppsrcs
FINAL_LIBRARY = 'xul'

View file

@ -0,0 +1,95 @@
# -*- 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/.
mtransport_lcppsrcs = [
'dtlsidentity.cpp',
'nr_socket_prsock.cpp',
'nr_timer.cpp',
'nricectx.cpp',
'nricectxhandler.cpp',
'nricemediastream.cpp',
'nriceresolver.cpp',
'nriceresolverfake.cpp',
'nrinterfaceprioritizer.cpp',
'rlogconnector.cpp',
'simpletokenbucket.cpp',
'stun_socket_filter.cpp',
'test_nr_socket.cpp',
'transportflow.cpp',
'transportlayer.cpp',
'transportlayerdtls.cpp',
'transportlayerice.cpp',
'transportlayerlog.cpp',
'transportlayerloopback.cpp',
'transportlayerprsock.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
mtransport_lcppsrcs += [
'gonk_addrs.cpp',
]
mtransport_cppsrcs = [
'/media/mtransport/%s' % s for s in sorted(mtransport_lcppsrcs)
]
LOCAL_INCLUDES += [
'/media/mtransport/',
'/media/mtransport/third_party/',
'/media/mtransport/third_party/nICEr/src/crypto',
'/media/mtransport/third_party/nICEr/src/ice',
'/media/mtransport/third_party/nICEr/src/net',
'/media/mtransport/third_party/nICEr/src/stun',
'/media/mtransport/third_party/nICEr/src/util',
'/media/mtransport/third_party/nrappkit/src/event',
'/media/mtransport/third_party/nrappkit/src/log',
'/media/mtransport/third_party/nrappkit/src/plugin',
'/media/mtransport/third_party/nrappkit/src/port/generic/include',
'/media/mtransport/third_party/nrappkit/src/registry',
'/media/mtransport/third_party/nrappkit/src/share',
'/media/mtransport/third_party/nrappkit/src/stats',
'/media/mtransport/third_party/nrappkit/src/util/libekr',
]
if CONFIG['OS_TARGET'] in ['Darwin', 'DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD']:
if CONFIG['OS_TARGET'] == 'Darwin':
DEFINES['DARWIN'] = True
else:
DEFINES['BSD'] = True
LOCAL_INCLUDES += [
'/media/mtransport/third_party/nrappkit/src/port/darwin/include',
]
elif CONFIG['OS_TARGET'] == 'Linux':
DEFINES['LINUX'] = True
LOCAL_INCLUDES += [
'/media/mtransport/third_party/nrappkit/src/port/linux/include',
]
elif CONFIG['OS_TARGET'] == 'Android':
DEFINES['LINUX'] = True
DEFINES['ANDROID'] = True
LOCAL_INCLUDES += [
'/media/mtransport/third_party/nrappkit/src/port/android/include',
]
elif CONFIG['OS_TARGET'] == 'WINNT':
DEFINES['WIN'] = True
# for stun.h
DEFINES['WIN32'] = True
LOCAL_INCLUDES += [
'/media/mtransport/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'
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']

View file

@ -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
#ifndef databuffer_h__
#define databuffer_h__
#include <algorithm>
#include <mozilla/UniquePtr.h>
#include <m_cpp_utils.h>
#include <nsISupportsImpl.h>
namespace mozilla {
class DataBuffer {
public:
DataBuffer() : data_(nullptr), len_(0), capacity_(0) {}
DataBuffer(const uint8_t *data, size_t len) {
Assign(data, len, len);
}
DataBuffer(const uint8_t *data, size_t len, size_t capacity) {
Assign(data, len, capacity);
}
// to ensure extra space for expansion
void Assign(const uint8_t *data, size_t len, size_t capacity) {
MOZ_RELEASE_ASSERT(len <= capacity);
Allocate(capacity); // sets len_ = capacity
memcpy(static_cast<void *>(data_.get()),
static_cast<const void *>(data), len);
len_ = len;
}
void Allocate(size_t capacity) {
data_.reset(new uint8_t[capacity ? capacity : 1]); // Don't depend on new [0].
len_ = capacity_ = capacity;
}
void EnsureCapacity(size_t capacity) {
if (capacity_ < capacity) {
uint8_t *new_data = new uint8_t[ capacity ? capacity : 1];
memcpy(static_cast<void *>(new_data),
static_cast<const void *>(data_.get()), len_);
data_.reset(new_data); // after copying! Deletes old data
capacity_ = capacity;
}
}
// used when something writes to the buffer (having checked
// capacity() or used EnsureCapacity()) and increased the length.
void SetLength(size_t len) {
MOZ_RELEASE_ASSERT(len <= capacity_);
len_ = len;
}
const uint8_t *data() const { return data_.get(); }
uint8_t *data() { return data_.get(); }
size_t len() const { return len_; }
size_t capacity() const { return capacity_; }
private:
UniquePtr<uint8_t[]> data_;
size_t len_;
size_t capacity_;
DISALLOW_COPY_ASSIGN(DataBuffer);
};
}
#endif

View file

@ -0,0 +1,214 @@
/* -*- 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 "ssl.h"
#include "mozilla/Sprintf.h"
namespace mozilla {
RefPtr<DtlsIdentity> DtlsIdentity::Generate() {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
return nullptr;
}
uint8_t random_name[16];
SECStatus rv = PK11_GenerateRandomOnSlot(slot, random_name,
sizeof(random_name));
if (rv != SECSuccess)
return nullptr;
std::string name;
char chunk[3];
for (size_t i = 0; i < sizeof(random_name); ++i) {
SprintfLiteral(chunk, "%.2x", random_name[i]);
name += chunk;
}
std::string subject_name_string = "CN=" + name;
ScopedCERTName 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;
ScopedSECKEYPrivateKey private_key;
ScopedSECKEYPublicKey public_key;
SECKEYPublicKey *pubkey;
private_key =
PK11_GenerateKeyPair(slot,
CKM_EC_KEY_PAIR_GEN, &ecdsaParams, &pubkey,
PR_FALSE, PR_TRUE, nullptr);
if (private_key == nullptr)
return nullptr;
public_key = pubkey;
ScopedCERTSubjectPublicKeyInfo spki(
SECKEY_CreateSubjectPublicKeyInfo(pubkey));
if (!spki) {
return nullptr;
}
ScopedCERTCertificateRequest certreq(
CERT_CreateCertificateRequest(subject_name, spki, 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);
ScopedCERTValidity 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,
reinterpret_cast<unsigned char *>(&serial),
sizeof(serial));
if (rv != SECSuccess) {
return nullptr;
}
ScopedCERTCertificate certificate(
CERT_CreateCertificate(serial, subject_name, validity, certreq));
if (!certificate) {
return nullptr;
}
PLArenaPool *arena = certificate->arena;
rv = SECOID_SetAlgorithmID(arena, &certificate->signature,
SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0);
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,
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,
SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
if (rv != SECSuccess) {
return nullptr;
}
certificate->derCert = *signedCert;
RefPtr<DtlsIdentity> identity =
new DtlsIdentity(private_key.forget(), certificate.forget(), ssl_kea_ecdh);
return identity.forget();
}
const std::string DtlsIdentity::DEFAULT_HASH_ALGORITHM = "sha-256";
nsresult DtlsIdentity::ComputeFingerprint(const std::string algorithm,
uint8_t *digest,
size_t size,
size_t *digest_length) const {
const UniqueCERTCertificate& c = cert();
MOZ_ASSERT(c);
return ComputeFingerprint(c, algorithm, digest, size, digest_length);
}
nsresult DtlsIdentity::ComputeFingerprint(const UniqueCERTCertificate& cert,
const std::string algorithm,
uint8_t *digest,
size_t size,
size_t *digest_length) {
MOZ_ASSERT(cert);
HASH_HashType ht;
if (algorithm == "sha-1") {
ht = HASH_AlgSHA1;
} else if (algorithm == "sha-224") {
ht = HASH_AlgSHA224;
} else if (algorithm == "sha-256") {
ht = HASH_AlgSHA256;
} else if (algorithm == "sha-384") {
ht = HASH_AlgSHA384;
} else if (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
if (size < ho->length) {
return NS_ERROR_INVALID_ARG;
}
SECStatus rv = HASH_HashBuf(ho->type, digest,
cert->derCert.data,
cert->derCert.len);
if (rv != SECSuccess) {
return NS_ERROR_FAILURE;
}
*digest_length = ho->length;
return NS_OK;
}
} // close namespace

View file

@ -0,0 +1,70 @@
/* -*- 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 "m_cpp_utils.h"
#include "mozilla/RefPtr.h"
#include "nsISupportsImpl.h"
#include "sslt.h"
#include "ScopedNSSTypes.h"
// All code in this module requires NSS to be live.
// Callers must initialize NSS and implement the nsNSSShutdownObject
// protocol.
namespace mozilla {
class DtlsIdentity final {
public:
// This constructor takes ownership of privkey and cert.
DtlsIdentity(SECKEYPrivateKey *privkey,
CERTCertificate *cert,
SSLKEAType authType)
: private_key_(privkey), cert_(cert), auth_type_(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_; }
SECKEYPrivateKey *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(const std::string algorithm,
uint8_t *digest,
size_t size,
size_t *digest_length) const;
static nsresult ComputeFingerprint(const UniqueCERTCertificate& cert,
const std::string algorithm,
uint8_t *digest,
size_t size,
size_t *digest_length);
static const std::string DEFAULT_HASH_ALGORITHM;
enum {
HASH_ALGORITHM_MAX_LENGTH = 64
};
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DtlsIdentity)
private:
~DtlsIdentity() {}
DISALLOW_COPY_ASSIGN(DtlsIdentity);
ScopedSECKEYPrivateKey private_key_;
UniqueCERTCertificate cert_;
SSLKEAType auth_type_;
};
} // close namespace
#endif

View file

@ -0,0 +1,170 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
extern "C" {
#include <arpa/inet.h>
#include "r_types.h"
#include "stun.h"
#include "addrs.h"
}
#include <vector>
#include <string>
#include "nsINetworkInterface.h"
#include "nsINetworkInterfaceListService.h"
#include "runnable_utils.h"
#include "nsCOMPtr.h"
#include "nsMemory.h"
#include "nsThreadUtils.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/SyncRunnable.h"
namespace {
struct NetworkInterface {
struct sockaddr_in addr;
std::string name;
// See NR_INTERFACE_TYPE_* in nICEr/src/net/local_addrs.h
int type;
};
nsresult
GetInterfaces(std::vector<NetworkInterface>* aInterfaces)
{
MOZ_ASSERT(aInterfaces);
// Obtain network interfaces from network manager.
nsresult rv;
nsCOMPtr<nsINetworkInterfaceListService> listService =
do_GetService("@mozilla.org/network/interface-list-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
int32_t flags =
nsINetworkInterfaceListService::LIST_NOT_INCLUDE_SUPL_INTERFACES |
nsINetworkInterfaceListService::LIST_NOT_INCLUDE_MMS_INTERFACES |
nsINetworkInterfaceListService::LIST_NOT_INCLUDE_IMS_INTERFACES |
nsINetworkInterfaceListService::LIST_NOT_INCLUDE_DUN_INTERFACES |
nsINetworkInterfaceListService::LIST_NOT_INCLUDE_FOTA_INTERFACES;
nsCOMPtr<nsINetworkInterfaceList> networkList;
NS_ENSURE_SUCCESS(listService->GetDataInterfaceList(flags,
getter_AddRefs(networkList)),
NS_ERROR_FAILURE);
// Translate nsINetworkInterfaceList to NetworkInterface.
int32_t listLength;
NS_ENSURE_SUCCESS(networkList->GetNumberOfInterface(&listLength),
NS_ERROR_FAILURE);
aInterfaces->clear();
for (int32_t i = 0; i < listLength; i++) {
nsCOMPtr<nsINetworkInfo> info;
if (NS_FAILED(networkList->GetInterfaceInfo(i, getter_AddRefs(info)))) {
continue;
}
char16_t **ips = nullptr;
uint32_t *prefixs = nullptr;
uint32_t count = 0;
bool isAddressGot = false;
NetworkInterface interface;
memset(&(interface.addr), 0, sizeof(interface.addr));
interface.addr.sin_family = AF_INET;
if (NS_FAILED(info->GetAddresses(&ips, &prefixs, &count))) {
continue;
}
for (uint32_t j = 0; j < count; j++) {
nsAutoString ip;
ip.Assign(ips[j]);
if (inet_pton(AF_INET, NS_ConvertUTF16toUTF8(ip).get(),
&(interface.addr.sin_addr.s_addr)) == 1) {
isAddressGot = true;
break;
}
}
free(prefixs);
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, ips);
if (!isAddressGot) {
continue;
}
nsAutoString ifaceName;
if (NS_FAILED(info->GetName(ifaceName))) {
continue;
}
interface.name = NS_ConvertUTF16toUTF8(ifaceName).get();
int32_t type;
if (NS_FAILED(info->GetType(&type))) {
continue;
}
switch (type) {
case nsINetworkInfo::NETWORK_TYPE_WIFI:
interface.type = NR_INTERFACE_TYPE_WIFI;
break;
case nsINetworkInfo::NETWORK_TYPE_MOBILE:
interface.type = NR_INTERFACE_TYPE_MOBILE;
break;
}
aInterfaces->push_back(interface);
}
return NS_OK;
}
} // anonymous namespace
int
nr_stun_get_addrs(nr_local_addr aAddrs[], int aMaxAddrs,
int aDropLoopback, int aDropLinkLocal, int* aCount)
{
nsresult rv;
int r;
// Get network interface list.
std::vector<NetworkInterface> interfaces;
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
mozilla::SyncRunnable::DispatchToThread(
mainThread.get(),
mozilla::WrapRunnableNMRet(&rv, &GetInterfaces, &interfaces),
false);
if (NS_FAILED(rv)) {
return R_FAILED;
}
// Translate to nr_transport_addr.
int32_t n = 0;
size_t num_interface = std::min(interfaces.size(), (size_t)aMaxAddrs);
for (size_t i = 0; i < num_interface; ++i) {
NetworkInterface &interface = interfaces[i];
if (nr_sockaddr_to_transport_addr((sockaddr*)&(interface.addr),
IPPROTO_UDP, 0, &(aAddrs[n].addr))) {
r_log(NR_LOG_STUN, LOG_WARNING, "Problem transforming address");
return R_FAILED;
}
strlcpy(aAddrs[n].addr.ifname, interface.name.c_str(),
sizeof(aAddrs[n].addr.ifname));
aAddrs[n].interface.type = interface.type;
aAddrs[n].interface.estimated_speed = 0;
n++;
}
*aCount = n;
r = nr_stun_remove_duplicate_addrs(aAddrs, aDropLoopback, aDropLinkLocal, aCount);
if (r != 0) {
return r;
}
for (int i = 0; i < *aCount; ++i) {
char typestr[100];
nr_local_addr_fmt_info_string(aAddrs + i, typestr, sizeof(typestr));
r_log(NR_LOG_STUN, LOG_DEBUG, "Address %d: %s on %s, type: %s\n",
i, aAddrs[i].addr.as_string, aAddrs[i].addr.ifname, typestr);
}
return 0;
}

View file

@ -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 logging_h__
#define logging_h__
#include <sstream>
#include "mozilla/Logging.h"
#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 PRLogModuleInfo* getLogModule() { \
static PRLogModuleInfo* log; \
if (!log) \
log = PR_NewLogModule(n); \
return 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)
#endif // logging_h__

View file

@ -0,0 +1,28 @@
/* -*- 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)
} // close namespace
#endif

View file

@ -0,0 +1,13 @@
# -*- 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")
DIRS += [
'/media/mtransport/third_party',
'/media/mtransport/build',
'/media/mtransport/testlib',
]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,411 @@
/* -*- 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 <queue>
#include "nspr.h"
#include "prio.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsASocketHandler.h"
#include "nsISocketTransportService.h"
#include "nsXPCOM.h"
#include "nsIEventTarget.h"
#include "nsIUDPSocketChild.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsITCPSocketCallback.h"
#include "databuffer.h"
#include "m_cpp_utils.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/ClearOnShutdown.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 {
namespace dom {
class TCPSocketChild;
}
}
#endif
namespace mozilla {
namespace net {
union NetAddr;
}
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() {}
// Factory method; will create either an NrSocket, NrUdpSocketIpc, or
// NrTcpSocketIpc as appropriate.
static int CreateSocket(nr_transport_addr *addr, RefPtr<NrSocketBase> *sock);
// the nr_socket APIs
virtual int create(nr_transport_addr *addr) = 0;
virtual int sendto(const void *msg, size_t len,
int flags, nr_transport_addr *to) = 0;
virtual int recvfrom(void * buf, size_t maxlen,
size_t *len, int flags,
nr_transport_addr *from) = 0;
virtual int getaddr(nr_transport_addr *addrp) = 0;
virtual void close() = 0;
virtual int connect(nr_transport_addr *addr) = 0;
virtual int write(const void *msg, size_t len, size_t *written) = 0;
virtual int read(void* buf, size_t maxlen, size_t *len) = 0;
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_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
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_DECL_THREADSAFE_ISUPPORTS
// 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, 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(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, nsAutoPtr<DataBuffer> &data)
: from(from), data(data) {
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_udp_message);
PRNetAddr from;
nsAutoPtr<DataBuffer> data;
private:
~nr_udp_message() {}
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() {};
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 uint8_t *data,
uint32_t data_length);
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, 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(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();
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, nsAutoPtr<DataBuffer> buf);
void close_i();
#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
static void release_child_i(nsIUDPSocketChild* aChild, nsCOMPtr<nsIEventTarget> ststhread);
static void release_use_s();
#endif
// STS thread executor
void recv_callback_s(RefPtr<nr_udp_message> msg);
ReentrantMonitor monitor_; // protects err_and state_
bool err_;
NrSocketIpcState state_;
std::queue<RefPtr<nr_udp_message>> received_msgs_;
RefPtr<nsIUDPSocketChild> socket_child_; // only accessed from the io_thread
};
// 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_;
};
struct nr_tcp_message {
explicit nr_tcp_message(nsAutoPtr<DataBuffer> &data)
: read_bytes(0)
, data(data) {
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_tcp_message);
const uint8_t *reading_pointer() const {
return data->data() + read_bytes;
}
size_t unread_bytes() const {
return data->len() - read_bytes;
}
size_t read_bytes;
private:
~nr_tcp_message() {}
DISALLOW_COPY_ASSIGN(nr_tcp_message);
nsAutoPtr<DataBuffer> data;
};
#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
class NrTcpSocketIpc : public NrSocketIpc,
public nsITCPSocketCallback {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITCPSOCKETCALLBACK
explicit NrTcpSocketIpc(nsIThread* aThread);
// Implementations of the NrSocketBase APIs
virtual int create(nr_transport_addr *addr) override;
virtual int sendto(const void *msg, size_t len,
int flags, 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(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:
class TcpSocketReadyRunner;
DISALLOW_COPY_ASSIGN(NrTcpSocketIpc);
virtual ~NrTcpSocketIpc();
// Main thread executors of the NrSocketBase APIs
void connect_i(const nsACString &remote_addr,
uint16_t remote_port,
const nsACString &local_addr,
uint16_t local_port);
void write_i(nsAutoPtr<InfallibleTArray<uint8_t>> buf,
uint32_t tracking_number);
void close_i();
static void release_child_i(dom::TCPSocketChild* aChild, nsCOMPtr<nsIEventTarget> ststhread);
// STS thread executor
void message_sent_s(uint32_t bufferedAmount, uint32_t tracking_number);
void recv_message_s(nr_tcp_message *msg);
void update_state_s(NrSocketIpcState next_state);
void maybe_post_socket_ready();
// Accessed from UpdateReadyState (not sts_thread) to avoid sending
// runnables when not needed
NrSocketIpcState mirror_state_;
// variables that can only be accessed on STS.
NrSocketIpcState state_;
std::queue<RefPtr<nr_tcp_message>> msg_queue_;
uint32_t buffered_bytes_;
uint32_t tracking_number_;
std::deque<size_t> writes_in_flight_;
// main thread.
RefPtr<dom::TCPSocketChild> socket_child_;
};
#endif
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(nr_transport_addr *addr,
nsACString *host, int32_t *port);
} // close namespace
#endif

View file

@ -0,0 +1,265 @@
/* -*- 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 "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsIEventTarget.h"
#include "nsITimer.h"
#include "nsNetCID.h"
#include "runnable_utils.h"
#include "mozilla/DebugOnly.h"
extern "C" {
#include "nr_api.h"
#include "async_timer.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() {}
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:
// 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.
}
private:
nsCOMPtr<nsITimer> timer_;
virtual ~nrappkitTimerCallback() {}
};
NS_IMPL_ISUPPORTS(nrappkitTimerCallback, nsITimerCallback)
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_(0, 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_(0, 0, cb_arg_);
}
}
virtual void Cancel() override {
cb_ = nullptr;
}
~nrappkitScheduledCallback() {}
};
} // close namespace
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(
nsAutoPtr<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();
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return(R_FAILED);
}
nrappkitTimerCallback* callback =
new nrappkitTimerCallback(cb, arg, func, l);
rv = timer->InitWithCallback(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;
}

File diff suppressed because it is too large Load diff

390
media/mtransport/nricectx.h Normal file
View file

@ -0,0 +1,390 @@
/* -*- 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 <string>
#include <vector>
#include "sigslot.h"
#include "prnetdb.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "nsAutoPtr.h"
#include "nsIEventTarget.h"
#include "nsITimer.h"
#include "m_cpp_utils.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 {
// Timestamps set whenever a packet is dropped due to global rate limiting
// (see nr_socket_prsock.cpp)
TimeStamp nr_socket_short_term_violation_time();
TimeStamp nr_socket_long_term_violation_time();
class NrIceMediaStream;
extern const char kNrIceTransportUdp[];
extern const char kNrIceTransportTcp[];
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;
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_;
};
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 NrIceProxyServer {
public:
NrIceProxyServer(const std::string& host, uint16_t port,
const std::string& alpn) :
host_(host), port_(port), alpn_(alpn) {
}
NrIceProxyServer() : NrIceProxyServer("", 0, "") {}
const std::string& host() const { return host_; }
uint16_t port() const { return port_; }
const std::string& alpn() const { return alpn_; }
private:
std::string host_;
uint16_t port_;
std::string alpn_;
};
class TestNat;
class NrIceCtx {
friend class NrIceCtxHandler;
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
};
// initialize ICE globals, crypto, and logging
static void InitializeGlobals(bool allow_loopback = false,
bool tcp_enabled = true,
bool allow_link_local = false);
static std::string GetNewUfrag();
static std::string GetNewPwd();
bool Initialize();
bool Initialize(const std::string& ufrag, const std::string& pwd);
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();
void SetStream(size_t index, NrIceMediaStream* stream);
RefPtr<NrIceMediaStream> GetStream(size_t index) {
if (index < streams_.size()) {
return streams_[index];
}
return nullptr;
}
// Some might be null
size_t GetStreamCount() const
{
return streams_.size();
}
// The name of the ctx
const std::string& name() const { return name_; }
// Get ufrag and password.
std::string ufrag() const;
std::string pwd() const;
// 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 whether we're allowed to produce none, relay or all candidates.
// TODO(jib@mozilla.com): Work out what change means mid-connection (1181768)
nsresult SetPolicy(Policy policy);
Policy policy() const {
return policy_;
}
// 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 SetProxyServer(const NrIceProxyServer& proxy_server);
// Start ICE gathering
nsresult StartGathering(bool default_route_only, bool proxy_only);
// Start checking
nsresult StartChecks();
// Finalize the ICE negotiation. I.e., there will be no
// more forking.
nsresult Finalize();
// 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:
NrIceCtx(const std::string& name,
bool offerer,
Policy policy);
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);
ConnectionState connection_state_;
GatheringState gathering_state_;
const std::string name_;
bool offerer_;
bool ice_controlling_set_;
std::vector<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
Policy policy_;
RefPtr<TestNat> nat_;
};
} // close namespace
#endif

View file

@ -0,0 +1,160 @@
#include <sstream>
#include "logging.h"
// nICEr includes
extern "C" {
#include "nr_api.h"
#include "ice_ctx.h"
}
// Local includes
#include "nricectxhandler.h"
#include "nricemediastream.h"
#include "nriceresolver.h"
namespace mozilla {
MOZ_MTLOG_MODULE("mtransport")
NrIceCtxHandler::NrIceCtxHandler(const std::string& name,
bool offerer,
NrIceCtx::Policy policy)
: current_ctx(new NrIceCtx(name, offerer, policy)),
old_ctx(nullptr),
restart_count(0)
{
}
RefPtr<NrIceCtxHandler>
NrIceCtxHandler::Create(const std::string& name,
bool offerer,
bool allow_loopback,
bool tcp_enabled,
bool allow_link_local,
NrIceCtx::Policy policy)
{
// InitializeGlobals only executes once
NrIceCtx::InitializeGlobals(allow_loopback, tcp_enabled, allow_link_local);
RefPtr<NrIceCtxHandler> ctx = new NrIceCtxHandler(name, offerer, policy);
if (ctx == nullptr ||
ctx->current_ctx == nullptr ||
!ctx->current_ctx->Initialize()) {
return nullptr;
}
return ctx;
}
RefPtr<NrIceMediaStream>
NrIceCtxHandler::CreateStream(const std::string& name, int components)
{
// To make tracking NrIceMediaStreams easier during ICE restart
// prepend an int to the name that increments with each ICE restart
std::ostringstream os;
os << restart_count << "-" << name;
return NrIceMediaStream::Create(this->current_ctx, os.str(), components);
}
RefPtr<NrIceCtx>
NrIceCtxHandler::CreateCtx() const
{
return CreateCtx(NrIceCtx::GetNewUfrag(), NrIceCtx::GetNewPwd());
}
RefPtr<NrIceCtx>
NrIceCtxHandler::CreateCtx(const std::string& ufrag,
const std::string& pwd) const
{
RefPtr<NrIceCtx> new_ctx = new NrIceCtx(this->current_ctx->name(),
true, // offerer (hardcoded per bwc)
this->current_ctx->policy());
if (new_ctx == nullptr) {
return nullptr;
}
if (!new_ctx->Initialize(ufrag, pwd)) {
return nullptr;
}
// copy the stun, and turn servers from the current context
int r = nr_ice_ctx_set_stun_servers(new_ctx->ctx_,
this->current_ctx->ctx_->stun_servers,
this->current_ctx->ctx_->stun_server_ct);
if (r) {
MOZ_MTLOG(ML_ERROR, "Error while setting STUN servers in CreateCtx"
<< " (likely ice restart related)");
return nullptr;
}
r = nr_ice_ctx_copy_turn_servers(new_ctx->ctx_,
this->current_ctx->ctx_->turn_servers,
this->current_ctx->ctx_->turn_server_ct);
if (r) {
MOZ_MTLOG(ML_ERROR, "Error while copying TURN servers in CreateCtx"
<< " (likely ice restart related)");
return nullptr;
}
// grab the NrIceResolver stashed in the nr_resolver and allocate another
// for the new ctx. Note: there may not be an nr_resolver.
if (this->current_ctx->ctx_->resolver) {
NrIceResolver* resolver =
static_cast<NrIceResolver*>(this->current_ctx->ctx_->resolver->obj);
if (!resolver ||
NS_FAILED(new_ctx->SetResolver(resolver->AllocateResolver()))) {
MOZ_MTLOG(ML_ERROR, "Error while setting dns resolver in CreateCtx"
<< " (likely ice restart related)");
return nullptr;
}
}
return new_ctx;
}
bool
NrIceCtxHandler::BeginIceRestart(RefPtr<NrIceCtx> new_ctx)
{
MOZ_ASSERT(!old_ctx, "existing ice restart in progress");
if (old_ctx) {
MOZ_MTLOG(ML_ERROR, "Existing ice restart in progress");
return false; // ice restart already in progress
}
if (new_ctx == nullptr) {
return false;
}
++restart_count;
old_ctx = current_ctx;
current_ctx = new_ctx;
return true;
}
void
NrIceCtxHandler::FinalizeIceRestart()
{
// no harm calling this even if we're not in the middle of restarting
old_ctx = nullptr;
}
void
NrIceCtxHandler::RollbackIceRestart()
{
if (old_ctx == nullptr) {
return;
}
current_ctx = old_ctx;
old_ctx = nullptr;
}
} // close namespace

View file

@ -0,0 +1,52 @@
#ifndef nricectxhandler_h__
#define nricectxhandler_h__
#include "nricectx.h"
namespace mozilla {
class NrIceCtxHandler {
public:
// TODO(ekr@rtfm.com): Too many bools here. Bug 1193437.
static RefPtr<NrIceCtxHandler> Create(const std::string& name,
bool offerer,
bool allow_loopback = false,
bool tcp_enabled = true,
bool allow_link_local = false,
NrIceCtx::Policy policy =
NrIceCtx::ICE_POLICY_ALL);
RefPtr<NrIceMediaStream> CreateStream(const std::string& name,
int components);
// CreateCtx is necessary so we can create and initialize the context
// on main thread, but begin the ice restart mechanics on STS thread
RefPtr<NrIceCtx> CreateCtx() const; // for test
RefPtr<NrIceCtx> CreateCtx(const std::string& ufrag,
const std::string& pwd) const;
RefPtr<NrIceCtx> ctx() { return current_ctx; }
bool BeginIceRestart(RefPtr<NrIceCtx> new_ctx);
bool IsRestarting() const { return (old_ctx != nullptr); }
void FinalizeIceRestart();
void RollbackIceRestart();
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceCtxHandler)
private:
NrIceCtxHandler(const std::string& name,
bool offerer,
NrIceCtx::Policy policy);
NrIceCtxHandler() = delete;
~NrIceCtxHandler() {}
DISALLOW_COPY_ASSIGN(NrIceCtxHandler);
RefPtr<NrIceCtx> current_ctx;
RefPtr<NrIceCtx> old_ctx; // for while restart is in progress
int restart_count; // used to differentiate streams between restarted ctx
};
} // close namespace
#endif // nricectxhandler_h__

View file

@ -0,0 +1,610 @@
/* -*- 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"
// nICEr includes
extern "C" {
#include "nr_api.h"
#include "registry.h"
#include "async_timer.h"
#include "ice_util.h"
#include "transport_addr.h"
#include "nr_crypto.h"
#include "nr_socket.h"
#include "nr_socket_local.h"
#include "stun_client_ctx.h"
#include "stun_server_ctx.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:
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->isock) {
nr_transport_addr addr;
r = nr_socket_getaddr(cand->isock->sock, &addr);
if (r)
return false;
if (!ToNrIceAddr(addr, &out->local_addr))
return false;
}
NrIceCandidate::Type type;
switch (cand->type) {
case HOST:
type = NrIceCandidate::ICE_HOST;
break;
case SERVER_REFLEXIVE:
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;
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;
}
// NrIceMediaStream
RefPtr<NrIceMediaStream>
NrIceMediaStream::Create(NrIceCtx *ctx,
const std::string& name,
int components) {
RefPtr<NrIceMediaStream> stream =
new NrIceMediaStream(ctx, name, components);
MOZ_ASSERT(stream->ctx_ == ctx->ctx());
int r = nr_ice_add_media_stream(ctx->ctx(),
const_cast<char *>(name.c_str()),
components, &stream->stream_);
if (r) {
MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '"
<< name << "'");
return nullptr;
}
return stream;
}
NrIceMediaStream::NrIceMediaStream(NrIceCtx *ctx,
const std::string& name,
size_t components) :
state_(ICE_CONNECTING),
ctx_(ctx->ctx()),
ctx_peer_(ctx->peer()),
name_(name),
components_(components),
stream_(nullptr),
level_(0),
has_parsed_attrs_(false)
{
}
NrIceMediaStream::~NrIceMediaStream() {
// We do not need to destroy anything. All major resources
// are attached to the ice ctx.
}
nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>&
attributes) {
if (!stream_)
return NS_ERROR_FAILURE;
std::vector<char *> attributes_in;
for (size_t i=0; i<attributes.size(); ++i) {
attributes_in.push_back(const_cast<char *>(attributes[i].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.size() ?
&attributes_in[0] : nullptr,
attributes_in.size());
if (r) {
MOZ_MTLOG(ML_ERROR, "Couldn't parse attributes for stream "
<< name_ << "'");
return NS_ERROR_FAILURE;
}
has_parsed_attrs_ = true;
return NS_OK;
}
// Parse trickle ICE candidate
nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate) {
int r;
MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << ctx_->label << ")/STREAM(" <<
name() << ") : parsing trickle candidate " << candidate);
r = nr_ice_peer_ctx_parse_trickle_candidate(ctx_peer_,
stream_,
const_cast<char *>(
candidate.c_str())
);
if (r) {
if (r == R_ALREADY) {
MOZ_MTLOG(ML_ERROR, "Trickle candidates are redundant for stream '"
<< name_ << "' because it is completed");
} else {
MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '"
<< name_ << "'");
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 = Move(local);
if (remotep)
*remotep = 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.selected = p1->remote->component &&
p1->remote->component->active == p1;
pair.codeword = p1->codeword;
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 {
nr_ice_candidate *cand;
int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand);
if (r) {
MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '"
<< name_ << "'");
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::GetCandidates() const {
char **attrs = 0;
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;
}
nsresult NrIceMediaStream::SendPacket(int component_id,
const unsigned char *data,
size_t len) {
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;
SignalReady(this);
}
else {
MOZ_MTLOG(ML_DEBUG, "Stream ready callback fired again for '" << name_ << "'");
}
}
void NrIceMediaStream::Close() {
MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'");
state_ = ICE_CLOSED;
if (stream_) {
int r = nr_ice_remove_media_stream(ctx_, &stream_);
if (r) {
MOZ_ASSERT(false, "Failed to remove stream");
MOZ_MTLOG(ML_ERROR, "Failed to remove stream, error=" << r);
}
}
}
} // close namespace

View file

@ -0,0 +1,228 @@
/* -*- 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 "nsCOMPtr.h"
#include "nsIEventTarget.h"
#include "nsITimer.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;
Type type;
TcpType tcp_type;
std::string codeword;
};
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;
// 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;
};
class NrIceMediaStream {
public:
static RefPtr<NrIceMediaStream> Create(NrIceCtx *ctx,
const std::string& name,
int components);
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 candidates
std::vector<std::string> GetCandidates() 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 remote attributes
nsresult ParseAttributes(std::vector<std::string>& candidates);
bool HasParsedAttributes() const { return has_parsed_attrs_; }
// Parse trickle ICE candidate
nsresult ParseTrickleCandidate(const std::string& candidate);
// 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_; }
// The underlying nICEr stream
nr_ice_media_stream *stream() { return stream_; }
// 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();
// 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 level
// (ie; m-line index) the candidate belongs to.
void SetLevel(uint16_t level) { level_ = level; }
uint16_t GetLevel() const { return level_; }
sigslot::signal2<NrIceMediaStream *, 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(NrIceCtx *ctx,
const std::string& name,
size_t components);
~NrIceMediaStream();
DISALLOW_COPY_ASSIGN(NrIceMediaStream);
State state_;
nr_ice_ctx *ctx_;
nr_ice_peer_ctx *ctx_peer_;
const std::string name_;
const size_t components_;
nr_ice_media_stream *stream_;
uint16_t level_;
bool has_parsed_attrs_;
};
} // close namespace
#endif

View file

@ -0,0 +1,236 @@
/* -*- 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 "mtransport/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_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 = 0;
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;
uint32_t resolve_flags = 0;
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_->AsyncResolve(nsAutoCString(resource->domain_name),
resolve_flags, pr,
sts_thread_, getter_AddRefs(pr->request_)))) {
MOZ_MTLOG(ML_ERROR, "AsyncResolve failed.");
ABORT(R_NOT_FOUND);
}
// Because the C API offers no "finished" method to release the handle we
// 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 *record, 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;
if (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

View file

@ -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 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 <map>
#include <string>
#include "nspr.h"
#include "prnetdb.h"
#include "nsIDNSService.h"
#include "nsIDNSListener.h"
#include "nsICancelable.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(){};
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

View file

@ -0,0 +1,192 @@
/* -*- 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 "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 "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 = 0;
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

View file

@ -0,0 +1,147 @@
/* -*- 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 "nspr.h"
#include "prnetdb.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

View file

@ -0,0 +1,268 @@
/* 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 "nrinterfaceprioritizer.h"
#include "nsCOMPtr.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 + 41];
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;
}
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 (std::set<LocalAddress>::iterator i = local_addrs_.begin();
i != local_addrs_.end(); ++i) {
if (tmp_pref == 0) {
return R_FAILED;
}
preference_map_.insert(make_pair(i->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 = 0;
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

View file

@ -0,0 +1,18 @@
/* 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_api.h"
#include "nr_interface_prioritizer.h"
}
namespace mozilla {
nr_interface_prioritizer* CreateInterfacePrioritizer();
} // namespace mozilla
#endif

View file

@ -0,0 +1,191 @@
/* -*- 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 <cstdarg>
#include "rlogconnector.h"
#include <deque>
#include <string>
#include "logging.h"
#include "mozilla/Assertions.h"
#include "mozilla/Move.h" // Pinch hitting for <utility> and std::move
#include "mozilla/Mutex.h"
#include "mozilla/Sprintf.h"
#include <vector>
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) {
MOZ_ASSERT(mozilla::RLogConnector::GetInstance());
// 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() {
}
void RLogConnector::SetLogLimit(uint32_t new_limit) {
OffTheBooksMutexAutoLock lock(mutex_);
log_limit_ = new_limit;
RemoveOld();
}
void RLogConnector::Log(int level, std::string&& log) {
MOZ_MTLOG(rLogLvlToMozLogLvl(level), log);
if (level <= LOG_INFO) {
OffTheBooksMutexAutoLock lock(mutex_);
if (disableCount_ == 0) {
AddMsg(Move(log));
}
}
}
void RLogConnector::AddMsg(std::string&& msg) {
log_messages_.push_front(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

View file

@ -0,0 +1,129 @@
/* -*- 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);
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__

View file

@ -0,0 +1,258 @@
/* -*- 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 "nsThreadUtils.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/Move.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Tuple.h"
// 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)
{
nsCOMPtr<nsIRunnable> runnable_ref(runnable);
if (thread) {
bool on;
nsresult rv;
rv = thread->IsOnCurrentThread(&on);
// If the target thread has already shut down, we don't want to assert.
if (rv != NS_ERROR_NOT_INITIALIZED) {
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
if (NS_WARN_IF(NS_FAILED(rv))) {
// we're going to destroy the runnable on this thread!
return rv;
}
if (!on) {
return thread->Dispatch(runnable_ref.forget(), flags);
}
}
return runnable_ref->Run();
}
template<RunnableResult result>
class runnable_args_base : public Runnable {
public:
NS_IMETHOD Run() = 0;
};
template<typename R>
struct RunnableFunctionCallHelper
{
template<typename FunType, typename... Args, size_t... Indices>
static R apply(FunType func, Tuple<Args...>& args, IndexSequence<Indices...>)
{
return func(Get<Indices>(args)...);
}
};
// A void specialization is needed in the case where the template instantiator
// knows we don't want to return a value, but we don't know whether the called
// function returns void or something else.
template<>
struct RunnableFunctionCallHelper<void>
{
template<typename FunType, typename... Args, size_t... Indices>
static void apply(FunType func, Tuple<Args...>& args, IndexSequence<Indices...>)
{
func(Get<Indices>(args)...);
}
};
template<typename R>
struct RunnableMethodCallHelper
{
template<typename Class, typename M, typename... Args, size_t... Indices>
static R apply(Class obj, M method, Tuple<Args...>& args, IndexSequence<Indices...>)
{
return ((*obj).*method)(Get<Indices>(args)...);
}
};
// A void specialization is needed in the case where the template instantiator
// knows we don't want to return a value, but we don't know whether the called
// method returns void or something else.
template<>
struct RunnableMethodCallHelper<void>
{
template<typename Class, typename M, typename... Args, size_t... Indices>
static void apply(Class obj, M method, Tuple<Args...>& args, IndexSequence<Indices...>)
{
((*obj).*method)(Get<Indices>(args)...);
}
};
}
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|.
explicit runnable_args_func(FunType f, Args&&... args)
: mFunc(f), mArgs(Forward<Args>(args)...)
{}
NS_IMETHOD Run() {
detail::RunnableFunctionCallHelper<void>::apply(mFunc, mArgs, typename IndexSequenceFor<Args...>::Type());
return NS_OK;
}
private:
FunType mFunc;
Tuple<Args...> mArgs;
};
template<typename FunType, typename... Args>
runnable_args_func<FunType, Args...>*
WrapRunnableNM(FunType f, Args... args)
{
return new runnable_args_func<FunType, Args...>(f, Move(args)...);
}
template<typename Ret, typename FunType, typename... Args>
class runnable_args_func_ret : public detail::runnable_args_base<detail::ReturnsResult>
{
public:
runnable_args_func_ret(Ret* ret, FunType f, Args&&... args)
: mReturn(ret), mFunc(f), mArgs(Forward<Args>(args)...)
{}
NS_IMETHOD Run() {
*mReturn = detail::RunnableFunctionCallHelper<Ret>::apply(mFunc, mArgs, typename IndexSequenceFor<Args...>::Type());
return NS_OK;
}
private:
Ret* mReturn;
FunType mFunc;
Tuple<Args...> mArgs;
};
template<typename R, typename FunType, typename... Args>
runnable_args_func_ret<R, FunType, Args...>*
WrapRunnableNMRet(R* ret, FunType f, Args... args)
{
return new runnable_args_func_ret<R, FunType, Args...>(ret, f, Move(args)...);
}
template<typename Class, typename M, typename... Args>
class runnable_args_memfn : public detail::runnable_args_base<detail::NoResult>
{
public:
runnable_args_memfn(Class obj, M method, Args&&... args)
: mObj(obj), mMethod(method), mArgs(Forward<Args>(args)...)
{}
NS_IMETHOD Run() {
detail::RunnableMethodCallHelper<void>::apply(mObj, mMethod, mArgs, typename IndexSequenceFor<Args...>::Type());
return NS_OK;
}
private:
Class mObj;
M mMethod;
Tuple<Args...> mArgs;
};
template<typename Class, typename M, typename... Args>
runnable_args_memfn<Class, M, Args...>*
WrapRunnable(Class obj, M method, Args... args)
{
return new runnable_args_memfn<Class, M, Args...>(obj, method, Move(args)...);
}
template<typename Ret, typename Class, typename M, typename... Args>
class runnable_args_memfn_ret : public detail::runnable_args_base<detail::ReturnsResult>
{
public:
runnable_args_memfn_ret(Ret* ret, Class obj, M method, Args... args)
: mReturn(ret), mObj(obj), mMethod(method), mArgs(Forward<Args>(args)...)
{}
NS_IMETHOD Run() {
*mReturn = detail::RunnableMethodCallHelper<Ret>::apply(mObj, mMethod, mArgs, typename IndexSequenceFor<Args...>::Type());
return NS_OK;
}
private:
Ret* mReturn;
Class mObj;
M mMethod;
Tuple<Args...> mArgs;
};
template<typename R, typename Class, typename M, typename... Args>
runnable_args_memfn_ret<R, Class, M, Args...>*
WrapRunnableRet(R* ret, Class obj, M method, Args... args)
{
return new runnable_args_memfn_ret<R, Class, M, Args...>(ret, obj, method, Move(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) {}
NS_IMETHOD Run() {
ref_ = nullptr;
return NS_OK;
}
private:
RefPtr<T> ref_;
};
template <typename T>
DispatchedRelease<T>* WrapRelease(already_AddRefed<T>&& ref)
{
return new DispatchedRelease<T>(ref);
}
} /* namespace mozilla */
#endif

2859
media/mtransport/sigslot.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,62 @@
/* -*- 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

View file

@ -0,0 +1,55 @@
/* -*- 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__

View file

@ -0,0 +1,359 @@
/* 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>
extern "C" {
#include "nr_api.h"
#include "transport_addr.h"
#include "stun.h"
}
#include "mozilla/Attributes.h"
#include "mozilla/net/DNS.h"
#include "stun_socket_filter.h"
#include "nr_socket_prsock.h"
namespace {
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_;
};
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() {}
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()) {
return true;
}
// Check if we had sent any stun request to this destination. If we had sent a request
// to this host, we check the transaction id, and we can add this address to whitelist.
std::set<PendingSTUNRequest>::iterator it =
pending_requests_.find(PendingSTUNRequest(*remote_addr));
if (it != pending_requests_.end()) {
if (nr_is_stun_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
// 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)) {
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);
}
} 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_.insert(PendingSTUNRequest(*remote_addr, msg->id));
}
}
return true;
}
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()) {
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));
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()) {
return true;
}
}
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() {}
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;
}

View file

@ -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 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() {}
};
#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() {}
};
#endif // stun_socket_filter_h__

View file

@ -0,0 +1,61 @@
/* -*- 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 "nsThreadUtils.h"
#include "mozilla/SyncRunnable.h"
#include "gtest/gtest.h"
using namespace mozilla;
nsIThread *gThread = nullptr;
class TestRunnable : public Runnable {
public:
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());
}

View file

@ -0,0 +1,296 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Original author: ekr@rtfm.com
#include <iostream>
#include "nspr.h"
#include "nss.h"
#include "ssl.h"
extern "C" {
#include "nr_api.h"
#include "nr_socket.h"
#include "nr_socket_buffered_stun.h"
#include "transport_addr.h"
#include "stun.h"
}
#include "databuffer.h"
#include "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() {
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_ = dummy.forget(); // 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);
}

View file

@ -0,0 +1,233 @@
/* -*- 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 "databuffer.h"
#include "mozilla/UniquePtr.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
#include "gtest_utils.h"
namespace mozilla {
static UniquePtr<DataBuffer> merge(UniquePtr<DataBuffer> a, UniquePtr<DataBuffer> b) {
if (a && a->len() && b && b->len()) {
UniquePtr<DataBuffer> merged(new DataBuffer());
merged->Allocate(a->len() + b->len());
memcpy(merged->data(), a->data(), a->len());
memcpy(merged->data() + a->len(), b->data(), 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) {
return 0;
}
virtual int sendto(const void *msg, size_t len,
int flags, nr_transport_addr *to) {
MOZ_CRASH();
return 0;
}
virtual int recvfrom(void * buf, size_t maxlen,
size_t *len, int flags,
nr_transport_addr *from) {
MOZ_CRASH();
return 0;
}
virtual int getaddr(nr_transport_addr *addrp) {
MOZ_CRASH();
return 0;
}
virtual void close() {
}
virtual int connect(nr_transport_addr *addr) {
nr_transport_addr_copy(&connect_addr_, addr);
return 0;
}
virtual int listen(int backlog) {
return 0;
}
virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) {
return 0;
}
virtual int write(const void *msg, size_t len, size_t *written) {
size_t to_write = std::min(len, writable_);
if (to_write) {
UniquePtr<DataBuffer> msgbuf(new DataBuffer(static_cast<const uint8_t *>(msg), to_write));
write_buffer_ = merge(Move(write_buffer_), Move(msgbuf));
}
*written = to_write;
return 0;
}
virtual int read(void* buf, size_t maxlen, size_t *len) {
if (!read_buffer_.get()) {
return R_WOULDBLOCK;
}
size_t to_read = std::min(read_buffer_->len(),
std::min(maxlen, readable_));
memcpy(buf, read_buffer_->data(), to_read);
*len = to_read;
if (to_read < read_buffer_->len()) {
read_buffer_.reset(new DataBuffer(read_buffer_->data() + to_read,
read_buffer_->len() - to_read));
} 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) {
EXPECT_EQ(nullptr, cb_);
cb_ = cb;
cb_arg_ = cb_arg;
return 0;
}
virtual int cancel(int how) {
cb_ = nullptr;
cb_arg_ = nullptr;
return 0;
}
// Read/Manipulate the current state.
void CheckWriteBuffer(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 DataBuffer(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);
private:
~DummySocket() {}
DISALLOW_COPY_ASSIGN(DummySocket);
size_t writable_; // Amount we allow someone to write.
UniquePtr<DataBuffer> write_buffer_;
size_t readable_; // Amount we allow someone to read.
UniquePtr<DataBuffer> read_buffer_;
NR_async_cb cb_;
void *cb_arg_;
nr_socket *self_;
nr_transport_addr connect_addr_;
};
} //namespace mozilla
#endif

View file

@ -0,0 +1,83 @@
/* -*- 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) {
mozilla::SyncRunnable::DispatchToThread(
test_utils_->sts_target(),
WrapRunnable(this, &RingbufferDumper::ClearRingBuffer_s));
}
virtual void OnTestEnd(const ::testing::TestInfo& testInfo) {
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) {
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__

View file

@ -0,0 +1,203 @@
/* -*- 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,100 @@
# -*- 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['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
SOURCES += [
'buffered_stun_socket_unittest.cpp',
'ice_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_unittest.cpp',
'TestSyncRunnable.cpp',
'transport_unittests.cpp',
'turn_unittest.cpp',
]
if CONFIG['MOZ_SCTP']:
SOURCES += [
'sctp_unittest.cpp',
]
for var in ('HAVE_STRDUP', 'NR_SOCKET_IS_VOID_PTR', 'SCTP_DEBUG', 'INET'):
DEFINES[var] = True
if CONFIG['OS_TARGET'] == 'Android':
DEFINES['LINUX'] = True
DEFINES['ANDROID'] = True
LOCAL_INCLUDES += [
'/media/mtransport/third_party/nrappkit/src/port/android/include',
]
else:
DEFINES['INET6'] = True
if CONFIG['OS_TARGET'] == 'Linux':
DEFINES['LINUX'] = True
LOCAL_INCLUDES += [
'/media/mtransport/third_party/nrappkit/src/port/linux/include',
]
if CONFIG['OS_TARGET'] == 'Darwin':
LOCAL_INCLUDES += [
'/media/mtransport/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 += [
'/media/mtransport/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 += [
'/media/mtransport/',
'/media/mtransport/third_party/',
'/media/mtransport/third_party/nICEr/src/crypto',
'/media/mtransport/third_party/nICEr/src/ice',
'/media/mtransport/third_party/nICEr/src/net',
'/media/mtransport/third_party/nICEr/src/stun',
'/media/mtransport/third_party/nICEr/src/util',
'/media/mtransport/third_party/nrappkit/src/event',
'/media/mtransport/third_party/nrappkit/src/log',
'/media/mtransport/third_party/nrappkit/src/plugin',
'/media/mtransport/third_party/nrappkit/src/port/generic/include',
'/media/mtransport/third_party/nrappkit/src/registry',
'/media/mtransport/third_party/nrappkit/src/share',
'/media/mtransport/third_party/nrappkit/src/stats',
'/media/mtransport/third_party/nrappkit/src/util/',
'/media/mtransport/third_party/nrappkit/src/util/libekr',
'/netwerk/sctp/src/',
'/xpcom/tests/'
]
FINAL_LIBRARY = 'xul-gtest'
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']

View file

@ -0,0 +1,52 @@
/* -*- 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 "nsIEventTarget.h"
#include "nsPISocketTransportService.h"
#include "nsServiceManagerUtils.h"
class MtransportTestUtils {
public:
MtransportTestUtils() {
InitServices();
}
~MtransportTestUtils() {
}
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));
}
nsIEventTarget* sts_target() { return sts_target_; }
private:
nsCOMPtr<nsIEventTarget> 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

View file

@ -0,0 +1,540 @@
/* -*- 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 "nss.h"
#include "pk11pub.h"
extern "C" {
#include "nr_api.h"
#include "nr_socket.h"
#include "transport_addr.h"
#include "ice_ctx.h"
#include "nr_socket_multi_tcp.h"
}
#include "nr_socket_prsock.h"
#include "stunserver.h"
#include "nricectxhandler.h"
#include "nricemediastream.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();
ice_ctx_ = NrIceCtxHandler::Create("stun", true);
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 (std::vector<nr_socket *>::iterator it=socks.begin();
it!=socks.end(); ++it) {
nr_socket_destroy(&(*it));
}
}
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_->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()->ctx(),
&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<NrIceCtxHandler> ice_ctx_;
};
}
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));
}

View file

@ -0,0 +1,134 @@
/* -*- 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 "nsThreadUtils.h"
#include "nsXPCOM.h"
// 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() {}
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_;
};
}
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);
}

View file

@ -0,0 +1,344 @@
/* -*- 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 <iostream>
#include "nspr.h"
#include "nss.h"
#include "ssl.h"
extern "C" {
#include "nr_api.h"
#include "nr_socket.h"
#include "nr_proxy_tunnel.h"
#include "transport_addr.h"
#include "stun.h"
}
#include "databuffer.h"
#include "dummysocket.h"
#include "nr_socket_prsock.h"
#include "nriceresolverfake.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
#include "gtest_utils.h"
using namespace mozilla;
const std::string kRemoteAddr = "192.0.2.133";
const uint16_t kRemotePort = 3333;
const std::string kProxyHost = "example.com";
const std::string kProxyAddr = "192.0.2.134";
const uint16_t kProxyPort = 9999;
const std::string kHelloMessage = "HELLO";
const std::string kGarbageMessage = "xxxxxxxxxx";
std::string connect_message(const std::string &host, uint16_t port, const std::string &alpn, const std::string &tail) {
std::stringstream ss;
ss << "CONNECT " << host << ":" << port << " HTTP/1.0\r\n";
if (!alpn.empty()) {
ss << "ALPN: " << alpn << "\r\n";
}
ss << "\r\n" << tail;
return ss.str();
}
std::string connect_response(int code, const std::string &tail = "") {
std::stringstream ss;
ss << "HTTP/1.0 " << code << "\r\n\r\n" << tail;
return ss.str();
}
class DummyResolver {
public:
DummyResolver() {
vtbl_ = new nr_resolver_vtbl;
vtbl_->destroy = &DummyResolver::destroy;
vtbl_->resolve = &DummyResolver::resolve;
vtbl_->cancel = &DummyResolver::cancel;
nr_resolver_create_int((void *)this, vtbl_, &resolver_);
}
~DummyResolver() {
nr_resolver_destroy(&resolver_);
delete vtbl_;
}
static int destroy(void **objp) {
return 0;
}
static int resolve(void *obj,
nr_resolver_resource *resource,
int (*cb)(void *cb_arg, nr_transport_addr *addr),
void *cb_arg,
void **handle) {
nr_transport_addr addr;
nr_str_port_to_transport_addr(
(char *)kProxyAddr.c_str(), kProxyPort, IPPROTO_TCP, &addr);
cb(cb_arg, &addr);
return 0;
}
static int cancel(void *obj, void *handle) {
return 0;
}
nr_resolver *get_nr_resolver() {
return resolver_;
}
private:
nr_resolver_vtbl *vtbl_;
nr_resolver *resolver_;
};
class ProxyTunnelSocketTest : public MtransportTest {
public:
ProxyTunnelSocketTest()
: socket_impl_(nullptr),
nr_socket_(nullptr) {}
~ProxyTunnelSocketTest() {
nr_socket_destroy(&nr_socket_);
nr_proxy_tunnel_config_destroy(&config_);
}
void SetUp() override {
MtransportTest::SetUp();
nr_resolver_ = resolver_impl_.get_nr_resolver();
int r = nr_str_port_to_transport_addr(
(char *)kRemoteAddr.c_str(), kRemotePort, IPPROTO_TCP, &remote_addr_);
ASSERT_EQ(0, r);
r = nr_str_port_to_transport_addr(
(char *)kProxyAddr.c_str(), kProxyPort, IPPROTO_TCP, &proxy_addr_);
ASSERT_EQ(0, r);
nr_proxy_tunnel_config_create(&config_);
nr_proxy_tunnel_config_set_resolver(config_, nr_resolver_);
nr_proxy_tunnel_config_set_proxy(config_, kProxyAddr.c_str(), kProxyPort);
Configure();
}
// This reconfigures the socket with the updated information in config_.
void Configure() {
if (nr_socket_) {
EXPECT_EQ(0, nr_socket_destroy(&nr_socket_));
EXPECT_EQ(nullptr, nr_socket_);
}
RefPtr<DummySocket> dummy(new DummySocket());
int r = nr_socket_proxy_tunnel_create(
config_,
dummy->get_nr_socket(),
&nr_socket_);
ASSERT_EQ(0, r);
socket_impl_ = dummy.forget(); // Now owned by nr_socket_.
}
void Connect(int expectedReturn = 0) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
EXPECT_EQ(expectedReturn, r);
size_t written = 0;
r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
EXPECT_EQ(0, r);
EXPECT_EQ(kHelloMessage.size(), written);
}
nr_socket *socket() { return nr_socket_; }
protected:
RefPtr<DummySocket> socket_impl_;
DummyResolver resolver_impl_;
nr_socket *nr_socket_;
nr_resolver *nr_resolver_;
nr_proxy_tunnel_config *config_;
nr_transport_addr proxy_addr_;
nr_transport_addr remote_addr_;
};
TEST_F(ProxyTunnelSocketTest, TestCreate) {
}
TEST_F(ProxyTunnelSocketTest, TestConnectProxyAddress) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
ASSERT_EQ(0, r);
ASSERT_EQ(0, nr_transport_addr_cmp(socket_impl_->get_connect_addr(), &proxy_addr_,
NR_TRANSPORT_ADDR_CMP_MODE_ALL));
}
// TODO: Reenable once bug 1251212 is fixed
TEST_F(ProxyTunnelSocketTest, DISABLED_TestConnectProxyRequest) {
Connect();
std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
}
// TODO: Reenable once bug 1251212 is fixed
TEST_F(ProxyTunnelSocketTest, DISABLED_TestAlpnConnect) {
const std::string alpn = "this,is,alpn";
int r = nr_proxy_tunnel_config_set_alpn(config_, alpn.c_str());
EXPECT_EQ(0, r);
Configure();
Connect();
std::string msg = connect_message(kRemoteAddr, kRemotePort, alpn, kHelloMessage);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
}
// TODO: Reenable once bug 1251212 is fixed
TEST_F(ProxyTunnelSocketTest, DISABLED_TestNullAlpnConnect) {
int r = nr_proxy_tunnel_config_set_alpn(config_, nullptr);
EXPECT_EQ(0, r);
Configure();
Connect();
std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
}
// TODO: Reenable once bug 1251212 is fixed
TEST_F(ProxyTunnelSocketTest, DISABLED_TestConnectProxyHostRequest) {
nr_proxy_tunnel_config_set_proxy(config_, kProxyHost.c_str(), kProxyPort);
Configure();
// Because kProxyHost is a domain name and not an IP address,
// nr_socket_connect will need to resolve an IP address before continuing. It
// does that, and assumes that resolving the IP will take some time, so it
// returns R_WOULDBLOCK.
//
// However, In this test setup, the resolution happens inline immediately, so
// nr_socket_connect is called recursively on the inner socket in
// nr_socket_proxy_tunnel_resolved_cb. That also completes. Thus, the socket
// is actually successfully connected after this call, even though
// nr_socket_connect reports an error.
//
// Arguably nr_socket_proxy_tunnel_connect() is busted, because it shouldn't
// report an error when it doesn't need any further assistance from the
// calling code, but that's pretty minor.
Connect(R_WOULDBLOCK);
std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
}
// TODO: Reenable once bug 1251212 is fixed
TEST_F(ProxyTunnelSocketTest, DISABLED_TestConnectProxyWrite) {
Connect();
socket_impl_->ClearWriteBuffer();
size_t written = 0;
int r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
EXPECT_EQ(0, r);
EXPECT_EQ(kHelloMessage.size(), written);
socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
kHelloMessage.size());
}
TEST_F(ProxyTunnelSocketTest, TestConnectProxySuccessResponse) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
ASSERT_EQ(0, r);
std::string resp = connect_response(200, kHelloMessage);
socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
char buf[4096];
size_t read = 0;
r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
ASSERT_EQ(0, r);
ASSERT_EQ(kHelloMessage.size(), read);
ASSERT_EQ(0, memcmp(buf, kHelloMessage.c_str(), kHelloMessage.size()));
}
TEST_F(ProxyTunnelSocketTest, TestConnectProxyRead) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
ASSERT_EQ(0, r);
std::string resp = connect_response(200, kHelloMessage);
socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
char buf[4096];
size_t read = 0;
r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
ASSERT_EQ(0, r);
socket_impl_->ClearReadBuffer();
socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
kHelloMessage.size());
r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
ASSERT_EQ(0, r);
ASSERT_EQ(kHelloMessage.size(), read);
ASSERT_EQ(0, memcmp(buf, kHelloMessage.c_str(), kHelloMessage.size()));
}
TEST_F(ProxyTunnelSocketTest, TestConnectProxyReadNone) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
ASSERT_EQ(0, r);
std::string resp = connect_response(200);
socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
char buf[4096];
size_t read = 0;
r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
ASSERT_EQ(R_WOULDBLOCK, r);
socket_impl_->ClearReadBuffer();
socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
kHelloMessage.size());
r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
ASSERT_EQ(0, r);
}
TEST_F(ProxyTunnelSocketTest, TestConnectProxyFailResponse) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
ASSERT_EQ(0, r);
std::string resp = connect_response(500, kHelloMessage);
socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
char buf[4096];
size_t read = 0;
r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
ASSERT_NE(0, r);
}
TEST_F(ProxyTunnelSocketTest, TestConnectProxyGarbageResponse) {
int r = nr_socket_connect(nr_socket_, &remote_addr_);
ASSERT_EQ(0, r);
socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(kGarbageMessage.c_str()),
kGarbageMessage.size());
char buf[4096];
size_t read = 0;
r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
ASSERT_EQ(0ul, read);
}

View file

@ -0,0 +1,264 @@
/* -*- 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 "gtest_utils.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());
}

View file

@ -0,0 +1,227 @@
/* -*- 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 "nsXPCOM.h"
#include "nsXPCOMGlue.h"
#include "mozilla/RefPtr.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsISocketTransportService.h"
#include "nsASocketHandler.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 {
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(Destructor*) {
}
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, TestDestructor) {
bool destroyed = false;
RefPtr<Destructor> destructor = new Destructor(&destroyed);
target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target,
destructor),
NS_DISPATCH_SYNC);
ASSERT_FALSE(destroyed);
destructor = nullptr;
ASSERT_TRUE(destroyed);
}
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);
destructor = nullptr;
ASSERT_TRUE(destroyed);
}
} // end of namespace

View file

@ -0,0 +1,393 @@
/* -*- 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 <map>
#include "sigslot.h"
#include "logging.h"
#include "nsNetCID.h"
#include "nsITimer.h"
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsXPCOM.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:
SendPeriodic(TransportTestPeer *peer, int to_send) :
peer_(peer),
to_send_(to_send) {}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
protected:
virtual ~SendPeriodic() {}
TransportTestPeer *peer_;
int to_send_;
};
NS_IMPL_ISUPPORTS(SendPeriodic, nsITimerCallback)
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_(do_CreateInstance(NS_TIMER_CONTRACTID)),
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, flow_->PushLayer(loopback_));
flow_->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() {
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(const unsigned char* data, size_t len,
const RefPtr<TransportFlow>& flow) {
TransportResult res = flow->SendPacket(data, len);
delete data; // we always allocate
return res;
}
TransportResult SendPacket(const unsigned char* data, size_t len) {
unsigned char *buffer = new unsigned char[len];
memcpy(buffer, 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, buffer, len, flow_),
NS_DISPATCH_NORMAL);
return 0;
}
void PacketReceived(TransportFlow * flow, const unsigned char* data,
size_t len) {
std::cerr << "Received " << len << " bytes" << std::endl;
// Pass the data to SCTP
usrsctp_conninput(static_cast<void *>(this), data, 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_;
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;
}
class SctpTransportTest : public MtransportTest {
public:
SctpTransportTest() {
}
~SctpTransportTest() {
}
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_;
TransportTestPeer *p2_;
};
TEST_F(SctpTransportTest, TestConnect) {
ConnectSocket();
}
TEST_F(SctpTransportTest, TestConnectSymmetricalPorts) {
ConnectSocket(5002,5002);
}
TEST_F(SctpTransportTest, TestTransfer) {
ConnectSocket();
TestTransfer(50);
}
} // end namespace

View file

@ -0,0 +1,107 @@
/* -*- 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"
#include "gtest_utils.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));
}

View file

@ -0,0 +1,208 @@
/* -*- 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 "nsXPCOM.h"
#include "nsXPCOMGlue.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#include "nsIIOService.h"
#include "nsIServiceManager.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) :
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) :
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() {}
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

View file

@ -0,0 +1,664 @@
/* -*- 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 "databuffer.h"
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 "addrs.h"
#include "local_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 = 0;
delete wrapped;
return 0;
}
static int nr_socket_wrapped_sendto(void *obj, const void *msg, size_t len, int flags,
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,
0,
0,
0,
nr_socket_wrapped_close,
0,
0
};
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;
}
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;
}
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.addr->sa_family == 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"),
send_sock_,
&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_(reinterpret_cast<const uint8_t *>(data), len),
sock_(sock) {
nr_transport_addr_copy(&addr_, addr);
}
TestStunServer *server_;
DataBuffer 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++);
}
}
} // close namespace

View file

@ -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 "prio.h"
#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() {}
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

View file

@ -0,0 +1,878 @@
/* -*- 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
extern "C" {
#include "stun_msg.h" // for NR_STUN_MAX_MESSAGE_SIZE
#include "stun_util.h"
#include "nr_api.h"
#include "async_wait.h"
#include "nr_socket.h"
#include "nr_socket_local.h"
#include "stun_hint.h"
#include "local_addr.h"
#include "registry.h"
}
#include "test_nr_socket.h"
#include "nsCOMPtr.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
#include "nsAutoPtr.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,
// TODO(bug 1170299): Remove const_cast when no longer necessary
const_cast<nr_transport_addr*>(&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::TestNrSocketTest;
using mozilla::TestNat;
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]));
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,487 @@
/* -*- 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 "sigslot.h"
#include "logging.h"
#include "nspr.h"
#include "nss.h"
#include "ssl.h"
#include "nsThreadUtils.h"
#include "nsXPCOM.h"
#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 "registry.h"
#include "async_timer.h"
#include "r_crc32.h"
#include "ice_util.h"
#include "transport_addr.h"
#include "nr_crypto.h"
#include "nr_socket.h"
#include "nr_socket_local.h"
#include "nr_socket_buffered_stun.h"
#include "stun_client_ctx.h"
#include "turn_client_ctx.h"
}
#include "nricemediastream.h"
#include "nricectxhandler.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() {
}
static void SetUpTestCase() {
NrIceCtx::InitializeGlobals(false, false, false);
}
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, &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);
}

View file

@ -0,0 +1,892 @@
/* -*- 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 "nr_api.h"
#include "async_wait.h"
#include "async_timer.h"
#include "nr_socket.h"
#include "nr_socket_local.h"
#include "stun_hint.h"
#include "transport_addr.h"
}
#include "mozilla/RefPtr.h"
#include "test_nr_socket.h"
#include "runnable_utils.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.compare("ENDPOINT_INDEPENDENT")) {
return TestNat::ENDPOINT_INDEPENDENT;
} else if (!type.compare("ADDRESS_DEPENDENT")) {
return TestNat::ADDRESS_DEPENDENT;
} else 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!");
}
// TODO(bug 1170299): Remove const_cast when no longer necessary
if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&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;
}
TestNrSocket::TestNrSocket(TestNat *nat)
: nat_(nat),
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.
// TODO(bug 1170299): Remove const_cast when no longer necessary
if ((r = nr_transport_addr_copy(
&nat_external_addr,
const_cast<nr_transport_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);
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) {
return NrSocketBase::CreateSocket(addr, &internal_socket_);
}
int TestNrSocket::getaddr(nr_transport_addr *addrp) {
return internal_socket_->getaddr(addrp);
}
void TestNrSocket::close() {
if (timer_handle_) {
NR_async_timer_cancel(timer_handle_);
timer_handle_ = 0;
}
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 %s listening",
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, nr_transport_addr *to) {
MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg));
if (nat_->block_stun_ &&
nr_is_stun_message(buf, len)) {
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_) {
// Silently eat the packet
return 0;
}
// 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);
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);
readable_socket_ = nullptr;
if (!r) {
PortMapping *port_mapping_used;
ingress_allowed = allow_ingress(*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 lame 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 &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));
*port_mapping_used = get_port_mapping(from, nat_->filtering_type_);
if (!(*port_mapping_used)) {
r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
"Filtered",
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(nr_transport_addr *addr) {
if (connect_invoked_ || !port_mappings_.empty()) {
MOZ_CRASH("TestNrSocket::connect() called more than once!");
return R_INTERNAL;
}
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) {
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);
} else {
destroy_stale_port_mappings();
if (port_mappings_.empty()) {
return -1;
}
// 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) {
if (port_mappings_.empty()) {
return internal_socket_->read(buf, maxlen, len);
} else {
MOZ_ASSERT(port_mappings_.size() == 1);
int bytesRead =
port_mappings_.front()->external_socket_->read(buf, maxlen, len);
if (bytesRead > 0 && nat_->refresh_on_ingress_) {
port_mappings_.front()->last_used_ = PR_IntervalNow();
}
return bytesRead;
}
}
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);
// 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 {
// 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);
} else 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");
// 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!");
}
// TODO(bug 1170299): Remove const_cast when no longer necessary
if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&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 %s ready for read",
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 {
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;
}
for (PortMapping *port_mapping : port_mappings_) {
// TODO(bug 1170299): Remove const_cast when no longer necessary
if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&remote_address),
&port_mapping->remote_address_,
compare_flags))
return port_mapping;
}
return nullptr;
}
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) {
// TODO(bug 1170299): Remove const_cast when no longer necessary
nr_transport_addr_copy(&remote_address_,
const_cast<nr_transport_addr*>(&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,
// TODO(bug 1170299): Remove const_cast when no longer necessary
const_cast<nr_transport_addr*>(&to));
if (r == R_WOULDBLOCK) {
r_log(LOG_GENERIC, LOG_DEBUG, "Enqueueing UDP packet to %s", to.as_string);
send_queue_.push_back(RefPtr<UdpPacket>(new UdpPacket(msg, len, to)));
return 0;
} else 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);
}
} // namespace mozilla

View file

@ -0,0 +1,335 @@
/* -*- 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 <vector>
#include <map>
#include <list>
#include <string>
#include "mozilla/UniquePtr.h"
#include "prinrval.h"
namespace mozilla {
class TestNrSocket;
/**
* 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:
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),
delay_stun_resp_ms_(0),
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);
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_;
/* Note: this can only delay a single response so far (bug 1253657) */
uint32_t delay_stun_resp_ms_;
private:
std::set<TestNrSocket*> sockets_;
~TestNat(){}
};
/**
* 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, 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(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 DataBuffer(static_cast<const uint8_t*>(msg), len)) {
// TODO(bug 1170299): Remove const_cast when no longer necessary
nr_transport_addr_copy(&remote_address_,
const_cast<nr_transport_addr*>(&addr));
}
nr_transport_addr remote_address_;
UniquePtr<DataBuffer> buffer_;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UdpPacket);
private:
~UdpPacket(){}
};
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<RefPtr<UdpPacket>> send_queue_;
};
struct DeferredPacket {
DeferredPacket(TestNrSocket *sock,
const void *data, size_t len,
int flags,
nr_transport_addr *addr,
RefPtr<NrSocketBase> internal_socket) :
socket_(sock),
buffer_(reinterpret_cast<const uint8_t *>(data), len),
flags_(flags),
internal_socket_(internal_socket) {
nr_transport_addr_copy(&to_, addr);
}
TestNrSocket *socket_;
DataBuffer 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 &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;
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);
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_;
// 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_;
};
} // namespace mozilla
#endif // test_nr_socket__

View file

@ -0,0 +1,17 @@
# -*- 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('../common.build')
include("/ipc/chromium/chromium-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.
SOURCES += mtransport_cppsrcs
Library('mtransport_s')
FORCE_STATIC_LIB = True

40
media/mtransport/third_party/import.py vendored Normal file
View file

@ -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)

76
media/mtransport/third_party/moz.build vendored Normal file
View file

@ -0,0 +1,76 @@
# -*- 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',
]
# These files cannot be built in unified mode because of name clashes on RCSSTRING
nICEr_non_unified_sources = [
'nICEr/src/crypto/nr_crypto.c',
'nICEr/src/ice/ice_candidate.c',
'nICEr/src/ice/ice_candidate_pair.c',
'nICEr/src/ice/ice_component.c',
'nICEr/src/ice/ice_ctx.c',
'nICEr/src/ice/ice_media_stream.c',
'nICEr/src/ice/ice_parser.c',
'nICEr/src/ice/ice_peer_ctx.c',
'nICEr/src/ice/ice_socket.c',
'nICEr/src/net/nr_socket.c',
'nICEr/src/net/transport_addr.c',
'nICEr/src/net/transport_addr_reg.c',
'nICEr/src/stun/addrs.c',
'nICEr/src/stun/nr_socket_turn.c',
'nICEr/src/stun/stun_build.c',
'nICEr/src/stun/stun_client_ctx.c',
'nICEr/src/stun/stun_codec.c',
'nICEr/src/stun/stun_hint.c',
'nICEr/src/stun/stun_msg.c',
'nICEr/src/stun/stun_proc.c',
'nICEr/src/stun/stun_server_ctx.c',
'nICEr/src/stun/stun_util.c',
'nICEr/src/stun/turn_client_ctx.c',
'nICEr/src/util/cb_args.c',
'nICEr/src/util/ice_util.c',
]
# This file cannot be built in unified mode because it breaks Linux ASAN builds
nICEr_non_unified_sources += [
'nICEr/src/util/mbslen.c',
]
nrappkit_non_unified_sources = [
'nrappkit/src/log/r_log.c',
'nrappkit/src/util/byteorder.c',
'nrappkit/src/util/hex.c',
'nrappkit/src/util/libekr/debug.c',
'nrappkit/src/util/libekr/r_assoc.c',
'nrappkit/src/util/libekr/r_crc32.c',
'nrappkit/src/util/libekr/r_data.c',
'nrappkit/src/util/libekr/r_errors.c',
'nrappkit/src/util/libekr/r_list.c',
'nrappkit/src/util/libekr/r_memory.c',
'nrappkit/src/util/libekr/r_replace.c',
'nrappkit/src/util/libekr/r_time.c',
'nrappkit/src/util/p_buf.c',
'nrappkit/src/util/util.c',
]
GYP_DIRS['nICEr'].input = 'nICEr/nicer.gyp'
GYP_DIRS['nICEr'].variables = gyp_vars
# We allow warnings for third-party code that can be updated from upstream.
GYP_DIRS['nICEr'].sandbox_vars['ALLOW_COMPILER_WARNINGS'] = True
GYP_DIRS['nICEr'].sandbox_vars['FINAL_LIBRARY'] = 'webrtc'
GYP_DIRS['nICEr'].non_unified_sources += nICEr_non_unified_sources
GYP_DIRS['nrappkit'].input = 'nrappkit/nrappkit.gyp'
GYP_DIRS['nrappkit'].variables = gyp_vars
# We allow warnings for third-party code that can be updated from upstream.
GYP_DIRS['nrappkit'].sandbox_vars['ALLOW_COMPILER_WARNINGS'] = True
GYP_DIRS['nrappkit'].sandbox_vars['FINAL_LIBRARY'] = 'webrtc'
GYP_DIRS['nrappkit'].non_unified_sources += nrappkit_non_unified_sources

View file

@ -0,0 +1,71 @@
# 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/ifaddrs-android.c
./src/stun/ifaddrs-android.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
./src/util/mbslen.c
./src/util/mbslen.h

View file

@ -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.

View file

@ -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.

View file

@ -0,0 +1,254 @@
# 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' : {
'build_with_gonk%': 0,
'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_proxy_tunnel.c",
"./src/net/nr_proxy_tunnel.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/ifaddrs-android.c",
"./src/stun/ifaddrs-android.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",
"./src/util/mbslen.c",
"./src/util/mbslen.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',
],
'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',
'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'
],
}],
## Linux/Android
[ '(OS == "linux") or (OS=="android")', {
'cflags_mozilla': [
'-Wall',
'-Wno-parentheses',
'-Wno-strict-prototypes',
'-Wmissing-prototypes',
],
'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': [
],
}],
['moz_widget_toolkit_gonk==1', {
'defines' : [
'WEBRTC_GONK',
'NO_REG_RPC',
],
}],
# Gonk has its own nr_stun_get_addrs implementation.
['build_with_gonk==1', {
'defines': [
"USE_PLATFORM_NR_STUN_GET_ADDRS",
]
}],
['have_ethtool_cmd_speed_hi==0', {
'defines': [
"DONT_HAVE_ETHTOOL_SPEED_HI",
]
}]
],
}]
}

View file

@ -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.
*/
static char *RCSSTRING __UNUSED__="$Id: nr_crypto.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
#include <nr_api.h>
#include "nr_crypto.h"
static int nr_ice_crypto_dummy_random_bytes(UCHAR *buf, int len)
{
fprintf(stderr,"Need to define crypto API implementation\n");
exit(1);
}
static int nr_ice_crypto_dummy_hmac_sha1(UCHAR *key, int key_l, UCHAR *buf, int 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, int 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;

View file

@ -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, int len);
int (*hmac_sha1)(UCHAR *key, int key_l, UCHAR *buf, int buf_l, UCHAR digest[20]);
int (*md5)(UCHAR *buf, int 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

View file

@ -0,0 +1,979 @@
/*
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.
*/
static char *RCSSTRING __UNUSED__="$Id: ice_candidate.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
#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)
{
int _status;
*label = 0;
switch(cand->stun_server->type) {
case NR_ICE_STUN_SERVER_TYPE_ADDR:
snprintf(label, size, "%s(%s|%s)", nr_ctype_name(cand->type), cand->base.as_string,
cand->stun_server->u.addr.as_string);
break;
case NR_ICE_STUN_SERVER_TYPE_DNSNAME:
snprintf(label, size, "%s(%s|%s:%u)", nr_ctype_name(cand->type), cand->base.as_string,
cand->stun_server->u.dnsname.host, cand->stun_server->u.dnsname.port);
break;
default:
assert(0);
ABORT(R_BAD_ARGS);
}
_status=0;
abort:
return(_status);
}
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(!(ctx->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 || !cand->done_cb) {
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! */
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;
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);
}
switch(cand->type){
case HOST:
break;
#ifdef USE_TURN
case RELAYED:
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;
}
NR_async_timer_cancel(cand->delay_timer);
NR_async_timer_cancel(cand->ready_cb_timer);
if(cand->resolver_handle){
nr_resolver_cancel(cand->ctx->resolver,cand->resolver_handle);
}
RFREE(cand->foundation);
RFREE(cand->label);
RFREE(cand);
return(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;
if(cand->type != foundation->type)
goto next;
if(cand->stun_server != foundation->stun_server)
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;
foundation->stun_server=cand->stun_server;
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);
}
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(cand->stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR) {
if(nr_transport_addr_cmp(&cand->base,&cand->stun_server->u.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->u.addr.ip_version,cand->stun_server->u.addr.protocol==IPPROTO_UDP?"UDP":"TCP");
ABORT(R_NOT_FOUND); /* Same error code when DNS lookup fails */
}
/* Just copy the address */
if (r=nr_transport_addr_copy(&cand->stun_server_addr,
&cand->stun_server->u.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);
}
else {
nr_resolver_resource resource;
resource.domain_name=cand->stun_server->u.dnsname.host;
resource.port=cand->stun_server->u.dnsname.port;
resource.stun_turn=protocol;
resource.transport_protocol=cand->stun_server->transport;
switch (cand->base.ip_version) {
case NR_IPV4:
resource.address_family=AF_INET;
break;
case NR_IPV6:
resource.address_family=AF_INET6;
break;
default:
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);
}
}
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);
}
/* 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->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 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(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->ctx->flags &
(NR_ICE_CTX_FLAGS_RELAY_ONLY |
NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES)) ?
&cand->addr : &cand->base;
switch(cand->type){
case HOST:
break;
case SERVER_REFLEXIVE:
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);
}

View file

@ -0,0 +1,121 @@
/*
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;
#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 *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);
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

View file

@ -0,0 +1,684 @@
/*
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.
*/
static char *RCSSTRING __UNUSED__="$Id: ice_candidate_pair.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
#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;
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->pctx->ctx->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:
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, &copy))
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);
if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,copy))
ABORT(r);
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);
}
/* 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);
}
int 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);
}
return(0);
}
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);
}
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_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){
if(r=nr_ice_component_failed_pair(pair->remote->component, pair))
ABORT(r);
}
_status=0;
abort:
return(_status);
}
int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out)
{
/*r_log(LOG_ICE,LOG_DEBUG,"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);*/
return(0);
}
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);
}
int 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);
return(0);
}
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:
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);
}

View file

@ -0,0 +1,95 @@
/*
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 */
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);
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 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);
int 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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,104 @@
/*
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;
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);
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);
int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);
int 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);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,203 @@
/*
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"
#define NR_ICE_STUN_SERVER_TYPE_ADDR 1
#define NR_ICE_STUN_SERVER_TYPE_DNSNAME 2
typedef struct nr_ice_stun_server_ {
int type;
union {
nr_transport_addr addr;
struct {
char host[256]; /* Limit from RFC 1034, plus a 0 byte */
UINT2 port;
} dnsname;
} u;
int id;
int transport;
} nr_ice_stun_server;
typedef struct nr_ice_turn_server_ {
nr_ice_stun_server turn_server;
char *username;
Data *password;
} nr_ice_turn_server;
typedef struct nr_ice_foundation_ {
int index;
nr_transport_addr addr;
int type;
nr_ice_stun_server *stun_server;
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;
struct nr_ice_ctx_ {
UINT4 flags;
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 */
int turn_server_ct;
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_wrapper_factory *turn_tcp_socket_wrapper; /* The TURN TCP socket wrapper to use */
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];
};
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_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)
#define NR_ICE_CTX_FLAGS_RELAY_ONLY (1<<4)
#define NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES (1<<5)
#define NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS (1<<6)
#define NR_ICE_CTX_FLAGS_ONLY_PROXY (1<<7)
void nr_ice_ctx_add_flags(nr_ice_ctx *ctx, UINT4 flags);
void nr_ice_ctx_remove_flags(nr_ice_ctx *ctx, UINT4 flags);
int nr_ice_ctx_destroy(nr_ice_ctx **ctxp);
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,char *label,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);
int nr_ice_ctx_set_turn_tcp_socket_wrapper(nr_ice_ctx *ctx, nr_socket_wrapper_factory *wrapper);
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

View file

@ -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

View file

@ -0,0 +1,933 @@
/*
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.
*/
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_util.h"
#include "ice_ctx.h"
static char *nr_ice_media_stream_states[]={"INVALID",
"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;
if(!(stream=RCALLOC(sizeof(nr_ice_media_stream))))
ABORT(R_NO_MEMORY);
if(!(stream->label=r_strdup(label)))
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->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);
}
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);
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;
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=0;
nr_ice_component *comp;
char **attrs=0;
int index=0;
nr_ice_candidate *cand;
int r,_status;
*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);
}
if(attrct < 1){
r_log(LOG_ICE,LOG_ERR,"ICE-STREAM(%s): Failed to find any components for stream",stream->label);
ABORT(R_FAILED);
}
/* 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))
ABORT(r);
index++;
}
cand=TAILQ_NEXT(cand,entry_comp);
}
}
comp=STAILQ_NEXT(comp,entry);
}
*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_COMPLETED) {
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_WARNING,"ICE-PEER(%s): no pairs for %s",stream->pctx->label,stream->label);
}
_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)
{
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);
}
/* 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_COMPLETED) {
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;
nr_ice_component *comp;
int invalid_comps=0;
/* 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);
}
/* 2. See if there is a pair in the valid list for every component */
comp=STAILQ_FIRST(&stream->components);
while(comp){
if(!comp->valid_pairs)
invalid_comps++;
comp=STAILQ_NEXT(comp,entry);
}
/* If there is a pair in the valid list for every component... */
/* Now go through the check lists for the other streams */
str=STAILQ_FIRST(&stream->pctx->peer_streams);
while(str){
if(str!=stream){
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);
}
/* nr_ice_media_stream_dump_state(stream->pctx,stream,stderr); */
_status=0;
abort:
return(_status);
}
int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out)
{
nr_ice_cand_pair *pair;
/* r_log(LOG_ICE,LOG_DEBUG,"MEDIA-STREAM(%s): state dump", stream->label); */
pair=TAILQ_FIRST(&stream->check_list);
while(pair){
nr_ice_candidate_pair_dump_state(pair,out);
pair=TAILQ_NEXT(pair,check_queue_entry);
}
return(0);
}
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(state < sizeof(nr_ice_media_stream_states)/sizeof(char *));
assert(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;
return(0);
}
/* S OK, this component has a nominated. If every component has a nominated,
the stream is ready */
int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component)
{
int r,_status;
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)
goto done;
/* 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_COMPLETED);
/* Cancel our timer */
if(stream->timer){
NR_async_timer_cancel(stream->timer);
stream->timer=0;
}
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 connected */
if(r=nr_ice_peer_ctx_check_if_connected(stream->pctx))
ABORT(r);
done:
_status=0;
abort:
return(_status);
}
int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component)
{
int r,_status;
nr_ice_cand_pair *p2;
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);
/* OK, we need to cancel off everything on this component */
p2=TAILQ_FIRST(&stream->check_list);
while(p2){
if(r=nr_ice_candidate_pair_cancel(p2->pctx,p2,0))
ABORT(r);
p2=TAILQ_NEXT(p2,check_queue_entry);
}
/* Cancel our timer */
if(stream->timer){
NR_async_timer_cancel(stream->timer);
stream->timer=0;
}
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 connected */
if(r=nr_ice_peer_ctx_check_if_connected(stream->pctx))
ABORT(r);
_status=0;
abort:
return(_status);
}
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))
ABORT(r);
_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;
assert(stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED);
/* 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);
}

View file

@ -0,0 +1,108 @@
/*
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 */
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;
#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;
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,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_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);
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);
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

View file

@ -0,0 +1,575 @@
/*
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.
*/
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
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);
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);
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;
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);
RFREE(pctx->peer_ufrag);
pctx->peer_ufrag = 0;
if ((r=grab_token(&str, &pctx->peer_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(pctx->peer_pwd);
pctx->peer_pwd = 0;
if ((r=grab_token(&str, &pctx->peer_pwd)))
ABORT(r);
}
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);
}

View file

@ -0,0 +1,825 @@
/*
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.
*/
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 <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_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);
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{
if(ctx->flags & NR_ICE_CTX_FLAGS_OFFERER)
pctx->controlling=1;
else if(ctx->flags & NR_ICE_CTX_FLAGS_ANSWERER)
pctx->controlling=0;
}
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_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_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;
if (r=nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct))
ABORT(r);
/* Now that we have the ufrag and password, compute all the username/password
pairs */
lufrag=stream->ufrag?stream->ufrag:pctx->ctx->ufrag;
lpwd=stream->pwd?stream->pwd:pctx->ctx->pwd;
assert(lufrag);
assert(lpwd);
rufrag=pstream->ufrag?pstream->ufrag:pctx->peer_ufrag;
rpwd=pstream->pwd?pstream->pwd:pctx->peer_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);
_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])) {
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])) {
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 */
return(0);
}
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;
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);
}
/* 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_WARNING,"Peer answered with more components than we offered");
ABORT(R_BAD_DATA);
}
if (comp->state == NR_ICE_COMPONENT_DISABLED) {
r_log(LOG_ICE,LOG_WARNING,"Peer offered candidates for disabled remote component");
ABORT(R_BAD_DATA);
}
if (comp->local_component->state == NR_ICE_COMPONENT_DISABLED) {
r_log(LOG_ICE,LOG_WARNING,"Peer offered candidates for disabled local component");
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);
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)
{
nr_ice_media_stream *pstream;
int r,_status;
int needs_pairing = 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)){
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);
}
/* 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) {
/* 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;
/* Start grace period timer for incoming trickle candidates */
nr_ice_peer_ctx_start_trickle_timer(pctx);
stream=STAILQ_FIRST(&pctx->peer_streams);
while(stream){
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);
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);
}
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;
NR_async_timer_cancel(pctx->connected_cb_timer);
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);
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);
}
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;
int started = 0;
/* 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;
if((r=nr_ice_peer_ctx_check_if_connected(pctx))) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) initial connected check failed",pctx->ctx->label,pctx->label);
ABORT(r);
}
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) {
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) {
r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no checks to start",pctx->ctx->label,pctx->label);
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);
}
}
}
#ifndef NDEBUG
int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out)
{
int r,_status;
nr_ice_media_stream *stream;
fprintf(out,"PEER %s STATE DUMP\n",pctx->label);
fprintf(out,"==========================================\n");
stream=STAILQ_FIRST(&pctx->peer_streams);
while(stream){
if(r=nr_ice_media_stream_dump_state(pctx,stream,out))
ABORT(r);
stream=STAILQ_NEXT(stream,entry);
}
fprintf(out,"==========================================\n");
_status=0;
abort:
return(_status);
}
#endif
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);
}
}
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 */
int nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx)
{
int _status;
nr_ice_media_stream *str;
int failed=0;
int succeeded=0;
str=STAILQ_FIRST(&pctx->peer_streams);
while(str){
if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED){
succeeded++;
}
else if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FAILED){
failed++;
}
else{
break;
}
str=STAILQ_NEXT(str,entry);
}
if(str)
goto done; /* 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);
/* 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);
}
done:
_status=0;
// abort:
return(_status);
}
/* 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);
/* 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);
}
}
}

View file

@ -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;
char *peer_ufrag;
char *peer_pwd;
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);
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_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);
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_disconnected(nr_ice_peer_ctx *pctx);
void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx);
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_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

View file

@ -0,0 +1,79 @@
/*
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"
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif

View file

@ -0,0 +1,380 @@
/*
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.
*/
static char *RCSSTRING __UNUSED__="$Id: ice_socket.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
#include <assert.h>
#include <string.h>
#include "nr_api.h"
#include "ice_ctx.h"
#include "stun.h"
#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);
/* Re-arm first! */
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=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);
}
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);
}

View file

@ -0,0 +1,97 @@
/*
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);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif

View file

@ -0,0 +1,61 @@
/*
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)
{
nr_transport_addr_copy(&(to->addr), &(from->addr));
to->interface = from->interface;
return(0);
}
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",
vpn, type, addr->interface.estimated_speed);
buf[len - 1] = '\0';
return (0);
}

View file

@ -0,0 +1,59 @@
/*
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 0x0
#define NR_INTERFACE_TYPE_WIRED 0x1
#define NR_INTERFACE_TYPE_WIFI 0x2
#define NR_INTERFACE_TYPE_MOBILE 0x4
#define NR_INTERFACE_TYPE_VPN 0x8
int estimated_speed; /* Speed in kbps */
} nr_interface;
typedef struct nr_local_addr_ {
nr_transport_addr addr;
nr_interface interface;
} 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

View file

@ -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);
}

View file

@ -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

View file

@ -0,0 +1,686 @@
/*
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 <assert.h>
#include "nr_proxy_tunnel.h"
#define MAX_HTTP_CONNECT_ADDR_SIZE 256
#define MAX_HTTP_CONNECT_BUFFER_SIZE 1024
#define MAX_ALPN_LENGTH 64
#ifndef CRLF
#define CRLF "\r\n"
#endif
#define END_HEADERS CRLF CRLF
typedef enum {
PROXY_TUNNEL_NONE=0,
PROXY_TUNNEL_REQUESTED,
PROXY_TUNNEL_CONNECTED,
PROXY_TUNNEL_CLOSED,
PROXY_TUNNEL_FAILED
} nr_socket_proxy_tunnel_state;
typedef struct nr_socket_proxy_tunnel_ {
nr_proxy_tunnel_config *config;
nr_socket *inner;
nr_transport_addr remote_addr;
nr_socket_proxy_tunnel_state state;
char buffer[MAX_HTTP_CONNECT_BUFFER_SIZE];
size_t buffered_bytes;
void *resolver_handle;
} nr_socket_proxy_tunnel;
typedef struct nr_socket_wrapper_factory_proxy_tunnel_ {
nr_proxy_tunnel_config *config;
} nr_socket_wrapper_factory_proxy_tunnel;
static int nr_socket_proxy_tunnel_destroy(void **objpp);
static int nr_socket_proxy_tunnel_getfd(void *obj, NR_SOCKET *fd);
static int nr_socket_proxy_tunnel_getaddr(void *obj, nr_transport_addr *addrp);
static int nr_socket_proxy_tunnel_connect(void *sock, nr_transport_addr *addr);
static int nr_socket_proxy_tunnel_write(void *obj, const void *msg, size_t len, size_t *written);
static int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen, size_t *len);
static int nr_socket_proxy_tunnel_close(void *obj);
int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp);
int nr_socket_wrapper_factory_proxy_tunnel_wrap(void *obj,
nr_socket *inner,
nr_socket **socketpp);
int nr_socket_wrapper_factory_proxy_tunnel_destroy(void **objpp);
static nr_socket_vtbl nr_socket_proxy_tunnel_vtbl={
1,
nr_socket_proxy_tunnel_destroy,
0,
0,
nr_socket_proxy_tunnel_getfd,
nr_socket_proxy_tunnel_getaddr,
nr_socket_proxy_tunnel_connect,
nr_socket_proxy_tunnel_write,
nr_socket_proxy_tunnel_read,
nr_socket_proxy_tunnel_close
};
static int send_http_connect(nr_socket_proxy_tunnel *sock)
{
int r, _status;
int port;
int printed;
char addr[MAX_HTTP_CONNECT_ADDR_SIZE];
char mesg[MAX_HTTP_CONNECT_ADDR_SIZE + MAX_ALPN_LENGTH + 128];
size_t offset = 0;
size_t bytes_sent;
r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect");
if ((r=nr_transport_addr_get_port(&sock->remote_addr, &port))) {
ABORT(r);
}
if ((r=nr_transport_addr_get_addrstring(&sock->remote_addr, addr, sizeof(addr)))) {
ABORT(r);
}
printed = snprintf(mesg + offset, sizeof(mesg) - offset,
"CONNECT %s:%d HTTP/1.0", addr, port);
offset += printed;
if (printed < 0 || (offset >= sizeof(mesg))) {
ABORT(R_FAILED);
}
if (sock->config->alpn) {
printed = snprintf(mesg + offset, sizeof(mesg) - offset,
CRLF "ALPN: %s", sock->config->alpn);
offset += printed;
if (printed < 0 || (offset >= sizeof(mesg))) {
ABORT(R_FAILED);
}
}
if (offset + sizeof(END_HEADERS) >= sizeof(mesg)) {
ABORT(R_FAILED);
}
memcpy(mesg + offset, END_HEADERS, strlen(END_HEADERS));
offset += strlen(END_HEADERS);
if ((r=nr_socket_write(sock->inner, mesg, offset, &bytes_sent, 0))) {
ABORT(r);
}
if (bytes_sent < offset) {
/* TODO(bug 1116583): buffering and wait for */
r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect should be buffering %lu", (unsigned long)bytes_sent);
ABORT(R_IO_ERROR);
}
sock->state = PROXY_TUNNEL_REQUESTED;
_status = 0;
abort:
return(_status);
}
static char *find_http_terminator(char *response, size_t len)
{
char *term = response;
char *end = response + len;
int N = strlen(END_HEADERS);
for (; term = memchr(term, '\r', end - term); ++term) {
if (end - term >= N && memcmp(term, END_HEADERS, N) == 0) {
return term;
}
}
return NULL;
}
static int parse_http_response(char *begin, char *end, unsigned int *status)
{
size_t len = end - begin;
char response[MAX_HTTP_CONNECT_BUFFER_SIZE + 1];
// len should *never* be greater than nr_socket_proxy_tunnel::buffered_bytes.
// Which in turn should never be greater nr_socket_proxy_tunnel::buffer size.
assert(len <= MAX_HTTP_CONNECT_BUFFER_SIZE);
if (len > MAX_HTTP_CONNECT_BUFFER_SIZE) {
return R_BAD_DATA;
}
memcpy(response, begin, len);
response[len] = '\0';
// http://www.rfc-editor.org/rfc/rfc7230.txt
// status-line = HTTP-version SP status-code SP reason-phrase CRLF
// HTTP-version = HTTP-name "/" DIGIT "." DIGIT
// HTTP-name = "HTTP" ; "HTTP", case-sensitive
// status-code = 3DIGIT
if (sscanf(response, "HTTP/%*u.%*u %u", status) != 1) {
r_log(LOG_GENERIC,LOG_WARNING,"parse_http_response failed to find status (%s)", response);
return R_BAD_DATA;
}
return 0;
}
static int nr_socket_proxy_tunnel_destroy(void **objpp)
{
nr_socket_proxy_tunnel *sock;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_destroy");
if (!objpp || !*objpp)
return 0;
sock = (nr_socket_proxy_tunnel *)*objpp;
*objpp = 0;
if (sock->resolver_handle) {
nr_resolver_cancel(sock->config->resolver, sock->resolver_handle);
}
nr_proxy_tunnel_config_destroy(&sock->config);
nr_socket_destroy(&sock->inner);
RFREE(sock);
return 0;
}
static int nr_socket_proxy_tunnel_getfd(void *obj, NR_SOCKET *fd)
{
nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel *)obj;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_getfd");
return nr_socket_getfd(sock->inner, fd);
}
static int nr_socket_proxy_tunnel_getaddr(void *obj, nr_transport_addr *addrp)
{
nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel *)obj;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_getaddr");
return nr_socket_getaddr(sock->inner, addrp);
}
static int nr_socket_proxy_tunnel_resolved_cb(void *obj, nr_transport_addr *proxy_addr)
{
int r, _status;
nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_resolved_cb");
/* Mark the socket resolver as completed */
sock->resolver_handle = 0;
if (proxy_addr) {
r_log(LOG_GENERIC,LOG_DEBUG,"Resolved proxy address %s -> %s",
sock->config->proxy_host, proxy_addr->as_string);
}
else {
r_log(LOG_GENERIC,LOG_WARNING,"Failed to resolve proxy %s",
sock->config->proxy_host);
/* TODO: Mozilla bug 1241758: because of the callback the return value goes
* nowhere, so we can't mark the candidate as failed, so everything depends
* on the overall timeouts in this case. */
sock->state = PROXY_TUNNEL_FAILED;
ABORT(R_NOT_FOUND);
}
if ((r=nr_socket_connect(sock->inner, proxy_addr))) {
ABORT(r);
}
_status = 0;
abort:
return(_status);
}
int nr_socket_proxy_tunnel_connect(void *obj, nr_transport_addr *addr)
{
int r, _status;
int has_addr;
nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
nr_proxy_tunnel_config *config = sock->config;
nr_transport_addr proxy_addr, local_addr;
nr_resolver_resource resource;
if ((r=nr_transport_addr_copy(&sock->remote_addr, addr))) {
ABORT(r);
}
assert(config->proxy_host);
/* Check if the proxy_host is already an IP address */
has_addr = !nr_str_port_to_transport_addr(config->proxy_host,
config->proxy_port, IPPROTO_TCP, &proxy_addr);
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_connect: %s", config->proxy_host);
if (!has_addr && !config->resolver) {
r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_connect name resolver not configured");
ABORT(R_NOT_FOUND);
}
if (!has_addr) {
resource.domain_name=config->proxy_host;
resource.port=config->proxy_port;
resource.stun_turn=NR_RESOLVE_PROTOCOL_TURN;
resource.transport_protocol=IPPROTO_TCP;
if ((r=nr_socket_getaddr(sock->inner, &local_addr))) {
r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_connect failed to get local address");
ABORT(r);
}
switch(local_addr.ip_version) {
case NR_IPV4:
resource.address_family=AF_INET;
break;
case NR_IPV6:
resource.address_family=AF_INET6;
break;
default:
ABORT(R_BAD_ARGS);
}
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_connect: nr_resolver_resolve");
if ((r=nr_resolver_resolve(config->resolver, &resource,
nr_socket_proxy_tunnel_resolved_cb, (void *)sock, &sock->resolver_handle))) {
r_log(LOG_GENERIC,LOG_ERR,"Could not invoke DNS resolver");
ABORT(r);
}
ABORT(R_WOULDBLOCK);
}
if ((r=nr_socket_connect(sock->inner, &proxy_addr))) {
ABORT(r);
}
_status=0;
abort:
return(_status);
}
int nr_socket_proxy_tunnel_write(void *obj, const void *msg, size_t len,
size_t *written)
{
int r, _status;
nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_write");
if (sock->state >= PROXY_TUNNEL_CLOSED) {
return R_FAILED;
}
if (sock->state == PROXY_TUNNEL_NONE) {
if ((r=send_http_connect(sock))) {
ABORT(r);
}
}
if (sock->state != PROXY_TUNNEL_CONNECTED) {
return R_WOULDBLOCK;
}
if ((r=nr_socket_write(sock->inner, msg, len, written, 0))) {
ABORT(r);
}
_status=0;
abort:
return(_status);
}
int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen,
size_t *len)
{
int r, _status;
char *ptr, *http_term;
size_t bytes_read, available_buffer_len, maxlen_int;
size_t pending;
nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
unsigned int http_status;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_read");
*len = 0;
if (sock->state >= PROXY_TUNNEL_CLOSED) {
return R_FAILED;
}
if (sock->state == PROXY_TUNNEL_CONNECTED) {
return nr_socket_read(sock->inner, buf, maxlen, len, 0);
}
if (sock->buffered_bytes >= sizeof(sock->buffer)) {
r_log(LOG_GENERIC,LOG_ERR,"buffer filled waiting for CONNECT response");
assert(sock->buffered_bytes == sizeof(sock->buffer));
ABORT(R_INTERNAL);
}
/* Do not read more than maxlen bytes */
available_buffer_len = sizeof(sock->buffer) - sock->buffered_bytes;
maxlen_int = maxlen < available_buffer_len ? maxlen : available_buffer_len;
if ((r=nr_socket_read(sock->inner, sock->buffer + sock->buffered_bytes,
maxlen_int, &bytes_read, 0))) {
ABORT(r);
}
sock->buffered_bytes += bytes_read;
if (http_term = find_http_terminator(sock->buffer, sock->buffered_bytes)) {
if ((r = parse_http_response(sock->buffer, http_term, &http_status))) {
ABORT(r);
}
/* TODO (bug 1115934): Handle authentication challenges. */
if (http_status < 200 || http_status >= 300) {
r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_read unable to connect %u",
http_status);
ABORT(R_FAILED);
}
sock->state = PROXY_TUNNEL_CONNECTED;
ptr = http_term + strlen(END_HEADERS);
pending = sock->buffered_bytes - (ptr - sock->buffer);
if (pending == 0) {
ABORT(R_WOULDBLOCK);
}
assert(pending <= maxlen);
*len = pending;
memcpy(buf, ptr, *len);
}
_status=0;
abort:
if (_status && _status != R_WOULDBLOCK) {
sock->state = PROXY_TUNNEL_FAILED;
}
return(_status);
}
int nr_socket_proxy_tunnel_close(void *obj)
{
nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_close");
if (sock->resolver_handle) {
nr_resolver_cancel(sock->config->resolver, sock->resolver_handle);
sock->resolver_handle = 0;
}
sock->state = PROXY_TUNNEL_CLOSED;
return nr_socket_close(sock->inner);
}
int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **configpp)
{
int _status;
nr_proxy_tunnel_config *configp=0;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_create");
if (!(configp=RCALLOC(sizeof(nr_proxy_tunnel_config))))
ABORT(R_NO_MEMORY);
*configpp=configp;
_status=0;
abort:
return(_status);
}
int nr_proxy_tunnel_config_destroy(nr_proxy_tunnel_config **configpp)
{
nr_proxy_tunnel_config *configp;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_destroy");
if (!configpp || !*configpp)
return 0;
configp = *configpp;
*configpp = 0;
RFREE(configp->proxy_host);
RFREE(configp->alpn);
RFREE(configp);
return 0;
}
int nr_proxy_tunnel_config_set_proxy(nr_proxy_tunnel_config *config,
const char *host, UINT2 port)
{
char *hostdup;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_proxy %s %d", host, port);
if (!host) {
return R_BAD_ARGS;
}
if (!(hostdup = r_strdup(host))) {
return R_NO_MEMORY;
}
if (config->proxy_host) {
RFREE(config->proxy_host);
}
config->proxy_host = hostdup;
config->proxy_port = port;
return 0;
}
int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config,
nr_resolver *resolver)
{
r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_resolver");
config->resolver = resolver;
return 0;
}
int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config,
const char *alpn)
{
r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_alpn");
if (alpn && (strlen(alpn) > MAX_ALPN_LENGTH)) {
return R_BAD_ARGS;
}
if (config->alpn) {
RFREE(config->alpn);
}
config->alpn = NULL;
if (alpn) {
char *alpndup = r_strdup(alpn);
if (!alpndup) {
return R_NO_MEMORY;
}
config->alpn = alpndup;
}
return 0;
}
int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp)
{
int r,_status;
nr_proxy_tunnel_config *copy = 0;
if ((r=nr_proxy_tunnel_config_create(&copy)))
ABORT(r);
if ((r=nr_proxy_tunnel_config_set_proxy(copy, config->proxy_host, config->proxy_port)))
ABORT(r);
if ((r=nr_proxy_tunnel_config_set_resolver(copy, config->resolver)))
ABORT(r);
if ((r=nr_proxy_tunnel_config_set_alpn(copy, config->alpn)))
ABORT(r);
*copypp = copy;
_status=0;
abort:
if (_status) {
nr_proxy_tunnel_config_destroy(&copy);
}
return(_status);
}
int nr_socket_proxy_tunnel_create(nr_proxy_tunnel_config *config,
nr_socket *inner,
nr_socket **socketpp)
{
int r, _status;
nr_socket_proxy_tunnel *sock=0;
void *sockv;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_create");
if (!config) {
ABORT(R_BAD_ARGS);
}
if (!(sock=RCALLOC(sizeof(nr_socket_proxy_tunnel)))) {
ABORT(R_NO_MEMORY);
}
sock->inner = inner;
if ((r=nr_proxy_tunnel_config_copy(config, &sock->config)))
ABORT(r);
if ((r=nr_socket_create_int(sock, &nr_socket_proxy_tunnel_vtbl, socketpp))) {
ABORT(r);
}
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_created");
_status=0;
abort:
if (_status) {
sockv = sock;
nr_socket_proxy_tunnel_destroy(&sockv);
}
return(_status);
}
int nr_socket_wrapper_factory_proxy_tunnel_wrap(void *obj,
nr_socket *inner,
nr_socket **socketpp)
{
nr_socket_wrapper_factory_proxy_tunnel *wrapper = (nr_socket_wrapper_factory_proxy_tunnel *)obj;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_wrap");
return nr_socket_proxy_tunnel_create(wrapper->config, inner, socketpp);
}
int nr_socket_wrapper_factory_proxy_tunnel_destroy(void **objpp) {
nr_socket_wrapper_factory_proxy_tunnel *wrapper;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_destroy");
if (!objpp || !*objpp)
return 0;
wrapper = (nr_socket_wrapper_factory_proxy_tunnel *)*objpp;
*objpp = 0;
nr_proxy_tunnel_config_destroy(&wrapper->config);
RFREE(wrapper);
return 0;
}
static nr_socket_wrapper_factory_vtbl proxy_tunnel_wrapper_vtbl = {
nr_socket_wrapper_factory_proxy_tunnel_wrap,
nr_socket_wrapper_factory_proxy_tunnel_destroy
};
int nr_socket_wrapper_factory_proxy_tunnel_create(nr_proxy_tunnel_config *config,
nr_socket_wrapper_factory **factory) {
int r,_status;
nr_socket_wrapper_factory_proxy_tunnel *wrapper=0;
void *wrapperv;
r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_create");
if (!(wrapper=RCALLOC(sizeof(nr_socket_wrapper_factory_proxy_tunnel))))
ABORT(R_NO_MEMORY);
if ((r=nr_proxy_tunnel_config_copy(config, &wrapper->config)))
ABORT(r);
if ((r=nr_socket_wrapper_factory_create_int(wrapper, &proxy_tunnel_wrapper_vtbl, factory)))
ABORT(r);
_status=0;
abort:
if (_status) {
wrapperv = wrapper;
nr_socket_wrapper_factory_proxy_tunnel_destroy(&wrapperv);
}
return(_status);
}

View file

@ -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.
*/
#ifndef _nr_proxy_tunnel_h
#define _nr_proxy_tunnel_h
#include "nr_socket.h"
#include "nr_resolver.h"
#include "nr_socket_wrapper.h"
typedef struct nr_proxy_tunnel_config_ {
nr_resolver *resolver;
char *proxy_host;
UINT2 proxy_port;
char *alpn;
} nr_proxy_tunnel_config;
int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **config);
int nr_proxy_tunnel_config_destroy(nr_proxy_tunnel_config **config);
int nr_proxy_tunnel_config_set_proxy(nr_proxy_tunnel_config *config,
const char* host, UINT2 port);
int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config,
nr_resolver *resolver);
int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config,
const char *alpn);
int nr_socket_proxy_tunnel_create(nr_proxy_tunnel_config *config,
nr_socket *inner,
nr_socket **socketpp);
int nr_socket_wrapper_factory_proxy_tunnel_create(nr_proxy_tunnel_config *config,
nr_socket_wrapper_factory **factory);
#endif

View file

@ -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);
}

View file

@ -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

View file

@ -0,0 +1,191 @@
/*
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.
*/
static char *RCSSTRING __UNUSED__="$Id: nr_socket.c,v 1.2 2008/04/28 17:59:02 ekr Exp $";
#include <assert.h>
#include <nr_api.h>
#include "nr_socket.h"
#include "local_addr.h"
#define CHECK_DEFINED(f) assert(sock->vtbl->f); if (!sock->vtbl->f) ERETURN(R_INTERNAL);
int nr_socket_create_int(void *obj, nr_socket_vtbl *vtbl, nr_socket **sockp)
{
int _status;
nr_socket *sock=0;
if(!(sock=RCALLOC(sizeof(nr_socket))))
ABORT(R_NO_MEMORY);
assert(vtbl->version >= 1 && 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,
nr_transport_addr *addr)
{
CHECK_DEFINED(ssendto);
return sock->vtbl->ssendto(sock->obj,msg,len,flags,addr);
}
int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen,
size_t *len, int flags, nr_transport_addr *addr)
{
CHECK_DEFINED(srecvfrom);
return sock->vtbl->srecvfrom(sock->obj, buf, maxlen, len, flags, addr);
}
int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd)
{
CHECK_DEFINED(getfd);
return sock->vtbl->getfd(sock->obj, fd);
}
int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp)
{
CHECK_DEFINED(getaddr);
return sock->vtbl->getaddr(sock->obj, addrp);
}
int nr_socket_close(nr_socket *sock)
{
CHECK_DEFINED(close);
return sock->vtbl->close(sock->obj);
}
int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr)
{
CHECK_DEFINED(connect);
return sock->vtbl->connect(sock->obj, addr);
}
int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags)
{
CHECK_DEFINED(swrite);
return sock->vtbl->swrite(sock->obj, msg, len, written);
}
int nr_socket_read(nr_socket *sock,void * restrict buf, size_t maxlen,
size_t *len, int flags)
{
CHECK_DEFINED(sread);
return sock->vtbl->sread(sock->obj, buf, maxlen, len);
}
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);
}

View file

@ -0,0 +1,121 @@
/*
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)
#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,
nr_transport_addr *addr);
int (*srecvfrom)(void *obj,void * restrict buf, size_t maxlen, size_t *len, int flags,
nr_transport_addr *addr);
int (*getfd)(void *obj, NR_SOCKET *fd);
int (*getaddr)(void *obj, nr_transport_addr *addrp);
int (*connect)(void *obj, nr_transport_addr *addr);
int (*swrite)(void *obj,const void *msg, size_t len, size_t *written);
int (*sread)(void *obj,void * restrict buf, size_t maxlen, size_t *len);
int (*close)(void *obj);
/* 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,nr_transport_addr *addr);
int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen,
size_t *len, int flags, nr_transport_addr *addr);
int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd);
int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp);
int nr_socket_close(nr_socket *sock);
int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr);
int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags);
int nr_socket_read(nr_socket *sock, void * restrict buf, size_t maxlen, size_t *len, int flags);
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

View file

@ -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

View file

@ -0,0 +1,620 @@
/*
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,
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, 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, 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->transport!=IPPROTO_TCP) {
r_log(LOG_ICE,LOG_INFO,"%s:%d function %s skipping UDP STUN server(addr:%s)",__FILE__,__LINE__,__FUNCTION__,stun_server->u.addr.as_string);
ABORT(R_BAD_ARGS);
}
if (stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR &&
nr_transport_addr_cmp(&stun_server->u.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->u.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);
if (stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR) {
nr_transport_addr stun_server_addr;
nr_transport_addr_copy(&stun_server_addr, &stun_server->u.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,
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) {
if (sock->ctx && sock->ctx->stun_servers) {
for (i=0; i<sock->ctx->stun_server_ct; ++i) {
if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock,
sock->ctx->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 (sock->ctx && sock->ctx->turn_servers) {
for (i=0; i<sock->ctx->turn_server_ct; ++i) {
if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock,
&(sock->ctx->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,
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;
to->protocol=IPPROTO_TCP;
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,
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, 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, 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);
}

View file

@ -0,0 +1,52 @@
/*
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,
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,
nr_transport_addr *addr);
#endif

View file

@ -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;
}

View file

@ -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

View file

@ -0,0 +1,476 @@
/*
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.
*/
static char *RCSSTRING __UNUSED__="$Id: transport_addr.c,v 1.2 2008/04/28 17:59:03 ekr Exp $";
#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:
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;
char buffer[40];
switch(addr->ip_version){
case NR_IPV4:
if (!inet_ntop(AF_INET, &addr->u.addr4.sin_addr,buffer,sizeof(buffer))) {
strncpy(buffer, "[error]", len);
}
break;
case NR_IPV6:
if (!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,buffer,sizeof(buffer))) {
strncpy(buffer, "[error]", len);
}
break;
default:
ABORT(R_INTERNAL);
}
snprintf(buf,len,"%s:%s",addr->ifname,buffer);
_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));
addr->addr=(struct sockaddr *)&addr->u.addr4;
addr->addr_len=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));
addr->addr=(struct sockaddr *)&addr->u.addr6;
addr->addr_len=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, nr_transport_addr *from)
{
memcpy(to,from,sizeof(nr_transport_addr));
to->addr=(struct sockaddr *)((char *)to + ((char *)from->addr - (char *)from));
return(0);
}
int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, 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;
}
/* 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);
addr->addr=(struct sockaddr *)&addr->u.addr4;
addr->addr_len=sizeof(struct sockaddr_in);
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));
addr->addr=(struct sockaddr *)&addr->u.addr6;
addr->addr_len=sizeof(struct sockaddr_in6);
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;
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(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(nr_transport_addr *addr1,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);
assert(addr1->addr_len == addr2->addr_len);
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(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:
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:
UNIMPLEMENTED;
}
return(0);
}
int nr_transport_addr_is_link_local(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:
UNIMPLEMENTED;
}
return(0);
}
int nr_transport_addr_is_wildcard(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:
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(nr_transport_addr *addr)
{
switch(addr->ip_version){
case NR_IPV4:
{
UINT4 ip = ntohl(addr->u.addr4.sin_addr.s_addr);
for (int 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:
UNIMPLEMENTED;
}
return(0);
}
int nr_transport_addr_is_reliable_transport(nr_transport_addr *addr)
{
return addr->protocol == IPPROTO_TCP;
}

View file

@ -0,0 +1,103 @@
/*
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 <sys/types.h>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#endif
/* 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 */
struct sockaddr *addr;
int addr_len;
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];
} 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(nr_transport_addr *addr, int *port);
int nr_transport_addr_cmp(nr_transport_addr *addr1,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(nr_transport_addr *addr);
int nr_transport_addr_is_loopback(nr_transport_addr *addr);
int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr);
int nr_transport_addr_is_link_local(nr_transport_addr *addr);
int nr_transport_addr_copy(nr_transport_addr *to, nr_transport_addr *from);
int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, 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(nr_transport_addr *addr);
#endif

Some files were not shown because too many files have changed in this diff Show more