reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
Helper.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
5//#include <ctype.h>
6#include <cstring>
7#include <iomanip>
8#include <algorithm>
9#include <memory>
10#include <new>
11#include <utility>
12#include <vector>
13
14#include "resip/stack/Auth.hxx"
18#include "rutil/Coders.hxx"
19#include "resip/stack/Uri.hxx"
20#include "rutil/Logger.hxx"
21#include "rutil/Random.hxx"
22#include "rutil/Timer.hxx"
23#include "rutil/DataStream.hxx"
25#include "rutil/DnsUtil.hxx"
26#include "rutil/compat.hxx"
27#include "rutil/ParseBuffer.hxx"
35
36#ifdef USE_SSL
39#endif
40
41using namespace resip;
42using namespace std;
43
44#define RESIPROCATE_SUBSYSTEM Subsystem::SIP
45
46const int Helper::tagSize = 4;
47
48// !jf! this should be settable by the application in case a group of apps
49// (e.g. proxies) want to share the same secret
50Helper::NonceHelperPtr Helper::mNonceHelperPtr;
51
52void Helper::integer2hex(char* _d, unsigned int _s, bool _l)
53{
54 int i;
55 unsigned char j;
56 int k = 0;
57 char* s;
58
59 _s = htonl(_s);
60 s = (char*)&_s;
61
62 for (i = 0; i < 4; i++)
63 {
64 j = (s[i] >> 4) & 0xf;
65 if (j <= 9)
66 {
67 if(_l || j != 0 || k != 0)
68 {
69 _d[k++] = (j + '0');
70 }
71 }
72 else
73 {
74 _d[k++] = (j + 'a' - 10);
75 }
76
77 j = s[i] & 0xf;
78 if (j <= 9)
79 {
80 if(_l || j != 0 || k != 0)
81 {
82 _d[k++] = (j + '0');
83 }
84 }
85 else
86 {
87 _d[k++] = (j + 'a' - 10);
88 }
89 }
90}
91
92unsigned int Helper::hex2integer(const char* _s)
93{
94 unsigned int i, res = 0;
95
96 for(i = 0; i < 8; i++)
97 {
98 if ((_s[i] >= '0') && (_s[i] <= '9'))
99 {
100 res *= 16;
101 res += _s[i] - '0';
102 }
103 else if ((_s[i] >= 'a') && (_s[i] <= 'f'))
104 {
105 res *= 16;
106 res += _s[i] - 'a' + 10;
107 }
108 else if ((_s[i] >= 'A') && (_s[i] <= 'F'))
109 {
110 res *= 16;
111 res += _s[i] - 'A' + 10;
112 }
113 else
114 {
115 return res;
116 }
117 }
118
119 return res;
120}
121
122int
123Helper::jitterValue(int input, int lowerPercentage, int upperPercentage, int minimum)
124{
125 resip_assert(upperPercentage >= lowerPercentage);
126 if (input < minimum)
127 {
128 return input;
129 }
130 else if (lowerPercentage == 100 && upperPercentage == 100)
131 {
132 return input;
133 }
134 else
135 {
136 const int rnd = Random::getRandom() % (upperPercentage - lowerPercentage) + lowerPercentage;
137 return (input * rnd) / 100;
138 }
139}
140
142Helper::makeRequest(const NameAddr& target, const NameAddr& from, const NameAddr& contact, MethodTypes method)
143{
144 std::unique_ptr<SipMessage> request(new SipMessage);
145 RequestLine rLine(method);
146 rLine.uri() = target.uri();
147 request->header(h_To) = target;
148 request->header(h_RequestLine) = rLine;
149 request->header(h_MaxForwards).value() = 70;
150 request->header(h_CSeq).method() = method;
151 request->header(h_CSeq).sequence() = 1;
152 request->header(h_From) = from;
153 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
154 request->header(h_Contacts).push_back(contact);
155 request->header(h_CallId).value() = Helper::computeCallId();
156 //request->header(h_ContentLength).value() = 0;
157
158 Via via;
159 request->header(h_Vias).push_back(via);
160
161 return request.release();
162}
163
165Helper::makeRequest(const NameAddr& target, const NameAddr& from, MethodTypes method)
166{
167 NameAddr contact;
168 return makeRequest(target, from, contact, method);
169}
170
173{
174 NameAddr contact;
175 return makeRegister(to, from, contact);
176}
177
179Helper::makeRegister(const NameAddr& to, const NameAddr& from, const NameAddr& contact)
180{
181 std::unique_ptr<SipMessage> request(new SipMessage);
182 RequestLine rLine(REGISTER);
183
184 rLine.uri().scheme() = to.uri().scheme();
185 rLine.uri().host() = to.uri().host();
186 rLine.uri().port() = to.uri().port();
187 if (to.uri().exists(p_transport))
188 {
189 rLine.uri().param(p_transport) = to.uri().param(p_transport);
190 }
191
192 request->header(h_To) = to;
193 request->header(h_RequestLine) = rLine;
194 request->header(h_MaxForwards).value() = 70;
195 request->header(h_CSeq).method() = REGISTER;
196 request->header(h_CSeq).sequence() = 1;
197 request->header(h_From) = from;
198 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
199 request->header(h_CallId).value() = Helper::computeCallId();
200 resip_assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
201 request->header(h_Contacts).push_back( contact );
202
203 Via via;
204 request->header(h_Vias).push_back(via);
205
206 return request.release();
207}
208
210Helper::makeRegister(const NameAddr& to,const Data& transport)
211{
212 NameAddr contact;
213 return makeRegister(to, transport, contact);
214
215}
216
218Helper::makeRegister(const NameAddr& to, const Data& transport, const NameAddr& contact)
219{
220 std::unique_ptr<SipMessage> request(new SipMessage);
221 RequestLine rLine(REGISTER);
222
223 rLine.uri().scheme() = to.uri().scheme();
224 rLine.uri().host() = to.uri().host();
225 rLine.uri().port() = to.uri().port();
226 if (!transport.empty())
227 {
228 rLine.uri().param(p_transport) = transport;
229 }
230
231 request->header(h_To) = to;
232 request->header(h_RequestLine) = rLine;
233 request->header(h_MaxForwards).value() = 70;
234 request->header(h_CSeq).method() = REGISTER;
235 request->header(h_CSeq).sequence() = 1;
236 request->header(h_From) = to;
237 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
238 request->header(h_CallId).value() = Helper::computeCallId();
239 resip_assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
240 request->header(h_Contacts).push_back( contact );
241
242 Via via;
243 request->header(h_Vias).push_back(via);
244
245 return request.release();
246}
247
249Helper::makeInvite(const NameAddr& target, const NameAddr& from)
250{
251 return Helper::makeRequest(target, from, INVITE);
252}
253
255Helper::makeInvite(const NameAddr& target, const NameAddr& from, const NameAddr& contact)
256{
257 return Helper::makeRequest(target, from, contact, INVITE);
258}
259
262{
263 resip_assert(request.isRequest());
264 resip_assert(request.header(h_RequestLine).getMethod() == INVITE);
265 std::unique_ptr<SipMessage> cancel(new SipMessage);
266
267 RequestLine rLine(CANCEL, request.header(h_RequestLine).getSipVersion());
268 rLine.uri() = request.header(h_RequestLine).uri();
269 cancel->header(h_RequestLine) = rLine;
270 cancel->header(h_MaxForwards).value() = 70;
271 cancel->header(h_To) = request.header(h_To);
272 cancel->header(h_From) = request.header(h_From);
273 cancel->header(h_CallId) = request.header(h_CallId);
274 if (request.exists(h_ProxyAuthorizations))
275 {
276 cancel->header(h_ProxyAuthorizations) = request.header(h_ProxyAuthorizations);
277 }
278 if (request.exists(h_Authorizations))
279 {
280 cancel->header(h_Authorizations) = request.header(h_Authorizations);
281 }
282
283 if (request.exists(h_Routes))
284 {
285 cancel->header(h_Routes) = request.header(h_Routes);
286 }
287
288 cancel->header(h_CSeq) = request.header(h_CSeq);
289 cancel->header(h_CSeq).method() = CANCEL;
290 cancel->header(h_Vias).push_back(request.header(h_Vias).front());
291
292 return cancel.release();
293}
294
296Helper::makeFailureAck(const SipMessage& request, const SipMessage& response)
297{
298 resip_assert(request.header(h_Vias).size() >= 1);
299 resip_assert(request.header(h_RequestLine).getMethod() == INVITE);
300
301 std::unique_ptr<SipMessage> ack(new SipMessage);
302
303 RequestLine rLine(ACK, request.header(h_RequestLine).getSipVersion());
304 rLine.uri() = request.header(h_RequestLine).uri();
305 ack->header(h_RequestLine) = rLine;
306 ack->header(h_MaxForwards).value() = 70;
307 ack->header(h_CallId) = request.header(h_CallId);
308 ack->header(h_From) = request.header(h_From);
309 ack->header(h_To) = response.header(h_To); // to get to-tag
310 ack->header(h_Vias).push_back(request.header(h_Vias).front());
311 ack->header(h_CSeq) = request.header(h_CSeq);
312 ack->header(h_CSeq).method() = ACK;
313 if (request.exists(h_Routes))
314 {
315 ack->header(h_Routes) = request.header(h_Routes);
316 }
317
318 return ack.release();
319}
320
322Helper::makePublish(const NameAddr& target, const NameAddr& from)
323{
324 NameAddr contact;
325 return makePublish(target, from, contact);
326}
327
329Helper::makePublish(const NameAddr& target, const NameAddr& from, const NameAddr& contact)
330{
331 std::unique_ptr<SipMessage> request(new SipMessage);
332 RequestLine rLine(PUBLISH);
333 rLine.uri() = target.uri();
334
335 request->header(h_To) = target;
336 request->header(h_RequestLine) = rLine;
337 request->header(h_MaxForwards).value() = 70;
338 request->header(h_CSeq).method() = PUBLISH;
339 request->header(h_CSeq).sequence() = 1;
340 request->header(h_From) = from;
341 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
342 request->header(h_CallId).value() = Helper::computeCallId();
343 resip_assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
344 request->header(h_Contacts).push_back( contact );
345 Via via;
346 request->header(h_Vias).push_back(via);
347
348 return request.release();
349}
350
352Helper::makeMessage(const NameAddr& target, const NameAddr& from)
353{
354 NameAddr contact;
355 return makeMessage(target, from, contact);
356}
357
359Helper::makeMessage(const NameAddr& target, const NameAddr& from, const NameAddr& contact)
360{
361 std::unique_ptr<SipMessage> request(new SipMessage);
362 RequestLine rLine(MESSAGE);
363 rLine.uri() = target.uri();
364
365 request->header(h_To) = target;
366 request->header(h_RequestLine) = rLine;
367 request->header(h_MaxForwards).value() = 70;
368 request->header(h_CSeq).method() = MESSAGE;
369 request->header(h_CSeq).sequence() = 1;
370 request->header(h_From) = from;
371 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
372 request->header(h_CallId).value() = Helper::computeCallId();
373 resip_assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
374 request->header(h_Contacts).push_back( contact );
375 Via via;
376 request->header(h_Vias).push_back(via);
377
378 return request.release();
379}
380
382Helper::makeSubscribe(const NameAddr& target, const NameAddr& from)
383{
384 NameAddr contact;
385 return makeSubscribe(target, from, contact);
386}
387
389Helper::makeSubscribe(const NameAddr& target, const NameAddr& from, const NameAddr& contact)
390{
391 std::unique_ptr<SipMessage> request(new SipMessage);
392 RequestLine rLine(SUBSCRIBE);
393 rLine.uri() = target.uri();
394
395 request->header(h_To) = target;
396 request->header(h_RequestLine) = rLine;
397 request->header(h_MaxForwards).value() = 70;
398 request->header(h_CSeq).method() = SUBSCRIBE;
399 request->header(h_CSeq).sequence() = 1;
400 request->header(h_From) = from;
401 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
402 request->header(h_CallId).value() = Helper::computeCallId();
403 resip_assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
404 request->header(h_Contacts).push_front( contact );
405 Via via;
406 request->header(h_Vias).push_front(via);
407
408 return request.release();
409}
410
411void
413 const SipMessage& request,
414 int responseCode,
415 const Data& reason,
416 const Data& hostname,
417 const Data& warning)
418{
419 DebugLog(<< "Helper::makeResponse(" << request.brief() << " code=" << responseCode << " reason=" << reason);
420 response.header(h_StatusLine).responseCode() = responseCode;
421 response.header(h_From) = request.header(h_From);
422 response.header(h_To) = request.header(h_To);
423 response.header(h_CallId) = request.header(h_CallId);
424 response.header(h_CSeq) = request.header(h_CSeq);
425 response.header(h_Vias) = request.header(h_Vias);
426
427 if (!warning.empty())
428 {
429 WarningCategory warn;
430 warn.code() = 399;
431 warn.hostname() = hostname;
432 warn.text() = warning;
433 response.header(h_Warnings).push_back(warn);
434 }
435
436 if(responseCode > 100 &&
437 response.const_header(h_To).isWellFormed() &&
438 !response.const_header(h_To).exists(p_tag))
439 {
440 // Only generate a To: tag if one doesn't exist. Think Re-INVITE.
441 // No totag for failure responses or 100s
442 // ?bwc? Should we be generating to-tags for failure responses or not?
443 // The comments say no, but the code says yes. Which is it?
444 response.header(h_To).param(p_tag) = Helper::computeTag(Helper::tagSize);
445 }
446
447 // .bwc. This will only throw if the topmost Via is malformed, and that
448 // should have been caught at the transport level.
450
451 //response.header(h_ContentLength).value() = 0;
452
453 if (responseCode >= 180 && responseCode < 300 && request.exists(h_RecordRoutes))
454 {
455 response.header(h_RecordRoutes) = request.header(h_RecordRoutes);
456 }
457
458 // .bwc. If CSeq is malformed, basicCheck would have already attempted to
459 // parse it, meaning we won't throw here (we never try to parse the same
460 // thing twice, see LazyParser::checkParsed())
461 if (responseCode/100 == 2 &&
462 !response.exists(h_Contacts) &&
463 !(response.const_header(h_CSeq).method()==CANCEL) )
464 {
465 // in general, this should not create a Contact header since only requests
466 // that create a dialog (or REGISTER requests) should produce a response with
467 // a contact(s).
468
469 NameAddr contact;
470 response.header(h_Contacts).push_back(contact);
471 }
472
473 if (request.isExternal())
474 {
475 response.setFromTU();
476 }
477 else
478 {
479 // This makes a response to an internally generated request look like it's
480 // external even though it isn't
481 response.setFromExternal();
482 }
483
484 if (reason.size())
485 {
486 response.header(h_StatusLine).reason() = reason;
487 }
488 else
489 {
490 getResponseCodeReason(responseCode, response.header(h_StatusLine).reason());
491 }
492}
493
494void
496 const SipMessage& request,
497 int responseCode,
498 const NameAddr& myContact,
499 const Data& reason,
500 const Data& hostname,
501 const Data& warning)
502{
503 makeResponse(response,request, responseCode, reason,hostname, warning);
504 // in general, this should not create a Contact header since only requests
505 // that create a dialog (or REGISTER requests) should produce a response with
506 // a contact(s).
507 response.header(h_Contacts).clear();
508 response.header(h_Contacts).push_back(myContact);
509}
510
513 int responseCode,
514 const Data& reason,
515 const Data& hostname,
516 const Data& warning)
517{
518 // .bwc. Exception safety. Catch/rethrow is dicey because we can't rethrow
519 // resip::BaseException, since it is abstract.
520 std::unique_ptr<SipMessage> response(new SipMessage);
521
522 makeResponse(*response, request, responseCode, reason, hostname, warning);
523 return response.release();
524}
525
528 int responseCode,
529 const NameAddr& myContact,
530 const Data& reason,
531 const Data& hostname,
532 const Data& warning)
533{
534 // .bwc. Exception safety. Catch/rethrow is dicey because we can't rethrow
535 // resip::BaseException, since it is abstract.
536 std::unique_ptr<SipMessage> response(new SipMessage);
537
538 makeResponse(*response, request, responseCode, reason, hostname, warning);
539
540 // in general, this should not create a Contact header since only requests
541 // that create a dialog (or REGISTER requests) should produce a response with
542 // a contact(s).
543 response->header(h_Contacts).clear();
544 response->header(h_Contacts).push_back(myContact);
545 return response.release();
546}
547
548void
550 const SipMessage& msg,
551 int responseCode,
552 const Data& additionalHeaders,
553 const Data& body)
554{
555 raw.reserve(256);
556 {
557 DataStream encodeStream(raw);
558 encodeStream << "SIP/2.0 " << responseCode << " ";
559 Data reason;
560 getResponseCodeReason(responseCode, reason);
561 encodeStream << reason << Symbols::CRLF;
562 msg.encodeSingleHeader(Headers::Via,encodeStream);
563 msg.encodeSingleHeader(Headers::To,encodeStream);
564 msg.encodeSingleHeader(Headers::From,encodeStream);
565 msg.encodeSingleHeader(Headers::CallID,encodeStream);
566 msg.encodeSingleHeader(Headers::CSeq,encodeStream);
567 encodeStream << additionalHeaders;
568 encodeStream << "Content-Length: " << body.size() << "\r\n\r\n";
569 }
570}
571
574 const int* allowedMethods,
575 int len)
576{
577 SipMessage* resp = Helper::makeResponse(request, 405);
578
579 if (len < 0)
580 {
581 int upperBound = static_cast<int>(MAX_METHODS);
582
583 // The UNKNOWN method name is the first in the enum
584 for (int i = 1; i < upperBound; i++)
585 {
586 int last = 0;
587
588 // ENUMS must be contiguous in order for this to work.
589 resip_assert(i - last <= 1);
590 Token t;
591 t.value() = getMethodName(static_cast<resip::MethodTypes>(i));
592 resp->header(h_Allows).push_back(t);
593 last = i;
594 }
595 }
596 else
597 {
598 // use user's list
599 for (int i = 0; i < len; i++)
600 {
601 Token t;
602 t.value() = getMethodName(static_cast<resip::MethodTypes>(allowedMethods[i]));
603 resp->header(h_Allows).push_back(t);
604 }
605 }
606 return resp;
607}
608
609void
610Helper::getResponseCodeReason(int responseCode, Data& reason)
611{
612 switch (responseCode)
613 {
614 case 100: reason = "Trying"; break;
615 case 180: reason = "Ringing"; break;
616 case 181: reason = "Call Is Being Forwarded"; break;
617 case 182: reason = "Queued"; break;
618 case 183: reason = "Session Progress"; break;
619 case 200: reason = "OK"; break;
620 case 202: reason = "Accepted"; break;
621 case 300: reason = "Multiple Choices"; break;
622 case 301: reason = "Moved Permanently"; break;
623 case 302: reason = "Moved Temporarily"; break;
624 case 305: reason = "Use Proxy"; break;
625 case 380: reason = "Alternative Service"; break;
626 case 400: reason = "Bad Request"; break;
627 case 401: reason = "Unauthorized"; break;
628 case 402: reason = "Payment Required"; break;
629 case 403: reason = "Forbidden"; break;
630 case 404: reason = "Not Found"; break;
631 case 405: reason = "Method Not Allowed"; break;
632 case 406: reason = "Not Acceptable"; break;
633 case 407: reason = "Proxy Authentication Required"; break;
634 case 408: reason = "Request Timeout"; break;
635 case 410: reason = "Gone"; break;
636 case 412: reason = "Precondition Failed"; break;
637 case 413: reason = "Request Entity Too Large"; break;
638 case 414: reason = "Request-URI Too Long"; break;
639 case 415: reason = "Unsupported Media Type"; break;
640 case 416: reason = "Unsupported URI Scheme"; break;
641 case 420: reason = "Bad Extension"; break;
642 case 421: reason = "Extension Required"; break;
643 case 422: reason = "Session Interval Too Small"; break;
644 case 423: reason = "Interval Too Brief"; break;
645 case 428: reason = "Use Identity Header"; break;
646 case 430: reason = "Flow failed"; break;
647 case 436: reason = "Bad Identity Info"; break;
648 case 437: reason = "Unsupported Credential"; break;
649 case 438: reason = "Invalid Identity Header"; break;
650 case 439: reason = "First Hop Lacks Outbound Support"; break;
651 case 480: reason = "Temporarily Unavailable"; break;
652 case 481: reason = "Call/Transaction Does Not Exist"; break;
653 case 482: reason = "Loop Detected"; break;
654 case 483: reason = "Too Many Hops"; break;
655 case 484: reason = "Address Incomplete"; break;
656 case 485: reason = "Ambiguous"; break;
657 case 486: reason = "Busy Here"; break;
658 case 487: reason = "Request Terminated"; break;
659 case 488: reason = "Not Acceptable Here"; break;
660 case 489: reason = "Event Package Not Supported"; break;
661 case 491: reason = "Request Pending"; break;
662 case 493: reason = "Undecipherable"; break;
663 case 500: reason = "Server Internal Error"; break;
664 case 501: reason = "Not Implemented"; break;
665 case 502: reason = "Bad Gateway"; break;
666 case 503: reason = "Service Unavailable"; break;
667 case 504: reason = "Server Time-out"; break;
668 case 505: reason = "Version Not Supported"; break;
669 case 513: reason = "Message Too Large"; break;
670 case 600: reason = "Busy Everywhere"; break;
671 case 603: reason = "Decline"; break;
672 case 604: reason = "Does Not Exist Anywhere"; break;
673 case 606: reason = "Not Acceptable"; break;
674 }
675}
676
677static const Data cookie("z9hG4bK"); // magic cookie per rfc3261
678Data
680{
681 Data result(16, Data::Preallocate);
682 result += cookie;
683 result += Random::getCryptoRandomHex(4);
684 result += "C1";
685 result += Random::getCryptoRandomHex(2);
686 return result;
687}
688
689Data
691{
693#ifndef USE_SSL // .bwc. None of this is neccessary if we're using openssl
694#if defined(__linux__) || defined(__APPLE__)
695 pid_t pid = getpid();
696 hostAndSalt.append((char*)&pid,sizeof(pid));
697#endif
698#ifdef __APPLE__
699 pthread_t thread = pthread_self();
700 hostAndSalt.append((char*)&thread,sizeof(thread));
701#endif
702#ifdef WIN32
703 DWORD proccessId = ::GetCurrentProcessId();
704 DWORD threadId = ::GetCurrentThreadId();
705 hostAndSalt.append((char*)&proccessId,sizeof(proccessId));
706 hostAndSalt.append((char*)&threadId,sizeof(threadId));
707#endif
708#endif // of USE_SSL
709 return hostAndSalt.md5(Data::BASE64);
710}
711
712Data
714{
715 return Random::getCryptoRandomHex(numBytes);
716}
717
718void
720{
721 mNonceHelperPtr.mNonceHelper = nonceHelper;
722}
723
726{
727 if (mNonceHelperPtr.mNonceHelper == 0)
728 {
729 mNonceHelperPtr.mNonceHelper = new BasicNonceHelper();
730 }
731 return mNonceHelperPtr.mNonceHelper;
732}
733
734
735Data
736Helper::makeNonce(const SipMessage& request, const Data& timestamp)
737{
738 return getNonceHelper()->makeNonce(request, timestamp);
739}
740
741bool
743{
744 if (!auth.exists(p_algorithm))
745 {
746 // No algorithm specified, assume MD5
747 digestType = MD5;
748 return true;
749 }
750
751 Data algorithmName = auth.param(p_algorithm);
752 return isDigestAlgorithmSupported(algorithmName, digestType);
753}
754
755bool
756Helper::isDigestAlgorithmSupported(const Data& algorithmName, DigestType& digestType)
757{
758 if (resip::isEqualNoCase(algorithmName, "MD5"))
759 {
760 digestType = MD5;
761 return true;
762 }
763#ifdef USE_SSL
764 else if (resip::isEqualNoCase(algorithmName, "SHA-256"))
765 {
766 digestType = SHA256;
767 return true;
768 }
769 else if (resip::isEqualNoCase(algorithmName, "SHA-512-256"))
770 {
771 digestType = SHA512_256;
772 return true;
773 }
774#endif
775 return false;
776}
777
778// Create a custom A1 string (applicable for password storage, that supports multiple digest algorithms)
779// Format: [<algorithm_name>]<hex_encoded_A1_hash>[<algorithm2_name>]<hex_encoded_A1_hash2>....
780// Example: [MD5]5f4dcc3b5aa765d61d8327deb882cf99[SHA-256]6bb4837eb74329105ee4568dda7dc67ed2ca2ad9e59fcbfbcde5e7f8c1b6d9d1
781// For backward compatibility, if no algorithm name is found, MD5 is assumed.
782Data
784 const Data& realm,
785 const Data& password)
786{
787 Data a1Input;
788 {
789 DataStream a1InputStream(a1Input);
790 a1InputStream << username
792 << realm
794 << password;
795 }
796
797 Data result;
798 DataStream resultStream(result);
799
800 // Add MD5 tag/hash
801 DigestStream a1md5(MD5);
802 a1md5 << a1Input;
803 resultStream << "[" << DigestStream::getDigestName(resip::MD5) << "]" << a1md5.getHex();
804
805#ifdef USE_SSL
806 DigestStream a1sha256(SHA256);
807 a1sha256 << a1Input;
808 resultStream << "[" << DigestStream::getDigestName(resip::SHA256) << "]" << a1sha256.getHex();
809
810 DigestStream a1sha512_256(SHA512_256);
811 a1sha512_256 << a1Input;
812 resultStream << "[" << DigestStream::getDigestName(resip::SHA512_256) << "]" << a1sha512_256.getHex();
813#endif
814
815 resultStream.flush();
816 return result;
817}
818
819// Extract the algorithm specific A1 hash from a custom resip A1 hash string. Returns empty string if hash
820// algorithm not found in string.
821Data
822Helper::extractA1FromResipA1HashString(const Data& resipA1HashString, DigestType digestType)
823{
824 if (resipA1HashString.size() == 32)
825 {
826 if (digestType == MD5)
827 {
828 // Backward compatibility - assume MD5 if no tags found / size is full md5 hash in hex (32 bytes)
829 return resipA1HashString;
830 }
831 else
832 {
833 // SHA256 or SHA512-256 requested, but only MD5 hash found
834 return Data::Empty;
835 }
836 }
837
838 ParseBuffer pb(resipA1HashString);
839 Data digestTag;
840 {
841 DataStream digestTagStream(digestTag);
842 digestTagStream << "[" << DigestStream::getDigestName(digestType) << "]";
843 }
844 pb.skipToChars(digestTag);
845 if (!pb.eof())
846 {
847 pb.skipN(digestTag.size());
848 const char* anchor = (const char*)pb.position();
849 pb.skipToChar('[');
850 return pb.data(anchor);
851 }
852 return Data::Empty;
853}
854
855bool
857{
858 if (resipA1HashString.size() == 32)
859 {
860 // Backward compatibility - assume MD5 if no tags found / size is full md5 hash in hex (32 bytes)
861 return digestType == MD5;
862 }
863
864 ParseBuffer pb(resipA1HashString);
865 Data digestTag;
866 {
867 DataStream digestTagStream(digestTag);
868 digestTagStream << "[" << DigestStream::getDigestName(digestType) << "]";
869 }
870 pb.skipToChars(digestTag);
871 return !pb.eof();
872}
873
876 const Data& realm,
877 const Data& password,
878 int expiresDelta)
879{
880 return authenticateRequest(request, realm, password, expiresDelta, false /* isPasswordHashResipA1? */);
881}
882
885 const Data& realm,
886 const Data& resipPasswordHashA1,
887 int expiresDelta)
888{
889 return authenticateRequest(request, realm, resipPasswordHashA1, expiresDelta, true /* isPasswordHashResipA1? */);
890}
891
892static Data digest("digest");
895 const Data& realm,
896 const Data& passwordOrResipPasswordHashA1,
897 int expiresDelta,
898 bool isPasswordHashResipA1)
899{
900 DebugLog(<< "Authenticating " << (isPasswordHashResipA1 ? "with HA1" : "") << ": realm=" << realm << " expires=" << expiresDelta);
901 //DebugLog(<< request);
902
903 // !bwc! Somewhat inefficient. Maybe optimize later.
905
906 if (request.exists(h_ProxyAuthorizations))
907 {
908 auths.append(request.header(h_ProxyAuthorizations));
909 }
910
911 if (request.exists(h_Authorizations))
912 {
913 auths.append(request.header(h_Authorizations));
914 }
915
916 if (auths.empty())
917 {
918 DebugLog(<< "No authentication headers. Failing request.");
919 return Failed;
920 }
921
922 // ?bwc? Why is const_iterator& operator=(const iterator& rhs)
923 // not working properly?
924 //ParserContainer<Auth>::const_iterator i = auths.begin();
925
927
928 for (; i != auths.end(); i++)
929 {
930 if (i->exists(p_realm) &&
931 i->exists(p_nonce) &&
932 i->exists(p_response) &&
933 i->param(p_realm) == realm)
934 {
935 if (!isEqualNoCase(i->scheme(), digest))
936 {
937 DebugLog(<< "Scheme must be Digest");
938 continue;
939 }
940
941 DigestType digestType;
942 if (!isDigestAlgorithmSupported((*i), digestType))
943 {
944 DebugLog(<< "Digest algorithm not supported");
945 continue;
946 }
947
948 NonceHelper::Nonce x_nonce = getNonceHelper()->parseNonce(i->param(p_nonce));
949 if (x_nonce.getCreationTime() == 0)
950 {
951 return BadlyFormed;
952 }
953
954 if (expiresDelta > 0)
955 {
957 if (x_nonce.getCreationTime() + expiresDelta < now)
958 {
959 DebugLog(<< "Nonce has expired.");
960 return Expired;
961 }
962 }
963
964 Data then(x_nonce.getCreationTime());
965
966 if (i->param(p_nonce) != makeNonce(request, then))
967 {
968 InfoLog(<< "Not my nonce.");
969 return Failed;
970 }
971
972 InfoLog(<< " algorithm=" << DigestStream::getDigestName(digestType)
973 << " username=" << (i->param(p_username))
974 << " method=" << getMethodName(request.header(h_RequestLine).getMethod())
975 << " uri=" << i->param(p_uri)
976 << " nonce=" << i->param(p_nonce));
977
978 if (i->exists(p_qop))
979 {
980 if (i->param(p_qop) == Symbols::auth || i->param(p_qop) == Symbols::authInt)
981 {
982 if (i->exists(p_uri) && i->exists(p_cnonce) && i->exists(p_nc))
983 {
984 Data responseDigest;
985 if (isPasswordHashResipA1)
986 {
987 responseDigest = makeResponseDigestWithA1(extractA1FromResipA1HashString(passwordOrResipPasswordHashA1, digestType),
989 i->param(p_uri),
990 i->param(p_nonce),
991 i->param(p_qop),
992 i->param(p_cnonce),
993 i->param(p_nc),
994 request.getContents(),
995 digestType);
996 }
997 else
998 {
999 responseDigest = makeResponseDigest(i->param(p_username),
1000 passwordOrResipPasswordHashA1,
1001 realm,
1003 i->param(p_uri),
1004 i->param(p_nonce),
1005 i->param(p_qop),
1006 i->param(p_cnonce),
1007 i->param(p_nc),
1008 request.getContents(),
1009 digestType);
1010 }
1011 if (i->param(p_response) == responseDigest)
1012 {
1013 return Authenticated;
1014 }
1015 else
1016 {
1017 return Failed;
1018 }
1019 }
1020 }
1021 else
1022 {
1023 InfoLog(<< "Unsupported qop=" << i->param(p_qop));
1024 return Failed;
1025 }
1026 }
1027 else if (i->exists(p_uri))
1028 {
1029 Data responseDigest;
1030 if (isPasswordHashResipA1)
1031 {
1032 responseDigest = makeResponseDigestWithA1(extractA1FromResipA1HashString(passwordOrResipPasswordHashA1, digestType),
1034 i->param(p_uri),
1035 i->param(p_nonce),
1036 Data::Empty /* qop */, Data::Empty /* cnonce */, Data::Empty /* cnonceCount */, 0 /* body */,
1037 digestType);
1038 }
1039 else
1040 {
1041 responseDigest = makeResponseDigest(i->param(p_username),
1042 passwordOrResipPasswordHashA1,
1043 realm,
1045 i->param(p_uri),
1046 i->param(p_nonce),
1047 Data::Empty /* qop */, Data::Empty /* cnonce */, Data::Empty /* cnonceCount */, 0 /* body */,
1048 digestType);
1049 }
1050
1051 if (i->param(p_response) == responseDigest)
1052 {
1053 return Authenticated;
1054 }
1055 else
1056 {
1057 return Failed;
1058 }
1059 }
1060 }
1061 else
1062 {
1063 return BadlyFormed;
1064 }
1065 }
1066
1067 return BadlyFormed;
1068}
1069
1070std::pair<Helper::AuthResult,Data>
1072 const Data& realm,
1073 const Data& resipPasswordHashA1,
1074 int expiresDelta,
1075 bool proxyAuthorization)
1076{
1077 Data username;
1078 DebugLog(<< "Authenticating: realm=" << realm << " expires=" << expiresDelta);
1079 //DebugLog(<< request);
1080
1081 const ParserContainer<Auth>* auths = 0;
1082 if (proxyAuthorization)
1083 {
1084 if (request.exists(h_ProxyAuthorizations))
1085 {
1086 auths = &request.header(h_ProxyAuthorizations);
1087 }
1088 }
1089 else
1090 {
1091 if (request.exists(h_Authorizations))
1092 {
1093 auths = &request.header(h_Authorizations);
1094 }
1095 }
1096
1097 if (auths)
1098 {
1099 for (ParserContainer<Auth>::const_iterator i = auths->begin(); i != auths->end(); i++)
1100 {
1101 if (i->exists(p_realm) &&
1102 i->exists(p_nonce) &&
1103 i->exists(p_response) &&
1104 i->param(p_realm) == realm)
1105 {
1106 if (!isEqualNoCase(i->scheme(), digest))
1107 {
1108 DebugLog(<< "Scheme must be Digest");
1109 continue;
1110 }
1111
1112 DigestType digestType;
1113 if (!isDigestAlgorithmSupported((*i), digestType))
1114 {
1115 DebugLog(<< "Digest algorithm not supported");
1116 continue;
1117 }
1118
1119 NonceHelper::Nonce x_nonce = getNonceHelper()->parseNonce(i->param(p_nonce));
1120 if (x_nonce.getCreationTime() == 0)
1121 {
1122 return make_pair(BadlyFormed, username);
1123 }
1124
1125 if (expiresDelta > 0)
1126 {
1128 if (x_nonce.getCreationTime() + expiresDelta < now)
1129 {
1130 DebugLog(<< "Nonce has expired.");
1131 return make_pair(Expired, username);
1132 }
1133 }
1134
1135 Data then(x_nonce.getCreationTime());
1136 if (i->param(p_nonce) != makeNonce(request, then))
1137 {
1138 InfoLog(<< "Not my nonce. expected=" << makeNonce(request, then)
1139 << " received=" << i->param(p_nonce)
1140 << " then=" << then);
1141
1142 return make_pair(BadlyFormed, username);
1143 }
1144
1145 if (i->exists(p_qop))
1146 {
1147 if (i->param(p_qop) == Symbols::auth || i->param(p_qop) == Symbols::authInt)
1148 {
1149 if (i->exists(p_uri) && i->exists(p_cnonce) && i->exists(p_nc))
1150 {
1151 if (i->param(p_response) == makeResponseDigestWithA1(extractA1FromResipA1HashString(resipPasswordHashA1, digestType),
1153 i->param(p_uri),
1154 i->param(p_nonce),
1155 i->param(p_qop),
1156 i->param(p_cnonce),
1157 i->param(p_nc),
1158 request.getContents(),
1159 digestType))
1160 {
1161 if (i->exists(p_username))
1162 {
1163 username = i->param(p_username);
1164 }
1165 return make_pair(Authenticated, username);
1166 }
1167 else
1168 {
1169 return make_pair(Failed, username);
1170 }
1171 }
1172 }
1173 else
1174 {
1175 InfoLog(<< "Unsupported qop=" << i->param(p_qop));
1176 return make_pair(Failed, username);
1177 }
1178 }
1179 else if (i->exists(p_uri))
1180 {
1181 if (i->param(p_response) == makeResponseDigestWithA1(extractA1FromResipA1HashString(resipPasswordHashA1, digestType),
1183 i->param(p_uri),
1184 i->param(p_nonce),
1185 Data::Empty /* qop */, Data::Empty /* cnonce */, Data::Empty /* cnonceCount */, 0 /* body */,
1186 digestType))
1187 {
1188 if (i->exists(p_username))
1189 {
1190 username = i->param(p_username);
1191 }
1192 return make_pair(Authenticated, username);
1193 }
1194 else
1195 {
1196 return make_pair(Failed, username);
1197 }
1198 }
1199 }
1200 else
1201 {
1202 return make_pair(BadlyFormed, username);
1203 }
1204 }
1205 return make_pair(BadlyFormed, username);
1206 }
1207 DebugLog(<< "No authentication headers. Failing request.");
1208 return make_pair(Failed, username);
1209}
1210
1212Helper::makeProxyChallenge(const SipMessage& request, const Data& realm, bool useAuth, bool stale, const std::vector<DigestType>& digestTypes)
1213{
1214 return makeChallenge(request, realm, useAuth, stale, true, digestTypes);
1215}
1216
1218Helper::makeWWWChallenge(const SipMessage& request, const Data& realm, bool useAuth, bool stale, const std::vector<DigestType>& digestTypes)
1219{
1220 return makeChallenge(request, realm, useAuth, stale, false, digestTypes);
1221}
1222
1224Helper::makeChallenge(const SipMessage& request, const Data& realm, bool useAuth, bool stale, bool proxy, const std::vector<DigestType>& digestTypes)
1225{
1226 SipMessage* response;
1227 if (proxy)
1228 {
1229 response = Helper::makeResponse(request, 407);
1230 }
1231 else
1232 {
1233 response = Helper::makeResponse(request, 401);
1234 }
1235 if (digestTypes.empty())
1236 {
1237 addChallenge(*response, request, realm, useAuth, stale, proxy, MD5);
1238 }
1239 else
1240 {
1241 for (auto it = digestTypes.begin(); it != digestTypes.end(); ++it)
1242 {
1243 addChallenge(*response, request, realm, useAuth, stale, proxy, *it);
1244 }
1245 }
1246 return response;
1247}
1248
1249void
1250Helper::addChallenge(SipMessage& response, const SipMessage& request, const Data& realm, bool useAuth, bool stale, bool proxy, DigestType digestType)
1251{
1252 Auth auth;
1253 auth.scheme() = Symbols::Digest;
1254 Data timestamp(Timer::getTimeSecs());
1255 auth.param(p_nonce) = makeNonce(request, timestamp);
1256 auth.param(p_algorithm) = DigestStream::getDigestName(digestType);
1257 auth.param(p_realm) = realm;
1258 if (useAuth)
1259 {
1260 auth.param(p_qopOptions) = "auth,auth-int";
1261 }
1262 if (stale)
1263 {
1264 auth.param(p_stale) = "true";
1265 }
1266 if (proxy)
1267 {
1268 response.header(h_ProxyAuthenticates).push_back(auth);
1269 }
1270 else
1271 {
1272 response.header(h_WWWAuthenticates).push_back(auth);
1273 }
1274}
1275
1276// priority-order list of preferred qop tokens
1278{
1279 "auth-int",
1280 "auth"
1281};
1282static size_t pTokenSize = sizeof(preferredTokens) / sizeof(*preferredTokens);
1283Data
1284Helper::qopOption(const Auth& challenge)
1285{
1286 bool found = false;
1287 size_t index = pTokenSize;
1288 if (challenge.exists(p_qopOptions) && !challenge.param(p_qopOptions).empty())
1289 {
1290 ParseBuffer pb(challenge.param(p_qopOptions).data(), challenge.param(p_qopOptions).size());
1291 do
1292 {
1293 const char* anchor = pb.skipWhitespace();
1295 Data q;
1296 pb.data(q, anchor);
1297 if (!pb.eof())
1298 {
1299 pb.skipChar();
1300 }
1301 for (size_t i = 0; i < pTokenSize; i++)
1302 {
1303 if (q == preferredTokens[i])
1304 {
1305 // found a preferred token; is it higher priority?
1306 if (i < index)
1307 {
1308 found = true;
1309 index = i;
1310 }
1311 }
1312 }
1313 } while (!pb.eof());
1314 }
1315
1316 if (found)
1317 {
1318 return preferredTokens[index];
1319 }
1320
1321 return Data::Empty;
1322}
1323
1324void
1325Helper::updateNonceCount(unsigned int& nonceCount, Data& nonceCountString)
1326{
1327 if (!nonceCountString.empty())
1328 {
1329 return;
1330 }
1331 nonceCount++;
1332 {
1333 //DataStream s(nonceCountString);
1334
1335 //s << std::setw(8) << std::setfill('0') << std::hex << nonceCount;
1336 constexpr size_t bufSize = 128;
1337 char buf[bufSize];
1338 *buf = 0;
1339
1340 std::snprintf(buf, bufSize, "%08x", nonceCount);
1341 nonceCountString = buf;
1342 }
1343 DebugLog(<< "nonceCount is now: [" << nonceCountString << "]");
1344}
1345
1346bool
1348{
1349 DigestType digestType; // Will be discarded
1350 return algorithmAndQopSupported(challenge, digestType);
1351}
1352
1353bool
1355{
1356 if (!(challenge.exists(p_nonce) && challenge.exists(p_realm)))
1357 {
1358 return false;
1359 }
1360 return (isDigestAlgorithmSupported(challenge, digestType) &&
1361 (!challenge.exists(p_qop) ||
1362 isEqualNoCase(challenge.param(p_qop), Symbols::auth) ||
1363 isEqualNoCase(challenge.param(p_qop), Symbols::authInt)));
1364}
1365
1366
1369 const SipMessage& challenge,
1370 const Data& username,
1371 const Data& password,
1372 const Data& cnonce,
1373 unsigned int& nonceCount)
1374{
1375 Data nonceCountString = Data::Empty;
1376
1377 resip_assert(challenge.isResponse());
1378 resip_assert(challenge.header(h_StatusLine).responseCode() == 401 ||
1379 challenge.header(h_StatusLine).responseCode() == 407);
1380
1381 const ParserContainer<Auth>* pChallengeAuthenticates;
1382 ParserContainer<Auth>* pRequestAuthorizations;
1383
1384 if (challenge.exists(h_ProxyAuthenticates))
1385 {
1386 pChallengeAuthenticates = &challenge.header(h_ProxyAuthenticates);
1387 pRequestAuthorizations = &request.header(h_ProxyAuthorizations);
1388 }
1389 else if (challenge.exists(h_WWWAuthenticates))
1390 {
1391 pChallengeAuthenticates = &challenge.header(h_WWWAuthenticates);
1392 pRequestAuthorizations = &request.header(h_Authorizations);
1393 }
1394 else
1395 {
1396 return request;
1397 }
1398
1399 // iterate all auth challenges and add challenge response to request authorization header with the first supported digest algorithm
1400 for (auto i = pChallengeAuthenticates->begin(); i != pChallengeAuthenticates->end(); i++)
1401 {
1402 DigestType digestType;
1403 if (isDigestAlgorithmSupported(*i, digestType))
1404 {
1405 pRequestAuthorizations->push_back(makeChallengeResponseAuth(request, username, password, *i,
1406 cnonce, nonceCount, nonceCountString));
1407 break;
1408 }
1409 }
1410
1411 return request;
1412}
1413
1414Auth
1416 const Data& username,
1417 const Data& password,
1418 const Auth& challenge,
1419 const Data& cnonce,
1420 unsigned int& nonceCount,
1421 Data& nonceCountString)
1422{
1423 Auth auth;
1424 Data authQop = qopOption(challenge);
1425 if(!authQop.empty())
1426 {
1427 updateNonceCount(nonceCount, nonceCountString);
1428 }
1429 makeChallengeResponseAuth(request, username, password, challenge, cnonce, authQop, nonceCountString, auth);
1430 return auth;
1431}
1432
1433void
1435 const Data& username,
1436 const Data& password,
1437 const Auth& challenge,
1438 const Data& cnonce,
1439 const Data& authQop,
1440 const Data& nonceCountString,
1441 Auth& auth)
1442{
1443 auth.scheme() = Symbols::Digest;
1444 auth.param(p_username) = username;
1445 resip_assert(challenge.exists(p_realm));
1446 auth.param(p_realm) = challenge.param(p_realm);
1447 resip_assert(challenge.exists(p_nonce));
1448 auth.param(p_nonce) = challenge.param(p_nonce);
1449 Data digestUri;
1450 {
1451 DataStream s(digestUri);
1452 //s << request.header(h_RequestLine).uri().host(); // wrong
1453 s << request.header(h_RequestLine).uri(); // right
1454 }
1455 auth.param(p_uri) = digestUri;
1456
1457 DigestType digestType = MD5;
1458 isDigestAlgorithmSupported(challenge, digestType);
1459
1460 if (!authQop.empty())
1461 {
1462 auth.param(p_response) = Helper::makeResponseDigest(username,
1463 password,
1464 challenge.param(p_realm),
1466 digestUri,
1467 challenge.param(p_nonce),
1468 authQop,
1469 cnonce,
1470 nonceCountString,
1471 request.getContents(),
1472 digestType);
1473 auth.param(p_cnonce) = cnonce;
1474 auth.param(p_nc) = nonceCountString;
1475 auth.param(p_qop) = authQop;
1476 }
1477 else
1478 {
1479 resip_assert(challenge.exists(p_realm));
1480 auth.param(p_response) = Helper::makeResponseDigest(username,
1481 password,
1482 challenge.param(p_realm),
1484 digestUri,
1485 challenge.param(p_nonce),
1486 Data::Empty /* qop */, Data::Empty /* cnonce */, Data::Empty /* cnonceCount */, 0 /* body */,
1487 digestType);
1488 }
1489
1490 if (challenge.exists(p_algorithm))
1491 {
1492 auth.param(p_algorithm) = challenge.param(p_algorithm);
1493 }
1494 else
1495 {
1496 auth.param(p_algorithm) = "MD5";
1497 }
1498
1499 if (challenge.exists(p_opaque) && challenge.param(p_opaque).size() > 0)
1500 {
1501 auth.param(p_opaque) = challenge.param(p_opaque);
1502 }
1503}
1504
1505Auth
1507 const Data& username,
1508 const Data& resipPasswordHashA1,
1509 const Auth& challenge,
1510 const Data& cnonce,
1511 unsigned int& nonceCount,
1512 Data& nonceCountString)
1513{
1514 Auth auth;
1515 Data authQop = qopOption(challenge);
1516 if(!authQop.empty())
1517 {
1518 updateNonceCount(nonceCount, nonceCountString);
1519 }
1520 makeChallengeResponseAuthWithA1(request, username, resipPasswordHashA1, challenge, cnonce, authQop, nonceCountString, auth);
1521 return auth;
1522}
1523
1524void
1526 const Data& username,
1527 const Data& resipPasswordHashA1,
1528 const Auth& challenge,
1529 const Data& cnonce,
1530 const Data& authQop,
1531 const Data& nonceCountString,
1532 Auth& auth)
1533{
1534 auth.scheme() = Symbols::Digest;
1535 auth.param(p_username) = username;
1536 resip_assert(challenge.exists(p_realm));
1537 auth.param(p_realm) = challenge.param(p_realm);
1538 resip_assert(challenge.exists(p_nonce));
1539 auth.param(p_nonce) = challenge.param(p_nonce);
1540 Data digestUri;
1541 {
1542 DataStream s(digestUri);
1543 //s << request.const_header(h_RequestLine).uri().host(); // wrong
1544 s << request.const_header(h_RequestLine).uri(); // right
1545 }
1546 auth.param(p_uri) = digestUri;
1547
1548 DigestType digestType = MD5;
1549 isDigestAlgorithmSupported(challenge, digestType);
1550
1551 if (!authQop.empty())
1552 {
1553 auth.param(p_response) = Helper::makeResponseDigestWithA1(Helper::extractA1FromResipA1HashString(resipPasswordHashA1, digestType),
1555 digestUri,
1556 challenge.param(p_nonce),
1557 authQop,
1558 cnonce,
1559 nonceCountString,
1560 request.getContents(),
1561 digestType);
1562 auth.param(p_cnonce) = cnonce;
1563 auth.param(p_nc) = nonceCountString;
1564 auth.param(p_qop) = authQop;
1565 }
1566 else
1567 {
1568 resip_assert(challenge.exists(p_realm));
1569 auth.param(p_response) = Helper::makeResponseDigestWithA1(Helper::extractA1FromResipA1HashString(resipPasswordHashA1, digestType),
1571 digestUri,
1572 challenge.param(p_nonce),
1574 digestType);
1575 }
1576
1577 if (challenge.exists(p_algorithm))
1578 {
1579 auth.param(p_algorithm) = challenge.param(p_algorithm);
1580 }
1581 else
1582 {
1583 auth.param(p_algorithm) = "MD5";
1584 }
1585
1586 if (challenge.exists(p_opaque) && challenge.param(p_opaque).size() > 0)
1587 {
1588 auth.param(p_opaque) = challenge.param(p_opaque);
1589 }
1590}
1591
1592Data
1594 const Data& method, const Data& digestUri, const Data& nonce,
1595 const Data& qop, const Data& cnonce, const Data& cnonceCount,
1596 const Contents* entityBody,
1597 DigestType digestType)
1598{
1599#ifdef RESIP_DIGEST_LOGGING
1600 Data _a2;
1601 DataStream a2(_a2);
1602#else
1603 DigestStream a2(digestType);
1604#endif
1605 a2 << method
1607 << digestUri;
1608
1609 if (qop == Symbols::authInt)
1610 {
1611 if (entityBody)
1612 {
1613 DigestStream eStream(digestType);
1614 eStream << *entityBody;
1615 a2 << Symbols::COLON << eStream.getHex();
1616#ifdef RESIP_DIGEST_LOGGING
1617 StackLog(<< "auth-int, body length = " << eStream.bytesTaken());
1618#endif
1619 }
1620 else
1621 {
1622 // No body, use digest of empty string
1623 a2 << Symbols::COLON << DigestStream(digestType).getHex();
1624#ifdef RESIP_DIGEST_LOGGING
1625 StackLog(<< "auth-int, no body");
1626#endif
1627 }
1628 }
1629
1630#ifdef RESIP_DIGEST_LOGGING
1631 Data _r;
1632 DataStream r(_r);
1633#else
1634 DigestStream r(digestType);
1635#endif
1636 r << a1
1638 << nonce
1639 << Symbols::COLON;
1640
1641 if (!qop.empty())
1642 {
1643 r << cnonceCount
1645 << cnonce
1647 << qop
1648 << Symbols::COLON;
1649 }
1650#ifdef RESIP_DIGEST_LOGGING
1651 a2.flush();
1652 StackLog(<< "A2 = " << _a2);
1653 DigestStream a2digest(digestType);
1654 a2digest << _a2;
1655 r << a2digest.getHex();
1656 r.flush();
1657 StackLog(<< "response to be hashed (HA1:nonce:HA2) = " << _r);
1658 DigestStream rdigest(digestType);
1659 rdigest << _r;
1660 return rdigest.getHex();
1661#else
1662 r << a2.getHex();
1663
1664 return r.getHex();
1665#endif
1666}
1667
1668//RFC 2617 3.2.2.1
1669Data
1670Helper::makeResponseDigest(const Data& username, const Data& password, const Data& realm,
1671 const Data& method, const Data& digestUri, const Data& nonce,
1672 const Data& qop, const Data& cnonce, const Data& cnonceCount,
1673 const Contents* entity, DigestType digestType)
1674{
1675 DigestStream a1(digestType);
1676 a1 << username
1678 << realm
1680 << password;
1681
1682 return makeResponseDigestWithA1(a1.getHex(), method, digestUri, nonce, qop,
1683 cnonce, cnonceCount, entity, digestType);
1684}
1685
1686Uri
1687Helper::makeUri(const Data& aor, const Data& scheme)
1688{
1689 resip_assert(!aor.prefix("sip:"));
1690 resip_assert(!aor.prefix("sips:"));
1691
1692 Data tmp(aor.size() + scheme.size() + 1, Data::Preallocate);
1693 tmp += scheme;
1694 tmp += Symbols::COLON;
1695 tmp += aor;
1696 Uri uri(tmp);
1697 return uri;
1698}
1699
1700void
1702{
1703 if (request.exists(h_Routes) &&
1704 !request.const_header(h_Routes).empty() &&
1705 !request.const_header(h_Routes).front().uri().exists(p_lr))
1706 {
1707 // The next hop is a strict router. Move the next hop into the
1708 // Request-URI and move the ultimate destination to the end of the
1709 // route list. Force the message target to be the next hop router.
1710 request.header(h_Routes).push_back(NameAddr(request.const_header(h_RequestLine).uri()));
1711 request.header(h_RequestLine).uri() = request.const_header(h_Routes).front().uri();
1712 request.header(h_Routes).pop_front(); // !jf!
1713 resip_assert(!request.hasForceTarget());
1714 request.setForceTarget(request.const_header(h_RequestLine).uri());
1715 }
1716}
1717
1718void
1720{
1721 resip_assert(request.isRequest());
1722 // .bwc. Let's not record-route with a tel uri or something, shall we?
1723 // If the topmost route header is malformed, we can get along without.
1724 if (!request.empty(h_Routes) &&
1725 request.header(h_Routes).front().isWellFormed() &&
1726 (request.header(h_Routes).front().uri().scheme() == "sip" ||
1727 request.header(h_Routes).front().uri().scheme() == "sips" ))
1728 {
1729 rt.uri().scheme() = request.header(h_Routes).front().uri().scheme();
1730 }
1731 else if(request.header(h_RequestLine).uri().scheme() == "sip" ||
1732 request.header(h_RequestLine).uri().scheme() == "sips")
1733 {
1734 rt.uri().scheme() = request.header(h_RequestLine).uri().scheme();
1735 }
1736
1737 rt.uri().param(p_lr);
1738}
1739
1740int
1742{
1743 resip_assert(request.isRequest());
1744 int port = 0;
1745 TransportType transportType = toTransportType(
1746 request.const_header(h_Vias).front().transport());
1747 if(isReliable(transportType))
1748 {
1749 // 18.2.2 - bullet 1 and 2
1750 port = request.getSource().getPort();
1751 if(port == 0) // .slg. not sure if it makes sense for sourcePort to be 0
1752 {
1753 port = request.const_header(h_Vias).front().sentPort();
1754 }
1755 }
1756 else // unreliable transport 18.2.2 bullets 3 and 4
1757 {
1758 if (request.const_header(h_Vias).front().exists(p_rport))
1759 {
1760 port = request.getSource().getPort();
1761 }
1762 else
1763 {
1764 port = request.const_header(h_Vias).front().sentPort();
1765 }
1766 }
1767
1768 // If we haven't got a valid port yet, then use the default
1769 if (port <= 0 || port > 65535)
1770 {
1771 if(transportType == TLS ||
1772 transportType == DTLS)
1773 {
1775 }
1776 else
1777 {
1779 }
1780 }
1781 return port;
1782}
1783
1784Uri
1785Helper::fromAor(const Data& aor, const Data& scheme)
1786{
1787 return makeUri(aor, scheme);
1788}
1789
1790bool
1792{
1793 if (message.empty(h_To) ||
1794 message.empty(h_From) ||
1795 message.empty(h_CSeq) ||
1796 message.empty(h_CallId) ||
1797 message.empty(h_Vias) ||
1798 message.empty(h_Vias))
1799 {
1800 InfoLog(<< "Missing mandatory header fields (To, From, CSeq, Call-Id or Via)");
1801 DebugLog(<< message);
1802 if(reason) *reason="Missing mandatory header field";
1803 return false;
1804 }
1805 else
1806 {
1807 if(!message.header(h_CSeq).isWellFormed())
1808 {
1809 InfoLog(<<"Malformed CSeq header");
1810 if(reason) *reason="Malformed CSeq header";
1811 return false;
1812 }
1813
1814 if(!message.header(h_Vias).front().isWellFormed())
1815 {
1816 InfoLog(<<"Malformed topmost Via header");
1817 if(reason) *reason="Malformed topmost Via header";
1818 return false;
1819 }
1820
1821 if (message.isRequest())
1822 {
1823 if(!message.header(h_RequestLine).isWellFormed())
1824 {
1825 InfoLog(<< "Illegal request line");
1826 if(reason) *reason="Malformed Request Line";
1827 return false;
1828 }
1829
1830 if(message.header(h_RequestLine).method()!=message.header(h_CSeq).method())
1831 {
1832 InfoLog(<< "Method mismatch btw Request Line and CSeq");
1833 if(reason) *reason="Method mismatch btw Request Line and CSeq";
1834 return false;
1835 }
1836 }
1837 else
1838 {
1839 if(!message.header(h_StatusLine).isWellFormed())
1840 {
1841 InfoLog(<< "Malformed status line");
1842 if(reason) *reason="Malformed status line";
1843 return false;
1844 }
1845 }
1846
1847 return true;
1848 }
1849}
1850
1851#if defined(USE_SSL) && !defined(OPENSSL_NO_BF)
1852#include <openssl/opensslv.h>
1853#include <openssl/blowfish.h>
1854#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1855#include <openssl/evp.h>
1856#include <openssl/params.h>
1857#include <openssl/core_names.h>
1858#endif
1859
1860static const Data sep("[]");
1861static const Data pad("\0\0\0\0\0\0\0", 7);
1862static const Data GRUU("_GRUU");
1863static const int saltBytes(16);
1864
1865Data
1866Helper::gruuUserPart(const Data& instanceId,
1867 const Data& aor,
1868 const Data& key)
1869{
1870 unsigned char ivec[8];
1871
1872 ivec[0] = '\x6E';
1873 ivec[1] = '\xE7';
1874 ivec[2] = '\xB0';
1875 ivec[3] = '\x4A';
1876 ivec[4] = '\x45';
1877 ivec[5] = '\x93';
1878 ivec[6] = '\x7D';
1879 ivec[7] = '\x51';
1880
1882
1883 const Data token(salt + instanceId + sep + aor + '\0' +
1884 pad.substr(0, (8 - ((salt.size() +
1885 instanceId.size() +
1886 sep.size() + 1
1887 + aor.size() ) % 8))
1888 % 8));
1889 std::vector<unsigned char> out;
1890
1891#if OPENSSL_VERSION_NUMBER < 0x30000000L
1892 out.resize(token.size());
1893
1894 BF_KEY fish;
1895 BF_set_key(&fish, (int)key.size(), (const unsigned char*)key.data());
1896
1897 BF_cbc_encrypt((const unsigned char*)token.data(),
1898 &out[0],
1899 (long)token.size(),
1900 &fish,
1901 ivec,
1902 BF_ENCRYPT);
1903#else
1904 const EVP_CIPHER* pCipher = EVP_bf_cbc();
1905 std::size_t out_size = token.size();
1906 int block_size = EVP_CIPHER_get_block_size(pCipher);
1907 if (block_size > 1)
1908 {
1909 std::size_t tail_size = out_size % block_size;
1910 if (tail_size > 0)
1911 out_size += block_size - tail_size;
1912 }
1913 out.resize(out_size);
1914
1915 std::unique_ptr<EVP_CIPHER_CTX, OpenSSLDeleter> pCipherCtx(EVP_CIPHER_CTX_new());
1916 if (!pCipherCtx)
1917 throw std::bad_alloc();
1918
1919 std::size_t keylen = key.size();
1920 const OSSL_PARAM params[] = {
1921 OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &keylen),
1922 OSSL_PARAM_construct_end()
1923 };
1924 int res = EVP_EncryptInit_ex2(pCipherCtx.get(), pCipher,
1925 reinterpret_cast<const unsigned char*>(key.data()), ivec, params);
1926 if (res <= 0)
1927 throw std::runtime_error("Failed to initialize encryption context");
1928
1929 resip_assert(static_cast<unsigned int>(EVP_CIPHER_CTX_get_iv_length(pCipherCtx.get())) <= sizeof(ivec));
1930
1931 int outlen = static_cast<int>(out_size);
1932 res = EVP_EncryptUpdate(pCipherCtx.get(), out.data(), &outlen,
1933 reinterpret_cast<const unsigned char*>(token.data()), (int)token.size());
1934 if (res > 0)
1935 {
1936 resip_assert(static_cast<unsigned int>(outlen) <= out_size);
1937 int outlen2 = static_cast<int>(out_size - outlen);
1938 res = EVP_EncryptFinal_ex(pCipherCtx.get(), out.data() + outlen, &outlen2);
1939 if (res > 0)
1940 {
1941 outlen += outlen2;
1942 resip_assert(static_cast<unsigned int>(outlen) <= out_size);
1943 out.resize(static_cast<unsigned int>(outlen));
1944 }
1945 }
1946
1947 if (res <= 0)
1948 throw std::runtime_error("Failed to encrypt GRUU user part");
1949#endif
1950
1951 return GRUU + Data(out.data(), (Data::size_type)out.size()).base64encode(true/*safe URL*/);
1952}
1953
1954std::pair<Data, Data>
1955Helper::fromGruuUserPart(const Data& gruuUserPart,
1956 const Data& key)
1957{
1958 unsigned char ivec[8];
1959
1960 ivec[0] = '\x6E';
1961 ivec[1] = '\xE7';
1962 ivec[2] = '\xB0';
1963 ivec[3] = '\x4A';
1964 ivec[4] = '\x45';
1965 ivec[5] = '\x93';
1966 ivec[6] = '\x7D';
1967 ivec[7] = '\x51';
1968
1969 if (gruuUserPart.size() < GRUU.size())
1970 {
1971 return std::pair<Data, Data>();
1972 }
1973
1974 const Data gruu = gruuUserPart.substr(GRUU.size());
1975 const Data decoded = gruu.base64decode();
1976 std::vector<unsigned char> out(gruuUserPart.size() + 1);
1977
1978#if OPENSSL_VERSION_NUMBER < 0x30000000L
1979 BF_KEY fish;
1980 BF_set_key(&fish, (int)key.size(), (const unsigned char*)key.data());
1981
1982 BF_cbc_encrypt((const unsigned char*)decoded.data(),
1983 &out[0],
1984 (long)decoded.size(),
1985 &fish,
1986 ivec,
1987 BF_DECRYPT);
1988#else
1989 std::unique_ptr<EVP_CIPHER_CTX, OpenSSLDeleter> pCipherCtx(EVP_CIPHER_CTX_new());
1990 if (!pCipherCtx)
1991 throw std::bad_alloc();
1992
1993 std::size_t keylen = key.size();
1994 const OSSL_PARAM params[] = {
1995 OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &keylen),
1996 OSSL_PARAM_construct_end()
1997 };
1998 int res = EVP_DecryptInit_ex2(pCipherCtx.get(), EVP_bf_cbc(),
1999 reinterpret_cast<const unsigned char*>(key.data()), ivec, params);
2000 if (res <= 0)
2001 throw std::runtime_error("Failed to initialize decryption context");
2002
2003 resip_assert(static_cast<unsigned int>(EVP_CIPHER_CTX_get_iv_length(pCipherCtx.get())) <= sizeof(ivec));
2004
2005 const std::size_t out_size = out.size();
2006 int outlen = static_cast<int>(out_size);
2007 res = EVP_DecryptUpdate(pCipherCtx.get(), out.data(), &outlen,
2008 reinterpret_cast<const unsigned char*>(decoded.data()), (int)decoded.size());
2009 if (res > 0)
2010 {
2011 resip_assert(static_cast<unsigned int>(outlen) <= out_size);
2012 int outlen2 = static_cast<int>(out_size - outlen);
2013 res = EVP_DecryptFinal_ex(pCipherCtx.get(), out.data() + outlen, &outlen2);
2014 if (res > 0)
2015 {
2016 outlen += outlen2;
2017 resip_assert(static_cast<unsigned int>(outlen) <= out_size);
2018 out.resize(static_cast<unsigned int>(outlen));
2019 }
2020 }
2021
2022 if (res <= 0)
2023 throw std::runtime_error("Failed to decrypt GRUU user part");
2024#endif
2025
2026 const Data pair(out.data(), (Data::size_type)out.size());
2027
2028 Data::size_type pos = pair.find(sep);
2029 if (pos == Data::npos)
2030 {
2031 return std::pair<Data, Data>();
2032 }
2033
2034 return std::make_pair(pair.substr(2*saltBytes, pos), // strip out the salt
2035 pair.substr(pos+sep.size()));
2036}
2037#endif
2038
2039Helper::ContentsSecAttrs::ContentsSecAttrs(std::unique_ptr<Contents> contents,
2040 std::unique_ptr<SecurityAttributes> attributes)
2041 : mContents(std::move(contents)),
2042 mAttributes(std::move(attributes))
2043{}
2044
2045
2046Contents*
2048 const Data& signerAor,
2049 const Data& receiverAor,
2050 SecurityAttributes* attributes,
2051 Security& security)
2052{
2053 Pkcs7Contents* pk;
2054 if ((pk = dynamic_cast<Pkcs7Contents*>(tree)))
2055 {
2056 InfoLog( << "GREG1: " << *pk );
2057#if defined(USE_SSL)
2058 Contents* contents = security.decrypt(receiverAor, pk);
2059 if (contents)
2060 {
2061 attributes->setEncrypted();
2062 }
2063 return contents;
2064#else
2065 return 0;
2066#endif
2067 }
2069 if ((mps = dynamic_cast<MultipartSignedContents*>(tree)))
2070 {
2071 InfoLog( << "GREG2: " << *mps );
2072#if defined(USE_SSL)
2073 Data signer;
2074 SignatureStatus sigStatus;
2076 &signer,
2077 &sigStatus),
2078 signerAor,
2079 receiverAor, attributes, security);
2080 attributes->setSigner(signer);
2081 attributes->setSignatureStatus(sigStatus);
2082 return b->clone();
2083#else
2084 return mps->parts().front()->clone();
2085#endif
2086 }
2088 if ((alt = dynamic_cast<MultipartAlternativeContents*>(tree)))
2089 {
2090 InfoLog( << "GREG3: " << *alt );
2091 for (MultipartAlternativeContents::Parts::reverse_iterator i = alt->parts().rbegin();
2092 i != alt->parts().rend(); ++i)
2093 {
2094 Contents* b = extractFromPkcs7Recurse(*i, signerAor, receiverAor, attributes, security);
2095 if (b)
2096 {
2097 return b;
2098 }
2099 }
2100 }
2101
2103 if ((mult = dynamic_cast<MultipartMixedContents*>(tree)))
2104 {
2105 InfoLog( << "GREG4: " << *mult );
2106 for (MultipartMixedContents::Parts::iterator i = mult->parts().begin();
2107 i != mult->parts().end(); ++i)
2108 {
2109 Contents* b = extractFromPkcs7Recurse(*i, signerAor, receiverAor,
2110 attributes, security);
2111 if (b)
2112 {
2113 return b;
2114 }
2115 };
2116
2117 return 0;
2118 }
2119
2120 return tree->clone();
2121}
2122
2125 Security& security)
2126{
2128 // .dlb. currently flattening SecurityAttributes?
2129 //attr->setIdentity(message.getIdentity());
2130 attr->setIdentity(message.header(h_From).uri().getAor());
2131 Contents *b = message.getContents();
2132 if (b)
2133 {
2134 Data fromAor(message.header(h_From).uri().getAor());
2135 Data toAor(message.header(h_To).uri().getAor());
2136 if (message.isRequest())
2137 {
2138 b = extractFromPkcs7Recurse(b, fromAor, toAor, attr, security);
2139 }
2140 else // its a response
2141 {
2142 b = extractFromPkcs7Recurse(b, toAor, fromAor, attr, security);
2143 }
2144 }
2145 std::unique_ptr<Contents> c(b);
2146 std::unique_ptr<SecurityAttributes> a(attr);
2147 return ContentsSecAttrs(std::move(c), std::move(a));
2148}
2149
2152 const std::set<int>* additionalTransactionTerminatingResponses)
2153{
2154 resip_assert(response.isResponse());
2155 int code = response.header(h_StatusLine).statusCode();
2156 resip_assert(code >= 400);
2157
2158 if (additionalTransactionTerminatingResponses &&
2159 (additionalTransactionTerminatingResponses->end() != additionalTransactionTerminatingResponses->find(code)))
2160 {
2162 }
2163
2164 switch(code)
2165 {
2166 case 404:
2167 case 410:
2168 case 416:
2169 case 480: // but maybe not, still not quite decided:
2170 case 481:
2171 case 482: // but maybe not, still not quite decided:
2172 case 484:
2173 case 485:
2174 case 502:
2175 case 604:
2176 return DialogTermination;
2177 case 403:
2178 case 489: //only for only subscription
2179 case 408: //again, maybe not. This seems best.
2180 return UsageTermination;
2181 case 400:
2182 case 401:
2183 case 402:
2184 case 405: //doesn't agree w/ -00 of dialogusage
2185 case 406:
2186 case 412:
2187 case 413:
2188 case 414:
2189 case 415:
2190 case 420:
2191 case 421:
2192 case 423:
2193
2194 case 429: // but if this the refer creating the Subscription, no sub will be created.
2195 case 486:
2196 case 487:
2197 case 488:
2198 case 491:
2199 case 493:
2200 case 494:
2201 case 500:
2202 case 505:
2203 case 513:
2204 case 603:
2205 case 606:
2207 case 483: // who knows, gravefully terminate or just destroy dialog
2208 case 501:
2209 return ApplicationDependant;
2210 default:
2211 if (code < 600)
2212 {
2213 if (response.exists(h_RetryAfter))
2214
2215 {
2216 return RetryAfter;
2217 }
2218 else
2219 {
2220 return OptionalRetryAfter;
2221 }
2222 }
2223 else
2224 {
2225 if (response.exists(h_RetryAfter))
2226 {
2227 return RetryAfter;
2228 }
2229 else
2230 {
2231 return ApplicationDependant;
2232 }
2233 }
2234 }
2235}
2236
2239{
2240 if (dynamic_cast<SdpContents*>(tree))
2241 {
2242 return static_cast<SdpContents*>(tree);
2243 }
2244
2246 if ((mps = dynamic_cast<MultipartSignedContents*>(tree)))
2247 {
2248 try
2249 {
2250 // Read the parts through the const overload. The mutable one runs the
2251 // non-const checkParsed(), which marks the body DIRTY, and this
2252 // function only reads. Once the body is DIRTY,
2253 // BaseSecurity::checkSignature() no longer has the bytes that arrived
2254 // and has to fall back to a re-encoding. DUM callers are shielded by
2255 // EncryptionManager running first, direct Helper::getSdp() callers are
2256 // not.
2257 const MultipartSignedContents* constMps = mps;
2258 const MultipartSignedContents::Parts& parts = constMps->parts();
2259
2260 // front() below needs a part to hand out.
2261 if (parts.empty())
2262 {
2263 return 0;
2264 }
2265
2266 Contents* contents = getSdpRecurse(parts.front());
2267 return static_cast<SdpContents*>(contents);
2268 }
2269 catch (ParseException& e)
2270 {
2271 ErrLog(<< e.name() << endl << e.getMessage());
2272 }
2273 catch (BaseException& e)
2274 {
2275 ErrLog(<< e.name() << endl << e.getMessage());
2276 }
2277
2278 return 0;
2279 }
2280
2282 if ((alt = dynamic_cast<MultipartAlternativeContents*>(tree)))
2283 {
2284 try
2285 {
2286 for (MultipartAlternativeContents::Parts::reverse_iterator i = alt->parts().rbegin();
2287 i != alt->parts().rend(); ++i)
2288 {
2289 Contents* contents = getSdpRecurse(*i);
2290 if (contents)
2291 {
2292 return static_cast<SdpContents*>(contents);
2293 }
2294 }
2295 }
2296 catch (ParseException& e)
2297 {
2298 ErrLog(<< e.name() << endl << e.getMessage());
2299 }
2300 catch (BaseException& e)
2301 {
2302 ErrLog(<< e.name() << endl << e.getMessage());
2303 }
2304
2305 return 0;
2306 }
2307
2309 if ((mult = dynamic_cast<MultipartMixedContents*>(tree)))
2310 {
2311
2312 try
2313 {
2314 for (MultipartMixedContents::Parts::iterator i = mult->parts().begin();
2315 i != mult->parts().end(); ++i)
2316 {
2317 Contents* contents = getSdpRecurse(*i);
2318 if (contents)
2319 {
2320 return static_cast<SdpContents*>(contents);
2321 }
2322 }
2323 }
2324 catch (ParseException& e)
2325 {
2326 ErrLog(<< e.name() << endl << e.getMessage());
2327 }
2328 catch (BaseException& e)
2329 {
2330 ErrLog(<< e.name() << endl << e.getMessage());
2331 }
2332
2333 return 0;
2334 }
2335
2336 return 0;
2337}
2338
2339unique_ptr<SdpContents> Helper::getSdp(Contents* tree)
2340{
2341 if (tree)
2342 {
2343 SdpContents* sdp = getSdpRecurse(tree);
2344
2345 if (sdp)
2346 {
2347 //DebugLog(<< "Got sdp");
2348 return unique_ptr<SdpContents>(static_cast<SdpContents*>(sdp->clone()));
2349 }
2350 }
2351
2352 //DebugLog(<< "No sdp");
2353 return nullptr;
2354}
2355
2356bool
2357Helper::isClientBehindNAT(const SipMessage& request, bool privateToPublicOnly)
2358{
2359 resip_assert(request.isRequest());
2360 resip_assert(!request.header(h_Vias).empty());
2361
2362 // If received parameter is on top Via, then the source of the message doesn't match
2363 // the address provided in the via. Assume this is because the sender is behind a NAT.
2364 // The assumption here is that this SipStack instance is the first hop in a public SIP server
2365 // architecture, and that clients are directly connected to this instance.
2366 if(request.header(h_Vias).front().exists(p_received))
2367 {
2368 if(privateToPublicOnly)
2369 {
2370 // Ensure the via host is an IP address (note: web-rtc uses hostnames here instead)
2371 if(DnsUtil::isIpV4Address(request.header(h_Vias).front().sentHost())
2372#ifdef USE_IPV6
2373 || DnsUtil::isIpV6Address(request.header(h_Vias).front().sentHost())
2374#endif
2375 )
2376 {
2377 if(Tuple(request.header(h_Vias).front().sentHost(), 0, UNKNOWN_TRANSPORT).isPrivateAddress() &&
2378 !Tuple(request.header(h_Vias).front().param(p_received), 0, UNKNOWN_TRANSPORT).isPrivateAddress())
2379 {
2380 return true;
2381 }
2382 else
2383 {
2384 return false;
2385 }
2386 }
2387 else
2388 {
2389 // In this case the via host is likely a hostname (possible with web-rtc) and we will assume the
2390 // client is behind a NAT, even though we don't know for sure
2391 return !Tuple(request.header(h_Vias).front().param(p_received), 0, UNKNOWN_TRANSPORT).isPrivateAddress();
2392 }
2393 }
2394 return true;
2395 }
2396 return false;
2397}
2398
2399Tuple
2401{
2402 resip_assert(request.isRequest());
2403 resip_assert(!request.header(h_Vias).empty());
2404
2405 // Iterate through Via's starting at the bottom (closest to the client). Return the first
2406 // public address found from received parameter if present, or Via host.
2407 Vias::const_iterator it = request.header(h_Vias).end();
2408 while(true)
2409 {
2410 it--;
2411 if(it->exists(p_received))
2412 {
2413 // Check IP from received parameter
2414 Tuple address(it->param(p_received), 0, UNKNOWN_TRANSPORT);
2415 if(!address.isPrivateAddress())
2416 {
2417 address.setPort(it->exists(p_rport) ? it->param(p_rport).port() : it->sentPort());
2418 address.setType(Tuple::toTransport(it->transport()));
2419 return address;
2420 }
2421 }
2422
2423 // Check IP from Via sentHost
2424 if(DnsUtil::isIpV4Address(it->sentHost()) // Ensure the via host is an IP address (note: web-rtc uses hostnames here instead)
2425#ifdef USE_IPV6
2426 || DnsUtil::isIpV6Address(it->sentHost())
2427#endif
2428 )
2429 {
2430 Tuple address(it->sentHost(), 0, UNKNOWN_TRANSPORT);
2431 if(!address.isPrivateAddress())
2432 {
2433 address.setPort(it->exists(p_rport) ? it->param(p_rport).port() : it->sentPort());
2434 address.setType(Tuple::toTransport(it->transport()));
2435 return address;
2436 }
2437 }
2438
2439 if(it == request.header(h_Vias).begin()) break;
2440 }
2441 return Tuple();
2442}
2443
2444
2445/* ====================================================================
2446 * The Vovida Software License, Version 1.0
2447 *
2448 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
2449 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
2450 *
2451 * Redistribution and use in source and binary forms, with or without
2452 * modification, are permitted provided that the following conditions
2453 * are met:
2454 *
2455 * 1. Redistributions of source code must retain the above copyright
2456 * notice, this list of conditions and the following disclaimer.
2457 *
2458 * 2. Redistributions in binary form must reproduce the above copyright
2459 * notice, this list of conditions and the following disclaimer in
2460 * the documentation and/or other materials provided with the
2461 * distribution.
2462 *
2463 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
2464 * and "Vovida Open Communication Application Library (VOCAL)" must
2465 * not be used to endorse or promote products derived from this
2466 * software without prior written permission. For written
2467 * permission, please contact vocal@vovida.org.
2468 *
2469 * 4. Products derived from this software may not be called "VOCAL", nor
2470 * may "VOCAL" appear in their name, without prior written
2471 * permission of Vovida Networks, Inc.
2472 *
2473 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
2474 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2475 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
2476 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
2477 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
2478 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
2479 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2480 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2481 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
2482 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2483 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
2484 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
2485 * DAMAGE.
2486 *
2487 * ====================================================================
2488 *
2489 * This software consists of voluntary contributions made by Vovida
2490 * Networks, Inc. and many individuals on behalf of Vovida Networks,
2491 * Inc. For more information on Vovida Networks, Inc., please see
2492 * <http://www.vovida.org/>.
2493 *
2494 */
static const Data GRUU("_GRUU")
static const Data pad("\0\0\0\0\0\0\0", 7)
static const Data cookie("z9hG4bK")
static const Data sep("[]")
static const int saltBytes(16)
static size_t pTokenSize
Definition Helper.cxx:1282
static Data preferredTokens[]
Definition Helper.cxx:1277
Contents * extractFromPkcs7Recurse(Contents *tree, const Data &signerAor, const Data &receiverAor, SecurityAttributes *attributes, Security &security)
Definition Helper.cxx:2047
static Data digest("digest")
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
Represents the "credentials" and "challenge" elements in the SIP grammar.
Definition Auth.hxx:18
bool exists(const Param< Auth > &paramType) const
Definition Auth.cxx:225
Data & scheme()
Definition Auth.cxx:46
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
The abstract base-class for all exceptions thrown by resip code.
const Data & getMessage() const noexcept
Contents * checkSignature(MultipartSignedContents *, Data *signedBy, SignatureStatus *sigStat)
Contents * decrypt(const Data &decryptorAor, const Pkcs7Contents *)
Implements the makeNonce function in the same way as the original implementation of makeNonce within ...
Base class for all SIP body types.
Definition Contents.hxx:51
virtual Contents * clone() const =0
returns a copy of a Contents object
An iostream that operates on an existing Data.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
Data base64decode() const
Performs RFC 3548 Base 64 decoding of the contents of this data.
Definition Data.cxx:2607
static const Data Empty
Constant that represents a zero-length data.
Definition Data.hxx:882
Data base64encode(bool useUrlSafe=false) const
Performs RFC 3548 Base 64 encoding of the contents of this data.
Definition Data.cxx:2682
static const PreallocateType Preallocate
used only to disambiguate constructors
Definition Data.hxx:116
bool prefix(const Data &pre) const noexcept
Returns true if this Data starts with the bytes indicated by the passed-in Data.
Definition Data.cxx:1888
Data & append(const char *str, size_type len)
Appends the specified number of bytes to the end of this Data.
Definition Data.cxx:1058
Data substr(size_type first, size_type count=Data::npos) const
Copies a portion of this Data into a new Data.
Definition Data.cxx:1932
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_t size_type
Definition Data.hxx:94
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
size_type find(const Data &match, size_type start=0) const
Finds a specified sequence of bytes in this Data.
Definition Data.cxx:1947
static const size_type npos
Represents an impossible position; returned to indicate failure to find.
Definition Data.hxx:887
Data md5(EncodingType type=HEX) const
Computes the MD5 hash of the current data.
Definition Data.cxx:1174
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
void reserve(size_type capacity)
Guarantees that the underlying buffer used by the Data is at least the number of bytes specified.
Definition Data.cxx:1049
Used to accumlate data written to the stream in a DigestBuffer and convert the data to Digest.
Data getHex()
Calls flush() on itself and returns the Digest data in hex format.
static const Data & getDigestName(DigestType digestType)
static bool isIpV6Address(const Data &ipAddress)
Definition DnsUtil.cxx:386
static const Data & getLocalHostName()
Definition DnsUtil.cxx:118
static bool isIpV4Address(const Data &ipAddress)
Definition DnsUtil.cxx:290
static Auth makeChallengeResponseAuthWithA1(const SipMessage &request, const Data &username, const Data &resipPasswordHashA1, const Auth &challenge, const Data &cnonce, unsigned int &nonceCount, Data &nonceCountString)
Definition Helper.cxx:1506
static SipMessage * makeChallenge(const SipMessage &request, const Data &realm, bool useAuth=true, bool stale=false, bool proxy=false, const std::vector< DigestType > &digestTypes=std::vector< DigestType >())
Definition Helper.cxx:1224
static Uri makeUri(const Data &aor, const Data &scheme=Symbols::DefaultSipScheme)
///////////////////////////////////////////////////////////////////////////////////////////
Definition Helper.cxx:1687
static SdpContents * getSdpRecurse(Contents *tree)
Definition Helper.cxx:2238
static AuthResult authenticateRequestWithA1(const SipMessage &request, const Data &realm, const Data &resipPasswordHashA1, int expiresDelta=0)
Definition Helper.cxx:884
static Uri fromAor(const Data &aor, const Data &scheme=Symbols::DefaultSipScheme)
Definition Helper.cxx:1785
static Data makeResponseDigestWithA1(const Data &a1, const Data &method, const Data &digestUri, const Data &nonce, const Data &qop=Data::Empty, const Data &cnonce=Data::Empty, const Data &cnonceCount=Data::Empty, const Contents *entityBody=0, DigestType digestType=MD5)
Definition Helper.cxx:1593
static void updateNonceCount(unsigned int &nonceCount, Data &nonceCountString)
Definition Helper.cxx:1325
static Data extractA1FromResipA1HashString(const Data &resipA1HashString, DigestType digestType)
Definition Helper.cxx:822
static const int tagSize
bytes in to-tag& from-tag, should prob. live somewhere else
Definition Helper.hxx:42
static SipMessage * makePublish(const NameAddr &target, const NameAddr &from)
Create a Publish request with an empty Contact. See makeRequest.
Definition Helper.cxx:322
static SipMessage * makeRegister(const NameAddr &to, const NameAddr &from)
Create a Register request with an empty Contact. See makeRequest.
Definition Helper.cxx:172
static Data gruuUserPart(const Data &instanceId, const Data &aor, const Data &key)
Definition Helper.cxx:1866
static bool validateMessage(const SipMessage &message, resip::Data *reason=0)
Definition Helper.cxx:1791
static bool isClientBehindNAT(const SipMessage &request, bool privateToPublicOnly=true)
Looks at SIP headers and message source for a mismatch to make an assumption that the sender is behin...
Definition Helper.cxx:2357
static bool algorithmAndQopSupported(const Auth &challenge)
Definition Helper.cxx:1347
static void getResponseCodeReason(int responseCode, Data &reason)
Returns the default reason string for a particular response code.
Definition Helper.cxx:610
static NonceHelper * getNonceHelper()
Definition Helper.cxx:725
static SipMessage * makeInvite(const NameAddr &target, const NameAddr &from)
Make an invite request - Empty Contact and Via is added and will be populated by the stack when sent.
Definition Helper.cxx:249
static void processStrictRoute(SipMessage &request)
Definition Helper.cxx:1701
static std::pair< Data, Data > fromGruuUserPart(const Data &gruuUserPart, const Data &key)
Definition Helper.cxx:1955
static SipMessage & addAuthorization(SipMessage &request, const SipMessage &challenge, const Data &username, const Data &password, const Data &cnonce, unsigned int &nonceCount)
Definition Helper.cxx:1368
static Data createResipA1HashString(const Data &username, const Data &realm, const Data &password)
Definition Helper.cxx:783
static void setNonceHelper(NonceHelper *nonceHelper)
///////////////////////////////////////////////////////////////////////////////////////////
Definition Helper.cxx:719
static void massageRoute(const SipMessage &request, NameAddr &route)
Definition Helper.cxx:1719
static SipMessage * makeProxyChallenge(const SipMessage &request, const Data &realm, bool useAuth=true, bool stale=false, const std::vector< DigestType > &digestTypes=std::vector< DigestType >())
Definition Helper.cxx:1212
static SipMessage * makeMessage(const NameAddr &target, const NameAddr &from)
Create a Message request with an empty Contact. See makeRequest.
Definition Helper.cxx:352
static unsigned int hex2integer(const char *_s)
Converts a character string containing a hexidecimal value into an unsigned int.
Definition Helper.cxx:92
static Data computeTag(int numBytes)
Definition Helper.cxx:713
static std::unique_ptr< SdpContents > getSdp(Contents *tree)
Definition Helper.cxx:2339
static Data qopOption(const Auth &challenge)
Definition Helper.cxx:1284
static SipMessage * makeCancel(const SipMessage &request)
Make a new Cancel request for the specified request.
Definition Helper.cxx:261
static void addChallenge(SipMessage &response, const SipMessage &request, const Data &realm, bool useAuth=true, bool stale=false, bool proxy=false, DigestType digestType=MD5)
Definition Helper.cxx:1250
static int getPortForReply(SipMessage &request)
Definition Helper.cxx:1741
static bool isDigestTypeSupportedInResipA1HashString(const Data &resipA1HashString, DigestType digestType)
Definition Helper.cxx:856
static SipMessage * make405(const SipMessage &request, const int *allowedMethods=0, int nMethods=-1)
Make a 405 response to a provided request.
Definition Helper.cxx:573
static ContentsSecAttrs extractFromPkcs7(const SipMessage &message, Security &security)
Definition Helper.cxx:2124
static void makeResponse(SipMessage &response, const SipMessage &request, int responseCode, const Data &reason=Data::Empty, const Data &hostname=Data::Empty, const Data &warning=Data::Empty)
Make a response to a provided request.
Definition Helper.cxx:412
@ TransactionTermination
Definition Helper.hxx:614
@ ApplicationDependant
Definition Helper.hxx:615
static Tuple getClientPublicAddress(const SipMessage &request)
Look at Via headers, and find the first public IP address closest to the sending client.
Definition Helper.cxx:2400
static int jitterValue(int input, int lowerPercentage, int upperPercentage, int minimum=0)
Used to jitter the expires in a SUBSCRIBE or REGISTER expires header.
Definition Helper.cxx:123
static void makeRawResponse(Data &rawBuffer, const SipMessage &request, int responseCode, const Data &additionalHeaders=Data::Empty, const Data &body=Data::Empty)
Definition Helper.cxx:549
static void integer2hex(char *_d, unsigned int _s, bool _l=true)
Converts an interger in a character string containing the hexidecimal representation of the integer.
Definition Helper.cxx:52
static SipMessage * makeWWWChallenge(const SipMessage &request, const Data &realm, bool useAuth=true, bool stale=false, const std::vector< DigestType > &digestTypes=std::vector< DigestType >())
Definition Helper.cxx:1218
static SipMessage * makeRequest(const NameAddr &target, const NameAddr &from, const NameAddr &contact, MethodTypes method)
///////////////////////////////////////////////////////////////////////////////////////////
Definition Helper.cxx:142
static AuthResult authenticateRequest(const SipMessage &request, const Data &realm, const Data &password, int expiresDelta=0)
Definition Helper.cxx:875
static std::pair< AuthResult, Data > advancedAuthenticateRequest(const SipMessage &request, const Data &realm, const Data &resipPasswordHashA1, int expiresDelta=0, bool proxyAuthorization=true)
Definition Helper.cxx:1071
static Data makeResponseDigest(const Data &username, const Data &password, const Data &realm, const Data &method, const Data &digestUri, const Data &nonce, const Data &qop=Data::Empty, const Data &cnonce=Data::Empty, const Data &cnonceCount=Data::Empty, const Contents *entityBody=0, DigestType digestType=MD5)
Definition Helper.cxx:1670
static FailureMessageEffect determineFailureMessageEffect(const SipMessage &response, const std::set< int > *additionalTransactionTerminatingResponses=NULL)
Definition Helper.cxx:2151
static bool isDigestAlgorithmSupported(const Auth &auth, DigestType &digestType)
Definition Helper.cxx:742
static Data computeUniqueBranch()
Creates and returns a unique branch parameter.
Definition Helper.cxx:679
static SipMessage * makeFailureAck(const SipMessage &request, const SipMessage &response)
This interface should be used by the stack (TransactionState) to create an AckMsg to a failure respon...
Definition Helper.cxx:296
static Auth makeChallengeResponseAuth(const SipMessage &request, const Data &username, const Data &password, const Auth &challenge, const Data &cnonce, unsigned int &nonceCount, Data &nonceCountString)
Definition Helper.cxx:1415
static Data makeNonce(const SipMessage &request, const Data &timestamp)
Definition Helper.cxx:736
static SipMessage * makeSubscribe(const NameAddr &target, const NameAddr &from)
Create a Subscribe request with an empty Contact. See makeRequest.
Definition Helper.cxx:382
static Data computeCallId()
Definition Helper.cxx:690
bool isWellFormed() const
Returns true iff this element was parsed successfully, according to the implementation of parse().
virtual Brief brief() const
return a facet for brief encoding of message
Definition Message.cxx:16
SIP body type for holding Multipart-Alternative body contents (MIME content-type multipart/alternativ...
SIP body type for holding Multipart-Mixed body contents (MIME content-type multipart/mixed).
SIP body type for holding Multipart-Signed body contents (MIME content-type multipart/signed).
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
Definition NameAddr.hxx:19
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
virtual NonceHelper::Nonce parseNonce(const Data &nonce)=0
virtual Data makeNonce(const SipMessage &request, const Data &timestamp)=0
Provides string-parsing functionality with protection from buffer overflow.
CurrentPosition skipWhitespace()
CurrentPosition skipToChars(const char *cs)
CurrentPosition skipToChar(char c)
CurrentPosition position() const
CurrentPosition skipN(size_t count)
CurrentPosition skipChar()
void data(Data &data, const char *start) const
make the passed in data share memory with the buffer (uses Data::Share)
BaseException used to indicate parse failures.
const char * name() const noexcept override
bool empty() const
return true or false indicating whether the mParsers vector is empty or not
void append(const ParserContainerBase &rhs)
append the vector to the mParsers vector held locally
A const_iterator class (bidirectional)
An iterator class (bidirectional)
Container class for ParserCategory, used by SipMessage to represent multi-valued headers (Contact,...
iterator end()
Returns an iterator pointing to the last header field value.
iterator begin()
Returns an iterator pointing to the first header field value.
void push_back(const T &t)
Inserts a header field value at the back of this container.
SIP body type for holding PKCS7 contents (MIME content-type application/pkcs7-mime).
static int getRandom()
Returns a postive integer (31 bits) of randomness.
Definition Random.cxx:261
static Data getCryptoRandomHex(unsigned int numBytes)
Definition Random.cxx:394
Represents the "Request-Line" element in the RFC 3261 grammar.
const Data & getSipVersion() const
MethodTypes getMethod() const
const Uri & uri() const
MethodTypes & method()
Provides an interface to process and generate SDP bodies (MIME content-type application/sdp).
virtual Contents * clone() const
duplicate an SdpContents object
void setIdentity(const Data &identity)
void setSignatureStatus(SignatureStatus status)
void setSigner(const Data &signer)
Represents a SIP message.
void setRFC2543TransactionId(const Data &tid)
bool isRequest() const
Returns true if message is a request, false otherwise.
const Data & getRFC2543TransactionId() const
Calculates an MD5 hash over the Request-URI, To tag (for non-INVITE transactions),...
bool hasForceTarget() const
Contents * getContents() const
Retrieves the body of a SIP message.
void setForceTarget(const Uri &uri)
deal with a notion of an "out-of-band" forced target for SIP routing
bool empty(const HeaderBase &headerType) const
Returns true if the header field is present and non-empty, false otherwise.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
bool isResponse() const
Returns true if message is a response, false otherwise.
const RequestLine & const_header(const RequestLineType &l) const
EncodeStream & encodeSingleHeader(Headers::Type type, EncodeStream &str) const
void setFromExternal()
Mark message as externally generated.
bool exists(const HeaderBase &headerType) const
Returns true if the given header field is present, false otherwise.
const Tuple & getSource() const
Returns the source tuple that the message was received from only makes sense for messages received fr...
bool isExternal() const
Check if SipMessage is to be treated as it came off the wire.
void setFromTU()
Mark message as internally generated.
static const char * auth
Definition Symbols.hxx:72
static const int DefaultSipsPort
Definition Symbols.hxx:85
static const char * authInt
Definition Symbols.hxx:73
static const char * COLON
Definition Symbols.hxx:15
static const int DefaultSipPort
Definition Symbols.hxx:81
static const char * COMMA
Definition Symbols.hxx:35
static const char * Digest
Definition Symbols.hxx:74
static const char * CRLF
Definition Symbols.hxx:21
static uint64_t getTimeSecs()
Returns the current clock time in seconds.
Definition Timer.hxx:78
Represents the "token" element in the RFC 3261 grammar.
Definition Token.hxx:18
const Data & value() const
Gets the value (ie; no parameters) of this Token as a Data&.
Definition Token.cxx:77
Represents a network address.
Definition Tuple.hxx:59
void setPort(int port)
Definition Tuple.cxx:449
bool isPrivateAddress() const
Definition Tuple.cxx:552
void setType(TransportType type)
Definition Tuple.hxx:123
int getPort() const
Definition Tuple.cxx:466
static TransportType toTransport(const Data &)
Converts a string representation of transport type, i.e.
Definition Tuple.cxx:921
Represents the "SIP-URI" and "SIPS-URI" elements in the RFC 3261 grammar.
Definition Uri.hxx:22
Data getAor() const
Definition Uri.cxx:1095
int & port()
Definition Uri.hxx:145
Data & scheme()
Definition Uri.hxx:143
bool exists(const Param< Uri > &paramType) const
Definition Uri.cxx:1701
Data & host()
Definition Uri.hxx:40
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
Represents the "via-parm" element in the RFC 3261 grammar.
Definition Via.hxx:18
Represents the "warning-value" element in the RFC 3261 grammar.
This file is used to handle compatibility fixes/tweaks so reSIProcate can function on multiple platfo...
dcm! – add UnusedChecking(_enum) below;
StatusLineType h_StatusLine
Definition Headers.cxx:348
bool isEqualNoCase(const Data &left, const Data &right)
Definition Data.hxx:1116
TransportType toTransportType(const resip::Data &transportName)
unsigned long long uint64_t
Definition Stun.hxx:39
const Data & getMethodName(MethodTypes t)
TransportType
An enumeration of transport protocols.
@ UNKNOWN_TRANSPORT
RequestLineType h_RequestLine
Definition Headers.cxx:347
@ MAX_METHODS
bool isReliable(TransportType type)
Returns true if passed in transport type is a reliable transport protocol.
H_CallId h_CallId
Definition Headers.cxx:266