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