国标GBT28181协议,注册功能服务端与客户端实现代码

国标GBT28181协议的用户注册时候,需要用户名密码认证,其本质是使用 http digest的算法,

http digest算法,在RFC2617 [HTTP Authentication: Basic and Digest Access Authentication]文档里面有详细的描述,并且有实现代码,

具体请参考我的另外一篇博客,[MD5 SHA1 UUID 算法的C语言实现]

国标GBT28181注册流程如下,[GBT 28181-2011安全防范视频监控联网系统信息传输、交换、控制技术要求,第9.1.2.1 基本注册]


  1. 客户端(SIP代理),向服务器发送注册请求1,不带口令(密码),服务器返回401,其中携带一些字段值,要求客户端验证;
  2. 客户端接收到401未认证消息之后,携带口令已经401消息中携带的字段,进行 http digest密码数字摘要计算之后,传回给服务器,服务端对客户端的设备编码,设备口令进行验证,验证通过则发送200 OK消息。

我使用  eXosip2 库,实现了国标的注册代码的客户端与服务端,代码如下,请参考,


服务器端注册代码,


#include 
#include 
#include 
#include 
#include     //INADDR_ANY

#include 

//MD5
#include "HTTPDigest.h"

char *pcPassword = "12345";


char *pcRealm = "640001";
char *pcNonce = "6fe9ba44a76be22a";
static void Register401Unauthorized(struct eXosip_t * peCtx,eXosip_event_t *je)
{
    int iReturnCode = 0;
    osip_message_t * pSRegister = NULL;

    osip_www_authenticate_t * header = NULL;
    osip_www_authenticate_init(&header);

    osip_www_authenticate_set_auth_type (header,osip_strdup("Digest"));
    osip_www_authenticate_set_realm(header,osip_enquote(pcRealm));
    osip_www_authenticate_set_nonce(header,osip_enquote(pcNonce));

    char *pDest = NULL;
    osip_www_authenticate_to_str(header,&pDest);

    iReturnCode = eXosip_message_build_answer (peCtx,je->tid,401,&pSRegister);
    if ( iReturnCode == 0 && pSRegister != NULL )
    {
        osip_message_set_www_authenticate(pSRegister,pDest);
        osip_message_set_content_type(pSRegister,"Application/MANSCDP+xml");
        eXosip_lock(peCtx);
        eXosip_message_send_answer (peCtx,je->tid,401,pSRegister);
        eXosip_unlock(peCtx);
    }

    osip_www_authenticate_free(header);
    osip_free(pDest);
}

static void SelfCalculateResponse(char *pcUsername,char *pcURI,char *pcMethod,HASHHEX pcResponse)
{
    //MD5 计算
    HASHHEX HA1;
    DigestCalcHA1("REGISTER",pcUsername,pcRealm,pcPassword,pcNonce,NULL,HA1);

    HASHHEX Response;
    //在下面这个函数里面,已经计算了 H(A2),所以不需要自己计算 H(A2)
    DigestCalcResponse(HA1,pcNonce,NULL,NULL,NULL,0,pcMethod,pcURI,NULL,Response);

    memcpy(pcResponse,Response,HASHHEXLEN+1);
}

static void RegisterSuccess(struct eXosip_t * peCtx,eXosip_event_t *je)
{
    int iReturnCode = 0 ;
    osip_message_t * pSRegister = NULL;
    iReturnCode = eXosip_message_build_answer (peCtx,je->tid,200,&pSRegister);
    if ( iReturnCode == 0 && pSRegister != NULL )
    {
        eXosip_lock(peCtx);
        eXosip_message_send_answer (peCtx,je->tid,200,pSRegister);
        eXosip_unlock(peCtx);
    }
}

static void RegisterFailed(struct eXosip_t * peCtx,eXosip_event_t *je)
{
    int iReturnCode = 0 ;
    osip_message_t * pSRegister = NULL;
    iReturnCode = eXosip_message_build_answer (peCtx,je->tid,401,&pSRegister);
    if ( iReturnCode == 0 && pSRegister != NULL )
    {
        eXosip_lock(peCtx);
        eXosip_message_send_answer (peCtx,je->tid,401,pSRegister);
        eXosip_unlock(peCtx);
    }
}

