bool CallTracker::handleCleanUpTimerTick( void ) { bool bPleaseDeleteMeNow = false; ssize_t numberOfTrackedDialogs = 0; // send a timer event to all instantiated SessionContext objects UtlHashMapIterator sessionContextIterator( mSessionContextsMap ); while( sessionContextIterator() ) { SessionContext *pSessionContext; pSessionContext = dynamic_cast<SessionContext*>( sessionContextIterator.value() ); pSessionContext->handleCleanUpTimerTick(); numberOfTrackedDialogs += pSessionContext->getNumberOfTrackedDialogs(); } // check if have have been without an active dialog long enough // to delete ourself if( numberOfTrackedDialogs == 0 ) { mNumberOfTicksWithoutActiveDialogs++; if( mNumberOfTicksWithoutActiveDialogs >= MAX_TIMER_TICK_COUNTS_BEFORE_CALL_TRACKER_CLEAN_UP ) { bPleaseDeleteMeNow = true; } } else { mNumberOfTicksWithoutActiveDialogs = 0; } deleteSessionContextsReadyForDeletion(); return bPleaseDeleteMeNow; }
CommandResultPtr ZScanCommand::execute(InMemoryData& db, SessionContext& ctx) { size_t cmdNum = mTokens.size(); try { if (cmdNum < Consts::MIN_ARG_NUM || cmdNum > Consts::MAX_ARG_NUM) { throw EInvalidArgument(); } const string& key = mTokens[1].first; int cursor = Utils::convertToInt(mTokens[2].first); string pattern = ""; int count = 10; // Return max 10 items if (cmdNum > 3) { int pos = 3; if (mTokens[pos].first == "MATCH") { pattern = mTokens[pos + 1].first; pos += 2; } if (cmdNum > pos && mTokens[pos].first == "COUNT") { count = Utils::convertToInt(mTokens[pos + 1].first); } } if (cursor == 0) { ctx.setScanCommandStartTime(SessionContext::ZSCAN); } SortedSetMap& smap = db.getSortedSetMap(); KeyMap::RedisArray secondArray; int nextCursor = smap.scan(key, secondArray, pattern, cursor, ctx.getScanCommandStartTime(SessionContext::ZSCAN), cursor + count); CommandResultPtr response(new CommandResult(CommandResult::MULTI_RESPONSE)); response->appendToMultiArray(CommandResultPtr(new CommandResult(to_string(nextCursor), RedisProtocol::BULK_STRING))); response->appendToMultiArray(CommandResultPtr(new CommandResult(secondArray))); return response; } catch (EInvalidKey& e) { return CommandResult::redisErrorResult(e.what()); } catch (std::exception& e) { } return CommandResult::redisNULLResult(); }
bool CallTracker::handleRequest( SipMessage& message, const char* address, int port ) { // message is a request. If this is a request we care about, it will contain // a SIP_SIPX_SESSION_CONTEXT_ID_HEADER header containing the value of the session context // tracking the session and a SIP_SIPX_FROM_CALLER_DIRECTION_HEADER if the request // is in the Caller->Callee direction. For more on these headers, look for // 'OPTIMIZATION CODE' comments in NatTraversalAgent.cpp file. bool result = false; const char *pSessionHandle = 0; if( ( pSessionHandle = message.getHeaderValue( 0, SIP_SIPX_SESSION_CONTEXT_ID_HEADER ) ) ) { UtlString sessionHandle( pSessionHandle ); message.removeHeader( SIP_SIPX_SESSION_CONTEXT_ID_HEADER, 0 ); bool bFromCallerToCallee = false; if( message.getHeaderValue( 0, SIP_SIPX_FROM_CALLER_DIRECTION_HEADER ) ) { bFromCallerToCallee = true; message.removeHeader( SIP_SIPX_FROM_CALLER_DIRECTION_HEADER, 0 ); } SessionContext* pSessionContext; pSessionContext = getSessionContextFromHandle( sessionHandle ); if( pSessionContext ) { // Responses to this request may not contain the RouteState which normally // provides the Session Handle used to retrieve the SessionContext. // To compensate for that, if the request is one that the SessionContext // wants to track, as indicated by a 'true' return value from // pSessionContext->handleRequest(), the session handle is encoded in the // Via header to allow SessionContext retrieval for // RouteState-less responses. result = true; if( pSessionContext->handleRequest( message, address, port, bFromCallerToCallee ) ) { setSessionHandleInVia( message, 1, sessionHandle ); } } else { Os::Logger::instance().log(FAC_NAT, PRI_ERR, "CallTracker[%zd]::handleRequest failed to retrieve session context " "associated with handle '%s'", mHandle, sessionHandle.data() ); } } else { // we can see that log entry in 'normal' cases when a dialog-forming INVITE we processed // is coming back to us and is spiraling, i.e. carries a X-Sipx-Spiral: header. Os::Logger::instance().log(FAC_NAT, PRI_DEBUG, "CallTracker[%zd]::handleRequest did not find SIP_SIPX_SESSION_CONTEXT_ID_HEADER in message" , mHandle ); } deleteSessionContextsReadyForDeletion(); return result; }
static void HandleExtern(SessionContext &S) { if (auto P = ParseExtern()) S.addPrototypeAST(std::move(P)); else { // Skip token for error recovery. getNextToken(); } }
static void HandleDefinition(SessionContext &S, KaleidoscopeJIT &J) { if (auto F = ParseDefinition()) { S.addPrototypeAST(llvm::make_unique<PrototypeAST>(*F->Proto)); J.addFunctionAST(std::move(F)); } else { // Skip token for error recovery. getNextToken(); } }
void CallTracker::handleResponse( SipMessage& message, const char* address, int port ) { // Outgoing SIP message is a response. We cannot rely on the presence of RouteState // in responses. Given that, some other way had to be devised to link this response // to a SessionContext object. Use the SesionContext handle that was encoded in the // top Via. That handle is used as a key to index the mSessionContextsMap which yields the // SessionContext object. // UtlString branchId; SessionContext* pSessionContext; if( ( pSessionContext = getSessionContextFromVia( message, 0 ) ) != 0 ) { pSessionContext->handleResponse( message, address, port ); } else { Os::Logger::instance().log(FAC_NAT, PRI_DEBUG, "CallTracker[%zd]::handleResponse: no session context present in response", mHandle ); } deleteSessionContextsReadyForDeletion(); }
CommandResultPtr SelectCommand::execute(InMemoryData& data, SessionContext& ctx) { vector<string> out; size_t cmdNum = mTokens.size(); try { if (cmdNum < Consts::MIN_ARG_NUM || cmdNum > Consts::MAX_ARG_NUM) { throw EInvalidArgument(); } int index = Utils::convertToInt(mTokens[1].first); if (index < 0 || index >= Config::DATABASE_COUNT) { return CommandResultPtr(new CommandResult("Invalid index", RedisProtocol::ERROR)); } ctx.setDatabaseIndex(index); return CommandResult::redisOKResult(); } catch (std::exception& e) { return CommandResult::redisNULLResult(); } }
KaleidoscopeJIT(SessionContext &Session) : Session(Session), CompileLayer(ObjectLayer, SimpleCompiler(Session.getTarget())), LazyEmitLayer(CompileLayer) {}
KaleidoscopeJIT(SessionContext &Session) : DL(Session.getTarget().createDataLayout()), CompileLayer(ObjectLayer, SimpleCompiler(Session.getTarget())), LazyEmitLayer(CompileLayer) {}
KaleidoscopeJIT(SessionContext &Session) : Mang(Session.getTarget().getDataLayout()), CompileLayer(ObjectLayer, SimpleCompiler(Session.getTarget())) {}
bool CallTracker::notifyIncomingDialogFormingInvite( SipMessage& request, RouteState& routeState, const EndpointDescriptor*& prCaller, const EndpointDescriptor*& prCallee ) { bool bResult = false; UtlString sessionContextHandle; SessionContext* pSessionContext; // We need to process and incoming dialog-forming request. In some scenarios, it is possible // for such a request to visit us twice. If there is no session-id encoded in the RouteState, // it indicates that this is the first time we see it. if( !getSessionContextHandle( routeState, sessionContextHandle ) ) { // we are not tracking this session yet. Allocate a new session context to track // this particular fork and save a copy of the message's SDP body if not already done - this // may be useful if the request spirals back to us and we need to restore a patched // SDP to its original form. pSessionContext = createSessionContextAndSetHandle( request, routeState, true, sessionContextHandle ); if( pSessionContext ) { prCaller = &pSessionContext->getEndpointDescriptor( CALLER ); prCallee = &pSessionContext->getEndpointDescriptor( CALLEE ); if( !mpSavedOriginalSdpOfferCopy ) { mpSavedOriginalSdpOfferCopy = const_cast<SdpBody*>( request.getSdpBody() ); } bResult = true; } else { Os::Logger::instance().log( FAC_NAT, PRI_ERR, "CallTracker[%zd]::notifyIncomingDialogFormingInvite[1] failed to create session context ", mHandle ); } } else { // the RouteState already has a session-id which means that we are already tracking this // session. There are several scenarios that can lead up to that situation. Consider this: // // User A--------sipXecs1-----------sipXecs2-------User C(call forwarded to User B) // User B---+ // User B---+ // // Users A & B are registered against sipXecs1 and User C is registered againt sipXecs2. // User B is registered from two endpoints. // User C has its sipXecs-based call forward all calls set to SetB@sipXecs1. // When User A calls User C, the INVITE takes the following path: // UserA-->sipXecs1-->sipXecs2-->sipXecs1-->UserB // +->UserB // A SessionContext is created when sipXecs1 is visited the first time and its session-id // is encoded in the RouteState. When sipXecs1 is revisited by the INVITE it sees that // the RouteState already carries a session-id which tells it that this is a revisiting // INVITE. // If no special handling is performed here, several problems can appear. // // Problem #1- callee's real location wasn't known when SessionContext was created on first visit // ============================================================================================== // When the dialog-forming INVITE was first seen by sipXecs1, the target was UserC@sipxecs2. // Given that the call was routed via a SIP trunk, sipXecs1 didn't know the real location of the // callee and therefore pegged the callee a being at an UNKNOWN location and imposed the use // of a media relay using the media relay's public IP adddress as the media connection address // in the SDP presented to the callee. // When the dialog-forming INVITE comes back to sipxecs1, the request target of the INVITE, namely // UserB@sipxecs1, is known to sipXecs1 and can therefore promote its location information // from UNKNOWN to its precise location based on the information it collected from the set // at registration time. The SessionContext that was initially created by the CallTracker to // handle the NAT traversal was created at the time when the location of the callee was unknown. // Now that the location is known, the old SessionContext can be abandonned and a new one created // that will choose whether or not to involve a media relay based on an accurate representation // of the caller and callee's locations. // // Solution to Problem #1 // ====================== // - Restore the SDP to its original state (i.e. before any transformation) if it got changed by us. // - Allocate a new SessionContext that will be responsible handling all the dialogs forked off // of this INVITE using the updated callee location information. // - Remove any 'id' param containing the handle of the original SessionContext in the Vias // of the request. This procedure will prevent the original SessionContext from handling // responses pertaining to the newly created SessionContext. pSessionContext = getSessionContextFromHandle( sessionContextHandle ); if( pSessionContext ) { // First, allocate a new SessionContext that will take care of this new fork. UtlString handleOfnewSessionHandle; pSessionContext = createSessionContextAndSetHandle( request, routeState, true, handleOfnewSessionHandle ); if( pSessionContext ) { prCaller = &pSessionContext->getEndpointDescriptor( CALLER ); prCallee = &pSessionContext->getEndpointDescriptor( CALLEE ); bResult = true; // Second, restore the SDP to its original form as saved by the original SessionContext if( mpSavedOriginalSdpOfferCopy ) { request.setBody( mpSavedOriginalSdpOfferCopy->copy() ); } // third, remove the handle of the SessionContext being replaced in the Vias removeSessionHandleFromVias( request, sessionContextHandle ); } else { Os::Logger::instance().log( FAC_NAT, PRI_ERR, "CallTracker[%zd]::notifyIncomingDialogFormingInvite[2] failed to create session context ", mHandle ); } } // Problem #2- sipXecs2 may not know how to reach UserB directly // ============================================================= // As we can see in the example above, the sipXecs1 is visited twice but the SipRouter // logic is such that a Record-Route is added the first time it sees the request and // not the others. This means that when the request arrives at UserB's sets it will // have Record-route: <sip:sipXecs2>,<sip:sipXecs1>. That particular arrangement // means that although sipXecs1 was the proxy that routed the dialog-forming // request to UserB's sets, sipXecs2 will be responsible for routing all subsequent in-dialog // requests. Since UserB's sets are registered against sipXecs1 only, it is the only // proxy that truly knows the public and private IP address information of UserB's sets // and therefore the only one that can successfully deliver requests to them accross NATs. // Even if sipXecs2 could somehow "learn" the sets public IP addresses, if UserB's set happened to // be behind a non-full cone NAT sipXecs2 will not have the ability to send requests to // that set as they will be rejected by the NAT because pinholes exist between the set and sipXecs2 // // Solution to Problem #2 // ====================== // To solve this problem, the following piece of logic will ask the RouteState to add a copy of the // Record-Route header when it is updated in cases where sipXproxy is not already at the top of the // route set. UtlString topRecordRoute; if( routeState.isFound() ) { if( request.getRecordRouteUri( 0, &topRecordRoute ) ) { Url topRecordRouteUrl( topRecordRoute ); UtlString topRecordRouteHost; topRecordRouteUrl.getHostAddress( topRecordRouteHost ); int topRecordRoutePort = topRecordRouteUrl.getHostPort(); if( ( topRecordRouteHost != mpNatTraversalRules->getProxyTransportInfo().getAddress() || topRecordRoutePort != mpNatTraversalRules->getProxyTransportInfo().getPort() ) && ( topRecordRouteHost != mpNatTraversalRules->getPublicTransportInfo().getAddress() || topRecordRoutePort != mpNatTraversalRules->getPublicTransportInfo().getPort() ) ) { routeState.addCopy(); } } } } // If the method successfully completed and if the caller is a remote worker then add it to the // list of endpoints whose NAT pinholes need to be kept alive for the duration of the call. // NOTE: the called party is already handled the NatMaintainer's RegDB lookups. if( bResult == true ) { if( prCaller->getLocationCode() == REMOTE_NATED && prCaller->getPublicTransportAddress().getTransportProtocol().compareTo( "udp", UtlString::ignoreCase ) == 0 && !mpCallerPinholeInformation && mpNatMaintainer ) { mpCallerPinholeInformation = new TransportData( prCaller->getPublicTransportAddress().getAddress(), prCaller->getPublicTransportAddress().getPort() ); mpNatMaintainer->addEndpointToKeepAlive( *mpCallerPinholeInformation ); } } return bResult; }