1 #include "Am100rel.h"
2 #include "AmConfig.h"
3 
4 #include "AmUtils.h"
5 #include "AmSipHeaders.h"
6 #include "AmSession.h"
7 
8 #include "log.h"
9 
Am100rel(AmSipDialog * dlg,AmSipDialogEventHandler * hdl)10 Am100rel::Am100rel(AmSipDialog* dlg, AmSipDialogEventHandler* hdl)
11   : reliable_1xx(AmConfig::rel100), rseq(0), rseq_confirmed(false),
12     rseq_1st(0), dlg(dlg), hdl(hdl)
13 {
14   // if (reliable_1xx)
15   //   rseq = 0;
16 }
17 
onRequestIn(const AmSipRequest & req)18 int  Am100rel::onRequestIn(const AmSipRequest& req)
19 {
20   if (reliable_1xx == REL100_IGNORED)
21     return 1;
22 
23   /* activate the 100rel, if needed */
24   if (req.method == SIP_METH_INVITE) {
25     switch(reliable_1xx) {
26       case REL100_SUPPORTED: /* if support is on, enforce if asked by UAC */
27         if (key_in_list(getHeader(req.hdrs, SIP_HDR_SUPPORTED, SIP_HDR_SUPPORTED_COMPACT),
28               SIP_EXT_100REL) ||
29             key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE),
30               SIP_EXT_100REL)) {
31           reliable_1xx = REL100_REQUIRE;
32           DBG(SIP_EXT_100REL " now active.\n");
33         }
34         break;
35 
36       case REL100_REQUIRE: /* if support is required, reject if UAC doesn't */
37         if (! (key_in_list(getHeader(req.hdrs,SIP_HDR_SUPPORTED, SIP_HDR_SUPPORTED_COMPACT),
38               SIP_EXT_100REL) ||
39             key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE),
40               SIP_EXT_100REL))) {
41           ERROR("'" SIP_EXT_100REL "' extension required, but not advertised"
42             " by peer.\n");
43 	  AmBasicSipDialog::reply_error(req, 421, SIP_REPLY_EXTENSION_REQUIRED,
44 					SIP_HDR_COLSP(SIP_HDR_REQUIRE)
45 					SIP_EXT_100REL CRLF);
46           if (hdl) hdl->onFailure();
47           return 0; // has been replied
48         }
49         break; // 100rel required
50 
51       case REL100_DISABLED:
52         // TODO: shouldn't this be part of a more general check in SEMS?
53         if (key_in_list(getHeader(req.hdrs,SIP_HDR_REQUIRE),SIP_EXT_100REL)) {
54           AmBasicSipDialog::reply_error(req, 420, SIP_REPLY_BAD_EXTENSION,
55 					SIP_HDR_COLSP(SIP_HDR_UNSUPPORTED)
56 					SIP_EXT_100REL CRLF);
57           if (hdl) hdl->onFailure();
58           return 0; // has been replied
59         }
60         break;
61 
62       default:
63         ERROR("BUG: unexpected value `%d' for '" SIP_EXT_100REL "' switch.",
64           reliable_1xx);
65 #ifndef NDEBUG
66         abort();
67 #endif
68     } // switch reliable_1xx
69   } else if (req.method == SIP_METH_PRACK) {
70     if (reliable_1xx != REL100_REQUIRE) {
71       WARN("unexpected PRACK received while " SIP_EXT_100REL " not active.\n");
72       // let if float up
73     } else if (rseq_1st<=req.rseq && req.rseq<=rseq) {
74       if (req.rseq == rseq) {
75         rseq_confirmed = true; // confirmed
76       }
77       // else: confirmation for one of the pending 1xx
78       DBG("%sRSeq (%u) confirmed.\n", (req.rseq==rseq) ? "latest " : "", rseq);
79     }
80   }
81 
82   return 1;
83 }
84 
onReplyIn(const AmSipReply & reply)85 int  Am100rel::onReplyIn(const AmSipReply& reply)
86 {
87   if (reliable_1xx == REL100_IGNORED)
88     return 1;
89 
90   if (dlg->getStatus() != AmSipDialog::Trying &&
91       dlg->getStatus() != AmSipDialog::Proceeding &&
92       dlg->getStatus() != AmSipDialog::Early &&
93       dlg->getStatus() != AmSipDialog::Connected)
94     return 1;
95 
96   if (100<reply.code && reply.code<200 && reply.cseq_method==SIP_METH_INVITE) {
97     switch (reliable_1xx) {
98     case REL100_SUPPORTED:
99       if (key_in_list(getHeader(reply.hdrs, SIP_HDR_REQUIRE),
100           SIP_EXT_100REL))
101         reliable_1xx = REL100_REQUIRE;
102         // no break!
103       else
104         break;
105 
106     case REL100_REQUIRE:
107       if (!key_in_list(getHeader(reply.hdrs,SIP_HDR_REQUIRE),SIP_EXT_100REL) ||
108           !reply.rseq) {
109         ERROR(SIP_EXT_100REL " not supported or no positive RSeq value in "
110             "(reliable) 1xx.\n");
111 	dlg->bye();
112         if (hdl) hdl->onFailure();
113       } else {
114         DBG(SIP_EXT_100REL " now active.\n");
115         if (hdl) ((AmSipDialogEventHandler*)hdl)->onInvite1xxRel(reply);
116       }
117       break;
118 
119     case REL100_DISABLED:
120       // 100rel support disabled
121       break;
122     default:
123       ERROR("BUG: unexpected value `%d' for " SIP_EXT_100REL " switch.",
124           reliable_1xx);
125 #ifndef NDEBUG
126       abort();
127 #endif
128     } // switch reliable 1xx
129   } else if (reliable_1xx && reply.cseq_method==SIP_METH_PRACK) {
130     if (300 <= reply.code) {
131       // if PRACK fails, tear down session
132       dlg->bye();
133       if (hdl) hdl->onFailure();
134     } else if (200 <= reply.code) {
135       if (hdl)
136 	((AmSipDialogEventHandler*)hdl)->onPrack2xx(reply);
137     } else {
138       WARN("received '%d' for " SIP_METH_PRACK " method.\n", reply.code);
139     }
140     // absorbe the replys for the prack (they've been dispatched through
141     // onPrack2xx, if necessary)
142     return 0;
143   }
144   return 1;
145 }
146 
onRequestOut(AmSipRequest & req)147 void Am100rel::onRequestOut(AmSipRequest& req)
148 {
149   if (reliable_1xx == REL100_IGNORED || req.method!=SIP_METH_INVITE)
150     return;
151 
152   switch(reliable_1xx) {
153     case REL100_SUPPORTED:
154       if (! key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), SIP_EXT_100REL))
155         req.hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
156       break;
157     case REL100_REQUIRE:
158       if (! key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE), SIP_EXT_100REL))
159         req.hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
160       break;
161     default:
162       ERROR("BUG: unexpected reliability switch value of '%d'.\n",
163           reliable_1xx);
164     case 0:
165       break;
166   }
167 }
168 
onReplyOut(AmSipReply & reply)169 void Am100rel::onReplyOut(AmSipReply& reply)
170 {
171   if (reliable_1xx == REL100_IGNORED)
172     return;
173 
174   if (reply.cseq_method == SIP_METH_INVITE) {
175     if (100 < reply.code && reply.code < 200) {
176       switch (reliable_1xx) {
177         case REL100_SUPPORTED:
178           if (! key_in_list(getHeader(reply.hdrs, SIP_HDR_REQUIRE),
179 			    SIP_EXT_100REL))
180             reply.hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
181           break;
182         case REL100_REQUIRE:
183           // add Require HF
184           if (! key_in_list(getHeader(reply.hdrs, SIP_HDR_REQUIRE),
185 			    SIP_EXT_100REL))
186             reply.hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
187           // add RSeq HF
188           if (getHeader(reply.hdrs, SIP_HDR_RSEQ).length())
189             // already added (by app?)
190             break;
191           if (! rseq) { // only init rseq if 1xx is used
192             rseq = (get_random() & 0x3ff) + 1; // start small (<1024) and non-0
193             rseq_confirmed = false;
194             rseq_1st = rseq;
195           } else {
196             if ((! rseq_confirmed) && (rseq_1st == rseq))
197               // refuse subsequent 1xx if first isn't yet PRACKed
198               throw AmSession::Exception(491, "first reliable 1xx not yet "
199                   "PRACKed");
200             rseq ++;
201           }
202           reply.hdrs += SIP_HDR_COLSP(SIP_HDR_RSEQ) + int2str(rseq) + CRLF;
203           break;
204         default:
205           break;
206       }
207     } else if (reply.code < 300 && reliable_1xx == REL100_REQUIRE) { //code = 2xx
208       if (rseq && !rseq_confirmed)
209         // reliable 1xx is pending, 2xx'ing not allowed yet
210         throw AmSession::Exception(491, "last reliable 1xx not yet PRACKed");
211     }
212   }
213 }
214 
onTimeout(const AmSipRequest & req,const AmSipReply & rpl)215 void Am100rel::onTimeout(const AmSipRequest& req, const AmSipReply& rpl)
216 {
217   if (reliable_1xx == REL100_IGNORED)
218     return;
219 
220   INFO("reply <%s> timed out (not PRACKed).\n", rpl.print().c_str());
221   if (100 < rpl.code && rpl.code < 200 && reliable_1xx == REL100_REQUIRE &&
222       rseq == rpl.rseq && rpl.cseq_method == SIP_METH_INVITE) {
223     INFO("reliable %d reply timed out; rejecting request.\n", rpl.code);
224     if(hdl) hdl->onNoPrack(req, rpl);
225   } else {
226     WARN("reply timed-out, but not reliable.\n"); // debugging
227   }
228 }
229 
230 
231