/* Copyright(C) 2007-2018 VoIP objects (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 "ICEBoxImpl.h" #include "ICELog.h" #include #define LOG_SUBSYSTEM "ICE" using namespace ice; StackImpl::StackImpl(const ServerConfig& config) :mConfig(config), mEventHandler(NULL), mEventTag(NULL), mTimeout(false), mActionTimestamp(0) { setup(config); } StackImpl::~StackImpl() { } void StackImpl::setEventHandler(StageHandler* handler, void* tag) { mEventHandler = handler; mEventTag = tag; } int StackImpl::addStream() { return mSession.addStream(); } int StackImpl::addComponent(int streamID, void* tag, unsigned short port4, unsigned short port6) { return mSession.addComponent(streamID, tag, port4, port6); } void StackImpl::removeStream(int streamID) { mSession.removeStream(streamID); } bool StackImpl::findStreamAndComponent(int family, unsigned short port, int* stream, int* component) { return mSession.findStreamAndComponent(family, port, stream, component); } bool StackImpl::hasStream(int streamId) { return mSession.hasStream(streamId); } bool StackImpl::hasComponent(int streamId, int componentId) { return mSession.hasComponent(streamId, componentId); } void StackImpl::setComponentPort(int streamId, int componentId, unsigned short port4, unsigned short port6) { mSession.setComponentPort(streamId, componentId, port4, port6); } void StackImpl::setRole(AgentRole role) { mSession.setRole(role); } AgentRole StackImpl::role() { return mSession.role(); } bool StackImpl::processIncomingData(int stream, int component, ByteBuffer& incomingData) { #if defined(ICE_EMULATE_SYMMETRIC_NAT) //&& (TARGET_IPHONE_SIMULATOR) if (!isRelayHost(incomingData.remoteAddress())) { ICELogDebug(<<"Discard packet as symmetric NAT is emulating now"); return false; } #endif if (incomingData.remoteAddress().isEmpty()) { ICELogDebug(<< "Incoming packet; remote address is unknown"); incomingData.setRemoteAddress(NetworkAddress::LoopbackAddress4); } else { ICELogDebug(<< "Incoming packet from " << incomingData.remoteAddress().toStdString()); } // Save previous ICE stack state int icestate = state(); incomingData.setComponent(component); bool result = mSession.processData(incomingData, stream, component); // Run handlers if (result && mEventHandler) { int newicestate = state(); if (icestate < IceCheckSuccess && newicestate >= IceCheckSuccess) { // Connectivity check finished if (newicestate == IceCheckSuccess) mEventHandler->onSuccess(this, mEventTag); else mEventHandler->onFailed(this, mEventTag); } else if (icestate < IceGathered && newicestate >= IceGathered) { // Candidates are gathered mEventHandler->onGathered(this, mEventTag); } } return result; } PByteBuffer StackImpl::generateOutgoingData(bool& response, int& stream, int& component, void*& tag) { // Get current timestamp unsigned timestamp = ICETimeHelper::timestamp(); // Find amount of spent time unsigned spent = ICETimeHelper::findDelta(mActionTimestamp, timestamp); // Check for timeout if (mConfig.mTimeout && spent > mConfig.mTimeout) { ice::RunningState sessionState = mSession.state(); bool timeout = sessionState == ice::ConnCheck || sessionState == ice::CandidateGathering; if (!mTimeout && timeout) { // Mark stack as timeouted mTimeout = true; ICELogInfo(<< "Timeout detected."); if (sessionState == ice::CandidateGathering) mSession.cancelAllocations(); // Find default address amongst host candidates mSession.chooseDefaults(); if (mEventHandler) { if (sessionState == ice::ConnCheck) { // Session should not be cleared here - the CT uses direct path for connectivity checks and relayed if checks are failed. Relayed allocations will not be refreshed in such case mEventHandler->onFailed(this, this->mEventTag); } else mEventHandler->onGathered(this, this->mEventTag); } } if (timeout) { // Check if keepalive transactions are scheduled if (!mSession.hasAllocations()) return PByteBuffer(); } } // No timeout, proceed... PByteBuffer result = mSession.getDataToSend(response, stream, component, tag); if (result) ICELogInfo(<< "Sending: " << result->comment() << " to " << result->remoteAddress().toStdString() << ". Data: \r\n" << result->hexstring()); return result; } void StackImpl::gatherCandidates() { mActionTimestamp = ICETimeHelper::timestamp(); mSession.gatherCandidates(); // If there is no STUN server set or IP6 only interfaces - the candidate gathering can finish without network I/O if (mSession.state() == CreatingSDP && mEventHandler) mEventHandler->onGathered(this, mEventTag); } void StackImpl::checkConnectivity() { // Connectivity check can work if candidate gathering timeout-ed yet - it relies on host candidates only in this case // So timeout flag is reset mTimeout = false; mActionTimestamp = ICETimeHelper::timestamp(); mSession.checkConnectivity(); } void StackImpl::stopChecks() { mTimeout = false; mActionTimestamp = 0; mSession.stopChecks(); } IceState StackImpl::state() { if (mTimeout) return IceTimeout; switch (mSession.state()) { case ice::None: return IceNone; case ice::CandidateGathering: return IceGathering; case ice::CreatingSDP: return IceGathered; case ice::ConnCheck: return IceChecking; case ice::Failed: return IceFailed; case ice::Success: return IceCheckSuccess; default: break; } assert(0); return IceNone; // to avoid compiler warning } void StackImpl::createSdp(std::vector& commonPart) { mSession.createSdp(commonPart); } NetworkAddress StackImpl::defaultAddress(int stream, int component) { return mSession.defaultAddress(stream, component); } void StackImpl::fillCandidateList(int streamID, int componentID, std::vector& candidateList) { mSession.fillCandidateList(streamID, componentID, candidateList); } bool StackImpl::processSdpOffer(int streamIndex, std::vector& candidateList, const std::string& defaultIP, unsigned short defaultPort, bool deleteRelayed) { return mSession.processSdpOffer(streamIndex, candidateList, defaultIP, defaultPort, deleteRelayed); } NetworkAddress StackImpl::getRemoteRelayedCandidate(int stream, int component) { return mSession.getRemoteRelayedCandidate(stream, component); } NetworkAddress StackImpl::getRemoteReflexiveCandidate(int stream, int component) { return mSession.getRemoteReflexiveCandidate(stream, component); } void StackImpl::setRemotePassword(const std::string& pwd, int streamId) { mSession.setRemotePassword(pwd, streamId); } std::string StackImpl::remotePassword(int streamId) const { return mSession.remotePassword(streamId); } void StackImpl::setRemoteUfrag(const std::string& ufrag, int streamId) { mSession.setRemoteUfrag(ufrag, streamId); } std::string StackImpl::remoteUfrag(int streamId) const { return mSession.remoteUfrag(streamId); } std::string StackImpl::localPassword() const { return mSession.mLocalPwd; } std::string StackImpl::localUfrag() const { return mSession.mLocalUfrag; } bool StackImpl::hasTurnPrefix(unsigned short prefix) { return mSession.hasTurnPrefix((TurnPrefix)prefix); } NetworkAddress StackImpl::remoteAddress(int stream, int component) { return mSession.remoteAddress(stream, component); } NetworkAddress StackImpl::localAddress(int stream, int component) { return mSession.localAddress(stream, component); } bool StackImpl::findConcludePair(int stream, Candidate& local, Candidate& remote) { // Find nominated pair in stream return mSession.findConcludePair(stream, local, remote); } bool StackImpl::candidateListContains(int stream, const std::string& remoteIP, unsigned short remotePort) { return mSession.mStreamMap[stream]->candidateListContains(remoteIP, remotePort); } void StackImpl::dump(std::ostream& output) { mSession.dump(output); } bool StackImpl::mustRestart() { return mSession.mustRestart(); } void StackImpl::clear() { mSession.clear(); mActionTimestamp = 0; mTimeout = false; } void StackImpl::clearForRestart(bool localNetworkChanged) { mSession.clearForRestart(localNetworkChanged); mActionTimestamp = 0; mTimeout = false; } void StackImpl::refreshPwdUfrag() { mSession.refreshPwdUfrag(); } TurnPrefix StackImpl::bindChannel(int stream, int component, const ice::NetworkAddress &target, ChannelBoundCallback* cb) { return mSession.bindChannel(stream, component, target, cb); } bool StackImpl::isChannelBindingFailed(int stream, int component, TurnPrefix prefix) { return mSession.isChannelBindingFailed(stream, component, prefix); } void StackImpl::installPermissions(int stream, int component, const NetworkAddress &address, InstallPermissionsCallback* cb) { mSession.installPermissions(stream, component, address, cb); } void StackImpl::freeAllocation(int stream, int component, DeleteAllocationCallback* cb) { mSession.freeAllocation(stream, component, cb); } bool StackImpl::hasAllocations() { return mSession.hasAllocations(); } int StackImpl::errorCode() { return mSession.errorCode(); } std::vector* StackImpl::remoteCandidates(int stream) { return mSession.remoteCandidates(stream); } NetworkAddress StackImpl::activeStunServer(int stream) const { return mSession.activeStunServer(stream); } void StackImpl::setup(const ServerConfig& config) { mConfig = config; mSession.mConfig.mServerList4 = config.mServerList4; mSession.mConfig.mServerList6 = config.mServerList6; // Set initial STUN/TURN server IP for case if there will no gathering candidates & selecting fastest server. if (mSession.mConfig.mServerList4.size()) mSession.mConfig.mServerAddr4 = mSession.mConfig.mServerList4.front(); if (mSession.mConfig.mServerList6.size()) mSession.mConfig.mServerAddr6 = mSession.mConfig.mServerList6.front(); mSession.mConfig.mUseIPv4 = config.mUseIPv4; mSession.mConfig.mUseIPv6 = config.mUseIPv6; if (mConfig.mRelay) { mSession.mConfig.mTurnPassword = config.mPassword; mSession.mConfig.mTurnUsername = config.mUsername; mSession.mConfig.mUseTURN = true; mSession.mConfig.mUseSTUN = false; } else { mSession.mConfig.mUseTURN = false; mSession.mConfig.mUseSTUN = true; } mSession.mConfig.mTimeout = config.mTimeout; mSession.setup(mSession.mConfig); } bool StackImpl::isRelayHost(const NetworkAddress& remote) { for (unsigned i=0; i