请求路由

松散路由(Loose Router)和严格路由(Strict Router)这是SIP协议(RFC3261)中的一个非常重要的概念。 在SIP消息的Route头域或Record-route头域中,携带的域值是SIP URI或SIPS URI,如果这个URI带有”lr”属性值,那么,就表示URI的地址所标识的SIP PROXY是松散路由的,否则,是严格路由的

松散路由例子

Record-Route: <sip:p1.example.com;lr>

Record-Route: <sip:61.23.23.12:5070;lr>

Route: <sip:p2.example.com;lr>

Route: <sip:61.23.23.12:5070;lr>

注意尖括号


关键字lr

lr是松散路由(Loose Router)的首字母小写


应答消息路由

SIP应答消息的路由机制,相对请求来说,比较简单,基本思想就是,请求从哪里来,应答回哪里去。那是如何实现的呢?很简单,Via头域就是完成这个差事的。Via头域表说明了SIP请求实际的路由过程,用于应答消息的回程路由。 下面是一个via的示例 Via: SIP/2.0/UDP 192.168.3.100:5070;rport=5070;branch=z9hG4bK647374545;received=61.176.71.234


分析注册SIP报文

REGISTER sip:44010000002000000099@44010000 SIP/2.0
Via: SIP/2.0/UDP 192.168.3.100:5070;rport;branch=z9hG4bK647374545
From: <sip:44010000001180000001@44010000>;tag=1166168965
To: <sip:44010000001180000001@44010000>
Call-ID: 1662597673
CSeq: 1 REGISTER
Contact: <sip:44010000001180000001@192.168.3.100:5070>
Max-Forwards: 70
User-Agent: Embedded Net DVR/NVR/DVS
Expires: 86400
Content-Length: 0

SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.3.100:5070;rport=5070;branch=z9hG4bK647374545;received=61.176.71.234
From: <sip:44010000001180000001@44010000>;tag=1166168965
To: <sip:44010000001180000001@44010000>;tag=544225908
Call-ID: 1662597673
CSeq: 1 REGISTER
User-Agent: eXosip/4.1.0
Www-authenticate: Digest realm="Exsip", algorithm=MD5, nonce="65e56e6b"
Content-Length: 0


获取下级国标平台注册的外网地址

_eXosip_handle_incoming_message负责处理接收到的报文,char *host, int port保存了下级国标服务器外网发送的IP地址和端口

然后调用osip_message_fix_last_via_header,保存IP地址和端口到via字段中

下级国标注册上来,调用函数

osip_message_fix_last_via_header (se->sip, host, port);

添加下级国标的外网发送地址到via字段,

via字段变成:SIP/2.0/UDP 192.168.3.100:5070;rport=5070;branch=z9hG4bK647374545;received=61.176.71.234

其中received是下级国标的公网地址,rport是下级国标的公网端口

/* This method just add a received parameter in the Via
   as requested by rfc3261 */
int
osip_message_fix_last_via_header (osip_message_t * request, const char *ip_addr, int port)
{
  osip_generic_param_t *rport;
  osip_via_t *via;

  /* get Top most Via header: */
  if (request == NULL)
    return OSIP_BADPARAMETER;
  if (MSG_IS_RESPONSE (request))
    return OSIP_SUCCESS;        /* Don't fix Via header */

  via = osip_list_get (&request->vias, 0);
  if (via == NULL || via->host == NULL)
    /* Hey, we could build it? */
    return OSIP_BADPARAMETER;

  osip_via_param_get_byname (via, "rport", &rport);
  if (rport != NULL) {
    if (rport->gvalue == NULL) {
      rport->gvalue = (char *) osip_malloc (9);
      if (rport->gvalue == NULL)
        return OSIP_NOMEM;
#if !defined __PALMOS__ && (defined WIN32 || defined _WIN32_WCE)
      _snprintf (rport->gvalue, 8, "%i", port);
#else
      snprintf (rport->gvalue, 8, "%i", port);
#endif
    }                           /* else bug? */
  }

  /* only add the received parameter if the 'sent-by' value does not contains
     this ip address */
  if (0 == strcmp (via->host, ip_addr)) /* don't need the received parameter */
    return OSIP_SUCCESS;
  osip_via_set_received (via, osip_strdup (ip_addr));
  return OSIP_SUCCESS;
}

这里会判断报文里面的IP地址跟接收到的发送IP地址是否是同一个,如果不同,说明有进行了转发,然后保存在received字段中,回复401的时候,会将其拷贝到发送的via中

osip_via_param_get_byname 获取via属性

osip提供了大量的接口操作via字段,进行数据的填充,via字段又涉及到发送的目的IP和端口,因此进行详细记录

      osip_via_param_get_byname (via, "maddr", &maddr);
      osip_via_param_get_byname (via, "received", &received);
      osip_via_param_get_byname (via, "rport", &rport);


查询目录

发送查询目录请求给到下级国标服务器,需要先发送给下级国标服务器所在的公网,下级服务器所在的公网会转发SIP报文给下级国标服务器,也就是俗称的NAT穿透

const char* route = "<sip:61.23.23.12:5070;lr>";
osip_message_set_route(msg, route);
eXosip_message_build_request

上面的代码生成的SIP报文中,不会出现61.23.23.12:5070等公网的信息,这个信息单纯用来发送到指定的公网的IP地址和端口

其中route中的IP地址和端口,需要从注册的下级国标服务器的报文中获取,报文中的via保存了公网的IP地址和端口

osip_message_fix_last_via_header (se->sip, host, port);

开始点播

/* this method can't be called unless the previous
   INVITE transaction is over. */
int
eXosip_call_build_initial_invite (struct eXosip_t *excontext, 
                                  osip_message_t ** invite, 
                                  const char *to, 
                                  const char *from, 
                                  const char *route, 
                                  const char *subject)

设置跟查询目录一致,都是通过设置route为<sip:61.23.23.12:5070;lr>,然后发送出去


停止点播

SIP会话发送BYE报文,通过设置route_set,指定发送到下级国标的外网地址,外网再转发给下级国标

#include "eXosip2/eXosip2.h"
const char* pszroute = "<sip:61.23.23.12:5070;lr>";
eXosip_dialog_t* jd = NULL;
eXosip_call_t* jc = NULL;
 _eXosip_call_dialog_find(excontext, did, &jc, &jd);
osip_route_t* route = NULL;
osip_route_init(&route);
osip_route_parse(route, pszroute);
osip_list_add(&jd->d_dialog->route_set, route, 0);
eXosip_call_terminate(excontext, cid, did);