blob: 9da8d250803736d5adf145b6224188d53f049892 [file] [log] [blame]
Benny Prijono1f7767b2007-10-03 18:28:49 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include "test.h"
21#include <pjsip_ua.h>
22#include <pjsip.h>
23#include <pjlib.h>
24
25#define THIS_FILE "inv_offer_answer_test.c"
26#define PORT 5068
27#define CONTACT "sip:127.0.0.1:5068"
28#define TRACE_(x) PJ_LOG(3,x)
29
30static struct oa_sdp_t
31{
32 const char *offer;
33 const char *answer;
34 unsigned pt_result;
35} oa_sdp[] =
36{
37 {
38 /* Offer: */
39 "v=0\r\n"
40 "o=alice 1 1 IN IP4 host.anywhere.com\r\n"
41 "s= \r\n"
42 "c=IN IP4 host.anywhere.com\r\n"
43 "t=0 0\r\n"
44 "m=audio 49170 RTP/AVP 0\r\n"
45 "a=rtpmap:0 PCMU/8000\r\n",
46
47 /* Answer: */
48 "v=0\r\n"
49 "o=bob 1 1 IN IP4 host.example.com\r\n"
50 "s= \r\n"
51 "c=IN IP4 host.example.com\r\n"
52 "t=0 0\r\n"
53 "m=audio 49920 RTP/AVP 0\r\n"
54 "a=rtpmap:0 PCMU/8000\r\n"
55 "m=video 0 RTP/AVP 31\r\n",
56
57 0
58 },
59
60 {
61 /* Offer: */
62 "v=0\r\n"
63 "o=alice 2 2 IN IP4 host.anywhere.com\r\n"
64 "s= \r\n"
65 "c=IN IP4 host.anywhere.com\r\n"
66 "t=0 0\r\n"
67 "m=audio 49170 RTP/AVP 8\r\n"
68 "a=rtpmap:0 PCMA/8000\r\n",
69
70 /* Answer: */
71 "v=0\r\n"
72 "o=bob 2 2 IN IP4 host.example.com\r\n"
73 "s= \r\n"
74 "c=IN IP4 host.example.com\r\n"
75 "t=0 0\r\n"
76 "m=audio 49920 RTP/AVP 8\r\n"
77 "a=rtpmap:0 PCMA/8000\r\n",
78
79 8
80 },
81
82 {
83 /* Offer: */
84 "v=0\r\n"
85 "o=alice 3 3 IN IP4 host.anywhere.com\r\n"
86 "s= \r\n"
87 "c=IN IP4 host.anywhere.com\r\n"
88 "t=0 0\r\n"
89 "m=audio 49170 RTP/AVP 3\r\n",
90
91 /* Answer: */
92 "v=0\r\n"
93 "o=bob 3 3 IN IP4 host.example.com\r\n"
94 "s= \r\n"
95 "c=IN IP4 host.example.com\r\n"
96 "t=0 0\r\n"
97 "m=audio 49920 RTP/AVP 3\r\n",
98
99 3
100 },
101
102 {
103 /* Offer: */
104 "v=0\r\n"
105 "o=alice 4 4 IN IP4 host.anywhere.com\r\n"
106 "s= \r\n"
107 "c=IN IP4 host.anywhere.com\r\n"
108 "t=0 0\r\n"
109 "m=audio 49170 RTP/AVP 4\r\n",
110
111 /* Answer: */
112 "v=0\r\n"
113 "o=bob 4 4 IN IP4 host.example.com\r\n"
114 "s= \r\n"
115 "c=IN IP4 host.example.com\r\n"
116 "t=0 0\r\n"
117 "m=audio 49920 RTP/AVP 4\r\n",
118
119 4
120 }
121};
122
123
124
125typedef enum oa_t
126{
127 OFFERER_NONE,
128 OFFERER_UAC,
129 OFFERER_UAS
130} oa_t;
131
132typedef struct inv_test_param_t
133{
134 char *title;
135 unsigned inv_option;
136 pj_bool_t need_established;
137 unsigned count;
138 oa_t oa[4];
139} inv_test_param_t;
140
141typedef struct inv_test_t
142{
143 inv_test_param_t param;
144 pjsip_inv_session *uac;
145 pjsip_inv_session *uas;
146
147 pj_bool_t complete;
148 pj_bool_t uas_complete,
149 uac_complete;
150
151 unsigned oa_index;
152 unsigned uac_update_cnt,
153 uas_update_cnt;
154} inv_test_t;
155
156
157/**************** GLOBALS ******************/
158static inv_test_t inv_test;
159static unsigned job_cnt;
160
161typedef enum job_type
162{
163 SEND_OFFER,
164 ESTABLISH_CALL
165} job_type;
166
167typedef struct job_t
168{
169 job_type type;
170 pjsip_role_e who;
171} job_t;
172
173static job_t jobs[128];
174
175
176/**************** UTILS ******************/
177static pjmedia_sdp_session *create_sdp(pj_pool_t *pool, const char *body)
178{
179 pjmedia_sdp_session *sdp;
180 pj_str_t dup;
181 pj_status_t status;
182
183 pj_strdup2_with_null(pool, &dup, body);
184 status = pjmedia_sdp_parse(pool, dup.ptr, dup.slen, &sdp);
185 pj_assert(status == PJ_SUCCESS);
186
187 return sdp;
188}
189
190/**************** INVITE SESSION CALLBACKS ******************/
191static void on_rx_offer(pjsip_inv_session *inv,
192 const pjmedia_sdp_session *offer)
193{
194 pjmedia_sdp_session *sdp;
195
196 sdp = create_sdp(inv->dlg->pool, oa_sdp[inv_test.oa_index].answer);
197 pjsip_inv_set_sdp_answer(inv, sdp);
198
199 if (inv_test.oa_index == inv_test.param.count-1 &&
200 inv_test.param.need_established)
201 {
202 jobs[job_cnt].type = ESTABLISH_CALL;
203 jobs[job_cnt].who = PJSIP_ROLE_UAS;
204 job_cnt++;
205 }
206}
207
208
209static void on_create_offer(pjsip_inv_session *inv,
210 pjmedia_sdp_session **p_offer)
211{
212 pj_assert(!"Should not happen");
213}
214
215static void on_media_update(pjsip_inv_session *inv_ses,
216 pj_status_t status)
217{
218 if (inv_ses == inv_test.uas) {
219 inv_test.uas_update_cnt++;
220 pj_assert(inv_test.uas_update_cnt - inv_test.uac_update_cnt <= 1);
221 TRACE_((THIS_FILE, " Callee media is established"));
222 } else if (inv_ses == inv_test.uac) {
223 inv_test.uac_update_cnt++;
224 pj_assert(inv_test.uac_update_cnt - inv_test.uas_update_cnt <= 1);
225 TRACE_((THIS_FILE, " Caller media is established"));
226
227 } else {
228 pj_assert(!"Unknown session!");
229 }
230
231 if (inv_test.uac_update_cnt == inv_test.uas_update_cnt) {
232 inv_test.oa_index++;
233
234 if (inv_test.oa_index < inv_test.param.count) {
235 switch (inv_test.param.oa[inv_test.oa_index]) {
236 case OFFERER_UAC:
237 jobs[job_cnt].type = SEND_OFFER;
238 jobs[job_cnt].who = PJSIP_ROLE_UAC;
239 job_cnt++;
240 break;
241 case OFFERER_UAS:
242 jobs[job_cnt].type = SEND_OFFER;
243 jobs[job_cnt].who = PJSIP_ROLE_UAS;
244 job_cnt++;
245 break;
246 default:
247 pj_assert(!"Invalid oa");
248 }
249 }
250
251 pj_assert(job_cnt <= PJ_ARRAY_SIZE(jobs));
252 }
253}
254
255static void on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
256{
257 const char *who = NULL;
258
259 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
260 TRACE_((THIS_FILE, " %s call disconnected",
261 (inv==inv_test.uas ? "Callee" : "Caller")));
262 return;
263 }
264
265 if (inv->state != PJSIP_INV_STATE_CONFIRMED)
266 return;
267
268 if (inv == inv_test.uas) {
269 inv_test.uas_complete = PJ_TRUE;
270 who = "Callee";
271 } else if (inv == inv_test.uac) {
272 inv_test.uac_complete = PJ_TRUE;
273 who = "Caller";
274 } else
275 pj_assert(!"No session");
276
277 TRACE_((THIS_FILE, " %s call is confirmed", who));
278
279 if (inv_test.uac_complete && inv_test.uas_complete)
280 inv_test.complete = PJ_TRUE;
281}
282
283
284/**************** MODULE TO RECEIVE INITIAL INVITE ******************/
285
286static pj_bool_t on_rx_request(pjsip_rx_data *rdata)
287{
288 if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG &&
289 rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD)
290 {
291 pjsip_dialog *dlg;
292 pjmedia_sdp_session *sdp;
293 pj_str_t uri;
294 pjsip_tx_data *tdata;
295 pj_status_t status;
296
297 /*
298 * Create UAS
299 */
300 uri = pj_str(CONTACT);
301 status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
302 &uri, &dlg);
303 pj_assert(status == PJ_SUCCESS);
304
305 if (inv_test.param.oa[0] == OFFERER_UAC)
306 sdp = create_sdp(rdata->tp_info.pool, oa_sdp[0].answer);
307 else if (inv_test.param.oa[0] == OFFERER_UAS)
308 sdp = create_sdp(rdata->tp_info.pool, oa_sdp[0].offer);
309 else
310 pj_assert(!"Invalid offerer type");
311
312 status = pjsip_inv_create_uas(dlg, rdata, sdp, inv_test.param.inv_option, &inv_test.uas);
313 pj_assert(status == PJ_SUCCESS);
314
315 TRACE_((THIS_FILE, " Sending 183 with SDP"));
316
317 /*
318 * Answer with 183
319 */
320 status = pjsip_inv_initial_answer(inv_test.uas, rdata, 183, NULL,
321 NULL, &tdata);
322 pj_assert(status == PJ_SUCCESS);
323
324 status = pjsip_inv_send_msg(inv_test.uas, tdata);
325 pj_assert(status == PJ_SUCCESS);
326
327 return PJ_TRUE;
328 }
329
330 return PJ_FALSE;
331}
332
333static pjsip_module mod_inv_oa_test =
334{
335 NULL, NULL, /* prev, next. */
336 { "mod-inv-oa-test", 15 }, /* Name. */
337 -1, /* Id */
338 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
339 NULL, /* load() */
340 NULL, /* start() */
341 NULL, /* stop() */
342 NULL, /* unload() */
343 &on_rx_request, /* on_rx_request() */
344 NULL, /* on_rx_response() */
345 NULL, /* on_tx_request. */
346 NULL, /* on_tx_response() */
347 NULL, /* on_tsx_state() */
348};
349
350
351/**************** THE TEST ******************/
352static void run_job(job_t *j)
353{
354 pjsip_inv_session *inv;
355 pjsip_tx_data *tdata;
356 pjmedia_sdp_session *sdp;
357 pj_status_t status;
358
359 if (j->who == PJSIP_ROLE_UAC)
360 inv = inv_test.uac;
361 else
362 inv = inv_test.uas;
363
364 switch (j->type) {
365 case SEND_OFFER:
366 sdp = create_sdp(inv->dlg->pool, oa_sdp[inv_test.oa_index].offer);
367
368 TRACE_((THIS_FILE, " Sending UPDATE with offer"));
369 status = pjsip_inv_update(inv, NULL, sdp, &tdata);
370 pj_assert(status == PJ_SUCCESS);
371
372 status = pjsip_inv_send_msg(inv, tdata);
373 pj_assert(status == PJ_SUCCESS);
374 break;
375 case ESTABLISH_CALL:
376 TRACE_((THIS_FILE, " Sending 200/OK"));
377 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
378 pj_assert(status == PJ_SUCCESS);
379
380 status = pjsip_inv_send_msg(inv, tdata);
381 pj_assert(status == PJ_SUCCESS);
382 break;
383 }
384}
385
386
387static int perform_test(inv_test_param_t *param)
388{
389 pj_str_t uri;
390 pjsip_dialog *dlg;
391 pjmedia_sdp_session *sdp;
392 pjsip_tx_data *tdata;
393 pj_status_t status;
394
395 PJ_LOG(3,(THIS_FILE, " %s", param->title));
396
397 pj_bzero(&inv_test, sizeof(inv_test));
398 pj_memcpy(&inv_test.param, param, sizeof(*param));
399 job_cnt = 0;
400
401 uri = pj_str(CONTACT);
402
403 /*
404 * Create UAC
405 */
406 status = pjsip_dlg_create_uac(pjsip_ua_instance(),
407 &uri, &uri, &uri, &uri, &dlg);
408 PJ_ASSERT_RETURN(status==PJ_SUCCESS, -10);
409
410 if (inv_test.param.oa[0] == OFFERER_UAC)
411 sdp = create_sdp(dlg->pool, oa_sdp[0].offer);
412 else
413 sdp = NULL;
414
415 status = pjsip_inv_create_uac(dlg, sdp, inv_test.param.inv_option, &inv_test.uac);
416 PJ_ASSERT_RETURN(status==PJ_SUCCESS, -20);
417
418 TRACE_((THIS_FILE, " Sending INVITE %s offer", (sdp ? "with" : "without")));
419
420 /*
421 * Make call!
422 */
423 status = pjsip_inv_invite(inv_test.uac, &tdata);
424 PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30);
425
426 status = pjsip_inv_send_msg(inv_test.uac, tdata);
427 PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30);
428
429 /*
430 * Wait until test completes
431 */
432 while (!inv_test.complete) {
433 pj_time_val delay = {0, 20};
434
435 pjsip_endpt_handle_events(endpt, &delay);
436
437 while (job_cnt) {
438 job_t j;
439
440 j = jobs[0];
441 pj_array_erase(jobs, sizeof(jobs[0]), job_cnt, 0);
442 --job_cnt;
443
444 run_job(&j);
445 }
446 }
447
448 flush_events(100);
449
450 /*
451 * Hangup
452 */
453 TRACE_((THIS_FILE, " Disconnecting call"));
454 status = pjsip_inv_end_session(inv_test.uas, PJSIP_SC_DECLINE, 0, &tdata);
455 pj_assert(status == PJ_SUCCESS);
456
457 status = pjsip_inv_send_msg(inv_test.uas, tdata);
458 pj_assert(status == PJ_SUCCESS);
459
460 flush_events(500);
461
462 return 0;
463}
464
465
466static pj_bool_t log_on_rx_msg(pjsip_rx_data *rdata)
467{
468 pjsip_msg *msg = rdata->msg_info.msg;
469 char info[80];
470
471 if (msg->type == PJSIP_REQUEST_MSG)
472 pj_ansi_snprintf(info, sizeof(info), "%.*s",
473 (int)msg->line.req.method.name.slen,
474 msg->line.req.method.name.ptr);
475 else
476 pj_ansi_snprintf(info, sizeof(info), "%d/%.*s",
477 msg->line.status.code,
478 (int)rdata->msg_info.cseq->method.name.slen,
479 rdata->msg_info.cseq->method.name.ptr);
480
481 TRACE_((THIS_FILE, " Received %s %s sdp", info,
482 (msg->body ? "with" : "without")));
483
484 return PJ_FALSE;
485}
486
487
488/* Message logger module. */
489static pjsip_module mod_msg_logger =
490{
491 NULL, NULL, /* prev and next */
492 { "mod-msg-loggee", 14}, /* Name. */
493 -1, /* Id */
494 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
495 NULL, /* load() */
496 NULL, /* start() */
497 NULL, /* stop() */
498 NULL, /* unload() */
499 &log_on_rx_msg, /* on_rx_request() */
500 &log_on_rx_msg, /* on_rx_response() */
501 NULL, /* on_tx_request() */
502 NULL, /* on_tx_response() */
503 NULL, /* on_tsx_state() */
504};
505
506static inv_test_param_t test_params[] =
507{
508/* Normal scenario:
509
510 UAC UAS
511 INVITE (offer) -->
512 200/INVITE (answer) <--
513 ACK -->
514 */
515#if 0
516 {
517 "Standard INVITE with offer",
518 0,
519 PJ_TRUE,
520 1,
521 { OFFERER_UAC }
522 },
523
524 {
525 "Standard INVITE with offer, with 100rel",
526 PJSIP_INV_REQUIRE_100REL,
527 PJ_TRUE,
528 1,
529 { OFFERER_UAC }
530 },
531#endif
532
533/* Delayed offer:
534 UAC UAS
535 INVITE (no SDP) -->
536 200/INVITE (offer) <--
537 ACK (answer) -->
538 */
539#if 1
540 {
541 "INVITE with no offer",
542 0,
543 PJ_TRUE,
544 1,
545 { OFFERER_UAS }
546 },
547
548 {
549 "INVITE with no offer, with 100rel",
550 PJSIP_INV_REQUIRE_100REL,
551 PJ_TRUE,
552 1,
553 { OFFERER_UAS }
554 },
555#endif
556
557/* Subsequent UAC offer with UPDATE:
558
559 UAC UAS
560 INVITE (offer) -->
561 180/rel (answer) <--
562 UPDATE (offer) --> inv_update() on_rx_offer()
563 set_sdp_answer()
564 200/UPDATE (answer) <--
565 200/INVITE <--
566 ACK -->
567*/
568#if 1
569 {
570 "INVITE and UPDATE by UAC",
571 0,
572 PJ_TRUE,
573 2,
574 { OFFERER_UAC, OFFERER_UAC }
575 },
576 {
577 "INVITE and UPDATE by UAC, with 100rel",
578 PJSIP_INV_REQUIRE_100REL,
579 PJ_TRUE,
580 2,
581 { OFFERER_UAC, OFFERER_UAC }
582 },
583#endif
584
585/* Subsequent UAS offer with UPDATE:
586
587 INVITE (offer -->
588 180/rel (answer) <--
589 UPDATE (offer) <-- inv_update()
590 on_rx_offer()
591 set_sdp_answer()
592 200/UPDATE (answer) -->
593 UPDATE (offer) --> on_rx_offer()
594 set_sdp_answer()
595 200/UPDATE (answer) <--
596 200/INVITE <--
597 ACK -->
598
599 */
600 {
601 "INVITE and many UPDATE by UAC and UAS",
602 0,
603 PJ_TRUE,
604 4,
605 { OFFERER_UAC, OFFERER_UAS, OFFERER_UAC, OFFERER_UAS }
606 },
607
608};
609
610
611static pjsip_dialog* on_dlg_forked(pjsip_dialog *first_set, pjsip_rx_data *res)
612{
613 return NULL;
614}
615
616
617static void on_new_session(pjsip_inv_session *inv, pjsip_event *e)
618{
619}
620
621
622int inv_offer_answer_test(void)
623{
624 unsigned i;
625 int rc = 0;
626
627 /* Init UA layer */
628 if (pjsip_ua_instance()->id == -1) {
629 pjsip_ua_init_param ua_param;
630 pj_bzero(&ua_param, sizeof(ua_param));
631 ua_param.on_dlg_forked = &on_dlg_forked;
632 pjsip_ua_init_module(endpt, &ua_param);
633 }
634
635 /* Init inv-usage */
636 if (pjsip_inv_usage_instance()->id == -1) {
637 pjsip_inv_callback inv_cb;
638 pj_bzero(&inv_cb, sizeof(inv_cb));
639 inv_cb.on_media_update = &on_media_update;
640 inv_cb.on_rx_offer = &on_rx_offer;
641 inv_cb.on_create_offer = &on_create_offer;
642 inv_cb.on_state_changed = &on_state_changed;
643 inv_cb.on_new_session = &on_new_session;
644 pjsip_inv_usage_init(endpt, &inv_cb);
645 }
646
647 /* 100rel module */
648 pjsip_100rel_init_module(endpt);
649
650 /* Our module */
651 pjsip_endpt_register_module(endpt, &mod_inv_oa_test);
652 pjsip_endpt_register_module(endpt, &mod_msg_logger);
653
654 /* Create SIP UDP transport */
655 {
656 pj_sockaddr_in addr;
657 pjsip_transport *tp;
658 pj_status_t status;
659
660 pj_sockaddr_in_init(&addr, NULL, PORT);
661 status = pjsip_udp_transport_start(endpt, &addr, NULL, 1, &tp);
662 pj_assert(status == PJ_SUCCESS);
663 }
664
665 /* Do tests */
666 for (i=0; i<PJ_ARRAY_SIZE(test_params); ++i) {
667 rc = perform_test(&test_params[i]);
668 if (rc != 0)
669 goto on_return;
670 }
671
672
673on_return:
674 return rc;
675}
676