//////////////////////////////////////////////////
//Title    : pub_inet.c
//Auhtor   : Liu Wei
//Desc     : public inet function implementation
//Created  : 2007-06-22
//Revision :
//
//Revision :
//
//////////////////////////////////////////////////

#include "./include/pub_inet.h"
/*@ignore@*/
///////////////////////////////////////////////////////////////////////////
//   Inet If Address infomation interface                                                                 
///////////////////////////////////////////////////////////////////////////
extern int GetNetIfInfo ( IfAddr * pIfAddr )
{
    int     fd;
    struct ifreq buf[MAXINTERFACES];
    //struct arpreq arp;
    struct ifconf ifc;

    pIfAddr->uIfNum = 0;

    if ( ( fd = socket ( AF_INET, SOCK_DGRAM, 0 ) ) >= 0 )
    {
        ifc.ifc_len = sizeof buf;
        ifc.ifc_buf = ( caddr_t ) buf;
        if ( !ioctl ( fd, SIOCGIFCONF, ( char * ) &ifc ) )
        {
            int     i;

            pIfAddr->uIfNum = ifc.ifc_len / sizeof ( struct ifreq );
            pIfAddr->uIfNum = pIfAddr->uIfNum % MAXINTERFACES;
            for ( i = 0; i < pIfAddr->uIfNum; i++ )
            {
                ioctl ( fd, SIOCGIFFLAGS, ( char * ) &buf[i] );
                pIfAddr->tIfEntity[i].uIfState = buf[i].ifr_flags & IFF_UP;
                if ( !( ioctl ( fd, SIOCGIFADDR, ( char * ) &buf[i] ) ) )
                {
                    pIfAddr->tIfEntity[i].dIfIP = ( ( struct sockaddr_in * )
                                                    ( &buf[i].ifr_addr ) )->
                        sin_addr.s_addr;
                }
            }
        }

    }
    close ( fd );
    return pIfAddr->uIfNum;
}

static u32 wxc2_bind_ip=0xFFFFFFFF, wxc2_shm_shift;

void ReadMCMSParam()
{
	char cnf_file[]="./conf/mcms_conf.txt";
	char info_str[256], *buf_ptr;;
	FILE	*fp=NULL;
	
	fp = fopen(cnf_file,"r");

	wxc2_bind_ip = 0;
	wxc2_shm_shift = 0;

	if(fp == NULL)
		return;

	while (!feof(fp))
	{
		info_str[0] = '\n';
		fgets(info_str,250,fp);
		if (info_str[0] == '#' || info_str[0] == '\n')
			continue;
		buf_ptr = info_str;
		
		if(!strncasecmp(info_str,"bind ip=",8))
		{

			wxc2_bind_ip = inet_addr(&info_str[8]);
			continue;
		}
		if(!strncasecmp(info_str,"shm key shift=",14))
		{
			wxc2_shm_shift = atoi(&info_str[14]);
			continue;
		}
	}
	
	fclose(fp);
	return;
}

u32 GetLocalIP( )
{
    struct hostent *host;
    struct in_addr *hostip_addr;
    char    name[100];

	if(wxc2_bind_ip == 0xFFFFFFFF)
		ReadMCMSParam();
		
	if(wxc2_bind_ip == 0)
	{
	    gethostname ( name, 64 );
	    host = gethostbyname ( name );

	    hostip_addr = ( struct in_addr * ) ( *host->h_addr_list );
	    return hostip_addr->s_addr;
	}
	return wxc2_bind_ip;
}

u32 GetLocalIP1( )
{
        if(wxc2_bind_ip == 0xFFFFFFFF)
                ReadMCMSParam();

        if(wxc2_bind_ip == 0)
        {
            return INADDR_ANY;

        }
        return wxc2_bind_ip;
}

u32 wxc2_get_shm_offset()
{
        if(wxc2_bind_ip == 0xFFFFFFFF)
                ReadMCMSParam();
	return wxc2_shm_shift;
}
///////////////////////////////////////////////////////////////////////////
//   Inet If Address infomation interface                                                                 
///////////////////////////////////////////////////////////////////////////

//校验和算法  
static u16 IcmpCheckSum ( u16 * addr, int len )
{
    int     nleft = len;
    int     sum = 0;
    u16    *w = addr;
    u16     answer = 0;

    //把ICMP报头二进制数据以2字节为单位累加起来  
    while ( nleft > 1 )
    {
        sum += *w++;
        nleft -= 2;
    }

    //若ICMP报头为奇数个字节，会剩下最后一字节。
    //把最后一个字节视为一个2字节数据的高字节，这个2字节数据的低字节为0，继续累加  
    if ( nleft == 1 )
    {
        *( unsigned char * ) ( &answer ) = *( unsigned char * ) w;
        sum += answer;
    }

    sum = ( sum >> 16 ) + ( sum & 0xffff );
    sum += ( sum >> 16 );
    answer = ~sum;

    return answer;
}

//设置ICMP报头  
static int IcmpPacking ( IcmpSrv * pIS )
{
    int     packsize;
    struct icmp *icmp;
    struct timeval *tval;

    icmp = ( struct icmp * ) pIS->aSendBuff;
    icmp->icmp_type = ICMP_ECHO;
    icmp->icmp_code = 0;
    icmp->icmp_cksum = 0;
    icmp->icmp_seq = pIS->nPackNumSend;
    icmp->icmp_id = pIS->tPid;
    packsize = 8 + pIS->uDataLen;

    //记录发送时间
    tval = ( struct timeval * ) icmp->icmp_data;
    gettimeofday ( tval, NULL );

    icmp->icmp_cksum = IcmpCheckSum ( ( u16 * ) icmp, packsize );

    return packsize;
}

//剥去ICMP报头  
static int IcmpUnPack ( u8 * buf, int len, IcmpSrv * pIS )
{
    int     nIpHdrLen;
    struct ip *pIP;
    struct icmp *icmp;
    struct timeval *tvSend;
    struct timeval tvRecv;
    double  rtt;

    gettimeofday ( &tvRecv, NULL );
    pIP = ( struct ip * ) buf;
    //求ip报头长度,即ip报头的长度标志乘4 
    nIpHdrLen = pIP->ip_hl << 2;

    //越过ip报头,指向ICMP报头 
    icmp = ( struct icmp * ) ( buf + nIpHdrLen );

    //ICMP报头及ICMP数据报的总长度  
    len -= nIpHdrLen;

    if ( len < 8 )
        //小于ICMP报头长度则不合理  
    {
        printf ( "ICMP packets's length is less than 8 " );
        return 0;
    }

    //确保所接收的是我所发的的ICMP的回应  
    if ( ( icmp->icmp_type == ICMP_ECHOREPLY )
         && ( icmp->icmp_id == pIS->tPid ) )
    {
        tvSend = ( struct timeval * ) icmp->icmp_data;

        rtt = ( tvRecv.tv_sec * 1000 + tvRecv.tv_usec / 1000 ) -
            ( tvSend->tv_sec * 1000 + tvSend->tv_usec / 1000 );

        printf ( "%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms \n", len,
                 inet_ntoa ( pIS->tFromAddr.sin_addr ), icmp->icmp_seq,
                 pIP->ip_ttl, rtt );
        return 1;
    }
    return 0;
}

static void IcmpStatis ( IcmpSrv * pIS )
{
    int     nSend, nRecv;

    nSend = pIS->nPackNumSend;
    nRecv = pIS->nPackNumRecv;

    printf ( " --------------------PING statistics------------------- \n" );
    printf ( "%d packets transmitted, %d received , %%%d lost \n\n", nSend,
             nRecv, ( nSend - nRecv ) / nSend * 100 );
    close ( pIS->wSockfd );
    pIS->uSrvState = 0;
}

//发送三个ICMP报文  
static int IcmpSend ( IcmpSrv * pIS )
{
    int     nSize;

    if ( pIS->nPackNumSend < MAX_NO_PACKETS )
    {
        pIS->nPackNumSend++;
        nSize = IcmpPacking ( pIS );

        //设置ICMP报头  
        if ( sendto ( pIS->wSockfd, pIS->aSendBuff, nSize, 0,
                      ( struct sockaddr * ) &pIS->tToAddr,
                      sizeof ( pIS->tToAddr ) ) < 0 )
        {
            perror ( "sendto error" );
            return 0;
        }
        printf ( "[IcmpSend] : Send icmp Pack %d \n", pIS->nPackNumSend );
        return 1;
    }
    return 0;
}

//接收所有ICMP报文  
static void IcmpRecv ( IcmpSrv * pIS )
{
    int     n;
    extern int errno;

    socklen_t fromlen = sizeof ( pIS->tFromAddr );
    while ( pIS->nPackNumRecv < pIS->nPackNumSend )
    {
        if ( ( n =
               recvfrom ( pIS->wSockfd, pIS->aRecvBuff,
                          sizeof ( pIS->aRecvBuff ), 0,
                          ( struct sockaddr * ) &pIS->tFromAddr,
                          &fromlen ) ) < 0 )

        {
            if ( errno == EINTR )
                continue;
            perror ( "recvfrom error" );
            continue;
        }
        if ( IcmpUnPack ( pIS->aRecvBuff, n, pIS ) == -1 )
            continue;
        pIS->nPackNumRecv++;
    }
}

extern int PingInit ( IcmpSrv * pIS, int nDataLen )
{
    memset ( pIS, 0, sizeof ( IcmpSrv ) );
    pIS->uSrvState = 0;

    pIS->uDataLen = 56;

    if ( ( pIS->pProtoent = getprotobyname ( "icmp" ) ) == NULL )
    {
        perror ( "getprotobyname" );
        exit ( 1 );
    }

    // 回收root权限,设置当前用户权限  
    setuid ( getuid (  ) );

    //获取main的进程id,用于设置ICMP的标志符  
    pIS->tPid = getpid (  );
    return 0;
}

extern int PingStart ( IcmpSrv * pIS, char *sIP, PingCallBack fCallBack )
{
    int     nSize;
    struct hostent *pHost;

    //struct in_addr *pInAddr;
    u32     nInetAddr;

    if ( pIS->uSrvState )
    {
        return 0;
    }

    pIS->fCallBack = fCallBack;

    //生成使用ICMP的原始套接字,这种套接字只有root才能生成  
    if ( ( pIS->wSockfd =
           socket ( AF_INET, SOCK_RAW, pIS->pProtoent->p_proto ) ) < 0 )
    {
        perror ( "socket error" );
        exit ( 1 );
    }

    //扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的
    //的可能性, 若无意中ping会引来大量应答
    setsockopt ( pIS->wSockfd, SOL_SOCKET, SO_RCVBUF, &nSize,
                 sizeof ( nSize ) );
    bzero ( &pIS->tToAddr, sizeof ( pIS->tToAddr ) );
    pIS->tToAddr.sin_family = AF_INET;

    //判断是主机名还是ip地址  
    nInetAddr = inet_addr ( sIP );
    if ( nInetAddr == INADDR_NONE )
    {
        if ( ( pHost = gethostbyname ( sIP ) ) == NULL )        //是主机名        
        {
            perror ( "gethostbyname error" );
            exit ( 1 );
        }
        memcpy ( ( char * ) &pIS->tToAddr.sin_addr, pHost->h_addr,
                 pHost->h_length );
    }
    else                        //是ip地址          
    {
        pIS->tToAddr.sin_addr.s_addr = nInetAddr;
        //memcpy( (char *)&pIS->tToAddr,(char *)&nInetAddr,host->h_length);
    }
    printf ( "PING %s(%s): %d bytes data in ICMP packets. \n", sIP,
             inet_ntoa ( pIS->tToAddr.sin_addr ), pIS->uDataLen );

    pIS->uSrvState = 1;
    pIS->uIcmpState = 1;
    return 0;
}

extern int PingTimer ( IcmpSrv * pIS )
{
    if ( pIS->uSrvState )
    {
        IcmpRecv ( pIS );
        switch ( pIS->uIcmpState )
        {
        case 1:
        case 2:
        case 3:
            IcmpSend ( pIS );
            pIS->uIcmpState++;
            break;
        default:
            if ( pIS->nPackNumRecv >= 3 )
            {
                IcmpStatis ( pIS );
                pIS->fCallBack ( 1 );
            }
            if ( pIS->uIcmpState++ > 200 )
            {
                IcmpStatis ( pIS );
                pIS->fCallBack ( 0 );
            }
            break;
        }
    }

    return 0;
}
/*@end@*/

