
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/time.h>
#include<netinet/in_systm.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/ip_icmp.h>
#include<arpa/inet.h>

#include<sys/stat.h> 
#include<sys/un.h>

#include"sitar.h"
#include"sitar_scan.h"
#include"sitar_com.h"
#include<almemsys/almemsys.h>
#include"jpreturn.h"

extern global_data_t gd;


/* ------------ TVSUB --------------- */
int tvsub(struct timeval *out, struct timeval *in) 
{
if((out->tv_usec -= in->tv_usec) < 0){
   out->tv_sec--;
   out->tv_usec += 1000000;
   }
out->tv_sec -= in->tv_sec;
return 0;
}


/* ------------- CHECKSUM --------------- */
u_short checksum(u_short *data, int len)
{
// static long sum;
long sum;

sum = 0;
for(; len > 1; len -=2){
   sum += *data++;
   if(sum & 0x80000000){
      sum = (sum & 0xffff) + (sum >> 16);
      }
   }

if(len == 1){
   u_short i = 0; 
   *(u_char*)(&i) = *(u_char *)data;
   sum += i;
   }

while(sum >> 16){
   sum = (sum & 0xffff) + (sum >> 16);
   }

return ~sum;
}



/*--------------- MAKE ICMP8 PACKET --------------*/
int make_icmp8_packet(struct icmp *icmp, int len, int n)
{
memset((char *) icmp, 0, len);
gettimeofday((struct timeval *) (icmp->icmp_data), (struct timezone *) 0);

icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_id = 0;
icmp->icmp_seq = n;
icmp->icmp_cksum =0;
icmp->icmp_cksum = checksum((u_short *) icmp, len);

return 0;
}



/*----------------- SCAN ICMP --------------------*/
char * scan_icmp(char * url_data, char * backdata, int timeout, int * stat, int coe)
{
int resolv_ret;

int send_sk, hlen, i;
float rtt;
unsigned long resoip;
char send_buff[1024];
char recv_buff[1024];
char ipa2[IP_LEN];
char ipx[BUF_URL];
char * ip_addr = ipx;
char pas[BUF_URL];
char * url_pass = pas;
char * ip_addr2 = ipa2;
char ntp[IP_LEN];
char * ntopbuff = ntp;

struct sockaddr_in send_addr;
struct timeval tv;
struct ip *ip;
fd_set select_fd;

// http:// とかつけちゃった場合の救済
memset(ip_addr, 0x00, BUF_URL);
memset(url_pass, 0x00, BUF_URL);

urldompass_port(url_data, ip_addr, url_pass, strlen(url_data));


// 初期化
rtt = 0.0;
i = 0;
resolv_ret = 0;

memset((char *)&send_addr, 0, sizeof(struct sockaddr_in));
memset(ipa2, 0x00, IP_LEN);
memset(send_buff, 0x00, 1024);
memset(recv_buff, 0x00, 1024);

send_addr.sin_family = AF_INET;

// 名前解決
resoip = 0;
// backdata = resolvename_th(backdata, ip_addr, timeout, &resoip);
// backdata = resolvename_sk(backdata, ip_addr, timeout, &resoip, &resolv_ret);

if(0 == self_memcmp(gd.di.dns_name, "RESOLV")){
   backdata = resolvename_th(backdata, ip_addr, timeout, &resoip);
   }
else{
   backdata = resolvename_sk(backdata, ip_addr, timeout, &resoip, gd.di.dns_name, &resolv_ret, '4');
   if(resolv_ret < 0){
      backdata = resolvename_sk(backdata, ip_addr, DEFAULT_TIMEOUT, &resoip, gd.di.dns_name2, &resolv_ret, '4');
      fprintf(stderr,"RESOLV ERR: DNS name resolution failure <%s> %s\n", ip_addr, backdata);
      }
   }

send_addr.sin_addr.s_addr = resoip;

if(0 == (int)send_addr.sin_addr.s_addr || resoip == (int)NULL){
// backdata = safe_sprintf(backdata,BUF_MAX, "%s %s %d\n", SOCK_2, ip_addr, (int)send_addr.sin_addr.s_addr);
   backdata = safe_sprintf(backdata,BUF_MAX, "%s %s\n", SOCK_2, ip_addr);
   #ifdef ICMP_BUG
   printf("ICMP: scan_icmp(0): START %s\n", backdata);
   #endif
   * stat = -1;
   return backdata;
   }

// ip_addr2 = inet_ntoa(send_addr.sin_addr);
ip_addr2 = (char *)inet_ntop(AF_INET, &send_addr.sin_addr, ntopbuff, IP_LEN);

#ifdef ICMP_BUG
printf("ICMP: scan_icmp(1): ICMP host<%s> ip<%s>\n", ip_addr, ip_addr2);
#endif

// RAW socketの生成
if((send_sk = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0){
   backdata=safe_sprintf(backdata, BUF_MAX,
    "HOST=%s IP=%s %s", ip_addr, ip_addr2, ICMP_1);
   #ifdef ICMP_BUG
   printf("ICMP: scan_icmp(2): SOCKET CREATE %s\n", backdata);
   #endif
   * stat = -10;
   return backdata;
   }

// ICMPパケットの生成と送信
make_icmp8_packet((struct icmp *)(long int)send_buff, 72, i);
if(sendto(send_sk, (char *)&send_buff, 72, 0, (struct sockaddr *)(long int)&send_addr, sizeof(send_addr)) < 0){
   backdata=safe_sprintf(backdata, BUF_MAX,"HOST=%s IP=%s %s", ip_addr, ip_addr2, ICMP_2);
   #ifdef ICMP_BUG
   printf("ICMP: scan_icmp(3): SEND RAW SOCKET %s\n", backdata);
   #endif
   close(send_sk);
   * stat = -10;
   return backdata;
   }

// select()の待ち時間をimeout * 1秒に設定
tv.tv_sec = timeout;
tv.tv_usec = 0;

while(1){
   FD_ZERO(&select_fd);
   FD_SET(send_sk, &select_fd);

   // select()でtime out をまつ
   if(select(send_sk + 1, &select_fd, NULL, NULL, &tv) <= 0){
      break;
      }

   // FDに変化があった場合はrecvする
   if(recvfrom(send_sk, recv_buff, 1024, 0, NULL, NULL) < 0){
      backdata=safe_sprintf(backdata, BUF_MAX,"HOST=%s IP=%s %s", ip_addr, ip_addr2, ICMP_3);
      #ifdef ICMP_BUG
      printf("ICMP: scan_icmp(4): RECV RAW SOCKET %s\n", backdata);
      #endif
      close(send_sk);
      * stat = -10;
      return backdata;
      } 

   // 戻ったパケットを判断
   ip = (struct ip *)(long int)recv_buff;
   hlen = ip->ip_hl << 2;

   if(ip->ip_src.s_addr == send_addr.sin_addr.s_addr){
      struct icmp *icmp;
      icmp = (struct icmp *)(long int)(recv_buff + hlen);

      // 戻ったパケットのRTTを判断  RTTの時間は閾値が1の場合に100msec
      if(icmp->icmp_type == ICMP_ECHOREPLY){
         gettimeofday(&tv, (struct timezone *)0);
         tvsub(&tv, (struct timeval *)(icmp->icmp_data));
         rtt = tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;

         if(rtt > (timeout * coe)){
            backdata=safe_sprintf(backdata,BUF_MAX,"%s :RTT=%7.4fms TIMEOUT=%dms %s",
//          inet_ntoa(*(struct in_addr *)(long int)&(ip->ip_src.s_addr)),
            (char *)inet_ntop(AF_INET, (struct in_addr *)(long int)&(ip->ip_src.s_addr), ntopbuff, IP_LEN),
            rtt, (timeout * coe), ICMP_5);
            #ifdef ICMP_BUG
            printf("ICMP: scan_icmp(5): RECV RAW DATA NG! %s\n",  backdata);
            #endif
            close(send_sk);
            * stat = -1;
            return backdata;
            }

         else if(rtt <= (timeout * coe)){
            backdata=safe_sprintf(backdata,BUF_MAX,"%s :RTT=%7.4fms TIMEOUT=%dms %s",
//          inet_ntoa(*(struct in_addr *)(long int)&(ip->ip_src.s_addr))
            (char *)inet_ntop(AF_INET, (struct in_addr *)(long int)&(ip->ip_src.s_addr), ntopbuff, IP_LEN),
            rtt, (timeout * coe), ICMP_6);
            #ifdef ICMP_BUG
            printf("ICMP: scan_icmp(6): RECV RAW DATA OK! %s\n", backdata);
            #endif
            close(send_sk);
            * stat = 0;
            return backdata;
            }
         else{
            backdata=safe_sprintf(backdata,BUF_MAX,"%s :RTT=%7.4fms TIMEOUT=%dms %s",
//          inet_ntoa(*(struct in_addr *)(long int)&(ip->ip_src.s_addr)),
            (char *)inet_ntop(AF_INET, (struct in_addr *)(long int)&(ip->ip_src.s_addr), ntopbuff, IP_LEN),
            rtt, (timeout * coe), ICMP_5);
            #ifdef ICMP_BUG
            printf("ICMP: scan_icmp(5): RTT err! %s\n",  backdata);
            #endif
            close(send_sk);
            * stat = -1;
            return backdata;
            }

         }

      // 戻ったパケットがエコーリプライの場合、正常
      else if(icmp->icmp_type == ICMP_ECHO){
         #ifdef ICMP_BUG
         printf("ICMP: scan_icmp(7): RECV RAW type<%d> sendip<%d> inip<%d>\n",
           icmp->icmp_type, send_addr.sin_addr.s_addr, ip->ip_src.s_addr);
         #endif
         }

      // 到達出来ないと帰った場合
      else if(icmp->icmp_type == ICMP_UNREACH){
         backdata=safe_sprintf(backdata,BUF_MAX,"%s :RTT=%7.4fms %s",
//          inet_ntoa(*(struct in_addr *)(long int)&(ip->ip_src.s_addr)),
            (char *)inet_ntop(AF_INET, (struct in_addr *)(long int)&(ip->ip_src.s_addr), ntopbuff, IP_LEN),
            tv.tv_sec*1000.0 + tv.tv_usec/1000.0, ICMP_7);
         #ifdef ICMP_BUG
         printf("ICMP: scan_icmp(8): RECV RAW %s type<%d>\n", backdata, icmp->icmp_type);
         #endif
         close(send_sk);
         * stat = -1;
         return backdata;
         }
      else{
         backdata=safe_sprintf(backdata,BUF_MAX,"%s :RTT=%7.4fms %s",
//          inet_ntoa(*(struct in_addr *)(long int)&(ip->ip_src.s_addr)),
            (char *)inet_ntop(AF_INET, (struct in_addr *)(long int)&(ip->ip_src.s_addr), ntopbuff, IP_LEN),
            tv.tv_sec*1000.0 + tv.tv_usec/1000.0, ICMP_4);
         #ifdef ICMP_BUG
         printf("ICMP: scan_icmp(10): RECV RAW %s type<%d>\n", backdata, icmp->icmp_type);
         #endif
         close(send_sk);
         * stat = -1;
         return backdata;
         }

      }
   else{
      #ifdef ICMP_BUG
      printf("ICMP: scan_icmp(11): RECV RAW DATA sendip<%d> inip<%d>\n",
        send_addr.sin_addr.s_addr, ip->ip_src.s_addr);
      #endif
      }
   }  // while(loop)


rtt = tv.tv_sec*1000.0 + tv.tv_usec/1000.0;
backdata=safe_sprintf(backdata,BUF_MAX,
 "HOST=%s IP=%s :RTT=%7.4fms %s", ip_addr, ip_addr2, rtt, ICMP_13);
#ifdef ICMP_BUG
printf("ICMP: scan_icmp(12): LAST %s\n", backdata);
#endif

close(send_sk);
* stat = -1;
return backdata;
}

