📄 sip_ua_layer.c
字号:
/* Unlock user agent. */ pj_mutex_unlock(mod_ua.mutex); } } else { /* Unlock user agent. */ pj_mutex_unlock(mod_ua.mutex); } return dlg;}/* * Find the first dialog in dialog set in hash table for an incoming message. */static struct dlg_set *find_dlg_set_for_msg( pjsip_rx_data *rdata ){ /* CANCEL message doesn't have To tag, so we must lookup the dialog * by finding the INVITE UAS transaction being cancelled. */ if (rdata->msg_info.cseq->method.id == PJSIP_CANCEL_METHOD) { pjsip_dialog *dlg; /* Create key for the rdata, but this time, use INVITE as the * method. */ pj_str_t key; pjsip_role_e role; pjsip_transaction *tsx; if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) role = PJSIP_ROLE_UAS; else role = PJSIP_ROLE_UAC; pjsip_tsx_create_key(rdata->tp_info.pool, &key, role, &pjsip_invite_method, rdata); /* Lookup the INVITE transaction */ tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE); /* We should find the dialog attached to the INVITE transaction */ if (tsx) { dlg = tsx->mod_data[mod_ua.mod.id]; pj_mutex_unlock(tsx->mutex); /* Dlg may be NULL on some extreme condition * (e.g. during debugging where initially there is a dialog) */ return dlg ? dlg->dlg_set : NULL; } else { return NULL; } } else { pj_str_t *tag; struct dlg_set *dlg_set; if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) tag = &rdata->msg_info.to->tag; else tag = &rdata->msg_info.from->tag; /* Lookup the dialog set. */ dlg_set = pj_hash_get(mod_ua.dlg_table, tag->ptr, tag->slen, NULL); return dlg_set; }}/* On received requests. */static pj_bool_t mod_ua_on_rx_request(pjsip_rx_data *rdata){ struct dlg_set *dlg_set; pj_str_t *from_tag; pjsip_dialog *dlg; pj_status_t status; /* Optimized path: bail out early if request is not CANCEL and it doesn't * have To tag */ if (rdata->msg_info.to->tag.slen == 0 && rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD) { return PJ_FALSE; }retry_on_deadlock: /* Lock user agent before looking up the dialog hash table. */ pj_mutex_lock(mod_ua.mutex); /* Lookup the dialog set, based on the To tag header. */ dlg_set = find_dlg_set_for_msg(rdata); /* If dialog is not found, respond with 481 (Call/Transaction * Does Not Exist). */ if (dlg_set == NULL) { /* Unable to find dialog. */ pj_mutex_unlock(mod_ua.mutex); if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { PJ_LOG(5,(THIS_FILE, "Unable to find dialogset for %s, answering with 481", pjsip_rx_data_get_info(rdata))); /* Respond with 481 . */ pjsip_endpt_respond_stateless( mod_ua.endpt, rdata, 481, NULL, NULL, NULL ); } return PJ_TRUE; } /* Dialog set has been found. * Find the dialog in the dialog set based on the content of the remote * tag. */ from_tag = &rdata->msg_info.from->tag; dlg = dlg_set->dlg_list.next; while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { if (pj_strcmp(&dlg->remote.info->tag, from_tag) == 0) break; dlg = dlg->next; } /* Dialog may not be found, e.g. in this case: * - UAC sends SUBSCRIBE, then UAS sends NOTIFY before answering * SUBSCRIBE request with 2xx. * * In this case, we can accept the request ONLY when the original * dialog still has empty To tag. */ if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { pjsip_dialog *first_dlg = dlg_set->dlg_list.next; if (first_dlg->remote.info->tag.slen != 0) { /* Not found. Mulfunction UAC? */ pj_mutex_unlock(mod_ua.mutex); if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { PJ_LOG(5,(THIS_FILE, "Unable to find dialog for %s, answering with 481", pjsip_rx_data_get_info(rdata))); pjsip_endpt_respond_stateless(mod_ua.endpt, rdata, PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL); } else { PJ_LOG(5,(THIS_FILE, "Unable to find dialog for %s", pjsip_rx_data_get_info(rdata))); } return PJ_TRUE; } dlg = first_dlg; } /* Mark the dialog id of the request. */ rdata->endpt_info.mod_data[mod_ua.mod.id] = dlg; /* Try to lock the dialog */ PJ_LOG(6,(dlg->obj_name, "UA layer acquiring dialog lock for request")); status = pjsip_dlg_try_inc_lock(dlg); if (status != PJ_SUCCESS) { /* Failed to acquire dialog mutex immediately, this could be * because of deadlock. Release UA mutex, yield, and retry * the whole thing once again. */ pj_mutex_unlock(mod_ua.mutex); pj_thread_sleep(0); goto retry_on_deadlock; } /* Done with processing in UA layer, release lock */ pj_mutex_unlock(mod_ua.mutex); /* Pass to dialog. */ pjsip_dlg_on_rx_request(dlg, rdata); /* Unlock the dialog. This may destroy the dialog */ pjsip_dlg_dec_lock(dlg); /* Report as handled. */ return PJ_TRUE;}/* On rx response notification. */static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata){ pjsip_transaction *tsx; struct dlg_set *dlg_set; pjsip_dialog *dlg; pj_status_t status; /* * Find the dialog instance for the response. * All outgoing dialog requests are sent statefully, which means * there will be an UAC transaction associated with this response, * and the dialog instance will be recorded in that transaction. * * But even when transaction is found, there is possibility that * the response is a forked response. */retry_on_deadlock: dlg = NULL; /* Lock user agent dlg table before we're doing anything. */ pj_mutex_lock(mod_ua.mutex); /* Check if transaction is present. */ tsx = pjsip_rdata_get_tsx(rdata); if (tsx) { /* Check if dialog is present in the transaction. */ dlg = pjsip_tsx_get_dlg(tsx); if (!dlg) { /* Unlock dialog hash table. */ pj_mutex_unlock(mod_ua.mutex); return PJ_FALSE; } /* Get the dialog set. */ dlg_set = dlg->dlg_set; /* Even if transaction is found and (candidate) dialog has been * identified, it's possible that the request has forked. */ } else { /* Transaction is not present. * Check if this is a 2xx/OK response to INVITE, which in this * case the response will be handled directly by the * dialog. */ pjsip_cseq_hdr *cseq_hdr = rdata->msg_info.cseq; if (cseq_hdr->method.id != PJSIP_INVITE_METHOD || rdata->msg_info.msg->line.status.code / 100 != 2) { /* Not a 2xx response to INVITE. * This must be some stateless response sent by other modules, * or a very late response. */ /* Unlock dialog hash table. */ pj_mutex_unlock(mod_ua.mutex); return PJ_FALSE; } /* Get the dialog set. */ dlg_set = pj_hash_get(mod_ua.dlg_table, rdata->msg_info.from->tag.ptr, rdata->msg_info.from->tag.slen, NULL); if (!dlg_set) { /* Unlock dialog hash table. */ pj_mutex_unlock(mod_ua.mutex); /* Strayed 2xx response!! */ PJ_LOG(4,(THIS_FILE, "Received strayed 2xx response (no dialog is found)" " from %s:%d: %s", rdata->pkt_info.src_name, rdata->pkt_info.src_port, pjsip_rx_data_get_info(rdata))); return PJ_TRUE; } } /* At this point, we must have the dialog set, and the dialog set * must have a dialog in the list. */ pj_assert(dlg_set && !pj_list_empty(&dlg_set->dlg_list)); /* Check for forked response. * Request will fork only for the initial INVITE request. */ //This doesn't work when there is authentication challenge, since //first_cseq evaluation will yield false. //if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD && // rdata->msg_info.cseq->cseq == dlg_set->dlg_list.next->local.first_cseq) if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) { int st_code = rdata->msg_info.msg->line.status.code; pj_str_t *to_tag = &rdata->msg_info.to->tag; dlg = dlg_set->dlg_list.next; while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { /* If there is dialog with no remote tag (i.e. dialog has not * been established yet), then send this response to that * dialog. */ if (dlg->remote.info->tag.slen == 0) break; /* Otherwise find the one with matching To tag. */ if (pj_strcmp(to_tag, &dlg->remote.info->tag) == 0) break; dlg = dlg->next; } /* If no dialog with matching remote tag is found, this must be * a forked response. Respond to this ONLY when response is non-100 * provisional response OR a 2xx response. */ if (dlg == (pjsip_dialog*)&dlg_set->dlg_list && ((st_code/100==1 && st_code!=100) || st_code/100==2)) { PJ_LOG(5,(THIS_FILE, "Received forked %s for existing dialog %s", pjsip_rx_data_get_info(rdata), dlg_set->dlg_list.next->obj_name)); /* Report to application about forked condition. * Application can either create a dialog or ignore the response. */ if (mod_ua.param.on_dlg_forked) { dlg = (*mod_ua.param.on_dlg_forked)(dlg_set->dlg_list.next, rdata); } else { dlg = dlg_set->dlg_list.next; PJ_LOG(4,(THIS_FILE, "Unhandled forked %s from %s:%d, response will be " "handed over to the first dialog", pjsip_rx_data_get_info(rdata), rdata->pkt_info.src_name, rdata->pkt_info.src_port)); } } else if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { /* For 100 or non-2xx response which has different To tag, * pass the response to the first dialog. */ dlg = dlg_set->dlg_list.next; } } else { /* Either this is a non-INVITE response, or subsequent INVITE * within dialog. The dialog should have been identified when * the transaction was found. */ pj_assert(tsx != NULL); pj_assert(dlg != NULL); } /* The dialog must have been found. */ pj_assert(dlg != NULL); /* Put the dialog instance in the rdata. */ rdata->endpt_info.mod_data[mod_ua.mod.id] = dlg; /* Attempt to acquire lock to the dialog. */ PJ_LOG(6,(dlg->obj_name, "UA layer acquiring dialog lock for response")); status = pjsip_dlg_try_inc_lock(dlg); if (status != PJ_SUCCESS) { /* Failed to acquire dialog mutex. This could indicate a deadlock * situation, and for safety, try to avoid deadlock by releasing * UA mutex, yield, and retry the whole processing once again. */ pj_mutex_unlock(mod_ua.mutex); pj_thread_sleep(0); goto retry_on_deadlock; } /* We're done with processing in the UA layer, we can release the mutex */ pj_mutex_unlock(mod_ua.mutex); /* Pass the response to the dialog. */ pjsip_dlg_on_rx_response(dlg, rdata); /* Unlock the dialog. This may destroy the dialog. */ pjsip_dlg_dec_lock(dlg); /* Done. */ return PJ_TRUE;}#if PJ_LOG_MAX_LEVEL >= 3static void print_dialog( const char *title, pjsip_dialog *dlg, char *buf, pj_size_t size){ int len; char userinfo[128]; len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); if (len < 1) pj_ansi_strcpy(userinfo, "<--uri too long-->"); else userinfo[len] = '\0'; len = pj_ansi_snprintf(buf, size, "%s[%s] %s", title, (dlg->state==PJSIP_DIALOG_STATE_NULL ? " - " : "est"), userinfo); if (len < 1 || len >= (int)size) { pj_ansi_strcpy(buf, "<--uri too long-->"); } else buf[len] = '\0';}#endif/* * Dump user agent contents (e.g. all dialogs). */PJ_DEF(void) pjsip_ua_dump(pj_bool_t detail){#if PJ_LOG_MAX_LEVEL >= 3 pj_hash_iterator_t itbuf, *it; char dlginfo[128]; pj_mutex_lock(mod_ua.mutex); PJ_LOG(3, (THIS_FILE, "Number of dialog sets: %u", pj_hash_count(mod_ua.dlg_table))); if (detail && pj_hash_count(mod_ua.dlg_table)) { PJ_LOG(3, (THIS_FILE, "Dumping dialog sets:")); it = pj_hash_first(mod_ua.dlg_table, &itbuf); for (; it != NULL; it = pj_hash_next(mod_ua.dlg_table, it)) { struct dlg_set *dlg_set; pjsip_dialog *dlg; const char *title; dlg_set = pj_hash_this(mod_ua.dlg_table, it); if (!dlg_set || pj_list_empty(&dlg_set->dlg_list)) continue; /* First dialog in dialog set. */ dlg = dlg_set->dlg_list.next; if (dlg->role == PJSIP_ROLE_UAC) title = " [out] "; else title = " [in] "; print_dialog(title, dlg, dlginfo, sizeof(dlginfo)); PJ_LOG(3,(THIS_FILE, "%s", dlginfo)); /* Next dialog in dialog set (forked) */ dlg = dlg->next; while (dlg != (pjsip_dialog*) &dlg_set->dlg_list) { print_dialog(" [forked] ", dlg, dlginfo, sizeof(dlginfo)); dlg = dlg->next; } } } pj_mutex_unlock(mod_ua.mutex);#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -