Benny Prijono | 0a749f1 | 2005-10-31 21:02:30 +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 |
|