⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_dialog.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 4 页
字号:

    /* Add tag etc. if necessary */
    dlg_beautify_response(dlg, PJ_FALSE, st_code, tdata);


    /* Must add reference counter, since tsx_send_msg() will decrement it */
    pjsip_tx_data_add_ref(tdata);

    /* Force to re-print message. */
    pjsip_tx_data_invalidate_msg(tdata);

    /* Unlock dialog and dec session, may destroy dialog. */
    pjsip_dlg_dec_lock(dlg);

    return PJ_SUCCESS;
}

/*
 * Send response statefully.
 */
PJ_DEF(pj_status_t) pjsip_dlg_send_response( pjsip_dialog *dlg,
					     pjsip_transaction *tsx,
					     pjsip_tx_data *tdata)
{
    pj_status_t status;

    /* Sanity check. */
    PJ_ASSERT_RETURN(dlg && tsx && tdata && tdata->msg, PJ_EINVAL);
    PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG,
		     PJSIP_ENOTRESPONSEMSG);

    /* The transaction must belong to this dialog.  */
    PJ_ASSERT_RETURN(tsx->mod_data[dlg->ua->id] == dlg, PJ_EINVALIDOP);

    PJ_LOG(5,(dlg->obj_name, "Sending %s",
	      pjsip_tx_data_get_info(tdata)));

    /* Check that transaction method and cseq match the response. 
     * This operation is sloooww (search CSeq header twice), that's why
     * we only do it in debug mode.
     */
#if defined(PJ_DEBUG) && PJ_DEBUG!=0
    PJ_ASSERT_RETURN( PJSIP_MSG_CSEQ_HDR(tdata->msg)->cseq == tsx->cseq &&
		      pjsip_method_cmp(&PJSIP_MSG_CSEQ_HDR(tdata->msg)->method, 
				       &tsx->method)==0,
		      PJ_EINVALIDOP);
#endif

    /* Must acquire dialog first, to prevent deadlock */
    pjsip_dlg_inc_lock(dlg);

    /* Last chance to add mandatory headers before the response is
     * sent.
     */
    dlg_beautify_response(dlg, PJ_TRUE, tdata->msg->line.status.code, tdata);

    /* If the dialog is locked to transport, make sure that transaction
     * is locked to the same transport too.
     */
    if (dlg->tp_sel.type != tsx->tp_sel.type ||
	dlg->tp_sel.u.ptr != tsx->tp_sel.u.ptr)
    {
	status = pjsip_tsx_set_transport(tsx, &dlg->tp_sel);
	pj_assert(status == PJ_SUCCESS);
    }

    /* Ask transaction to send the response */
    status = pjsip_tsx_send_msg(tsx, tdata);

    pjsip_dlg_dec_lock(dlg);

    return status;
}


/*
 * Combo function to create and send response statefully.
 */
PJ_DEF(pj_status_t) pjsip_dlg_respond(  pjsip_dialog *dlg,
					pjsip_rx_data *rdata,
					int st_code,
					const pj_str_t *st_text,
					const pjsip_hdr *hdr_list,
					const pjsip_msg_body *body )
{
    pj_status_t status;
    pjsip_tx_data *tdata;

    /* Sanity check. */
    PJ_ASSERT_RETURN(dlg && rdata && rdata->msg_info.msg, PJ_EINVAL);
    PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
		     PJSIP_ENOTREQUESTMSG);

    /* The transaction must belong to this dialog.  */
    PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata) &&
		     pjsip_rdata_get_tsx(rdata)->mod_data[dlg->ua->id] == dlg,
		     PJ_EINVALIDOP);

    /* Create the response. */
    status = pjsip_dlg_create_response(dlg, rdata, st_code, st_text, &tdata);
    if (status != PJ_SUCCESS)
	return status;

    /* Add additional header, if any */
    if (hdr_list) {
	const pjsip_hdr *hdr;

	hdr = hdr_list->next;
	while (hdr != hdr_list) {
	    pjsip_msg_add_hdr(tdata->msg,
			      pjsip_hdr_clone(tdata->pool, hdr));
	    hdr = hdr->next;
	}
    }

    /* Add the message body, if any. */
    if (body) {
	tdata->msg->body = pjsip_msg_body_clone( tdata->pool, body);
    }

    /* Send the response. */
    return pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
}


/* This function is called by user agent upon receiving incoming response
 * message.
 */
