void SipClient::getClientNames(UtlString& clientNames) const { char portString[40]; // host DNS name sprintf(portString, "%d", mRemoteHostPort); clientNames = " remote host: "; clientNames.append(mRemoteHostName); clientNames.append(":"); clientNames.append(portString); // host IP address clientNames.append(" remote IP: "); clientNames.append(mRemoteSocketAddress); clientNames.append(":"); clientNames.append(portString); // via address clientNames.append(" remote Via address: "); clientNames.append(mRemoteViaAddress); clientNames.append(":"); clientNames.appendNumber(mRemoteViaPort); // received address clientNames.append(" received address: "); clientNames.append(mReceivedAddress); clientNames.append(":"); clientNames.appendNumber(mRemoteReceivedPort); }
void RegistrationDB::getUnexpiredContactsFieldsContaining ( UtlString& substringToMatch, const int& timeNow, UtlSList& matchingContactFields ) const { // Clear the results matchingContactFields.destroyAll(); if( m_pFastDB != NULL ) { SMART_DB_ACCESS; dbCursor< RegistrationRow > cursor; UtlString queryString = "contact like '%" + substringToMatch + "%' and expires>"; queryString.appendNumber( timeNow ); dbQuery query; query = queryString; if ( cursor.select(query) > 0 ) { // Copy all the unexpired contacts into the result list do { UtlString* contactValue = new UtlString(cursor->contact); matchingContactFields.append( contactValue ); } while ( cursor.next() ); } } else { OsSysLog::add(FAC_DB, PRI_CRIT, "RegistrationDB::getUnexpiredContactsFieldsContaining failed - no DB"); } }
void CallId::getNewCallId(UtlString& callId) { // Lock to protect common variables. OsLock lock(sCallIdMutex); // Increment the call number, rolling over after 0xFFFF, since we only // use the low-order 16 bits. sCallNum = (sCallNum + 1) & 0xFFFF; // Compute the next value. nextValue(); // Compose the new Call-Id. callId.remove(0); callId.append(sChainValue, 0, CALL_ID_CHAIN_VALUE_REVEALED); callId.appendNumber(sCallNum, "%04x"); }
void NatMaintainer::sendKeepAliveToEndpoint( const char* pIpAddress, uint16_t portNumber ) { bool bDoSendKeepAlive = true; KeepAliveEndpointDescriptor* pKeepAliveEndpointDescriptor; pKeepAliveEndpointDescriptor = &( mpEndpointsKeptAliveList[ portNumber ] ); if( pKeepAliveEndpointDescriptor->mLastRefreshRoundNumber == mRefreshRoundNumber ) { // We have already sent a keep-alive to an endpoint utilizing this port in this refresh // round. Check to see if we also have an IP address match. If we do, that endpoint // has already been refreshed in this round so do not send it yet another refresh. if( strcmp( pIpAddress, pKeepAliveEndpointDescriptor->mIpAddress ) == 0 ) { bDoSendKeepAlive = false; } } else { // We have yet to send a keep-alive to an endpoint utilizing this port in this refresh round // Save the endpoint's IP address against the port in mpEndpointsKeptAliveList[portNumber] // to prevent the generation of any aditional keep alives to that endpoint during that round. pKeepAliveEndpointDescriptor->mLastRefreshRoundNumber = mRefreshRoundNumber; strncpy( pKeepAliveEndpointDescriptor->mIpAddress, pIpAddress, MAX_IP_ADDRESS_STRING_SIZE ); pKeepAliveEndpointDescriptor->mIpAddress[ MAX_IP_ADDRESS_STRING_SIZE ] = 0; } if( bDoSendKeepAlive ) { // Generate unique call-id and branches for each remote endpoint UtlString callId = pIpAddress; callId.append('-').appendNumber( portNumber, "%d" ) .append('-').append( NAT_MAINTAINER_MARKER_STRING ).append('-').append( mBaseCallId ); mpKeepAliveMessage->setCallIdField( callId ); UtlString branchId = callId; branchId.appendNumber( mNextSeqValue, "%d" ); mpKeepAliveMessage->setSendProtocol(OsSocket::UDP); mpKeepAliveMessage->setTopViaTag( branchId.data(), "branch" ); mpSipRouter->sendUdpKeepAlive( *mpKeepAliveMessage, pIpAddress, portNumber ); } }
void Notifier::sendNotifyForeachSubscription ( const char* key, const char* event, SipMessage& notify, const SipMessage* subscribe) { SubscribeDB::Subscriptions subscriptions; int timeNow = (int)OsDateTime::getSecsSinceEpoch(); // Get all subscriptions associated with this identity SubscribeDB::defaultCollection().collection().getUnexpiredSubscriptions( SUBSCRIPTION_COMPONENT_STATUS, key, event, timeNow, subscriptions ); // Add the static configured contacts. UtlString userUri(key); UtlString eventType(event); UtlString userContact; UtlString userFromUri; UtlString userToUri; UtlString userCallid; EntityRecord entity; if (EntityDB::defaultCollection().collection().findByIdentity(std::string(key), entity)) { std::vector<EntityRecord::StaticUserLoc> staticUserLoc = entity.staticUserLoc(); if (staticUserLoc.size() > 0) { //staticUserLoc[0].event.c_str(); userContact = staticUserLoc[0].contact.c_str(); userFromUri = staticUserLoc[0].fromUri.c_str(); userToUri = staticUserLoc[0].toUri.c_str(); userCallid = staticUserLoc[0].callId.c_str(); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "Notifier::sendNotifyForeachSubscription configured contact %s", userUri.data()); int notifycseq = 0; userCallid.append("-"); userCallid.appendNumber((mpStaticSeq++ & 0xFFFF), "%04x"); UtlString id(""); UtlString subscriptionState("terminated"); UtlString recordroute(""); SendTheNotify(notify, mpSipUserAgent, userUri, userContact, userFromUri, userToUri, userCallid, notifycseq, eventType, id, subscriptionState, recordroute); } } int numSubscriptions = subscriptions.size(); if ( numSubscriptions > 0 ) { Os::Logger::instance().log( FAC_SIP, PRI_INFO, "Notifier::sendNotifyForeachSubscription: " " %d '%s' subscriptions for '%s'", numSubscriptions, event, key ); UtlString subscribeCallid; if ( subscribe ) { subscribe->getCallIdField(&subscribeCallid); Os::Logger::instance().log( FAC_SIP, PRI_INFO, "Notifier::sendNotifyForeachSubscription: " " notify only '%s'", subscribeCallid.data() ); } // There may be any number of subscriptions // for the same identity and event type! // send a notify to each for (SubscribeDB::Subscriptions::iterator iter = subscriptions.begin(); iter != subscriptions.end(); iter++ ) { Subscription& record = *iter; UtlString uri = record.uri().c_str(); UtlString callid = record.callId().c_str(); UtlString contact = record.contact().c_str(); int expires = record.expires(); UtlString eventtype = record.eventType().c_str(); UtlString id = record.id().c_str(); UtlString to = record.toUri().c_str(); UtlString from = record.fromUri().c_str(); if ( subscribe && subscribeCallid != callid ) { Os::Logger::instance().log( FAC_SIP, PRI_DEBUG, "Notifier::sendNotifyForeachSubscription: " " skipping '%s'; notify only '%s'", callid.data(), subscribeCallid.data() ); continue; } UtlString key = record.key().c_str(); UtlString recordroute= record.recordRoute().c_str(); int notifycseq = record.notifyCseq(); // the recordRoute column in the database is optional // and can be set to null, the IMDB does not support null columns // so look to see if the field is set to "%" a special single character // reserved value we use to null columns in the IMDB if ( record.recordRoute().empty() ) { recordroute.remove(0); } UtlString subscriptionState; int requestedExpires = -1; if ( subscribe ) { subscribe->getExpiresField(&requestedExpires); } if ( requestedExpires == 0 ) { subscriptionState = SIP_SUBSCRIPTION_TERMINATED; // the subscription should be removed from the database after this NOTIFY is sent } else { subscriptionState = SIP_SUBSCRIPTION_ACTIVE ";expires="; char expStr[10]; sprintf(expStr, "%d", expires); subscriptionState.append(expStr); } // increment the outbound cseq sent with the notify // this will guarantee that duplicate messages are rejected notifycseq += 1; SendTheNotify(notify, mpSipUserAgent, uri, contact, to, from, callid, notifycseq, eventtype, id, subscriptionState, recordroute); // Update the Notify sequence number (CSeq) in the IMDB // (We must supply a dummy XML version number.) SubscribeDB::defaultCollection().collection().updateNotifyUnexpiredSubscription ( SUBSCRIPTION_COMPONENT_STATUS, to, from, callid, eventtype, id, timeNow, notifycseq, 0 ); } } else { Os::Logger::instance().log( FAC_SIP, PRI_INFO, "Notifier::sendNotifyForeachSubscription: " " no '%s' subscriptions for '%s'", event, key ); } }
void Dialog::getBytes(UtlString& b, ssize_t& l) { b.remove(0); b.append(BEGIN_DIALOG); UtlString singleLine; singleLine = DOUBLE_QUOTE; XmlEscape(singleLine, mId); singleLine += DOUBLE_QUOTE; b += singleLine; if (!mCallId.isNull()) { b.append(CALL_ID_EQUAL); singleLine = DOUBLE_QUOTE; XmlEscape(singleLine, mCallId); singleLine += DOUBLE_QUOTE; b += singleLine; } if (!mLocalTag.isNull()) { b.append(LOCAL_TAG_EQUAL); // mLocalTag is a token singleLine = DOUBLE_QUOTE + mLocalTag + DOUBLE_QUOTE; b += singleLine; } if (!mRemoteTag.isNull()) { b.append(REMOTE_TAG_EQUAL); // mRemoteTag is a token singleLine = DOUBLE_QUOTE + mRemoteTag + DOUBLE_QUOTE; b += singleLine; } if (!mDirection.isNull()) { b.append(DIRECTION_EQUAL); // mDirection is a token singleLine = DOUBLE_QUOTE + mDirection + DOUBLE_QUOTE; b += singleLine; } b.append(END_LINE); // State element b.append(BEGIN_STATE); if (!mEvent.isNull()) { b.append(EVENT_EQUAL); // mEvent is a token singleLine = DOUBLE_QUOTE + mEvent + DOUBLE_QUOTE; b += singleLine; } if (!mCode.isNull()) { b.append(CODE_EQUAL); // mCode is a token singleLine = DOUBLE_QUOTE + mCode + DOUBLE_QUOTE; b += singleLine; } // End of state element singleLine = END_BRACKET + mState + END_STATE; b += singleLine; // Duration element if (mDuration !=0) { b += BEGIN_DURATION; b.appendNumber((Int64) OsDateTime::getSecsSinceEpoch() - mDuration); b += END_DURATION; } // Local element b.append(BEGIN_LOCAL); if (!mLocalIdentity.isNull()) { b.append(BEGIN_IDENTITY); if (!mLocalDisplay.isNull()) { UtlString displayName = mLocalDisplay; NameValueTokenizer::frontBackTrim(&displayName, "\""); b.append(DISPLAY_EQUAL); singleLine = DOUBLE_QUOTE; XmlEscape(singleLine, displayName); singleLine += DOUBLE_QUOTE; b += singleLine; } singleLine = END_BRACKET; XmlEscape(singleLine, mLocalIdentity); singleLine += END_IDENTITY; b += singleLine; } if (!mLocalTarget.isNull() && mLocalTarget.compareTo("sip:") != 0) { singleLine = BEGIN_TARGET; XmlEscape(singleLine, mLocalTarget); singleLine += DOUBLE_QUOTE END_LINE; b += singleLine; // add optional parameters UtlDListIterator* iterator = getLocalParameterIterator(); NameValuePairInsensitive* nvp; while ((nvp = (NameValuePairInsensitive*) (*iterator)())) { singleLine = BEGIN_DIALOG_PARAM; singleLine += PNAME; XmlEscape(singleLine, nvp->data()); singleLine += PVALUE; XmlEscape(singleLine, nvp->getValue()); singleLine += END_DIALOG_PARAM; b += singleLine; } delete iterator; singleLine = END_TARGET; b += singleLine; } // End of local element b.append(END_LOCAL); // Remote element b.append(BEGIN_REMOTE); if (!mRemoteIdentity.isNull()) { b.append(BEGIN_IDENTITY); if (!mRemoteDisplay.isNull()) { UtlString displayName = mRemoteDisplay; NameValueTokenizer::frontBackTrim(&displayName, "\""); b.append(DISPLAY_EQUAL); singleLine = DOUBLE_QUOTE; XmlEscape(singleLine, displayName); singleLine += DOUBLE_QUOTE; b += singleLine; } singleLine = END_BRACKET; XmlEscape(singleLine, mRemoteIdentity); singleLine += END_IDENTITY; b += singleLine; } if (!mRemoteTarget.isNull() && mRemoteTarget.compareTo("sip:") != 0) { singleLine = BEGIN_TARGET; XmlEscape(singleLine, mRemoteTarget); singleLine += DOUBLE_QUOTE END_LINE; b += singleLine; // add optional parameters UtlDListIterator* iterator = getRemoteParameterIterator(); NameValuePairInsensitive* nvp; while ((nvp = (NameValuePairInsensitive*) (*iterator)())) { singleLine = BEGIN_DIALOG_PARAM; singleLine += PNAME; XmlEscape(singleLine, nvp->data()); singleLine += PVALUE; XmlEscape(singleLine, nvp->getValue()); singleLine += END_DIALOG_PARAM; b += singleLine; } delete iterator; singleLine = END_TARGET; b += singleLine; } // End of remote element b.append(END_REMOTE); // End of dialog element b.append(END_DIALOG); l = b.length(); }
void AppearanceTest() { instantiateAllTestFixtures( "appearance-groups1.xml", "subscription1", "credential1", "177.0.0.1:54140"); UtlString sharedUri = "sip:[email protected]:54140"; UtlString app1uri = "sip:127.0.0.1:45141"; UtlString dialogHandle; // receive the reg-info subscribe SipMessage request; UtlString b; ssize_t l; while(getNextMessageFromAppearanceAgentUnderTest( request, 5 )) { request.getBytes(&b, &l); OsSysLog::add(FAC_RLS, PRI_DEBUG, "got message %s", b.data()); UtlString method; request.getRequestMethod(&method); if(!request.isResponse() && 0 == method.compareTo(SIP_SUBSCRIBE_METHOD) ) { // Accept the Subscription, regardless of whether it for a 'dialog' or 'reg' event // in order to stop retransmissions SipMessage regResponse; regResponse.setResponseData(&request, 202, "Accepted", app1uri); SipMessage * dispatchedMessage = new SipMessage(regResponse); dispatchedMessage->getBytes(&b, &l); OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent message %s", b.data()); pAppearanceAgentUnderTest->mServerUserAgent.dispatch(dispatchedMessage); // Deal with the two events separately UtlString eventField; request.getEventField(eventField); if(0 == eventField.compareTo("reg")) { UtlString contactInfo; request.getContactUri(0, &contactInfo); UtlString callid; request.getCallIdField(&callid); int cseq; request.getCSeqField(&cseq, NULL); Url toField; regResponse.getToUrl(toField); SipMessage regNotify; regNotify.setNotifyData(&request, 1, "", "", "reg"); UtlString regInfo ("<?xml version=\"1.0\"?>\r\n" "<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" " "xmlns:gr=\"urn:ietf:params:xml:ns:gruuinfo\" version=\"911\" state=\"full\">\r\n" " <registration aor=\"sip:[email protected]:54140\" id=\"sip:[email protected]:54140\" state=\"active\">\r\n " " <contact id=\"sip:[email protected]:54140@@<"); regInfo.append(contactInfo); regInfo.append(">\" state=\"active\" event=\"registered\" q=\"1\" callid=\""); regInfo.append(callid); regInfo.append("\" cseq=\""); regInfo.appendNumber(cseq); regInfo.append("\">\r\n"); regInfo.append("<uri>"); regInfo.append(app1uri); regInfo.append("</uri>"); regInfo.append(" </contact>\r\n" " </registration>\r\n" "</reginfo>"); HttpBody * newBody = new HttpBody (regInfo, strlen(regInfo), "application/reginfo+xml"); regNotify.setContentType("application/reginfo+xml"); regNotify.setBody(newBody); // Set the From field the same as the to field from the 202 response, as it // contains the dialog identifying to tags regNotify.setRawFromField(toField.toString().data()); sendToAppearanceAgentUnderTest( regNotify ); regNotify.getBytes(&b, &l); OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent reg NOTIFY to AppAgent"); OsSysLog::add(FAC_RLS, PRI_DEBUG, "sent message %s", b.data()); } else if (0 == eventField.compareTo(SLA_EVENT_TYPE)) { // should send empty NOTIFY, but no one will care // save dialogHandle for this subscription/Appearance (ignore retransmissions) if (dialogHandle.isNull()) { SipMessage fake(b); fake.getDialogHandle(dialogHandle); OsSysLog::add(FAC_RLS, PRI_DEBUG, "got SUBSCRIBE(sla) request: dialogHandle %s", dialogHandle.data()); } } } } CPPUNIT_ASSERT( !dialogHandle.isNull() ); OsSysLog::add(FAC_RLS, PRI_DEBUG, "we now have an Appearance - test it"); AppearanceGroup* pAppGroup = pAppearanceAgentUnderTest->getAppearanceGroupSet(). findAppearanceGroup(sharedUri); CPPUNIT_ASSERT( pAppGroup ); Appearance* pApp = pAppGroup->findAppearance(dialogHandle); CPPUNIT_ASSERT( pApp ); ASSERT_STR_EQUAL( app1uri.data(), pApp->getUri()->data() ); // test adding a new dialog const char* dialogEventString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"0\" state=\"partial\" entity=\"sip:[email protected]:54140\">\n" "<dialog id=\"1\" call-id=\"[email protected]\" local-tag=\"264460498\" remote-tag=\"1c10982\" direction=\"recipient\">\n" "<state>confirmed</state>\n" "<local>\n" "<identity>[email protected]:5120</identity>\n" "<target uri=\"sip:[email protected]:5120\">\n" "<param pname=\"x-line-id\" pval=\"0\"/>\n" "<param pname=\"+sip.rendering\" pval=\"yes\"/>\n" "</target>\n" "</local>\n" "<remote>\n" "<identity>[email protected]</identity>\n" "</remote>\n" "</dialog>\n" "</dialog-info>\n" ; SipDialogEvent dialogEvent(dialogEventString); bool bFullContentChanged = false; bool bPartialContentChanged = pApp->updateState(&dialogEvent, bFullContentChanged); CPPUNIT_ASSERT(bPartialContentChanged); CPPUNIT_ASSERT(bFullContentChanged); pApp->dumpState(); CPPUNIT_ASSERT(pApp->appearanceIsBusy()); CPPUNIT_ASSERT(pApp->appearanceIdIsSeized("0")); CPPUNIT_ASSERT(!pApp->appearanceIdIsSeized("1")); // simulate user putting the call on hold const char* dialogEventString2 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"0\" state=\"partial\" entity=\"sip:[email protected]:54140\">\n" "<dialog id=\"1\" call-id=\"[email protected]\" local-tag=\"264460498\" remote-tag=\"1c10982\" direction=\"recipient\">\n" "<state>confirmed</state>\n" "<local>\n" "<identity>[email protected]:5120</identity>\n" "<target uri=\"sip:[email protected]:5120\">\n" "<param pname=\"x-line-id\" pval=\"0\"/>\n" "<param pname=\"+sip.rendering\" pval=\"no\"/>\n" "</target>\n" "</local>\n" "<remote>\n" "<identity>[email protected]</identity>\n" "</remote>\n" "</dialog>\n" "</dialog-info>\n" ; SipDialogEvent dialogEvent2(dialogEventString2); bPartialContentChanged = pApp->updateState(&dialogEvent2, bFullContentChanged); CPPUNIT_ASSERT(bPartialContentChanged); CPPUNIT_ASSERT(bFullContentChanged); pApp->dumpState(); CPPUNIT_ASSERT(!pApp->appearanceIsBusy()); CPPUNIT_ASSERT(pApp->appearanceIdIsSeized("0")); CPPUNIT_ASSERT(!pApp->appearanceIdIsSeized("1")); // test MESSAGE debug handling const char* message = "MESSAGE sip:[email protected]:54140 SIP/2.0\r\n" "From: <sip:[email protected]>;tag=17211757-9E4FBD78\r\n" "To: <sip:[email protected]:54140>\r\n" "CSeq: 1 MESSAGE\r\n" "Call-ID: 51405734-b9be4835-dcd9d196\r\n" "Contact: <sip:[email protected]>\r\n" "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, INFO, MESSAGE, SUBSCRIBE, NOTIFY, PRACK, UPDATE, REFER\r\n" "Event: dialog\r\n" "User-Agent: UnitTest\r\n" "Accept-Language: en\r\n" "Accept: application/dialog-info+xml\r\n" "Max-Forwards: 70\r\n" "Expires: 3600\r\n" "Content-Length: 0\r\n" "\r\n"; // send the MESSAGE SipMessage messageRequest( message, strlen( message ) ); CPPUNIT_ASSERT( sendToAppearanceAgentUnderTest( messageRequest ) ); // receive the 200 OK response SipMessage response; CPPUNIT_ASSERT( getNextMessageFromAppearanceAgentUnderTest( response, 5 ) ); CPPUNIT_ASSERT( response.isResponse() ); CPPUNIT_ASSERT( response.getResponseStatusCode() == SIP_OK_CODE ); }
// Add to the HttpBody the current state of the resource. void ResourceInstance::generateBody(UtlString& rlmi, HttpBody& body, UtlBoolean consolidated, const UtlString& displayName) const { OsSysLog::add(FAC_RLS, PRI_DEBUG, "ResourceInstance::generateBody mInstanceName = '%s', consolidated = %d, displayName = '%s', mContentPresent = %d", mInstanceName.data(), consolidated, displayName.data(), mContentPresent); if (consolidated) { if (mContentPresent) { // If this is a consolidated dialog event list, edit the // stored XML into the right form and append each dialog // to the resource list event notice. TiXmlUtlStringWriter writer(&rlmi); // Iterate through all the <dialog> elements. UtlHashMapIterator itor(mXmlDialogs); UtlContainable* id; while ((id = itor())) { UtlVoidPtr* p = dynamic_cast <UtlVoidPtr*> (itor.value()); TiXmlElement* dialog_element = static_cast <TiXmlElement*> (p->getValue()); // Now that we've got a <dialog> element, edit it to fit // into a consolidated event notice. // Get the display name right. // Find the <local> element, which we know exists due to // earlier processing. TiXmlNode* local = dialog_element->FirstChild("local"); // Find the <local><identity> element, which we know // exists due to earlier processing. TiXmlNode* identity = local->FirstChild("identity"); // Update the display attribute, as that is what will show // on the phone. identity->ToElement()-> SetAttribute("display", displayName); // Un-parse the dialog into the string for storage. writer << *dialog_element; writer << "\r\n"; } } } else { // Generate the XML for the instance. rlmi += " <instance id=\""; XmlEscape(rlmi, mInstanceName); rlmi += "\" state=\""; // Subscription states don't require escaping. rlmi += mSubscriptionState; rlmi += "\""; // Generate the body part for the instance, if necessary. if (mContentPresent) { UtlString contentBodyPartCid; // Use the count of parts in 'body' to generate a unique identifier for // each part. contentBodyPartCid.appendNumber(body.getMultipartCount()); contentBodyPartCid += "@"; contentBodyPartCid += getResourceListServer()->getDomainName(); rlmi += " cid=\""; rlmi += contentBodyPartCid; rlmi += "\""; // Now add the <...> and use it in the header. contentBodyPartCid.prepend("<"); contentBodyPartCid.append(">"); HttpBody content_body(mContent.data(), mContent.length(), getResourceListServer()->getContentType()); UtlDList content_body_parameters; content_body_parameters.append( new NameValuePair(HTTP_CONTENT_ID_FIELD, contentBodyPartCid)); body.appendBodyPart(content_body, content_body_parameters); content_body_parameters.destroyAll(); } rlmi += "/>\r\n"; } }
/// Provide access to files as allowed by process definitions. void HttpFileAccess::processRequest(const HttpRequestContext& requestContext, const HttpMessage& request, HttpMessage*& response ) { UtlString message; response = new HttpMessage(); UtlString peerName; if (mSipxRpc->isAllowedPeer(requestContext, peerName)) { if (requestContext.methodIs(HTTP_GET_METHOD)) { UtlString path; requestContext.getMappedPath(path); FileResource* resource = FileResourceManager::getInstance()->find(path); if (resource) { if (resource->isReadable()) { sendFile(path, peerName, requestContext, request, response); } else { message.append("resource "); resource->appendDescription(message); message.append(" does not allow write access to '"); message.append(path); message.append("'"); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s", peerName.data(), message.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION, HTTP_FORBIDDEN_CODE, "Access denied by process definition"); response->setBody(new HttpBody(message.data(),message.length())); response->setContentType(CONTENT_TYPE_TEXT_PLAIN); response->setContentLength(message.length()); } } else { message.append("File resource '"); message.append(path); message.append("' not known to sipXsupervisor."); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s", peerName.data(), message.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION, HTTP_FILE_NOT_FOUND_CODE, HTTP_FILE_NOT_FOUND_TEXT); response->setBody(new HttpBody(message.data(),message.length())); response->setContentType(CONTENT_TYPE_TEXT_PLAIN); response->setContentLength(message.length()); } } else if (requestContext.methodIs(HTTP_DELETE_METHOD)) { UtlString path; requestContext.getMappedPath(path); FileResource* resource = FileResourceManager::getInstance()->find(path); if (resource) { if (resource->isWriteable()) { OsPath filePath(path); if (OsFileSystem::exists(filePath)) { if (OS_SUCCESS == OsFileSystem::remove(filePath, TRUE /* recursive */, TRUE /* force */)) { message.append("File '"); message.append(path); message.append("' deleted"); OsSysLog::add(FAC_SUPERVISOR, PRI_INFO, "HttpFileAccess::processRequest from %s %s", peerName.data(), message.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION_1_1, HTTP_OK_CODE, "Deleted"); response->setContentLength(0); // tell anyone who cares that this has changed resource->modified(); } else { int httpStatusCode; UtlString httpStatusText; switch (errno) { case EACCES: httpStatusCode = HTTP_FORBIDDEN_CODE; httpStatusText = "File Access Denied"; break; default: httpStatusCode = HTTP_SERVER_ERROR_CODE; httpStatusText.append("Unknown error "); httpStatusText.appendNumber(errno); break; } message.append("File '"); message.append(path); message.append("' errno "); message.appendNumber(errno); message.append(" "); char errnoMsg[1024]; strerror_r(errno, errnoMsg, sizeof(errnoMsg)); message.append(errnoMsg); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s", peerName.data(), message.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION, httpStatusCode, httpStatusText); response->setBody(new HttpBody(message.data(),message.length())); response->setContentType(CONTENT_TYPE_TEXT_PLAIN); response->setContentLength(message.length()); } } else { message.append("File to be deleted '"); message.append(path); message.append("' does not exist"); OsSysLog::add(FAC_SUPERVISOR, PRI_INFO, "HttpFileAccess::processRequest from %s %s", peerName.data(), message.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION_1_1, HTTP_OK_CODE, "File does not exist"); response->setContentLength(0); } } else { message.append("resource "); resource->appendDescription(message); message.append(" does not allow write access to '"); message.append(path); message.append("'"); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s", peerName.data(), message.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION, HTTP_FORBIDDEN_CODE, "Access denied by process definition"); response->setBody(new HttpBody(message.data(),message.length())); response->setContentType(CONTENT_TYPE_TEXT_PLAIN); response->setContentLength(message.length()); } } else { message.append("File resource '"); message.append(path); message.append("' not known to sipXsupervisor."); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s", peerName.data(), message.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION, HTTP_FILE_NOT_FOUND_CODE, HTTP_FILE_NOT_FOUND_TEXT); response->setBody(new HttpBody(message.data(),message.length())); response->setContentType(CONTENT_TYPE_TEXT_PLAIN); response->setContentLength(message.length()); } } else { OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest %s from %s", HTTP_UNSUPPORTED_METHOD_TEXT, peerName.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION, HTTP_UNSUPPORTED_METHOD_CODE, HTTP_UNSUPPORTED_METHOD_TEXT); } } else { message.append("Request not supported from untrusted peer."); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest %s", message.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION, HTTP_FORBIDDEN_CODE, HTTP_FORBIDDEN_TEXT); response->setBody(new HttpBody(message.data(),message.length())); response->setContentType(CONTENT_TYPE_TEXT_PLAIN); response->setContentLength(message.length()); } }
/// wrapper function for all reg-info event tests bool ContactSetTest(UtlString regContactxml, UtlString requestUri, UtlString route) { bool ret = FALSE; instantiateAllTestFixtures( "resource-lists2.xml", "subscription1", "credential1", "sip:127.0.0.1:45141", FALSE); // receive the reg-info subscribe SipMessage request; while(getNextMessageFromRlsClientUnderTest( request, 5 ) ) { UtlString method; request.getRequestMethod(&method); if(!request.isResponse() && 0 == method.compareTo(SIP_SUBSCRIBE_METHOD) ) { // Accept the Subscription, regardless of whether it for a 'dialog' or 'reg' event // in order to stop retransmissions SipMessage regResponse; regResponse.setResponseData(&request, 202, "Accepted", "sip:127.0.0.1:45141"); SipMessage * dispatchedMessage = new SipMessage(regResponse); pResourceServerUnderTest->mClientUserAgent.dispatch(dispatchedMessage); // Deal with the two events separately UtlString eventField; request.getEventField(eventField); if(0 == eventField.compareTo("reg")) { UtlString contactInfo; request.getContactUri(0, &contactInfo); UtlString callid; request.getCallIdField(&callid); int cseq; request.getCSeqField(&cseq, NULL); Url toField; regResponse.getToUrl(toField); SipMessage regNotify; regNotify.setNotifyData(&request, 1, "", "", "reg"); UtlString regInfo ("<?xml version=\"1.0\"?>\r\n" "<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" " "xmlns:gr=\"urn:ietf:params:xml:ns:gruuinfo\" version=\"911\" state=\"full\">\r\n" " <registration aor=\"sip:[email protected]\" id=\"sip:[email protected]\" state=\"active\">\r\n " " <contact id=\"sip:[email protected]@@<"); regInfo.append(contactInfo); regInfo.append(">\" state=\"active\" event=\"registered\" q=\"1\" callid=\""); regInfo.append(callid); regInfo.append("\" cseq=\""); regInfo.appendNumber(cseq); regInfo.append("\">\r\n"); regInfo.append(regContactxml); regInfo.append(" </contact>\r\n" " </registration>\r\n" "</reginfo>"); HttpBody * newBody = new HttpBody (regInfo, strlen(regInfo), "application/reginfo+xml"); regNotify.setContentType("application/reginfo+xml"); regNotify.setBody(newBody); // Set the From field the same as the to field from the 202 response, as it // contains the dialog identifying to tags regNotify.setRawFromField(toField.toString().data()); sendToRlsServerUnderTest( regNotify ); } else if(0 == eventField.compareTo("dialog")) { // If we find a dialog event subscription with the request uri and route // that we are looking for, mark the test as passed UtlString uri; UtlString myRoute; request.getRequestUri(&uri); request.getRouteField(&myRoute); if(0 == uri.compareTo(requestUri) && 0 == route.compareTo(myRoute)) { ret = true; } } } } return ret; }
void testOneShotAfter() { struct TestOneShotStruct { const char* testDescription; long seconds; long milliseconds; int tolerance; }; OsCallback* pNotifier; string Message; int testCount; TestOneShotStruct testData[] = { { "Test one shot after when time is specified as 0", 0, 0, OSTIMETOLERANCE }, { "Test one shot after when time is equal to one second", 1, 0, OSTIMETOLERANCE }, /* The next case was added to check if the inaccuracy applies ONLY to decimal values or even to integers */ { "Test one shot after when time is greater than one second", 2, 285, OSTIMETOLERANCE }, { "Test one shot after when time is an integer > 1", 3, 0, OSTIMETOLERANCE }, { "Test one shot after when time is greater than 0 but less than 1", 0, 252, OSTIMETOLERANCE }, }; testCount = sizeof(testData)/ sizeof(testData[0]); for (int i = 0; i < testCount; i++) { long expectedWaitUSecs; OsTimer* pTimer; UtlBoolean returnValue; OsTime timeToWait(testData[i].seconds, testData[i].milliseconds*OsTime::USECS_PER_MSEC); pNotifier = new OsCallback((void*)this, TVCallback); pTimer = new OsTimer(*pNotifier); expectedWaitUSecs = SecsToUsecs(testData[i].seconds) + MsecsToUsecs(testData[i].milliseconds); // Give a small delay so we synchronize with the timer. OsTask::delay(20); gettimeofday(&startTV, NULL); gCallBackCount = 0; returnValue = pTimer->oneshotAfter(timeToWait); OsTask::delay(expectedWaitUSecs / OsTime::USECS_PER_MSEC + testData[i].tolerance); // gCallBackCount is reinitialized to 0 each iteration, so // its value should be 1 now. UtlString failureMessage; failureMessage.remove(0); failureMessage.append("Timer did not fire for iteration "); failureMessage.appendNumber(i); failureMessage.append("\n"); failureMessage.append(testData[i].testDescription); failureMessage.append("\n seconds: "); failureMessage.appendNumber(testData[i].seconds); failureMessage.append("\n milliseconds: "); failureMessage.appendNumber(testData[i].milliseconds); failureMessage.append("\n tolerance: "); failureMessage.appendNumber(testData[i].tolerance); KNOWN_BUG("Fails on ecs-fc8. Timing issue.", "XECS-1975"); CPPUNIT_ASSERT_MESSAGE(failureMessage.data(), gCallBackCount == 1); failureMessage.remove(0); failureMessage.append("oneshotAfter returned failure on iteration "); failureMessage.appendNumber(i); failureMessage.append("\n"); failureMessage.append(testData[i].testDescription); failureMessage.append("\n seconds: "); failureMessage.appendNumber(testData[i].seconds); failureMessage.append("\n milliseconds: "); failureMessage.appendNumber(testData[i].milliseconds); failureMessage.append("\n tolerance: "); failureMessage.appendNumber(testData[i].tolerance); CPPUNIT_ASSERT_MESSAGE(failureMessage.data(), returnValue); REPORT_SKEW((" Timing inaccuracy for iter %3d = %8ld us; Time = %ld.%03ld;\n", i, getTimeDeltaInUsecs() - expectedWaitUSecs, testData[i].seconds, testData[i].milliseconds )); delete pTimer; delete pNotifier; } }
bool FileRpcReplaceFile::execute(const HttpRequestContext& requestContext, UtlSList& params, void* userData, XmlRpcResponse& response, ExecutionStatus& status) { const int minPermissions = 0100; const int maxPermissions = 0777; bool result = false; status = XmlRpcMethod::FAILED; if (4 != params.entries()) { handleExtraExecuteParam(name(), response, status); } else { if (!params.at(0) || !params.at(0)->isInstanceOf(UtlString::TYPE)) { handleMissingExecuteParam(name(), PARAM_NAME_CALLING_HOST, response, status); } else { UtlString* pCallingHostname = dynamic_cast<UtlString*>(params.at(0)); if (!params.at(1) || !params.at(1)->isInstanceOf(UtlString::TYPE)) { handleMissingExecuteParam(name(), PARAM_NAME_FILE_NAME, response, status); } else { UtlString* pfileName = dynamic_cast<UtlString*>(params.at(1)); if (!params.at(2) || !params.at(2)->isInstanceOf(UtlInt::TYPE)) { handleMissingExecuteParam(name(), PARAM_NAME_FILE_PERMISSIONS, response, status); } else { UtlInt* pfilePermissions = dynamic_cast<UtlInt*>(params.at(2)); if (!params.at(3) || !params.at(3)->isInstanceOf(UtlString::TYPE)) { handleMissingExecuteParam(name(), PARAM_NAME_FILE_DATA, response, status); } else { UtlBool method_result(true); SipxRpc* pSipxRpcImpl = ((SipxRpc *)userData); if(validCaller(requestContext, *pCallingHostname, response, *pSipxRpcImpl, name())) { // Check the resource permissions. To be added when available. FileResource* fileResource = FileResourceManager::getInstance()->find(pfileName->data()); if (!fileResource) { UtlString faultMsg; faultMsg.append("File '"); faultMsg.append(*pfileName); faultMsg.append("' not declared as a resource by any sipXecs process"); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "FileRpc::replaceFile %s", faultMsg.data()); result=false; response.setFault(FileRpcMethod::InvalidParameter, faultMsg); } else if (!fileResource->isWriteable()) { UtlString faultMsg; faultMsg.append("File '"); faultMsg.append(*pfileName); faultMsg.append("' is not writeable (configAccess='read-only')"); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "FileRpc::replaceFile %s", faultMsg.data()); result=false; response.setFault(FileRpcMethod::InvalidParameter, faultMsg); } else if ( ( pfilePermissions->getValue() <= minPermissions ) || ( pfilePermissions->getValue() > maxPermissions )) { UtlString faultMsg; faultMsg.appendNumber(pfilePermissions->getValue(),"File permissions %04o"); faultMsg.appendNumber(minPermissions,"not within valid range (%04o - "); faultMsg.appendNumber(maxPermissions,"%04o)"); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "FileRpc::replaceFile %s", faultMsg.data()); result = false; response.setFault(FileRpcMethod::InvalidParameter, faultMsg); } else { // Write out the file. UtlString* pfileData = dynamic_cast<UtlString*>(params.at(3)); UtlString faultMsg; if((result=replicateFile(*pfileName,*pfilePermissions,*pfileData,faultMsg ))) { // Construct and set the response. response.setResponse(&method_result); status = XmlRpcMethod::OK; // Tell anyone who cares that this changed fileResource->modified(); } else { // Replication failed. response.setFault(FileRpcMethod::InvalidParameter, faultMsg); } } } } } } } } return result; }
// Constructor SipPersistentSubscriptionMgr::SipPersistentSubscriptionMgr( const UtlString& component, const UtlString& domain, const UtlString& fileName) : mComponent(component), mDomain(domain), mSubscriptionDBInstance(SubscriptionDB::getInstance(fileName)), mPersistenceTimer(mPersistTask.getMessageQueue(), 0), mPersistTask(mSubscriptionDBInstance) { OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipPersistentSubscriptionMgr:: " "mComponent = '%s', mDomain = '%s', fileName = '%s'", mComponent.data(), mDomain.data(), fileName.data()); // Start the persist task. mPersistTask.start(); // Read the subscription table and initialize the SipSubscriptionMgr. unsigned long now = OsDateTime::getSecsSinceEpoch(); ResultSet rs; mSubscriptionDBInstance->getAllRows(rs); UtlSListIterator itor(rs); UtlHashMap* rowp; while ((rowp = dynamic_cast <UtlHashMap*> (itor()))) { if (OsSysLog::willLog(FAC_SIP, PRI_DEBUG)) { UtlString out; UtlHashMapIterator itor(*rowp); UtlString* key; UtlContainable* value; while ((key = dynamic_cast <UtlString*> (itor()))) { value = itor.value(); if (!out.isNull()) { out.append(", "); } out.append(*key); out.append(" = "); if (value->getContainableType() == UtlString::TYPE) { out.append("'"); out.append(*(dynamic_cast <UtlString*> (value))); out.append("'"); } else if (value->getContainableType() == UtlInt::TYPE) { out.appendNumber((int) (*(dynamic_cast <UtlInt*> (value)))); } else { out.append(value->getContainableType()); } } OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipPersistentSubscriptionMgr:: " "table row: %s", out.data()); } // First, filter for rows that have the right component and have // not yet expired. UtlString* componentp = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gComponentKey)); assert(componentp); int expires = *(dynamic_cast <UtlInt*> (rowp->findValue(&SubscriptionDB::gExpiresKey))); if (componentp->compareTo(mComponent) == 0 && expires - now >= 0) { OsSysLog::add(FAC_SIP, PRI_DEBUG, "SipPersistentSubscriptionMgr:: " "loading row"); // Extract the values from the row. const UtlString* top = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gToKey)); const UtlString* fromp = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gFromKey)); const UtlString* callidp = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gCallidKey)); const UtlString* eventtypep = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gEventtypeKey)); const UtlString* eventidp = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gIdKey)); // Correct the null string if it is returned as // SPECIAL_IMDB_NULL_VALUE. if (eventidp->compareTo(special_imdb_null_value) == 0) { eventidp = &null_string; } int subcseq = *(dynamic_cast <UtlInt*> (rowp->findValue(&SubscriptionDB::gSubscribecseqKey))); const UtlString* urip = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gUriKey)); const UtlString* contactp = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gContactKey)); const UtlString* routep = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gRecordrouteKey)); // Correct the null string if it is returned as // SPECIAL_IMDB_NULL_VALUE. if (routep->compareTo(special_imdb_null_value) == 0) { routep = &null_string; } int notifycseq = *(dynamic_cast <UtlInt*> (rowp->findValue(&SubscriptionDB::gNotifycseqKey))); const UtlString* acceptp = dynamic_cast <UtlString*> (rowp->findValue(&SubscriptionDB::gAcceptKey)); int version = *(dynamic_cast <UtlInt*> (rowp->findValue(&SubscriptionDB::gVersionKey))); // Use SipSubscriptionMgr to update the in-memory data. // Construct a fake SUBSCRIBE request to carry most of the data // that updateDialogInfo needs. SipMessage subscribeRequest; subscribeRequest.setSubscribeData(urip->data(), fromp->data(), top->data(), callidp->data(), subcseq, eventtypep->data(), acceptp->data(), eventidp->data(), contactp->data(), routep->data(), expires - now); // Variables to hold the output of insertDialogInfo. UtlString subscribeDialogHandle; UtlBoolean isNew, isSubscriptionExpired; SipMessage subscribeResponse; UtlBoolean ret = SipSubscriptionMgr::insertDialogInfo(subscribeRequest, // *urip is the request-URI of // of the SUBSCRIBE. *urip, *eventtypep, subscribeDialogHandle, isNew, isSubscriptionExpired, subscribeResponse); if (!ret) { OsSysLog::add(FAC_SIP, PRI_ERR, "SipPersistentSubscriptionMgr:: " "insertDialogInfo failed urip = '%s', subscribeDialogHandle = '%s'", urip->data(), subscribeDialogHandle.data()); } else { // Set the next NOTIFY CSeq value. // (The data in IMDB has already been set.) SipSubscriptionMgr::setNextNotifyCSeq(subscribeDialogHandle, notifycseq, version); } } } }
// Do preliminary processing of message to log it, // clean up its data, and extract any needed source address. void SipClient::preprocessMessage(SipMessage& msg, const UtlString& msgText, int msgLength) { // Canonicalize short field names. msg.replaceShortFieldNames(); // Get the send address. UtlString fromIpAddress; int fromPort; msg.getSendAddress(&fromIpAddress, &fromPort); // Log the message. // Only bother processing if the logs are enabled if ( mpSipUserAgent->isMessageLoggingEnabled() || Os::Logger::instance().willLog(FAC_SIP_INCOMING, PRI_INFO) ) { UtlString logMessage; logMessage.append("Read SIP message:\n"); logMessage.append("----Local Host:"); logMessage.append(mLocalHostAddress); logMessage.append("---- Port: "); logMessage.appendNumber( portIsValid(mLocalHostPort) ? mLocalHostPort : defaultPort()); logMessage.append("----\n"); logMessage.append("----Remote Host:"); logMessage.append(fromIpAddress); logMessage.append("---- Port: "); logMessage.appendNumber( portIsValid(fromPort) ? fromPort : defaultPort()); logMessage.append("----\n"); logMessage.append(msgText.data(), msgLength); UtlString messageString; logMessage.append(messageString); logMessage.append("====================END====================\n"); // Send the message to the SipUserAgent for its internal log. mpSipUserAgent->logMessage(logMessage.data(), logMessage.length()); // Write the message to the syslog. Os::Logger::instance().log(FAC_SIP_INCOMING, PRI_INFO, "%s", logMessage.data()); } // Set the date field if not present long epochDate; if (!msg.getDateField(&epochDate)) { msg.setDateField(); } // Set the protocol and time. msg.setSendProtocol(mSocketType); msg.setTransportTime(touchedTime); // Keep track of where this message came from msg.setSendAddress(fromIpAddress.data(), fromPort); // Keep track of the interface on which this message was // received. msg.setInterfaceIpPort(mClientSocket->getLocalIp(), mClientSocket->getLocalHostPort()); if (mReceivedAddress.isNull()) { mReceivedAddress = fromIpAddress; mRemoteReceivedPort = fromPort; } // If this is a request... if (!msg.isResponse()) { UtlString lastAddress; int lastPort; UtlString lastProtocol; int receivedPort; UtlBoolean receivedSet; UtlBoolean maddrSet; UtlBoolean receivedPortSet; // Fill in 'received' and 'rport' in top Via if needed. msg.setReceivedViaParams(fromIpAddress, fromPort); // Derive information about the other end of the connection // from the Via that the other end provided. // Get the addresses from the topmost Via. msg.getTopVia(&lastAddress, &lastPort, &lastProtocol, &receivedPort, &receivedSet, &maddrSet, &receivedPortSet); // :QUERY: Should this test be mClientSocket->isConnected()? if ( ( mSocketType == OsSocket::TCP || mSocketType == OsSocket::SSL_SOCKET ) && !receivedPortSet ) { // we can use this socket as if it were // connected to the port specified in the // via field mRemoteReceivedPort = lastPort; } // Keep track of the address the other // side said they sent from. Note, this cannot // be trusted unless this transaction is // authenticated if (mRemoteViaAddress.isNull()) { mRemoteViaAddress = lastAddress; mRemoteViaPort = portIsValid(lastPort) ? lastPort : defaultPort(); } } // // Call all sip input processors // system_tap_sip_rx(fromIpAddress.data(), portIsValid(fromPort) ? fromPort : defaultPort(), mLocalHostAddress.data(), portIsValid(mLocalHostPort) ? mLocalHostPort : defaultPort(), msgText.data(), msgLength); mpSipUserAgent->executeAllSipInputProcessors(msg, fromIpAddress.data(), portIsValid(fromPort) ? fromPort : defaultPort()); }
// Thread execution code. int SipClient::run(void* runArg) { OsMsg* pMsg = NULL; OsStatus res; // Buffer to hold data read from the socket but not yet parsed // into incoming SIP messages. UtlString readBuffer; bool waitingToReportErr = FALSE; // controls whether to read-select on socket bool tcpOnErrWaitForSend = TRUE; int repeatedEOFs = 0; Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run start " "tcpOnErrWaitForSend-%d waitingToReportErr-%d mbTcpOnErrWaitForSend-%d repeatedEOFs-%d", mName.data(), tcpOnErrWaitForSend, waitingToReportErr, mbTcpOnErrWaitForSend, repeatedEOFs); // Wait structure: struct pollfd fds[2]; // Incoming message on the message queue (to be sent on the socket). fds[0].fd = mPipeReadingFd; // Socket ready to write (to continue sending message). // Socket ready to read (message to be received). do { assert(repeatedEOFs < 20); // The file descriptor for the socket may changemsg->getSendAddress(&fromIpAddress, &fromPort);, as OsSocket's // can be re-opened. fds[1].fd = mClientSocket->getSocketDescriptor(); // Initialize the revents members. // This may not be necessary (the man page is not clear), but // Valgrind flags 'fds[*].revents' as undefined if they aren't // initialized. fds[0].revents = 0; fds[1].revents = 0; fds[0].events = POLLIN; // only read-select on pipe // For non-blocking connect failures, don't read-select on socket if // the initial read showed an error but we have to wait to report it. if (!waitingToReportErr) { // This is the normal path. // Read the socket only if the socket is not shared. // If it is shared, the ancestral SipClient will read it. // If multiple threads attempt to read the socket, poll() may // succeed but another may read the data, leaving us to block on // read. fds[1].events = mbSharedSocket ? 0 : POLLIN; // Set wait for writing the socket if there is queued messages to // send. if (mWriteQueued) { // Wait for output on the socket to not block. fds[1].events |= POLLOUT; } } else { // just waiting to report error, ignore the socket fds[1].fd =-1; fds[1].events = 0; } // If there is residual data in the read buffer, // pretend the socket is ready to read. if (!readBuffer.isNull()) { fds[1].revents = POLLIN; } else { // Otherwise, call poll() to wait. int resPoll = poll(&fds[0], sizeof (fds) / sizeof (fds[0]), POLL_TIMEOUT); assert(resPoll >= 0 || (resPoll == -1 && errno == EINTR)); if (resPoll != 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run " "resPoll= %d revents: fd[0]= %x fd[1]= %x", mName.data(), resPoll, fds[0].revents, fds[1].revents ); } } if ((fds[1].revents & (POLLERR | POLLHUP)) != 0) { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run " "SipMessage::poll error(%d) ", mName.data(), errno); if (OsSocket::isFramed(mClientSocket->getIpProtocol())) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SipClient[%s]::run " "SipMessage::poll error(%d) got POLLERR | POLLHUP on UDP socket", mName.data(), errno); } else // eg. tcp socket // This client's socket is a connection-oriented protocol and the // connection has been terminated (probably by the remote end). // We must terminate the SipClient. // We drop the queued messages, but we do not report them to // SipUserAgent as failed sends. This will cause SipUserAgent to // retry the send using the same transport (rather than continuing // to the next transport), which should cause a new connection to // be made to the remote end. { // On non-blocking connect failures, we need to get the first send message // in order to successfully trigger the protocol fallback mechanism if (!tcpOnErrWaitForSend) { // Return all buffered messages with a transport error indication. emptyBuffer(TRUE); clientStopSelf(); } else { fds[1].revents &= ~(POLLERR | POLLHUP); // clear error bit if waiting waitingToReportErr = TRUE; } } } // Check for message queue messages (fds[0]) before checking the socket(fds[1]), // to make sure that we process shutdown messages promptly, even // if we would be spinning trying to service the socket. else if ((fds[0].revents & POLLIN) != 0) { // Poll finished because the pipe is ready to read. // (One byte in pipe means message available in queue.) // Only a SipClient with a derived SipClientWriteBuffer // uses the pipe in the Sip message send process // Check to see how many messages are in the queue. int numberMsgs = (getMessageQueue())->numMsgs(); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run got pipe-select " "Number of Messages waiting: %d", mName.data(), numberMsgs); int i; char buffer[1]; for (i = 0; i < numberMsgs; i++) { // Receive the messages. res = receiveMessage((OsMsg*&) pMsg, OsTime::NO_WAIT); assert(res == OS_SUCCESS); // Normally, this is a SIP message for the write buffer. Once we have gotten // here, we are able to report any initial non-blocking connect error. mbTcpOnErrWaitForSend = FALSE; tcpOnErrWaitForSend = FALSE; Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run got pipe-select " "mbTcpOnErrWaitForSend-%d waitingToReportErr-%d mbTcpOnErrWaitForSend-%d repeatedEOFs-%d", mName.data(), mbTcpOnErrWaitForSend, waitingToReportErr, mbTcpOnErrWaitForSend, repeatedEOFs); // Read 1 byte from the pipe to clear it for this message. One byte is // inserted into the pipe for each message. assert(read(mPipeReadingFd, &buffer, 1) == 1); if (!handleMessage(*pMsg)) // process the message (from queue) { OsServerTask::handleMessage(*pMsg); } if (!pMsg->getSentFromISR()) { pMsg->releaseMsg(); // free the message } // In order to report an unframed(eg TCP) socket error to SipUserAgent dispatcher, // the error must be carried in a sip message from the client's message queue. // The message holds all the identifying information. if (waitingToReportErr) { // Return all buffered messages with a transport error indication. emptyBuffer(TRUE); clientStopSelf(); } } } // end reading msg-available-for-output-queue pipe else if ((fds[1].revents & POLLOUT) != 0) { // Poll finished because socket is ready to write. // Call method to continue writing data. writeMore(); } else if ((fds[1].revents & POLLIN) != 0) { // Poll finished because socket is ready to read. // Read message. // Must allocate a new message because SipUserAgent::dispatch will // take ownership of it. SipMessage* msg = new SipMessage; int res = msg->read(mClientSocket, HTTP_DEFAULT_SOCKET_BUFFER_SIZE, &readBuffer); if (res >= 65536) { // // This is more than the allowable size of a SIP message. Discard! // UtlString remoteHostAddress; int remoteHostPort; msg->getSendAddress(&remoteHostAddress, &remoteHostPort); OS_LOG_WARNING(FAC_SIP, "Received a SIP Message (" << res << " bytes) beyond the maximum allowable size from host " << remoteHostAddress.data() << ":" << remoteHostPort); delete msg; readBuffer.remove(0); continue; } // Use readBuffer to hold any unparsed data after the message // we read. // Note that if a message was successfully parsed, readBuffer // still contains as its prefix the characters of that message. // We save them for logging purposes below and will delete them later. UtlString remoteHostAddress; int remoteHostPort; msg->getSendAddress(&remoteHostAddress, &remoteHostPort); if (!mClientSocket->isSameHost(remoteHostAddress.data(), mLocalHostAddress.data())) { try { if (!remoteHostAddress.isNull()) { boost::asio::ip::address remoteIp = boost::asio::ip::address::from_string(remoteHostAddress.data()); if (rateLimit().isBannedAddress(remoteIp)) { delete msg; readBuffer.remove(0); continue; } rateLimit().logPacket(remoteIp, 0); } } catch(const std::exception& e) { Os::Logger::instance().log(FAC_SIP_INCOMING, PRI_CRIT, "SipClient[%s]::run rate limit exception: %s", mName.data(), e.what()); } } // Note that input was processed at this time. touch(); // // Count the CR/LF to see if this is a keep-alive // int crlfCount = 0; for (int i = 0; i < res; i++) { if (readBuffer(i) == '\r' || readBuffer(i) == '\n') { crlfCount++; } else { break; } } if (res == crlfCount) { repeatedEOFs = 0; // The 'message' was a keepalive (CR-LF or CR-LF-CR-LF). UtlString fromIpAddress; int fromPort; UtlString buffer; int bufferLen; // send one CRLF set in the reply buffer.append("\r\n"); bufferLen = buffer.length(); // Get the send address for response. msg->getSendAddress(&fromIpAddress, &fromPort); if ( !portIsValid(fromPort)) { fromPort = defaultPort(); } // Log the message at DEBUG level. // Only bother processing if the logs are enabled if ( mpSipUserAgent->isMessageLoggingEnabled() || Os::Logger::instance().willLog(FAC_SIP_INCOMING, PRI_DEBUG) ) { UtlString logMessage; logMessage.append("Read keepalive message:\n"); logMessage.append("----Local Host:"); logMessage.append(mLocalHostAddress); logMessage.append("---- Port: "); logMessage.appendNumber( portIsValid(mLocalHostPort) ? mLocalHostPort : defaultPort()); logMessage.append("----\n"); logMessage.append("----Remote Host:"); logMessage.append(fromIpAddress); logMessage.append("---- Port: "); logMessage.appendNumber( portIsValid(fromPort) ? fromPort : defaultPort()); logMessage.append("----\n"); logMessage.append(readBuffer.data(), res); UtlString messageString; logMessage.append(messageString); logMessage.append("====================END====================\n"); // Don't bother to send the message to the SipUserAgent for its internal log. // Write the message to the syslog. Os::Logger::instance().log(FAC_SIP_INCOMING, PRI_DEBUG, "%s", logMessage.data()); } // send the CR-LF response message switch (mSocketType) { case OsSocket::TCP: { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run send TCP keep-alive CR-LF response, ", mName.data()); SipClientSendMsg sendMsg(OsMsg::OS_EVENT, SipClientSendMsg::SIP_CLIENT_SEND_KEEP_ALIVE, fromIpAddress, fromPort); handleMessage(sendMsg); // add newly created keep-alive to write buffer } break; case OsSocket::UDP: { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run send UDP keep-alive CR-LF response, ", mName.data()); (dynamic_cast <OsDatagramSocket*> (mClientSocket))->write(buffer.data(), bufferLen, fromIpAddress, fromPort); } break; default: break; } // Delete the SipMessage allocated above, which is no longer needed. delete msg; // Now that logging is done, remove the parsed bytes and // remember any unparsed input for later use. readBuffer.remove(0, res); } // end keep-alive msg else if (res > 0) // got message, but not keep-alive { // Message successfully read. repeatedEOFs = 0; // Do preliminary processing of message to log it, // clean up its data, and extract any needed source address. preprocessMessage(*msg, readBuffer, res); // Dispatch the message. // dispatch() takes ownership of *msg. mpSipUserAgent->dispatch(msg); // Now that logging is done, remove the parsed bytes and // remember any unparsed input for later use. readBuffer.remove(0, res); } // end process read of >0 bytes else { // Something went wrong while reading the message. // (Possibly EOF on a connection-oriented socket.) repeatedEOFs++; // Delete the SipMessage allocated above, which is no longer needed. delete msg; Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run SipMessage::read returns %d (error(%d) or EOF), " "readBuffer = '%.1000s'", mName.data(), res, errno, readBuffer.data()); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "SipClient[%s]::run error wait status " "tcpOnErrWaitForSend-%d waitingToReportErr-%d " "mbTcpOnErrWaitForSend-%d repeatedEOFs-%d " "protocol %d framed %d", mName.data(), tcpOnErrWaitForSend, waitingToReportErr, mbTcpOnErrWaitForSend, repeatedEOFs, mClientSocket->getIpProtocol(), OsSocket::isFramed(mClientSocket->getIpProtocol())); // If the socket is not framed (is connection-oriented), // we need to abort the connection and post a message // :TODO: This doesn't work right for framed connection-oriented // protocols (like SCTP), but OsSocket doesn't have an EOF-query // method -- we need to close all connection-oriented // sockets as well in case it was an EOF. // Define a virtual function that returns the correct bit. if (!OsSocket::isFramed(mClientSocket->getIpProtocol())) { // On non-blocking connect failures, we need to get the first send message // in order to successfully trigger the protocol fallback mechanism if (!tcpOnErrWaitForSend) { // Return all buffered messages with a transport error indication. emptyBuffer(TRUE); clientStopSelf(); } else { fds[1].revents &= ~(POLLERR | POLLHUP); // clear error bit if waiting waitingToReportErr = TRUE; } } // Delete the data read so far, which will not have been // deleted by HttpMessage::read. readBuffer.remove(0); } } // end POLLIN reading socket } while (isStarted()); return 0; // and then exit }
// Add to the HttpBody the current state of the resource. void ResourceCached::generateBody(UtlString& rlmi, HttpBody& body, UtlBoolean consolidated, const UtlString& nameXml, const UtlString& displayName) const { // Generate the preamble for the resource. rlmi += " <resource uri=\""; XmlEscape(rlmi, *(static_cast <const UtlString*> (this))); rlmi += "\">\r\n"; if (!nameXml.isNull()) { rlmi += " "; rlmi += nameXml; } if (consolidated) { // If consolidating resource instances, generate the XML for the // unified resource instance. rlmi += " <instance id=\"consolidated\" state=\"active\""; UtlString contentBodyPartCid; // Use the count of parts in 'body' to generate a unique identifier for // each part. contentBodyPartCid.appendNumber(body.getMultipartCount()); contentBodyPartCid += "@"; contentBodyPartCid += getResourceListServer()->getDomainName(); rlmi += " cid=\""; rlmi += contentBodyPartCid; rlmi += "\""; // Now add the <...> and use it in the header. contentBodyPartCid.prepend("<"); contentBodyPartCid.append(">"); // Create a single HttpBody to contain the unified dialog event. UtlString dialog_event; // XML declaration is optional, but Broadworks uses it. dialog_event += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; dialog_event += BEGIN_DIALOG_INFO; dialog_event += VERSION_EQUAL; dialog_event += "\""; // The consolidated dialog events need to have persistent // version numbers, as they all have the same instance id. // So we use a global version number in the ResourceListSet. dialog_event.appendNumber(getResourceListSet()->getVersion()); dialog_event += "\""; dialog_event += STATE_EQUAL; dialog_event += "\"full\""; dialog_event += ENTITY_EQUAL; dialog_event += "\""; dialog_event += *(static_cast <const UtlString*> (this)); dialog_event += "\">\r\n"; // Save the length of dialog_event, so we can tell later if // any <dialog>s have been added to it. unsigned int preamble_length = dialog_event.length(); // Call the ContactSet to generate the consolidated dialog event body. if (mContactSetP) { mContactSetP->generateBody(dialog_event, body, consolidated, displayName); } // If no <dialog>s have been added, we have to add a dummy // <dialog> to carry the display name. if (dialog_event.length() == preamble_length) { dialog_event += "<dialog id=\";\"><state>terminated</state><local><identity display=\""; XmlEscape(dialog_event, displayName); dialog_event += "\">"; XmlEscape(dialog_event, *(static_cast <const UtlString*> (this))); dialog_event += "</identity></local></dialog>\r\n"; } dialog_event += END_DIALOG_INFO; // Insert the consolidated dialog event body into the multiplart body. HttpBody content_body(dialog_event.data(), dialog_event.length(), DIALOG_EVENT_CONTENT_TYPE); UtlDList content_body_parameters; content_body_parameters.append( new NameValuePair(HTTP_CONTENT_ID_FIELD, contentBodyPartCid)); body.appendBodyPart(content_body, content_body_parameters); content_body_parameters.destroyAll(); // Finish the <instance> element. rlmi += "/>\r\n"; } else { // Call the ContactSet to do the work. if (mContactSetP) { mContactSetP->generateBody(rlmi, body, consolidated, displayName); } } // Generate the postamble for the resource. rlmi += " </resource>\r\n"; }
void HttpFileAccess::sendFile(const UtlString& path, const UtlString& peerName, const HttpRequestContext& requestContext, const HttpMessage& request, HttpMessage*& response ) { UtlString message; int file = open(path.data(), O_RDONLY); if (0 <= file) { UtlString fileBuffer; if (fileBuffer.capacity(MAX_FILE_CHUNK_SIZE + 1) >= MAX_FILE_CHUNK_SIZE + 1) { response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION_1_1, HTTP_OK_CODE, HTTP_OK_TEXT); response->useChunkedBody(true); // @TODO Annotate file resources with a mime type? // add a Content-Disposition header to suggest saving as the basename of the file response->setContentType("application/octet-stream"); UtlString dispositionValue; UtlString basename; ssize_t finalSlash = path.last('/'); if (UTL_NOT_FOUND != finalSlash) { basename.append(path, finalSlash+1, path.length()-finalSlash); } else { basename.append(path); // don't think this should ever be true.... } dispositionValue.append("attachment; filename=\""); dispositionValue.append(basename); dispositionValue.append("\""); response->setHeaderValue(HTTP_CONTENT_DISPOSITION_FIELD,dispositionValue.data()); bool writtenOk = response->writeHeaders(requestContext.socket()); ssize_t bytes; Int64 totalBytes = 0; Int64 chunks = 0; while ( writtenOk && (bytes = read(file, (void*)fileBuffer.data(), MAX_FILE_CHUNK_SIZE))) { fileBuffer.setLength(bytes); writtenOk = response->writeChunk(requestContext.socket(), fileBuffer); if (writtenOk) { totalBytes += bytes; chunks++; } OsSysLog::add(FAC_SUPERVISOR, PRI_DEBUG, "file block %"FORMAT_INTLL"d %zd %s", chunks, bytes, writtenOk ? "ok" : "failed"); } if (writtenOk) { response->writeEndOfChunks(requestContext.socket()); message.append(" sent file '"); message.append(path); message.append("' ("); message.appendNumber(totalBytes,"%"FORMAT_INTLL"d"); message.append(" bytes in "); message.appendNumber(chunks,"%"FORMAT_INTLL"d"); message.append(" chunks) to peer "); message.append(peerName); OsSysLog::add(FAC_SUPERVISOR, PRI_INFO, "HttpFileAccess::processRequest %s", message.data()); } else { message.append("error writing response after "); message.appendNumber(totalBytes,"%"FORMAT_INTLL"d"); message.append(" body bytes in "); message.appendNumber(chunks,"%"FORMAT_INTLL"d"); message.append(" chunks) to peer "); message.append(peerName); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest %s", message.data()); } /* * We've already written the response, so prevent HttpServer from sending it by * not passing it back. */ delete response; response = NULL; } else { // Send out-of-resources message message.append("Supervisor Buffer Exhausted"); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION, HTTP_OUT_OF_RESOURCES_CODE, message.data()); response->setContentType(CONTENT_TYPE_TEXT_PLAIN); response->setBody(new HttpBody(message.data(),message.length())); response->setContentLength(message.length()); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s", peerName.data(), message.data()); } } else { int httpStatusCode; UtlString httpStatusText; switch (errno) { case EACCES: httpStatusCode = HTTP_FORBIDDEN_CODE; httpStatusText = "File Access Denied"; break; case ENOENT: httpStatusCode = HTTP_FILE_NOT_FOUND_CODE; httpStatusText = HTTP_FILE_NOT_FOUND_TEXT; break; default: httpStatusCode = HTTP_SERVER_ERROR_CODE; httpStatusText = HTTP_SERVER_ERROR_TEXT; break; } message.append("File '"); message.append(path); message.append("' errno "); message.appendNumber(errno); message.append(" "); char errnoMsg[1024]; strerror_r(errno, errnoMsg, sizeof(errnoMsg)); message.append(errnoMsg); OsSysLog::add(FAC_SUPERVISOR, PRI_ERR, "HttpFileAccess::processRequest from %s %s", peerName.data(), message.data()); response->setResponseFirstHeaderLine(HTTP_PROTOCOL_VERSION, httpStatusCode, httpStatusText); response->setBody(new HttpBody(message.data(),message.length())); response->setContentType(CONTENT_TYPE_TEXT_PLAIN); response->setContentLength(message.length()); } }
void instantiateAllTestFixtures( UtlString resourceListFile, //< Name of the resource list to use for the test. The filename //< specified mus texist in .../sipXrls/src/test/rlsdata/ UtlString subscriptionDbName, //< Specifies the subscription DB to use for the test. The name //< is used to select which of the files in .../sipXrls/src/test/rlsdata/ //< will get used to preload the subscription IMDB. The name provided here is the //< xml file in .../sipXrls/src/test/rlsdata/ without the ".xml" extension. UtlString credentialDbName, //< Specifies the credential DB to use for the test. The name //< is used to select which of the files in .../sipXrls/src/test/rlsdata/ //< will get used to preload the credential IMDB. The name provided here is the //< xml file in .../sipXrls/src/test/rlsdata/ without the ".xml" extension. UtlString domianName, //< Specify the domain name to use for the test UtlBoolean routeAllRequestsToRlsServer //< Send all requests to either the RLS Server UA or the RLS Client UA //< The Client UA typically deals with the outgoing subscriptions and //< the Server UA deals witht the incoming subscriptions ) { mCredentialDbName = credentialDbName; // force copy of input files into the 'work' directory sipDbContext.inputFile( subscriptionDbName + ".xml" ); sipDbContext.inputFile( credentialDbName + ".xml" ); UtlString tempResourceListFile = UtlString(TEST_DATA_DIR) + "/" + resourceListFile; pSipUserAgent = new SipUserAgent( 45141, 45141, 45142 ,"127.0.0.1" // default publicAddress ,NULL // default defaultUser ,"127.0.0.1" ); // default defaultSipAddress // Stop SipUserAgent from rejecting all SUBSCRIBEs pSipUserAgent->allowMethod(SIP_SUBSCRIBE_METHOD, true); SipXecsService* rlsTestService = new SipXecsService("sipxrlsTest", "SIPX_RLS", "TestCase"); pResourceServerUnderTest = new ResourceListServer(rlsTestService, domianName, // domain "rlstest.test", // realm NULL, DIALOG_EVENT_TYPE, DIALOG_EVENT_CONTENT_TYPE, 45140, // TCP port 45140, // UDP port 45140, // TLS port "127.0.0.1", // Bind IP address &tempResourceListFile, (60 * 60), // Default subscription resubscribe interval. (40 * 60), // Default minimum subscription resubscribe interval. 250, // publishing delay? 20, // The maximum number of reg subscriptions per resource. 20, // The maximum number of contacts per reg subscription. 20, // The maximum number of resource instances per contact 20, // The maximum number of dialogs per resource instance 32, 3600, 86400, // Subscribe server expiration parameters subscriptionDbName, credentialDbName ); pUacOutputProcessor = new OutputProcessorFixture(); pUasOutputProcessor = new OutputProcessorFixture(); pResourceServerUnderTest->mClientUserAgent.addSipOutputProcessor( pUacOutputProcessor ); pResourceServerUnderTest->mServerUserAgent.addSipOutputProcessor( pUasOutputProcessor ); UtlString proxyAddress; if(routeAllRequestsToRlsServer) { proxyAddress = "127.0.0.1:45140"; } else { int pPort; pResourceServerUnderTest->mClientUserAgent.getLocalAddress(&proxyAddress, &pPort); proxyAddress.append(':'); proxyAddress.appendNumber(pPort); } pSipUserAgent->setProxyServers(proxyAddress.data()); pResourceServerUnderTest->start(); }
AuthPlugin::AuthResult CallerAlias::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 regarding modifying bool bSpiralingRequest, ///< spiraling indication UtlString& reason ///< rejection reason ) { // get the call-id to use in logging UtlString callId; request.getCallIdField(&callId); if ( (priorResult != DENY) // no point in modifying a request that won't be sent ) { UtlString callerFrom; UtlString callerFromTagOffsetStr; UtlString aliasFrom; UtlString aliasFromTagOffsetStr; UtlString originalFromTag; if ( !routeState.getParameter(mInstanceName.data(), CALLER_FROM_PARAM, callerFrom) || !routeState.getParameter(mInstanceName.data(), CALLER_TAG_OFFSET_PARAM, callerFromTagOffsetStr) || !routeState.getParameter(mInstanceName.data(), ALIAS_FROM_PARAM, aliasFrom) || !routeState.getParameter(mInstanceName.data(), ALIAS_TAG_OFFSET_PARAM, aliasFromTagOffsetStr) || !routeState.originalCallerFromTagValue(mInstanceName.data(), originalFromTag) ) { if ( routeState.isMutable() && routeState.directionIsCallerToCalled(mInstanceName.data()) ) // a new dialog? { /* * Get the callers identity by getting the caller URI and: * remove all parameters * remove the scheme name */ UtlString callerIdentity; UtlString originalFromField; request.getFromField(&originalFromField); Url originalFromUrl(originalFromField); /* * Extract the from identity as a key for the caller alias table * Start with the From header field (someday we should use the Identity if present) */ Url fromUrl(originalFromUrl); fromUrl.removeParameters(); // parameters are not relevant for this Url::Scheme fromUrlScheme = fromUrl.getScheme(); switch (fromUrlScheme) { case Url::SipsUrlScheme: // sips and sip are equivalent for identity purposes, // so just set to sip fromUrl.setScheme(Url::SipUrlScheme); // and fall through to extract the identity... case Url::SipUrlScheme: // case Url::TelUrlScheme: will go here, since 'tel' and 'sip' are the same length fromUrl.getUri(callerIdentity); callerIdentity.remove(0,4 /* strlen("sip:") */); // strip off the scheme name break; default: // for all other schemes, treat identity as null Os::Logger::instance().log(FAC_SIP, PRI_WARNING, "CallerAlias[%s]::check4andApplyAlias From uses unsupported scheme '%s'" " - using null identity", mInstanceName.data(), Url::schemeName(fromUrlScheme) ); break; } /* * Determine whether the identity is one for which this proxy * is authoritative; if not, we will not use wildcard matches. */ bool identityIsLocal = mpSipRouter->isLocalDomain(fromUrl); // now we have callerIdentity set; use for looking up each contact. Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "CallerAlias[%s]::check4andApplyAlias " "\n caller '%s' %s", mInstanceName.data(), callerIdentity.data(), identityIsLocal ? "is local" : "is not local" ); /* * Examine the request URI, * checking for a caller alias set for its domain(including asssociated gateway sipxecsLineid) with callerIdentity */ UtlString sipxecsLineIdField; requestUri.getUrlParameter(SIPX_SIPXECS_LINEID_URI_PARAM, sipxecsLineIdField); Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "getUrlParameter: sipxecsLineid[%s]" " in CallerAlias", sipxecsLineIdField.data() ); UtlString targetDomain; requestUri.getHostWithPort(targetDomain); if (!(sipxecsLineIdField.isNull())) { targetDomain.append(";").append(SIPX_SIPXECS_LINEID_URI_PARAM).append("=").append(sipxecsLineIdField.data()); } Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "CallerAlias::targetDomain [%s]", targetDomain.data() ); // look up any caller alias for this identity and contact domain UtlString callerAlias; if (identityIsLocal && getCallerAlias(callerIdentity, targetDomain, callerAlias) ) { // found a caller alias, so rewrite the From information /* * The From header requires special handling * - we need to preserve the tag, if any, from the original header */ originalFromUrl.getFieldParameter("tag", originalFromTag); Url newFromUrl(callerAlias.data()); newFromUrl.removeFieldParameter("tag"); // specifying a tag is a no-no if ( !originalFromTag.isNull() ) { newFromUrl.setFieldParameter("tag", originalFromTag.data()); } UtlString newFromFieldValue; newFromUrl.toString(newFromFieldValue); // log the change we are making before stripping the tag from the field values Os::Logger::instance().log( FAC_SIP, PRI_INFO, "CallerAlias[%s]::check4andApplyAlias call %s set caller alias\n" " Original-From: %s\n" " Aliased-From: %s", mInstanceName.data(), callId.data(), originalFromField.data(), newFromFieldValue.data() ); // rewrite the caller identity with the aliased value request.setRawFromField(newFromFieldValue.data()); // Factor the tag values out of the field values stored in the RouteState // We do this because otherwise we'll end up encoding and sending two copies // of the tag; since some phones send really long tag values (no one knows why), // this can cause such large Record-Route headers that they cause interop problems. if ( ! originalFromTag.isNull() ) { // find the offset of the tag value in the callers from field ssize_t callerFromTagOffset; callerFromTagOffset = originalFromField.index(originalFromTag); callerFromTagOffsetStr.appendNumber(callerFromTagOffset); // strip the tag value from the original From value to be stored in the RouteState originalFromField.replace(callerFromTagOffset, originalFromTag.length(), ""); // find the offset of the tag value in the aliased from field ssize_t aliasFromTagOffset; aliasFromTagOffset = newFromFieldValue.index(originalFromTag); aliasFromTagOffsetStr.appendNumber(aliasFromTagOffset); // strip the tag value from the aliased From value to be stored in the RouteState newFromFieldValue.replace(aliasFromTagOffset, originalFromTag.length(), ""); } // save the original and new values so that we can fix them later routeState.setParameter(mInstanceName.data(), CALLER_FROM_PARAM,originalFromField); routeState.setParameter(mInstanceName.data(), CALLER_TAG_OFFSET_PARAM,callerFromTagOffsetStr); routeState.setParameter(mInstanceName.data(), ALIAS_FROM_PARAM,newFromFieldValue); routeState.setParameter(mInstanceName.data(), ALIAS_TAG_OFFSET_PARAM,aliasFromTagOffsetStr); } else { Os::Logger::instance().log( FAC_SIP, PRI_DEBUG, "CallerAlias[%s]::check4andApplyAlias call %s found no alias", mInstanceName.data(), callId.data() ); } } else { Os::Logger::instance().log(FAC_SIP, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify " "not mutable - no rewrite", mInstanceName.data() ); } } else // the callerFrom and aliasFrom parameters were found { /* * This request has had its From rewritten, so fix either the From * or the To depending on which direction this request is going. */ if (!request.isResponse()) // can't modify responses, so don't bother { size_t tagOffset; if (routeState.directionIsCallerToCalled(mInstanceName.data())) { // replace the from tag value in the stored aliased header tagOffset = strtol(aliasFromTagOffsetStr.data(), NULL, 10); aliasFrom.insert(tagOffset, originalFromTag); // put the aliased header into the message request.setRawFromField(aliasFrom); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify " "call %s reset From", mInstanceName.data(), callId.data() ); } else // direction is Called to Caller { // replace the from tag value in the stored original header tagOffset = strtol(callerFromTagOffsetStr.data(), NULL, 10); callerFrom.insert(tagOffset, originalFromTag); request.setRawToField(callerFrom.data()); Os::Logger::instance().log(FAC_AUTH, PRI_DEBUG, "CallerAlias[%s]::authorizeAndModify " "call %s reset To", mInstanceName.data(), callId.data() ); } } } } return AuthPlugin::CONTINUE; }