Benny Prijono | dd859a6 | 2005-11-01 16:42:51 +0000 | [diff] [blame^] | 1 | /* $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 | */ |
| 27 | static pj_status_t ua_init( pjsip_endpoint *endpt, |
| 28 | struct pjsip_module *mod, pj_uint32_t id ); |
| 29 | static pj_status_t ua_start( struct pjsip_module *mod ); |
| 30 | static pj_status_t ua_deinit( struct pjsip_module *mod ); |
| 31 | static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt ); |
| 32 | static pjsip_dlg *find_dialog( pjsip_user_agent *ua, |
| 33 | pjsip_rx_data *rdata ); |
| 34 | static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg, |
| 35 | pj_str_t *key ); |
| 36 | PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg ); |
| 37 | |
| 38 | /* |
| 39 | * Default UA instance. |
| 40 | */ |
| 41 | static pjsip_user_agent ua_instance; |
| 42 | |
| 43 | /* |
| 44 | * Module interface. |
| 45 | */ |
| 46 | static 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 | */ |
| 63 | static 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 | */ |
| 112 | static 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 | */ |
| 121 | static 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 | */ |
| 137 | PJ_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 | */ |
| 146 | PJ_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 | */ |
| 157 | static 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 | */ |
| 210 | static 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 | */ |
| 361 | static 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 | */ |
| 379 | PJ_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 | */ |
| 426 | PJ_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 | */ |
| 467 | PJ_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 | |