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; }
/** * 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; }