436 lines
11 KiB
C++
436 lines
11 KiB
C++
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
|
* 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 "ICEPlatform.h"
|
|
#include "ICECheckList.h"
|
|
#include "ICENetworkHelper.h"
|
|
#include "ICELog.h"
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
|
|
using namespace ice;
|
|
#define LOG_SUBSYSTEM "ice"
|
|
|
|
const char* CheckList::stateToString(int state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case Running: return "Running";
|
|
case Completed: return "Completed";
|
|
case Failed: return "Failed";
|
|
}
|
|
return "Undefined";
|
|
}
|
|
CheckList::CheckList()
|
|
:mState(CheckList::Running)
|
|
{
|
|
}
|
|
|
|
CheckList::State CheckList::state()
|
|
{
|
|
return mState;
|
|
}
|
|
|
|
void CheckList::setState(CheckList::State state)
|
|
{
|
|
mState = state;
|
|
}
|
|
|
|
CheckList::PairList& CheckList::pairlist()
|
|
{
|
|
return mPairList;
|
|
}
|
|
|
|
static bool ComparePairByPriority(const PCandidatePair& pair1, const PCandidatePair& pair2)
|
|
{
|
|
return pair1->priority() > pair2->priority();
|
|
}
|
|
|
|
void CheckList::pruneDuplicates()
|
|
{
|
|
// Remove duplicates
|
|
for (PairList::iterator pairIter = mPairList.begin(); pairIter != mPairList.end(); pairIter++)
|
|
{
|
|
PCandidatePair& pair = *pairIter;
|
|
|
|
// Get the iterator to next element
|
|
PairList::iterator nextPairIter = pairIter; std::advance(nextPairIter, 1);
|
|
|
|
// Iterate next elements to find duplicates
|
|
while (nextPairIter != mPairList.end())
|
|
{
|
|
PCandidatePair& nextPair = *nextPairIter;
|
|
|
|
bool sameType = pair->first().mLocalAddr.family() == nextPair->second().mLocalAddr.family();
|
|
bool sameAddress = pair->second().mExternalAddr == nextPair->second().mExternalAddr;
|
|
bool relayed1 = pair->first().mType == Candidate::ServerRelayed;
|
|
bool relayed2 = nextPair->first().mType == Candidate::ServerRelayed;
|
|
bool sameRelayed = relayed1 == relayed2;
|
|
#ifdef ICE_SMART_PRUNE_CHECKLIST
|
|
if (sameType && sameAddress && sameRelayed)
|
|
#else
|
|
if (equalCandidates && sameAddress && sameRelayed)
|
|
#endif
|
|
nextPairIter = mPairList.erase(nextPairIter);
|
|
else
|
|
nextPairIter++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void CheckList::prune(int /*checkLimit*/)
|
|
{
|
|
// Sort list by priority
|
|
std::sort(mPairList.begin(), mPairList.end(), ComparePairByPriority);
|
|
|
|
// Replace server reflexive candidates with bases
|
|
for (unsigned ci=0; ci<mPairList.size(); ci++)
|
|
{
|
|
PCandidatePair& pair = mPairList[ci];
|
|
if (pair->first().mType == Candidate::ServerReflexive)
|
|
pair->first().mType = Candidate::Host;
|
|
}
|
|
|
|
/*
|
|
#ifdef _WIN32
|
|
Win32Preprocess();
|
|
#endif
|
|
*/
|
|
|
|
pruneDuplicates();
|
|
|
|
// Erase all relayed checks to LAN candidates
|
|
PairList::iterator pairIter = mPairList.begin();
|
|
while (pairIter != mPairList.end())
|
|
{
|
|
PCandidatePair& pair = *pairIter;
|
|
if (pair->first().mType == Candidate::ServerRelayed && !pair->second().mExternalAddr.isPublic())
|
|
pairIter = mPairList.erase(pairIter);
|
|
else
|
|
pairIter++;
|
|
}
|
|
|
|
#ifndef ICE_LOOPBACK_SUPPORT
|
|
pairIter = mPairList.begin();
|
|
while (pairIter != mPairList.end())
|
|
{
|
|
PCandidatePair& pair = *pairIter;
|
|
if (pair->second().mExternalAddr.isLoopback())
|
|
pairIter = mPairList.erase(pairIter);
|
|
else
|
|
pairIter++;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ICE_SKIP_LINKLOCAL
|
|
pairIter = mPairList.begin();
|
|
while (pairIter != mPairList.end())
|
|
{
|
|
PCandidatePair& pair = *pairIter;
|
|
if (pair->second().mExternalAddr.isLinkLocal())
|
|
pairIter = mPairList.erase(pairIter);
|
|
else
|
|
pairIter++;
|
|
}
|
|
#endif
|
|
pruneDuplicates();
|
|
|
|
#ifdef ICE_POSTPONE_RELAYEDCHECKS
|
|
postponeRelayed();
|
|
#endif
|
|
|
|
// Put all LAN checks before other.
|
|
// Therefore it should be priorities sorting should be enough.
|
|
// But in answer's SDP remote peer can put external IP to the top of the list
|
|
// It can cause its promote to the top of list of connectivity checks
|
|
std::sort(mPairList.begin(), mPairList.end(), [](const PCandidatePair& p1, const PCandidatePair& p2) -> bool
|
|
{
|
|
return p1->isLanOnly() && !p2->isLanOnly();
|
|
});
|
|
|
|
// Cut all checks that are behind the limit
|
|
if (mPairList.size() > ICE_CONNCHECK_LIMIT)
|
|
{
|
|
ICELogDebug(<<"Cut extra connection checks. The total number of checks should not exceed " << ICE_CONNCHECK_LIMIT);
|
|
PairList::iterator bcIter = mPairList.begin();
|
|
std::advance(bcIter, ICE_CONNCHECK_LIMIT);
|
|
mPairList.erase(bcIter, mPairList.end());
|
|
}
|
|
}
|
|
|
|
void CheckList::win32Preprocess()
|
|
{
|
|
for (unsigned i=0; i<mPairList.size(); i++)
|
|
{
|
|
PCandidatePair& pair = mPairList[i];
|
|
ICELogDebug(<<"Win32Preprocess pair " << pair->toStdString());
|
|
|
|
if (pair->first().mType == Candidate::Host)
|
|
{
|
|
// Get best source interface for remote candidate
|
|
NetworkAddress bestInterface = NetworkHelper::instance().sourceInterface(pair->second().mExternalAddr);
|
|
|
|
// Replace IP address to found
|
|
if (!bestInterface.isEmpty())
|
|
pair->first().mLocalAddr.setIp(bestInterface.ip());
|
|
else
|
|
ICELogDebug(<<"Failed to find source interface for remote candidate");
|
|
}
|
|
}
|
|
}
|
|
|
|
PCandidatePair CheckList::findEqualPair(CandidatePair& _pair, ComparisionType ct)
|
|
{
|
|
for (PCandidatePair& p: mPairList)
|
|
{
|
|
if (p->role() != CandidatePair::None)
|
|
{
|
|
switch (ct)
|
|
{
|
|
case CT_TreatHostAsUniform:
|
|
if (p->first().mType == Candidate::Host && _pair.first().mType == Candidate::Host && p->second() == _pair.second())
|
|
return p;
|
|
if (p->first().mLocalAddr == _pair.first().mLocalAddr && p->first().mType == Candidate::Host && p->second() == _pair.second() && _pair.first().mType != Candidate::ServerRelayed)
|
|
return p;
|
|
if (_pair == *p)
|
|
return p;
|
|
|
|
break;
|
|
|
|
case CT_Strict:
|
|
if (_pair == *p)
|
|
return p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return PCandidatePair();
|
|
}
|
|
|
|
unsigned CheckList::add(CandidatePair& p)
|
|
{
|
|
mPairList.push_back(std::make_shared<CandidatePair>(p));
|
|
|
|
// Sort list by priority
|
|
std::sort(mPairList.begin(), mPairList.end(), ComparePairByPriority);
|
|
|
|
return (unsigned)mPairList.size();
|
|
}
|
|
|
|
unsigned CheckList::count()
|
|
{
|
|
return (unsigned)mPairList.size();
|
|
}
|
|
std::string CheckList::toStdString()
|
|
{
|
|
std::string dump = "";
|
|
for (size_t i=0; i<mPairList.size(); i++)
|
|
{
|
|
dump += mPairList[i]->toStdString();
|
|
dump += "\n";
|
|
}
|
|
|
|
return dump;
|
|
}
|
|
|
|
PCandidatePair& CheckList::operator[] (size_t index)
|
|
{
|
|
return mPairList[index];
|
|
}
|
|
|
|
PCandidatePair CheckList::findNominatedPair(int component)
|
|
{
|
|
for (PCandidatePair& p: mPairList)
|
|
{
|
|
if (p->first().component() == component &&
|
|
p->role() != CandidatePair::None &&
|
|
p->nomination() == CandidatePair::Nomination_Finished)
|
|
return p;
|
|
}
|
|
return PCandidatePair();
|
|
}
|
|
|
|
PCandidatePair CheckList::findValidPair(int component)
|
|
{
|
|
for (PCandidatePair& p: mPairList)
|
|
{
|
|
if (p->first().component() == component &&
|
|
p->role() == CandidatePair::Valid)
|
|
return p;
|
|
}
|
|
return PCandidatePair();
|
|
}
|
|
|
|
PCandidatePair CheckList::findBestValidPair(int componentId)
|
|
{
|
|
PairList found;
|
|
std::copy_if(mPairList.begin(), mPairList.end(), std::back_inserter(found),
|
|
[componentId](const PCandidatePair& p) -> bool
|
|
{
|
|
return (p->first().component() == componentId &&
|
|
p->role() == CandidatePair::Valid &&
|
|
p->first().mExternalAddr.isLAN() &&
|
|
p->second().mExternalAddr.isLAN());
|
|
});
|
|
|
|
if (found.size())
|
|
return found.front();
|
|
|
|
return findValidPair(componentId);
|
|
}
|
|
|
|
PCandidatePair CheckList::findHighestNominatedPair(int component)
|
|
{
|
|
#ifdef ICE_POSTPONE_RELAYEDCHECKS
|
|
PairList found;
|
|
for (PCandidatePair& p: mPairList)
|
|
{
|
|
if (p->first().component() == component &&
|
|
p->role() != CandidatePair::None &&
|
|
p->nomination() == CandidatePair::Nomination_Finished &&
|
|
p->first().mType != Candidate::ServerRelayed &&
|
|
p->second().mType != Candidate::ServerRelayed)
|
|
found.push_back( p );
|
|
}
|
|
|
|
if (found.size())
|
|
{
|
|
PCandidatePair& f0 = found.front();
|
|
for (PCandidatePair& p: mPairList)
|
|
if (p == f0)
|
|
return p;
|
|
}
|
|
#endif
|
|
int64_t priority = -1;
|
|
PCandidatePair result;
|
|
|
|
std::ostringstream oss;
|
|
oss << "Looking for highest nominated pair in list:";
|
|
for (PCandidatePair& p: mPairList)
|
|
{
|
|
oss << "\n " << p->toStdString().c_str() << ", priority " << p->priority();
|
|
|
|
if (p->first().component() == component &&
|
|
p->role() != CandidatePair::None &&
|
|
p->nomination() == CandidatePair::Nomination_Finished &&
|
|
p->priority() > priority )
|
|
{
|
|
result = p;
|
|
priority = p->priority();
|
|
}
|
|
}
|
|
|
|
// ICELogDebug( << oss.str() );
|
|
|
|
if (result)
|
|
{
|
|
ICELogDebug(<< "Result is " << result->toStdString());
|
|
}
|
|
else
|
|
{
|
|
// ICELogDebug(<< "No nominated pair");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void CheckList::removePairs(CandidatePair::State state, int component)
|
|
{
|
|
for (unsigned i=0; i<mPairList.size(); i++)
|
|
{
|
|
CandidatePair& p = *mPairList[i];
|
|
if (p.first().component() == component &&
|
|
p.state() == state &&
|
|
p.role() != CandidatePair::None)
|
|
p.setRole(CandidatePair::None);
|
|
}
|
|
}
|
|
|
|
PCandidatePair CheckList::findLowestNominatedPair(int component)
|
|
{
|
|
#ifdef _MSC_VER
|
|
int64_t priority = 0x7FFFFFFFFFFF;
|
|
#else
|
|
int64_t priority = 0x7FFFFFFFFFFFLL;
|
|
#endif
|
|
PCandidatePair result;
|
|
|
|
for (PCandidatePair& p: mPairList)
|
|
{
|
|
if (p->first().component() == component &&
|
|
p->role() != CandidatePair::None &&
|
|
p->nomination() == CandidatePair::Nomination_Finished &&
|
|
p->priority() < priority )
|
|
{
|
|
result = p;
|
|
priority = p->priority();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CheckList::updatePairPriorities()
|
|
{
|
|
for (unsigned i=0; i<mPairList.size(); i++)
|
|
mPairList[i]->updatePriority();
|
|
}
|
|
|
|
void CheckList::clear()
|
|
{
|
|
mState = Running;
|
|
mPairList.clear();
|
|
}
|
|
|
|
void CheckList::dump(std::ostream& output)
|
|
{
|
|
output << Logger::TabPrefix << Logger::TabPrefix << "State: " << CheckList::stateToString(mState) << std::endl;
|
|
for (unsigned i=0; i<mPairList.size(); i++)
|
|
if (mPairList[i]->role() != CandidatePair::None)
|
|
output << Logger::TabPrefix << Logger::TabPrefix << mPairList[i]->toStdString().c_str() << std::endl;
|
|
}
|
|
|
|
unsigned CheckList::countOfValidPairs()
|
|
{
|
|
unsigned result = 0;
|
|
for (unsigned i=0; i<mPairList.size(); i++)
|
|
if (mPairList[i]->role() >= CandidatePair::Valid)
|
|
result++;
|
|
return result;
|
|
}
|
|
|
|
void CheckList::postponeRelayed()
|
|
{
|
|
PairList relayed, relayedTarget;
|
|
|
|
PairList::iterator pairIter = mPairList.begin();
|
|
while ( pairIter != mPairList.end() )
|
|
{
|
|
PCandidatePair& p = *pairIter;
|
|
if (p->first().mType == Candidate::ServerRelayed)
|
|
{
|
|
relayed.push_back(p);
|
|
pairIter = mPairList.erase( pairIter );
|
|
}
|
|
else
|
|
if (p->second().mType == Candidate::ServerRelayed)
|
|
{
|
|
relayedTarget.push_back(p);
|
|
pairIter = mPairList.erase( pairIter );
|
|
}
|
|
else
|
|
pairIter++;
|
|
}
|
|
|
|
for (pairIter = relayedTarget.begin(); pairIter != relayedTarget.end(); pairIter++)
|
|
mPairList.push_back(*pairIter);
|
|
|
|
for (pairIter = relayed.begin(); pairIter != relayed.end(); pairIter++)
|
|
mPairList.push_back(*pairIter);
|
|
}
|