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

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

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

extern global_data_t gd;


#define JAN_1970   2208988800.0        /* 1970 - 1900 in seconds */

// maclo
#define write_net_2B(p,x) {\
    ((unsigned char *)(p))[0] = ((unsigned long)(x)>> 8)&0xFF; \
    ((unsigned char *)(p))[1] =  (unsigned long)(x)     &0xFF; \
}

#define write_net_4B(p,x) {\
    ((unsigned char *)(p))[0] = ((unsigned long)(x)>>24)&0xFF; \
    ((unsigned char *)(p))[1] = ((unsigned long)(x)>>16)&0xFF; \
    ((unsigned char *)(p))[2] = ((unsigned long)(x)>> 8)&0xFF; \
    ((unsigned char *)(p))[3] =  (unsigned long)(x)     &0xFF; \
}

#define write_net_8B(p,x) {\
    ((unsigned char *)(p))[0] = ((unsigned long long)(x)>>56)&0xFF; \
    ((unsigned char *)(p))[1] = ((unsigned long long)(x)>>48)&0xFF; \
    ((unsigned char *)(p))[2] = ((unsigned long long)(x)>>40)&0xFF; \
    ((unsigned char *)(p))[3] = ((unsigned long long)(x)>>32)&0xFF; \
    ((unsigned char *)(p))[4] = ((unsigned long long)(x)>>24)&0xFF; \
    ((unsigned char *)(p))[5] = ((unsigned long long)(x)>>16)&0xFF; \
    ((unsigned char *)(p))[6] = ((unsigned long long)(x)>> 8)&0xFF; \
    ((unsigned char *)(p))[7] =  (unsigned long long)(x)     &0xFF; \
}

#define read_net_2B(p) (\
    ((unsigned long)((unsigned char *)(p))[0]<< 8) | \
     (unsigned long)((unsigned char *)(p))[1] \
)

#define read_net_4B(p) (\
    ((unsigned long)((unsigned char *)(p))[0]<<24) | \
    ((unsigned long)((unsigned char *)(p))[1]<<16) | \
    ((unsigned long)((unsigned char *)(p))[2]<< 8) | \
     (unsigned long)((unsigned char *)(p))[3] \
)
#define read_net_8B(p) (\
    ((unsigned long long)((unsigned char *)(p))[0]<<56) | \
    ((unsigned long long)((unsigned char *)(p))[1]<<48) | \
    ((unsigned long long)((unsigned char *)(p))[2]<<40) | \
    ((unsigned long long)((unsigned char *)(p))[3]<<32) | \
    ((unsigned long long)((unsigned char *)(p))[4]<<24) | \
    ((unsigned long long)((unsigned char *)(p))[5]<<16) | \
    ((unsigned long long)((unsigned char *)(p))[6]<< 8) | \
     (unsigned long long)((unsigned char *)(p))[7] \
)




// SNTP struct
struct req_pkt {
        unsigned int rm_vn_mode;       
        unsigned short err_nitems;    
        unsigned short mbz_itemsize; 
        unsigned short stratum;   
        double tstamp;           
        double recvtstamp;     
        double sendtstamp;    
        double tranststamp;  
        unsigned int    keyid;    
};


// prototype
// char * scan_sntp_connect(int, char [], char *, int, int *);





/*----------------- SCAN SNTP CONNECT --------------------*/
char * scan_sntp_connect(int port, char ip_addr[], char * backdata, int timeout, int * ret)
{
int send_sk, con, retval, resolv_ret;
struct sockaddr_in server;
struct sockaddr_in client;
struct timeval tv;
struct req_pkt ntpdata;
struct req_pkt bk_ntpdata;
char mesg[64];
struct timeval tv_tts, tv_vts; 
double recv_time, send_time;
unsigned long resoip;


fd_set select_fd;
con = 0;
* ret = 0;
retval = 0;
resolv_ret = 0;
backdata[0] = (char)0x00;



// create socket
if((send_sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
   backdata = safe_sprintf(backdata, BUF_MAX,"IP=%s Port=%d dgram socket error!", ip_addr, port);
   * ret = -1;
   return(backdata);
   }

client.sin_family = AF_INET;
client.sin_addr.s_addr = htonl(INADDR_ANY);
client.sin_port = htons(0);

if(bind(send_sk, (void *)&client, sizeof(client)) < 0){
   backdata = safe_sprintf(backdata, BUF_MAX,"IP=%s Port=%d not bind ip or port.", ip_addr, port);
   * ret = -1;
   return(backdata);
   }

server.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);
      }
   }

server.sin_addr.s_addr = resoip;

if(0 == (int)server.sin_addr.s_addr || resoip == (int)NULL){
   backdata=safe_sprintf(backdata,BUF_MAX,"%s %s %d\n",
    SOCK_2, ip_addr, (int)server.sin_addr.s_addr);
   * ret = -1;
   return backdata;
   }
server.sin_port = htons(port);


// create sntp structer
memset((char *)&ntpdata, 0, 48);
memset((char *)&bk_ntpdata, 0, 48);

ntpdata.rm_vn_mode = htonl(0x0B000000);   
ntpdata.err_nitems = 256;               
ntpdata.mbz_itemsize = 256;           
ntpdata.stratum = 15;                  
ntpdata.tstamp = 0;                  
ntpdata.sendtstamp = 0;            
ntpdata.recvtstamp = 0;           
ntpdata.tranststamp = 0;         
ntpdata.keyid = 0;             


// send packet
gettimeofday(&tv_tts, NULL);
sendto(send_sk, (char *)&ntpdata, 48, 0, (void *)&server, sizeof(server));

tv.tv_sec = timeout;
tv.tv_usec = 0;

// recves packet
while(1){
   FD_ZERO(&select_fd);
   FD_SET(send_sk, &select_fd);
   retval = select(send_sk + 1, &select_fd, NULL, NULL, &tv);

   if(retval < 0){
      #ifdef SNTP_BUG
      printf("SNTP: scan-sntp(3): NG select <%d> ERRNO: %d %s\n",
        retval, errno, strerror(errno));
      #endif
      backdata = safe_sprintf(backdata, BUF_MAX,"正常に監視できません\n ネットワークの異常が考えられます\n");
      * ret = -1;
      close(send_sk);
      return(backdata);
      }
   if(retval == 0){
      #ifdef SNTP_BUG
      printf("SNTP: scan-sntp(3): NG select <%d> ERRNO: %d %s\n",
        retval, errno, strerror(errno));
      #endif
      backdata = safe_sprintf(backdata, BUF_MAX,
      "タイムアウトしました\n 対象はSNTPポートを開けていないか、サービスを行っていません\n");
      * ret = -1;
      close(send_sk);
      return(backdata);
      }
   if(retval >= 1){
      con = recvfrom(send_sk, (char *)&mesg, 48, 0, NULL, NULL);
      gettimeofday(&tv_vts, NULL);
      #ifdef SNTP_BUG
      printf("SNTP: scan-sntp(3): NG select <%d> ERRNO: %d %s\n",
        retval, errno, strerror(errno));
      #endif
      * ret = 1;
      close(send_sk);
      break;
      }
   } /* while */





// check recves data

bk_ntpdata.tstamp = read_net_8B(mesg+16);
bk_ntpdata.recvtstamp = read_net_8B(mesg+32);
bk_ntpdata.tranststamp = read_net_8B(mesg+40);
recv_time = (bk_ntpdata.recvtstamp/0x100000000ULL) - JAN_1970;
send_time = (bk_ntpdata.tranststamp/0x100000000ULL) - JAN_1970;

double my_start =(double)tv_tts.tv_sec + (double)tv_tts.tv_usec * 1/(1000 * 1000);
double my_stop = (double)tv_vts.tv_sec + (double)tv_vts.tv_usec * 1/(1000 * 1000);
// double ldtimeout = (double)(timeout * 100000) * 1/(1000 * 1000);  // 0.1
double ldtimeout = (double)(timeout * 1000000) * 1/(1000 * 1000);   // 1

