请求路由
松散路由(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);