static void *MainProcess(void * pvSClientGB)
{
    struct eXosip_t * peCtx = (struct eXosip_t *)pvSClientGB;
    for(;;)
    {
        eXosip_event_t *je = NULL;

        je = eXosip_event_wait (peCtx,0,4);

        if (je == NULL)
        {
            osip_usleep(10000);
            continue;
        }

        switch (je->type)
        {
            case EXOSIP_MESSAGE_NEW:
                {
                    //处理注册消息
                    if ( MSG_IS_REGISTER(je->request) )
                    {
                        //提取出各个字段值,进行 MD5 计算
                        osip_authorization_t * Sdest = NULL;
                        osip_message_get_authorization(je->request,0,&Sdest);
                        if ( Sdest != NULL )
                        {

                            char *pcMethod = je->request->sip_method;
                            char *pAlgorithm = osip_strdup_without_quote(Sdest->algorithm);
                            char *pUsername = NULL;
                            if ( Sdest->username != NULL )
                            {
                                pUsername = osip_strdup_without_quote(Sdest->username);
                            }
                            char *pRealm = NULL;
                            if ( Sdest->realm != NULL )
                            {
                                pRealm = osip_strdup_without_quote(Sdest->realm);
                            }
                            char *pNonce = NULL;
                            if ( Sdest->nonce != NULL )
                            {
                                pNonce = osip_strdup_without_quote(Sdest->nonce);
                            }
                            char *pNonce_count = NULL;
                            if ( Sdest->nonce_count != NULL)
                            {
                                pNonce_count = osip_strdup_without_quote(Sdest->nonce_count);
                            }

                            char *pUri = NULL;
                            if ( Sdest->uri != NULL )
                            {
                                pUri = osip_strdup_without_quote(Sdest->uri);
                            }

                            //需要去掉两端多余的引号
                            HASHHEX HA1;
                            DigestCalcHA1(pAlgorithm,pUsername,pRealm,pcPassword,pNonce,
                                          pNonce_count, HA1);

                            HASHHEX Response;
                            HASHHEX HA2="";
                            //在下面这个函数里面,已经计算了 H(A2),所以不需要自己计算 H(A2)
                            DigestCalcResponse(HA1,pNonce,pNonce_count,Sdest->cnonce,Sdest->message_qop,0,
                                               pcMethod,pUri,HA2,Response);


                            //Authenticate
                            char acResponse[HASHHEXLEN];
                            SelfCalculateResponse(pUsername,pUri,pcMethod,acResponse);
                            if ( memcmp(acResponse,Response,HASHHEXLEN) == 0 )
                            {
                                //发送注册成功的消息
                                RegisterSuccess(peCtx,je);

                                fprintf(stderr,"Register Success!!\n");
                            }
                            else  //认证失败
                            {
                                RegisterFailed(peCtx,je);
                            }

                            osip_free(pAlgorithm);
                            osip_free(pUsername);
                            osip_free(pRealm);
                            osip_free(pNonce);
                            osip_free(pNonce_count);
                            osip_free(pUri);

                        }
                        else   //认证失败
                        {
                            Register401Unauthorized(peCtx,je);
                        }
                    }
                }
                break;
            default:
                {
                }
                break;
        }

        eXosip_event_free(je);
    }

    return NULL;
}


int main()
{
    struct eXosip_t *eCtx;
    eCtx = eXosip_malloc();

    int iReturnCode = 0;
    iReturnCode = eXosip_init (eCtx);
    if (iReturnCode != OSIP_SUCCESS )
    {
        printf ("Can't initialize eXosip!\n");
        return -1;
    }
    else
    {
        printf ("eXosip_init successfully!\n");
    }

    iReturnCode = eXosip_listen_addr (eCtx,IPPROTO_UDP, NULL,5060, AF_INET, 0);
    if ( iReturnCode !=  OSIP_SUCCESS )
    {
        printf ("eXosip_listen_addr error!\n");
        return -1;
    }

    MainProcess(eCtx);

    eXosip_quit(eCtx);
    osip_free(eCtx);
    eCtx = NULL;

    return 0;
}

客户端注册代码,

#include 
#include 
#include 
#include 
#include     //INADDR_ANY

#include 


char *pcServerID = "34010000002000000001";
char *pcServerIP = "192.168.120.107";
int iServerPort = 5060;
char *pcClientID = "34010000001310000001";
int iClientPort = 5062;
char *pcPassword = "12345";


int g_Rid = 0;

/**
 * @brief 注册 向上级服务器注册
 * @param pSClientGB
 * @return
 */
