#if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include "resip/stack/Auth.hxx" #include "resip/stack/BasicNonceHelper.hxx" #include "resip/stack/Helper.hxx" #include "resip/stack/NonceHelper.hxx" #include "rutil/Coders.hxx" #include "resip/stack/Uri.hxx" #include "rutil/Logger.hxx" #include "rutil/Random.hxx" #include "rutil/Timer.hxx" #include "rutil/DataStream.hxx" #include "rutil/MD5Stream.hxx" #include "rutil/DnsUtil.hxx" #include "rutil/compat.hxx" #include "rutil/ParseBuffer.hxx" #include "resip/stack/SipMessage.hxx" #include "resip/stack/Pkcs7Contents.hxx" #include "resip/stack/MultipartSignedContents.hxx" #include "resip/stack/MultipartMixedContents.hxx" #include "resip/stack/MultipartAlternativeContents.hxx" #include "rutil/WinLeakCheck.hxx" #ifdef USE_SSL #include "resip/stack/ssl/Security.hxx" #endif using namespace resip; using namespace std; #define RESIPROCATE_SUBSYSTEM Subsystem::SIP const int Helper::tagSize = 4; // !jf! this should be settable by the application in case a group of apps // (e.g. proxies) want to share the same secret Helper::NonceHelperPtr Helper::mNonceHelperPtr; void Helper::integer2hex(char* _d, unsigned int _s, bool _l) { int i; unsigned char j; int k = 0; char* s; _s = htonl(_s); s = (char*)&_s; for (i = 0; i < 4; i++) { j = (s[i] >> 4) & 0xf; if (j <= 9) { if(_l || j != 0 || k != 0) { _d[k++] = (j + '0'); } } else { _d[k++] = (j + 'a' - 10); } j = s[i] & 0xf; if (j <= 9) { if(_l || j != 0 || k != 0) { _d[k++] = (j + '0'); } } else { _d[k++] = (j + 'a' - 10); } } } unsigned int Helper::hex2integer(const char* _s) { unsigned int i, res = 0; for(i = 0; i < 8; i++) { if ((_s[i] >= '0') && (_s[i] <= '9')) { res *= 16; res += _s[i] - '0'; } else if ((_s[i] >= 'a') && (_s[i] <= 'f')) { res *= 16; res += _s[i] - 'a' + 10; } else if ((_s[i] >= 'A') && (_s[i] <= 'F')) { res *= 16; res += _s[i] - 'A' + 10; } else { return res; } } return res; } SipMessage* Helper::makeRequest(const NameAddr& target, const NameAddr& from, const NameAddr& contact, MethodTypes method) { std::unique_ptr request(new SipMessage); RequestLine rLine(method); rLine.uri() = target.uri(); request->header(h_To) = target; request->header(h_RequestLine) = rLine; request->header(h_MaxForwards).value() = 70; request->header(h_CSeq).method() = method; request->header(h_CSeq).sequence() = 1; request->header(h_From) = from; request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize); request->header(h_Contacts).push_back(contact); request->header(h_CallId).value() = Helper::computeCallId(); //request->header(h_ContentLength).value() = 0; Via via; request->header(h_Vias).push_back(via); return request.release(); } SipMessage* Helper::makeRequest(const NameAddr& target, const NameAddr& from, MethodTypes method) { NameAddr contact; return makeRequest(target, from, contact, method); } SipMessage* Helper::makeRegister(const NameAddr& to, const NameAddr& from) { NameAddr contact; return makeRegister(to, from, contact); } SipMessage* Helper::makeRegister(const NameAddr& to, const NameAddr& from, const NameAddr& contact) { std::unique_ptr request(new SipMessage); RequestLine rLine(REGISTER); rLine.uri().scheme() = to.uri().scheme(); rLine.uri().host() = to.uri().host(); rLine.uri().port() = to.uri().port(); if (to.uri().exists(p_transport)) { rLine.uri().param(p_transport) = to.uri().param(p_transport); } request->header(h_To) = to; request->header(h_RequestLine) = rLine; request->header(h_MaxForwards).value() = 70; request->header(h_CSeq).method() = REGISTER; request->header(h_CSeq).sequence() = 1; request->header(h_From) = from; request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize); request->header(h_CallId).value() = Helper::computeCallId(); assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty()); request->header(h_Contacts).push_back( contact ); Via via; request->header(h_Vias).push_back(via); return request.release(); } SipMessage* Helper::makeRegister(const NameAddr& to,const Data& transport) { NameAddr contact; return makeRegister(to, transport, contact); } SipMessage* Helper::makeRegister(const NameAddr& to, const Data& transport, const NameAddr& contact) { std::unique_ptr request(new SipMessage); RequestLine rLine(REGISTER); rLine.uri().scheme() = to.uri().scheme(); rLine.uri().host() = to.uri().host(); rLine.uri().port() = to.uri().port(); if (!transport.empty()) { rLine.uri().param(p_transport) = transport; } request->header(h_To) = to; request->header(h_RequestLine) = rLine; request->header(h_MaxForwards).value() = 70; request->header(h_CSeq).method() = REGISTER; request->header(h_CSeq).sequence() = 1; request->header(h_From) = to; request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize); request->header(h_CallId).value() = Helper::computeCallId(); assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty()); request->header(h_Contacts).push_back( contact ); Via via; request->header(h_Vias).push_back(via); return request.release(); } SipMessage* Helper::makePublish(const NameAddr& target, const NameAddr& from) { NameAddr contact; return makePublish(target, from, contact); } SipMessage* Helper::makePublish(const NameAddr& target, const NameAddr& from, const NameAddr& contact) { std::unique_ptr request(new SipMessage); RequestLine rLine(PUBLISH); rLine.uri() = target.uri(); request->header(h_To) = target; request->header(h_RequestLine) = rLine; request->header(h_MaxForwards).value() = 70; request->header(h_CSeq).method() = PUBLISH; request->header(h_CSeq).sequence() = 1; request->header(h_From) = from; request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize); request->header(h_CallId).value() = Helper::computeCallId(); assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty()); request->header(h_Contacts).push_back( contact ); Via via; request->header(h_Vias).push_back(via); return request.release(); } SipMessage* Helper::makeMessage(const NameAddr& target, const NameAddr& from) { NameAddr contact; return makeMessage(target, from, contact); } SipMessage* Helper::makeMessage(const NameAddr& target, const NameAddr& from, const NameAddr& contact) { std::unique_ptr request(new SipMessage); RequestLine rLine(MESSAGE); rLine.uri() = target.uri(); request->header(h_To) = target; request->header(h_RequestLine) = rLine; request->header(h_MaxForwards).value() = 70; request->header(h_CSeq).method() = MESSAGE; request->header(h_CSeq).sequence() = 1; request->header(h_From) = from; request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize); request->header(h_CallId).value() = Helper::computeCallId(); assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty()); request->header(h_Contacts).push_back( contact ); Via via; request->header(h_Vias).push_back(via); return request.release(); } SipMessage* Helper::makeSubscribe(const NameAddr& target, const NameAddr& from) { NameAddr contact; return makeSubscribe(target, from, contact); } SipMessage* Helper::makeSubscribe(const NameAddr& target, const NameAddr& from, const NameAddr& contact) { std::unique_ptr request(new SipMessage); RequestLine rLine(SUBSCRIBE); rLine.uri() = target.uri(); request->header(h_To) = target; request->header(h_RequestLine) = rLine; request->header(h_MaxForwards).value() = 70; request->header(h_CSeq).method() = SUBSCRIBE; request->header(h_CSeq).sequence() = 1; request->header(h_From) = from; request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize); request->header(h_CallId).value() = Helper::computeCallId(); assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty()); request->header(h_Contacts).push_front( contact ); Via via; request->header(h_Vias).push_front(via); return request.release(); } int Helper::jitterValue(int input, int lowerPercentage, int upperPercentage, int minimum) { assert(upperPercentage >= lowerPercentage); if (input < minimum) { return input; } else if (lowerPercentage == 100 && upperPercentage == 100) { return input; } else { const int rnd = Random::getRandom() % (upperPercentage-lowerPercentage) + lowerPercentage; return (input * rnd) / 100; } } SipMessage* Helper::makeInvite(const NameAddr& target, const NameAddr& from) { return Helper::makeRequest(target, from, INVITE); } SipMessage* Helper::makeInvite(const NameAddr& target, const NameAddr& from, const NameAddr& contact) { return Helper::makeRequest(target, from, contact, INVITE); } void Helper::makeResponse(SipMessage& response, const SipMessage& request, int responseCode, const NameAddr& myContact, const Data& reason, const Data& hostname, const Data& warning) { makeResponse(response,request, responseCode, reason,hostname, warning); // in general, this should not create a Contact header since only requests // that create a dialog (or REGISTER requests) should produce a response with // a contact(s). response.header(h_Contacts).clear(); response.header(h_Contacts).push_back(myContact); } void Helper::makeResponse(SipMessage& response, const SipMessage& request, int responseCode, const Data& reason, const Data& hostname, const Data& warning) { DebugLog(<< "Helper::makeResponse(" << request.brief() << " code=" << responseCode << " reason=" << reason); response.header(h_StatusLine).responseCode() = responseCode; response.header(h_From) = request.header(h_From); response.header(h_To) = request.header(h_To); response.header(h_CallId) = request.header(h_CallId); response.header(h_CSeq) = request.header(h_CSeq); response.header(h_Vias) = request.header(h_Vias); if (!warning.empty()) { WarningCategory warn; warn.code() = 399; warn.hostname() = hostname; warn.text() = warning; response.header(h_Warnings).push_back(warn); } if(responseCode > 100 && response.const_header(h_To).isWellFormed() && !response.const_header(h_To).exists(p_tag)) { // Only generate a To: tag if one doesn't exist. Think Re-INVITE. // No totag for failure responses or 100s // ?bwc? Should we be generating to-tags for failure responses or not? // The comments say no, but the code says yes. Which is it? response.header(h_To).param(p_tag) = Helper::computeTag(Helper::tagSize); } // .bwc. This will only throw if the topmost Via is malformed, and that // should have been caught at the transport level. response.setRFC2543TransactionId(request.getRFC2543TransactionId()); //response.header(h_ContentLength).value() = 0; if (responseCode >= 180 && responseCode < 300 && request.exists(h_RecordRoutes)) { response.header(h_RecordRoutes) = request.header(h_RecordRoutes); } // .bwc. If CSeq is malformed, basicCheck would have already attempted to // parse it, meaning we won't throw here (we never try to parse the same // thing twice, see LazyParser::checkParsed()) if (responseCode/100 == 2 && !response.exists(h_Contacts) && !(response.const_header(h_CSeq).method()==CANCEL) ) { // in general, this should not create a Contact header since only requests // that create a dialog (or REGISTER requests) should produce a response with // a contact(s). NameAddr contact; response.header(h_Contacts).push_back(contact); } if (request.isExternal()) { response.setFromTU(); } else { response.setFromExternal(); } if (reason.size()) { response.header(h_StatusLine).reason() = reason; } else { getResponseCodeReason(responseCode, response.header(h_StatusLine).reason()); } } SipMessage* Helper::makeResponse(const SipMessage& request, int responseCode, const NameAddr& myContact, const Data& reason, const Data& hostname, const Data& warning) { // .bwc. Exception safety. Catch/rethrow is dicey because we can't rethrow // resip::BaseException, since it is abstract. std::unique_ptr response(new SipMessage); makeResponse(*response, request, responseCode, reason, hostname, warning); // in general, this should not create a Contact header since only requests // that create a dialog (or REGISTER requests) should produce a response with // a contact(s). response->header(h_Contacts).clear(); response->header(h_Contacts).push_back(myContact); return response.release(); } SipMessage* Helper::makeResponse(const SipMessage& request, int responseCode, const Data& reason, const Data& hostname, const Data& warning) { // .bwc. Exception safety. Catch/rethrow is dicey because we can't rethrow // resip::BaseException, since it is abstract. std::unique_ptr response(new SipMessage); makeResponse(*response, request, responseCode, reason, hostname, warning); return response.release(); } void Helper::makeRawResponse(Data& raw, const SipMessage& msg, int responseCode, const Data& additionalHeaders, const Data& body) { raw.reserve(256); { DataStream encodeStream(raw); encodeStream << "SIP/2.0 " << responseCode << " "; Data reason; getResponseCodeReason(responseCode, reason); encodeStream << reason; msg.encodeSingleHeader(Headers::Via,encodeStream); msg.encodeSingleHeader(Headers::To,encodeStream); msg.encodeSingleHeader(Headers::From,encodeStream); msg.encodeSingleHeader(Headers::CallID,encodeStream); msg.encodeSingleHeader(Headers::CSeq,encodeStream); encodeStream << additionalHeaders; encodeStream << "Content-Length: " << body.size() << "\r\n\r\n"; } } void Helper::getResponseCodeReason(int responseCode, Data& reason) { switch (responseCode) { case 100: reason = "Trying"; break; case 180: reason = "Ringing"; break; case 181: reason = "Call Is Being Forwarded"; break; case 182: reason = "Queued"; break; case 183: reason = "Session Progress"; break; case 200: reason = "OK"; break; case 202: reason = "Accepted"; break; case 300: reason = "Multiple Choices"; break; case 301: reason = "Moved Permanently"; break; case 302: reason = "Moved Temporarily"; break; case 305: reason = "Use Proxy"; break; case 380: reason = "Alternative Service"; break; case 400: reason = "Bad Request"; break; case 401: reason = "Unauthorized"; break; case 402: reason = "Payment Required"; break; case 403: reason = "Forbidden"; break; case 404: reason = "Not Found"; break; case 405: reason = "Method Not Allowed"; break; case 406: reason = "Not Acceptable"; break; case 407: reason = "Proxy Authentication Required"; break; case 408: reason = "Request Timeout"; break; case 410: reason = "Gone"; break; case 412: reason = "Precondition Failed"; break; case 413: reason = "Request Entity Too Large"; break; case 414: reason = "Request-URI Too Long"; break; case 415: reason = "Unsupported Media Type"; break; case 416: reason = "Unsupported URI Scheme"; break; case 420: reason = "Bad Extension"; break; case 421: reason = "Extension Required"; break; case 422: reason = "Session Interval Too Small"; break; case 423: reason = "Interval Too Brief"; break; case 430: reason = "Flow failed"; break; case 439: reason = "First Hop Lacks Outbound Support"; break; case 480: reason = "Temporarily Unavailable"; break; case 481: reason = "Call/Transaction Does Not Exist"; break; case 482: reason = "Loop Detected"; break; case 483: reason = "Too Many Hops"; break; case 484: reason = "Address Incomplete"; break; case 485: reason = "Ambiguous"; break; case 486: reason = "Busy Here"; break; case 487: reason = "Request Terminated"; break; case 488: reason = "Not Acceptable Here"; break; case 489: reason = "Event Package Not Supported"; break; case 491: reason = "Request Pending"; break; case 493: reason = "Undecipherable"; break; case 500: reason = "Server Internal Error"; break; case 501: reason = "Not Implemented"; break; case 502: reason = "Bad Gateway"; break; case 503: reason = "Service Unavailable"; break; case 504: reason = "Server Time-out"; break; case 505: reason = "Version Not Supported"; break; case 513: reason = "Message Too Large"; break; case 600: reason = "Busy Everywhere"; break; case 603: reason = "Decline"; break; case 604: reason = "Does Not Exist Anywhere"; break; case 606: reason = "Not Acceptable"; break; } } SipMessage* Helper::makeCancel(const SipMessage& request) { assert(request.isRequest()); assert(request.header(h_RequestLine).getMethod() == INVITE); std::unique_ptr cancel(new SipMessage); RequestLine rLine(CANCEL, request.header(h_RequestLine).getSipVersion()); rLine.uri() = request.header(h_RequestLine).uri(); cancel->header(h_RequestLine) = rLine; cancel->header(h_MaxForwards).value() = 70; cancel->header(h_To) = request.header(h_To); cancel->header(h_From) = request.header(h_From); cancel->header(h_CallId) = request.header(h_CallId); if (request.exists(h_ProxyAuthorizations)) { cancel->header(h_ProxyAuthorizations) = request.header(h_ProxyAuthorizations); } if (request.exists(h_Authorizations)) { cancel->header(h_Authorizations) = request.header(h_Authorizations); } if (request.exists(h_Routes)) { cancel->header(h_Routes) = request.header(h_Routes); } cancel->header(h_CSeq) = request.header(h_CSeq); cancel->header(h_CSeq).method() = CANCEL; cancel->header(h_Vias).push_back(request.header(h_Vias).front()); return cancel.release(); } SipMessage* Helper::makeFailureAck(const SipMessage& request, const SipMessage& response) { assert (request.header(h_Vias).size() >= 1); assert (request.header(h_RequestLine).getMethod() == INVITE); std::unique_ptr ack(new SipMessage); RequestLine rLine(ACK, request.header(h_RequestLine).getSipVersion()); rLine.uri() = request.header(h_RequestLine).uri(); ack->header(h_RequestLine) = rLine; ack->header(h_MaxForwards).value() = 70; ack->header(h_CallId) = request.header(h_CallId); ack->header(h_From) = request.header(h_From); ack->header(h_To) = response.header(h_To); // to get to-tag ack->header(h_Vias).push_back(request.header(h_Vias).front()); ack->header(h_CSeq) = request.header(h_CSeq); ack->header(h_CSeq).method() = ACK; if (request.exists(h_Routes)) { ack->header(h_Routes) = request.header(h_Routes); } return ack.release(); } static const Data cookie("z9hG4bK"); // magic cookie per rfc3261 Data Helper::computeUniqueBranch() { Data result(16, Data::Preallocate); result += cookie; result += Random::getRandomHex(4); result += "C1"; result += Random::getRandomHex(2); return result; } // It is overwritten by Dmytro; because doing getLocalHostName() during initialization is not best idea. static Data localhostname = "localhost"; //DnsUtil::getLocalHostName(); Data Helper::computeCallId() { Data hostAndSalt(localhostname + Random::getRandomHex(16)); #ifndef USE_SSL // .bwc. None of this is neccessary if we're using openssl #if defined(__linux__) || defined(__APPLE__) pid_t pid = getpid(); hostAndSalt.append((char*)&pid,sizeof(pid)); #endif #ifdef __APPLE__ pthread_t thread = pthread_self(); hostAndSalt.append((char*)&thread,sizeof(thread)); #endif #ifdef WIN32 DWORD proccessId = ::GetCurrentProcessId(); DWORD threadId = ::GetCurrentThreadId(); hostAndSalt.append((char*)&proccessId,sizeof(proccessId)); hostAndSalt.append((char*)&threadId,sizeof(threadId)); #endif #endif // of USE_SSL return hostAndSalt.md5(Data::BASE64); } Data Helper::computeTag(int numBytes) { return Random::getRandomHex(numBytes); } void Helper::setNonceHelper(NonceHelper *nonceHelper) { mNonceHelperPtr.mNonceHelper = nonceHelper; } NonceHelper* Helper::getNonceHelper() { if (mNonceHelperPtr.mNonceHelper == 0) { mNonceHelperPtr.mNonceHelper = new BasicNonceHelper(); } return mNonceHelperPtr.mNonceHelper; } Data Helper::makeNonce(const SipMessage& request, const Data& timestamp) { return getNonceHelper()->makeNonce(request, timestamp); } static Data noBody = MD5Stream().getHex(); Data Helper::makeResponseMD5WithA1(const Data& a1, const Data& method, const Data& digestUri, const Data& nonce, const Data& qop, const Data& cnonce, const Data& cnonceCount, const Contents* entityBody) { MD5Stream a2; a2 << method << Symbols::COLON << digestUri; if (qop == Symbols::authInt) { if (entityBody) { MD5Stream eStream; eStream << *entityBody; a2 << Symbols::COLON << eStream.getHex(); } else { a2 << Symbols::COLON << noBody; } } MD5Stream r; r << a1 << Symbols::COLON << nonce << Symbols::COLON; if (!qop.empty()) { r << cnonceCount << Symbols::COLON << cnonce << Symbols::COLON << qop << Symbols::COLON; } r << a2.getHex(); return r.getHex(); } //RFC 2617 3.2.2.1 Data Helper::makeResponseMD5(const Data& username, const Data& password, const Data& realm, const Data& method, const Data& digestUri, const Data& nonce, const Data& qop, const Data& cnonce, const Data& cnonceCount, const Contents *entity) { MD5Stream a1; a1 << username << Symbols::COLON << realm << Symbols::COLON << password; return makeResponseMD5WithA1(a1.getHex(), method, digestUri, nonce, qop, cnonce, cnonceCount, entity); } static Data digest("digest"); std::pair Helper::advancedAuthenticateRequest(const SipMessage& request, const Data& realm, const Data& a1, int expiresDelta, bool proxyAuthorization) { Data username; DebugLog(<< "Authenticating: realm=" << realm << " expires=" << expiresDelta); //DebugLog(<< request); const ParserContainer* auths = 0; if(proxyAuthorization) { if(request.exists(h_ProxyAuthorizations)) { auths = &request.header(h_ProxyAuthorizations); } } else { if(request.exists(h_Authorizations)) { auths = &request.header(h_Authorizations); } } if (auths) { for (ParserContainer::const_iterator i = auths->begin(); i != auths->end(); i++) { if (i->exists(p_realm) && i->exists(p_nonce) && i->exists(p_response) && i->param(p_realm) == realm) { if(!isEqualNoCase(i->scheme(),digest)) { DebugLog(<< "Scheme must be Digest"); continue; } /* ParseBuffer pb(i->param(p_nonce).data(), i->param(p_nonce).size()); if (!pb.eof() && !isdigit(*pb.position())) { DebugLog(<< "Invalid nonce; expected timestamp."); return make_pair(BadlyFormed,username); } const char* anchor = pb.position(); pb.skipToChar(Symbols::COLON[0]); if (pb.eof()) { DebugLog(<< "Invalid nonce; expected timestamp terminator."); return make_pair(BadlyFormed,username); } Data then; pb.data(then, anchor); if (expiresDelta > 0) { unsigned int now = (unsigned int)(Timer::getTimeMs()/1000); if ((unsigned int)then.convertUInt64() + expiresDelta < now) { DebugLog(<< "Nonce has expired."); return make_pair(Expired,username); } } */ NonceHelper::Nonce x_nonce = getNonceHelper()->parseNonce(i->param(p_nonce)); if(x_nonce.getCreationTime() == 0) return make_pair(BadlyFormed,username); if (expiresDelta > 0) { UInt64 now = Timer::getTimeSecs(); if (x_nonce.getCreationTime() + expiresDelta < now) { DebugLog(<< "Nonce has expired."); return make_pair(Expired,username); } } Data then(x_nonce.getCreationTime()); if (i->param(p_nonce) != makeNonce(request, then)) { InfoLog(<< "Not my nonce. expected=" << makeNonce(request, then) << " received=" << i->param(p_nonce) << " then=" << then); return make_pair(BadlyFormed,username); } if (i->exists(p_qop)) { if (i->param(p_qop) == Symbols::auth || i->param(p_qop) == Symbols::authInt) { if(i->exists(p_uri) && i->exists(p_cnonce) && i->exists(p_nc)) { if (i->param(p_response) == makeResponseMD5WithA1(a1, getMethodName(request.header(h_RequestLine).getMethod()), i->param(p_uri), i->param(p_nonce), i->param(p_qop), i->param(p_cnonce), i->param(p_nc), request.getContents())) { if(i->exists(p_username)) { username = i->param(p_username); } return make_pair(Authenticated,username); } else { return make_pair(Failed,username); } } } else { InfoLog (<< "Unsupported qop=" << i->param(p_qop)); return make_pair(Failed,username); } } else if(i->exists(p_uri)) { if (i->param(p_response) == makeResponseMD5WithA1(a1, getMethodName(request.header(h_RequestLine).getMethod()), i->param(p_uri), i->param(p_nonce))) { if(i->exists(p_username)) { username = i->param(p_username); } return make_pair(Authenticated,username); } else { return make_pair(Failed,username); } } } else { return make_pair(BadlyFormed,username); } } return make_pair(BadlyFormed,username); } DebugLog (<< "No authentication headers. Failing request."); return make_pair(Failed,username); } Helper::AuthResult Helper::authenticateRequest(const SipMessage& request, const Data& realm, const Data& password, int expiresDelta) { DebugLog(<< "Authenticating: realm=" << realm << " expires=" << expiresDelta); //DebugLog(<< request); // !bwc! Somewhat inefficient. Maybe optimize later. ParserContainer auths; if(request.exists(h_ProxyAuthorizations)) { auths.append(request.header(h_ProxyAuthorizations)); } if(request.exists(h_Authorizations)) { auths.append(request.header(h_Authorizations)); } if(auths.empty()) { DebugLog (<< "No authentication headers. Failing request."); return Failed; } // ?bwc? Why is const_iterator& operator=(const iterator& rhs) // not working properly? //ParserContainer::const_iterator i = auths.begin(); ParserContainer::iterator i = auths.begin(); for (; i != auths.end(); i++) { if (i->exists(p_realm) && i->exists(p_nonce) && i->exists(p_response) && i->param(p_realm) == realm) { if(!isEqualNoCase(i->scheme(),digest)) { DebugLog(<< "Scheme must be Digest"); continue; } /* ParseBuffer pb(i->param(p_nonce).data(), i->param(p_nonce).size()); if (!pb.eof() && !isdigit(*pb.position())) { DebugLog(<< "Invalid nonce; expected timestamp."); return BadlyFormed; } const char* anchor = pb.position(); pb.skipToChar(Symbols::COLON[0]); if (pb.eof()) { DebugLog(<< "Invalid nonce; expected timestamp terminator."); return BadlyFormed; } Data then; pb.data(then, anchor); */ NonceHelper::Nonce x_nonce = getNonceHelper()->parseNonce(i->param(p_nonce)); if(x_nonce.getCreationTime() == 0) return BadlyFormed; if (expiresDelta > 0) { UInt64 now = Timer::getTimeSecs(); if (x_nonce.getCreationTime() + expiresDelta < now) { DebugLog(<< "Nonce has expired."); return Expired; } } Data then(x_nonce.getCreationTime()); if (i->param(p_nonce) != makeNonce(request, then)) { InfoLog(<< "Not my nonce."); return Failed; } InfoLog (<< " username=" << (i->param(p_username)) << " password=" << password << " realm=" << realm << " method=" << getMethodName(request.header(h_RequestLine).getMethod()) << " uri=" << i->param(p_uri) << " nonce=" << i->param(p_nonce)); if (i->exists(p_qop)) { if (i->param(p_qop) == Symbols::auth || i->param(p_qop) == Symbols::authInt) { if(i->exists(p_uri) && i->exists(p_cnonce) && i->exists(p_nc)) { if (i->param(p_response) == makeResponseMD5(i->param(p_username), password, realm, getMethodName(request.header(h_RequestLine).getMethod()), i->param(p_uri), i->param(p_nonce), i->param(p_qop), i->param(p_cnonce), i->param(p_nc)), request.getContents()) { return Authenticated; } else { return Failed; } } } else { InfoLog (<< "Unsupported qop=" << i->param(p_qop)); return Failed; } } else if(i->exists(p_uri)) { if (i->param(p_response) == makeResponseMD5(i->param(p_username), password, realm, getMethodName(request.header(h_RequestLine).getMethod()), i->param(p_uri), i->param(p_nonce))) { return Authenticated; } else { return Failed; } } } else { return BadlyFormed; } } return BadlyFormed; } Helper::AuthResult Helper::authenticateRequestWithA1(const SipMessage& request, const Data& realm, const Data& hA1, int expiresDelta) { DebugLog(<< "Authenticating with HA1: realm=" << realm << " expires=" << expiresDelta); //DebugLog(<< request); // !bwc! Somewhat inefficient. Maybe optimize later. ParserContainer auths; if(request.exists(h_ProxyAuthorizations)) { auths.append(request.header(h_ProxyAuthorizations)); } if(request.exists(h_Authorizations)) { auths.append(request.header(h_Authorizations)); } if(auths.empty()) { DebugLog (<< "No authentication headers. Failing request."); return Failed; } // ?bwc? Why is const_iterator& operator=(const iterator& rhs) // not working properly? //ParserContainer::const_iterator i = auths.begin(); ParserContainer::iterator i = auths.begin(); for (;i != auths.end(); i++) { if (i->exists(p_realm) && i->exists(p_nonce) && i->exists(p_response) && i->param(p_realm) == realm) { if(!isEqualNoCase(i->scheme(),digest)) { DebugLog(<< "Scheme must be Digest"); continue; } /* ParseBuffer pb(i->param(p_nonce).data(), i->param(p_nonce).size()); if (!pb.eof() && !isdigit(*pb.position())) { DebugLog(<< "Invalid nonce; expected timestamp."); return BadlyFormed; } const char* anchor = pb.position(); pb.skipToChar(Symbols::COLON[0]); if (pb.eof()) { DebugLog(<< "Invalid nonce; expected timestamp terminator."); return BadlyFormed; } Data then; pb.data(then, anchor); */ NonceHelper::Nonce x_nonce = getNonceHelper()->parseNonce(i->param(p_nonce)); if(x_nonce.getCreationTime() == 0) return BadlyFormed; if (expiresDelta > 0) { UInt64 now = Timer::getTimeSecs(); if (x_nonce.getCreationTime() + expiresDelta < now) { DebugLog(<< "Nonce has expired."); return Expired; } } Data then(x_nonce.getCreationTime()); if (i->param(p_nonce) != makeNonce(request, then)) { InfoLog(<< "Not my nonce."); return Failed; } InfoLog (<< " username=" << (i->param(p_username)) << " H(A1)=" << hA1 << " realm=" << realm << " method=" << getMethodName(request.header(h_RequestLine).getMethod()) << " uri=" << i->param(p_uri) << " nonce=" << i->param(p_nonce)); if (i->exists(p_qop)) { if (i->param(p_qop) == Symbols::auth || i->param(p_qop) == Symbols::authInt) { if(i->exists(p_uri) && i->exists(p_cnonce) && i->exists(p_nc)) { if (i->param(p_response) == makeResponseMD5WithA1(hA1, getMethodName(request.header(h_RequestLine).getMethod()), i->param(p_uri), i->param(p_nonce), i->param(p_qop), i->param(p_cnonce), i->param(p_nc), request.getContents())) { return Authenticated; } else { return Failed; } } } else { InfoLog (<< "Unsupported qop=" << i->param(p_qop)); return Failed; } } else if(i->exists(p_uri)) { if (i->param(p_response) == makeResponseMD5WithA1(hA1, getMethodName(request.header(h_RequestLine).getMethod()), i->param(p_uri), i->param(p_nonce))) { return Authenticated; } else { return Failed; } } } else { return BadlyFormed; } } return BadlyFormed; } SipMessage* Helper::make405(const SipMessage& request, const int* allowedMethods, int len ) { SipMessage* resp = Helper::makeResponse(request, 405); if (len < 0) { int upperBound = static_cast(MAX_METHODS); // The UNKNOWN method name is the first in the enum for (int i = 1 ; i < upperBound; i ++) { int last = 0; // ENUMS must be contiguous in order for this to work. assert( i - last <= 1); Token t; t.value() = getMethodName(static_cast(i)); resp->header(h_Allows).push_back(t); last = i; } } else { // use user's list for ( int i = 0 ; i < len ; i++) { Token t; t.value() = getMethodName(static_cast(allowedMethods[i])); resp->header(h_Allows).push_back(t); } } return resp; } SipMessage* Helper::makeProxyChallenge(const SipMessage& request, const Data& realm, bool useAuth, bool stale) { return makeChallenge(request,realm,useAuth,stale,true); } SipMessage* Helper::makeWWWChallenge(const SipMessage& request, const Data& realm, bool useAuth, bool stale) { return makeChallenge(request,realm,useAuth,stale,false); } SipMessage* Helper::makeChallenge(const SipMessage& request, const Data& realm, bool useAuth, bool stale, bool proxy) { Auth auth; auth.scheme() = Symbols::Digest; Data timestamp(Timer::getTimeSecs()); auth.param(p_nonce) = makeNonce(request, timestamp); auth.param(p_algorithm) = "MD5"; auth.param(p_realm) = realm; if (useAuth) { auth.param(p_qopOptions) = "auth,auth-int"; } if (stale) { auth.param(p_stale) = "true"; } SipMessage *response; if(proxy) { response = Helper::makeResponse(request, 407); response->header(h_ProxyAuthenticates).push_back(auth); } else { response = Helper::makeResponse(request, 401); response->header(h_WWWAuthenticates).push_back(auth); } return response; } void Helper::updateNonceCount(unsigned int& nonceCount, Data& nonceCountString) { if (!nonceCountString.empty()) { return; } nonceCount++; { //DataStream s(nonceCountString); //s << std::setw(8) << std::setfill('0') << std::hex << nonceCount; char buf[128]; *buf = 0; #if (defined(_MSC_VER) && _MSC_VER >= 1400) sprintf_s(buf,128,"%08x",nonceCount); #else sprintf(buf,"%08x",nonceCount); #endif nonceCountString = buf; } DebugLog(<< "nonceCount is now: [" << nonceCountString << "]"); } Auth Helper::makeChallengeResponseAuth(const SipMessage& request, const Data& username, const Data& password, const Auth& challenge, const Data& cnonce, unsigned int& nonceCount, Data& nonceCountString) { Auth auth; Data authQop = qopOption(challenge); if(!authQop.empty()) { updateNonceCount(nonceCount, nonceCountString); } makeChallengeResponseAuth(request, username, password, challenge, cnonce, authQop, nonceCountString, auth); return auth; } void Helper::makeChallengeResponseAuth(const SipMessage& request, const Data& username, const Data& password, const Auth& challenge, const Data& cnonce, const Data& authQop, const Data& nonceCountString, Auth& auth) { auth.scheme() = Symbols::Digest; auth.param(p_username) = username; assert(challenge.exists(p_realm)); auth.param(p_realm) = challenge.param(p_realm); assert(challenge.exists(p_nonce)); auth.param(p_nonce) = challenge.param(p_nonce); Data digestUri; { DataStream s(digestUri); //s << request.header(h_RequestLine).uri().host(); // wrong s << request.header(h_RequestLine).uri(); // right } auth.param(p_uri) = digestUri; if (!authQop.empty()) { auth.param(p_response) = Helper::makeResponseMD5(username, password, challenge.param(p_realm), getMethodName(request.header(h_RequestLine).getMethod()), digestUri, challenge.param(p_nonce), authQop, cnonce, nonceCountString, request.getContents()); auth.param(p_cnonce) = cnonce; auth.param(p_nc) = nonceCountString; auth.param(p_qop) = authQop; } else { assert(challenge.exists(p_realm)); auth.param(p_response) = Helper::makeResponseMD5(username, password, challenge.param(p_realm), getMethodName(request.header(h_RequestLine).getMethod()), digestUri, challenge.param(p_nonce)); } if (challenge.exists(p_algorithm)) { auth.param(p_algorithm) = challenge.param(p_algorithm); } else { auth.param(p_algorithm) = "MD5"; } if (challenge.exists(p_opaque) && challenge.param(p_opaque).size() > 0) { auth.param(p_opaque) = challenge.param(p_opaque); } } // priority-order list of preferred qop tokens static Data preferredTokens[] = { "auth-int", "auth" }; static size_t pTokenSize=sizeof(preferredTokens)/sizeof(*preferredTokens); Data Helper::qopOption(const Auth& challenge) { bool found = false; size_t index = pTokenSize; if (challenge.exists(p_qopOptions) && !challenge.param(p_qopOptions).empty()) { ParseBuffer pb(challenge.param(p_qopOptions).data(), challenge.param(p_qopOptions).size()); do { const char* anchor = pb.skipWhitespace(); pb.skipToChar(Symbols::COMMA[0]); Data q; pb.data(q, anchor); if (!pb.eof()) pb.skipChar(); for (size_t i=0; i < pTokenSize; i++) { if (q == preferredTokens[i]) { // found a preferred token; is it higher priority? if (i < index) { found = true; index = i; } } } } while(!pb.eof()); } if (found) return preferredTokens[index]; return Data::Empty; } Auth Helper::makeChallengeResponseAuthWithA1(const SipMessage& request, const Data& username, const Data& passwordHashA1, const Auth& challenge, const Data& cnonce, unsigned int& nonceCount, Data& nonceCountString) { Auth auth; Data authQop = qopOption(challenge); if(!authQop.empty()) { updateNonceCount(nonceCount, nonceCountString); } makeChallengeResponseAuthWithA1(request, username, passwordHashA1, challenge, cnonce, authQop, nonceCountString, auth); return auth; } void Helper::makeChallengeResponseAuthWithA1(const SipMessage& request, const Data& username, const Data& passwordHashA1, const Auth& challenge, const Data& cnonce, const Data& authQop, const Data& nonceCountString, Auth& auth) { auth.scheme() = Symbols::Digest; auth.param(p_username) = username; assert(challenge.exists(p_realm)); auth.param(p_realm) = challenge.param(p_realm); assert(challenge.exists(p_nonce)); auth.param(p_nonce) = challenge.param(p_nonce); Data digestUri; { DataStream s(digestUri); //s << request.const_header(h_RequestLine).uri().host(); // wrong s << request.const_header(h_RequestLine).uri(); // right } auth.param(p_uri) = digestUri; if (!authQop.empty()) { auth.param(p_response) = Helper::makeResponseMD5WithA1(passwordHashA1, getMethodName(request.header(h_RequestLine).getMethod()), digestUri, challenge.param(p_nonce), authQop, cnonce, nonceCountString, request.getContents()); auth.param(p_cnonce) = cnonce; auth.param(p_nc) = nonceCountString; auth.param(p_qop) = authQop; } else { assert(challenge.exists(p_realm)); auth.param(p_response) = Helper::makeResponseMD5WithA1(passwordHashA1, getMethodName(request.header(h_RequestLine).getMethod()), digestUri, challenge.param(p_nonce)); } if (challenge.exists(p_algorithm)) { auth.param(p_algorithm) = challenge.param(p_algorithm); } else { auth.param(p_algorithm) = "MD5"; } if (challenge.exists(p_opaque) && challenge.param(p_opaque).size() > 0) { auth.param(p_opaque) = challenge.param(p_opaque); } } //.dcm. all the auth stuff should be yanked out of helper and //architected for algorithm independance bool Helper::algorithmAndQopSupported(const Auth& challenge) { if ( !(challenge.exists(p_nonce) && challenge.exists(p_realm))) { return false; } return ((!challenge.exists(p_algorithm) || isEqualNoCase(challenge.param(p_algorithm), "MD5")) && (!challenge.exists(p_qop) || isEqualNoCase(challenge.param(p_qop), Symbols::auth) || isEqualNoCase(challenge.param(p_qop), Symbols::authInt))); } SipMessage& Helper::addAuthorization(SipMessage& request, const SipMessage& challenge, const Data& username, const Data& password, const Data& cnonce, unsigned int& nonceCount) { Data nonceCountString = Data::Empty; assert(challenge.isResponse()); assert(challenge.header(h_StatusLine).responseCode() == 401 || challenge.header(h_StatusLine).responseCode() == 407); if (challenge.exists(h_ProxyAuthenticates)) { const ParserContainer& auths = challenge.header(h_ProxyAuthenticates); for (ParserContainer::const_iterator i = auths.begin(); i != auths.end(); i++) { request.header(h_ProxyAuthorizations).push_back(makeChallengeResponseAuth(request, username, password, *i, cnonce, nonceCount, nonceCountString)); } } if (challenge.exists(h_WWWAuthenticates)) { const ParserContainer& auths = challenge.header(h_WWWAuthenticates); for (ParserContainer::const_iterator i = auths.begin(); i != auths.end(); i++) { request.header(h_Authorizations).push_back(makeChallengeResponseAuth(request, username, password, *i, cnonce, nonceCount, nonceCountString)); } } return request; } Uri Helper::makeUri(const Data& aor, const Data& scheme) { assert(!aor.prefix("sip:")); assert(!aor.prefix("sips:")); Data tmp(aor.size() + scheme.size() + 1, Data::Preallocate); tmp += scheme; tmp += Symbols::COLON; tmp += aor; Uri uri(tmp); return uri; } void Helper::processStrictRoute(SipMessage& request) { if (request.exists(h_Routes) && !request.const_header(h_Routes).empty() && !request.const_header(h_Routes).front().uri().exists(p_lr)) { // The next hop is a strict router. Move the next hop into the // Request-URI and move the ultimate destination to the end of the // route list. Force the message target to be the next hop router. request.header(h_Routes).push_back(NameAddr(request.const_header(h_RequestLine).uri())); request.header(h_RequestLine).uri() = request.const_header(h_Routes).front().uri(); request.header(h_Routes).pop_front(); // !jf! assert(!request.hasForceTarget()); request.setForceTarget(request.const_header(h_RequestLine).uri()); } } void Helper::massageRoute(const SipMessage& request, NameAddr& rt) { assert(request.isRequest()); // .bwc. Let's not record-route with a tel uri or something, shall we? // If the topmost route header is malformed, we can get along without. if (!request.empty(h_Routes) && request.header(h_Routes).front().isWellFormed() && (request.header(h_Routes).front().uri().scheme() == "sip" || request.header(h_Routes).front().uri().scheme() == "sips" )) { rt.uri().scheme() = request.header(h_Routes).front().uri().scheme(); } else if(request.header(h_RequestLine).uri().scheme() == "sip" || request.header(h_RequestLine).uri().scheme() == "sips") { rt.uri().scheme() = request.header(h_RequestLine).uri().scheme(); } rt.uri().param(p_lr); } int Helper::getPortForReply(SipMessage& request) { assert(request.isRequest()); int port = 0; if(request.const_header(h_Vias).front().transport() == Symbols::TCP || request.const_header(h_Vias).front().transport() == Symbols::TLS) { // 18.2.2 - bullet 1 and 2 port = request.getSource().getPort(); if(port == 0) // .slg. not sure if it makes sense for sourcePort to be 0 { port = request.const_header(h_Vias).front().sentPort(); } } else // unreliable transport 18.2.2 bullets 3 and 4 { if (request.const_header(h_Vias).front().exists(p_rport)) { port = request.getSource().getPort(); } else { port = request.const_header(h_Vias).front().sentPort(); } } // If we haven't got a valid port yet, then use the default if (port <= 0 || port > 65535) { if(request.const_header(h_Vias).front().transport() == Symbols::TLS || request.const_header(h_Vias).front().transport() == Symbols::DTLS) { port = Symbols::DefaultSipsPort; } else { port = Symbols::DefaultSipPort; } } return port; } Uri Helper::fromAor(const Data& aor, const Data& scheme) { return makeUri(aor, scheme); } bool Helper::validateMessage(const SipMessage& message,resip::Data* reason) { if (message.empty(h_To) || message.empty(h_From) || message.empty(h_CSeq) || message.empty(h_CallId) || message.empty(h_Vias) || message.empty(h_Vias)) { InfoLog(<< "Missing mandatory header fields (To, From, CSeq, Call-Id or Via)"); DebugLog(<< message); if(reason) *reason="Missing mandatory header field"; return false; } else { if(!message.header(h_CSeq).isWellFormed()) { InfoLog(<<"Malformed CSeq header"); if(reason) *reason="Malformed CSeq header"; return false; } if(!message.header(h_Vias).front().isWellFormed()) { InfoLog(<<"Malformed topmost Via header"); if(reason) *reason="Malformed topmost Via header"; return false; } if (message.isRequest()) { if(!message.header(h_RequestLine).isWellFormed()) { InfoLog(<< "Illegal request line"); if(reason) *reason="Malformed Request Line"; return false; } if(message.header(h_RequestLine).method()!=message.header(h_CSeq).method()) { InfoLog(<< "Method mismatch btw Request Line and CSeq"); if(reason) *reason="Method mismatch btw Request Line and CSeq"; return false; } } else { if(!message.header(h_StatusLine).isWellFormed()) { InfoLog(<< "Malformed status line"); if(reason) *reason="Malformed status line"; return false; } } return true; } } #if defined(USE_SSL) #include static const Data sep("[]"); static const Data pad("\0\0\0\0\0\0\0", 7); static const Data GRUU("_GRUU"); static const int saltBytes(16); Data Helper::gruuUserPart(const Data& instanceId, const Data& aor, const Data& key) { unsigned char ivec[8]; ivec[0] = '\x6E'; ivec[1] = '\xE7'; ivec[2] = '\xB0'; ivec[3] = '\x4A'; ivec[4] = '\x45'; ivec[5] = '\x93'; ivec[6] = '\x7D'; ivec[7] = '\x51'; BF_KEY fish; BF_set_key(&fish, (int)key.size(), (const unsigned char*)key.data()); const Data salt(resip::Random::getRandomHex(saltBytes)); const Data token(salt + instanceId + sep + aor + '\0' + pad.substr(0, (8 - ((salt.size() + instanceId.size() + sep.size() + 1 + aor.size() ) % 8)) % 8)); unique_ptr out(new unsigned char[token.size()]); BF_cbc_encrypt((const unsigned char*)token.data(), out.get(), (long)token.size(), &fish, ivec, BF_ENCRYPT); return GRUU + Data(out.get(),token.size()).base64encode(true/*safe URL*/); } std::pair Helper::fromGruuUserPart(const Data& gruuUserPart, const Data& key) { unsigned char ivec[8]; ivec[0] = '\x6E'; ivec[1] = '\xE7'; ivec[2] = '\xB0'; ivec[3] = '\x4A'; ivec[4] = '\x45'; ivec[5] = '\x93'; ivec[6] = '\x7D'; ivec[7] = '\x51'; static const std::pair empty; if (gruuUserPart.size() < GRUU.size()) { return empty; } const Data gruu = gruuUserPart.substr(GRUU.size()); BF_KEY fish; BF_set_key(&fish, (int)key.size(), (const unsigned char*)key.data()); const Data decoded = gruu.base64decode(); unique_ptr out(new unsigned char[gruuUserPart.size()+1]); BF_cbc_encrypt((const unsigned char*)decoded.data(), out.get(), (long)decoded.size(), &fish, ivec, BF_DECRYPT); const Data pair(out.get(), decoded.size()); Data::size_type pos = pair.find(sep); if (pos == Data::npos) { return empty; } return std::make_pair(pair.substr(2*saltBytes, pos), // strip out the salt pair.substr(pos+sep.size())); } #endif Helper::ContentsSecAttrs::ContentsSecAttrs() : mContents(nullptr), mAttributes(nullptr) {} Helper::ContentsSecAttrs::ContentsSecAttrs(std::unique_ptr contents, std::unique_ptr attributes) : mContents(std::move(contents)), mAttributes(std::move(attributes)) {} // !!bwc!! Yikes! Destructive copy c'tor! Are we _sure_ this is the // intended behavior? Helper::ContentsSecAttrs::ContentsSecAttrs(const ContentsSecAttrs& rhs) : mContents(std::move(rhs.mContents)), mAttributes(std::move(rhs.mAttributes)) {} Helper::ContentsSecAttrs& Helper::ContentsSecAttrs::operator=(const ContentsSecAttrs& rhs) { if (&rhs != this) { // !!bwc!! Yikes! Destructive assignment operator! Are we _sure_ this is // the intended behavior? mContents = std::move(rhs.mContents); mAttributes = std::move(rhs.mAttributes); } return *this; } Contents* extractFromPkcs7Recurse(Contents* tree, const Data& signerAor, const Data& receiverAor, SecurityAttributes* attributes, Security& security) { Pkcs7Contents* pk; if ((pk = dynamic_cast(tree))) { InfoLog( << "GREG1: " << *pk ); #if defined(USE_SSL) Contents* contents = security.decrypt(receiverAor, pk); if (contents) { attributes->setEncrypted(); } return contents; #else return 0; #endif } MultipartSignedContents* mps; if ((mps = dynamic_cast(tree))) { InfoLog( << "GREG2: " << *mps ); #if defined(USE_SSL) Data signer; SignatureStatus sigStatus; Contents* b = extractFromPkcs7Recurse(security.checkSignature(mps, &signer, &sigStatus), signerAor, receiverAor, attributes, security); attributes->setSigner(signer); attributes->setSignatureStatus(sigStatus); return b->clone(); #else return mps->parts().front()->clone(); #endif } MultipartAlternativeContents* alt; if ((alt = dynamic_cast(tree))) { InfoLog( << "GREG3: " << *alt ); for (MultipartAlternativeContents::Parts::reverse_iterator i = alt->parts().rbegin(); i != alt->parts().rend(); ++i) { Contents* b = extractFromPkcs7Recurse(*i, signerAor, receiverAor, attributes, security); if (b) { return b; } } } MultipartMixedContents* mult; if ((mult = dynamic_cast(tree))) { InfoLog( << "GREG4: " << *mult ); for (MultipartMixedContents::Parts::iterator i = mult->parts().begin(); i != mult->parts().end(); ++i) { Contents* b = extractFromPkcs7Recurse(*i, signerAor, receiverAor, attributes, security); if (b) { return b; } }; return 0; } return tree->clone(); } Helper::ContentsSecAttrs Helper::extractFromPkcs7(const SipMessage& message, Security& security) { SecurityAttributes* attr = new SecurityAttributes; // .dlb. currently flattening SecurityAttributes? //attr->setIdentity(message.getIdentity()); attr->setIdentity(message.header(h_From).uri().getAor()); Contents *b = message.getContents(); if (b) { Data fromAor(message.header(h_From).uri().getAor()); Data toAor(message.header(h_To).uri().getAor()); if (message.isRequest()) { b = extractFromPkcs7Recurse(b, fromAor, toAor, attr, security); } else // its a response { b = extractFromPkcs7Recurse(b, toAor, fromAor, attr, security); } } std::unique_ptr c(b); std::unique_ptr a(attr); return ContentsSecAttrs(std::move(c), std::move(a)); } Helper::FailureMessageEffect Helper::determineFailureMessageEffect(const SipMessage& response) { assert(response.isResponse()); int code = response.header(h_StatusLine).statusCode(); assert(code >= 400); switch(code) { case 404: case 410: case 416: case 480: // but maybe not, still not quite decided: case 481: case 482: // but maybe not, still not quite decided: case 484: case 485: case 502: case 604: return DialogTermination; case 403: case 489: //only for only subscription case 408: //again, maybe not. This seems best. return UsageTermination; case 400: case 401: case 402: case 405: //doesn't agree w/ -00 of dialogusage case 406: case 412: case 413: case 414: case 415: case 420: case 421: case 423: case 429: // but if this the refer creating the Subscription, no sub will be created. case 486: case 487: case 488: case 491: case 493: case 494: case 500: case 505: case 513: case 603: case 606: return TransactionTermination; case 483: // who knows, gravefully terminate or just destroy dialog case 501: return ApplicationDependant; default: if (code < 600) { if (response.exists(h_RetryAfter)) { return RetryAfter; } else { return OptionalRetryAfter; } } else { if (response.exists(h_RetryAfter)) { return RetryAfter; } else { return ApplicationDependant; } } } } SdpContents* getSdpRecurse(Contents* tree) { if (dynamic_cast(tree)) { return static_cast(tree); } MultipartSignedContents* mps; if ((mps = dynamic_cast(tree))) { try { MultipartSignedContents::Parts::const_iterator it = mps->parts().begin(); Contents* contents = getSdpRecurse(*it); return static_cast(contents); } catch (ParseException& e) { ErrLog(<< e.name() << endl << e.getMessage()); } catch (BaseException& e) { ErrLog(<< e.name() << endl << e.getMessage()); } return 0; } MultipartAlternativeContents* alt; if ((alt = dynamic_cast(tree))) { try { for (MultipartAlternativeContents::Parts::reverse_iterator i = alt->parts().rbegin(); i != alt->parts().rend(); ++i) { Contents* contents = getSdpRecurse(*i); if (contents) { return static_cast(contents); } } } catch (ParseException& e) { ErrLog(<< e.name() << endl << e.getMessage()); } catch (BaseException& e) { ErrLog(<< e.name() << endl << e.getMessage()); } return 0; } MultipartMixedContents* mult; if ((mult = dynamic_cast(tree))) { try { for (MultipartMixedContents::Parts::iterator i = mult->parts().begin(); i != mult->parts().end(); ++i) { Contents* contents = getSdpRecurse(*i); if (contents) { return static_cast(contents); } } } catch (ParseException& e) { ErrLog(<< e.name() << endl << e.getMessage()); } catch (BaseException& e) { ErrLog(<< e.name() << endl << e.getMessage()); } return 0; } return 0; } static std::unique_ptr emptysdp; unique_ptr Helper::getSdp(Contents* tree) { if (tree) { SdpContents* sdp = getSdpRecurse(tree); if (sdp) { DebugLog(<< "Got sdp" << endl); return unique_ptr(static_cast(sdp->clone())); } } //DebugLog(<< "No sdp" << endl); return std::unique_ptr(); } bool Helper::isClientBehindNAT(const SipMessage& request, bool privateToPublicOnly) { assert(request.isRequest()); assert(!request.header(h_Vias).empty()); // If received parameter is on top Via, then the source of the message doesn't match // the address provided in the via. Assume this is because the sender is behind a NAT. // The assumption here is that this SipStack instance is the first hop in a public SIP server // architecture, and that clients are directly connected to this instance. if(request.header(h_Vias).front().exists(p_received)) { if(privateToPublicOnly) { if(Tuple(request.header(h_Vias).front().sentHost(), 0, UNKNOWN_TRANSPORT).isPrivateAddress() && !Tuple(request.header(h_Vias).front().param(p_received), 0, UNKNOWN_TRANSPORT).isPrivateAddress()) { return true; } else { return false; } } return true; } return false; } Tuple Helper::getClientPublicAddress(const SipMessage& request) { assert(request.isRequest()); assert(!request.header(h_Vias).empty()); // Iterate through Via's starting at the bottom (closest to the client). Return the first // public address found from received parameter if present, or Via host. Vias::const_iterator it = request.header(h_Vias).end(); while(true) { it--; if(it->exists(p_received)) { // Check IP from received parameter Tuple address(it->param(p_received), 0, UNKNOWN_TRANSPORT); if(!address.isPrivateAddress()) { address.setPort(it->exists(p_rport) ? it->param(p_rport).port() : it->sentPort()); address.setType(Tuple::toTransport(it->transport())); return address; } } // Check IP from Via sentHost Tuple address(it->sentHost(), 0, UNKNOWN_TRANSPORT); if(!address.isPrivateAddress()) { address.setPort(it->exists(p_rport) ? it->param(p_rport).port() : it->sentPort()); address.setType(Tuple::toTransport(it->transport())); return address; } if(it == request.header(h_Vias).begin()) break; } return Tuple(); } /* ==================================================================== * The Vovida Software License, Version 1.0 * * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The names "VOCAL", "Vovida Open Communication Application Library", * and "Vovida Open Communication Application Library (VOCAL)" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact vocal@vovida.org. * * 4. Products derived from this software may not be called "VOCAL", nor * may "VOCAL" appear in their name, without prior written * permission of Vovida Networks, Inc. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * ==================================================================== * * This software consists of voluntary contributions made by Vovida * Networks, Inc. and many individuals on behalf of Vovida Networks, * Inc. For more information on Vovida Networks, Inc., please see * . * */