blob: bd7fe015281e74a52877a6cf71bb9c61fc524bcd [file] [log] [blame]
Benny Prijonodd859a62005-11-01 16:42:51 +00001/* $Header: /pjproject-0.3/pjsip/src/pjsip_mod_ua/sip_ua.c 16 10/14/05 12:23a Bennylp $ */
2#include <pjsip_mod_ua/sip_ua.h>
3#include <pjsip_mod_ua/sip_dialog.h>
4#include <pjsip_mod_ua/sip_ua_private.h>
5#include <pjsip/sip_module.h>
6#include <pjsip/sip_event.h>
7#include <pjsip/sip_misc.h>
8#include <pjsip/sip_endpoint.h>
9#include <pjsip/sip_transaction.h>
10#include <pj/list.h>
11#include <pj/log.h>
12#include <pj/string.h>
13#include <pj/guid.h>
14#include <pj/os.h>
15#include <pj/hash.h>
16#include <pj/pool.h>
17
18#define PJSIP_POOL_LEN_USER_AGENT 1024
19#define PJSIP_POOL_INC_USER_AGENT 0
20
21
22#define LOG_THIS "useragent.."
23
24/*
25 * Static prototypes.
26 */
27static pj_status_t ua_init( pjsip_endpoint *endpt,
28 struct pjsip_module *mod, pj_uint32_t id );
29static pj_status_t ua_start( struct pjsip_module *mod );
30static pj_status_t ua_deinit( struct pjsip_module *mod );
31static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt );
32static pjsip_dlg *find_dialog( pjsip_user_agent *ua,
33 pjsip_rx_data *rdata );
34static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
35 pj_str_t *key );
36PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg );
37
38/*
39 * Default UA instance.
40 */
41static pjsip_user_agent ua_instance;
42
43/*
44 * Module interface.
45 */
46static struct pjsip_module mod_ua =
47{
48 { "User-Agent", 10 }, /* Name. */
49 0, /* Flag */
50 128, /* Priority */
51 NULL, /* User agent instance, initialized by APP. */
52 0, /* Number of methods supported (will be initialized later). */
53 { 0 }, /* Array of methods (will be initialized later) */
54 &ua_init, /* init_module() */
55 &ua_start, /* start_module() */
56 &ua_deinit, /* deinit_module() */
57 &ua_tsx_handler, /* tsx_handler() */
58};
59
60/*
61 * Initialize user agent instance.
62 */
63static pj_status_t ua_init( pjsip_endpoint *endpt,
64 struct pjsip_module *mod, pj_uint32_t id )
65{
66 static pjsip_method m_invite, m_ack, m_cancel, m_bye;
67 pjsip_user_agent *ua = mod->mod_data;
68 extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */
69
70 pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD );
71 pjsip_method_set( &m_ack, PJSIP_ACK_METHOD );
72 pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD );
73 pjsip_method_set( &m_bye, PJSIP_BYE_METHOD );
74
75 mod->method_cnt = 4;
76 mod->methods[0] = &m_invite;
77 mod->methods[1] = &m_ack;
78 mod->methods[2] = &m_cancel;
79 mod->methods[3] = &m_bye;
80
81 /* Initialize the user agent. */
82 ua->endpt = endpt;
83 ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA,
84 PJSIP_POOL_INC_UA);
85 if (!ua->pool) {
86 return -1;
87 }
88 ua->mod_id = id;
89 ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0);
90 if (!ua->mutex) {
91 return -1;
92 }
93 ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT);
94 if (ua->dlg_table == NULL) {
95 return -1;
96 }
97 pj_list_init(&ua->dlg_list);
98
99 /* Initialize dialog lock. */
100 pjsip_dlg_lock_tls_id = pj_thread_local_alloc();
101 if (pjsip_dlg_lock_tls_id == -1) {
102 return -1;
103 }
104 pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL);
105
106 return 0;
107}
108
109/*
110 * Start user agent instance.
111 */
112static pj_status_t ua_start( struct pjsip_module *mod )
113{
114 PJ_UNUSED_ARG(mod)
115 return 0;
116}
117
118/*
119 * Destroy user agent.
120 */
121static pj_status_t ua_deinit( struct pjsip_module *mod )
122{
123 pjsip_user_agent *ua = mod->mod_data;
124
125 pj_mutex_unlock(ua->mutex);
126
127 /* Release pool */
128 if (ua->pool) {
129 pjsip_endpt_destroy_pool( ua->endpt, ua->pool );
130 }
131 return 0;
132}
133
134/*
135 * Get the module interface for the UA module.
136 */
137PJ_DEF(pjsip_module*) pjsip_ua_get_module(void)
138{
139 mod_ua.mod_data = &ua_instance;
140 return &mod_ua;
141}
142
143/*
144 * Register callback to receive dialog notifications.
145 */
146PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
147 pjsip_dlg_callback *cb )
148{
149 ua->dlg_cb = cb;
150}
151
152/*
153 * Find dialog.
154 * This function is called for a new transactions, which a dialog hasn't been
155 * 'attached' to the transaction.
156 */
157static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata )
158{
159 pjsip_dlg *dlg;
160 pj_str_t *tag;
161
162 /* Non-CANCEL requests/response can be found by looking at the tag in the
163 * hash table. CANCEL requests don't have tags, so instead we'll try to
164 * find the UAS INVITE transaction in endpoint's hash table
165 */
166 if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) {
167
168 /* Create key for the rdata, but this time, use INVITE as the
169 * method.
170 */
171 pj_str_t key;
172 pjsip_role_e role;
173 pjsip_method invite_method;
174 pjsip_transaction *invite_tsx;
175
176 if (rdata->msg->type == PJSIP_REQUEST_MSG) {
177 role = PJSIP_ROLE_UAS;
178 } else {
179 role = PJSIP_ROLE_UAC;
180 }
181 pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD);
182 pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata);
183
184 /* Lookup the INVITE transaction */
185 invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key);
186
187 /* We should find the dialog attached to the INVITE transaction */
188 return invite_tsx ?
189 (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL;
190
191 } else {
192 if (rdata->msg->type == PJSIP_REQUEST_MSG) {
193 tag = &rdata->to_tag;
194 } else {
195 tag = &rdata->from_tag;
196 }
197 /* Find the dialog in UA hash table */
198 pj_mutex_lock(ua->mutex);
199 dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen );
200 pj_mutex_unlock(ua->mutex);
201 }
202
203 return dlg;
204}
205
206/*
207 * This function receives event notification from transactions. It is called by
208 * endpoint.
209 */
210static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
211{
212 pjsip_user_agent *ua = mod->mod_data;
213 pjsip_dlg *dlg = NULL;
214 pjsip_transaction *tsx = event->obj.tsx;
215
216 PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)",
217 (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type),
218 pjsip_event_str(event->src_type), event->src.data));
219
220 /* Special case to handle ACK which doesn't match any INVITE transactions. */
221 if (event->type == PJSIP_EVENT_RX_ACK_MSG) {
222 /* Find the dialog based on the "tag". */
223 dlg = find_dialog( ua, event->src.rdata );
224
225 /* We should be able to find it. */
226 if (!dlg) {
227 PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK"));
228 return;
229 }
230
231 /* Match CSeq with pending INVITE in dialog. */
232 if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) {
233 /* A match found. */
234 tsx = dlg->invite_tsx;
235
236 /* Pass the event to transaction if transaction handles ACK. */
237 if (tsx->handle_ack) {
238 PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction"));
239 pjsip_tsx_on_rx_msg(tsx, event->src.rdata);
240 return;
241 }
242 } else {
243 tsx = NULL;
244 PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK"));
245 return;
246 }
247 }
248
249 /* For discard event, transaction is NULL. */
250 if (tsx == NULL) {
251 return;
252 }
253
254 /* Try to pickup the dlg from the transaction. */
255 dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id];
256
257 if (dlg != NULL) {
258
259 /* Nothing to do now. */
260
261 } else if (event->src_type == PJSIP_EVENT_RX_MSG) {
262
263 /* This must be a new UAS transaction. */
264
265 /* Finds dlg that can handle this transaction. */
266 dlg = find_dialog( ua, event->src.rdata);
267
268 /* Create a new dlg if there's no existing dlg that can handle
269 the request, ONLY if the incoming message is an INVITE request.
270 */
271 if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) {
272
273 if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
274 /* Create new dialog. */
275 dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS );
276
277 if (dlg == NULL ||
278 pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0)
279 {
280 pjsip_tx_data *tdata;
281
282 /* Dialog initialization has failed. Respond request with 500 */
283 if (dlg) {
284 pjsip_ua_destroy_dialog(dlg);
285 }
286 tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
287 PJSIP_SC_INTERNAL_SERVER_ERROR);
288 if (tdata) {
289 pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
290 }
291 return;
292 }
293
294 } else {
295 pjsip_tx_data *tdata;
296
297 /* Check the method */
298 switch (tsx->method.id) {
299 case PJSIP_INVITE_METHOD:
300 case PJSIP_ACK_METHOD:
301 case PJSIP_BYE_METHOD:
302 case PJSIP_CANCEL_METHOD:
303 /* Stale non-INVITE request.
304 * For now, respond all stale requests with 481 (?).
305 */
306 tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
307 PJSIP_SC_CALL_TSX_DOES_NOT_EXIST);
308 if (tdata) {
309 pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
310 }
311 break;
312 }
313
314 return;
315 }
316 } else {
317 /* Check the method */
318 switch (tsx->method.id) {
319 case PJSIP_INVITE_METHOD:
320 case PJSIP_ACK_METHOD:
321 case PJSIP_BYE_METHOD:
322 case PJSIP_CANCEL_METHOD:
323 /* These methods belongs to dialog.
324 * If we receive these methods while no dialog is found,
325 * then it must be a stale responses.
326 */
327 break;
328 default:
329 return;
330 }
331
332 }
333
334 if (dlg == NULL) {
335 PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d",
336 event->src.rdata,
337 pj_sockaddr_get_str_addr(&event->src.rdata->addr),
338 pj_sockaddr_get_port(&event->src.rdata->addr)));
339 }
340
341 /* Set the dlg in the transaction (dlg can be NULL). */
342 tsx->module_data[ua->mod_id] = dlg;
343
344 } else {
345 /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG
346 * if UAS is responding to a transaction which does not exist.
347 * Just ignore.
348 */
349 return;
350 }
351
352 /* Pass the event to the dlg. */
353 if (dlg) {
354 pjsip_dlg_on_tsx_event(dlg, tsx, event);
355 }
356}
357
358/*
359 * Register dialog to UA.
360 */
361static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
362 pj_str_t *key )
363{
364 /* Assure that no entry with similar key exists in the hash table. */
365 pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0);
366
367 /* Insert entry to hash table. */
368 pj_hash_set( dlg->pool, ua->dlg_table,
369 key->ptr, key->slen, dlg);
370
371 /* Insert to the list. */
372 pj_list_insert_before(&ua->dlg_list, dlg);
373 return PJ_SUCCESS;
374}
375
376/*
377 * Create a new dialog.
378 */
379PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
380 pjsip_role_e role )
381{
382 pj_pool_t *pool;
383 pjsip_dlg *dlg;
384
385 PJ_UNUSED_ARG(ua)
386
387 /* Create pool for the dialog. */
388 pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p",
389 PJSIP_POOL_LEN_DIALOG,
390 PJSIP_POOL_INC_DIALOG);
391
392 /* Create the dialog. */
393 dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg));
394 dlg->pool = pool;
395 dlg->ua = ua;
396 dlg->role = role;
397 sprintf(dlg->obj_name, "dlg%p", dlg);
398
399 /* Create mutex for the dialog. */
400 dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
401 if (!dlg->mutex) {
402 pjsip_endpt_destroy_pool(ua->endpt, pool);
403 return NULL;
404 }
405
406 /* Create unique tag for the dialog. */
407 pj_create_unique_string( pool, &dlg->local.tag );
408
409 /* Register dialog. */
410 pj_mutex_lock(ua->mutex);
411 if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) {
412 pj_mutex_unlock(ua->mutex);
413 pj_mutex_destroy(dlg->mutex);
414 pjsip_endpt_destroy_pool( ua->endpt, pool );
415 return NULL;
416 }
417 pj_mutex_unlock(ua->mutex);
418
419 PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role)));
420 return dlg;
421}
422
423/*
424 * Destroy dialog.
425 */
426PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg )
427{
428 PJ_LOG(5, (dlg->obj_name, "destroying.."));
429
430 /* Lock dialog's mutex.
431 * Check the mutex validity first since this function can be called
432 * on dialog initialization failure (which might be because mutex could not
433 * be allocated in the first place).
434 */
435 if (dlg->mutex) {
436 pj_mutex_lock(dlg->mutex);
437 }
438
439 /* This must be called while holding dialog's mutex, if any. */
440 pjsip_on_dialog_destroyed(dlg);
441
442 /* Lock UA. */
443 pj_mutex_lock(dlg->ua->mutex);
444
445 /* Erase from hash table. */
446 pj_hash_set( dlg->pool, dlg->ua->dlg_table,
447 dlg->local.tag.ptr, dlg->local.tag.slen, NULL);
448
449 /* Erase from the list. */
450 pj_list_erase(dlg);
451
452 /* Unlock UA. */
453 pj_mutex_unlock(dlg->ua->mutex);
454
455 /* Unlock mutex. */
456 if (dlg->mutex) {
457 pj_mutex_unlock(dlg->mutex);
458 }
459
460 /* Destroy the pool. */
461 pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool);
462}
463
464/*
465 * Dump user agent state to log file.
466 */
467PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua)
468{
469#if PJ_LOG_MAX_LEVEL >= 3
470 PJ_LOG(3,(LOG_THIS, "Dumping user agent"));
471 PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u",
472 pj_pool_get_capacity(ua->pool),
473 pj_pool_get_used_size(ua->pool)));
474 PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table)));
475
476 if (pj_hash_count(ua->dlg_table)) {
477 pjsip_dlg *dlg;
478
479 PJ_LOG(3,(LOG_THIS, " Dumping dialog list:"));
480 dlg = ua->dlg_list.next;
481 while (dlg != (pjsip_dlg*) &ua->dlg_list) {
482 PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name,
483 pjsip_dlg_state_str(dlg->state)));
484 dlg = dlg->next;
485 }
486 }
487#endif
488}
489