/[resiprocate]/branches/b-identity-0505/Helper.cxx
ViewVC logotype

Contents of /branches/b-identity-0505/Helper.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4598 - (show annotations) (download)
Wed May 11 22:53:07 2005 UTC (14 years, 6 months ago) by derek
File size: 50917 byte(s)
set svn:eol-style to LF
1 #if defined(HAVE_CONFIG_H)
2 #include "resiprocate/config.hxx"
3 #endif
4
5 #include <string.h>
6 #include <iomanip>
7 #include <algorithm>
8 #include <memory>
9
10 #include "resiprocate/Helper.hxx"
11 #include "resiprocate/os/Coders.hxx"
12 #include "resiprocate/Uri.hxx"
13 #include "resiprocate/os/Logger.hxx"
14 #include "resiprocate/os/Random.hxx"
15 #include "resiprocate/os/Timer.hxx"
16 #include "resiprocate/os/DataStream.hxx"
17 #include "resiprocate/os/MD5Stream.hxx"
18 #include "resiprocate/os/DnsUtil.hxx"
19 #include "resiprocate/os/compat.hxx"
20 #include "resiprocate/os/ParseBuffer.hxx"
21 #include "resiprocate/SipMessage.hxx"
22 #include "resiprocate/Security.hxx"
23 #include "resiprocate/SecurityAttributes.hxx"
24 #include "resiprocate/Pkcs7Contents.hxx"
25 #include "resiprocate/MultipartSignedContents.hxx"
26 #include "resiprocate/MultipartMixedContents.hxx"
27 #include "resiprocate/MultipartAlternativeContents.hxx"
28 #include "resiprocate/os/WinLeakCheck.hxx"
29
30 using namespace resip;
31 using namespace std;
32
33 #define RESIPROCATE_SUBSYSTEM Subsystem::SIP
34
35 const int Helper::tagSize = 4;
36
37 SipMessage*
38 Helper::makeRequest(const NameAddr& target, const NameAddr& from, const NameAddr& contact, MethodTypes method)
39 {
40 SipMessage* request = new SipMessage;
41 RequestLine rLine(method);
42 rLine.uri() = target.uri();
43 request->header(h_To) = target;
44 request->header(h_RequestLine) = rLine;
45 request->header(h_MaxForwards).value() = 70;
46 request->header(h_CSeq).method() = method;
47 request->header(h_CSeq).sequence() = 1;
48 request->header(h_From) = from;
49 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
50 request->header(h_Contacts).push_back(contact);
51 request->header(h_CallId).value() = Helper::computeCallId();
52 //request->header(h_ContentLength).value() = 0;
53
54 Via via;
55 request->header(h_Vias).push_back(via);
56
57 return request;
58 }
59
60 SipMessage*
61 Helper::makeRequest(const NameAddr& target, const NameAddr& from, MethodTypes method)
62 {
63 NameAddr contact;
64 return makeRequest(target, from, contact, method);
65 }
66
67 SipMessage*
68 Helper::makeRegister(const NameAddr& to, const NameAddr& from)
69 {
70 NameAddr contact;
71 return makeRegister(to, from, contact);
72 }
73
74 SipMessage*
75 Helper::makeRegister(const NameAddr& to, const NameAddr& from, const NameAddr& contact)
76 {
77 SipMessage* request = new SipMessage;
78 RequestLine rLine(REGISTER);
79
80 rLine.uri().scheme() = to.uri().scheme();
81 rLine.uri().host() = to.uri().host();
82 rLine.uri().port() = to.uri().port();
83 if (to.uri().exists(p_transport))
84 {
85 rLine.uri().param(p_transport) = to.uri().param(p_transport);
86 }
87
88 request->header(h_To) = to;
89 request->header(h_RequestLine) = rLine;
90 request->header(h_MaxForwards).value() = 70;
91 request->header(h_CSeq).method() = REGISTER;
92 request->header(h_CSeq).sequence() = 1;
93 request->header(h_From) = from;
94 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
95 request->header(h_CallId).value() = Helper::computeCallId();
96 assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
97 request->header(h_Contacts).push_back( contact );
98
99 Via via;
100 request->header(h_Vias).push_back(via);
101
102 return request;
103 }
104
105 SipMessage*
106 Helper::makeRegister(const NameAddr& to,const Data& transport)
107 {
108 NameAddr contact;
109 return makeRegister(to, transport, contact);
110
111 }
112
113 SipMessage*
114 Helper::makeRegister(const NameAddr& to, const Data& transport, const NameAddr& contact)
115 {
116 SipMessage* request = new SipMessage;
117 RequestLine rLine(REGISTER);
118
119 rLine.uri().scheme() = to.uri().scheme();
120 rLine.uri().host() = to.uri().host();
121 rLine.uri().port() = to.uri().port();
122 if (!transport.empty())
123 {
124 rLine.uri().param(p_transport) = transport;
125 }
126
127 request->header(h_To) = to;
128 request->header(h_RequestLine) = rLine;
129 request->header(h_MaxForwards).value() = 70;
130 request->header(h_CSeq).method() = REGISTER;
131 request->header(h_CSeq).sequence() = 1;
132 request->header(h_From) = to;
133 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
134 request->header(h_CallId).value() = Helper::computeCallId();
135 assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
136 request->header(h_Contacts).push_back( contact );
137
138 Via via;
139 request->header(h_Vias).push_back(via);
140
141 return request;
142 }
143
144
145 SipMessage*
146 Helper::makePublish(const NameAddr& target, const NameAddr& from)
147 {
148 NameAddr contact;
149 return makePublish(target, from, contact);
150 }
151
152 SipMessage*
153 Helper::makePublish(const NameAddr& target, const NameAddr& from, const NameAddr& contact)
154 {
155 SipMessage* request = new SipMessage;
156 RequestLine rLine(PUBLISH);
157 rLine.uri() = target.uri();
158
159 request->header(h_To) = target;
160 request->header(h_RequestLine) = rLine;
161 request->header(h_MaxForwards).value() = 70;
162 request->header(h_CSeq).method() = PUBLISH;
163 request->header(h_CSeq).sequence() = 1;
164 request->header(h_From) = from;
165 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
166 request->header(h_CallId).value() = Helper::computeCallId();
167 assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
168 request->header(h_Contacts).push_back( contact );
169 Via via;
170 request->header(h_Vias).push_back(via);
171
172 return request;
173 }
174
175 SipMessage*
176 Helper::makeMessage(const NameAddr& target, const NameAddr& from)
177 {
178 NameAddr contact;
179 return makeMessage(target, from, contact);
180 }
181
182 SipMessage*
183 Helper::makeMessage(const NameAddr& target, const NameAddr& from, const NameAddr& contact)
184 {
185 SipMessage* request = new SipMessage;
186 RequestLine rLine(MESSAGE);
187 rLine.uri() = target.uri();
188
189 request->header(h_To) = target;
190 request->header(h_RequestLine) = rLine;
191 request->header(h_MaxForwards).value() = 70;
192 request->header(h_CSeq).method() = MESSAGE;
193 request->header(h_CSeq).sequence() = 1;
194 request->header(h_From) = from;
195 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
196 request->header(h_CallId).value() = Helper::computeCallId();
197 assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
198 request->header(h_Contacts).push_back( contact );
199 Via via;
200 request->header(h_Vias).push_back(via);
201
202 return request;
203 }
204
205
206 SipMessage*
207 Helper::makeSubscribe(const NameAddr& target, const NameAddr& from)
208 {
209 NameAddr contact;
210 return makeSubscribe(target, from, contact);
211 }
212
213 SipMessage*
214 Helper::makeSubscribe(const NameAddr& target, const NameAddr& from, const NameAddr& contact)
215 {
216 SipMessage* request = new SipMessage;
217 RequestLine rLine(SUBSCRIBE);
218 rLine.uri() = target.uri();
219
220 request->header(h_To) = target;
221 request->header(h_RequestLine) = rLine;
222 request->header(h_MaxForwards).value() = 70;
223 request->header(h_CSeq).method() = SUBSCRIBE;
224 request->header(h_CSeq).sequence() = 1;
225 request->header(h_From) = from;
226 request->header(h_From).param(p_tag) = Helper::computeTag(Helper::tagSize);
227 request->header(h_CallId).value() = Helper::computeCallId();
228 assert(!request->exists(h_Contacts) || request->header(h_Contacts).empty());
229 request->header(h_Contacts).push_front( contact );
230 Via via;
231 request->header(h_Vias).push_front(via);
232
233 return request;
234 }
235
236 int
237 Helper::jitterValue(int input, int lowerPercentage, int upperPercentage, int minimum)
238 {
239 assert(upperPercentage >= lowerPercentage);
240 if (input < minimum)
241 {
242 return input;
243 }
244 else if (lowerPercentage == 100 && upperPercentage == 100)
245 {
246 return input;
247 }
248 else
249 {
250 const int rnd = Random::getRandom() % (upperPercentage-lowerPercentage) + lowerPercentage;
251 return (input * rnd) / 100;
252 }
253 }
254
255 SipMessage*
256 Helper::makeInvite(const NameAddr& target, const NameAddr& from)
257 {
258 return Helper::makeRequest(target, from, INVITE);
259 }
260
261 SipMessage*
262 Helper::makeInvite(const NameAddr& target, const NameAddr& from, const NameAddr& contact)
263 {
264 return Helper::makeRequest(target, from, contact, INVITE);
265 }
266
267
268 void
269 Helper::makeResponse(SipMessage& response,
270 const SipMessage& request,
271 int responseCode,
272 const NameAddr& myContact,
273 const Data& reason,
274 const Data& hostname,
275 const Data& warning)
276 {
277 makeResponse(response,request, responseCode, reason,hostname, warning);
278 // in general, this should not create a Contact header since only requests
279 // that create a dialog (or REGISTER requests) should produce a response with
280 // a contact(s).
281 response.header(h_Contacts).clear();
282 response.header(h_Contacts).push_back(myContact);
283 }
284
285
286 void
287 Helper::makeResponse(SipMessage& response,
288 const SipMessage& request,
289 int responseCode,
290 const Data& reason,
291 const Data& hostname,
292 const Data& warning)
293 {
294 DebugLog(<< "Helper::makeResponse(" << request.brief() << " code=" << responseCode << " reason=" << reason);
295 response.header(h_StatusLine).responseCode() = responseCode;
296 response.header(h_From) = request.header(h_From);
297 response.header(h_To) = request.header(h_To);
298 response.header(h_CallId) = request.header(h_CallId);
299 response.header(h_CSeq) = request.header(h_CSeq);
300 response.header(h_Vias) = request.header(h_Vias);
301
302 if (!warning.empty())
303 {
304 WarningCategory warn;
305 warn.code() = 499;
306 warn.hostname() = hostname;
307 warn.text() = warning;
308 response.header(h_Warnings).push_back(warn);
309 }
310
311 // Only generate a To: tag if one doesn't exist. Think Re-INVITE.
312 // No totag for failure responses or 100s
313 if (!response.header(h_To).exists(p_tag) && responseCode > 100)
314 {
315 response.header(h_To).param(p_tag) = Helper::computeTag(Helper::tagSize);
316 }
317
318 response.setRFC2543TransactionId(request.getRFC2543TransactionId());
319 //response.header(h_ContentLength).value() = 0;
320
321 if (responseCode >= 180 && responseCode < 300 && request.exists(h_RecordRoutes))
322 {
323 response.header(h_RecordRoutes) = request.header(h_RecordRoutes);
324 }
325
326 if (responseCode/100 == 2)
327 {
328 // in general, this should not create a Contact header since only requests
329 // that create a dialog (or REGISTER requests) should produce a response with
330 // a contact(s).
331
332 NameAddr contact;
333 response.header(h_Contacts).push_back(contact);
334 }
335
336 if (request.isExternal())
337 {
338 response.setFromTU();
339 }
340 else
341 {
342 response.setFromExternal();
343 }
344
345 if (reason.size())
346 {
347 response.header(h_StatusLine).reason() = reason;
348 }
349 else
350 {
351 Data &reason(response.header(h_StatusLine).reason());
352 switch (responseCode)
353 {
354 case 100: reason = "Trying"; break;
355 case 180: reason = "Ringing"; break;
356 case 181: reason = "Call Is Being Forwarded"; break;
357 case 182: reason = "Queued"; break;
358 case 183: reason = "Session Progress"; break;
359 case 200: reason = "OK"; break;
360 case 202: reason = "Accepted"; break;
361 case 300: reason = "Multiple Choices"; break;
362 case 301: reason = "Moved Permanently"; break;
363 case 302: reason = "Moved Temporarily"; break;
364 case 305: reason = "Use Proxy"; break;
365 case 380: reason = "Alternative Service"; break;
366 case 400: reason = "Bad Request"; break;
367 case 401: reason = "Unauthorized"; break;
368 case 402: reason = "Payment Required"; break;
369 case 403: reason = "Forbidden"; break;
370 case 404: reason = "Not Found"; break;
371 case 405: reason = "Method Not Allowed"; break;
372 case 406: reason = "Not Acceptable"; break;
373 case 407: reason = "Proxy Authentication Required"; break;
374 case 408: reason = "Request Timeout"; break;
375 case 410: reason = "Gone"; break;
376 case 412: reason = "Precondition Failed"; break;
377 case 413: reason = "Request Entity Too Large"; break;
378 case 414: reason = "Request-URI Too Long"; break;
379 case 415: reason = "Unsupported Media Type"; break;
380 case 416: reason = "Unsupported URI Scheme"; break;
381 case 420: reason = "Bad Extension"; break;
382 case 421: reason = "Extension Required"; break;
383 case 422: reason = "Session Interval Too Small"; break;
384 case 423: reason = "Interval Too Brief"; break;
385 case 480: reason = "Temporarily Unavailable"; break;
386 case 481: reason = "Call/Transaction Does Not Exist"; break;
387 case 482: reason = "Loop Detected"; break;
388 case 483: reason = "Too Many Hops"; break;
389 case 484: reason = "Address Incomplete"; break;
390 case 485: reason = "Ambiguous"; break;
391 case 486: reason = "Busy Here"; break;
392 case 487: reason = "Request Terminated"; break;
393 case 488: reason = "Not Acceptable Here"; break;
394 case 489: reason = "Event Package Not Supported"; break;
395 case 491: reason = "Request Pending"; break;
396 case 493: reason = "Undecipherable"; break;
397 case 500: reason = "Server Internal Error"; break;
398 case 501: reason = "Not Implemented"; break;
399 case 502: reason = "Bad Gateway"; break;
400 case 503: reason = "Service Unavailable"; break;
401 case 504: reason = "Server Time-out"; break;
402 case 505: reason = "Version Not Supported"; break;
403 case 513: reason = "Message Too Large"; break;
404 case 600: reason = "Busy Everywhere"; break;
405 case 603: reason = "Decline"; break;
406 case 604: reason = "Does Not Exist Anywhere"; break;
407 case 606: reason = "Not Acceptable"; break;
408 }
409 }
410 }
411
412 SipMessage*
413 Helper::makeResponse(const SipMessage& request,
414 int responseCode,
415 const NameAddr& myContact,
416 const Data& reason,
417 const Data& hostname,
418 const Data& warning)
419 {
420 SipMessage* response = new SipMessage;
421 makeResponse(*response, request, responseCode, reason, hostname, warning);
422
423 // in general, this should not create a Contact header since only requests
424 // that create a dialog (or REGISTER requests) should produce a response with
425 // a contact(s).
426 response->header(h_Contacts).clear();
427 response->header(h_Contacts).push_back(myContact);
428 return response;
429 }
430
431
432 SipMessage*
433 Helper::makeResponse(const SipMessage& request,
434 int responseCode,
435 const Data& reason,
436 const Data& hostname,
437 const Data& warning)
438 {
439 SipMessage* response = new SipMessage;
440 makeResponse(*response, request, responseCode, reason, hostname, warning);
441 return response;
442 }
443
444 SipMessage*
445 Helper::makeCancel(const SipMessage& request)
446 {
447 assert(request.isRequest());
448 assert(request.header(h_RequestLine).getMethod() == INVITE);
449 SipMessage* cancel = new SipMessage;
450
451 RequestLine rLine(CANCEL, request.header(h_RequestLine).getSipVersion());
452 rLine.uri() = request.header(h_RequestLine).uri();
453 cancel->header(h_RequestLine) = rLine;
454 cancel->header(h_To) = request.header(h_To);
455 cancel->header(h_From) = request.header(h_From);
456 cancel->header(h_CallId) = request.header(h_CallId);
457 if (request.exists(h_ProxyAuthorizations))
458 {
459 cancel->header(h_ProxyAuthorizations) = request.header(h_ProxyAuthorizations);
460 }
461 if (request.exists(h_Authorizations))
462 {
463 cancel->header(h_Authorizations) = request.header(h_Authorizations);
464 }
465
466 if (request.exists(h_Routes))
467 {
468 cancel->header(h_Routes) = request.header(h_Routes);
469 }
470
471 cancel->header(h_CSeq) = request.header(h_CSeq);
472 cancel->header(h_CSeq).method() = CANCEL;
473 cancel->header(h_Vias).push_back(request.header(h_Vias).front());
474
475 return cancel;
476 }
477
478
479 // This interface should be used by the stack (TransactionState) to create an
480 // AckMsg to a failure response
481 // See RFC3261 section 17.1.1.3
482 // Note that the branch in this ACK needs to be the
483 // For TU generated ACK, see Dialog::makeAck(...)
484 SipMessage*
485 Helper::makeFailureAck(const SipMessage& request, const SipMessage& response)
486 {
487 assert (request.header(h_Vias).size() >= 1);
488 assert (request.header(h_RequestLine).getMethod() == INVITE);
489
490 SipMessage* ack = new SipMessage;
491
492 RequestLine rLine(ACK, request.header(h_RequestLine).getSipVersion());
493 rLine.uri() = request.header(h_RequestLine).uri();
494 ack->header(h_RequestLine) = rLine;
495
496 ack->header(h_CallId) = request.header(h_CallId);
497 ack->header(h_From) = request.header(h_From);
498 ack->header(h_To) = response.header(h_To); // to get to-tag
499 ack->header(h_Vias).push_back(request.header(h_Vias).front());
500 ack->header(h_CSeq) = request.header(h_CSeq);
501 ack->header(h_CSeq).method() = ACK;
502 if (request.exists(h_Routes))
503 {
504 ack->header(h_Routes) = request.header(h_Routes);
505 }
506
507 return ack;
508 }
509
510
511 Data
512 Helper::computeUniqueBranch()
513 {
514 static const Data cookie("z9hG4bK"); // magic cookie per rfc2543bis-09
515
516 Data result(16, true);
517 result += cookie;
518 result += Random::getRandomHex(4);
519 result += "C1";
520 result += Random::getRandomHex(2);
521 return result;
522 }
523
524
525 Data
526 Helper::computeCallId()
527 {
528 // !jf! need to include host as well (should cache it)
529 static Data hostname = Symbols::AT_SIGN + DnsUtil::getLocalHostName().base64encode(true);
530 return Random::getRandomHex(8) + hostname;
531 }
532
533
534 Data
535 Helper::computeTag(int numBytes)
536 {
537 return Random::getRandomHex(4);
538 }
539
540 // !jf! this should be settable by the application in case a group of apps
541 // (e.g. proxies) want to share the same secret
542 static Data privateKey("asdfklsadflkj"); // Random::getRandomHex(1));
543
544 Data
545 Helper::makeNonce(const SipMessage& request, const Data& timestamp)
546 {
547 Data nonce(100, true);
548 nonce += timestamp;
549 nonce += Symbols::COLON;
550 Data noncePrivate(100, true);
551 noncePrivate += timestamp;
552 noncePrivate += Symbols::COLON;
553 //!jf! don't include the Call-Id since it might not be the same.
554 //noncePrivate += request.header(h_CallId).value();
555 noncePrivate += request.header(h_From).uri().user();
556 noncePrivate += privateKey;
557 nonce += noncePrivate.md5();
558 return nonce;
559 }
560
561 Data
562 Helper::makeResponseMD5WithA1(const Data& a1,
563 const Data& method, const Data& digestUri, const Data& nonce,
564 const Data& qop, const Data& cnonce, const Data& cnonceCount,
565 const Contents* entityBody)
566 {
567 MD5Stream a2;
568 a2 << method
569 << Symbols::COLON
570 << digestUri;
571
572 if (qop == Symbols::authInt)
573 {
574 if (entityBody)
575 {
576 MD5Stream eStream;
577 eStream << *entityBody;
578 a2 << Symbols::COLON << eStream.getHex();
579 }
580 else
581 {
582 static Data noBody = MD5Stream().getHex();
583 a2 << Symbols::COLON << noBody;
584 }
585 }
586
587 MD5Stream r;
588 r << a1
589 << Symbols::COLON
590 << nonce
591 << Symbols::COLON;
592
593 if (!qop.empty())
594 {
595 r << cnonceCount
596 << Symbols::COLON
597 << cnonce
598 << Symbols::COLON
599 << qop
600 << Symbols::COLON;
601 }
602 r << a2.getHex();
603
604 return r.getHex();
605 }
606
607 //RFC 2617 3.2.2.1
608 Data
609 Helper::makeResponseMD5(const Data& username, const Data& password, const Data& realm,
610 const Data& method, const Data& digestUri, const Data& nonce,
611 const Data& qop, const Data& cnonce, const Data& cnonceCount,
612 const Contents *entity)
613 {
614 MD5Stream a1;
615 a1 << username
616 << Symbols::COLON
617 << realm
618 << Symbols::COLON
619 << password;
620 a1.flush();
621
622 return makeResponseMD5WithA1(a1.getHex(), method, digestUri, nonce, qop,
623 cnonce, cnonceCount, entity);
624 }
625
626 std::pair<Helper::AuthResult,Data>
627 Helper::advancedAuthenticateRequest(const SipMessage& request,
628 const Data& realm,
629 const Data& a1,
630 int expiresDelta)
631 {
632 Data username;
633 DebugLog(<< "Authenticating: realm=" << realm << " expires=" << expiresDelta);
634 //DebugLog(<< request);
635
636 if (request.exists(h_ProxyAuthorizations))
637 {
638 const ParserContainer<Auth>& auths = request.header(h_ProxyAuthorizations);
639 for (ParserContainer<Auth>::const_iterator i = auths.begin(); i != auths.end(); i++)
640 {
641 if (i->exists(p_realm) &&
642 i->exists(p_nonce) &&
643 i->exists(p_response) &&
644 i->param(p_realm) == realm)
645 {
646 ParseBuffer pb(i->param(p_nonce).data(), i->param(p_nonce).size());
647 if (!pb.eof() && !isdigit(*pb.position()))
648 {
649 DebugLog(<< "Invalid nonce; expected timestamp.");
650 return make_pair(BadlyFormed,username);
651 }
652 const char* anchor = pb.position();
653 pb.skipToChar(Symbols::COLON[0]);
654
655 if (pb.eof())
656 {
657 DebugLog(<< "Invalid nonce; expected timestamp terminator.");
658 return make_pair(BadlyFormed,username);
659 }
660
661 Data then;
662 pb.data(then, anchor);
663 if (expiresDelta > 0)
664 {
665 int now = (int)(Timer::getTimeMs()/1000);
666 if (then.convertInt() + expiresDelta < now)
667 {
668 DebugLog(<< "Nonce has expired.");
669 return make_pair(BadlyFormed,username);
670 }
671 }
672 if (i->param(p_nonce) != makeNonce(request, then))
673 {
674 InfoLog(<< "Not my nonce.");
675 return make_pair(Failed,username);
676 }
677
678 if (i->exists(p_qop))
679 {
680 if (i->param(p_qop) == Symbols::auth || i->param(p_qop) == Symbols::authInt)
681 {
682 if (i->param(p_response) == makeResponseMD5WithA1(a1,
683 getMethodName(request.header(h_RequestLine).getMethod()),
684 i->param(p_uri),
685 i->param(p_nonce),
686 i->param(p_qop),
687 i->param(p_cnonce),
688 i->param(p_nc),
689 request.getContents()))
690 {
691 if(i->exists(p_username))
692 {
693 username = i->param(p_username);
694 }
695 return make_pair(Authenticated,username);
696 }
697 else
698 {
699 return make_pair(Failed,username);
700 }
701 }
702 else
703 {
704 InfoLog (<< "Unsupported qop=" << i->param(p_qop));
705 return make_pair(Failed,username);
706 }
707 }
708 else if (i->param(p_response) == makeResponseMD5WithA1(a1,
709 getMethodName(request.header(h_RequestLine).getMethod()),
710 i->param(p_uri),
711 i->param(p_nonce)))
712 {
713 if(i->exists(p_username))
714 {
715 username = i->param(p_username);
716 }
717 return make_pair(Authenticated,username);
718 }
719 else
720 {
721 return make_pair(Failed,username);
722 }
723 }
724 else
725 {
726 return make_pair(BadlyFormed,username);
727 }
728 }
729 return make_pair(BadlyFormed,username);
730 }
731 DebugLog (<< "No authentication headers. Failing request.");
732 return make_pair(Failed,username);
733 }
734
735 // !jf! note that this only authenticates a ProxyAuthenticate header, need to
736 // add support for WWWAuthenticate as well!!
737 Helper::AuthResult
738 Helper::authenticateRequest(const SipMessage& request,
739 const Data& realm,
740 const Data& password,
741 int expiresDelta)
742 {
743 DebugLog(<< "Authenticating: realm=" << realm << " expires=" << expiresDelta);
744 //DebugLog(<< request);
745
746 if (request.exists(h_ProxyAuthorizations))
747 {
748 const ParserContainer<Auth>& auths = request.header(h_ProxyAuthorizations);
749 for (ParserContainer<Auth>::const_iterator i = auths.begin(); i != auths.end(); i++)
750 {
751 if (i->exists(p_realm) &&
752 i->exists(p_nonce) &&
753 i->exists(p_response) &&
754 i->param(p_realm) == realm)
755 {
756 ParseBuffer pb(i->param(p_nonce).data(), i->param(p_nonce).size());
757 if (!pb.eof() && !isdigit(*pb.position()))
758 {
759 DebugLog(<< "Invalid nonce; expected timestamp.");
760 return BadlyFormed;
761 }
762 const char* anchor = pb.position();
763 pb.skipToChar(Symbols::COLON[0]);
764
765 if (pb.eof())
766 {
767 DebugLog(<< "Invalid nonce; expected timestamp terminator.");
768 return BadlyFormed;
769 }
770
771 Data then;
772 pb.data(then, anchor);
773 if (expiresDelta > 0)
774 {
775 int now = (int)(Timer::getTimeMs()/1000);
776 if (then.convertInt() + expiresDelta < now)
777 {
778 DebugLog(<< "Nonce has expired.");
779 return Expired;
780 }
781 }
782 if (i->param(p_nonce) != makeNonce(request, then))
783 {
784 InfoLog(<< "Not my nonce.");
785 return Failed;
786 }
787
788 InfoLog (<< " username=" << (i->param(p_username))
789 << " password=" << password
790 << " realm=" << realm
791 << " method=" << getMethodName(request.header(h_RequestLine).getMethod())
792 << " uri=" << i->param(p_uri)
793 << " nonce=" << i->param(p_nonce));
794
795 if (i->exists(p_qop))
796 {
797 if (i->param(p_qop) == Symbols::auth || i->param(p_qop) == Symbols::authInt)
798 {
799 if (i->param(p_response) == makeResponseMD5(i->param(p_username),
800 password,
801 realm,
802 getMethodName(request.header(h_RequestLine).getMethod()),
803 i->param(p_uri),
804 i->param(p_nonce),
805 i->param(p_qop),
806 i->param(p_cnonce),
807 i->param(p_nc)),
808 request.getContents())
809 {
810 return Authenticated;
811 }
812 else
813 {
814 return Failed;
815 }
816 }
817 else
818 {
819 InfoLog (<< "Unsupported qop=" << i->param(p_qop));
820 return Failed;
821 }
822 }
823 else if (i->param(p_response) == makeResponseMD5(i->param(p_username),
824 password,
825 realm,
826 getMethodName(request.header(h_RequestLine).getMethod()),
827 i->param(p_uri),
828 i->param(p_nonce)))
829 {
830 return Authenticated;
831 }
832 else
833 {
834 return Failed;
835 }
836 }
837 else
838 {
839 return BadlyFormed;
840 }
841 }
842 return BadlyFormed;
843 }
844 DebugLog (<< "No authentication headers. Failing request.");
845 return Failed;
846 }
847
848 SipMessage*
849 Helper::make405(const SipMessage& request,
850 const int* allowedMethods,
851 int len )
852 {
853 SipMessage* resp = Helper::makeResponse(request, 405);
854
855 if (len < 0)
856 {
857 int upperBound = static_cast<int>(MAX_METHODS);
858 // The UNKNOWN method name marks the end of the enum
859
860 for (int i = 0 ; i < upperBound; i ++)
861 {
862 int last = 0;
863 // ENUMS must be contiguous in order for this to work.
864 assert( i - last <= 1);
865 //MethodTypes type = static_cast<MethodTypes>(i);
866 Token t;
867 t.value() = getMethodName(static_cast<resip::MethodTypes>(i));
868 resp->header(h_Allows).push_back(t);
869 last = i;
870 }
871 }
872 else
873 {
874 // use user's list
875 for ( int i = 0 ; i < len ; i++)
876 {
877 Token t;
878 t.value() = getMethodName(static_cast<resip::MethodTypes>(allowedMethods[i]));
879 resp->header(h_Allows).push_back(t);
880 }
881 }
882 return resp;
883 }
884
885
886 SipMessage*
887 Helper::makeProxyChallenge(const SipMessage& request, const Data& realm, bool useAuth, bool stale)
888 {
889 Auth auth;
890 auth.scheme() = "Digest";
891 Data timestamp((int)(Timer::getTimeMs()/1000));
892 auth.param(p_nonce) = makeNonce(request, timestamp);
893 auth.param(p_algorithm) = "MD5";
894 auth.param(p_realm) = realm;
895 if (useAuth)
896 {
897 auth.param(p_qopOptions) = "auth,auth-int";
898 }
899 if (stale)
900 {
901 auth.param(p_stale) = "true";
902 }
903 SipMessage *response = Helper::makeResponse(request, 407);
904 response->header(h_ProxyAuthenticates).push_back(auth);
905 return response;
906 }
907
908 void updateNonceCount(unsigned int& nonceCount, Data& nonceCountString)
909 {
910 if (!nonceCountString.empty())
911 {
912 return;
913 }
914 nonceCount++;
915 {
916 DataStream s(nonceCountString);
917
918 s << std::setw(8) << std::setfill('0') << std::hex << nonceCount;
919 }
920 DebugLog(<< "nonceCount is now: [" << nonceCountString << "]");
921 }
922
923
924 Auth
925 Helper::makeChallengeResponseAuth(SipMessage& request,
926 const Data& username,
927 const Data& password,
928 const Auth& challenge,
929 const Data& cnonce,
930 unsigned int& nonceCount,
931 Data& nonceCountString)
932 {
933 Auth auth;
934 auth.scheme() = "Digest";
935 auth.param(p_username) = username;
936 assert(challenge.exists(p_realm));
937 auth.param(p_realm) = challenge.param(p_realm);
938 assert(challenge.exists(p_nonce));
939 auth.param(p_nonce) = challenge.param(p_nonce);
940 Data digestUri;
941 {
942 DataStream s(digestUri);
943 //s << request.header(h_RequestLine).uri().host(); // wrong
944 s << request.header(h_RequestLine).uri(); // right
945 }
946 auth.param(p_uri) = digestUri;
947
948 Data authQop = qopOption(challenge);
949 if (!authQop.empty())
950 {
951 updateNonceCount(nonceCount, nonceCountString);
952 auth.param(p_response) = Helper::makeResponseMD5(username,
953 password,
954 challenge.param(p_realm),
955 getMethodName(request.header(h_RequestLine).getMethod()),
956 digestUri,
957 challenge.param(p_nonce),
958 authQop,
959 cnonce,
960 nonceCountString,
961 request.getContents());
962 auth.param(p_cnonce) = cnonce;
963 auth.param(p_nc) = nonceCountString;
964 auth.param(p_qop) = authQop;
965 }
966 else
967 {
968 assert(challenge.exists(p_realm));
969 auth.param(p_response) = Helper::makeResponseMD5(username,
970 password,
971 challenge.param(p_realm),
972 getMethodName(request.header(h_RequestLine).getMethod()),
973 digestUri,
974 challenge.param(p_nonce));
975 }
976
977 if (challenge.exists(p_algorithm))
978 {
979 auth.param(p_algorithm) = challenge.param(p_algorithm);
980 }
981 else
982 {
983 auth.param(p_algorithm) = "MD5";
984 }
985
986 if (challenge.exists(p_opaque))
987 {
988 auth.param(p_opaque) = challenge.param(p_opaque);
989 }
990
991 return auth;
992 }
993
994 Data
995 Helper::qopOption(const Auth& challenge)
996 {
997 // priority-order list of preferred qop tokens
998 static Data preferredTokens[] =
999 {
1000 Symbols::authInt,
1001 Symbols::auth
1002 };
1003
1004 bool found = false;
1005 size_t index = 0;
1006 if (challenge.exists(p_qopOptions) && !challenge.param(p_qopOptions).empty())
1007 {
1008 ParseBuffer pb(challenge.param(p_qopOptions).data(), challenge.param(p_qopOptions).size());
1009 do
1010 {
1011 const char* anchor = pb.skipWhitespace();
1012 pb.skipToChar(Symbols::COMMA[0]);
1013 if (!pb.eof())
1014 pb.skipChar();
1015 Data q;
1016 pb.data(q, anchor);
1017 for (size_t i=0; i < sizeof(preferredTokens)/sizeof(*preferredTokens); i++)
1018 {
1019 if (q == preferredTokens[i])
1020 {
1021 // found a preferred token; is it higher priority?
1022 if (!found || i < index)
1023 {
1024 found = true;
1025 index = i;
1026 }
1027 }
1028 }
1029 }
1030 while(!pb.eof());
1031 }
1032
1033 if (found)
1034 return preferredTokens[index];
1035
1036 return Data::Empty;
1037
1038 }
1039
1040
1041 Auth
1042 Helper::makeChallengeResponseAuthWithA1(const SipMessage& request,
1043 const Data& username,
1044 const Data& a1,
1045 const Auth& challenge,
1046 const Data& cnonce,
1047 unsigned int& nonceCount,
1048 Data& nonceCountString)
1049 {
1050 Auth auth;
1051 auth.scheme() = "Digest";
1052 auth.param(p_username) = username;
1053 assert(challenge.exists(p_realm));
1054 auth.param(p_realm) = challenge.param(p_realm);
1055 assert(challenge.exists(p_nonce));
1056 auth.param(p_nonce) = challenge.param(p_nonce);
1057 Data digestUri;
1058 {
1059 DataStream s(digestUri);
1060 //s << request.header(h_RequestLine).uri().host(); // wrong
1061 s << request.header(h_RequestLine).uri(); // right
1062 }
1063 auth.param(p_uri) = digestUri;
1064
1065 Data authQop = qopOption(challenge);
1066 if (!authQop.empty())
1067 {
1068 updateNonceCount(nonceCount, nonceCountString);
1069 auth.param(p_response) = Helper::makeResponseMD5WithA1(a1,
1070 getMethodName(request.header(h_RequestLine).getMethod()),
1071 digestUri,
1072 challenge.param(p_nonce),
1073 authQop,
1074 cnonce,
1075 nonceCountString,
1076 request.getContents());
1077 auth.param(p_cnonce) = cnonce;
1078 auth.param(p_nc) = nonceCountString;
1079 auth.param(p_qop) = authQop;
1080 }
1081 else
1082 {
1083 assert(challenge.exists(p_realm));
1084 auth.param(p_response) = Helper::makeResponseMD5WithA1(a1,
1085 getMethodName(request.header(h_RequestLine).getMethod()),
1086 digestUri,
1087 challenge.param(p_nonce));
1088 }
1089
1090 if (challenge.exists(p_algorithm))
1091 {
1092 auth.param(p_algorithm) = challenge.param(p_algorithm);
1093 }
1094 else
1095 {
1096 auth.param(p_algorithm) = "MD5";
1097 }
1098
1099 if (challenge.exists(p_opaque))
1100 {
1101 auth.param(p_opaque) = challenge.param(p_opaque);
1102 }
1103
1104 return auth;
1105 }
1106
1107
1108 SipMessage&
1109 Helper::addAuthorization(SipMessage& request,
1110 const SipMessage& challenge,
1111 const Data& username,
1112 const Data& password,
1113 const Data& cnonce,
1114 unsigned int& nonceCount)
1115 {
1116 Data nonceCountString = Data::Empty;
1117
1118 assert(challenge.isResponse());
1119 assert(challenge.header(h_StatusLine).responseCode() == 401 ||
1120 challenge.header(h_StatusLine).responseCode() == 407);
1121
1122 if (challenge.exists(h_ProxyAuthenticates))
1123 {
1124 const ParserContainer<Auth>& auths = challenge.header(h_ProxyAuthenticates);
1125 for (ParserContainer<Auth>::const_iterator i = auths.begin();
1126 i != auths.end(); i++)
1127 {
1128 request.header(h_ProxyAuthorizations).push_back(makeChallengeResponseAuth(request, username, password, *i,
1129 cnonce, nonceCount, nonceCountString));
1130 }
1131 }
1132 if (challenge.exists(h_WWWAuthenticates))
1133 {
1134 const ParserContainer<Auth>& auths = challenge.header(h_WWWAuthenticates);
1135 for (ParserContainer<Auth>::const_iterator i = auths.begin();
1136 i != auths.end(); i++)
1137 {
1138 request.header(h_Authorizations).push_back(makeChallengeResponseAuth(request, username, password, *i,
1139 cnonce, nonceCount, nonceCountString));
1140 }
1141 }
1142 return request;
1143 }
1144
1145 Uri
1146 Helper::makeUri(const Data& aor, const Data& scheme)
1147 {
1148 assert(!aor.prefix("sip:"));
1149 assert(!aor.prefix("sips:"));
1150
1151 Data tmp(aor.size() + scheme.size() + 1, true);
1152 tmp += scheme;
1153 tmp += Symbols::COLON;
1154 tmp += aor;
1155 Uri uri(tmp);
1156 return uri;
1157 }
1158
1159 void
1160 Helper::processStrictRoute(SipMessage& request)
1161 {
1162 if (request.exists(h_Routes) &&
1163 !request.header(h_Routes).empty() &&
1164 !request.header(h_Routes).front().uri().exists(p_lr))
1165 {
1166 // The next hop is a strict router. Move the next hop into the
1167 // Request-URI and move the ultimate destination to the end of the
1168 // route list. Force the message target to be the next hop router.
1169 request.header(h_Routes).push_back(NameAddr(request.header(h_RequestLine).uri()));
1170 request.header(h_RequestLine).uri() = request.header(h_Routes).front().uri();
1171 request.header(h_Routes).pop_front(); // !jf!
1172 assert(!request.hasForceTarget());
1173 request.setForceTarget(request.header(h_RequestLine).uri());
1174 }
1175 }
1176
1177 int
1178 Helper::getPortForReply(SipMessage& request)
1179 {
1180 assert(request.isRequest());
1181 int port = -1;
1182 if (request.header(h_Vias).front().exists(p_rport))
1183 {
1184 port = request.getSource().getPort();
1185 }
1186 else
1187 {
1188 port = request.header(h_Vias).front().sentPort();
1189 if (port <= 0 || port > 65535)
1190 {
1191 port = Symbols::DefaultSipPort;
1192 }
1193 }
1194 assert(port != -1);
1195 return port;
1196 }
1197
1198 Uri
1199 Helper::fromAor(const Data& aor, const Data& scheme)
1200 {
1201 return makeUri(aor, scheme);
1202 }
1203
1204 bool
1205 Helper::validateMessage(const SipMessage& message)
1206 {
1207 if (!message.exists(h_To) ||
1208 !message.exists(h_From) ||
1209 !message.exists(h_CSeq) ||
1210 !message.exists(h_CallId) ||
1211 !message.exists(h_Vias) ||
1212 message.header(h_Vias).empty())
1213 {
1214 InfoLog(<< "Missing mandatory header fields (To, From, CSeq, Call-Id or Via)");
1215 DebugLog(<< message);
1216 return false;
1217 }
1218 else
1219 {
1220 return true;
1221 }
1222 }
1223
1224 #if defined(USE_SSL)
1225 #include <openssl/blowfish.h>
1226
1227 static const Data sep("[]");
1228 static const Data pad("\0\0\0\0\0\0\0", 7);
1229 static const Data GRUU("_GRUU");
1230 static const int saltBytes(16);
1231
1232 Data
1233 Helper::gruuUserPart(const Data& instanceId,
1234 const Data& aor,
1235 const Data& key)
1236 {
1237 unsigned char ivec[8];
1238
1239 ivec[0] = '\x6E';
1240 ivec[1] = '\xE7';
1241 ivec[2] = '\xB0';
1242 ivec[3] = '\x4A';
1243 ivec[4] = '\x45';
1244 ivec[5] = '\x93';
1245 ivec[6] = '\x7D';
1246 ivec[7] = '\x51';
1247
1248 BF_KEY fish;
1249 BF_set_key(&fish, key.size(), (const unsigned char*)key.data());
1250
1251 const Data salt(resip::Random::getRandomHex(saltBytes));
1252
1253 const Data token(salt + instanceId + sep + aor + '\0' +
1254 pad.substr(0, (8 - ((salt.size() +
1255 instanceId.size() +
1256 sep.size() + 1
1257 + aor.size() ) % 8))
1258 % 8));
1259 auto_ptr <unsigned char> out(new unsigned char[token.size()]);
1260 BF_cbc_encrypt((const unsigned char*)token.data(),
1261 out.get(),
1262 token.size(),
1263 &fish,
1264 ivec,
1265 BF_ENCRYPT);
1266
1267 return GRUU + Data(out.get(),token.size()).base64encode(true/*safe URL*/);
1268 }
1269
1270 std::pair<Data,Data>
1271 Helper::fromGruuUserPart(const Data& gruuUserPart,
1272 const Data& key)
1273 {
1274 unsigned char ivec[8];
1275
1276 ivec[0] = '\x6E';
1277 ivec[1] = '\xE7';
1278 ivec[2] = '\xB0';
1279 ivec[3] = '\x4A';
1280 ivec[4] = '\x45';
1281 ivec[5] = '\x93';
1282 ivec[6] = '\x7D';
1283 ivec[7] = '\x51';
1284
1285 static const std::pair<Data, Data> empty;
1286
1287 if (gruuUserPart.size() < GRUU.size())
1288 {
1289 return empty;
1290 }
1291
1292 const Data gruu = gruuUserPart.substr(GRUU.size());
1293
1294 BF_KEY fish;
1295 BF_set_key(&fish, key.size(), (const unsigned char*)key.data());
1296
1297 const Data decoded = gruu.base64decode();
1298
1299 auto_ptr <unsigned char> out(new unsigned char[gruuUserPart.size()+1]);
1300 BF_cbc_encrypt((const unsigned char*)decoded.data(),
1301 out.get(),
1302 decoded.size(),
1303 &fish,
1304 ivec,
1305 BF_DECRYPT);
1306 const Data pair(out.get(), decoded.size());
1307
1308 Data::size_type pos = pair.find(sep);
1309 if (pos == Data::npos)
1310 {
1311 return empty;
1312 }
1313
1314 return std::make_pair(pair.substr(2*saltBytes, pos), // strip out the salt
1315 pair.substr(pos+sep.size()).c_str());
1316 }
1317 #endif
1318 Helper::ContentsSecAttrs::ContentsSecAttrs()
1319 : mContents(0),
1320 mAttributes(0)
1321 {}
1322
1323 Helper::ContentsSecAttrs::ContentsSecAttrs(std::auto_ptr<Contents> contents,
1324 std::auto_ptr<SecurityAttributes> attributes)
1325 : mContents(contents),
1326 mAttributes(attributes)
1327 {}
1328
1329 Helper::ContentsSecAttrs::ContentsSecAttrs(const ContentsSecAttrs& rhs)
1330 : mContents(rhs.mContents),
1331 mAttributes(rhs.mAttributes)
1332 {}
1333
1334 Helper::ContentsSecAttrs&
1335 Helper::ContentsSecAttrs::operator=(const ContentsSecAttrs& rhs)
1336 {
1337 if (&rhs != this)
1338 {
1339 mContents = rhs.mContents;
1340 mAttributes = rhs.mAttributes;
1341 }
1342 return *this;
1343 }
1344
1345
1346 Contents*
1347 extractFromPkcs7Recurse(Contents* tree,
1348 const Data& signerAor,
1349 const Data& receiverAor,
1350 SecurityAttributes* attributes,
1351 Security& security)
1352 {
1353 Pkcs7Contents* pk;
1354 if ((pk = dynamic_cast<Pkcs7Contents*>(tree)))
1355 {
1356 #if defined(USE_SSL)
1357 Contents* contents = security.decrypt(receiverAor, pk);
1358 if (contents)
1359 {
1360 attributes->setEncrypted();
1361 }
1362 return contents;
1363 #else
1364 return 0;
1365 #endif
1366 }
1367 MultipartSignedContents* mps;
1368 if ((mps = dynamic_cast<MultipartSignedContents*>(tree)))
1369 {
1370 #if defined(USE_SSL)
1371 Data signer;
1372 SignatureStatus sigStatus;
1373 Contents* b = extractFromPkcs7Recurse(security.checkSignature(mps,
1374 &signer,
1375 &sigStatus),
1376 signerAor,
1377 receiverAor, attributes, security);
1378 attributes->setSigner(signer);
1379 attributes->setSignatureStatus(sigStatus);
1380 return b->clone();
1381 #else
1382 return mps->parts().front()->clone();
1383 #endif
1384 }
1385 MultipartAlternativeContents* alt;
1386 if ((alt = dynamic_cast<MultipartAlternativeContents*>(tree)))
1387 {
1388 for (MultipartAlternativeContents::Parts::reverse_iterator i = alt->parts().rbegin();
1389 i != alt->parts().rend(); ++i)
1390 {
1391 Contents* b = extractFromPkcs7Recurse(*i, signerAor, receiverAor, attributes, security);
1392 if (b)
1393 {
1394 return b;
1395 }
1396 }
1397 }
1398
1399 MultipartMixedContents* mult;
1400 if ((mult = dynamic_cast<MultipartMixedContents*>(tree)))
1401 {
1402 for (MultipartMixedContents::Parts::iterator i = mult->parts().begin();
1403 i != mult->parts().end(); ++i)
1404 {
1405 Contents* b = extractFromPkcs7Recurse(*i, signerAor, receiverAor,
1406 attributes, security);
1407 if (b)
1408 {
1409 return b;
1410 }
1411 };
1412
1413 return 0;
1414 }
1415
1416 return tree->clone();
1417 }
1418
1419 Helper::ContentsSecAttrs
1420 Helper::extractFromPkcs7(const SipMessage& message,
1421 Security& security)
1422 {
1423 SecurityAttributes* attr = new SecurityAttributes;
1424 // .dlb. currently flattening SecurityAttributes?
1425 //attr->setIdentity(message.getIdentity());
1426 attr->setIdentity(message.header(h_From).uri().getAor());
1427 Contents *b = extractFromPkcs7Recurse(message.getContents(),
1428 message.header(h_To).uri().getAor(),
1429 message.header(h_From).uri().getAor(),
1430 attr, security);
1431 std::auto_ptr<Contents> c(b);
1432 std::auto_ptr<SecurityAttributes> a(attr);
1433 return ContentsSecAttrs(c, a);
1434 }
1435
1436 Helper::FailureMessageEffect
1437 Helper::determineFailureMessageEffect(const SipMessage& response)
1438 {
1439 assert(response.isResponse());
1440 int code = response.header(h_StatusLine).statusCode();
1441 assert(code >= 400);
1442
1443 switch(code)
1444 {
1445 case 404:
1446 case 410:
1447 case 416:
1448 case 480: // but maybe not, still not quite decided:
1449 case 481:
1450 case 482: // but maybe not, still not quite decided:
1451 case 484:
1452 case 485:
1453 case 502:
1454 case 604:
1455 return DialogTermination;
1456 case 403:
1457 case 489: //only for only subscription
1458 case 408: //again, maybe not. This seems best.
1459 return UsageTermination;
1460 case 400:
1461 case 401:
1462 case 402:
1463 case 405: //doesn't agree w/ -00 of dialogusage
1464 case 406:
1465 case 412:
1466 case 413:
1467 case 414:
1468 case 415:
1469 case 420:
1470 case 421:
1471 case 423:
1472
1473 case 429: // but if this the refer creating the Subscription, no sub will be created.
1474 case 486:
1475 case 487:
1476 case 488:
1477 case 491:
1478 case 493:
1479 case 494:
1480 case 505:
1481 case 513:
1482 case 603:
1483 case 606:
1484 return TransactionTermination;
1485 case 483: // who knows, gravefully terminate or just destroy dialog
1486 case 501:
1487 return ApplicationDependant;
1488 default:
1489 if (code < 600)
1490 {
1491 if (response.exists(h_RetryAfter))
1492
1493 {
1494 return RetryAfter;
1495 }
1496 else
1497 {
1498 return OptionalRetryAfter;
1499 }
1500 }
1501 else
1502 {
1503 if (response.exists(h_RetryAfter))
1504 {
1505 return RetryAfter;
1506 }
1507 else
1508 {
1509 return ApplicationDependant;
1510 }
1511 }
1512 }
1513 }
1514
1515 /* ====================================================================
1516 * The Vovida Software License, Version 1.0
1517 *
1518 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1519 *
1520 * Redistribution and use in source and binary forms, with or without
1521 * modification, are permitted provided that the following conditions
1522 * are met:
1523 *
1524 * 1. Redistributions of source code must retain the above copyright
1525 * notice, this list of conditions and the following disclaimer.
1526 *
1527 * 2. Redistributions in binary form must reproduce the above copyright
1528 * notice, this list of conditions and the following disclaimer in
1529 * the documentation and/or other materials provided with the
1530 * distribution.
1531 *
1532 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1533 * and "Vovida Open Communication Application Library (VOCAL)" must
1534 * not be used to endorse or promote products derived from this
1535 * software without prior written permission. For written
1536 * permission, please contact vocal@vovida.org.
1537 *
1538 * 4. Products derived from this software may not be called "VOCAL", nor
1539 * may "VOCAL" appear in their name, without prior written
1540 * permission of Vovida Networks, Inc.
1541 *
1542 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1543 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1544 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1545 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1546 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1547 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1548 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1549 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1550 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1551 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1552 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1553 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1554 * DAMAGE.
1555 *
1556 * ====================================================================
1557 *
1558 * This software consists of voluntary contributions made by Vovida
1559 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1560 * Inc. For more information on Vovida Networks, Inc., please see
1561 * <http://www.vovida.org/>.
1562 *
1563 */

Properties

Name Value
svn:eol-style LF

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27