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

📄 sip_dialog.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 4 页
字号:
/* $Id: sip_dialog.c 1027 2007-03-01 18:41:36Z bennylp $ */
/* 
 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include <pjsip/sip_dialog.h>
#include <pjsip/sip_ua_layer.h>
#include <pjsip/sip_errno.h>
#include <pjsip/sip_endpoint.h>
#include <pjsip/sip_parser.h>
#include <pjsip/sip_module.h>
#include <pjsip/sip_util.h>
#include <pjsip/sip_transaction.h>
#include <pj/assert.h>
#include <pj/os.h>
#include <pj/string.h>
#include <pj/pool.h>
#include <pj/guid.h>
#include <pj/rand.h>
#include <pj/array.h>
#include <pj/except.h>
#include <pj/hash.h>
#include <pj/log.h>

#define THIS_FILE	"sip_dialog.c"

long pjsip_dlg_lock_tls_id;

PJ_DEF(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m)
{
    const pjsip_method subscribe = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}};
    const pjsip_method refer = { PJSIP_OTHER_METHOD, {"REFER", 5}};
    const pjsip_method notify = { PJSIP_OTHER_METHOD, {"NOTIFY", 6}};

    return m->id == PJSIP_INVITE_METHOD ||
	   (pjsip_method_cmp(m, &subscribe)==0) ||
	   (pjsip_method_cmp(m, &refer)==0) ||
	   (pjsip_method_cmp(m, &notify)==0);
}

static pj_status_t create_dialog( pjsip_user_agent *ua,
				  pjsip_dialog **p_dlg)
{
    pjsip_endpoint *endpt;
    pj_pool_t *pool;
    pjsip_dialog *dlg;
    pj_status_t status;

    endpt = pjsip_ua_get_endpt(ua);
    if (!endpt)
	return PJ_EINVALIDOP;

    pool = pjsip_endpt_create_pool(endpt, "dlg%p", 
				   PJSIP_POOL_LEN_DIALOG, 
				   PJSIP_POOL_INC_DIALOG);
    if (!pool)
	return PJ_ENOMEM;

    dlg = pj_pool_zalloc(pool, sizeof(pjsip_dialog));
    PJ_ASSERT_RETURN(dlg != NULL, PJ_ENOMEM);

    dlg->pool = pool;
    pj_ansi_snprintf(dlg->obj_name, sizeof(dlg->obj_name), "dlg%p", dlg);
    dlg->ua = ua;
    dlg->endpt = endpt;
    dlg->state = PJSIP_DIALOG_STATE_NULL;

    pj_list_init(&dlg->inv_hdr);

    status = pj_mutex_create_recursive(pool, dlg->obj_name, &dlg->mutex_);
    if (status != PJ_SUCCESS)
	goto on_error;


    *p_dlg = dlg;
    return PJ_SUCCESS;

on_error:
    if (dlg->mutex_)
	pj_mutex_destroy(dlg->mutex_);
    pjsip_endpt_release_pool(endpt, pool);
    return status;
}

static void destroy_dialog( pjsip_dialog *dlg )
{
    if (dlg->mutex_) {
	pj_mutex_destroy(dlg->mutex_);
	dlg->mutex_ = NULL;
    }
    if (dlg->tp_sel.type != PJSIP_TPSELECTOR_NONE) {
	pjsip_tpselector_dec_ref(&dlg->tp_sel);
	pj_bzero(&dlg->tp_sel, sizeof(pjsip_tpselector));
    }
    pjsip_endpt_release_pool(dlg->endpt, dlg->pool);
}


/*
 * Create an UAC dialog.
 */
