📄 sip_reg.c
字号:
/* $Id: sip_reg.c 974 2007-02-19 01:13:53Z 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-ua/sip_regc.h>
#include <pjsip/sip_endpoint.h>
#include <pjsip/sip_parser.h>
#include <pjsip/sip_module.h>
#include <pjsip/sip_transaction.h>
#include <pjsip/sip_event.h>
#include <pjsip/sip_util.h>
#include <pjsip/sip_auth_msg.h>
#include <pjsip/sip_errno.h>
#include <pj/assert.h>
#include <pj/guid.h>
#include <pj/os.h>
#include <pj/pool.h>
#include <pj/log.h>
#include <pj/rand.h>
#include <pj/string.h>
#define REFRESH_TIMER 1
#define DELAY_BEFORE_REFRESH 5
#define THIS_FILE "sip_regc.c"
/* Outgoing transaction timeout when server sends 100 but never replies
* with final response. Value is in MILISECONDS!
*/
#define REGC_TSX_TIMEOUT 33000
/**
* SIP client registration structure.
*/
struct pjsip_regc
{
pj_pool_t *pool;
pjsip_endpoint *endpt;
pj_bool_t _delete_flag;
pj_bool_t has_tsx;
pj_int32_t busy;
void *token;
pjsip_regc_cb *cb;
pj_str_t str_srv_url;
pjsip_uri *srv_url;
pjsip_cid_hdr *cid_hdr;
pjsip_cseq_hdr *cseq_hdr;
pj_str_t from_uri;
pjsip_from_hdr *from_hdr;
pjsip_to_hdr *to_hdr;
char *contact_buf;
pjsip_generic_string_hdr *contact_hdr;
pjsip_expires_hdr *expires_hdr;
pjsip_contact_hdr *unreg_contact_hdr;
pjsip_expires_hdr *unreg_expires_hdr;
pj_uint32_t expires;
pjsip_route_hdr route_set;
pjsip_hdr hdr_list;
/* Authorization sessions. */
pjsip_auth_clt_sess auth_sess;
/* Auto refresh registration. */
pj_bool_t auto_reg;
pj_time_val last_reg;
pj_time_val next_reg;
pj_timer_entry timer;
/* Transport selector */
pjsip_tpselector tp_sel;
};
PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
pjsip_regc_cb *cb,
pjsip_regc **p_regc)
{
pj_pool_t *pool;
pjsip_regc *regc;
pj_status_t status;
/* Verify arguments. */
PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
regc = pj_pool_zalloc(pool, sizeof(struct pjsip_regc));
regc->pool = pool;
regc->endpt = endpt;
regc->token = token;
regc->cb = cb;
regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
status = pjsip_auth_clt_init(®c->auth_sess, endpt, regc->pool, 0);
if (status != PJ_SUCCESS)
return status;
pj_list_init(®c->route_set);
pj_list_init(®c->hdr_list);
/* Done */
*p_regc = regc;
return PJ_SUCCESS;
}
PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
{
PJ_ASSERT_RETURN(regc, PJ_EINVAL);
if (regc->has_tsx || regc->busy) {
regc->_delete_flag = 1;
regc->cb = NULL;
} else {
pjsip_tpselector_dec_ref(®c->tp_sel);
pjsip_endpt_release_pool(regc->endpt, regc->pool);
}
return PJ_SUCCESS;
}
PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
pjsip_regc_info *info )
{
PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
info->server_uri = regc->str_srv_url;
info->client_uri = regc->from_uri;
info->is_busy = (regc->busy || regc->has_tsx);
info->auto_reg = regc->auto_reg;
info->interval = regc->expires;
if (regc->has_tsx)
info->next_reg = 0;
else if (regc->auto_reg == 0)
info->next_reg = 0;
else if (regc->expires < 0)
info->next_reg = regc->expires;
else {
pj_time_val now, next_reg;
next_reg = regc->next_reg;
pj_gettimeofday(&now);
PJ_TIME_VAL_SUB(next_reg, now);
info->next_reg = next_reg.sec;
}
return PJ_SUCCESS;
}
PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
{
return regc->pool;
}
static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
{
if (expires != regc->expires) {
regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
} else {
regc->expires_hdr = NULL;
}
}
static pj_status_t set_contact( pjsip_regc *regc,
int contact_cnt,
const pj_str_t contact[] )
{
int i;
char *s;
const pj_str_t contact_STR = { "Contact", 7};
/* Concatenate contacts. */
for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
return PJSIP_EURITOOLONG;
}
pj_memcpy(s, contact[i].ptr, contact[i].slen);
s += contact[i].slen;
if (i != contact_cnt - 1) {
*s++ = ',';
*s++ = ' ';
}
}
/* Set "Contact" header. */
regc->contact_hdr = pjsip_generic_string_hdr_create(regc->pool,
&contact_STR,
NULL);
regc->contact_hdr->hvalue.ptr = regc->contact_buf;
regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
return PJ_SUCCESS;
}
PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
const pj_str_t *srv_url,
const pj_str_t *from_url,
const pj_str_t *to_url,
int contact_cnt,
const pj_str_t contact[],
pj_uint32_t expires)
{
pj_str_t tmp;
pj_status_t status;
PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
contact_cnt && contact && expires, PJ_EINVAL);
/* Copy server URL. */
pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url);
/* Set server URL. */
tmp = regc->str_srv_url;
regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
if (regc->srv_url == NULL) {
return PJSIP_EINVALIDURI;
}
/* Set "From" header. */
pj_strdup_with_null(regc->pool, ®c->from_uri, from_url);
tmp = regc->from_uri;
regc->from_hdr = pjsip_from_hdr_create(regc->pool);
regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
PJSIP_PARSE_URI_AS_NAMEADDR);
if (!regc->from_hdr->uri) {
PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
from_url->slen, from_url->ptr));
return PJSIP_EINVALIDURI;
}
/* Set "To" header. */
pj_strdup_with_null(regc->pool, &tmp, to_url);
regc->to_hdr = pjsip_to_hdr_create(regc->pool);
regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
PJSIP_PARSE_URI_AS_NAMEADDR);
if (!regc->to_hdr->uri) {
PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
return PJSIP_EINVALIDURI;
}
/* Set "Contact" header. */
status = set_contact( regc, contact_cnt, contact);
if (status != PJ_SUCCESS)
return status;
/* Set "Expires" header, if required. */
set_expires( regc, expires);
/* Set "Call-ID" header. */
regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
pj_create_unique_string(regc->pool, ®c->cid_hdr->id);
/* Set "CSeq" header. */
regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD);
/* Create "Contact" header used in unregistration. */
regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
regc->unreg_contact_hdr->star = 1;
/* Create "Expires" header used in unregistration. */
regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool, 0);
/* Done. */
return PJ_SUCCESS;
}
PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
int count,
const pjsip_cred_info cred[] )
{
PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
return pjsip_auth_clt_set_credentials(®c->auth_sess, count, cred);
}
PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
const pjsip_route_hdr *route_set)
{
const pjsip_route_hdr *chdr;
PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
pj_list_init(®c->route_set);
chdr = route_set->next;
while (chdr != route_set) {
pj_list_push_back(®c->route_set, pjsip_hdr_clone(regc->pool, chdr));
chdr = chdr->next;
}
return PJ_SUCCESS;
}
/*
* Bind client registration to a specific transport/listener.
*/
PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
const pjsip_tpselector *sel)
{
PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
pjsip_tpselector_dec_ref(®c->tp_sel);
pj_memcpy(®c->tp_sel, sel, sizeof(*sel));
pjsip_tpselector_add_ref(®c->tp_sel);
return PJ_SUCCESS;
}
PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
const pjsip_hdr *hdr_list)
{
const pjsip_hdr *hdr;
PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
//This is "add" operation, so don't remove headers.
//pj_list_init(®c->hdr_list);
hdr = hdr_list->next;
while (hdr != hdr_list) {
pj_list_push_back(®c->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
hdr = hdr->next;
}
return PJ_SUCCESS;
}
static pj_status_t create_request(pjsip_regc *regc,
pjsip_tx_data **p_tdata)
{
pj_status_t status;
pjsip_tx_data *tdata;
PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
/* Create the request. */
status = pjsip_endpt_create_request_from_hdr( regc->endpt,
&pjsip_register_method,
regc->srv_url,
regc->from_hdr,
regc->to_hdr,
NULL,
regc->cid_hdr,
regc->cseq_hdr->cseq,
NULL,
&tdata);
if (status != PJ_SUCCESS)
return status;
/* Add cached authorization headers. */
pjsip_auth_clt_init_req( ®c->auth_sess, tdata );
/* Add Route headers from route set, ideally after Via header */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -