/*********************************************************************
 * DBS: Distributed Benchmark System
 * Copyright (c) 1995, 1996, 1997 Yukio Murayama
 * Copyright (c) 1995, 1996, 1997 Nara Institute of Science and Technology
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided only with the following
 * conditions are satisfied:
 *
 * 1. Both the copyright notice and this permission notice appear in
 *    all copies of the software, derivative works or modified versions,
 *    and any portions thereof, and that both notices appear in
 *    supporting documentation.
 * 2. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgement:
 *      This product includes software developed by Nara Institute of 
 *      Science and Technology and its contributors.
 * 3. Neither the name of Nara Institute of Science and Technology nor 
 *    the names of its contributors may be used to endorse or promote 
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND NARA 
 * INSTITUTE OF SCIENCE AND TECHNOLOGY DISCLAIMS ANY LIABILITY OF 
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF 
 * THIS SOFTWARE. ALSO, THERE IS NO WARRANTY IMPLIED OR OTHERWISE, 
 * NOR IS SUPPORT PROVIDED.
 *
 * Feedback of the results generated from any improvements or
 * extensions made to this software would be much appreciated.
 * Any such feedback should be sent to:
 *
 *  Yukio Murayama
 *  E-mail:  <yukio-m@is.aist-nara.ac.jp>
 *  URL:     <http://shika.aist-nara.ac.jp/member/yukio-m/index.html>
 *  Address: Graduate School of Information Science, 
 *           Nara Institute of Science and Technology,
 *           Takayama 8916-5, Ikoma, Nara, Japan
 *
 * Nara Institute of Science and Technology has the rights to 
 * redistribute these changes.
 *********************************************************************/
/*****************************************************************
 * Distributed Benchmark System
 * DBS Daemon
 * $Revision: 1.25 $
 * $Date: 1997/07/11 00:54:04 $
 * $Author: yukio-m $
 *****************************************************************/

#define DBSD

#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/resource.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
#include <math.h>

#include <sys/param.h>

#ifdef SIGTSTP
#include <sys/file.h>
#include <sys/ioctl.h>
#endif

#if defined(__svr4__)
#include <sys/fcntl.h>
#endif

#if (!defined(BSD) || (BSD < 199306))
#include <malloc.h>
#endif

#include "dbs.h"
#include "dbs_net.h"
#include "dbsd.h"
#include "record.h"

#define CLOSE(FD) if((FD)!=-1 && close(FD)!=0){shutdown(FD, 1+1);}
#define SHUTDOWN(FD) if((FD)!=-1) shutdown(FD, 1+1)

void dbsd                __P((int argc, char *argv[]));

void reset_time          __P((struct dbsd_param *cmd, struct timeval origin_time));
void pre_proc            __P((struct dbsd_param *cmd, struct record *rd, struct record *rd2, struct tcp_trace *rt, char **data));
void pre_proc2           __P((struct dbsd_param *cmd, struct record *rd, struct record *rd2, struct tcp_trace *rt, char **data));
void post_proc           __P((struct dbsd_param *cmd, struct record *rd, struct record *rd2, struct tcp_trace *rt, char **data));
void receive_origin_time __P((int fd, struct timeval *origin_time));
void recv_command        __P((int fd, struct dbsd_param *dbsd_cmd));
void recv_traffic        __P((int fd, struct dbsd_traffic *traffic, int n));

int exec_cmd            __P((struct dbsd_param *cmd, struct record *rd, struct record *rd2, struct tcp_trace *rt, char **data));

void send_status         __P((int fd, int n, char *msg));
void send_result         __P((int fd, struct record rd, struct timeval origin_time, char *debug_msg));
void send_tcp_trace      __P((int fd, struct tcp_trace rt, struct timeval origin_time));

void getaddr             __P((struct assosiation *a, struct sockaddr_in *d_address, struct sockaddr_in *b_address));
void setaddr             __P((int fd, struct assosiation *a));
int recv_command2        __P((int fd));

void safe_exit_int       __P((int signo));
void safe_exit_pip       __P((int signo));
void safe_exit_alm       __P((int signo));
void safe_exit_urg       __P((int signo));
void safe_exit_bug       __P((int signo));
void reaper              __P((void));
void help                __P((char *prog));

int no_daemon=0;
int yes_daemon=0;
int fd=-1, sockfd=-1;
struct dbsd_param cmd;

/*****************************************************************
 * MAIN
 *****************************************************************/
void main(argc, argv)
int argc;
char *argv[];
{
    char *myname;

    if ((myname = strrchr(*argv, '/')) == NULL)
	myname = *argv;
    else
	++myname;
    
    if (strcmp(myname, "dbsd") == 0) {
	dbsd(argc, argv);
    } else {
	fprintf(stderr, "What is '%s'?\n", myname);
	exit(1);
    }

    exit(0);
}

/*****************************************************************
 * Distribute Benchimark Daemon   Main Routine
 *****************************************************************/
