mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-30 04:17:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
59
media/mtransport/README
Normal file
59
media/mtransport/README
Normal 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
36
media/mtransport/build/moz.build
Normal file
36
media/mtransport/build/moz.build
Normal 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'
|
||||
95
media/mtransport/common.build
Normal file
95
media/mtransport/common.build
Normal 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']
|
||||
74
media/mtransport/databuffer.h
Normal file
74
media/mtransport/databuffer.h
Normal 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
|
||||
214
media/mtransport/dtlsidentity.cpp
Normal file
214
media/mtransport/dtlsidentity.cpp
Normal 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
|
||||
70
media/mtransport/dtlsidentity.h
Normal file
70
media/mtransport/dtlsidentity.h
Normal 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
|
||||
170
media/mtransport/gonk_addrs.cpp
Normal file
170
media/mtransport/gonk_addrs.cpp
Normal 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;
|
||||
}
|
||||
38
media/mtransport/logging.h
Normal file
38
media/mtransport/logging.h
Normal 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__
|
||||
28
media/mtransport/m_cpp_utils.h
Normal file
28
media/mtransport/m_cpp_utils.h
Normal 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
|
||||
13
media/mtransport/moz.build
Normal file
13
media/mtransport/moz.build
Normal 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',
|
||||
]
|
||||
2299
media/mtransport/nr_socket_prsock.cpp
Normal file
2299
media/mtransport/nr_socket_prsock.cpp
Normal file
File diff suppressed because it is too large
Load diff
411
media/mtransport/nr_socket_prsock.h
Normal file
411
media/mtransport/nr_socket_prsock.h
Normal 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
|
||||
265
media/mtransport/nr_timer.cpp
Normal file
265
media/mtransport/nr_timer.cpp
Normal 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;
|
||||
}
|
||||
|
||||
1017
media/mtransport/nricectx.cpp
Normal file
1017
media/mtransport/nricectx.cpp
Normal file
File diff suppressed because it is too large
Load diff
390
media/mtransport/nricectx.h
Normal file
390
media/mtransport/nricectx.h
Normal 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
|
||||
160
media/mtransport/nricectxhandler.cpp
Normal file
160
media/mtransport/nricectxhandler.cpp
Normal 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
|
||||
52
media/mtransport/nricectxhandler.h
Normal file
52
media/mtransport/nricectxhandler.h
Normal 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__
|
||||
610
media/mtransport/nricemediastream.cpp
Normal file
610
media/mtransport/nricemediastream.cpp
Normal 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
|
||||
228
media/mtransport/nricemediastream.h
Normal file
228
media/mtransport/nricemediastream.h
Normal 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
|
||||
236
media/mtransport/nriceresolver.cpp
Normal file
236
media/mtransport/nriceresolver.cpp
Normal 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
|
||||
123
media/mtransport/nriceresolver.h
Normal file
123
media/mtransport/nriceresolver.h
Normal 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
|
||||
192
media/mtransport/nriceresolverfake.cpp
Normal file
192
media/mtransport/nriceresolverfake.cpp
Normal 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
|
||||
147
media/mtransport/nriceresolverfake.h
Normal file
147
media/mtransport/nriceresolverfake.h
Normal 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
|
||||
268
media/mtransport/nrinterfaceprioritizer.cpp
Normal file
268
media/mtransport/nrinterfaceprioritizer.cpp
Normal 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
|
||||
18
media/mtransport/nrinterfaceprioritizer.h
Normal file
18
media/mtransport/nrinterfaceprioritizer.h
Normal 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
|
||||
191
media/mtransport/rlogconnector.cpp
Normal file
191
media/mtransport/rlogconnector.cpp
Normal 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
|
||||
129
media/mtransport/rlogconnector.h
Normal file
129
media/mtransport/rlogconnector.h
Normal 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__
|
||||
258
media/mtransport/runnable_utils.h
Normal file
258
media/mtransport/runnable_utils.h
Normal 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
2859
media/mtransport/sigslot.h
Normal file
File diff suppressed because it is too large
Load diff
62
media/mtransport/simpletokenbucket.cpp
Normal file
62
media/mtransport/simpletokenbucket.cpp
Normal 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
|
||||
|
||||
55
media/mtransport/simpletokenbucket.h
Normal file
55
media/mtransport/simpletokenbucket.h
Normal 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__
|
||||
|
||||
359
media/mtransport/stun_socket_filter.cpp
Normal file
359
media/mtransport/stun_socket_filter.cpp
Normal 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;
|
||||
}
|
||||
|
||||
36
media/mtransport/stun_socket_filter.h
Normal file
36
media/mtransport/stun_socket_filter.h
Normal 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__
|
||||
61
media/mtransport/test/TestSyncRunnable.cpp
Normal file
61
media/mtransport/test/TestSyncRunnable.cpp
Normal 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());
|
||||
}
|
||||
296
media/mtransport/test/buffered_stun_socket_unittest.cpp
Normal file
296
media/mtransport/test/buffered_stun_socket_unittest.cpp
Normal 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);
|
||||
}
|
||||
233
media/mtransport/test/dummysocket.h
Normal file
233
media/mtransport/test/dummysocket.h
Normal 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
|
||||
|
||||
83
media/mtransport/test/gtest_ringbuffer_dumper.h
Normal file
83
media/mtransport/test/gtest_ringbuffer_dumper.h
Normal 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__
|
||||
|
||||
|
||||
|
||||
203
media/mtransport/test/gtest_utils.h
Normal file
203
media/mtransport/test/gtest_utils.h
Normal 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
|
||||
3934
media/mtransport/test/ice_unittest.cpp
Normal file
3934
media/mtransport/test/ice_unittest.cpp
Normal file
File diff suppressed because it is too large
Load diff
100
media/mtransport/test/moz.build
Normal file
100
media/mtransport/test/moz.build
Normal 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']
|
||||
52
media/mtransport/test/mtransport_test_utils.h
Normal file
52
media/mtransport/test/mtransport_test_utils.h
Normal 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
|
||||
540
media/mtransport/test/multi_tcp_socket_unittest.cpp
Normal file
540
media/mtransport/test/multi_tcp_socket_unittest.cpp
Normal 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));
|
||||
}
|
||||
134
media/mtransport/test/nrappkit_unittest.cpp
Normal file
134
media/mtransport/test/nrappkit_unittest.cpp
Normal 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);
|
||||
}
|
||||
344
media/mtransport/test/proxy_tunnel_socket_unittest.cpp
Normal file
344
media/mtransport/test/proxy_tunnel_socket_unittest.cpp
Normal 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);
|
||||
}
|
||||
264
media/mtransport/test/rlogconnector_unittest.cpp
Normal file
264
media/mtransport/test/rlogconnector_unittest.cpp
Normal 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());
|
||||
}
|
||||
227
media/mtransport/test/runnable_utils_unittest.cpp
Normal file
227
media/mtransport/test/runnable_utils_unittest.cpp
Normal 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
|
||||
393
media/mtransport/test/sctp_unittest.cpp
Normal file
393
media/mtransport/test/sctp_unittest.cpp
Normal 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
|
||||
107
media/mtransport/test/simpletokenbucket_unittest.cpp
Normal file
107
media/mtransport/test/simpletokenbucket_unittest.cpp
Normal 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));
|
||||
}
|
||||
208
media/mtransport/test/sockettransportservice_unittest.cpp
Normal file
208
media/mtransport/test/sockettransportservice_unittest.cpp
Normal 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
|
||||
664
media/mtransport/test/stunserver.cpp
Normal file
664
media/mtransport/test/stunserver.cpp
Normal 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
|
||||
123
media/mtransport/test/stunserver.h
Normal file
123
media/mtransport/test/stunserver.h
Normal 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
|
||||
878
media/mtransport/test/test_nr_socket_unittest.cpp
Normal file
878
media/mtransport/test/test_nr_socket_unittest.cpp
Normal 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]));
|
||||
}
|
||||
1344
media/mtransport/test/transport_unittests.cpp
Normal file
1344
media/mtransport/test/transport_unittests.cpp
Normal file
File diff suppressed because it is too large
Load diff
487
media/mtransport/test/turn_unittest.cpp
Normal file
487
media/mtransport/test/turn_unittest.cpp
Normal 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);
|
||||
}
|
||||
892
media/mtransport/test_nr_socket.cpp
Normal file
892
media/mtransport/test_nr_socket.cpp
Normal 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
|
||||
|
||||
335
media/mtransport/test_nr_socket.h
Normal file
335
media/mtransport/test_nr_socket.h
Normal 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__
|
||||
|
||||
17
media/mtransport/testlib/moz.build
Normal file
17
media/mtransport/testlib/moz.build
Normal 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
40
media/mtransport/third_party/import.py
vendored
Normal 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
76
media/mtransport/third_party/moz.build
vendored
Normal 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
|
||||
71
media/mtransport/third_party/nICEr/IMPORT_FILES
vendored
Normal file
71
media/mtransport/third_party/nICEr/IMPORT_FILES
vendored
Normal 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
|
||||
74
media/mtransport/third_party/nICEr/README
vendored
Normal file
74
media/mtransport/third_party/nICEr/README
vendored
Normal 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.
|
||||
|
||||
11
media/mtransport/third_party/nICEr/README_MOZILLA
vendored
Normal file
11
media/mtransport/third_party/nICEr/README_MOZILLA
vendored
Normal 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.
|
||||
|
||||
|
||||
254
media/mtransport/third_party/nICEr/nicer.gyp
vendored
Normal file
254
media/mtransport/third_party/nICEr/nicer.gyp
vendored
Normal 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",
|
||||
]
|
||||
}]
|
||||
],
|
||||
}]
|
||||
}
|
||||
|
||||
71
media/mtransport/third_party/nICEr/src/crypto/nr_crypto.c
vendored
Normal file
71
media/mtransport/third_party/nICEr/src/crypto/nr_crypto.c
vendored
Normal 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;
|
||||
|
||||
|
||||
52
media/mtransport/third_party/nICEr/src/crypto/nr_crypto.h
vendored
Normal file
52
media/mtransport/third_party/nICEr/src/crypto/nr_crypto.h
vendored
Normal 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
|
||||
|
||||
979
media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
vendored
Normal file
979
media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
vendored
Normal 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);
|
||||
}
|
||||
|
||||
121
media/mtransport/third_party/nICEr/src/ice/ice_candidate.h
vendored
Normal file
121
media/mtransport/third_party/nICEr/src/ice/ice_candidate.h
vendored
Normal 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
|
||||
|
||||
684
media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
vendored
Normal file
684
media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
vendored
Normal 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, ©))
|
||||
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);
|
||||
}
|
||||
|
||||
95
media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h
vendored
Normal file
95
media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h
vendored
Normal 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
|
||||
|
||||
41
media/mtransport/third_party/nICEr/src/ice/ice_codeword.h
vendored
Normal file
41
media/mtransport/third_party/nICEr/src/ice/ice_codeword.h
vendored
Normal 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
|
||||
|
||||
1688
media/mtransport/third_party/nICEr/src/ice/ice_component.c
vendored
Normal file
1688
media/mtransport/third_party/nICEr/src/ice/ice_component.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
104
media/mtransport/third_party/nICEr/src/ice/ice_component.h
vendored
Normal file
104
media/mtransport/third_party/nICEr/src/ice/ice_component.h
vendored
Normal 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
|
||||
1057
media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
vendored
Normal file
1057
media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
203
media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
vendored
Normal file
203
media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
vendored
Normal 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
|
||||
|
||||
84
media/mtransport/third_party/nICEr/src/ice/ice_handler.h
vendored
Normal file
84
media/mtransport/third_party/nICEr/src/ice/ice_handler.h
vendored
Normal 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
|
||||
|
||||
933
media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
vendored
Normal file
933
media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
vendored
Normal 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);
|
||||
}
|
||||
108
media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h
vendored
Normal file
108
media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h
vendored
Normal 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
|
||||
|
||||
575
media/mtransport/third_party/nICEr/src/ice/ice_parser.c
vendored
Normal file
575
media/mtransport/third_party/nICEr/src/ice/ice_parser.c
vendored
Normal 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);
|
||||
}
|
||||
|
||||
825
media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
vendored
Normal file
825
media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
101
media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
vendored
Normal file
101
media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
vendored
Normal 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
|
||||
|
||||
79
media/mtransport/third_party/nICEr/src/ice/ice_reg.h
vendored
Normal file
79
media/mtransport/third_party/nICEr/src/ice/ice_reg.h
vendored
Normal 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
|
||||
|
||||
380
media/mtransport/third_party/nICEr/src/ice/ice_socket.c
vendored
Normal file
380
media/mtransport/third_party/nICEr/src/ice/ice_socket.c
vendored
Normal 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);
|
||||
}
|
||||
|
||||
97
media/mtransport/third_party/nICEr/src/ice/ice_socket.h
vendored
Normal file
97
media/mtransport/third_party/nICEr/src/ice/ice_socket.h
vendored
Normal 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
|
||||
|
||||
61
media/mtransport/third_party/nICEr/src/net/local_addr.c
vendored
Normal file
61
media/mtransport/third_party/nICEr/src/net/local_addr.c
vendored
Normal 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);
|
||||
}
|
||||
59
media/mtransport/third_party/nICEr/src/net/local_addr.h
vendored
Normal file
59
media/mtransport/third_party/nICEr/src/net/local_addr.h
vendored
Normal 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
|
||||
88
media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.c
vendored
Normal file
88
media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.c
vendored
Normal 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);
|
||||
}
|
||||
66
media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.h
vendored
Normal file
66
media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.h
vendored
Normal 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
|
||||
686
media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c
vendored
Normal file
686
media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c
vendored
Normal 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(©)))
|
||||
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(©);
|
||||
}
|
||||
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);
|
||||
}
|
||||
70
media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h
vendored
Normal file
70
media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h
vendored
Normal 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
|
||||
85
media/mtransport/third_party/nICEr/src/net/nr_resolver.c
vendored
Normal file
85
media/mtransport/third_party/nICEr/src/net/nr_resolver.c
vendored
Normal 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);
|
||||
}
|
||||
96
media/mtransport/third_party/nICEr/src/net/nr_resolver.h
vendored
Normal file
96
media/mtransport/third_party/nICEr/src/net/nr_resolver.h
vendored
Normal 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
|
||||
191
media/mtransport/third_party/nICEr/src/net/nr_socket.c
vendored
Normal file
191
media/mtransport/third_party/nICEr/src/net/nr_socket.c
vendored
Normal 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);
|
||||
}
|
||||
|
||||
121
media/mtransport/third_party/nICEr/src/net/nr_socket.h
vendored
Normal file
121
media/mtransport/third_party/nICEr/src/net/nr_socket.h
vendored
Normal 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
|
||||
|
||||
41
media/mtransport/third_party/nICEr/src/net/nr_socket_local.h
vendored
Normal file
41
media/mtransport/third_party/nICEr/src/net/nr_socket_local.h
vendored
Normal 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
|
||||
|
||||
620
media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c
vendored
Normal file
620
media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c
vendored
Normal 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);
|
||||
}
|
||||
52
media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.h
vendored
Normal file
52
media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.h
vendored
Normal 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
|
||||
82
media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.c
vendored
Normal file
82
media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.c
vendored
Normal 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;
|
||||
}
|
||||
|
||||
63
media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.h
vendored
Normal file
63
media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.h
vendored
Normal 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
|
||||
476
media/mtransport/third_party/nICEr/src/net/transport_addr.c
vendored
Normal file
476
media/mtransport/third_party/nICEr/src/net/transport_addr.c
vendored
Normal 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;
|
||||
}
|
||||
103
media/mtransport/third_party/nICEr/src/net/transport_addr.h
vendored
Normal file
103
media/mtransport/third_party/nICEr/src/net/transport_addr.h
vendored
Normal 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
Loading…
Add table
Add a link
Reference in a new issue