Exosip源码学习2

最近遇到一个问题,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));
    }

你可能感兴趣的:(学习)