📄 sip_dialog.c
字号:
/* $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, ¬ify)==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, ¶m->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 + -