Files
rtphone/src/libs/ice/ICECheckList.cpp

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