void dbsd(argc, argv)
int argc;
char *argv[];
{
    struct assosiation a;
    struct sockaddr_in serv_addr, cmd_addr, tmp_addr;
    struct record    rd;
    struct record    rd2;
    struct tcp_trace rt;
    struct timeval origin_time;
    struct timeval current_time;
    struct timezone tzp;
    struct servent *sp;
    int port, addrlen;
    int pid = -1;
    int one = 1;
    int ch;
    char localhost[MAXHOSTNAME];
    char cmdhost[256];
    char msg[CHAR_ARRAY];
    char *data;
    extern char *optarg;
    extern int optind, opterr;

    cmd.fd = -1;

    strcpy(cmdhost, "");

    if ((sp = getservbyname("dbs", "tcp")) != NULL)
	port = (int)ntohs((u_short)sp->s_port);
    else
	port = DEFAULT_PORT;
    
    while ((ch = getopt(argc, argv, "dDh:sp:v")) != EOF) {
	switch(ch) {
	  case 'p':    /* -p: Port Number */
	    port = atoi(optarg);
	    break;
	  case 'h':    /* -h: Acceptable Control Host */
	    strcpy(cmdhost, optarg);
	    break;
	  case 'd':    /* -d: DEBUG MODE */
	    debug++;       /* Grobal Variable over dbsd modules */
	    break;
	  case 's':    /* -s: running single process for DEBUGGER */
	    no_daemon++;   /* Grobal Variable */
	    break;
	  case 'D':    /* -D: DBSD are booted by inetd */
	    yes_daemon++;      /* Grobal Variable */
	    break;
	  case 'v':    /* -v: show version */
	  default:
	    help(argv[0]);
	    exit(1);
	}
    }

    if (yes_daemon>=1)
	debug = 0;

    DEBUGMSG2(1, fprintf(stderr, "debug level = %d\n", debug));

    signal(SIGINT,  safe_exit_int);
    signal(SIGPIPE, safe_exit_pip);
    signal(SIGSEGV, safe_exit_bug);
    signal(SIGBUS,  safe_exit_bug);
    signal(SIGSYS,  safe_exit_bug);

/*
    if (gethostname(localhost, MAXHOSTNAME-1) == -1) {
        DEBUGMSG2(1, perror("gethostname"));
        safe_exit(ABNORMAL, "DBSD.gethostname", strerror(errno));
    } 
*/
    strcpy(localhost,"");

    strcpy(a.dsthostname, cmdhost);
    strcpy(a.srchostname, localhost);
    a.source_port = (u_short)port;
    a.dest_port   = (u_short)0;
	
    getaddr(&a, &tmp_addr, &serv_addr);

    if (yes_daemon == 0) {
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	if ((sockfd=socket(serv_addr.sin_family, SOCK_STREAM, 0)) < 0) {
	    DEBUGMSG2(1, perror("socket"));
	    safe_exit(SOCKET, "DBSD.soket", strerror(errno));
	}
	
	if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
	    DEBUGMSG2(1, perror("bind"));
	    safe_exit(BIND, "DBSD.bind", strerror(errno));
	}
	
	listen(sockfd, LISTEN_NUM);
	addrlen = sizeof(cmd_addr);

	DEBUGMSG2(1, fprintf(stderr, "DBS Daemon Version %s is Starting.\n", DBS_VERSION));
	
	signal(SIGCHLD, (void *)reaper); 
	
	while (1) {
	    fd=-1;
	    if ((fd = accept(sockfd, (struct sockaddr *)&cmd_addr, &addrlen)) < 0) {
		if (errno == EINTR) {
		    continue;
		} else {
		    DEBUGMSG2(1, perror("accept"));
		    safe_exit(ACCEPT, "DBSD.accept", strerror(errno));
		}
	    }
	    
	    DEBUGMSG(1, "Connection is established.\n");
	    
	    if (no_daemon == 0) {
		if ((pid = fork()) < 0) {
		    DEBUGMSG2(1, perror("fork"));
		    safe_exit(FORK, "DBSD.fork", strerror(errno));
		}

		if (pid == 0) {
		    close(sockfd);
		    break;
		} else {
		    close(fd);
		}
	    } else {
		close(sockfd);
		break;
	    }
	}
    } else {
	fd = 0;
    }

    if(fcntl(fd, F_SETOWN, getpid()) < 0) {
        DEBUGMSG2(1, perror("fcntl(F_SETOWN)"));
	safe_exit(ABNORMAL, "DBSD.fctl(F_SETOWN)", strerror(errno));
    }

    signal(SIGURG, safe_exit_urg);
    signal(SIGALRM, safe_exit_alm);

    DEBUGMSG(1, "DBSD Accept\n");

    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one)) < 0)
	/* NULL */;

    /*****                     *****
     *****  DBSD Main routine  *****
     *****                     *****/

    /*
     *   Check Command Host.
     */
    setaddr(fd, &a);
    if (strcmp(cmdhost,"") != 0 && a.dest_address != tmp_addr.sin_addr.s_addr) {
        DEBUGMSG(1, "DBSD: Command Host access denyed");
	safe_exit(ACCESSDENY, "DBSD", "Command Host Access denyed");
    }
    /*
     *  Receive Command. 
     */
    alarm(60);
    recv_command(fd, &cmd);
    if ((cmd.traffic = (struct dbsd_traffic *)malloc(sizeof(struct dbsd_traffic) * cmd.traffic_n)) == NULL) {
        DEBUGMSG2(1, perror("malloc"));
	safe_exit(MALLOC, "DBSD.malloc(traffic)", strerror(errno));
    }

    alarm(60);
    recv_traffic(fd, cmd.traffic, cmd.traffic_n);

    DEBUGMSG(1, "RECV COMMAND: Normal END.\n");

    /*
     *  Pre-Process1. 
     *
     * Memory Allocation
     * Socket Bind
     * Socket Option
     */

    alarm(60);
    pre_proc(&cmd, &rd, &rd2, &rt, &data);
    DEBUGMSG(1, "PRE PROC: Normal END.\n");
    /*
     * Send Status (Ready). 
     */
    gettimeofday(&current_time, &tzp);
    sprintf(msg, "%d.%06d", current_time.tv_sec, current_time.tv_usec % 1000000);

    send_status(fd, NORMAL, msg);
    DEBUGMSG(1, "SEND STATUS: Normal END.\n");

    /*
     *  Pre-Process2.
     *  Server Accept
     */
    alarm(60);
    pre_proc2(&cmd, &rd, &rd2, &rt, &data);

    /*
     * Receive Origin Time. 
     * Set Start Time
     */
    alarm(60);
    receive_origin_time(fd, &origin_time);
    DEBUGMSG(5, "RECV START TIME: Normal END.\n");
    reset_time(&cmd, origin_time);

    /*
     *   DBS Measurement start.
     *   Execute Command 
     *   Data Transfer and Measurement
     */
    {
	int status;
	alarm(0);    
	status = exec_cmd(&cmd, &rd, &rd2, &rt, &data);
	DEBUGMSG(5, "EXEC CMD: Normal END.\n");
	
	/*
	 *   Receive Command1. 
	 */
	signal(SIGALRM, safe_exit_alm);
	alarm(360);    
	if (recv_command2(fd) == ABNORMAL) {
	    DEBUGMSG(1, "KILLED by DBSC.\n");
	    exit (1);
	}
	
	/*
	 *  Send Status (Result). 
	 */
	if (status == ON) {
	    send_status(fd, NORMAL, "*Time Out*");
	} else {
	    send_status(fd, NORMAL, "");
	}
	DEBUGMSG(1, "SEND STATUS: Normal END.\n");
    }
    
    /*
     *   Receive Command2. 
     */
    alarm(360);    
    if (recv_command2(fd) == ABNORMAL) {
	DEBUGMSG(1, "KILLED by DBSC.\n");
	exit (1);
    }

    /*
     *  Send Results 
     *  Send TCP_Trace 
     */
    alarm(360);
    DEBUGMSG(1, "Sending Results...\n");
    if (cmd.connection_mode == AFTER)
	send_result(fd, rd2, origin_time, "SEND CONNECTION.");

    alarm(360);
    send_result(fd, rd, origin_time, "SEND RESULT.....");
    if(cmd.tcp_trace == ON)
	send_tcp_trace(fd, rt, origin_time);
    DEBUGMSG(1, "SEND RESULT: Normal END\n");
    close(fd);

    /*
     * Post Process
     * Memory Free
     */
    alarm(360);
    post_proc(&cmd, &rd, &rd2, &rt, &data);
    DEBUGMSG(1, "POST PROC: Normal END.\n");

    /*
     *  DBS Normal End
     */    
    alarm(0);
    DEBUGMSG(1, "DBSD Child process is end.\n");
    exit(0);
}

/*****************************************************************
 * Sub Routine
 *****************************************************************/
/*
 * Reset wait_at time
 */
void reset_time(cmd, origin_time)
struct dbsd_param *cmd;
struct timeval origin_time;
{
    /*scan_cmd[i].total_size = total_size(send_traffic, &scan_cmd);*/
    timeval_add(&(cmd->start_time), &origin_time);
    DEBUGMSG2(4, fprintf(stderr, "start time (%12d %12d)\n", cmd->start_time.tv_sec, cmd->start_time.tv_usec));
    timeval_add(&(cmd->end_time),   &origin_time);
    DEBUGMSG2(4, fprintf(stderr, "end time   (%12d %12d)\n", cmd->end_time.tv_sec, cmd->end_time.tv_usec));
}

/*
 *  Pre-Process1. 
 *
 * Memory Allocation
 * Socket Bind
 * Socket Option
 */
void pre_proc(cmd, rd, rd2, rt, data)
struct dbsd_param *cmd;
struct record *rd;
struct record *rd2;
struct tcp_trace *rt;
char **data;
{
    int packet_stream, max_message, pattern_number, pattern_size;
    int record_buff;
    int m, i;


    /*
     * Socket Inisialize
     */
    if (cmd->assosiation.transport == TCP) {
	if (cmd->serverflg == CLIENT) {
	    tcp_init_client(&cmd->assosiation, cmd);

	    if (cmd->connection_mode == BEFORE && cmd->tcp_trace == ON) {
		init_so_debug(rt, &cmd->assosiation, TA_OUTPUT, cmd->connection_mode);
	    }
	} else if(cmd->serverflg == SERVER) {
	    tcp_init_server(&cmd->assosiation, cmd);
	} else {
	    DEBUGMSG2(1, fprintf(stderr,"cmd->serverflg=%d Error\n", cmd->serverflg));
	    safe_exit(INTERNALERROR, "DBSD.Serverflg", "");
	}
    } else if (cmd->assosiation.transport == UDP) {
	if (cmd->sendflg == SEND) {
	    udp_init(&cmd->assosiation, cmd);
	} else if(cmd->sendflg == RECEIVE) {
	    udp_init(&cmd->assosiation, cmd);
	} else {
	    DEBUGMSG2(1, fprintf(stderr, "cmd->sendflg=%d Error\n", cmd->sendflg));
	    safe_exit(INTERNALERROR, "DBSD.Sendflg", "");
	}
    } else {
	DEBUGMSG2(1, fprintf(stderr, "cmd->sendflg=%d Error\n", cmd->sendflg));
	safe_exit(INTERNALERROR, "DBSD.transport", "");
    }

    /*
     * Record Buffer  Initialize
     */
    /* Caluculation of Buffer Size */
    packet_stream = PACKET;
    max_message = cmd->traffic[0].size;        
    pattern_number = 0;
    pattern_size = 0;
    for (i = 0; i < cmd->traffic_n; i++) {
	if (cmd->traffic[i].packet <= 0) {
	    packet_stream = STREAM;
	} else {
	    pattern_number += ((int)ceil((double)cmd->traffic[i].size/(double)cmd->traffic[i].packet));
	    pattern_size   += cmd->traffic[i].size;
	}
	if (cmd->traffic[i].size > max_message)
	    max_message = cmd->traffic[i].size;
    }

    cmd->buf_size  = max_message;
    cmd->recv_mode = packet_stream;

    if (cmd->sendflg == SEND) {
	record_buff = pattern_number*cmd->send_times;
	if (record_buff != cmd->total_message) {
	    DEBUGMSG(1, "DBSD: Traffic Pattern Error");
	    safe_exit(ABNORMAL, "DBSD", "Traffic Pattern Error");
	}
    } else {
	if ((packet_stream == STREAM || cmd->assosiation.transport == UDP) && cmd->record_buff > 0)
	    record_buff = cmd->record_buff;
	else if (cmd->assosiation.transport == UDP)
	    record_buff = cmd->total_message;
	else if (cmd->assosiation.transport == TCP && packet_stream != STREAM)
	    if (pattern_size >= 0)
		record_buff = (cmd->total_size/pattern_size + 1)*pattern_number;
	    else {
		DEBUGMSG(1, "DBSD: DBSC's Command Error");
		safe_exit(ABNORMAL, "pattern_size <= 0", "");
	    }
	else if (cmd->assosiation.transport == TCP && packet_stream == STREAM)
	    if (cmd->sock_opt.mss > 0)
		record_buff = (cmd->total_size/cmd->sock_opt.mss)+ cmd->total_message;
	    else
		record_buff = (cmd->total_size/ 512 )+ cmd->total_message;
	else {
	    DEBUGMSG(1, "DBSD: pre-process error");
	    safe_exit(INTERNALERROR, "DBSD.Pre-Process", "");
	}
    }

    m = record_buff * sizeof(struct record_d); /* (XXX don't remove + 1.) (XXX But REMOVED) */
    
    rd->d    = (struct record_d *)malloc(m);
    rd->size = record_buff;
    rd->n    = 0;

    DEBUGMSG2(2, fprintf(stderr,"Malloc [%3d * %4d] = [%6d]byte for RECORD.\n",(int)sizeof(struct record_d),record_buff,m));
    
    if (rd->d == NULL) {
        DEBUGMSG2(1, perror("malloc"));
	safe_exit(MALLOC, "DBSD.malloc(record_buff)", strerror(errno));
    }
    if (cmd->connection_mode == AFTER) {
	record_buff = 5;
	m = record_buff * sizeof(struct record_d); /* (XXXdon't remove +1XXXREMOVED) */
	rd2->d    = (struct record_d *)malloc(5);
	rd2->size = record_buff;
	rd2->n    = 0;

	if (rd->d == NULL) {
	    DEBUGMSG2(1, perror("malloc"));
	    safe_exit(MALLOC, "DBSD.malloc(connection)", strerror(errno));
	}

	DEBUGMSG2(2, fprintf(stderr,"Malloc [%3d * %4d] = [%6d]byte for RECORD2.\n",(int)sizeof(struct record_d),record_buff,m));
    } else {
	rd2->d    = NULL;
	rd2->size = 0;
	rd2->n    = 0;
    }
    
    /*
     * TCP Trace Initialize
     */
    if (cmd->tcp_trace == ON) {
#ifdef TCP_NDEBUG
	if (cmd->trace_buff > 1)
	    record_buff = cmd->trace_buff;
	else if( cmd->sock_opt.mss > 0)
	    record_buff = (cmd->total_size/cmd->sock_opt.mss)*2 + cmd->total_message; /* XXX */
	else
	    record_buff = (cmd->total_size/512)*2 + cmd->total_message; /* XXX */

	m = record_buff * sizeof(struct tcp_debug);
	
	rt->d    = (struct tcp_debug *)malloc(m);
	rt->size = record_buff;
	rt->n    = 0;
/*	rt->p    = 0;*/

	if (rt->d == NULL) {
	    DEBUGMSG2(1, perror("malloc"));
	    safe_exit(MALLOC, "DBSD.malloc(tcp_trace)", strerror(errno));
	}

	DEBUGMSG2(2, fprintf(stderr,"Malloc [%3d * %4d] = [%6d]byte for TCP TRACE.\n",(int)sizeof(struct tcp_debug),record_buff,m));
#endif
    } else {
	rt->d    = NULL;
	rt->size = 0;
	rt->n    = 0;
/*	rt->p    = 0;*/
    }

    /*
     * Memory Alignment
     */
    DEBUGMSG(2, "MALLOC OF BUFFER : Normal END.\n");
    if ((*data=(char *)malloc(cmd->buf_size + cmd->mem_align + cmd->align_pad)) == NULL) {
        DEBUGMSG2(1, perror("malloc"));
	safe_exit(MALLOC, "DBSD.malloc(Data Buffer)", strerror(errno));
    }
    if (cmd->mem_align > 0)
	*data = (char *)(((long)*data + (long)cmd->mem_align - 1L) & ~((long)cmd->mem_align - 1L));

    if (cmd->align_offset != 0)
	*data = (char *)(((long)*data + (long)cmd->align_offset));

    DEBUGMSG2(2, fprintf(stderr,"Malloc size = %d\n", cmd->buf_size));
}