PJ_DEF(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua,
					  const pj_str_t *local_uri,
					  const pj_str_t *local_contact,
					  const pj_str_t *remote_uri,
					  const pj_str_t *target,
					  pjsip_dialog **p_dlg)
{
    pj_status_t status;
    pj_str_t tmp;
    pjsip_dialog *dlg;

    /* Check arguments. */
    PJ_ASSERT_RETURN(ua && local_uri && remote_uri && p_dlg, PJ_EINVAL);

    /* Create dialog instance. */
    status = create_dialog(ua, &dlg);
    if (status != PJ_SUCCESS)
	return status;

    /* Parse target. */
    pj_strdup_with_null(dlg->pool, &tmp, target ? target : remote_uri);
    dlg->target = pjsip_parse_uri(dlg->pool, tmp.ptr, tmp.slen, 0);
    if (!dlg->target) {
	status = PJSIP_EINVALIDURI;
	goto on_error;
    }

    /* Put any header param in the target URI into INVITE header list. */
    if (PJSIP_URI_SCHEME_IS_SIP(dlg->target) ||
	PJSIP_URI_SCHEME_IS_SIPS(dlg->target))
    {
	pjsip_param *param;
	pjsip_sip_uri *uri = (pjsip_sip_uri*)pjsip_uri_get_uri(dlg->target);

	param = uri->header_param.next;
	while (param != &uri->header_param) {
	    pjsip_hdr *hdr;
	    int c;

	    c = param->value.ptr[param->value.slen];
	    param->value.ptr[param->value.slen] = '\0';

	    hdr = pjsip_parse_hdr(dlg->pool, &param->name, param->value.ptr,
				  param->value.slen, NULL);

	    param->value.ptr[param->value.slen] = (char)c;

	    if (hdr == NULL) {
		status = PJSIP_EINVALIDURI;
		goto on_error;
	    }
	    pj_list_push_back(&dlg->inv_hdr, hdr);

	    param = param->next;
	}

	/* Now must remove any header params from URL, since that would
	 * create another header in pjsip_endpt_create_request().
	 */
	pj_list_init(&uri->header_param);
    }

    /* Init local info. */
    dlg->local.info = pjsip_from_hdr_create(dlg->pool);
    pj_strdup_with_null(dlg->pool, &dlg->local.info_str, local_uri);
    dlg->local.info->uri = pjsip_parse_uri(dlg->pool, 
					   dlg->local.info_str.ptr, 
					   dlg->local.info_str.slen, 0);
    if (!dlg->local.info->uri) {
	status = PJSIP_EINVALIDURI;
	goto on_error;
    }

    /* Generate local tag. */
    pj_create_unique_string(dlg->pool, &dlg->local.info->tag);

    /* Calculate hash value of local tag. */
    dlg->local.tag_hval = pj_hash_calc(0, dlg->local.info->tag.ptr,
				       dlg->local.info->tag.slen);

    /* Randomize local CSeq. */
    dlg->local.first_cseq = pj_rand() % 0x7FFFFFFFL;
    dlg->local.cseq = dlg->local.first_cseq;

    /* Init local contact. */
    dlg->local.contact = pjsip_contact_hdr_create(dlg->pool);
    pj_strdup_with_null(dlg->pool, &tmp, 
			local_contact ? local_contact : local_uri);
    dlg->local.contact->uri = pjsip_parse_uri(dlg->pool, tmp.ptr, tmp.slen,
					      PJSIP_PARSE_URI_AS_NAMEADDR);
    if (!dlg->local.contact->uri) {
	status = PJSIP_EINVALIDURI;
	goto on_error;
    }

    /* Init remote info. */
    dlg->remote.info = pjsip_to_hdr_create(dlg->pool);
    pj_strdup_with_null(dlg->pool, &dlg->remote.info_str, remote_uri);
    dlg->remote.info->uri = pjsip_parse_uri(dlg->pool, 
					    dlg->remote.info_str.ptr, 
					    dlg->remote.info_str.slen, 0);
    if (!dlg->remote.info->uri) {
	status = PJSIP_EINVALIDURI;
	goto on_error;
    }

    /* Remove header param from remote.info_str, if any */
    if (PJSIP_URI_SCHEME_IS_SIP(dlg->remote.info->uri) ||
	PJSIP_URI_SCHEME_IS_SIPS(dlg->remote.info->uri))
    {
	pjsip_sip_uri *sip_uri = pjsip_uri_get_uri(dlg->remote.info->uri);
	if (!pj_list_empty(&sip_uri->header_param)) {
	    pj_str_t tmp;

	    /* Remove all header param */
	    pj_list_init(&sip_uri->header_param);

	    /* Print URI */
	    tmp.ptr = pj_pool_alloc(dlg->pool, dlg->remote.info_str.slen);
	    tmp.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR,
				       sip_uri, tmp.ptr, 
				       dlg->remote.info_str.slen);

	    if (tmp.slen < 1) {
		status = PJSIP_EURITOOLONG;
		goto on_error;
	    }

	    /* Assign remote.info_str */
	    dlg->remote.info_str = tmp;
	}
    }


    /* Initialize remote's CSeq to -1. */
    dlg->remote.cseq = dlg->remote.first_cseq = -1;

    /* Initial role is UAC. */
    dlg->role = PJSIP_ROLE_UAC;

    /* Secure? */
    dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(dlg->target);

    /* Generate Call-ID header. */
    dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
    pj_create_unique_string(dlg->pool, &dlg->call_id->id);

    /* Initial route set is empty. */
    pj_list_init(&dlg->route_set);

    /* Init client authentication session. */
    status = pjsip_auth_clt_init(&dlg->auth_sess, dlg->endpt, 
				 dlg->pool, 0);
    if (status != PJ_SUCCESS)
	goto on_error;

    /* Register this dialog to user agent. */
    status = pjsip_ua_register_dlg( ua, dlg );
    if (status != PJ_SUCCESS)
	goto on_error;


    /* Done! */
    *p_dlg = dlg;


    PJ_LOG(5,(dlg->obj_name, "UAC dialog created"));

    return PJ_SUCCESS;

on_error:
    destroy_dialog(dlg);
    return status;
}


/*
 * Create UAS dialog.
 */
