📄 sip_util.c
字号:
/* $Id: sip_util.c 1121 2007-04-01 22:58:47Z 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_util.h>
#include <pjsip/sip_transport.h>
#include <pjsip/sip_msg.h>
#include <pjsip/sip_endpoint.h>
#include <pjsip/sip_event.h>
#include <pjsip/sip_transaction.h>
#include <pjsip/sip_module.h>
#include <pjsip/sip_errno.h>
#include <pj/log.h>
#include <pj/string.h>
#include <pj/guid.h>
#include <pj/pool.h>
#include <pj/except.h>
#include <pj/rand.h>
#include <pj/assert.h>
#include <pj/errno.h>
#define THIS_FILE "endpoint"
static const char *event_str[] =
{
"UNIDENTIFIED",
"TIMER",
"TX_MSG",
"RX_MSG",
"TRANSPORT_ERROR",
"TSX_STATE",
"USER",
};
static pj_str_t str_TEXT = { "text", 4},
str_PLAIN = { "plain", 5 };
/*
* Initialize transmit data (msg) with the headers and optional body.
* This will just put the headers in the message as it is. Be carefull
* when calling this function because once a header is put in a message,
* it CAN NOT be put in other message until the first message is deleted,
* because the way the header is put in the list.
* That's why the session will shallow_clone it's headers before calling
* this function.
*/
static void init_request_throw( pjsip_endpoint *endpt,
pjsip_tx_data *tdata,
pjsip_method *method,
pjsip_uri *param_target,
pjsip_from_hdr *param_from,
pjsip_to_hdr *param_to,
pjsip_contact_hdr *param_contact,
pjsip_cid_hdr *param_call_id,
pjsip_cseq_hdr *param_cseq,
const pj_str_t *param_text)
{
pjsip_msg *msg;
pjsip_msg_body *body;
pjsip_via_hdr *via;
const pjsip_hdr *endpt_hdr;
/* Create the message. */
msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
/* Init request URI. */
pj_memcpy(&msg->line.req.method, method, sizeof(*method));
msg->line.req.uri = param_target;
/* Add additional request headers from endpoint. */
endpt_hdr = pjsip_endpt_get_request_headers(endpt)->next;
while (endpt_hdr != pjsip_endpt_get_request_headers(endpt)) {
pjsip_hdr *hdr = pjsip_hdr_shallow_clone(tdata->pool, endpt_hdr);
pjsip_msg_add_hdr( tdata->msg, hdr );
endpt_hdr = endpt_hdr->next;
}
/* Add From header. */
if (param_from->tag.slen == 0)
pj_create_unique_string(tdata->pool, ¶m_from->tag);
pjsip_msg_add_hdr(msg, (void*)param_from);
/* Add To header. */
pjsip_msg_add_hdr(msg, (void*)param_to);
/* Add Contact header. */
if (param_contact) {
pjsip_msg_add_hdr(msg, (void*)param_contact);
}
/* Add Call-ID header. */
pjsip_msg_add_hdr(msg, (void*)param_call_id);
/* Add CSeq header. */
pjsip_msg_add_hdr(msg, (void*)param_cseq);
/* Add a blank Via header in the front of the message. */
via = pjsip_via_hdr_create(tdata->pool);
via->rport_param = 0;
pjsip_msg_insert_first_hdr(msg, (void*)via);
/* Add header params as request headers */
if (PJSIP_URI_SCHEME_IS_SIP(param_target) ||
PJSIP_URI_SCHEME_IS_SIPS(param_target))
{
pjsip_sip_uri *uri = (pjsip_sip_uri*) pjsip_uri_get_uri(param_target);
pjsip_param *hparam;
hparam = uri->header_param.next;
while (hparam != &uri->header_param) {
pjsip_generic_string_hdr *hdr;
hdr = pjsip_generic_string_hdr_create(tdata->pool,
&hparam->name,
&hparam->value);
pjsip_msg_add_hdr(msg, (pjsip_hdr*)hdr);
hparam = hparam->next;
}
}
/* Create message body. */
if (param_text) {
body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body));
body->content_type.type = str_TEXT;
body->content_type.subtype = str_PLAIN;
body->data = pj_pool_alloc(tdata->pool, param_text->slen );
pj_memcpy(body->data, param_text->ptr, param_text->slen);
body->len = param_text->slen;
body->print_body = &pjsip_print_text_body;
msg->body = body;
}
PJ_LOG(5,(THIS_FILE, "%s created.",
pjsip_tx_data_get_info(tdata)));
}
/*
* Create arbitrary request.
*/
PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
const pjsip_method *method,
const pj_str_t *param_target,
const pj_str_t *param_from,
const pj_str_t *param_to,
const pj_str_t *param_contact,
const pj_str_t *param_call_id,
int param_cseq,
const pj_str_t *param_text,
pjsip_tx_data **p_tdata)
{
pjsip_uri *target;
pjsip_tx_data *tdata;
pjsip_from_hdr *from;
pjsip_to_hdr *to;
pjsip_contact_hdr *contact;
pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */
pjsip_cid_hdr *call_id;
pj_str_t tmp;
pj_status_t status;
PJ_USE_EXCEPTION;
status = pjsip_endpt_create_tdata(endpt, &tdata);
if (status != PJ_SUCCESS)
return status;
/* Init reference counter to 1. */
pjsip_tx_data_add_ref(tdata);
PJ_TRY {
/* Request target. */
pj_strdup_with_null(tdata->pool, &tmp, param_target);
target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0);
if (target == NULL) {
status = PJSIP_EINVALIDREQURI;
goto on_error;
}
/* From */
from = pjsip_from_hdr_create(tdata->pool);
pj_strdup_with_null(tdata->pool, &tmp, param_from);
from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
PJSIP_PARSE_URI_AS_NAMEADDR);
if (from->uri == NULL) {
status = PJSIP_EINVALIDHDR;
goto on_error;
}
pj_create_unique_string(tdata->pool, &from->tag);
/* To */
to = pjsip_to_hdr_create(tdata->pool);
pj_strdup_with_null(tdata->pool, &tmp, param_to);
to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
PJSIP_PARSE_URI_AS_NAMEADDR);
if (to->uri == NULL) {
status = PJSIP_EINVALIDHDR;
goto on_error;
}
/* Contact. */
if (param_contact) {
contact = pjsip_contact_hdr_create(tdata->pool);
pj_strdup_with_null(tdata->pool, &tmp, param_contact);
contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
PJSIP_PARSE_URI_AS_NAMEADDR);
if (contact->uri == NULL) {
status = PJSIP_EINVALIDHDR;
goto on_error;
}
} else {
contact = NULL;
}
/* Call-ID */
call_id = pjsip_cid_hdr_create(tdata->pool);
if (param_call_id != NULL && param_call_id->slen)
pj_strdup(tdata->pool, &call_id->id, param_call_id);
else
pj_create_unique_string(tdata->pool, &call_id->id);
/* CSeq */
cseq = pjsip_cseq_hdr_create(tdata->pool);
if (param_cseq >= 0)
cseq->cseq = param_cseq;
else
cseq->cseq = pj_rand() & 0xFFFF;
/* Method */
pjsip_method_copy(tdata->pool, &cseq->method, method);
/* Create the request. */
init_request_throw( endpt, tdata, &cseq->method, target, from, to,
contact, call_id, cseq, param_text);
}
PJ_CATCH_ANY {
status = PJ_ENOMEM;
goto on_error;
}
PJ_END
*p_tdata = tdata;
return PJ_SUCCESS;
on_error:
pjsip_tx_data_dec_ref(tdata);
return status;
}
PJ_DEF(pj_status_t)
pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
const pjsip_method *method,
const pjsip_uri *param_target,
const pjsip_from_hdr *param_from,
const pjsip_to_hdr *param_to,
const pjsip_contact_hdr *param_contact,
const pjsip_cid_hdr *param_call_id,
int param_cseq,
const pj_str_t *param_text,
pjsip_tx_data **p_tdata)
{
pjsip_uri *target;
pjsip_tx_data *tdata;
pjsip_from_hdr *from;
pjsip_to_hdr *to;
pjsip_contact_hdr *contact;
pjsip_cid_hdr *call_id;
pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */
pj_status_t status;
PJ_USE_EXCEPTION;
/* Check arguments. */
PJ_ASSERT_RETURN(endpt && method && param_target && param_from &&
param_to && p_tdata, PJ_EINVAL);
/* Create new transmit data. */
status = pjsip_endpt_create_tdata(endpt, &tdata);
if (status != PJ_SUCCESS)
return status;
/* Set initial reference counter to 1. */
pjsip_tx_data_add_ref(tdata);
PJ_TRY {
/* Duplicate target URI and headers. */
target = pjsip_uri_clone(tdata->pool, param_target);
from = pjsip_hdr_clone(tdata->pool, param_from);
pjsip_fromto_hdr_set_from(from);
to = pjsip_hdr_clone(tdata->pool, param_to);
pjsip_fromto_hdr_set_to(to);
if (param_contact)
contact = pjsip_hdr_clone(tdata->pool, param_contact);
else
contact = NULL;
call_id = pjsip_hdr_clone(tdata->pool, param_call_id);
cseq = pjsip_cseq_hdr_create(tdata->pool);
if (param_cseq >= 0)
cseq->cseq = param_cseq;
else
cseq->cseq = pj_rand() % 0xFFFF;
pjsip_method_copy(tdata->pool, &cseq->method, method);
/* Copy headers to the request. */
init_request_throw(endpt, tdata, &cseq->method, target, from, to,
contact, call_id, cseq, param_text);
}
PJ_CATCH_ANY {
status = PJ_ENOMEM;
goto on_error;
}
PJ_END;
*p_tdata = tdata;
return PJ_SUCCESS;
on_error:
pjsip_tx_data_dec_ref(tdata);
return status;
}
/*
* Construct a minimal response message for the received request.
*/
PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
const pjsip_rx_data *rdata,
int st_code,
const pj_str_t *st_text,
pjsip_tx_data **p_tdata)
{
pjsip_tx_data *tdata;
pjsip_msg *msg, *req_msg;
pjsip_hdr *hdr;
pjsip_via_hdr *via;
pjsip_rr_hdr *rr;
pj_status_t status;
/* Check arguments. */
PJ_ASSERT_RETURN(endpt && rdata && p_tdata, PJ_EINVAL);
/* Check status code. */
PJ_ASSERT_RETURN(st_code >= 100 && st_code <= 699, PJ_EINVAL);
/* rdata must be a request message. */
req_msg = rdata->msg_info.msg;
pj_assert(req_msg->type == PJSIP_REQUEST_MSG);
/* Request MUST NOT be ACK request! */
PJ_ASSERT_RETURN(req_msg->line.req.method.id != PJSIP_ACK_METHOD,
PJ_EINVALIDOP);
/* Create a new transmit buffer. */
status = pjsip_endpt_create_tdata( endpt, &tdata);
if (status != PJ_SUCCESS)
return status;
/* Set initial reference count to 1. */
pjsip_tx_data_add_ref(tdata);
/* Create new response message. */
tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
/* Set status code and reason text. */
msg->line.status.code = st_code;
if (st_text)
pj_strdup(tdata->pool, &msg->line.status.reason, st_text);
else
msg->line.status.reason = *pjsip_get_status_text(st_code);
/* Set TX data attributes. */
tdata->rx_timestamp = rdata->pkt_info.timestamp;
/* Copy all the via headers, in order. */
via = rdata->msg_info.via;
while (via) {
pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via));
via = via->next;
if (via != (void*)&req_msg->hdr)
via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via);
else
break;
}
/* Copy all Record-Route headers, in order. */
rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL);
while (rr) {
pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr));
rr = rr->next;
if (rr != (void*)&req_msg->hdr)
rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr);
else
break;
}
/* Copy Call-ID header. */
hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL);
pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr));
/* Copy From header. */
hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.from);
pjsip_msg_add_hdr( msg, hdr);
/* Copy To header. */
hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.to);
pjsip_msg_add_hdr( msg, hdr);
/* Copy CSeq header. */
hdr = pjsip_hdr_clone(tdata->pool, rdata->msg_info.cseq);
pjsip_msg_add_hdr( msg, hdr);
/* All done. */
*p_tdata = tdata;
PJ_LOG(5,(THIS_FILE, "%s created", pjsip_tx_data_get_info(tdata)));
return PJ_SUCCESS;
}
/*
* Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of
* RFC3261). Note that the generation of ACK for 2xx response is different,
* and one must not use this function to generate such ACK.
*/
PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt,
const pjsip_tx_data *tdata,
const pjsip_rx_data *rdata,
pjsip_tx_data **ack_tdata)
{
pjsip_tx_data *ack = NULL;
const pjsip_msg *invite_msg;
const pjsip_from_hdr *from_hdr;
const pjsip_to_hdr *to_hdr;
const pjsip_cid_hdr *cid_hdr;
const pjsip_cseq_hdr *cseq_hdr;
const pjsip_hdr *hdr;
pjsip_hdr *via;
pjsip_to_hdr *to;
pj_status_t status;
/* rdata must be a non-2xx final response. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -