//////////////////////////////////////////////////
//Title    : wxc_netcap.c
//Auhtor   : Liu Wei
//Desc     : wxc2 netcap implementation
//Created  : 2007-06-05
//Revision :
//
//Revision :
//
//////////////////////////////////////////////////

#include "./include/pub_netcap.h"

/*@ignore@*/
#ifndef IPPROTO_SCTP
#define IPPROTO_SCTP    132
#endif

static NetCap tNC;

#define BASE 65521
extern u32 GetLocalIP();

static u32 NcAd32 ( u32 ad, u8 * buf, int len )
{
    u32     s1 = ad & 0xffff;
    u32     s2 = ( ad >> 16 ) & 0xffff;
    int     n;

    for ( n = 0; n < len; n++, buf++ )
    {
        s1 = ( s1 + *buf );

        if ( s1 >= BASE )
            s1 -= BASE;

        s2 = ( s2 + s1 );

        if ( s2 >= BASE )
        {

            s2 -= BASE;
        }
    }
    return ( s2 << 16 ) + s1;
}

#define HEADER_LEN 12

static u32 NcCS ( u8 * ptr, u16 count )
{

    u32     ad = 1L;
    u32     zero = 0L;

    ad = NcAd32 ( ad, ptr, sizeof ( HEADER_LEN - sizeof ( u32 ) ) );
    ad = NcAd32 ( ad, ( u8 * ) & zero, sizeof ( u32 ) );
    ptr += HEADER_LEN;
    count -= HEADER_LEN;

    ad = NcAd32 ( ad, ptr, count );
    return ad;
}

static int NcGetSocket ( u32 port )
{
    int     nSock;
    int     nOn = 1;
    unsigned long  cmdarg = 1;
    struct sockaddr_in sin_addr;

    sin_addr.sin_family = AF_INET;
    sin_addr.sin_port = htons ( port );
    sin_addr.sin_addr.s_addr =  GetLocalIP(); //htonl ( INADDR_ANY );

    if ( ( nSock = socket ( AF_INET, SOCK_RAW, IPPROTO_SCTP ) ) < 0 )
    {
        perror ( "Get SCTP socket error " );
        return -1;
    }

    setsockopt ( nSock, SOL_SOCKET, SO_REUSEADDR, &nOn, sizeof ( nOn ) );

    ioctl ( nSock, FIONBIO, &cmdarg );

    return ( nSock );
}

extern void NcSetPort ( u32 port )
{
    tNC.nPort = port;
}

extern void NcSetIP ( u32 ip )
{
    tNC.nDstIp = ip;
}

extern int NcInit (  )
{
    memset ( &tNC, 0, sizeof ( NetCap ) );
    tNC.nPort = 8000;
    tNC.nDstIp = inet_addr ( "172.54.240.3" );
    return 1;
}

extern int NcStart ( u32 wIP, u32 wPort )
{
    if ( tNC.uState )
        return 0;
    tNC.nDstIp = wIP ? wIP : tNC.nDstIp;
    tNC.nPort = wPort ? wPort : tNC.nPort;
    tNC.nSock = NcGetSocket ( tNC.nPort );
    tNC.uState = 1;
    return 1;
}

extern int NcStop (  )
{
    shutdown ( tNC.nSock, 2 );
    tNC.uState = 0;
    return 1;
}

#define SCTP_HEADER_LEN 0x1C

#define SCCTP_CS_POS 0x08

#define SCTP_CHUNKLEN_POS 15

static u8 SCTP_HEADER[] = {
    0x13, 0x56, 0x13, 0x56, 0x00, 0x00, 0x2d, 0xd1,
    0x05, 0x67, 0x59, 0x7a, 0x00, 0x00, 0x00, 0x3c,
    0x00, 0x00, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11,
    0x00, 0x00, 0x00, 0x02
};

static int NcStuffSCTP ( u8 * pIPBuff )
{
    memcpy ( pIPBuff, SCTP_HEADER, SCTP_HEADER_LEN );
    pIPBuff[0] = 4950 >> 8;
    pIPBuff[1] = 4950 & 0xFF;
    pIPBuff[2] = tNC.nPort >> 8;
    pIPBuff[3] = tNC.nPort & 0xFF;
    return SCTP_HEADER_LEN;
}

#define M2UA_HEADER_LEN 0x14

#define M2UA_MSGLEN_POS 0x07

#define M2UA_PARALEN_POS 0x13

static  int NcGetM2uaParaLen ( u32 wMtp3Len )
{
    return ( wMtp3Len + 4 );
}

static  int NcGetM2uaMsgLen ( u32 wMtp3Len )
{
    return ( NcGetM2uaParaLen ( wMtp3Len ) + 16 );
}

static u8 M2UA_HEADER[] = {
    0x01, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00, 0x2c,
    0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x00, 0x1c
};

int NcStuffM2UA ( u8 * pIPBuff, u32 wMtp3Len )
{
    memcpy ( pIPBuff, M2UA_HEADER, M2UA_HEADER_LEN );
    *( pIPBuff + M2UA_PARALEN_POS ) = NcGetM2uaParaLen ( wMtp3Len );
    *( pIPBuff + M2UA_MSGLEN_POS ) = NcGetM2uaMsgLen ( wMtp3Len );
    return M2UA_HEADER_LEN;
}

#define MTP3_HAEDER_ANSI_LEN 0x08

static u8 MTP3_HEADER_ANSI[] = {
    0x83, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00
};

#define MTP3_HAEDER_ITU_LEN 0x05

static u8 MTP3_HEADER_ITU[] = {
    0x83, 0x20, 0x00, 0x08, 0x00
};

static int NcStuffMtp3 ( u8 * pIPBuff, u32 wDPC, u32 wOPC, u8 uSLS,
                         u8 uAnsiFlag )
{
    u32     MTP3_HAEDER_LEN;
    u8     *MTP3_HEADER;

    //printf ( "stuff mtp3 : dpc :%ld opc :%ld sls: %d , ansi :%d \n", wDPC, wOPC,
    //         uSLS, uAnsiFlag );
    if ( uAnsiFlag )
    {
        MTP3_HEADER_ANSI[1] = wDPC ? MTP3_HEADER_ANSI[1] : wDPC;  
        MTP3_HEADER_ANSI[4] = wOPC ? MTP3_HEADER_ANSI[4] : wOPC;
        MTP3_HEADER_ANSI[7] = uSLS ? MTP3_HEADER_ANSI[7] : uSLS;
    }
    else
    {
        if ( wDPC )
        {
            MTP3_HEADER_ITU[1] = wDPC & 0xFF;
            MTP3_HEADER_ITU[2] &= 0xC0;
            MTP3_HEADER_ITU[2] |= ( wDPC >> 8 ) & 0x3F;
        }
        if ( wOPC )
        {
            MTP3_HEADER_ITU[2] &= 0x3F;
            MTP3_HEADER_ITU[2] |= ( wDPC & 0x03 ) << 6;
            MTP3_HEADER_ITU[3] = wDPC >> 2;
            MTP3_HEADER_ITU[4] &= 0xF0;
            MTP3_HEADER_ITU[4] |= wOPC >> 10;
        }

        MTP3_HEADER_ITU[4] &= 0x0F;
        MTP3_HEADER_ITU[4] |= uSLS << 4;
    }

    MTP3_HEADER     = uAnsiFlag ? MTP3_HEADER_ANSI : MTP3_HEADER_ITU;
    MTP3_HAEDER_LEN = uAnsiFlag ? MTP3_HAEDER_ANSI_LEN : MTP3_HAEDER_ITU_LEN;

    memcpy ( pIPBuff, MTP3_HEADER, MTP3_HAEDER_LEN );
    return MTP3_HAEDER_LEN;
}

extern void NcMtp3Send ( u8 * pMtp3Msg, u32 wMtp3Len, u8 uAnsiFlag )
{
    u8      IPBuff[512];
    u32     wPackLen = 0;
    u32     wM2uaLen = 0;
    u32     ad = 0;
    u32     MTP3_HAEDER_LEN;
    u8      uPad = 0;
    struct sockaddr_in sin_addr;

    memset ( IPBuff, 0, 512 * sizeof ( u8 ) );
    wPackLen += NcStuffSCTP ( IPBuff + wPackLen );
    MTP3_HAEDER_LEN = uAnsiFlag ? MTP3_HAEDER_ANSI_LEN : MTP3_HAEDER_ITU_LEN;
    wM2uaLen = NcStuffM2UA ( IPBuff + wPackLen, wMtp3Len );
    wPackLen += wM2uaLen;
    memcpy ( IPBuff + wPackLen, pMtp3Msg, wMtp3Len );
    wPackLen += wMtp3Len;

    uPad = ( IPBuff[SCTP_HEADER_LEN + 7] ) % 4;
    uPad = uPad ? ( 4 - uPad ) : 0;

    for ( ; uPad > 0; uPad-- )
    {
        IPBuff[SCTP_HEADER_LEN + 7]++;  //m2ua msg len
        IPBuff[wPackLen++] = 0x00;
    }

    IPBuff[SCTP_CHUNKLEN_POS] = 0x10 + IPBuff[SCTP_HEADER_LEN + 7];

    ad = NcCS ( IPBuff, wPackLen );
    IPBuff[SCCTP_CS_POS] = ad & 0xFF;
    IPBuff[SCCTP_CS_POS + 1] = ad / ( 256 );
    IPBuff[SCCTP_CS_POS + 2] = ad / ( 256 * 256 );
    IPBuff[SCCTP_CS_POS + 3] = ad / ( 256 * 256 * 256 );

    sin_addr.sin_family = AF_INET;
    sin_addr.sin_port = htons ( tNC.nPort );
    sin_addr.sin_addr.s_addr = tNC.nDstIp;

    sendto ( tNC.nSock, ( char * ) IPBuff, wPackLen, 0,
             ( struct sockaddr * ) &sin_addr, sizeof ( struct sockaddr ) );
    //printf ( "\nMTP3 Send Net cap message!\n " );
}

