/* 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 #include #include 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; cifirst().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; itoStdString()); 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(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; itoStdString(); 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; ifirst().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; iupdatePriority(); } 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; irole() != CandidatePair::None) output << Logger::TabPrefix << Logger::TabPrefix << mPairList[i]->toStdString().c_str() << std::endl; } unsigned CheckList::countOfValidPairs() { unsigned result = 0; for (unsigned i=0; irole() >= 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); }