PJ_DEF(pj_status_t) pjsip_dlg_create_uas(   pjsip_user_agent *ua,
					    pjsip_rx_data *rdata,
					    const pj_str_t *contact,
					    pjsip_dialog **p_dlg)
{
    pj_status_t status;
    pjsip_hdr *contact_hdr;
    pjsip_rr_hdr *rr;
    pjsip_transaction *tsx = NULL;
    pj_str_t tmp;
    enum { TMP_LEN=128};
    pj_ssize_t len;
    pjsip_dialog *dlg;

    /* Check arguments. */
    PJ_ASSERT_RETURN(ua && rdata && p_dlg, PJ_EINVAL);

    /* rdata must have request message. */
    PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
		     PJSIP_ENOTREQUESTMSG);

    /* Request must not have To tag. 
     * This should have been checked in the user agent (or application?).
     */
    PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen == 0, PJ_EINVALIDOP);
		     
    /* The request must be a dialog establishing request. */
    PJ_ASSERT_RETURN(
	pjsip_method_creates_dialog(&rdata->msg_info.msg->line.req.method),
	PJ_EINVALIDOP);

    /* Create dialog instance. */
    status = create_dialog(ua, &dlg);
    if (status != PJ_SUCCESS)
	return status;

    /* Temprary string for getting the string representation of
     * both local and remote URI.
     */
    tmp.ptr = pj_pool_alloc(rdata->tp_info.pool, TMP_LEN);

    /* Init local info from the To header. */
    dlg->local.info = pjsip_hdr_clone(dlg->pool, rdata->msg_info.to);
    pjsip_fromto_hdr_set_from(dlg->local.info);

    /* Generate local tag. */
    pj_create_unique_string(dlg->pool, &dlg->local.info->tag);


    /* Print the local info. */
    len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR,
			  dlg->local.info->uri, tmp.ptr, TMP_LEN);
    if (len < 1) {
	pj_ansi_strcpy(tmp.ptr, "<-error: uri too long->");
	tmp.slen = pj_ansi_strlen(tmp.ptr);
    } else
	tmp.slen = len;

    /* Save the local info. */
    pj_strdup(dlg->pool, &dlg->local.info_str, &tmp);

    /* Calculate hash value of local tag. */
    dlg->local.tag_hval = pj_hash_calc(0, dlg->local.info->tag.ptr,
				       dlg->local.info->tag.slen);


    /* Randomize local cseq */
    dlg->local.first_cseq = pj_rand() % 0x7FFFFFFFL;
    dlg->local.cseq = dlg->local.first_cseq;

    /* Init local contact. */
    /* TODO:
     *  Section 12.1.1, paragraph about using SIPS URI in Contact.
     *  If the request that initiated the dialog contained a SIPS URI 
     *  in the Request-URI or in the top Record-Route header field value, 
     *  if there was any, or the Contact header field if there was no 
     *  Record-Route header field, the Contact header field in the response
     *  MUST be a SIPS URI.
     */
    if (contact) {
	pj_str_t tmp;

	dlg->local.contact = pjsip_contact_hdr_create(dlg->pool);
	pj_strdup_with_null(dlg->pool, &tmp, contact);
	dlg->local.contact->uri = pjsip_parse_uri(dlg->pool, tmp.ptr, tmp.slen,
						  PJSIP_PARSE_URI_AS_NAMEADDR);
	if (!dlg->local.contact->uri) {
	    status = PJSIP_EINVALIDURI;
	    goto on_error;
	}

    } else {
	dlg->local.contact = pjsip_contact_hdr_create(dlg->pool);
	dlg->local.contact->uri = dlg->local.info->uri;
    }

    /* Init remote info from the From header. */
    dlg->remote.info = pjsip_hdr_clone(dlg->pool, rdata->msg_info.from);
    pjsip_fromto_hdr_set_to(dlg->remote.info);

    /* Print the remote info. */
    len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR,
			  dlg->remote.info->uri, tmp.ptr, TMP_LEN);
    if (len < 1) {
	pj_ansi_strcpy(tmp.ptr, "<-error: uri too long->");
	tmp.slen = pj_ansi_strlen(tmp.ptr);
    } else
	tmp.slen = len;

    /* Save the remote info. */
    pj_strdup(dlg->pool, &dlg->remote.info_str, &tmp);


    /* Init remote's contact from Contact header. */
    contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, 
				     NULL);
    if (!contact_hdr) {
	status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
	goto on_error;
    }
    dlg->remote.contact = pjsip_hdr_clone(dlg->pool, contact_hdr);

    /* Init remote's CSeq from CSeq header */
    dlg->remote.cseq = dlg->remote.first_cseq = rdata->msg_info.cseq->cseq;

    /* Set initial target to remote's Contact. */
    dlg->target = dlg->remote.contact->uri;

    /* Initial role is UAS */
    dlg->role = PJSIP_ROLE_UAS;

    /* Secure? 
     *  RFC 3261 Section 12.1.1:
     *  If the request arrived over TLS, and the Request-URI contained a 
     *  SIPS URI, the 'secure' flag is set to TRUE.
     */
    dlg->secure = PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport) &&

⌨️ 快捷键说明

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