typedef struct WXC_NETCAP_SCCP_INTRA_MESSAGE
{
    u8      msgsource;
    u8      msgtype;
    u8      msgclass;
    u8      msgMNP;
    int     OPC;
    int     DPC;
    u8      SLS;
}
Wxc_NCSccpIntra;

extern int NcSccpSend ( u8 * pSccpMsg, u32 wSccpLen, u8 * pIntraMsg,
                        BYTE uAnsiFlag )
{
    u8      IPBuff[512];
    u32     wPackLen = 0;
    u32     wM2uaLen = 0;
    u32     ad = 0;
    u8      uPad = 0;
    u32     MTP3_HAEDER_LEN;
    struct sockaddr_in sin_addr;
    Wxc_NCSccpIntra *pIntra = ( Wxc_NCSccpIntra * ) pIntraMsg;

    memset ( IPBuff, 0, 512 * sizeof ( u8 ) );
    wPackLen += NcStuffSCTP ( IPBuff + wPackLen );
    MTP3_HAEDER_LEN = uAnsiFlag ? MTP3_HAEDER_ANSI_LEN : MTP3_HAEDER_ITU_LEN;
    wM2uaLen = NcStuffM2UA ( IPBuff + wPackLen, wSccpLen + MTP3_HAEDER_LEN );
    wPackLen += wM2uaLen;

    wPackLen +=
        NcStuffMtp3 ( IPBuff + wPackLen, pIntra->DPC, pIntra->DPC, pIntra->SLS,
                      uAnsiFlag );
    memcpy ( IPBuff + wPackLen, pSccpMsg, wSccpLen );
    wPackLen += wSccpLen;

    uPad = ( IPBuff[SCTP_HEADER_LEN + 7] ) % 4;
    uPad = uPad ? ( 4 - uPad ) : 0;

    for ( ; uPad > 0; uPad-- )
    {
        IPBuff[SCTP_HEADER_LEN + 7]++;  //m2ua msg len
        IPBuff[wPackLen++] = 0x00;
    }

    IPBuff[SCTP_CHUNKLEN_POS] = 0x10 + IPBuff[SCTP_HEADER_LEN + 7];

    ad = NcCS ( IPBuff, wPackLen );

    IPBuff[SCCTP_CS_POS] = ad & 0xFF;
    IPBuff[SCCTP_CS_POS + 1] = ad / ( 256 );
    IPBuff[SCCTP_CS_POS + 2] = ad / ( 256 * 256 );
    IPBuff[SCCTP_CS_POS + 3] = ad / ( 256 * 256 * 256 );

    sin_addr.sin_family = AF_INET;
    sin_addr.sin_port = htons ( tNC.nPort );
    sin_addr.sin_addr.s_addr = tNC.nDstIp;

    if ( sendto
         ( tNC.nSock, ( char * ) IPBuff, wPackLen, 0,
           ( struct sockaddr * ) &sin_addr, sizeof ( struct sockaddr ) ) < 0 )
    {
        perror ( "SCCP send tNC error!\n" );
        return 0;
    }

    //printf ( "\nSCCP Send Net cap message!\n " );
    //write(nSock, IPBuff, wPackLen);
    return 1;
}

/*
   #define SCCP_RLSE_LEN 0x0a

   static u8 SCCP_RLSD[ ] = 
   {
   0x04, 0x01, 0x04, 0x00, 0x00, 0x00, 0x74, 0x00, 0x01, 
   0x00
   };

   void main()
   {
   int i;

   NcInit();
   NcStart(inet_addr("10.167.144.35") , 8000 );
   for( i = 0 ; i < 5000 ; i ++ )
   {
   sleep(1);
   NcSccpSend( SCCP_RLSD , SCCP_RLSE_LEN );
   }
   NcStop();
   return ;
   }
 */
/*@end@*/
