rtphone/src/libs/resiprocate/resip/stack/Helper.cxx

2300 lines
71 KiB
C++

#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <string.h>
#include <iomanip>
#include <algorithm>
#include <memory>
#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<SipMessage> 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<SipMessage> 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<SipMessage> 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<SipMessage> 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<SipMessage> 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<SipMessage> 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<SipMessage> 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<SipMessage> 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<SipMessage> 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<SipMessage> 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::AuthResult,Data>
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<Auth>* 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<Auth>::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<Auth> 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<Auth>::const_iterator i = auths.begin();
ParserContainer<Auth>::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<Auth> 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<Auth>::const_iterator i = auths.begin();
ParserContainer<Auth>::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<int>(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<resip::MethodTypes>(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<resip::MethodTypes>(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<Auth>& auths = challenge.header(h_ProxyAuthenticates);
for (ParserContainer<Auth>::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<Auth>& auths = challenge.header(h_WWWAuthenticates);
for (ParserContainer<Auth>::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 <openssl/blowfish.h>
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 <unsigned char> 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<Data,Data>
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<Data, Data> 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 <unsigned char> 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> contents,
std::unique_ptr<SecurityAttributes> 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<Pkcs7Contents*>(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<MultipartSignedContents*>(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<MultipartAlternativeContents*>(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<MultipartMixedContents*>(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<Contents> c(b);
std::unique_ptr<SecurityAttributes> 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<SdpContents*>(tree))
{
return static_cast<SdpContents*>(tree);
}
MultipartSignedContents* mps;
if ((mps = dynamic_cast<MultipartSignedContents*>(tree)))
{
try
{
MultipartSignedContents::Parts::const_iterator it = mps->parts().begin();
Contents* contents = getSdpRecurse(*it);
return static_cast<SdpContents*>(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<MultipartAlternativeContents*>(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<SdpContents*>(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<MultipartMixedContents*>(tree)))
{
try
{
for (MultipartMixedContents::Parts::iterator i = mult->parts().begin();
i != mult->parts().end(); ++i)
{
Contents* contents = getSdpRecurse(*i);
if (contents)
{
return static_cast<SdpContents*>(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<SdpContents> emptysdp;
unique_ptr<SdpContents> Helper::getSdp(Contents* tree)
{
if (tree)
{
SdpContents* sdp = getSdpRecurse(tree);
if (sdp)
{
DebugLog(<< "Got sdp" << endl);
return unique_ptr<SdpContents>(static_cast<SdpContents*>(sdp->clone()));
}
}
//DebugLog(<< "No sdp" << endl);
return std::unique_ptr<SdpContents>();
}
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
* <http://www.vovida.org/>.
*
*/