/** * Cancel a suspended redirection. * * Caller must hold mRedirectorMutex. * * containableSeqNo - UtlInt containing the sequence number. * * suspendObject - pointer to the suspense object. */ void SipRedirectServer::cancelRedirect(UtlInt& containableSeqNo, RedirectSuspend* suspendObject) { RedirectPlugin::RequestSeqNo seqNo = containableSeqNo.getValue(); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::cancelRedirect " "Canceling suspense of request %d", seqNo); // Call cancel for redirectors that need it. PluginIterator iterator(mRedirectPlugins); RedirectPlugin* redirector; int i; // Iterator sequence number. for (i = 0; (redirector = static_cast <RedirectPlugin*> (iterator.next())); i++) { if (mpConfiguredRedirectors[i].bActive && suspendObject->mRedirectors[i].needsCancel) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::cancelRedirect " "Calling cancel(%d) for redirector %d", seqNo, i); redirector->cancel(seqNo); } } // Remove the entry from mSuspendList. // Also deletes the suspend object. // Deleting the suspend object frees the array of information about // the redirectors, and the private storage for each redirector. // (See RedirectSuspend::~RedirectSuspend().) mSuspendList.destroy(&containableSeqNo); }
bool ContactList::remove( size_t index, const RedirectPlugin& plugin ) { bool success = false; if( index < mContactList.size() ) { success = true; mbListWasModified = true; OsSysLog::add(FAC_SIP, PRI_NOTICE, "ContactList::remove(): %s removed contact index %d for '%s':\n" " was: '%s'", plugin.name().data(), index, mRequestString.data(), mContactList[ index ].data() ); mContactList.erase( mContactList.begin() + index ); } else { OsSysLog::add(FAC_SIP, PRI_ERR, "ContactList::remove(): %s failed to remove contact index %d - list only has %d elements", plugin.name().data(), index, mContactList.size() ); } return success; }
bool ContactList::set( size_t index, const UtlString& contact, const RedirectPlugin& plugin ) { bool success = false; if( index < mContactList.size() ) { mbListWasModified = true; success = true; OsSysLog::add(FAC_SIP, PRI_NOTICE, "ContactList::set(): %s modified contact index %d for '%s':\n" " was: '%s'\n" " now is: '%s'", plugin.name().data(), index, mRequestString.data(), mContactList[ index ].data(), contact.data() ); mContactList[ index ] = contact; } else { OsSysLog::add(FAC_SIP, PRI_ERR, "ContactList::set(): %s failed to set contact index %d - list only has %d elements", plugin.name().data(), index, mContactList.size() ); } return success; }
bool ContactList::removeAll( const RedirectPlugin& plugin ) { OsSysLog::add(FAC_SIP, PRI_NOTICE, "ContactList::removeAll(): %s removed %d contacts for '%s'", plugin.name().data(), mContactList.size(), mRequestString.data() ); mbListWasModified = true; mContactList.clear(); return true; }
bool ContactList::add( const UtlString& contact, const RedirectPlugin& plugin ) { mbListWasModified = true; mContactList.push_back( contact ); OsSysLog::add(FAC_SIP, PRI_NOTICE, "ContactList::add(): %s added contact for '%s':\n" " '%s' (contact index %d)", plugin.name().data(), mRequestString.data(), contact.data(), mContactList.size() - 1 ); return true; }
UtlBoolean SipRedirectServer::initialize(OsConfigDb& configDb ///< Configuration parameters ) { configDb.get("SIP_REGISTRAR_DOMAIN_NAME", mDefaultDomain); mProxyNormalPort = configDb.getPort("SIP_REGISTRAR_PROXY_PORT"); if (mProxyNormalPort == PORT_DEFAULT) { mProxyNormalPort = SIP_PORT; } mAckRouteToProxy.insert(0, "<"); mAckRouteToProxy.append(mDefaultDomain); mAckRouteToProxy.append(";lr>"); // Load the list of redirect processors. mRedirectPlugins.readConfig(configDb); mRedirectorCount = mRedirectPlugins.entries(); // Call their ::initialize() methods. mpConfiguredRedirectors = new RedirectorDescriptor[ mRedirectorCount ]; PluginIterator iterator(mRedirectPlugins); RedirectPlugin* redirector; UtlString redirectorName; bool bAuthorityLevelDbAvailable; UtlString authorityLevelDbPrefix = RedirectPlugin::Prefix; authorityLevelDbPrefix.append( AuthorityLevelPrefix ); authorityLevelDbPrefix.append( '.' ); OsConfigDb authorityLevelDb; bAuthorityLevelDbAvailable = ( configDb.getSubHash( authorityLevelDbPrefix, authorityLevelDb ) == OS_SUCCESS ); int i; // Iterator sequence number. for (i = 0; (redirector = static_cast <RedirectPlugin*> (iterator.next( &redirectorName ))); i++) { mpConfiguredRedirectors[i].name = redirectorName; if( ( mpConfiguredRedirectors[i].bActive = ( redirector->initialize(configDb, i, mDefaultDomain) == OS_SUCCESS ) ) ) { redirector->setUserAgent(mpSipUserAgent); int authorityLevel; if( bAuthorityLevelDbAvailable && authorityLevelDb.get( redirectorName, authorityLevel ) == OS_SUCCESS ) { mpConfiguredRedirectors[i].authorityLevel = authorityLevel; } else { mpConfiguredRedirectors[i].authorityLevel = LOWEST_AUTHORITY_LEVEL; } Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::initialize " "Initialized redirector %s (authority level = %zd)", redirectorName.data(), mpConfiguredRedirectors[i].authorityLevel ); } else { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::initialize " "Redirector %s is inactive ", redirectorName.data() ); } } return true; }
UtlBoolean SipRedirectServer::handleMessage(OsMsg& eventMessage) { UtlBoolean handled = FALSE; int msgType = eventMessage.getMsgType(); switch (msgType) { case OsMsg::PHONE_APP: { // An incoming request to be redirected. // Get a pointer to the SIP message. const SipMessage* message = ((SipMessageEvent&) eventMessage).getMessage(); // Extract the request method. UtlString method; message->getRequestMethod(&method); if (Os::Logger::instance().willLog(FAC_SIP, PRI_DEBUG)) { UtlString stringUri; message->getRequestUri(&stringUri); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::handleMessage " "Start processing redirect message %d: '%s' '%s'", mNextSeqNo, method.data(), stringUri.data()); } if (method.compareTo(SIP_CANCEL_METHOD, UtlString::ignoreCase) == 0) { // For CANCEL. // If we have a suspended request with this Call-Id, cancel it. // Send a 200 response. // Cancel the suspended request. UtlString cancelCallId; message->getCallIdField(&cancelCallId); { // Seize the lock that protects the list of suspend objects. OsLock lock(mRedirectorMutex); // Look for a suspended request that had this Call-Id. UtlHashMapIterator itor(mSuspendList); // Fetch a pointer to each suspend object. while (itor()) { RedirectSuspend* suspend_object = dynamic_cast<RedirectSuspend*> (itor.value()); // Is this a request to which the CANCEL applies? if (suspend_object->mMessage.isInviteFor(message)) { // Send a 487 response to the original request. SipMessage response; response.setResponseData(&suspend_object->mMessage, SIP_REQUEST_TERMINATED_CODE, SIP_REQUEST_TERMINATED_TEXT); mpSipUserAgent->send(response); // Cancel the redirection. // (After we are done using suspend_object->mMessage // to generate the response.) UtlInt requestNo = *dynamic_cast<UtlInt*> (itor.key()); cancelRedirect(requestNo, suspend_object); } } } // We do not need to send a 200 for the CANCEL, as the stack does that // for us. (And will eat a 200 that we generate, it seems!) } else { // For all methods other than CANCEL: // Note: any ACK that gets passed up to the redirector // needs to be processed and forwarded if possible. // ACKs for the error responses sent from the redirector // are recognized by the stack and not passed to this application // // Call processRedirect to call the redirectors, and handle // their results, send a response or suspend processing of // the request. // For ACKs, no response is sent(or allowed). Instead, the ACK is routed // back to the proxy with information that allows it to be sent // to the correct next hop (ReqUri is replaced). // Assign mNextSeqNo as the sequence number for this request. // If this request needs to be suspended, processRedirect will // increment mNextSeqNo so that value will not be reused (soon). // Initially, the suspendObject is NULL. processRedirect(message, method, mNextSeqNo, (RedirectSuspend*) 0); } handled = TRUE; } break; case RedirectResumeMsg::REDIRECT_RESTART: { // A message saying that a redirector is now willing to resume // processing of a request. // Get the redirector and sequence number. const RedirectResumeMsg* msg = dynamic_cast<RedirectResumeMsg*> (&eventMessage); RedirectPlugin::RequestSeqNo seqNo = msg->getRequestSeqNo(); int redirectorNo = msg->getRedirectorNo(); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::handleMessage " "Resume for redirector %d request %d", redirectorNo, seqNo); // Look up the suspend object. UtlInt containableSeqNo(seqNo); RedirectSuspend* suspendObject = dynamic_cast<RedirectSuspend*> (mSuspendList.findValue(&containableSeqNo)); // If there is no request with this sequence number, ignore the message. if (!suspendObject) { Os::Logger::instance().log(FAC_SIP, PRI_WARNING, "SipRedirectServer::handleMessage No suspended request " "with seqNo %d", seqNo); break; } // Check that this redirector is suspended. if (redirectorNo < 0 || redirectorNo >= mRedirectorCount) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipRedirectServer::handleMessage " "Invalid redirector %d", redirectorNo); break; } if (!suspendObject->mRedirectors[redirectorNo].suspended) { Os::Logger::instance().log(FAC_SIP, PRI_WARNING, "SipRedirectServer::handleMessage Redirector %d is " "not suspended for seqNo %d", redirectorNo, seqNo); break; } // Mark this redirector as no longer wanting suspension. suspendObject->mRedirectors[redirectorNo].suspended = FALSE; suspendObject->mSuspendCount--; // If no more redirectors want suspension, reprocess the request. if (suspendObject->mSuspendCount == 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::handleMessage " "Start reprocessing request %d", seqNo); // Get a pointer to the message. const SipMessage* message = &suspendObject->mMessage; // Extract the request method. UtlString method; message->getRequestMethod(&method); processRedirect(message, method, seqNo, suspendObject); } handled = TRUE; } break; case OsMsg::OS_SHUTDOWN: { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::handleMessage received shutdown request" ); // Seize the lock that protects the list of suspend objects. OsLock lock(mRedirectorMutex); // Cancel all suspended requests. UtlHashMapIterator itor(mSuspendList); UtlInt* key; while ((key = dynamic_cast<UtlInt*> (itor()))) { cancelRedirect(*key, dynamic_cast<RedirectSuspend*>(itor.value())); } // Finalize and delete all the redirectors. PluginIterator redirectors(mRedirectPlugins); RedirectPlugin* redirector; while ((redirector = dynamic_cast<RedirectPlugin*>(redirectors.next()))) { redirector->finalize(); delete redirector; } spInstance = NULL; OsTask::requestShutdown(); // tell OsServerTask::run to exit handled = TRUE; } break; default: { Os::Logger::instance().log(FAC_SIP, PRI_CRIT, "SipRedirectServer::handleMessage unhandled msg type %d", msgType ); } } return handled; }
/** * Process a redirection. The caller sets up our processing, but we * carry it through to the generation of the response, queuing of the * suspend object, etc. We send the response if we finish processing. * * message is the message to be processed. Its memory is owned by our * caller, or is attached to the suspend object. * * method is the request's SIP method. Its memory is owned by our caller. * * seqNo is the sequence number to be used for this request. (If this * request is to be suspended and seqNo == mNextSeqNo, we must increment * mNextSeqNo.) * * suspendObject is the suspend object for this request (if it already * exists) or NULL. It is passed as an argument to avoid attempting to * look it up if the caller knows that it does not exist (because this * is a first processing attempt). */ void SipRedirectServer::processRedirect(const SipMessage* pMessage, UtlString& method, RedirectPlugin::RequestSeqNo seqNo, RedirectSuspend* suspendObject) { ErrorDescriptor errorDescriptor; // Extract the request URI. // This code is not strictly correct, as the request could have: // Route: <registrar [added by proxy]>, // sip:[user]@[domain], // [more] // due to an element Record-Routing with its AOR, but no element seems // to do that. // If we desire to handle that case, we have to correct the code here // to remove/skip any top routes that refer to the Registrar, then examine // the topmost remaining route (if any). // It would also require changing the code for routing ACKs that we forward // to modify the topmost route if present, instead of the request-URI. // There may be further complications due to strict routing. // All of this would be much like the Proxy forwarding requests. // Currently, we trust that all Route values refer to the registrar // and blindly remove them from any ACK that we must forward directly. UtlString stringUri; pMessage->getRequestUri(&stringUri); // The requestUri is an addr-spec, not a name-addr. Url requestUri(stringUri, TRUE); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::processRedirect " "Starting to process request URI '%s'", stringUri.data()); /* * Normalize the port in the Request URI * This is not strictly kosher, but it solves interoperability problems. * Technically, user@foo:5060 != user@foo , but many implementations * insist on including the explicit port even when they should not, and * it causes registration mismatches, so we normalize the URI when inserting * and looking up in the database so that if explicit port is the same as * the proxy listening port, then we remove it. * (Since our proxy has mProxyNormalPort open, no other SIP entity * can use sip:user@domain:mProxyNormalPort, so this normalization * cannot interfere with valid addresses.) * * For the strict rules, set the configuration parameter * SIP_REGISTRAR_PROXY_PORT : PORT_NONE */ if ( mProxyNormalPort != PORT_NONE && requestUri.getHostPort() == mProxyNormalPort ) { requestUri.setHostPort(PORT_NONE); } // Seize the lock that protects the list of suspend objects. OsLock lock(mRedirectorMutex); // Process with the redirectors. // Set to TRUE if any of the redirectors requests suspension. UtlBoolean willSuspend = FALSE; // Set to TRUE if any of the redirectors requests an error response. UtlBoolean willError = FALSE; PluginIterator iterator(mRedirectPlugins); RedirectPlugin* redirector; // Authority level of the last redirector to have modified the contact list. ssize_t contactListAuthorityLevel = LOWEST_AUTHORITY_LEVEL; ContactList contactList( stringUri ); int i; // Iterator sequence number. for (i = 0; (redirector = static_cast <RedirectPlugin*> (iterator.next())) && !willError; i++) { if (mpConfiguredRedirectors[i].bActive) { // verify if the redirector has a suitable authority level to perform a look-up if( mpConfiguredRedirectors[i].authorityLevel >= contactListAuthorityLevel ) { // Place to store the private storage pointer. SipRedirectorPrivateStorage* privateStorageP; // Initialize it. privateStorageP = (suspendObject ? suspendObject->mRedirectors[i].privateStorage : NULL); // Call the redirector to process the request. contactList.resetWasModifiedFlag(); RedirectPlugin::LookUpStatus status = redirector->lookUp(*pMessage, stringUri, requestUri, method, contactList, seqNo, i, privateStorageP, errorDescriptor); // Create the suspend object if it does not already exist and we need it. if (!suspendObject && (status == RedirectPlugin::SEARCH_PENDING || privateStorageP)) { suspendObject = new RedirectSuspend(mRedirectorCount); // Insert it into mSuspendList, keyed by seqNo. UtlInt* containableSeqNo = new UtlInt(seqNo); mSuspendList.insertKeyAndValue(containableSeqNo, suspendObject); // Save in it a copy of the message. (*pMessage is // dependent on the OsMsg bringing the message to us, and // will be freed when we are done with that OsMsg.) suspendObject->mMessage = *pMessage; // Use the next sequence number for the next request. if (seqNo == mNextSeqNo) { // Increment to the next value (and roll over if necessary). mNextSeqNo++; } } // Store the private storage pointer. if (suspendObject) { suspendObject->mRedirectors[i].privateStorage = privateStorageP; } int statusCode; UtlString reasonPhrase; // Dispatch on status. switch (status) { case RedirectPlugin::SUCCESS: // Processing was successful. If the plug-in modified the Contact list then // raise the authority level required to modify the contact list to the // authority level of this plug-in. if( contactList.wasListModified() ) { contactListAuthorityLevel = mpConfiguredRedirectors[i].authorityLevel; } break; case RedirectPlugin::ERROR: // Processing detected an error. Log it and set the 'error' flag. errorDescriptor.getStatusLineData( statusCode, reasonPhrase ); Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipRedirectServer::processRedirect " "ERROR returned by redirector " "'%s' while processing method '%s' URI '%s': " "Status code = %d (%s)", redirector->name().data(), method.data(), stringUri.data(), statusCode, reasonPhrase.data() ); willError = TRUE; break; case RedirectPlugin::SEARCH_PENDING: Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::processRedirect " "SEARCH_PENDING returned by redirector " "'%s' while processing method '%s' URI '%s'", redirector->name().data(), method.data(), stringUri.data()); willSuspend = TRUE; // Mark that this redirector has requested suspension. suspendObject->mRedirectors[i].suspended = TRUE; suspendObject->mRedirectors[i].needsCancel = TRUE; suspendObject->mSuspendCount++; break; default: Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipRedirectServer::processRedirect " "Invalid status value %d returned by redirector " "'%s' while processing method '%s' URI '%s'", status, redirector->name().data(), method.data(), stringUri.data()); break; } // end status switch } // end authorityLevel >= contactListAuthorityLevel else { // redirector plug-in does not have a suitable authority level to look up the // request - it is only allowed to observe it. redirector->observe(*pMessage, stringUri, requestUri, method, contactList, seqNo, i); } } // end mpConfiguredRedirectors[i] } // end Redirector plug-in iterator if (willError || !willSuspend) // redirector has done all it can { int numContacts = 0; if (method.compareTo(SIP_ACK_METHOD, UtlString::ignoreCase) == 0) { // See if location server has returned an address to use // to forward or just drop the ACK here. if (!willError && ((numContacts = contactList.entries()) == 1)) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::processRedirect " "Forwarding ACK for URI '%s': ", stringUri.data()); UtlString contactNameAddr; UtlString routeEntries; int maxForwards; // Get the (single) contact. contactList.get(0, contactNameAddr); // Convert from name-addr format (which is what is in // contactList) to addr-spec (which is what is needed // for a request-URI. Url contactUri(contactNameAddr, Url::NameAddr); UtlString contactAddrSpec; contactUri.getUri(contactAddrSpec); // create the message to forward - // change reqUri to located value, add route for debugging info, decrement max-forwards // leave last via since we are circling back, not responding SipMessage ackCopy(*pMessage); // copy original ACK ackCopy.setRequestFirstHeaderLine(SIP_ACK_METHOD, contactAddrSpec, SIP_PROTOCOL_VERSION); // Remove any existing Route headers. // See the comment at the top of this method ("Extract // the request URI") for discussion. while (ackCopy.removeHeader(SIP_ROUTE_FIELD, 0)) { // empty } // Process header parameters in the request URI, // especially moving any Route parameters to Route headers. ackCopy.applyTargetUriHeaderParams(); // Put Route to proxy in the first position, so the proxy // can see the ACK. ackCopy.addRouteUri(mAckRouteToProxy.data()); // Update or create the Max-Forwards header. if (!ackCopy.getMaxForwards(maxForwards)) { maxForwards = SIP_DEFAULT_MAX_FORWARDS; } maxForwards--; ackCopy.setMaxForwards(maxForwards); // get send-to address/port/protocol from top via UtlString lastViaAddress; int lastViaPort; UtlString lastViaProtocol; OsSocket::IpProtocolSocketType viaProtocol = OsSocket::UNKNOWN; ackCopy.getTopVia(&lastViaAddress, &lastViaPort, &lastViaProtocol); SipMessage::convertProtocolStringToEnum(lastViaProtocol.data(), viaProtocol); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::processRedirect " "sending ACK to '%s':%d using '%s' " "location service returned '%s', " "converted into '%s'", lastViaAddress.data(), lastViaPort, lastViaProtocol.data(), contactNameAddr.data(), contactAddrSpec.data()); // This ACK has no matching INVITE, special case mpSipUserAgent->sendStatelessAck(ackCopy, // this will add via lastViaAddress, lastViaPort, viaProtocol); } else // locater must return EXACTLY one value to forward { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipRedirectServer::processRedirect " "Cannot redirect ACK for URI '%s', dropping: " "number of contacts=%d ", stringUri.data(), numContacts); } } // end handle ACK redirection else // build and send the proper (error) response { // The response we will compose and, hopefully, send. SipMessage response; // Send a response and terminate processing now if an error has // been found or if no redirector has requested suspension. if (willError) { buildResponseFromRequestAndErrorDescriptor( response, *pMessage, errorDescriptor ); } else { // If request processing is finished, construct a response, // either 302 or 404. if (contactList.entries() > 0) { // There are contacts, so send a 302 Moved Temporarily. Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::processRedirect " "Contacts added, sending 302 response"); response.setResponseData(pMessage, SIP_TEMPORARY_MOVE_CODE, SIP_TEMPORARY_MOVE_TEXT); // add contacts collected in the contactList to the response size_t index; size_t numEntries = contactList.entries(); for( index = 0; index < numEntries; index++ ) { UtlString contactString; if( contactList.get( index, contactString ) ) { response.setContactField(contactString, index ); } else { Os::Logger::instance().log(FAC_SIP, PRI_CRIT, "SipRedirectServer::processRedirect " "Failed to retrieve contact index %zu", index ); } } } else { // There are no contacts, send back a 404 Not Found. Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::processRedirect " "No contacts added, sending 404 response"); response.setResponseData(pMessage, SIP_NOT_FOUND_CODE, SIP_NOT_FOUND_TEXT); } } // Identify ourselves in the response mpSipUserAgent->setUserAgentHeader(response); // Now that we've set the right code into the response, send the // response. mpSipUserAgent->send(response); } // end sending response to Invite // If the suspend object exists, remove it from mSuspendList // and delete it. if (suspendObject) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::processRedirect " "Cleaning up suspense of request %d", seqNo); UtlInt containableSeqNo(seqNo); cancelRedirect(containableSeqNo, suspendObject); } } // end redirector did all it could else { // Request is suspended. Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipRedirectServer::processRedirect " "Suspending request %d", seqNo); } }
void ContactList::touch( const RedirectPlugin& plugin ) { OsSysLog::add(FAC_SIP, PRI_NOTICE, "ContactList::touch(): list touched by %s", plugin.name().data() ); mbListWasModified = true; }