最近遇到一个问题,SIP会话BYE的时候发送到了错误的IP和端口,抓包发现from域的uri和contact域的uri是不一样的,一个是内网地址,一个是外网地址。exosip在调用 eXosip_call_terminate 的时候使用了默认的from域字段,导致发送到了内网的错误地址。现在来看看EXOSIP的具体实现,并考虑怎么修改。
首先看看发送过程,bye消息属于NICT消息,由上一节可以知道调用的是osip_nict_execute ,第一个状态NICT_PRE_TRYING调用接口nict_snd_request,发送地址为nict->nict_context->destination。
void
nict_snd_request (osip_transaction_t * nict, osip_event_t * evt)
{
int i;
osip_t *osip = (osip_t *) nict->config;
/* Here we have ict->orig_request == NULL */
nict->orig_request = evt->sip;
i = osip->cb_send_message (nict, evt->sip, nict->nict_context->destination, nict->nict_context->port, nict->out_socket);
...
}
int
osip_nict_set_destination (osip_nict_t * nict, char *destination, int port)
{
if (nict == NULL)
return OSIP_BADPARAMETER;
if (nict->destination != NULL)
osip_free (nict->destination);
nict->destination = destination;
nict->port = port;
return OSIP_SUCCESS;
}
而设置地址的接口为osip_nict_set_destination,通过__osip_nict_init进行调用。可以看出如果有route字段,sip会优先使用route字段的uri,然后是"x-obr" "x-obp" "maddr",最后才是request->req_uri。
int
__osip_nict_init (osip_nict_t ** nict, osip_t * osip, osip_message_t * request)
{
osip_route_t *route;
int i;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL, "allocating NICT context\n"));
*nict = (osip_nict_t *) osip_malloc (sizeof (osip_nict_t));
if (*nict == NULL)
return OSIP_NOMEM;
memset (*nict, 0, sizeof (osip_nict_t));
/* for REQUEST retransmissions */
/* for PROXY, the destination MUST be set by the application layer,
this one may not be correct. */
osip_message_get_route (request, 0, &route);
if (route != NULL && route->url != NULL) {
osip_uri_param_t *lr_param;
osip_uri_uparam_get_byname (route->url, "lr", &lr_param);
if (lr_param == NULL) {
/* using uncompliant proxy: destination is the request-uri */
route = NULL;
}
}
if (route != NULL && route->url != NULL) {
int port = 5060;
if (route->url->port != NULL)
port = osip_atoi (route->url->port);
osip_nict_set_destination ((*nict), osip_strdup (route->url->host), port);
}
else {
int port = 5060;
/* search for maddr parameter */
osip_uri_param_t *maddr_param = NULL;
osip_uri_param_t *obr_param = NULL;
osip_uri_param_t *obp_param = NULL;
port = 5060;
if (request->req_uri->port != NULL)
port = osip_atoi (request->req_uri->port);
/* if ob was used in Contact, then exosip adds "x-obr" and "x-obp", thus, when
processing request, the ip/port destination are re-used here */
osip_uri_uparam_get_byname(request->req_uri, "x-obr", &obr_param);
osip_uri_uparam_get_byname(request->req_uri, "x-obp", &obp_param);
osip_uri_uparam_get_byname (request->req_uri, "maddr", &maddr_param);
if (maddr_param != NULL && maddr_param->gvalue != NULL)
osip_nict_set_destination ((*nict), osip_strdup (maddr_param->gvalue), port);
else if (obr_param != NULL && obr_param->gvalue != NULL && obp_param != NULL && obp_param->gvalue != NULL)
osip_nict_set_destination ((*nict), osip_strdup (obr_param->gvalue), osip_atoi(obp_param->gvalue));
else
osip_nict_set_destination ((*nict), osip_strdup (request->req_uri->host), port);
}
(*nict)->timer_f_length = 64 * DEFAULT_T1;
osip_gettimeofday (&(*nict)->timer_f_start, NULL);
add_gettimeofday (&(*nict)->timer_f_start, (*nict)->timer_f_length);
/* Oups! a Bug! */
/* (*nict)->port = 5060; */
return OSIP_SUCCESS;
}
接下来看eXosip_call_terminate是怎么调用到__osip_nict_init 的,首先调用eXosip_create_transaction,通过一个比较长的调用链,最后到__osip_nict_init ,接下来的问题是,eXosip_call_terminate在哪里设置request->req_uri的。
static int
eXosip_create_transaction (struct eXosip_t *excontext, eXosip_call_t * jc, eXosip_dialog_t * jd, osip_message_t * request)
{
osip_event_t *sipevent;
osip_transaction_t *tr;
int i;
i = _eXosip_transaction_init (excontext, &tr, NICT, excontext->j_osip, request);
if (i != 0) {
/* TODO: release the j_call.. */
osip_message_free (request);
return i;
}
if (jd != NULL)
osip_list_add (jd->d_out_trs, tr, 0);
sipevent = osip_new_outgoing_sipmessage (request);
sipevent->transactionid = tr->transactionid;
osip_transaction_set_reserved2 (tr, jc);
osip_transaction_set_reserved3 (tr, jd);
osip_transaction_add_event (tr, sipevent);
_eXosip_wakeup (excontext);
return OSIP_SUCCESS;
}
int
_eXosip_transaction_init (struct eXosip_t *excontext, osip_transaction_t ** transaction, osip_fsm_type_t ctx_type, osip_t * osip, osip_message_t * message)
{
int i;
i = osip_transaction_init (transaction, ctx_type, osip, message);
if (i != 0) {
return i;
}
...
}
int
osip_transaction_init (osip_transaction_t ** transaction, osip_fsm_type_t ctx_type, osip_t * osip, osip_message_t * request)
{
...
if (ctx_type == NICT) {
(*transaction)->state = NICT_PRE_TRYING;
i = __osip_nict_init (&((*transaction)->nict_context), osip, request);
if (i != 0) {
osip_transaction_free (*transaction);
*transaction = NULL;
return i;
}
__osip_add_nict (osip, *transaction);
}
return OSIP_SUCCESS;
}
在eXosip_call_terminate_with_reason函数中,调用_eXosip_generating_bye去设置request的各种参数,如下:
int
eXosip_call_terminate (struct eXosip_t *excontext, int cid, int did) {
return eXosip_call_terminate_with_reason(excontext, cid, did, NULL);
}
int
eXosip_call_terminate_with_reason (struct eXosip_t *excontext, int cid, int did, const char *reason)
{
osip_transaction_t *tr;
osip_message_t *request = NULL;
eXosip_dialog_t *jd = NULL;
eXosip_call_t *jc = NULL;
if (did <= 0 && cid <= 0)
return OSIP_BADPARAMETER;
if (did > 0) {
_eXosip_call_dialog_find (excontext, did, &jc, &jd);
if (jd == NULL) {
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "eXosip: No call here?\n"));
return OSIP_NOTFOUND;
}
}
else {
_eXosip_call_find (excontext, cid, &jc);
}
......
i = _eXosip_generating_bye (excontext, &request, jd->d_dialog);
if (i != 0) {
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "eXosip: cannot terminate this call!\n"));
return i;
}
if (reason != NULL) {
osip_message_set_header(request, "Reason", reason);
}
_eXosip_add_authentication_information (excontext, request, NULL);
i = eXosip_create_transaction (excontext, jc, jd, request);
if (i != 0) {
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "eXosip: cannot initiate SIP transaction!\n"));
return i;
}
...
}
/* this request is only build within a dialog!! */
int
_eXosip_generating_bye (struct eXosip_t *excontext, osip_message_t ** bye, osip_dialog_t * dialog)
{
int i;
i = _eXosip_build_request_within_dialog (excontext, bye, "BYE", dialog);
if (i != 0)
return i;
return OSIP_SUCCESS;
}
int
_eXosip_build_request_within_dialog (struct eXosip_t *excontext, osip_message_t ** dest, const char *method, osip_dialog_t * dialog)
{
int i;
osip_message_t *request;
*dest = NULL;
i = osip_message_init (&request);
if (i != 0)
return i;
if (dialog->remote_contact_uri == NULL) {
/* this dialog is probably not established! or the remote UA
is not compliant with the latest RFC
*/
osip_message_free (request);
return OSIP_SYNTAXERROR;
}
/* and the request uri???? */
if (osip_list_eol (&dialog->route_set, 0)) {
/* The UAC must put the remote target URI (to field) in the req_uri */
if(NULL!= dialog->remote_uri)
{
i = osip_uri_clone(dialog->remote_uri->url,&(request->req_uri));
if(i != 0){
i = osip_uri_clone(dialog->remote_contact_uri->url,&(request->req_uri));
}
}
else{
i = osip_uri_clone (dialog->remote_contact_uri->url, &(request->req_uri));
}
//i = osip_uri_clone (dialog->remote_contact_uri->url, &(request->req_uri));
if (i != 0) {
osip_message_free (request);
return i;
}
}
else {
/* fill the request-uri, and the route headers. */
i = dialog_fill_route_set (dialog, request);
if (i != 0) {
osip_message_free (request);
return i;
}
}
/* To and From already contains the proper tag! */
i = osip_to_clone (dialog->remote_uri, &(request->to));
if (i != 0) {
osip_message_free (request);
return i;
}
i = osip_from_clone (dialog->local_uri, &(request->from));
if (i != 0) {
osip_message_free (request);
return i;
}
osip_message_set_user_agent (request, excontext->user_agent);
/* else if ... */
*dest = request;
return OSIP_SUCCESS;
}
看到这里基本就清楚了,构造request的时候的对端地址优先使用dialog->remote_uri,其次才是dialog->remote_contact_uri,调用osip_uri_clone将对话中的对端地址dialog->remote_uri拷贝到req_uri,所以只要在收到消息以后,使用contact_uri去更新到dialog->remote_uri就可以了。
osip_contact_t *contact;
osip_message_get_contact(rEvent.request, 0, &contact);
if(contact != NULL)
{
eXosip_dialog_t *jd = NULL;
eXosip_call_t *jc = NULL;
_eXosip_call_dialog_find (m_pEXosipCtx, rEvent.did, &jc, &jd);
osip_uri_free (jd->d_dialog->remote_uri->url);
jd->d_dialog->remote_uri->url = NULL;
osip_uri_clone(contact->url,&(jd->d_dialog->remote_uri->url));
}