// printf("start %lf,  stop %lf\n", my_start, my_stop);
// printf("recv %lf,  send %lf\n", recv_time, send_time);
// printf("ldtimeout %lf\n", ldtimeout);


// create return data
if(recv_time > my_stop){
   if((recv_time - my_stop) > ldtimeout){
      backdata = safe_sprintf(backdata, BUF_MAX,
      "対象サーバの時間と監視サーバの時間の差が閾値 %.3f を越えています\n対象サーバ時間: %.3f\n監視サーバ時間: %.3f\n差: %.3f 秒"
      ,ldtimeout, recv_time, my_stop, (double)(recv_time - my_stop));
      * ret = -1;
      return(backdata);
      }
   else{
      backdata = safe_sprintf(backdata, BUF_MAX,
      "このNTP監視で異常はありませんでした\n対象サーバの時間: %.3f\n自分の時間: %.3f\n"
      , recv_time, my_stop);
      * ret = 1;
      }
   }
else if(recv_time < my_stop){
   if((my_stop - recv_time) > ldtimeout){
      backdata = safe_sprintf(backdata, BUF_MAX,
      "対象サーバの時間と監視サーバの時間の差が閾値 %.3f を越えています\n対象サーバ時間: %.3f\n監視サーバ時間: %.3f\n差: %.3f 秒"
      ,ldtimeout, recv_time, my_stop, (double)(my_stop - recv_time));
      * ret = -1;
      return(backdata);
      }
   else{
      backdata = safe_sprintf(backdata, BUF_MAX,
      "このNTP監視で異常はありませんでした\n対象サーバの時間: %.3f\n自分の時間: %.3f\n"
      , recv_time, my_stop);
      * ret = 1;
      }
   }

else if(recv_time > send_time){
   if((recv_time - send_time) > ldtimeout){
      backdata = safe_sprintf(backdata, BUF_MAX,
      "対象サーバ内での処理時間が閾値 %.3f を越えています\n受信時間: %.3f\n送信時間: %.3f\n差: %.3f 秒"
      ,ldtimeout, recv_time, send_time, (double)(recv_time - send_time));
      * ret = -1;
      return(backdata);
      }
   else{
      backdata = safe_sprintf(backdata, BUF_MAX,
      "このNTP監視で異常はありませんでした\n対象サーバの時間: %.3f\n自分の時間: %.3f\n"
      , recv_time, my_stop);
      * ret = 1;
      }
   }
else if(send_time < recv_time){
   if((recv_time - send_time) > ldtimeout){
      backdata = safe_sprintf(backdata, BUF_MAX,
      "対象サーバ内での処理時間が閾値 %.3f を越えています\n受信時間: %.3f\n送信時間: %.3f\n差: %.3f 秒"
      ,ldtimeout, recv_time, send_time, (double)(recv_time - send_time));
      * ret = -1;
      return(backdata);
      }
   else{
      backdata = safe_sprintf(backdata, BUF_MAX,
      "このNTP監視で異常はありませんでした\n対象サーバの時間: %.3f\n自分の時間: %.3f\n"
      , recv_time, my_stop);
      * ret = 1;
      }
   }
else if(my_start < my_stop){
   if((my_stop - my_start) > ldtimeout){
      backdata = safe_sprintf(backdata, BUF_MAX,
      "このSNTP監視処理全体の時間が閾値 %.3f を越えています\n監視開始時間: %.3f\n監視終了時間: %.3f\n差: %.3f 秒"
      , ldtimeout, my_start, my_stop, (double)(my_stop - my_start));
      * ret = -1;
      return(backdata);
      }
   else{
      backdata = safe_sprintf(backdata, BUF_MAX,
      "このNTP監視で異常はありませんでした\n対象サーバの時間: %.3f\n自分の時間: %.3f\n"
      , recv_time, my_stop);
      * ret = 1;
      }
   }
else{
   backdata = safe_sprintf(backdata, BUF_MAX,
   "このNTP監視で異常はありませんでした\n対象サーバの時間: %.3f\n自分の時間: %.3f\n"
   , recv_time, my_stop);
   * ret = 1;
   }



return(backdata);
}