/*
 *  Pre-Process2.
 *  Server Accept
 */
void pre_proc2(cmd, rd, rd2, rt, data)
struct dbsd_param *cmd;
struct record *rd;
struct record *rd2;
struct tcp_trace *rt;
char **data;
{
    /*
     * Socket Inisialize
     */

    if (cmd->assosiation.transport == TCP && cmd->serverflg == SERVER) {
	tcp_init_server2(&cmd->assosiation, cmd);
	if (cmd->connection_mode == BEFORE && cmd->tcp_trace == ON) {
	    init_so_debug(rt, &cmd->assosiation, TA_INPUT, cmd->connection_mode);
	}
    }
}

/*
 * Main Process
 * Data Transfer and Measurement
 */
int exec_cmd(cmd, rd, rd2, rt, data)
struct dbsd_param *cmd;
struct record    *rd;
struct record    *rd2;
struct tcp_trace *rt;
char **data;
{
    int status = 0;

    if (cmd->assosiation.transport == TCP) {
	if (cmd->sendflg == SEND) {
	    status = tcp_send(cmd, *data, rd, rd2, rt);
	} else if(cmd->sendflg == RECEIVE) {
	    status = tcp_recv(cmd, *data, rd, rd2, rt);
	} else {
	    DEBUGMSG2(2, fprintf(stderr, "cmd->sendflg=%d Error\n", cmd->sendflg));
	    safe_exit(ABNORMAL, "DBSD.sendflg", "");
	}
    } else {
	if (cmd->sendflg == SEND) {
	    status = udp_send(cmd, *data, rd);
	} else if(cmd->sendflg == RECEIVE) {
	    status = udp_recv(cmd, *data, rd);
	} else {
	    DEBUGMSG2(2, fprintf(stderr, "cmd->sendflg=%d Error\n", cmd->sendflg));
	    safe_exit(ABNORMAL, "DBSD.sendflg", "");
	}
    }
    return status;
}

/*
 * Post-Process
 * Memory Free
 */
void post_proc(cmd, rd, rd2, rt, data)
struct dbsd_param *cmd;
struct record *rd;
struct record *rd2;
struct tcp_trace *rt;
char **data;
{
#define FREE(A) if((A) != (NULL)) free(A)
    free(*data);
    FREE(rd->d);
    FREE(rd2->d);
    FREE(rt->d);
#undef FREE
}    

/*****************************************************************
 * Recv Subroutine
 *****************************************************************/
/*
 * DBSC -> DBSD
 *
 * Get Origin Time
 *
 * Origin Time = DBS time 0.0s
 */
void receive_origin_time(fd, origin_time)
int fd;
struct timeval *origin_time;
{
    struct timeval net_tv;

    RECV(fd, &net_tv, sizeof(struct timeval));

    origin_time->tv_sec  = ntohl((u_int)net_tv.tv_sec);  
    origin_time->tv_usec = ntohl((u_int)net_tv.tv_usec);

    DEBUGMSG2(4, fprintf(stderr,"Origin Time (%12d %12d)\n", origin_time->tv_sec, origin_time->tv_usec));

    if (origin_time->tv_sec + origin_time->tv_usec <= 0) {
        DEBUGMSG(1, "DBSD: Time is abnormal.");
	safe_exit(TIME, "DBSD.origin_time", "Time is abnormal");
    }
}

/*
 * DBSC -> DBSD
 *
 * Get Small Command
 *
 * Get Value:
 *   NORMAL:   continue
 *   ABNORMAL: abort
 */
int recv_command2(fd)
int fd;
{
    int i;
    RECV(fd, &i, sizeof(i));

    return ntohl((u_int)i);
}

/*
 * DBSC -> DBSD
 *
 * Get Main Command
 *
 */
void recv_command(fd, dbsd_cmd)
int fd;
struct dbsd_param *dbsd_cmd;
{
    struct net_cmd net_cmd;
    
    DEBUGMSG2(4, fprintf(stderr,"sizeof(struct net_cmd)=%d\n",(int)sizeof(struct net_cmd)));

    RECV(fd, &net_cmd, sizeof(struct net_cmd));

    dbsd_cmd->assosiation.transport   = (u_int)ntohl((u_int)net_cmd.transport);
    dbsd_cmd->sendflg                 = (u_int)ntohl((u_int)net_cmd.sendflg);
    dbsd_cmd->serverflg               = (u_int)ntohl((u_int)net_cmd.serverflg);
    strcpy(dbsd_cmd->assosiation.dsthostname, net_cmd.dsthostname);
    strcpy(dbsd_cmd->assosiation.srchostname, net_cmd.srchostname);
    dbsd_cmd->assosiation.source_port = (u_short)ntohl((u_int)net_cmd.source_port);
    dbsd_cmd->assosiation.dest_port   = (u_short)ntohl((u_int)net_cmd.dest_port);
    dbsd_cmd->start_time.tv_sec       = (u_int)ntohl((u_int)net_cmd.start_time.tv_sec);
    dbsd_cmd->start_time.tv_usec      = (u_int)ntohl((u_int)net_cmd.start_time.tv_usec);
    dbsd_cmd->end_time.tv_sec         = (u_int)ntohl((u_int)net_cmd.end_time.tv_sec);
    dbsd_cmd->end_time.tv_usec        = (u_int)ntohl((u_int)net_cmd.end_time.tv_usec);
    dbsd_cmd->send_times              = (u_int)ntohl((u_int)net_cmd.send_times);
    dbsd_cmd->record_buff             = (u_int)ntohl((u_int)net_cmd.record_buff);
    dbsd_cmd->sock_opt.send_buff      = (u_int)ntohl((u_int)net_cmd.send_buff);
    dbsd_cmd->sock_opt.recv_buff      = (u_int)ntohl((u_int)net_cmd.recv_buff);
    dbsd_cmd->sock_opt.no_delay       = (u_int)ntohl((u_int)net_cmd.no_delay);
    dbsd_cmd->sock_opt.mss            = (u_int)ntohl((u_int)net_cmd.mss);
    dbsd_cmd->sock_opt.so_debug       = (u_int)ntohl((u_int)net_cmd.so_debug);
    dbsd_cmd->tcp_trace               = (u_int)ntohl((u_int)net_cmd.tcp_trace);
    dbsd_cmd->trace_buff              = (u_int)ntohl((u_int)net_cmd.trace_buff);
    dbsd_cmd->traffic_n               = (u_int)ntohl((u_int)net_cmd.traffic_n);
    dbsd_cmd->mem_align               = (u_int)ntohl((u_int)net_cmd.mem_align);
    dbsd_cmd->align_offset            = (u_int)ntohl((u_int)net_cmd.align_offset);
    dbsd_cmd->align_pad               = (u_int)ntohl((u_int)net_cmd.align_pad);
    dbsd_cmd->total_size              = (u_int)ntohl((u_int)net_cmd.total_size);
    dbsd_cmd->total_message           = (u_int)ntohl((u_int)net_cmd.total_message);
    dbsd_cmd->connection_mode         = (u_int)ntohl((u_int)net_cmd.connection_mode);

    DEBUGMSG2(3, {
	fprintf(stderr, "Command Data -------------------------------------------\n");
	fprintf(stderr, "Destination    = %-12s %-12s\n", net_cmd.dsthostname,    dbsd_cmd->assosiation.dsthostname);
	fprintf(stderr, "Source         = %-12s %-12s\n", net_cmd.srchostname,    dbsd_cmd->assosiation.srchostname);
	fprintf(stderr, "Protocol       = %-12d %-12d\n", net_cmd.transport,      dbsd_cmd->assosiation.transport); 
	fprintf(stderr, "Server flag    = %-12d %-12d\n", net_cmd.serverflg,      dbsd_cmd->serverflg);
	fprintf(stderr, "Send flag      = %-12d %-12d\n", net_cmd.sendflg,        dbsd_cmd->sendflg);
	fprintf(stderr, "Source Port    = %-12d %-12d\n", net_cmd.source_port,    dbsd_cmd->assosiation.source_port);
	fprintf(stderr, "Receive Port   = %-12d %-12d\n", net_cmd.dest_port,      dbsd_cmd->assosiation.dest_port);
	fprintf(stderr, "Send times     = %-12d %-12d\n", net_cmd.send_times,     dbsd_cmd->send_times);
	fprintf(stderr, "Send Buff      = %-12d %-12d\n", net_cmd.send_buff,      dbsd_cmd->sock_opt.send_buff);
	fprintf(stderr, "Receive Buff   = %-12d %-12d\n", net_cmd.recv_buff,      dbsd_cmd->sock_opt.recv_buff);
	fprintf(stderr, "Total Size     = %-12d %-12d\n", net_cmd.total_size,     dbsd_cmd->total_size);
	fprintf(stderr, "Total Message  = %-12d %-12d\n", net_cmd.total_message,  dbsd_cmd->total_message);
	fprintf(stderr, "So Debug       = %-12d %-12d\n", net_cmd.so_debug,       dbsd_cmd->sock_opt.so_debug);
	fprintf(stderr, "No Delay       = %-12d %-12d\n", net_cmd.no_delay,       dbsd_cmd->sock_opt.no_delay);
	fprintf(stderr, "MSS            = %-12d %-12d\n", net_cmd.mss,            dbsd_cmd->sock_opt.mss);
	fprintf(stderr, "TCP Trace      = %-12d %-12d\n", net_cmd.tcp_trace,      dbsd_cmd->tcp_trace);
	fprintf(stderr, "Traffic_N      = %-12d %-12d\n", net_cmd.traffic_n,      dbsd_cmd->traffic_n);
	fprintf(stderr, "Trace Buffer   = %-12d %-12d\n", net_cmd.trace_buff,     dbsd_cmd->trace_buff);
	fprintf(stderr, "Record Buffer  = %-12d %-12d\n", net_cmd.record_buff,    dbsd_cmd->record_buff);
	fprintf(stderr, "Memory Align   = %-12d %-12d\n", net_cmd.mem_align,      dbsd_cmd->mem_align);
	fprintf(stderr, "Align Offset   = %-12d %-12d\n", net_cmd.align_offset,   dbsd_cmd->align_offset);
	fprintf(stderr, "Align Pad      = %-12d %-12d\n", net_cmd.align_pad,      dbsd_cmd->align_pad);
	fprintf(stderr, "Connection Mode= %-12d %-12d\n", net_cmd.connection_mode,dbsd_cmd->connection_mode);

	fprintf(stderr, "Start Time     = (%-12d, %-12d) (%-12d, %-12d)\n",
		net_cmd.start_time.tv_sec, net_cmd.start_time.tv_usec,
		dbsd_cmd->start_time.tv_sec, dbsd_cmd->start_time.tv_usec);
	fprintf(stderr, "End time      = (%-12d, %-12d) (%-12d, %-12d)\n",
		net_cmd.end_time.tv_sec,  net_cmd.end_time.tv_usec,
		dbsd_cmd->end_time.tv_sec, dbsd_cmd->end_time.tv_usec);
	fprintf(stderr, "--------------------------------------------------------\n");
    }
	      ); /* <--------------------------------------------------- XXX  Don't Erase!!! */

}

/*
 * DBSC -> DBSD
 *
 * Get Traffic Pattern
 * {size, packet, esleep, isleep;
 *  size, packet, esleep, isleep;
 *    .     .       .       .
 *    .     .       .       .
 *    .     .       .       .    }
 */
void recv_traffic(fd, traffic, n)
int fd;
struct dbsd_traffic *traffic;
int n;
{
    int i;
    struct net_traffic net_traffic;

    for (i=0; i<n; i++) {
	RECV(fd, &net_traffic, sizeof(struct net_traffic));
	
	traffic[i].size           = ntohl(net_traffic.size);
	traffic[i].packet         = ntohl(net_traffic.packet);
	traffic[i].isleep.tv_sec  = ntohl(net_traffic.isleep) / 1000000;
	traffic[i].isleep.tv_usec = ntohl(net_traffic.isleep) % 1000000;
	traffic[i].esleep.tv_sec  = ntohl(net_traffic.esleep) / 1000000;
	traffic[i].esleep.tv_usec = ntohl(net_traffic.esleep) % 1000000;
	
	traffic[i].isleep_flag = (traffic[i].isleep.tv_sec > 0) || (traffic[i].isleep.tv_usec > 0);
	traffic[i].esleep_flag = (traffic[i].esleep.tv_sec > 0) || (traffic[i].esleep.tv_usec > 0);
	
	if (debug >= 2) {
	    fprintf(stderr, "Traffic Pattern ----------------------------------------\n");
	    fprintf(stderr, "SIZE   = %-12d %-12d\n",        net_traffic.size,   traffic[i].size);
	    fprintf(stderr, "PAKET  = %-12d %-12d\n",        net_traffic.packet, traffic[i].packet);
	    fprintf(stderr, "ESLEEP = %-12d(%-12d,%-12d)\n", net_traffic.esleep, traffic[i].esleep.tv_sec, traffic[i].esleep.tv_usec);
	    fprintf(stderr, "ISLEEP = %-12d(%-12d,%-12d)\n", net_traffic.isleep, traffic[i].isleep.tv_sec, traffic[i].isleep.tv_usec);
	    fprintf(stderr, "--------------------------------------------------------\n");
	}
    }
}

/*****************************************************************
 * Send Subroutine
 *****************************************************************/

/*
 * DBSD -> DBSC
 *
 * Send Result
 *
 * {(int) Number of Record, (struct record_d_net) A Record * Number of Record}
 */
void send_result(fd, rd, origin_time, debug_msg)
int fd;
struct record rd;
struct timeval origin_time;
char *debug_msg;
{
    int i, net, r;
    struct record_d_net *d = NULL;

    DEBUGMSG(2, debug_msg);

    net = htonl(rd.n);
    send(fd, (void *)&net, sizeof(net) , 0);

    r = sizeof(struct record_d_net) * rd.n;

    DEBUGMSG2(2, fprintf(stderr,"[%3d * %4d] = [%6d]byte", (int)sizeof(struct record_d_net), rd.n, r));

    if (rd.n >= 1) {
	if ((d=(struct record_d_net *)malloc(r)) == NULL) {
	    DEBUGMSG2(1, perror("malloc"));
	    safe_exit(MALLOC, "DBSD.malloc(send results)", strerror(errno));
	}
	for (i=0; i < rd.n; i++) {
	    d[i].packet_no   = htonl(rd.d[i].packet_no);
	    d[i].packet_size = htonl(rd.d[i].packet_size);
	    
	    timeval_sub(&rd.d[i].tv, &origin_time);
	    
	    d[i].tv_sec   = htonl(rd.d[i].tv.tv_sec);
	    d[i].tv_usec  = htonl(rd.d[i].tv.tv_usec);
	}
	send(fd, (void *)d, r, 0);
	free(d);
    }
    DEBUGMSG(2, "SENT.\n");
}

/*
 * DBSD -> DBSC
 *
 * Send TCP Trace
 *
 * {(int) Number of Record, (struct tcp_trace_d_net) A Record * Number of Record}
 */
void send_tcp_trace(fd, rt, origin_time)
int fd;
struct tcp_trace rt;
struct timeval origin_time;
{
    int i, net, s, origin;
    struct tcp_trace_d_net *d;

    DEBUGMSG(2, "SEND TCP Trace..");
#ifdef TCP_NDEBUG
    net = htonl(rt.n);
    send(fd, (void *)&net, sizeof(net) , 0);

    s = sizeof(struct tcp_trace_d_net) * rt.n;
    DEBUGMSG2(1, fprintf(stderr,"[%3d * %4d] = [%6d]byte", (int)sizeof(struct tcp_trace_d_net), rt.n, s));

    if (rt.n >= 1) {
	if ((d=(struct tcp_trace_d_net *)malloc(s)) == NULL) {
	    DEBUGMSG(1, "DBSD:malloc(send tcp_trace) Error.");
	    safe_exit(MALLOC, "DBSD.malloc(send tcp_trace)", strerror(errno));
	}
	origin = (int)(((double)(origin_time.tv_sec%(24*60*60))+(double)origin_time.tv_usec/(1000.0*1000.0))*1000);
		      
	for (i=0; i < rt.n; i++) {
	    d[i].td_time      = (int)htonl((u_int)((int)ntohl((u_int)rt.d[i].td_time) - (int)origin));
	    d[i].td_act       = htonl((int)rt.d[i].td_act);
	    if (d[i].td_act == TA_INPUT) {
		d[i].th_seq   =   (int)htonl(rt.d[i].td_ti.ti_t.th_seq);
		d[i].th_ack   =   (int)htonl(rt.d[i].td_ti.ti_t.th_ack);
	    } else {
		d[i].th_seq   =   (int)rt.d[i].td_ti.ti_t.th_seq;
		d[i].th_ack   =   (int)rt.d[i].td_ti.ti_t.th_ack;
	    }
	    d[i].th_flags     = htonl((int)rt.d[i].td_ti.ti_t.th_flags);

	    if (d[i].td_act == TA_INPUT) {
		d[i].len          = htonl((int)(rt.d[i].td_ti.ti_i.ih_len));
	    } else {
		d[i].len          = htonl((int)ntohs(rt.d[i].td_ti.ti_i.ih_len) - ((int)rt.d[i].td_ti.ti_t.th_off<<2));
	    }

	    d[i].t_state      = htonl((int)rt.d[i].td_cb.t_state);
	    d[i].t_rxtshift   = htonl((int)rt.d[i].td_cb.t_rxtshift);
	    d[i].t_rxtcur     = htonl((int)rt.d[i].td_cb.t_rxtcur);
	    d[i].t_dupacks    = htonl((int)rt.d[i].td_cb.t_dupacks);
	    d[i].t_maxseg     = htonl((int)rt.d[i].td_cb.t_maxseg);
/*
	    d[i].t_force      = htonl((int)rt.d[i].td_cb.t_force);
*/
	    d[i].t_flags      = htonl((int)rt.d[i].td_cb.t_flags);
	    d[i].snd_una      = htonl((int)rt.d[i].td_cb.snd_una);
	    d[i].snd_nxt      = htonl((int)rt.d[i].td_cb.snd_nxt);
	    d[i].snd_wl1      = htonl((int)rt.d[i].td_cb.snd_wl1);
	    d[i].snd_wl2      = htonl((int)rt.d[i].td_cb.snd_wl2);
	    d[i].iss          = htonl((int)rt.d[i].td_cb.iss);
	    d[i].snd_wnd      = htonl((int)rt.d[i].td_cb.snd_wnd);
	    d[i].rcv_wnd      = htonl((int)rt.d[i].td_cb.rcv_wnd);
	    d[i].rcv_nxt      = htonl((int)rt.d[i].td_cb.rcv_nxt);
	    d[i].irs          = htonl((int)rt.d[i].td_cb.irs);
	    d[i].rcv_adv      = htonl((int)rt.d[i].td_cb.rcv_adv);
	    d[i].snd_max      = htonl((int)rt.d[i].td_cb.snd_max);
	    d[i].snd_cwnd     = htonl((int)rt.d[i].td_cb.snd_cwnd);
	    d[i].snd_ssthresh = htonl((int)rt.d[i].td_cb.snd_ssthresh);
#if !defined(__FreeBSD_version) || (__FreeBSD_version < 400009)
	    d[i].t_idle       = htonl((int)rt.d[i].td_cb.t_idle);
	    d[i].t_rtt        = htonl((int)rt.d[i].td_cb.t_rtt);
#else
	    d[i].t_idle       = htonl((int)rt.d[i].td_cb.t_rcvtime);
	    d[i].t_rtt        = htonl((int)rt.d[i].td_cb.t_rtttime);
#endif
	    d[i].t_rtseq      = htonl((int)rt.d[i].td_cb.t_rtseq);
	    d[i].t_srtt       = htonl((int)rt.d[i].td_cb.t_srtt);
	    d[i].t_rttvar     = htonl((int)rt.d[i].td_cb.t_rttvar);
/*	    d[i].t_rttmin     = htonl((int)rt.d[i].td_cb.t_rttmin);*/      /* AAA */
	    d[i].max_sndwnd   = htonl((int)rt.d[i].td_cb.max_sndwnd);
	}
	send(fd, (void *)d, s, 0);
	free(d);
    }
#endif
    DEBUGMSG(2, "SENT.\n");
}

/*****************************************************************
 * Signal Handlers
 *****************************************************************/
/*
 * Bye-bye Zombie!
 */
void reaper()
{
/*
#ifdef SYSTYPE_SVR4
    union wait status;
#else
*/
    int status;
/*
#endif
*/

    while(wait3(&status, WNOHANG, (struct rusage *)0) > 0)
	;
}

/*
 * Safe Exit
 */
void safe_exit(i, msg, error)
int i;
char *msg, *error;
{
    extern int fd, sockfd;

    DEBUGMSG(1, "Safe Exitting....\n");

    if (fd != -1) {	
	DEBUGMSG(1, "DBSD Child process is stoped.\n");
	if (*error == '\0')
	    send_status(fd, i, msg);
	else {
	    char tmp[CHAR_ARRAY+CHAR_ARRAY+3];
	    sprintf(tmp, "%s : %s", msg, error);
	    send_status(fd, i, tmp);
	}
	SHUTDOWN(cmd.fd);
	CLOSE(fd);
    } else {
	DEBUGMSG(1, "DBSD Parent process is stoped.\n");
	close(sockfd);
    }
    DEBUGMSG(1, "Program was killed.\n");
    exit(1);
}

/*
 * Signal Handler for SigINT 
 */
void safe_exit_int(signo)
int signo;
{
    extern int fd, sockfd;

    DEBUGMSG(1, "Interrupting....\n");

    if (fd != -1) {	
	DEBUGMSG(1, "DBSD Child process is stoped.\n");
	send_status(fd, ABNORMAL, "Interrupted");
	SHUTDOWN(cmd.fd);
	CLOSE(fd);
    } else {
	DEBUGMSG(1, "DBSD Parent process is stoped.\n");
	close(sockfd);
    }
    DEBUGMSG(1, "Program was killed.\n");
    exit(2);
}

/*
 * Signal Handler for SIGPIP
 */
void safe_exit_pip(signo)
int signo;
{
    extern int fd, sockfd;

    DEBUGMSG(1, "Pipe is Broken....\n");

    if (fd != -1) {
	DEBUGMSG(1, "DBSD Child process is stoped.\n");
	send_status(fd, ABNORMAL, "Pipe is Broken");
	SHUTDOWN(cmd.fd);
	CLOSE(fd);
    } else {
	DEBUGMSG(1, "DBSD Parent process is stoped.\n");
	close(sockfd);
    }
    DEBUGMSG(1, "Program was killed.\n");
    exit(3);
}

/*
 * Signal Handler for SIGPIP
 */
void safe_exit_alm(signo)
int signo;
{
    extern int fd, sockfd;

    DEBUGMSG(1, "TIME OUT\n");

    if (fd != -1) {
	DEBUGMSG(1, "DBSD Child process is stoped.\n");
	send_status(fd, ABNORMAL, "ALARM");
	SHUTDOWN(cmd.fd);
	CLOSE(fd);
    } else {
	DEBUGMSG(1, "DBSD Parent process is stoped.\n");
	close(sockfd);
    }
    DEBUGMSG(1, "Program was killed.\n");
    exit(4);
}

/*
 * Signal Handler for SIGURG (TCP urgent data was received.)
 */
void safe_exit_urg(signo)
int signo;
{
    extern int fd, sockfd;

    DEBUGMSG(1, "DBSC kill Message\n");

    if (fd != -1) {
	DEBUGMSG(1, "DBSD Child process is stoped.\n");
	send_status(fd, ABNORMAL, "DBSC kill Message");
	SHUTDOWN(cmd.fd);
	CLOSE(fd);
    } else {
	DEBUGMSG(1, "DBSD Parent process is stoped.\n");
	close(sockfd);
    }
    DEBUGMSG(1, "Program was killed.\n");
    exit(5);
}

/****** run out from segmentation fault ******/

/*
 * Signal Handler for Internal Bug!
 */
void safe_exit_bug(signo)
int signo;
{
    extern int fd, sockfd;
    int i;

    signal(SIGSEGV, safe_exit_bug);
    signal(SIGBUS,  safe_exit_bug);
    signal(SIGSYS,  safe_exit_bug);

    DEBUGMSG(1, "DBSD Internal Error !!\n");

    if (fd != -1) {
	DEBUGMSG(1, "DBSD Child process is stoped.\n");

	switch (signo) {
	  case SIGSEGV:
	    send_status(fd, INTERNALERROR, "DBSD Internal Error!! Segmentation violation.\0");
	    break;
	  case SIGBUS:
	    send_status(fd, INTERNALERROR, "DBSD Internal Error!! Bus error.\0");
	    break;
	  case SIGSYS:
	    send_status(fd, INTERNALERROR, "DBSD Internal Error!! Bad argument to system call.\0");
	    break;
	}

	for (i=0; i < 15; i++) {
	    DEBUGMSG(1, ".");
	    sleep(1);
	}
	send_status(fd, INTERNALERROR, "DBSD Internal Error!!");
	SHUTDOWN(cmd.fd);
	CLOSE(fd);
    } else {
	DEBUGMSG(1, "DBSD Parent process is stoped.\n");
	close(sockfd);
    }

    DEBUGMSG(1, "Program was killed.\n");
    exit(6);
}

/*
 * DBSD -> DBSC
 *
 * Send Status
 *
 * {(int)Status, (int)strlen(msg), (char *)msg}
 */
void send_status(fd, n, msg)
int fd;
int n;
char *msg;
{
    int net, i;

    /* Error Number */    
    net = htonl(n);
    send(fd, (void *)&net, sizeof(net), 0);

    /* Message Length */    
    i = strlen(msg);
    net = htonl(i);
    send(fd, (void *)&net, sizeof(net) , 0);

    /* Message Strings */    
    if (i > 0)
	send(fd, msg, i, 0);

    DEBUGMSG2(2, fprintf(stderr, "%d, %i, %s\n", n, i, msg));
}

/*****************************************************************
 * Help Message
 *****************************************************************/
void help(prog)
char *prog;
{
    fprintf(stderr, "DBS Version %d.%d%s  by yukio-m@is.aist-nara.ac.jp\n", DBS_MAJOR_VERSION, DBS_MINOR_VERSION, DBS_PATCH_VERSION);
    fprintf(stderr, "Usage: %s [-p port_number] [-d] [-D] [-v] [-h command_host] \n", prog); 
}


syntax highlighted by Code2HTML, v. 0.9.1