Coverage Report

Created: 2026-02-13 06:31

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