void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata )
{
    pj_status_t status;
    pjsip_transaction *tsx = NULL;
    pj_bool_t processed = PJ_FALSE;
    unsigned i;

    PJ_LOG(5,(dlg->obj_name, "Received %s",
	      pjsip_rx_data_get_info(rdata)));

    /* Lock dialog and increment session. */
    pjsip_dlg_inc_lock(dlg);

    /* Check CSeq */
    if (rdata->msg_info.cseq->cseq <= dlg->remote.cseq &&
	rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD &&
	rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD) 
    {
	/* Invalid CSeq.
	 * Respond statelessly with 500 (Internal Server Error)
	 */
	pj_str_t warn_text;

	/* Unlock dialog and dec session, may destroy dialog. */
	pjsip_dlg_dec_lock(dlg);

	pj_assert(pjsip_rdata_get_tsx(rdata) == NULL);
	warn_text = pj_str("Invalid CSeq");
	pjsip_endpt_respond_stateless(dlg->endpt,
				      rdata, 500, &warn_text, NULL, NULL);
	return;
    }

    /* Update CSeq. */
    dlg->remote.cseq = rdata->msg_info.cseq->cseq;

    /* Update To tag if necessary.
     * This only happens if UAS sends a new request before answering
     * our request (e.g. UAS sends NOTIFY before answering our
     * SUBSCRIBE request).
     */
    if (dlg->remote.info->tag.slen == 0) {
	pj_strdup(dlg->pool, &dlg->remote.info->tag,
		  &rdata->msg_info.from->tag);
    }

    /* Create UAS transaction for this request. */
    if (pjsip_rdata_get_tsx(rdata) == NULL && 
	rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) 
    {
	status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx);
	PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;});

	/* Put this dialog in the transaction data. */
	tsx->mod_data[dlg->ua->id] = dlg;

	/* Add transaction count. */
	++dlg->tsx_count;
    }

    /* Report the request to dialog usages. */
    for (i=0; i<dlg->usage_cnt; ++i) {

	if (!dlg->usage[i]->on_rx_request)
	    continue;

	processed = (*dlg->usage[i]->on_rx_request)(rdata);

	if (processed)
	    break;
    }

    /* Feed the first request to the transaction. */
    if (tsx)
	pjsip_tsx_recv_msg(tsx, rdata);

    /* If no dialog usages has claimed the processing of the transaction,
     * and if transaction has not sent final response, respond with
     * 500/Internal Server Error.
     */
    if (!processed && tsx && tsx->status_code < 200) {
	pjsip_tx_data *tdata;
	const pj_str_t reason = { "No session found", 16};

	PJ_LOG(4,(tsx->obj_name, "%s was unhandled by "
				 "dialog usages, sending 500 response",
				 pjsip_rx_data_get_info(rdata)));

	status = pjsip_dlg_create_response(dlg, rdata, 500, &reason, &tdata);
	if (status == PJ_SUCCESS) {
	    status = pjsip_dlg_send_response(dlg, tsx, tdata);
	}
    }

on_return:
    /* Unlock dialog and dec session, may destroy dialog. */
    pjsip_dlg_dec_lock(dlg);
}

/* Update route-set from incoming message */
static void dlg_update_routeset(pjsip_dialog *dlg, const pjsip_msg *msg)
{
    const pjsip_hdr *hdr, *end_hdr;

    pj_list_init(&dlg->route_set);

    end_hdr = &msg->hdr;
    for (hdr=msg->hdr.prev; hdr!=end_hdr; hdr=hdr->prev) {
	if (hdr->type == PJSIP_H_RECORD_ROUTE) {
	    pjsip_route_hdr *r;
	    r = pjsip_hdr_clone(dlg->pool, hdr);
	    pjsip_routing_hdr_set_route(r);
	    pj_list_push_back(&dlg->route_set, r);
	}
    }
}

/* This function is called by user agent upon receiving incoming response
 * message.
 */
