UtlBoolean SipRefreshManager::getAcceptedExpiration(RefreshDialogState* state, const SipMessage& sipResponse, int& expirationPeriod) { UtlString method; UtlBoolean foundExpiration = FALSE; int cseq; sipResponse.getCSeqField(&cseq, &method); if(method.compareTo(SIP_REGISTER_METHOD) == 0) { // Get the presumably first contact in the REGISTER request // so that we can find the same contact in the response and // find out what the expiration is UtlString requestContact; Url requestContactUri; if(state && state->mpLastRequest && state->mpLastRequest->getContactEntry(0, &requestContact)) { requestContactUri = requestContact; } // Register could have it in the Contact header UtlString responseContactValue; int contactIndex = 0; while(sipResponse.getContactEntry(contactIndex , &responseContactValue)) { // Get the expires parameter for the contact if it exists Url contactUri(responseContactValue); if(requestContactUri.isUserHostPortEqual(contactUri)) { UtlString contactExpiresParameter; if(contactUri.getFieldParameter(SIP_EXPIRES_FIELD, contactExpiresParameter) && !contactExpiresParameter.isNull()) { foundExpiration = TRUE; // Convert to int expirationPeriod = atoi(contactExpiresParameter); } } contactIndex++; } } if(!foundExpiration) { // Not sure if we care if this is a request or response foundExpiration = sipResponse.getExpiresField(&expirationPeriod); } return(foundExpiration); }
EndpointDescriptor* SessionContext::createCallerEndpointDescriptor( const SipMessage& sipRequest, const NatTraversalRules& natTraversalRules ) { // The Caller endpoint descriptor is initialized based on the information contained in the // contact URI. This is where the NAT traversal feature encodes location information about // the caller for dialog-forming requests. UtlString tmpString; sipRequest.getContactEntry( 0, &tmpString ); Url contactUri( tmpString ); return new EndpointDescriptor( contactUri, natTraversalRules ); }
UtlBoolean SipRefreshManager::getInitialExpiration(const SipMessage& sipRequest, int& expirationPeriod) { UtlString method; UtlBoolean foundExpiration = FALSE; sipRequest.getRequestMethod(&method); if(method.compareTo(SIP_REGISTER_METHOD) == 0) { // Register could have it in the Contact header UtlString requestContactValue; if(sipRequest.getContactEntry(0 , &requestContactValue)) { // Get the expires parameter for the contact if it exists Url contactUri(requestContactValue); UtlString contactExpiresParameter; if(contactUri.getFieldParameter(SIP_EXPIRES_FIELD, contactExpiresParameter) && !contactExpiresParameter.isNull()) { foundExpiration = TRUE; // Convert to int expirationPeriod = atoi(contactExpiresParameter); } } } if(!foundExpiration) { // Not sure if we care if this is a request or response foundExpiration = sipRequest.getExpiresField(&expirationPeriod); } return(foundExpiration); }
/** * 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); } }
AuthPlugin::AuthResult TransferControl::authorizeAndModify(const UtlString& id, /**< The authenticated identity of the * request originator, if any (the null * string if not). * This is in the form of a SIP uri * identity value as used in the * credentials database (user@domain) * without the scheme or any parameters. */ const Url& requestUri, ///< parsed target Uri RouteState& routeState, ///< the state for this request. const UtlString& method,///< the request method AuthResult priorResult,///< results from earlier plugins. SipMessage& request, ///< see AuthPlugin wrt modifying bool bSpiralingRequest, ///< request spiraling indication UtlString& reason ///< rejection reason ) { AuthResult result = CONTINUE; // get the call-id to use in logging UtlString callId; request.getCallIdField(&callId); UtlString hostAddress; int hostPort; UtlString hostProtocols; //requestUri.getHostAddress(hostAddress); //request.getContactUri(0, &hostAddress);; request.getContactAddress(0, &hostAddress,&hostPort,&hostProtocols); if (DENY != priorResult) { if (method.compareTo(SIP_REFER_METHOD) == 0) { UtlString targetStr; if (request.getReferToField(targetStr)) { Url target(targetStr, Url::NameAddr); // parse the target URL UtlString targetMethod; if ( Url::SipUrlScheme == target.getScheme() /* REFER can create requests other than INVITE: we don't care about those * * so check that the method is INVITE or is unspecified (INVITE is the default) */ && ( ! target.getUrlParameter(SIP_METHOD_URI_PARAMETER, targetMethod) || (0==targetMethod.compareTo(SIP_INVITE_METHOD, UtlString::ignoreCase)) )) { if (id.isNull()) { // UnAuthenticated REFER. Do challenge the REFER to confirm the // identity of the transferor. Note: prior to XECS-2487, we used to challenge // only the unauthenticated REFERs that didn't carry a Replaces header. // The fix for XECS-2487 now requires that all unauthenticated REFERs // be challenged so that consultative transfers get routed properly // when user-based gateway section is used. See tracker for the details if (mpSipRouter->isLocalDomain(target)) { //White list of two servets to let Exchange REFER to sipXecs endpoints if (hostAddress.compareTo(server1, UtlString::ignoreCase) == 0 || hostAddress.compareTo(server2, UtlString::ignoreCase) == 0) { Os::Logger::instance().log(FAC_AUTH, PRI_INFO, "TransferControl[%s]::authorizeAndModify " "Whitelist host '%s' in call '%s'", mInstanceName.data(),hostAddress.data(),callId.data() ); result = ALLOW; //Whitelist matched so allow the transfer }else{ Os::Logger::instance().log(FAC_AUTH, PRI_INFO, "TransferControl[%s]::authorizeAndModify " "challenging transfer in call '%s' from host '%s'", mInstanceName.data(), callId.data(),hostAddress.data() ); result = DENY; // we need an identity to attach to the Refer-To URI } } else { /* * This is a transfer to a target outside our domain, so let it go * unchallenged. See XECS-806 */ Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify " "allowing foriegn transfer in call '%s'", mInstanceName.data(), callId.data() ); // Add the References to the refer-to. Adding the callId field as a reference // header (will be used in resulting INVITE) in the Refer-To provides us // with enough information to be able to logically tie the calls together. // Useful for CDR records. UtlString refcallId(callId); refcallId.append(";rel=refer"); target.setHeaderParameter(SIP_REFERENCES_FIELD, refcallId.data()); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify " "adding Reference field [%s] to refer-to", mInstanceName.data(), callId.data() ); request.setReferToField(target.toString().data()); result = ALLOW; } } else { UtlString contactString; request.getContactEntry(0, &contactString); Url contactUri( contactString ); UtlString userId; contactUri.getUserId(contactString); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl::authorizeAndModify - Contact field is: %s ", contactString.data()); if (contactString != "callcontroller") { // Authenticated REFER // annotate the refer-to with the authenticated controller identity SipXauthIdentity controllerIdentity; controllerIdentity.setIdentity(id); controllerIdentity.encodeUri(target); // add the References to the refer-to. UtlString refcallId(callId); refcallId.append(";rel=refer"); target.setHeaderParameter(SIP_REFERENCES_FIELD, refcallId.data()); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify " "adding Reference field [%s] to refer-to", mInstanceName.data(), callId.data() ); request.setReferToField(target.toString().data()); } } } else { Os::Logger::instance().log(FAC_AUTH, PRI_WARNING, "TransferControl[%s]::authorizeAndModify " "unrecognized refer target '%s' for call '%s'", mInstanceName.data(), targetStr.data(), callId.data() ); } } else { // REFER without a Refer-To header... incorrect, but just ignore it. Os::Logger::instance().log(FAC_AUTH, PRI_WARNING, "TransferControl[%s]::authorizeAndModify " "REFER method without Refer-To in call '%s'", mInstanceName.data(), callId.data() ); } } else if (method.compareTo(SIP_INVITE_METHOD) == 0) { UtlString targetCallId; UtlString targetFromTag; UtlString targetToTag; if (request.getReplacesData(targetCallId, targetToTag, targetFromTag)) { /* * This is an INVITE with Replaces: probably either the completion * of a call pickup or a consultative transfer. * In any case, it will not create a new call - just connect something * to an existing call - so we don't need to make any new authorization * decisions. */ result = ALLOW; } else { // INVITE without Replaces: is not a transfer - ignore it. } } else { // neither REFER nor INVITE, so is not a transfer - ignore it. } } else { // Some earlier plugin already denied this - don't waste time figuring it out. Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "TransferControl[%s]::authorizeAndModify " "prior authorization result %s for call %s", mInstanceName.data(), AuthResultStr(priorResult), callId.data() ); } return result; }
RedirectPlugin::LookUpStatus SipRedirectorRegDB::lookUp( const SipMessage& message, const UtlString& requestString, const Url& requestUri, const UtlString& method, ContactList& contactList, RequestSeqNo requestSeqNo, int redirectorNo, SipRedirectorPrivateStorage*& privateStorage, ErrorDescriptor& errorDescriptor) { unsigned long timeNow = OsDateTime::getSecsSinceEpoch(); // Local copy of requestUri Url requestUriCopy = requestUri; // Look for any grid parameter and remove it. UtlString gridParameter; UtlBoolean gridPresent = requestUriCopy.getUrlParameter("grid", gridParameter, 0); if (gridPresent) { requestUriCopy.removeUrlParameter("grid"); } if (Os::Logger::instance().willLog(FAC_SIP, PRI_DEBUG)) { UtlString temp; requestUriCopy.getUri(temp); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp gridPresent = %d, gridParameter = '%s', " "requestUriCopy after removing grid = '%s'", mLogName.data(), gridPresent, gridParameter.data(), temp.data()); } RegDB::Bindings registrations; // Give the ~~in~ URIs separate processing. UtlString user; requestUriCopy.getUserId(user); if (user.index(URI_IN_PREFIX) == 0) { // This is a ~~in~ URI. // Check for an '&' separator. ssize_t s = user.last('&'); if (s != UTL_NOT_FOUND) { // This is a ~~in~[user]&[instrument] URI. const char* instrumentp = user.data() + s + 1; UtlString u; u.append(user, sizeof (URI_IN_PREFIX) - 1, s - (sizeof (URI_IN_PREFIX) - 1)); requestUriCopy.setUserId(u); //regDB-> // getUnexpiredContactsUserInstrument(requestUriCopy, instrumentp, timeNow, registrations); UtlString identity; requestUriCopy.getIdentity(identity); _dataStore.regDB().getUnexpiredContactsUserInstrument(identity.str(), instrumentp, timeNow, registrations); } else { // This is a ~~in~[instrument] URI. const char* instrumentp = user.data() + sizeof (URI_IN_PREFIX) - 1; _dataStore.regDB().getUnexpiredContactsInstrument(instrumentp, timeNow, registrations); } } else { // Note that getUnexpiredContactsUser will reduce the requestUri to its // identity (user/host/port) part before searching in the // database. The requestUri identity is matched against the // "identity" column of the database, which is the identity part of // the "uri" column which is stored in registration.xml. UtlString identity; requestUriCopy.getIdentity(identity); _dataStore.regDB().getUnexpiredContactsUser(identity.str(), timeNow, registrations); } int numUnexpiredContacts = registrations.size(); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp got %d unexpired contacts", mLogName.data(), numUnexpiredContacts); // Check for a per-user call forward timer. // Don't set timer if we're not going to forward to voicemail. std::ostringstream userCfwdTimer; bool foundUserCfwdTimer = false; if (method.compareTo(SIP_INVITE_METHOD) == 0) { UtlString noRoute; requestUriCopy.getUrlParameter("sipx-noroute", noRoute); if ((!noRoute.isNull()) && (noRoute.compareTo("Voicemail") == 0)) { // This is not a call scenerio controlled by this users "forward to voicemail" timer } else { UtlString identity; requestUriCopy.getIdentity(identity); EntityRecord entity; foundUserCfwdTimer = _dataStore.entityDB().findByIdentity(identity.str(), entity); if (foundUserCfwdTimer) userCfwdTimer << entity.callForwardTime(); } } for (RegDB::Bindings::const_iterator iter = registrations.begin(); iter != registrations.end(); iter++) { // Query the Registration DB for the contact, expires and qvalue columns. Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp contact = '%s', qvalue = '%s', path = '%s'", mLogName.data(), iter->getContact().c_str(), iter->getQvalue().c_str(), iter->getPath().c_str() ); Url contactUri(iter->getContact().c_str()); // If available set the per-user call forward timer. if (foundUserCfwdTimer) { contactUri.setHeaderParameter("expires", userCfwdTimer.str().c_str()); } // If the contact URI is the same as the request URI, ignore it. if (!contactUri.isUserHostPortEqual(requestUriCopy)) { // Check if the q-value from the database is valid, and if so, // add it into contactUri. if (!iter->getQvalue().empty()) { // :TODO: (XPL-3) need a RegEx copy constructor here // Check if q value is numeric and between the range 0.0 and 1.0. static RegEx qValueValid("^(0(\\.\\d{0,3})?|1(\\.0{0,3})?)$"); if (qValueValid.Search(iter->getQvalue().c_str())) { contactUri.setFieldParameter(SIP_Q_FIELD, iter->getQvalue().c_str()); } } // Re-apply any grid parameter. if (gridPresent) { contactUri.setUrlParameter("grid", gridParameter); } contactUri.setUrlParameter(SIP_SIPX_CALL_DEST_FIELD, "INT"); // Check if database contained a Path value. If so, add a Route // header parameter to the contact with the Path vector taken from // the registration data. if (!iter->getPath().empty()) { UtlString existingRouteValue; std::string pathVector = iter->getPath(); if ( contactUri.getHeaderParameter(SIP_ROUTE_FIELD, existingRouteValue)) { // there is already a Route header parameter in the contact; append it to the // Route derived from the Path vector. pathVector += SIP_MULTIFIELD_SEPARATOR; pathVector += existingRouteValue.str(); } contactUri.setHeaderParameter(SIP_ROUTE_FIELD, pathVector.c_str()); } // Add the contact. contactList.add( contactUri, *this ); } } return RedirectPlugin::SUCCESS; }
RedirectPlugin::LookUpStatus SipRedirectorAliasDB::lookUp( const SipMessage& message, UtlString& requestString, Url& requestUri, const UtlString& method, ContactList& contactList, RequestSeqNo requestSeqNo, int redirectorNo, SipRedirectorPrivateStorage*& privateStorage, ErrorDescriptor& errorDescriptor) { // If url param sipx-userforward = false, do not redirect to user-forward // aliases. UtlString userforwardParam; requestUri.getUrlParameter("sipx-userforward", userforwardParam); bool disableForwarding = userforwardParam.compareTo("false", UtlString::ignoreCase) == 0; if (disableForwarding) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp user forwarding disabled by parameter", mLogName.data()); } if (_enableEarlyAliasResolution) { resolveAlias(message, requestString, requestUri); } UtlString requestIdentity; requestUri.getIdentity(requestIdentity); EntityDB::Aliases aliases; bool isUserIdentity = false; EntityDB* entityDb = SipRegistrar::getInstance(NULL)->getEntityDB(); entityDb->getAliasContacts(requestUri, aliases, isUserIdentity); int numAliasContacts = aliases.size(); if (numAliasContacts > 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp " "got %d AliasDB contacts", mLogName.data(), numAliasContacts); // Check if the request identity is a real user/extension UtlString realm; UtlString authType; SipXauthIdentity authIdentity; authIdentity.setIdentity(requestIdentity); for (EntityDB::Aliases::iterator iter = aliases.begin(); iter != aliases.end(); iter++) { // If disableForwarding and the relation value is "userforward", // do not record this contact. if (!(disableForwarding && iter->relation == ALIASDB_RELATION_USERFORWARD)) { UtlString contact = iter->contact.c_str(); Url contactUri(contact); // if the request identity is a real user if (isUserIdentity) { // Encode AuthIdentity into the URI authIdentity.encodeUri(contactUri, message); } contactUri.setUrlParameter(SIP_SIPX_CALL_DEST_FIELD, "AL"); contactList.add( contactUri, *this ); if (_enableDiversionHeader && contactList.getDiversionHeader().empty()) { // // Add a Diversion header for all deflections // UtlString stringUri; message.getRequestUri(&stringUri); // The requestUri is an addr-spec, not a name-addr. Url diversionUri(stringUri, TRUE); UtlString userId; diversionUri.getUserId(userId); UtlString host; diversionUri.getHostWithPort(host); std::ostringstream strm; strm << "<sip:"; if (!userId.isNull()) strm << userId.data() << "@"; strm << host.data(); strm << ">;reason=unconditional;sipxfwd=" << iter->relation; UtlString diversion = strm.str().c_str(); OS_LOG_INFO(FAC_SIP, "SipRedirectorAliasDB::lookUp inserting diversion from " << diversion.data()); contactList.setDiversionHeader(diversion.data()); } } } } return RedirectPlugin::SUCCESS; }
bool SipRedirectorAliasDB::resolveAlias( const SipMessage& message, UtlString& requestString, Url& requestUri ) { bool isDomainAlias = false; bool isUserAlias = false; UtlString domain; UtlString hostAlias; UtlString userAlias; requestUri.getHostAddress(domain); UtlBoolean isMyHostAlias = mpSipUserAgent->isMyHostAlias(requestUri); if (mpSipUserAgent && domain != _localDomain && isMyHostAlias) { isDomainAlias = true; hostAlias = domain; } UtlString requestIdentity; requestUri.getIdentity(requestIdentity); EntityDB::Aliases aliases; bool isUserIdentity = false; EntityDB* entityDb = SipRegistrar::getInstance(NULL)->getEntityDB(); entityDb->getAliasContacts(requestUri, aliases, isUserIdentity); int numAliasContacts = aliases.size(); EntityDB::Aliases::iterator iter = aliases.begin(); if (numAliasContacts == 1 && iter != aliases.end()) { // If disableForwarding and the relation value is "userforward", // do not record this contact. if (isUserIdentity && iter->relation != ALIASDB_RELATION_USERFORWARD && iter->relation != "callgroup") { UtlString contact = iter->contact.c_str(); Url contactUri(contact); isUserAlias = true; contactUri.getUserId(userAlias); } } if (isUserAlias) { requestUri.setUserId(userAlias.data()); requestUri.getUri(requestString); } if (isDomainAlias) { requestUri.setHostAddress(hostAlias); requestUri.getUri(requestString); } if (isUserAlias || isDomainAlias) { OS_LOG_NOTICE(FAC_SIP, "SipRedirectorAliasDB::lookUp normalized request-uri to " << requestString.data()); } return isUserAlias || isDomainAlias; }
RedirectPlugin::LookUpStatus SipRedirectorMapping::lookUp( const SipMessage& message, const UtlString& requestString, const Url& requestUri, const UtlString& method, ContactList& contactList, RequestSeqNo requestSeqNo, int redirectorNo, SipRedirectorPrivateStorage*& privateStorage, ErrorDescriptor& errorDescriptor) { UtlString callTag = "UNK"; UtlString permissionName; ResultSet urlMappingRegistrations; ResultSet urlMappingPermissions; // @JC This variable is strangely overloaded // If we have no permissions then add any encountered // contacts. If we do have permissions then the // permission must match UtlBoolean permissionFound = TRUE; if (mMappingRulesLoaded == OS_SUCCESS) { mMap.getContactList( requestUri, urlMappingRegistrations, urlMappingPermissions, callTag); } int numUrlMappingPermissions = urlMappingPermissions.getSize(); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp " "got %d UrlMapping Permission requirements for %d contacts", mLogName.data(), numUrlMappingPermissions, urlMappingRegistrations.getSize()); if (numUrlMappingPermissions > 0) { // check if we have field parameter that indicates that some permissions should be ignored UtlString ignorePermissionStr; // :KLUDGE: remove const_cast and uri declaration after XSL-88 gets fixed Url& uri = const_cast<Url&>(requestUri); uri.getUrlParameter("sipx-noroute", ignorePermissionStr); EntityRecord entity; std::set<std::string> permissions; if (_dataStore.entityDB().findByIdentityOrAlias(requestUri, entity)) permissions = entity.permissions(); size_t numDBPermissions = permissions.size(); for (int i = 0; i<numUrlMappingPermissions; i++) { UtlHashMap record; urlMappingPermissions.getIndex(i, record); UtlString permissionKey("permission"); UtlString urlMappingPermissionStr = *((UtlString*) record.findValue(&permissionKey)); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp checking permissions DB for " "urlMappingPermissions[%d] = '%s'", mLogName.data(), i, urlMappingPermissionStr.data()); // Try to match the permission // so assume it cannot be found unless we // see a match in the IMDB permissionFound = FALSE; // if the permission we are looking for is the one the we are supposed to ignore, // than assume that permission is not found if (urlMappingPermissionStr.compareTo(ignorePermissionStr, UtlString::ignoreCase) == 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp ignoring permission '%s'", mLogName.data(), ignorePermissionStr.data()); continue; } UtlString permissionsFound; for (std::set<std::string>::const_iterator iter = permissions.begin(); iter != permissions.end(); iter++) { UtlString dbPermissionStr = iter->c_str(); bool equal = dbPermissionStr.compareTo(urlMappingPermissionStr, UtlString::ignoreCase) == 0; if (Os::Logger::instance().willLog(FAC_SIP, PRI_DEBUG)) { permissionsFound.append(" "); permissionsFound.append(dbPermissionStr); if (equal) { permissionsFound.append("[matches]"); } } if (equal) { // matching permission found in IMDB permissionFound = TRUE; break; } dbPermissionStr.remove(0); } Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp %d permissions configured for request URI '%s'. Checking: %s", mLogName.data(), numDBPermissions, requestUri.toString().data(), permissionsFound.data()); if (permissionFound) { break; } urlMappingPermissionStr.remove(0); } } // either there were no requirements to match against voicemail // or there were and we found a match in the IMDB for the URI // so now add the contacts to the SIP message if (permissionFound) { int numUrlMappingRegistrations = urlMappingRegistrations.getSize(); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp got %d UrlMapping Contacts", mLogName.data(), numUrlMappingRegistrations); if (numUrlMappingRegistrations > 0) { for (int i = 0; i < numUrlMappingRegistrations; i++) { UtlHashMap record; urlMappingRegistrations.getIndex(i, record); UtlString contactKey("contact"); UtlString contact= *(dynamic_cast <UtlString*> (record.findValue(&contactKey))); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp contact = '%s'", mLogName.data(), contact.data()); Url contactUri(contact); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp contactUri = '%s'", mLogName.data(), contactUri.toString().data()); // We no longer check for recursive loops here because we // have comprehensive loop detection in the proxy. UtlString recordRoute; UtlString curCallDest; if (message.getRecordRouteField(0,&recordRoute)) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp RecordRouteField = '%s'", mLogName.data(), recordRoute.data()); } contactUri.setUrlParameter(SIP_SIPX_CALL_DEST_FIELD, callTag.data()); // Add the contact. contactList.add( contactUri, *this ); } } } return RedirectPlugin::SUCCESS; }
RedirectPlugin::LookUpStatus SipRedirectorAliasDB::lookUp( const SipMessage& message, UtlString& requestString, Url& requestUri, const UtlString& method, ContactList& contactList, RequestSeqNo requestSeqNo, int redirectorNo, SipRedirectorPrivateStorage*& privateStorage, ErrorDescriptor& errorDescriptor) { // If url param sipx-userforward = false, do not redirect to user-forward // aliases. UtlString userforwardParam; requestUri.getUrlParameter("sipx-userforward", userforwardParam); bool disableForwarding = userforwardParam.compareTo("false", UtlString::ignoreCase) == 0; if (disableForwarding) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp user forwarding disabled by parameter", mLogName.data()); } bool isDomainAlias = false; UtlString domain; UtlString hostAlias; requestUri.getHostAddress(domain); UtlBoolean isMyHostAlias = mpSipUserAgent->isMyHostAlias(requestUri); if (mpSipUserAgent && domain != _localDomain && isMyHostAlias) { isDomainAlias = true; hostAlias = domain; requestUri.setHostAddress(_localDomain); } UtlString requestIdentity; requestUri.getIdentity(requestIdentity); OS_LOG_DEBUG(FAC_SIP, mLogName.data() << "::lookUp identity: " << requestIdentity.data() << " domain: " << domain.data() << " local-domain: " << _localDomain.data() << " isHostAlias: " << isMyHostAlias); //ResultSet aliases; //AliasDB::getInstance()->getContacts(requestUri, aliases); //int numAliasContacts = aliases.getSize(); EntityDB::Aliases aliases; bool isUserIdentity = false; EntityDB* entityDb = SipRegistrar::getInstance(NULL)->getEntityDB(); entityDb->getAliasContacts(requestUri, aliases, isUserIdentity); int numAliasContacts = aliases.size(); if (numAliasContacts > 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp " "got %d AliasDB contacts", mLogName.data(), numAliasContacts); // Check if the request identity is a real user/extension UtlString realm; UtlString authType; SipXauthIdentity authIdentity; authIdentity.setIdentity(requestIdentity); for (EntityDB::Aliases::iterator iter = aliases.begin(); iter != aliases.end(); iter++) { // If disableForwarding and the relation value is "userforward", // do not record this contact. if (!(disableForwarding && iter->relation == ALIASDB_RELATION_USERFORWARD)) { UtlString contact = iter->contact.c_str(); Url contactUri(contact); // if the request identity is a real user if (isUserIdentity) { // Encode AuthIdentity into the URI authIdentity.encodeUri(contactUri, message); } contactUri.setUrlParameter(SIP_SIPX_CALL_DEST_FIELD, "AL"); if (numAliasContacts == 1 && isDomainAlias && isUserIdentity) { UtlString userId; contactUri.getUserId(userId); requestUri.setUserId(userId.data()); requestUri.getUri(requestString); OS_LOG_NOTICE(FAC_SIP, "SipRedirectorAliasDB::lookUp normalized request-uri to " << requestString.data()); } else { // Add the contact. contactList.add( contactUri, *this ); } } } } else if (isDomainAlias) { // // No alias found. If this is was towards a domain alias, make sure to reset it back to // the old value prior to feeding it to the rest of the redirectors. // requestUri.setHostAddress(hostAlias); requestUri.getUri(requestString); } return RedirectPlugin::SUCCESS; }
RedirectPlugin::LookUpStatus SipRedirectorFallback::lookUp( const SipMessage& message, UtlString& requestString, Url& requestUri, const UtlString& method, ContactList& contactList, RequestSeqNo requestSeqNo, int redirectorNo, SipRedirectorPrivateStorage*& privateStorage, ErrorDescriptor& errorDescriptor) { ResultSet urlMappingRegistrations; UtlString callTag = "UNK"; if (mMappingRulesLoaded == OS_SUCCESS) { UtlString callerLocation; determineCallerLocation( message, callerLocation ); #ifndef __USE_OLD_FALLBACKRULES_SCHEMA__ mMap.getContactList( requestUri, callerLocation, urlMappingRegistrations, callTag ); #else ResultSet dummyMappingPermissions; mMap.getContactList( requestUri, urlMappingRegistrations, dummyMappingPermissions ); #endif int numUrlMappingRegistrations = urlMappingRegistrations.getSize(); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp got %d UrlMapping Contacts for %s @ location '%s'", mLogName.data(), numUrlMappingRegistrations, requestString.data(), callerLocation.data() ); if (numUrlMappingRegistrations > 0) { for (int i = 0; i < numUrlMappingRegistrations; i++) { UtlHashMap record; urlMappingRegistrations.getIndex(i, record); UtlString contactKey("contact"); UtlString contact= *(dynamic_cast <UtlString*> (record.findValue(&contactKey))); UtlString callTagKey("callTag"); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp contact = '%s'", mLogName.data(), contact.data()); Url contactUri(contact); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "%s::lookUp contactUri = '%s'", mLogName.data(), contactUri.toString().data()); contactUri.setUrlParameter(SIP_SIPX_CALL_DEST_FIELD, callTag.data()); // Add the contact. contactList.add( contactUri, *this ); } } } return RedirectPlugin::SUCCESS; }
RedirectPlugin::LookUpStatus SipRedirectorAliasDB::lookUp( const SipMessage& message, const UtlString& requestString, const Url& requestUri, const UtlString& method, ContactList& contactList, RequestSeqNo requestSeqNo, int redirectorNo, SipRedirectorPrivateStorage*& privateStorage, ErrorDescriptor& errorDescriptor) { // If url param sipx-userforward = false, do not redirect to its aliases UtlString disableForwarding; requestUri.getUrlParameter("sipx-userforward", disableForwarding); if (disableForwarding.compareTo("false", UtlString::ignoreCase) == 0) { OsSysLog::add(FAC_SIP, PRI_DEBUG, "%s::lookUp user forwarding disabled by parameter", mLogName.data()); } else { UtlString requestIdentity; requestUri.getIdentity(requestIdentity); OsSysLog::add(FAC_SIP, PRI_DEBUG, "%s::lookUp identity '%s'", mLogName.data(), requestIdentity.data()); ResultSet aliases; AliasDB::getInstance()->getContacts(requestUri, aliases); int numAliasContacts = aliases.getSize(); if (numAliasContacts > 0) { OsSysLog::add(FAC_SIP, PRI_DEBUG, "%s::lookUp " "got %d AliasDB contacts", mLogName.data(), numAliasContacts); // Check if the request identity is a real user/extension UtlString realm; UtlString authType; bool isUserIdentity = CredentialDB::getInstance()->isUriDefined(requestUri, realm, authType); SipXauthIdentity authIdentity; authIdentity.setIdentity(requestIdentity); for (int i = 0; i < numAliasContacts; i++) { static UtlString contactKey("contact"); UtlHashMap record; if (aliases.getIndex(i, record)) { UtlString contact = *((UtlString*)record.findValue(&contactKey)); Url contactUri(contact); // if the request identity is a real user if (isUserIdentity) { // Encode AuthIdentity into the URI authIdentity.encodeUri(contactUri, message); } contactUri.setUrlParameter(SIP_SIPX_CALL_DEST_FIELD, "AL"); // Add the contact. contactList.add( contactUri, *this ); } } } } return RedirectPlugin::SUCCESS; }