视频监控安防平台-GB28181_2016版-注册、心跳、注销
前段时间好几个项目在同时进行,整天忙的不可开交,尤其是项目上的雪亮工程项目需求太多了(而且是各种奇葩的需求,估计也是招投标为了限制其他公司的吧,很多功能都没有实际用处),也没用静下心来好好整理下GB28181相关内容,我准备从头整理一下相关信息,主要以28181检测项为索引来整理,下面我简单介绍下注册、心跳和注销这几个协议,注册和注销很好理解,就是Expires: 0的时候为注销,心跳也很简单就是一个简单的无应答message。
下面将注册的流程图简单摘出来一下:
根据流程图还是比较容易读懂,下面粘贴一下抓包信息:
REGISTER sip:[email protected]:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME
From: ;tag=pck2i7pI
To:
Call-ID: [email protected]
CSeq: 5 REGISTER
Contact:
Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="bd2e4df9e3d9b280", uri="sip:[email protected]:5060", response="9c8411f2b96c5aef55eb136ba3f34655", algorithm=MD5
Max-Forwards: 70
User-Agent: iVMS 1.0
Expires: 200
Content-Length: 0
SIP/2.0 401 Unauthorized
To: ;tag=67239569_53173353_7ce4590f-587d-4d44-9690-96498367c675
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME;received=192.168.1.93
CSeq: 5 REGISTER
Call-ID: [email protected]
From: ;tag=pck2i7pI
WWW-Authenticate: Digest realm="3402000000",nonce="31ada55697307236"
Content-Length: 0
REGISTER sip:[email protected]:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-3068be5-lnl05y0C
From: ;tag=pck2i7pI
To:
Call-ID: [email protected]
CSeq: 6 REGISTER
Contact:
Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="31ada55697307236", uri="sip:[email protected]:5060", response="3526a5a5dd91a45e19d1e6a65704457f", algorithm=MD5
Max-Forwards: 70
User-Agent: iVMS 1.0
Expires: 200
Content-Length: 0
SIP/2.0 200 OK
To: ;tag=86156704_53173353_d7fb589f-4357-4fae-ae32-47c38d81fff1
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-3068be5-lnl05y0C;received=192.168.1.93
CSeq: 6 REGISTER
Call-ID: [email protected]
From: ;tag=pck2i7pI
Contact:
Expires: 200
Date: 2017-05-19T18:09:17.033
Content-Length: 0
其中注册要注意的是,注册的时候有个回复时间格式需要进行上下级和设备之间校时功能:
Date: 2017-05-19T18:09:17.033
心跳:
MESSAGE sip:[email protected]:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-2a2b21b-nddru7fy
From: ;tag=UL61Qycy
To:
Call-ID: [email protected]
CSeq: 35 MESSAGE
Contact:
Content-Type: Application/MANSCDP+xml
Max-Forwards: 70
User-Agent: iVMS 1.0
Content-Length: 151
Keepalive
26
64000000002000000001
OK
SIP/2.0 200 OK
To: ;tag=69112542_53173353_89bb4bc2-7f74-42a8-a102-6b6490b6daa2
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-2a2b21b-nddru7fy;received=192.168.1.93
CSeq: 35 MESSAGE
Call-ID: [email protected]
From: ;tag=UL61Qycy
Content-Length: 0
REGISTER sip:[email protected]:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME
From: ;tag=pck2i7pI
To:
Call-ID: [email protected]
CSeq: 5 REGISTER
Contact:
Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="bd2e4df9e3d9b280", uri="sip:[email protected]:5060", response="9c8411f2b96c5aef55eb136ba3f34655", algorithm=MD5
Max-Forwards: 70
User-Agent: iVMS 1.0
Expires: 0
Content-Length: 0
SIP/2.0 401 Unauthorized
To: ;tag=67239569_53173353_7ce4590f-587d-4d44-9690-96498367c675
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-23b5dfc-TFyCL5ME;received=192.168.1.93
CSeq: 5 REGISTER
Call-ID: [email protected]
From: ;tag=pck2i7pI
WWW-Authenticate: Digest realm="3402000000",nonce="31ada55697307236"
Content-Length: 0
REGISTER sip:[email protected]:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.93:5060;rport;branch=z9hG4bK-3d09000-3068be5-lnl05y0C
From: ;tag=pck2i7pI
To:
Call-ID: [email protected]
CSeq: 6 REGISTER
Contact:
Authorization: Digest username="34020000002000000001", realm="3402000000", nonce="31ada55697307236", uri="sip:[email protected]:5060", response="3526a5a5dd91a45e19d1e6a65704457f", algorithm=MD5
Max-Forwards: 70
User-Agent: iVMS 1.0
Expires: 0
Content-Length: 0
SIP/2.0 200 OK
To: ;tag=86156704_53173353_d7fb589f-4357-4fae-ae32-47c38d81fff1
Via: SIP/2.0/UDP 192.168.1.93:5060;rport=5060;branch=z9hG4bK-3d09000-3068be5-lnl05y0C;received=192.168.1.93
CSeq: 6 REGISTER
Call-ID: [email protected]
From: ;tag=pck2i7pI
Contact:
Expires: 0
Date: 2017-05-19T18:09:17.033
Content-Length: 0
下面粘贴一下部分注册的代码,由于使用c++写的代码,所有只能裁剪一部分出来:
#include
#include
#include "eXosip2/eXosip.h"
#include "osipparser2/osip_uri.h"
#include "osipparser2/osip_message.h"
#include "eXosip2.h"
//初始化 监听端口
int Init(int listenport)
{
int iRet = eXosip_init();
if (iRet != OSIP_SUCCESS)
{
//DBGPrint(M_SipUA, ERROR_LEVEL, "eXosip2 fail!");
//return -1;
printf("eXosip2 fail! \n");
exit(1);
}
iRet = eXosip_listen_addr(IPPROTO_UDP, NULL, listenport, AF_INET, 0);
if (iRet != OSIP_SUCCESS)
{
eXosip_quit();
//DBGPrint(M_SipUA, ERROR_LEVEL, "eXosip2 could not initialize transport layer!");
//return -1;
DBGPrint(M_SipUA, BREAK_LEVEL, "eXosip2 could not initialize transport layer! \n");
exit(1);
}
DBGPrint(M_SipUA, BREAK_LEVEL, "%s: SipSvr Listen Port:%d Sucess!!!!", __FUNCTION__, listenport);
return 0;
}
/*******************************************************************************
* Function:
* Digest MD5 authorization validate process.
*******************************************************************************/
//MD5鉴权
bool MD5AuthValidate(char *pMethod, osip_authorization_t *pAuthEcho)
{
if (NULL == pAuthEcho)
{
DBGPrint(M_SipUA, ERROR_LEVEL, "pAuthField is null pointer!");
return false;
}
char *pAlgorithm = NULL;
char *pUsername = NULL;
char *pRealm = NULL;
char *pPasswd = NULL;
char *pNonce = NULL;
char *pNonceCount = NULL;
char *pCNonce = NULL;
char *pQop = NULL;
char *pCMethod = NULL;
char *pUri = NULL;
char sNullAlg[] = ""; //"MD5";
char sNullQop[] = ""; //"auth";
char SessionKey[MD5_SESSION_KEY_LEN+1] = {0};
char Response[MD5_RESPONSE_LEN] = {0};
char *pResponse2 = NULL;
if (NULL == pAuthEcho->algorithm)
pAlgorithm = unquote(sNullAlg);
else
pAlgorithm = unquote(pAuthEcho->algorithm);
pUsername = unquote(pAuthEcho->username);
pRealm = unquote(pAuthEcho->realm);
pPasswd = unquote(CSBase::m_SipRegPasswd);
pNonce = unquote(pAuthEcho->nonce);
pCNonce = unquote(pAuthEcho->cnonce);
if (NULL == pUsername)
{
DBGPrint(M_SipUA, ERROR_LEVEL, "%s Get username is null error!",__FUNCTION__);
return false;
}
if(strcmp(pUsername, CSBase::m_SipRegUserName) != 0)
{
// DBGPrint(M_SipUA, ERROR_LEVEL, "%s %s username error!",__FUNCTION__,pAuthEcho->username);
// return false;
}
//calculate session key
DigestCalcHA1(pAlgorithm, pUsername, pRealm, pPasswd, pNonce, pCNonce, SessionKey);
pNonceCount = unquote(pAuthEcho->nonce_count);
if (NULL == pAuthEcho->message_qop)
pQop = unquote(sNullQop);
else
pQop = unquote(pAuthEcho->message_qop);
pCMethod = unquote(pMethod);
pUri = unquote(pAuthEcho->uri);
//calculate response
DigestCalcResponse(SessionKey, pNonce, pNonceCount, pCNonce, pQop, pCMethod, pUri, (char*)"", Response);
pResponse2 = unquote(pAuthEcho->response);
if (strcmp(Response, pResponse2) != 0)
{
DBGPrint(M_SipUA, ERROR_LEVEL, "Authorization failed <%s, %s>!", Response, pResponse2);
return false;
}
return true;
}
//回复没有包体的响应
int SendRegisterAnswer(int Tid, int Code, bool bSetTime)
{
osip_message_t * pMsgAnswer = NULL;
int ret = eXosip_message_build_answer(Tid, Code,&pMsgAnswer);
if(ret == OSIP_SUCCESS && pMsgAnswer != NULL )
{
if (bSetTime == true)
{
osip_header_t* pHeader = NULL;
char TmpBuf[CLIP_BUFFER_SIZE+1];
//initialize Date header
int iRet = osip_header_init(&pHeader);
if (OSIP_SUCCESS == iRet)
{
time_t now;
struct timeval tv;
//Get current time
gettimeofday(&tv,NULL);
now = tv.tv_sec;
// time(&now);
//struct tm* pTmVal = localtime(&now);
struct tm STm = {0};
struct tm* pTmVal = localtime_r(&now, &STm); //使用线程安全函数
if (pTmVal != NULL)
{
//系统启用夏令时,需要减去一个小时。
if (1 == pTmVal->tm_isdst)
{
now -= 3600;
//pTmVal = localtime(&now);
pTmVal = localtime_r(&now, &STm); //使用线程安全函数
if (NULL == pTmVal)
return -1;
}
//2010-07-26 16:05:10
snprintf(TmpBuf, CLIP_BUFFER_SIZE, "%4d-%02d-%02dT%02d:%02d:%02d.%03d", pTmVal->tm_year+1900, pTmVal->tm_mon+1, pTmVal->tm_mday, pTmVal->tm_hour, \
pTmVal->tm_min, pTmVal->tm_sec,(int)(tv.tv_usec/1000));
//set Date field
osip_header_set_name(pHeader, osip_strdup("Date") );
osip_header_set_value(pHeader, osip_strdup(TmpBuf) );
osip_list_add(&pMsgAnswer->headers, pHeader, -1);
}
}
}
eXosip_message_send_answer(Tid, Code, pMsgAnswer);
}
else
{
DBGPrint(M_SipUA, ERROR_LEVEL, "%s:Build Message Answer Failed Tid<%d> Code<%d>!", __FUNCTION__, Tid, Code);
}
return 0;
}
//初始接收的注册信息
int ProceRegister(const eXosip_event_t* pSipEvt)
{
int iReturnCode = -1;
int iRet = -1;
osip_authorization_t *pWWWAuEcho = NULL;
iRet = osip_message_get_authorization(pSipEvt->request, 0, &pWWWAuEcho);
if (iRet != -1)
{
//鉴权, 鉴权成功回复200OK
if (MD5AuthValidate(pSipEvt->request->sip_method, pWWWAuEcho))
{
//鉴权成功,回复200成功
//添加用户信息
SendRegisterAnswer(pSipEvt->tid, SIP_OK, true);
}
else
{
//鉴权失败,回复403
SendRegisterAnswer(pSipEvt->tid, SIP_FORBIDDEN, false);
}
}
else
{
//回复401 , 待认证
osip_www_authenticate_t *pWWWAuth = NULL;
iRet = osip_www_authenticate_init(&pWWWAuth);
if (-1 == iRet)
{
DBGPrint(M_SipUA, ERROR_LEVEL, "Failed to init www-authenticate header");
return -1;
}
char RTag[RANDOM_TAG_LEN+1] = {0};
char Nonce[DIGEST_NONCE_LEN+1] = {0};
char TmpBuf[CLIP_BUFFER_SIZE/2+1] = {0};
SIPGenerateNonce(Nonce, 0, GenerateRandomTag(RTag) );
osip_www_authenticate_set_auth_type(pWWWAuth, osip_strdup("Digest") );
memcpy(TmpBuf, CSBase::m_SipSvrPubID, 10);
char realm[CLIP_BUFFER_SIZE];
snprintf(realm, CLIP_BUFFER_SIZE/2, "\"%s\"", TmpBuf);
osip_www_authenticate_set_realm(pWWWAuth, osip_strdup(realm) );
snprintf(TmpBuf, CLIP_BUFFER_SIZE/2, "\"%s\"", Nonce);
osip_www_authenticate_set_nonce(pWWWAuth, osip_strdup(TmpBuf) );
char *pDest = NULL;
osip_www_authenticate_to_str(pWWWAuth, &pDest);
osip_message_t * pSRegister = NULL;
iReturnCode = eXosip_message_build_answer(pSipEvt->tid,SIP_UNAUTHORIZED,&pSRegister);
if ( iReturnCode == 0 && pSRegister != NULL )
{
osip_message_set_www_authenticate(pSRegister,pDest);
osip_message_set_content_type(pSRegister,"Application/MANSCDP+xml");
eXosip_message_send_answer(pSipEvt->tid,SIP_UNAUTHORIZED,pSRegister);
}
osip_www_authenticate_free(pWWWAuth);
osip_free(pDest);
}
return 0;
}
int ProceXsipEvt(eXosip_event_t* pSipEvt)
{
//Check input parameter exception
if (NULL == pSipEvt)
{
DBGPrint(M_SipUA, ERROR_LEVEL, "%s: pSipEvt is null pointer!", __FUNCTION__);
return -1;
}
//Check eXosip event type
switch (pSipEvt->type)
{
case EXOSIP_MESSAGE_NEW:
if (MSG_IS_NOTIFY(pSipEvt->request))
{
//ProceNotify(pSipEvt);
}
else if(MSG_IS_MESSAGE(pSipEvt->request))
{
//ProceMessage(pSipEvt);
}
else if(MSG_IS_REGISTER(pSipEvt->request))
{
ProceRegister(pSipEvt);
}
break;
case EXOSIP_SUBSCRIPTION_ANSWERED:
case EXOSIP_SUBSCRIPTION_REQUESTFAILURE:
{
//ProcSubScribleAnswer(pSipEvt);
}
break;
case EXOSIP_SUBSCRIPTION_NOTIFY:
{
//ProceNotify(pSipEvt);
}
break;
case EXOSIP_CALL_PROCEEDING:
case EXOSIP_CALL_RINGING:
break;
case EXOSIP_CALL_ANSWERED:
{
eXosip_call_t* pJCall = NULL;
if (OSIP_SUCCESS == eXosip_call_find(pSipEvt->cid, &pJCall) )
{
//ProcCallAnswer(pSipEvt);
}
}
break;
case EXOSIP_CALL_NOANSWER:
case EXOSIP_CALL_REDIRECTED:
case EXOSIP_CALL_REQUESTFAILURE:
case EXOSIP_CALL_SERVERFAILURE:
case EXOSIP_CALL_GLOBALFAILURE:
case EXOSIP_CALL_TIMEOUT:
{
if (pSipEvt->request == NULL || pSipEvt->response == NULL)
{
DBGPrint(M_SipUA, ERROR_LEVEL, "%s: Recv Sip Type<%d:%s> Error!", __FUNCTION__, pSipEvt->type, GetRecvSipType(pSipEvt->type));
break;
}
DBGPrint(M_SipUA, ERROR_LEVEL, "%s: ******** receive device:<%s> error !response status code:<%d> username:<%s>!", __FUNCTION__, pSipEvt->request->to->url->username, pSipEvt->response->status_code, pSipEvt->request->req_uri->username );
eXosip_call_t* pJCall = NULL;
if (OSIP_SUCCESS == eXosip_call_find(pSipEvt->cid, &pJCall) )
{
//ProcCallAnswer(pSipEvt);
}
}
break;
case EXOSIP_CALL_MESSAGE_NEW:
if (MSG_IS_BYE(pSipEvt->request) )
{
DBGPrint(M_SipUA, ERROR_LEVEL, "%s: ******** imcoming BYE for DevAor<%s>!", __FUNCTION__, CSBase::URIToAOR(pSipEvt->request->from->url) );
}
break;
case EXOSIP_CALL_MESSAGE_ANSWERED:
//BYE response
break;
case EXOSIP_CALL_CLOSED:
break;
case EXOSIP_CALL_RELEASED:
break;
case EXOSIP_MESSAGE_REQUESTFAILURE:
break;
case EXOSIP_MESSAGE_ANSWERED:
break;
default:
DBGPrint(M_SipUA, ERROR_LEVEL, "%s: not handle <%d:%s> event type!", __FUNCTION__, pSipEvt->type, GetRecvSipType(pSipEvt->type));
return -1;
}
}
int main()
{
//初始化exosip协议栈端口
Init(5060);
//等待接收sip数据
while(1)
{
//---------------- eXosip running process ----------------//
eXosip_execute();
eXosip_event_t* pSipEvt = eXosip_event_wait(0, 50);
while (pSipEvt != NULL)
{
eXosip_lock();
ProceXsipEvt(pSipEvt);
eXosip_default_action(pSipEvt);
eXosip_unlock();
eXosip_event_free(pSipEvt);
pSipEvt = eXosip_event_wait(0, 50);
}
usleep(50000);
}
}