void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
{
    unsigned i;
    pj_bool_t routeset_updated = PJ_FALSE;
    int res_code;

    PJ_LOG(5,(dlg->obj_name, "Received %s",
	      pjsip_rx_data_get_info(rdata)));

    /* Lock the dialog and inc session. */
    pjsip_dlg_inc_lock(dlg);

    /* Check that rdata already has dialog in mod_data. */
    pj_assert(pjsip_rdata_get_dlg(rdata) == dlg);

    /* Keep the response's status code */
    res_code = rdata->msg_info.msg->line.status.code;

    /* When we receive response that establishes dialog, update To tag, 
     * route set and dialog target.
     * 
     * The second condition of the "if" is a workaround for forking. 
     * Originally, the dialog takes the first To tag seen and set it as 
     * the remote tag. If the tag in 2xx response is different than this 
     * tag, ACK will be sent with wrong To tag and incoming request with 
     * this tag will be rejected with 481. 
     * 
     * The workaround for this is to take the To tag received in the 
     * 2xx response and set it as remote tag. 
     */ 
    if ((dlg->state == PJSIP_DIALOG_STATE_NULL &&  
         pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && 
 	 (res_code > 100 && res_code < 300) && 
 	 rdata->msg_info.to->tag.slen)  
 	|| 
 	(dlg->role==PJSIP_ROLE_UAC && 
 	 !dlg->uac_has_2xx && 
 	 res_code/100 == 2 && 
 	 pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && 
 	 pj_strcmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag)))
    {
	pjsip_contact_hdr *contact;

	/* Update To tag. */
	pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag);
	/* No need to update remote's tag_hval since its never used. */


	/* RFC 3271 Section 12.1.2:
	 * The route set MUST be set to the list of URIs in the Record-Route
	 * header field from the response, taken in reverse order and 
	 * preserving all URI parameters. If no Record-Route header field 
	 * is present in the response, the route set MUST be set to the 
	 * empty set. This route set, even if empty, overrides any pre-existing
	 * route set for future requests in this dialog.
	 */
	dlg_update_routeset(dlg, rdata->msg_info.msg);
	routeset_updated = PJ_TRUE;

	/* The remote target MUST be set to the URI from the Contact header 
	 * field of the response.
	 */
	contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, 
				     NULL);
	if (contact) {
	    dlg->remote.contact = pjsip_hdr_clone(dlg->pool, contact);
	    dlg->target = dlg->remote.contact->uri;
	}

	dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED;

 
 	/* Prevent dialog from being updated just in case more 2xx 
 	 * gets through this dialog (it shouldn't happen). 
 	 */ 
 	if (dlg->role==PJSIP_ROLE_UAC && !dlg->uac_has_2xx &&  
 	    res_code/100==2)  
 	{ 
 	    dlg->uac_has_2xx = PJ_TRUE; 
 	} 
    }

    /* Update remote target (again) when receiving 2xx response messages
     * that's defined as target refresh. 
     *
     * Also upon receiving 2xx response, recheck again the route set.
     * This is for compatibility with RFC 2543, as described in Section
     * 13.2.2.4 of RFC 3261:

	If the dialog identifier in the 2xx response matches the dialog
	identifier of an existing dialog, the dialog MUST be transitioned to
	the "confirmed" state, and the route set for the dialog MUST be
	recomputed based on the 2xx response using the procedures of Section
	12.2.1.2. 

	Note that the only piece of state that is recomputed is the route
	set.  Other pieces of state such as the highest sequence numbers
	(remote and local) sent within the dialog are not recomputed.  The
	route set only is recomputed for backwards compatibility.  RFC
	2543 did not mandate mirroring of the Record-Route header field in
	a 1xx, only 2xx.
     */
    if (pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) &&
	res_code/100 == 2)
    {
	pjsip_contact_hdr *contact;

	contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, 
				     NULL);
	if (contact) {
	    dlg->remote.contact = pjsip_hdr_clone(dlg->pool, contact);
	    dlg->target = dlg->remote.contact->uri;
	}

	if (!routeset_updated) {
	    dlg_update_routeset(dlg, rdata->msg_info.msg);
	    routeset_updated = PJ_TRUE;
	}
    }


    /* Pass to dialog usages. */
    for (i=0; i<dlg->usage_cnt; ++i) {
	pj_bool_t processed;

	if (!dlg->usage[i]->on_rx_response)
	    continue;

	processed = (*dlg->usage[i]->on_rx_response)(rdata);

	if (processed)
	    break;
    }

    /* Unhandled response does not necessarily mean error because
       dialog usages may choose to process the transaction state instead.
    if (i==dlg->usage_cnt) {
	PJ_LOG(4,(dlg->obj_name, "%s was not claimed by any dialog usages",
		  pjsip_rx_data_get_info(rdata)));
    }
    */

    /* Unlock dialog and dec session, may destroy dialog. */
    pjsip_dlg_dec_lock(dlg);
}

/* This function is called by user agent upon receiving transaction
 * state notification.
 */
void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg,
			     pjsip_transaction *tsx,
			     pjsip_event *e )
{
    unsigned i;

    PJ_LOG(5,(dlg->obj_name, "Transaction %s state changed to %s",
	      tsx->obj_name, pjsip_tsx_state_str(tsx->state)));

    /* Lock the dialog and increment session. */
    pjsip_dlg_inc_lock(dlg);

    /* Pass to dialog usages. */
    for (i=0; i<dlg->usage_cnt; ++i) {

	if (!dlg->usage[i]->on_tsx_state)
	    continue;

	(*dlg->usage[i]->on_tsx_state)(tsx, e);
    }


    if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
	--dlg->tsx_count;
	tsx->mod_data[dlg->ua->id] = NULL;
    }

    /* Unlock dialog and dec session, may destroy dialog. */
    pjsip_dlg_dec_lock(dlg);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -