//////////////////////////////////////////////////
//Title    : pub_inet_test.c
//Auhtor   : Liu Wei
//Desc     : public ipnet function test
//Created  : 2007-06-22
//Revision :
//
//Revision :
//
//////////////////////////////////////////////////

#include <stdio.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netdb.h>
#include <setjmp.h>
#include <errno.h>
#include <string.h>

typedef unsigned char   u8;
typedef unsigned int  u32;
typedef unsigned short u16;
typedef unsigned long long  u64;


#define MAXINTERFACES 16

typedef struct EIF
{
    	u32 dIfIP;
        u8  uIfState;
}
EIF;

typedef struct IF_ADDR
{
	EIF tIfEntity[MAXINTERFACES];
    u8  uIfNum;  
}
IfAddr;

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;
}


#define PACKET_SIZE     4096
#define MAX_NO_PACKETS  3

struct sockaddr_in dest_addr;
struct sockaddr_in from;
struct timeval tvrecv;

typedef void PingCallBack( int nPingResult );

typedef struct ICMP_SERVICE
{
	u8    uSrvState;
	u8    uPingResult;
	u8    uIcmpState;
	u8    uPackNo;
	u8    uDataLen; 
	u8    nPackNumSend;
	u8    nPackNumRecv;
	u8    aSendBuff[PACKET_SIZE];
    u8    aRecvBuff[PACKET_SIZE];
    u32   wSockfd;
    pid_t tPid;
    struct protoent *pProtoent;
    PingCallBack *fCallBack;
}
IcmpSrv;

IcmpSrv tIcmpSrvEntity;

//校验和算法  
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报头  
int IcmpPacking ( IcmpSrv *pIS )
{
    int i, 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报头  
int IcmpUnPack ( u8 *buf, int len , IcmpSrv *pIS )
{
    int i, 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 ( from.sin_addr )
                 , icmp->icmp_seq, pIP->ip_ttl, rtt 
               );
        return 1;
    }
    return 0;
}

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报文  
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 * ) &dest_addr, sizeof ( dest_addr ) ) < 0 )
        {
            perror ( "sendto error" );
            return 0;
        }
        printf("[IcmpSend] : Send icmp Pack %d \n" , pIS->nPackNumSend);
        return 1;        
    }
    return 0;
}

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


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

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

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 (  );
    
}


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;
	    }
	}
}

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 ( &dest_addr, sizeof ( dest_addr ) );
    dest_addr.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 * ) &dest_addr.sin_addr, pHost->h_addr, pHost->h_length );
    }
    else                        //是ip地址          
    {
        dest_addr.sin_addr.s_addr = nInetAddr;
        //memcpy( (char *)&dest_addr,(char *)&nInetAddr,host->h_length);
    }
    printf ( "PING %s(%s): %d bytes data in ICMP packets. \n", sIP
             , inet_ntoa ( dest_addr.sin_addr ) , pIS->uDataLen );
	
	pIS->uSrvState  = 1 ;
	pIS->uIcmpState = 1;
	
}

//打印ip报文的信息
void printIP ( char *packet, int len )
{
    struct sockaddr_in addr_src, addr_dst;
    struct ip *pip = ( struct ip * ) packet;

    printf ( "ip head len:%d ", pip->ip_hl << 2 );
    printf ( "ip len:%d ", ntohs ( pip->ip_len ) );
    printf ( "ip pro id:%d ", pip->ip_p );
    printf ( "ip ttl:%d ", pip->ip_ttl );
    printf ( "ip offset:%d ", ntohs ( pip->ip_off ) & IP_OFFMASK );

    memset ( &addr_src, 0, sizeof ( struct sockaddr_in ) );
    memset ( &addr_dst, 0, sizeof ( struct sockaddr_in ) );
    memcpy ( &addr_src, &pip->ip_src, sizeof ( struct sockaddr_in ) );
    memcpy ( &addr_dst, &pip->ip_dst, sizeof ( struct sockaddr_in ) );
    printf ( "src ip:%x ", addr_src.sin_addr );
    printf ( "dst ip:%x ", addr_dst.sin_addr );
}

void NetCapPing( int nRet )
{
	if( nRet )
	{
		printf("Host is rearchable\n");
	}
	else
	{
		printf("Host is not rearchable\n");
	}
}

static char *pStrEqTok = NULL;
static u8   uStrEqState = 0;

#define CNULL                 '\0'
#define STR_CHECK_END(str,ch) ( str[strlen(str)-1] == ch )
#define STR_CUT_LAST(str)     ( str[strlen(str)-1] = CNULL )

void StrTrimMoreSpace(char *pStr )
{
	 char *pSrc , *pDst , *pCh;
	 	
	 pDst = pStr;
	 pCh  = pSrc = strdup( pStr );
	 	
	 for( ; *pSrc ; pSrc++ )
	 {
	  	if( !( isspace(*pSrc) && isspace( *(pSrc+1) ) ) )
	   	{
	   		*pDst++ = *pSrc; 
	   	}
	 }   
	 *pDst = CNULL;
}

char *StrEqTok( char *pStr )
{
	 char *p, *pCh;
	 char *pDel = " ";
	 
	 pCh = NULL;
	
	 if( pStr == NULL )
	 {
	 	 pStr = pStrEqTok;
	 }
	 else
	 {
	 	pStr += strspn (pStr, pDel);
        if (*pStr == '\0')
        {
           pStrEqTok = NULL;
           return NULL;
        }
        StrTrimMoreSpace( pStr );
	 }
	  
	 if( pStr == NULL )
	 {
	 	 return NULL;
	 }
	 
	 uStrEqState = uStrEqState > 2 ? 0 : uStrEqState;
	 
     pCh = strpbrk (pStr, pDel );
	 if( pCh == NULL )
	 {
	 	pStrEqTok = NULL;
	 	pCh = pStr;
	 }
	 else
	 {
	 	pStrEqTok = pCh + 1;
	    *pCh = CNULL;
	    pCh = pStr;
	 }
	 
	 p  = strchr( pCh , '=');
	 if( p == NULL )
	 {
	 	 if( pStrEqTok == NULL )
	 	 {
	 	 	goto STREQTOK_CLEAN_UP;
	 	 }
	 	 
	 	 if( uStrEqState == 0 || uStrEqState == 2 )
	 	 {   //expres left and right  
	 	 	 uStrEqState++;
	 	 	 return pCh;
	 	 }
	 	 else
	 	 {   //Miss '='
	 	 	 goto STREQTOK_CLEAN_UP;
	 	 }
	 }
	 else
	 {
	 	 if( uStrEqState == 0 && STR_CHECK_END( pCh , '=') )
	 	 {   //expres left and '='
	 	 	 uStrEqState+=2;
	 	 	 STR_CUT_LAST(pCh);
	 	 	 return pCh;
	 	 }
	 	 else if( uStrEqState == 1 && pCh[0] == '=')
	 	 {   //'=' meet
	 	 	 if( pCh[1] == CNULL )
	 	 	 {
	 	 	 	 if( pStrEqTok == NULL )
	 	         {
	 	 	         goto STREQTOK_CLEAN_UP;
	 	         }
	 	         pStr = pStrEqTok;
	 	 	 	 pCh = strpbrk (pStr, pDel);
	 	 	 	 if( pCh == NULL )
	             {
	 	             pStrEqTok = NULL;
	 	             pCh = pStr;
	             }
	             else
	             {
	 	             pStrEqTok = pCh + 1;
	                 *pCh = CNULL;
	                 pCh = pStr;
	             }
	             uStrEqState = 3;
	             return pCh;
            
	 	 	 }   
	 	 	 else
	 	 	 { //expres '=' and right
	 	 	   uStrEqState = 3;    //all meet
	 	 	   return ++pCh;
	 	 	 }
	 	 }
	 	 else if( uStrEqState == 0 ) 
	 	 {  //expres left and '=' and right 
	 	 	if( pStrEqTok != NULL )
	 	 	{
	 	 		*(pStrEqTok -1) = ' ';
	 	 	}
	 	 	pStrEqTok = p + 1;
	 	 	*p = CNULL;
	 	 	uStrEqState = 2;
	 	 	return pCh;
	 	 }
	 	 else
	 	 {
	 	 	goto STREQTOK_CLEAN_UP;
	 	 }
	 }
STREQTOK_CLEAN_UP:
	 pStrEqTok = NULL;
	 uStrEqState = 0;
	 return NULL;
}


int main (  )
{
    int i, ret = 0;
    IfAddr tIfAddr;
    IcmpSrv tIcmpSrv;
    char    tmpStr[80];

    ret = GetNetIfInfo ( &tIfAddr );
    
    printf ( "Interface num :%d \n\n", ret );
    for ( i = 0; i < ret; i++ )
    {
        printf ( "If %d : state : %d ip : %s \n\n"
                  , i, tIfAddr.tIfEntity[i].uIfState 
                  , inet_ntoa ( *((struct in_addr* )&tIfAddr.tIfEntity[i].dIfIP )) 
               );
    }
    
    #define STR(s)  #s
    printf(STR(if(NOTSO(a)) return 0\n)); 
    	
    #define FPOS( type, field ) /*lint -e545 */ ( (u32) &(( type *) 0)-> field ) /*lint +e545 */ 


    #define FSIZ( type, field )  sizeof( ((type *) 0)->field ) 
    	
    printf("Pos  : %ld \n" , FPOS(IfAddr ,uIfNum ) );
    printf("Size : %ld \n" , FSIZ(IfAddr ,uIfNum ) );
    printf("Pos  : %ld \n" , FPOS(IfAddr ,tIfEntity ) );
    printf("Size : %ld \n" , FSIZ(IfAddr ,tIfEntity ) );
    printf("Pos  : %ld \n" , FPOS(EIF ,dIfIP ) );
    printf("Size : %ld \n" , FSIZ(EIF ,dIfIP ) );
    printf("Pos  : %ld \n" , FPOS(EIF ,uIfState ) );
    printf("Size : %ld \n" , FSIZ(EIF ,uIfState ) );
    printf("Pos  : %ld \n" , FPOS(IcmpSrv ,nPackNumSend ) );
    printf("Size : %ld \n" , FSIZ(IcmpSrv ,nPackNumSend ) );
    printf("Pos  : %ld \n" , FPOS(IcmpSrv ,aSendBuff ) );
    printf("Size : %ld \n" , FSIZ(IcmpSrv ,aSendBuff ) );
    
    printf("Please input ip and port : ");
    
    fgets( tmpStr, 80 ,stdin  );
    
    printf("%s\n", tmpStr);
    if( STR_CHECK_END(tmpStr , '\n' ) )
    {
    	STR_CUT_LAST(tmpStr );
    }
    //StrTrimMoreSpace(tmpStr);
    //printf("trim space : \n%s\n" ,tmpStr  );
    
    { 
    	char *p = NULL; 
    	u8   uFlag = 0;
    	
    	p = StrEqTok(tmpStr);
    	printf( "Name: %-20s " , p);
    	for( ; p = StrEqTok(NULL);  )
        {
        	 uFlag = uFlag ? 0 : 1;
        	 if( uFlag )
        	 {
        	 	printf( "Valu: %-20s \n" , p);
        	 }
        	 else
        	 {
        	 	printf( "Name: %-20s " , p);
        	 }
    	}
    }
    printf("\n\n");
    PingInit( &tIcmpSrv , 56 );
    
    ret = PingStart( &tIcmpSrv , "172.18.99.1" , NetCapPing );
    if( !ret )
    {
    	printf("ping is processing\n");
    }
    
    while(1)
    {	
    	PingTimer( &tIcmpSrv );
    	sleep ( 1 );
        //每隔一秒发送一个ICMP报文 
    }
    return 1;
}