#define GBT28181_REGISTER_SUCCESS 0
#define GBT28181_REGISTER_FAILURE -1
#define GBT28181_REGISTER_TIMEOUT -2
int GBT28181_Register(struct eXosip_t * peCtx)
{
    int i = 0;

    int iRid = 0;

    osip_message_t * reg = NULL;

    char acClientURI[128];
    char acServerURI[128];

    char acDeiveID10Bit[11];
    memset(acDeiveID10Bit,0,11);
    strncpy(acDeiveID10Bit,pcClientID,10);

    sprintf(acClientURI,"sip:%s@%s:%d",pcClientID,acDeiveID10Bit,iClientPort);
    sprintf(acServerURI,"sip:%s@%s:%d",pcServerID,pcServerIP,iServerPort);

    iRid = eXosip_register_build_initial_register(peCtx,acClientURI,acServerURI,NULL,3600,®);
    if (iRid < 0)
    {
        return -1;
    }

    g_Rid = iRid;

    i = eXosip_register_send_register (peCtx,iRid,reg);
    if ( i !=  OSIP_SUCCESS )
    {
        return GBT28181_REGISTER_TIMEOUT;
    }
    else
    {

    }

    return GBT28181_REGISTER_SUCCESS;
}



static void *MainProcess(void * pvSClientGB)
{
    struct eXosip_t * peCtx = (struct eXosip_t *)pvSClientGB;

    int iHasStartFlag = 0;
    for(;;)
    {
        eXosip_event_t *je = NULL;

        je = eXosip_event_wait (peCtx,0,4);

        if ( iHasStartFlag == 0 )
        {
            iHasStartFlag = 1;
        }
        if ( iHasStartFlag == 1 )
        {
            //Send Register to Server
            GBT28181_Register(peCtx);
            iHasStartFlag = 2;
        }

        if (je == NULL)
        {
            osip_usleep(10000);
            continue;
        }


        switch (je->type)
        {
         case EXOSIP_REGISTRATION_SUCCESS:
            {
                if ( je->response != NULL )
                {
                    osip_header_t *Date = NULL;
                    osip_message_header_get_byname (je->response,"Date",0,&Date);
                    if ( (Date != NULL) && (Date->hvalue != NULL) )
                    {
                        char *pcDate = Date->hvalue;
                        //HandleTiming(pSClientGB,pcDate);  //处理校时指令

                        fprintf(stderr,"Register Success!!\n");
                    }

                }
            }
            break;
         case EXOSIP_REGISTRATION_FAILURE:
            {
                //发送带用户名密码的注册消息
                if ( je->response != NULL )
                {
                    if(je->response->status_code == 401 || je->response->status_code == 407)
                    {
                        eXosip_lock(peCtx);
                        eXosip_add_authentication_info (peCtx,pcClientID,pcClientID,pcPassword,"MD5",NULL);//向eXosip提供本机的认证信息
                        eXosip_unlock(peCtx);

                        osip_message_t * reg = NULL;

                        eXosip_lock(peCtx);
                        eXosip_register_build_register (peCtx,g_Rid,3600,®);

                        eXosip_register_send_register (peCtx,g_Rid,reg);
                        eXosip_unlock(peCtx);
                    }
                }
            }
        }

        eXosip_event_free(je);
    }

    return NULL;
}


int main()
{
    struct eXosip_t *eCtx;
    eCtx = eXosip_malloc();

    int iReturnCode = 0;
    iReturnCode = eXosip_init (eCtx);
    if (iReturnCode != OSIP_SUCCESS )
    {
        printf ("Can't initialize eXosip!\n");
        return -1;
    }
    else
    {
        printf ("eXosip_init successfully!\n");
    }

    iReturnCode = eXosip_listen_addr (eCtx,IPPROTO_UDP, NULL,iClientPort, AF_INET, 0);
    if ( iReturnCode !=  OSIP_SUCCESS )
    {
        printf ("eXosip_listen_addr error!\n");
        return -1;
    }

    MainProcess(eCtx);

    eXosip_quit(eCtx);
    osip_free(eCtx);
    eCtx = NULL;

    return 0;
}


非常需要注意的是,内存泄漏,osip2 库与 eXosip2 库使用时,有很多地方都需要注意内存是否自动回收,若没有回收,需要自己手动回收内存。

比如上面代码中的函数,

osip_strdup_without_quote()

osip_www_authenticate_to_str()

库内部都没有回收内存,需要手动释放内存。内存泄漏检测,有一个 valgrind 工具,感觉挺好用,推荐!!


我在资源里面,上传了本代码的工程,编写好了makefile,可在 linux 下可以直接编译运行。请参考。


你可能感兴趣的:(国标GBT28181)