1 /*
2 * Copyright (C) 2010-2011 Raphael Coeffic
3 *
4 * This file is part of SEMS, a free SIP media server.
5 *
6 * SEMS is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version. This program is released under
10 * the GPL with the additional exemption that compiling, linking,
11 * and/or using OpenSSL is allowed.
12 *
13 * For a license to use the SEMS software under conditions
14 * other than those described here, or to purchase support for this
15 * software, please contact iptel.org by e-mail at the following addresses:
16 * info@iptel.org
17 *
18 * SEMS is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27 /** @file AmOfferAnswer.cpp */
28
29 #include "AmOfferAnswer.h"
30 #include "AmSipDialog.h"
31 #include "AmSipHeaders.h"
32 #include "log.h"
33
34 #include <assert.h>
35
36 const char* __dlg_oa_status2str[AmOfferAnswer::__max_OA] = {
37 "None",
38 "OfferRecved",
39 "OfferSent",
40 "Completed"
41 };
42
getOAStateStr(AmOfferAnswer::OAState st)43 static const char* getOAStateStr(AmOfferAnswer::OAState st) {
44 if((st < 0) || (st >= AmOfferAnswer::__max_OA))
45 return "Invalid";
46 else
47 return __dlg_oa_status2str[st];
48 }
49
50
AmOfferAnswer(AmSipDialog * dlg)51 AmOfferAnswer::AmOfferAnswer(AmSipDialog* dlg)
52 : state(OA_None),
53 cseq(0),
54 sdp_remote(),
55 sdp_local(),
56 dlg(dlg)
57 {
58
59 }
60
getState()61 AmOfferAnswer::OAState AmOfferAnswer::getState()
62 {
63 return state;
64 }
65
setState(AmOfferAnswer::OAState n_st)66 void AmOfferAnswer::setState(AmOfferAnswer::OAState n_st)
67 {
68 DBG("setting SIP dialog O/A status: %s->%s\n",
69 getOAStateStr(state), getOAStateStr(n_st));
70 state = n_st;
71 }
72
getLocalSdp()73 const AmSdp& AmOfferAnswer::getLocalSdp()
74 {
75 return sdp_local;
76 }
77
getRemoteSdp()78 const AmSdp& AmOfferAnswer::getRemoteSdp()
79 {
80 return sdp_remote;
81 }
82
83 /** State maintenance */
saveState()84 void AmOfferAnswer::saveState()
85 {
86 saved_state = state;
87 }
88
checkStateChange()89 int AmOfferAnswer::checkStateChange()
90 {
91 int ret = 0;
92
93 if((saved_state != state) &&
94 (state == OA_Completed)) {
95
96 ret = dlg->onSdpCompleted();
97 }
98
99 return ret;
100 }
101
clear()102 void AmOfferAnswer::clear()
103 {
104 setState(OA_None);
105 cseq = 0;
106 sdp_remote.clear();
107 sdp_local.clear();
108 }
109
clearTransitionalState()110 void AmOfferAnswer::clearTransitionalState()
111 {
112 if(state != OA_Completed){
113 clear();
114 }
115 }
116
onRequestIn(const AmSipRequest & req)117 int AmOfferAnswer::onRequestIn(const AmSipRequest& req)
118 {
119 saveState();
120
121 const char* err_txt = NULL;
122 int err_code = 0;
123
124 if((req.method == SIP_METH_INVITE ||
125 req.method == SIP_METH_UPDATE ||
126 req.method == SIP_METH_ACK ||
127 req.method == SIP_METH_PRACK) &&
128 !req.body.empty() ) {
129
130 const AmMimeBody* sdp_body = req.body.hasContentType(SIP_APPLICATION_SDP);
131 if(sdp_body)
132 err_code = onRxSdp(req.cseq,*sdp_body,&err_txt);
133 }
134
135 if(checkStateChange()){
136 err_code = 500;
137 err_txt = "internal error";
138 }
139
140 if(err_code){
141 if( req.method != SIP_METH_ACK ){ // INVITE || UPDATE || PRACK
142 dlg->reply(req,err_code,err_txt);
143 }
144 else { // ACK
145 // TODO: only if reply to initial INVITE (if re-INV, app should decide)
146 DBG("error %i with SDP received in ACK request: sending BYE\n",err_code);
147 dlg->bye();
148 }
149 }
150
151 if((req.method == SIP_METH_ACK) &&
152 (req.cseq == cseq)){
153 // 200 ACK received:
154 // -> reset OA state
155 DBG("200 ACK received: resetting OA state");
156 clearTransitionalState();
157 }
158
159 return err_code ? -1 : 0;
160 }
161
onReplyIn(const AmSipReply & reply)162 int AmOfferAnswer::onReplyIn(const AmSipReply& reply)
163 {
164 const char* err_txt = NULL;
165 int err_code = 0;
166
167 if((reply.cseq_method == SIP_METH_INVITE ||
168 reply.cseq_method == SIP_METH_UPDATE ||
169 reply.cseq_method == SIP_METH_PRACK) &&
170 !reply.body.empty() ) {
171
172 const AmMimeBody* sdp_body = reply.body.hasContentType(SIP_APPLICATION_SDP);
173 if(sdp_body) {
174
175 if(((state == OA_Completed) ||
176 (state == OA_OfferRecved)) &&
177 (reply.cseq == cseq)) {
178
179 DBG("ignoring subsequent SDP reply within the same transaction\n");
180 DBG("this usually happens when 183 and 200 have SDP\n");
181
182 /* Make sure that session is started when 200 OK is received */
183 if (reply.code == 200) dlg->onSdpCompleted();
184
185 }
186 else {
187 saveState();
188 err_code = onRxSdp(reply.cseq,reply.body,&err_txt);
189 checkStateChange();
190 }
191 }
192 }
193
194 if( (reply.code >= 300) &&
195 (reply.cseq == cseq) ) {
196 // final error reply -> cleanup OA state
197 DBG("after %u reply to %s: resetting OA state\n",
198 reply.code, reply.cseq_method.c_str());
199 clearTransitionalState();
200 }
201
202
203 if(err_code){
204 // TODO: only if initial INVITE (if re-INV, app should decide)
205 DBG("error %i (%s) with SDP received in %i reply: sending ACK+BYE\n",
206 err_code,err_txt?err_txt:"none",reply.code);
207 dlg->bye();
208 }
209
210 return 0;
211 }
212
onRxSdp(unsigned int m_cseq,const AmMimeBody & body,const char ** err_txt)213 int AmOfferAnswer::onRxSdp(unsigned int m_cseq, const AmMimeBody& body, const char** err_txt)
214 {
215 DBG("entering onRxSdp(), oa_state=%s\n", getOAStateStr(state));
216 OAState old_oa_state = state;
217
218 int err_code = 0;
219 assert(err_txt);
220
221 const AmMimeBody *sdp = body.hasContentType("application/sdp");
222
223 if (sdp == NULL) {
224 err_code = 400;
225 *err_txt = "sdp body part not found";
226 } else if (sdp_remote.parse((const char*)sdp->getPayload())) {
227 err_code = 400;
228 *err_txt = "session description parsing failed";
229 }
230 else if(sdp_remote.media.empty()){
231 err_code = 400;
232 *err_txt = "no media line found in SDP message";
233 }
234
235 if(err_code != 0) {
236 sdp_remote.clear();
237 }
238
239 if(err_code == 0) {
240
241 switch(state) {
242 case OA_None:
243 case OA_Completed:
244 setState(OA_OfferRecved);
245 cseq = m_cseq;
246 break;
247
248 case OA_OfferSent:
249 setState(OA_Completed);
250 break;
251
252 case OA_OfferRecved:
253 err_code = 400;// TODO: check correct error code
254 *err_txt = "pending SDP offer";
255 break;
256
257 default:
258 assert(0);
259 break;
260 }
261 }
262
263 DBG("oa_state: %s -> %s\n", getOAStateStr(old_oa_state), getOAStateStr(state));
264
265 return err_code;
266 }
267
onTxSdp(unsigned int m_cseq,const AmMimeBody & body)268 int AmOfferAnswer::onTxSdp(unsigned int m_cseq, const AmMimeBody& body)
269 {
270 // assume that the payload is ok if it is not empty.
271 // (do not parse again self-generated SDP)
272 if(body.empty()){
273 return -1;
274 }
275
276 switch(state) {
277
278 case OA_None:
279 case OA_Completed:
280 setState(OA_OfferSent);
281 cseq = m_cseq;
282 break;
283
284 case OA_OfferRecved:
285 setState(OA_Completed);
286 break;
287
288 case OA_OfferSent:
289 // There is already a pending offer!!!
290 DBG("There is already a pending offer, onTxSdp fails\n");
291 return -1;
292
293 default:
294 break;
295 }
296
297 return 0;
298 }
299
onRequestOut(AmSipRequest & req)300 int AmOfferAnswer::onRequestOut(AmSipRequest& req)
301 {
302 AmMimeBody* sdp_body = req.body.hasContentType(SIP_APPLICATION_SDP);
303 bool generate_sdp = sdp_body && !sdp_body->getLen();
304 bool has_sdp = sdp_body && sdp_body->getLen();
305
306 if (!sdp_body &&
307 ((req.method == SIP_METH_PRACK) ||
308 (req.method == SIP_METH_ACK))
309 && (state == OA_OfferRecved)) {
310 generate_sdp = true;
311 sdp_body = req.body.addPart(SIP_APPLICATION_SDP);
312 }
313
314 saveState();
315
316 if (generate_sdp) {
317 string sdp_buf;
318 if (!getSdpBody(sdp_buf)){
319 sdp_body->setPayload((const unsigned char*)sdp_buf.c_str(),
320 sdp_buf.length());
321 has_sdp = true;
322 }
323 else {
324 return -1;
325 }
326 } else if (sdp_body && has_sdp) {
327 // update local SDP copy
328 if (sdp_local.parse((const char*)sdp_body->getPayload())) {
329 ERROR("parser failed on Tx SDP: '%s'\n", (const char*)sdp_body->getPayload());
330 }
331 }
332
333 if(has_sdp && (onTxSdp(req.cseq,req.body) != 0)){
334 DBG("onTxSdp() failed\n");
335 return -1;
336 }
337
338 return 0;
339 }
340
onReplyOut(AmSipReply & reply)341 int AmOfferAnswer::onReplyOut(AmSipReply& reply)
342 {
343 AmMimeBody* sdp_body = reply.body.hasContentType(SIP_APPLICATION_SDP);
344 bool generate_sdp = sdp_body && !sdp_body->getLen();
345 bool has_sdp = sdp_body && sdp_body->getLen();
346
347 if (!has_sdp && !generate_sdp) {
348 // let's see whether we should force SDP or not.
349
350 if (reply.cseq_method == SIP_METH_INVITE){
351
352 if ((reply.code == 183)
353 || ((reply.code >= 200) && (reply.code < 300))) {
354
355 // either offer received or no offer at all:
356 // -> force SDP
357 generate_sdp = (state == OA_OfferRecved)
358 || (state == OA_None)
359 || (state == OA_Completed);
360 }
361 }
362 else if (reply.cseq_method == SIP_METH_UPDATE) {
363
364 if ((reply.code >= 200) &&
365 (reply.code < 300)) {
366
367 // offer received:
368 // -> force SDP
369 generate_sdp = (state == OA_OfferRecved);
370 }
371 }
372 }
373
374 if (reply.cseq_method == SIP_METH_INVITE && reply.code < 300) {
375 // ignore SDP repeated in 1xx and 2xx replies (183, 180, ... 2xx)
376 if (has_sdp &&
377 (state == OA_Completed || state == OA_OfferSent) &&
378 reply.cseq == cseq)
379 {
380 has_sdp = false;
381 }
382 }
383
384 saveState();
385
386 if (generate_sdp) {
387
388 string sdp_buf;
389 if(getSdpBody(sdp_buf)) {
390 if ((reply.code == 183 && reply.cseq_method == SIP_METH_INVITE) ||
391 (reply.code == 200 && reply.cseq_method == SIP_METH_INVITE && state == OA_Completed)) {
392 // just ignore if no SDP is generated (required for B2B)
393 }
394 else return -1;
395 }
396 else {
397 if(!sdp_body){
398 if( (sdp_body =
399 reply.body.addPart(SIP_APPLICATION_SDP))
400 == NULL ) {
401 DBG("AmMimeBody::addPart() failed\n");
402 return -1;
403 }
404 }
405
406 sdp_body->setPayload((const unsigned char*)sdp_buf.c_str(),
407 sdp_buf.length());
408 has_sdp = true;
409 }
410 } else if (sdp_body && has_sdp) {
411 // update local SDP copy
412 if (sdp_local.parse((const char*)sdp_body->getPayload())) {
413 ERROR("parser failed on Tx SDP: '%s'\n", (const char*)sdp_body->getPayload());
414 }
415 }
416
417 if (has_sdp && (onTxSdp(reply.cseq,reply.body) != 0)) {
418
419 DBG("onTxSdp() failed\n");
420 return -1;
421 }
422
423 if( (reply.code >= 300) &&
424 (reply.cseq == cseq) ) {
425 // final error reply -> cleanup OA state
426 DBG("after %u reply to %s: resetting OA state\n",
427 reply.code, reply.cseq_method.c_str());
428 clearTransitionalState();
429 }
430
431 return 0;
432 }
433
onRequestSent(const AmSipRequest & req)434 int AmOfferAnswer::onRequestSent(const AmSipRequest& req)
435 {
436 int ret = checkStateChange();
437
438 if((req.method == SIP_METH_ACK) &&
439 (req.cseq == cseq)) {
440
441 // transaction has been removed:
442 // -> cleanup OA state
443 DBG("200 ACK sent: resetting OA state\n");
444 clearTransitionalState();
445 }
446
447 return ret;
448 }
449
onReplySent(const AmSipReply & reply)450 int AmOfferAnswer::onReplySent(const AmSipReply& reply)
451 {
452 int ret = checkStateChange();
453
454 // final answer to non-invite req that triggered O/A transaction
455 if( (reply.code >= 200) &&
456 (reply.cseq_method != SIP_METH_CANCEL) &&
457 (reply.cseq == cseq) &&
458 (reply.cseq_method != SIP_METH_INVITE) ) {
459
460 // transaction has been removed:
461 // -> cleanup OA state
462 DBG("transaction finished by final reply %u: resetting OA state\n", reply.cseq);
463 clearTransitionalState();
464 }
465
466 return ret;
467 }
468
getSdpBody(string & sdp_body)469 int AmOfferAnswer::getSdpBody(string& sdp_body)
470 {
471 switch(state){
472 case OA_None:
473 case OA_Completed:
474 if(dlg->getSdpOffer(sdp_local)){
475 sdp_local.print(sdp_body);
476 }
477 else {
478 DBG("No SDP Offer.\n");
479 return -1;
480 }
481 break;
482 case OA_OfferRecved:
483 if(dlg->getSdpAnswer(sdp_remote,sdp_local)){
484 sdp_local.print(sdp_body);
485 }
486 else {
487 DBG("No SDP Answer.\n");
488 return -1;
489 }
490 break;
491
492 case OA_OfferSent:
493 DBG("Still waiting for a reply\n");
494 return -1;
495
496 default:
497 break;
498 }
499
500 return 0;
501 }
502
onNoAck(unsigned int ack_cseq)503 void AmOfferAnswer::onNoAck(unsigned int ack_cseq)
504 {
505 if(ack_cseq == cseq){
506 DBG("ACK timeout: resetting OA state\n");
507 clearTransitionalState();
508 }
509 }
510