/*
	Auther	Satoshi Yasuda	7m3tjz/ad6gz
*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <signal.h>
#include "dv_echo.h"
#include "shm.h"

#define	TRUE	1
#define	FALSE	0

void	exit (int status);
void    ConSend(void);
void	voice_frame_send(void);

int	dstSocket;
int	udpSocket;
struct	sockaddr_in	udpAddr;
struct	sockaddr_in	sndAddr;
struct	in_addr		dstIP;
struct	in_addr		dbIP;
in_port_t		dstPort;
struct	packet		send_pkt;
char	respFlags;

char	RelaySW;
char	ConnectWait;
char	FirstHeaderSend;
int	AckTimeOut;
int	DSTREQcnt;
FILE	*in_dat;
char	SendBack;
char	Connected;
char	DVwait;
unsigned char	PacketIdSave[2];

struct  repMsg  reqConMsg;

void CloseSocket()
{
        if (dstSocket != 0){
                close(dstSocket);
                dstSocket=0;
        }
        if (udpSocket != 0){
                close(dstSocket);
                dstSocket=0;
        }
        exit(0);
}

void	voice_send(void)
{
	if (ConnectWait) return;

	if (in_dat == NULL)
	{
		in_dat = fopen ("/tmp/dv_echo.bin", "rb");
		fread ((char *)&send_pkt, 56, 1, in_dat);
        	if (send_pkt.B_header.seq == 0x80)
        	{
                	fread ((char *)&send_pkt, 56, 1, in_dat);
                	ConSend();
                	ConnectWait = TRUE;
			AckTimeOut = 0;
			DSTREQcnt = 0;
			NakCount = 0;
			Connected = FALSE;
			DVwait = TRUE;
                	return;
        	}
	}
	else
	{
		voice_frame_send();
	}
}

void	voice_frame_send(void)
{
	int	n;
	struct	timeval	t1, t2, t3, t4;
	int	len;

        if (FirstHeaderSend)
        {
                if (RelaySW)
                {
                        memcpy ((char *)&RelayPacket.dv, (char *)&send_pkt, 56);
                        RelayPacket.ip = dstIP;
                        RelayPacket.port = dstPort;
                        sndAddr.sin_family = PF_INET;
                        sndAddr.sin_port = db_port;
                        sndAddr.sin_addr = dbIP;
			memcpy (&RelayPacket.dv.header.MyCall, &client_call, 8);
			memcpy (&RelayPacket.dv.header.YourCall, &CallerCall, 8);
			memcpy (&RelayPacket.dv.header.MyCall2, "ECHO", 4);
                        sendto (udpSocket, (char *)&RelayPacket, sizeof(RelayPacket), 0,
                                (struct sockaddr *)&sndAddr, sizeof(sndAddr));
                }
                else
                {
                        sndAddr.sin_addr = dstIP;
                        sndAddr.sin_port = dstPort;
                        sndAddr.sin_family = PF_INET;
                        memcpy (&send_pkt.header.MyCall, &client_call, 8);
                        memcpy (&send_pkt.header.YourCall, &CallerCall, 8);
                        memcpy (&send_pkt.header.MyCall2, "ECHO", 4);
                        sendto (udpSocket, &send_pkt, 56, 0,
                                (struct sockaddr *)&sndAddr, sizeof(sndAddr));
                }
                FirstHeaderSend = FALSE;
        	gettimeofday (&t1, NULL);
        	t4.tv_sec = 0;
        	t4.tv_usec = 20000;             /* 20 m sec */
        	timeradd (&t1, &t4, &t3);
        	t1.tv_sec = t3.tv_sec;
        	t1.tv_usec = t3.tv_usec;
	}

       	gettimeofday (&t2, NULL);
       	if (timercmp (&t2, &t1, >))
       	{
		if (fread ((char *)&send_pkt, 56, 1, in_dat) <= 0)
		{
			SendBack = FALSE;
			fclose(in_dat);
			in_dat = NULL;
			RelaySW = FALSE;
			return;
		}
		if	(RelaySW)
		{
			memcpy	((char *)&RelayPacket.dv, (char *)&send_pkt, 27);
			RelayPacket.ip = dstIP;
			RelayPacket.port = dstPort;
			sndAddr.sin_family = PF_INET;
			sndAddr.sin_port = db_port;
			sndAddr.sin_addr = dbIP;
			sendto	(udpSocket, (char *)&RelayPacket, 33, 0,
				(struct	sockaddr*)&sndAddr, sizeof(sndAddr));
		}
		else
		{
			sndAddr.sin_addr = dstIP;
			sndAddr.sin_port = dstPort;
			sndAddr.sin_family = PF_INET;
			sendto	(udpSocket, &send_pkt, 27, 0,
				(struct	sockaddr *)&sndAddr, sizeof(sndAddr));
		}
		if (send_pkt.B_header.seq & 0x40)
		{
			ConnectWait = FALSE;
			FirstHeaderSend = FALSE;
			RelaySW = FALSE;
			Connected = FALSE;
		}
               	timeradd (&t1, &t4, &t3);
               	t1.tv_sec = t3.tv_sec;
               	t1.tv_usec = t3.tv_usec;
	}
	else
	{
		usleep (2000);
	}
}

void    ConSend(void)
{
	int	ret;

	struct	shm_form	send_msg;

        memcpy (&send_msg.Rep2Call, &send_pkt.header.Rpt2Call, 32);
        memcpy (&send_msg.dstRep, &send_pkt.header.YourCall, 8);
        send_msg.type = ReqConnect;
        send_msg.protocol = G3;
        sndAddr.sin_family = PF_INET;
        sndAddr.sin_port = db_port;
        sndAddr.sin_addr = dbIP;
	PacketIdSave[0] = send_pkt.B_header.PacketID[0];
	PacketIdSave[1] = send_pkt.B_header.PacketID[1];
	send_msg.PacketID[0] = PacketIdSave[0];
	send_msg.PacketID[1] = PacketIdSave[1];
	memcpy (&send_msg.srcCall, &client_call, 8);
	memcpy (&send_msg.dstCall, &CallerCall, 8);
	memcpy (&send_msg.Rep1Call, &client_call, 8);
	memcpy (&send_msg.dstRep, &CallerCall, 8);
        ret = sendto (udpSocket, (char *)&send_msg, sizeof (struct shm_form), 0,
                (struct sockaddr *)&sndAddr, sizeof(sndAddr));
}

void	AckSend(char srcRptCall[8], char PacketID[2])
{
        struct  shm_form        ack_msg;
        int     ret;

        ack_msg.type = Ack;
	memcpy (ack_msg.Rep1Call, srcRptCall, 8);
	memcpy (ack_msg.dstCall, client_call, 8);
        sndAddr.sin_family = PF_INET;
        sndAddr.sin_port = db_port;
	sndAddr.sin_addr = dbIP;
	ack_msg.PacketID[0] = PacketID[0];
	ack_msg.PacketID[1] = PacketID[1];
        sendto (udpSocket, (char *)&ack_msg, sizeof(ack_msg), 0,
                 (struct sockaddr *)&sndAddr, sizeof(sndAddr));
	return;
}

void	ReqSendSender (void)
{
	sndAddr.sin_family = PF_INET;
        sndAddr.sin_port = dstPort;
        sndAddr.sin_addr = dstIP;
        sendto (udpSocket, "\0\0\0\0SRCREQ UDP hole punching", 28, 0,
                 (struct sockaddr *)&sndAddr, sizeof(sndAddr));
}

int main(int argc, const char **argv){

        int     ret;
        int     width;
        int     n;
	int	k;
	char	Send_Connect;
	char	Recv_Connect;
        fd_set  readOk,Mask;
        struct  timeval timeout;
        socklen_t addrlen;
	struct	packet	send_packet;

	// IP アドレス，ソケット，sockaddr_in 構造体
	struct sockaddr_in dstAddr;
	//struct	repMsg	reqConMsg;

	//struct sockaddr_in addr;
 	struct hostent *hp;
  	char   buf[256];
	int    numrcv;

	FILE	*dat;

        signal(SIGINT,CloseSocket);
        signal(SIGTERM,CloseSocket);


	config (argc, (char **)argv);

	//sockaddr_in 構造体のセット
	bzero((char *)&dstAddr, sizeof(dstAddr));
	dstAddr.sin_family = PF_INET;
	dstAddr.sin_port = trust_port;
  
	hp = gethostbyname(TrustDomainName);
	bcopy(hp->h_addr, &dstAddr.sin_addr, hp->h_length);

        hp = gethostbyname(TrustDbDomainName);
        bcopy(hp->h_addr, &dbIP, hp->h_length);

	//ソケットの生成
	dstSocket = socket(PF_INET, SOCK_STREAM, 0);
	udpSocket = socket(PF_INET, SOCK_DGRAM, 0);

	//接続
	if (connect(dstSocket, (struct sockaddr *)&dstAddr, 
			sizeof(dstAddr)) < 0){
	printf("Does not connect to '%s'.\n",TrustDomainName);
	return(-1);
	}

	printf("Connect to '%s'.\n", TrustDomainName);

	//コールサイン入力のプロンプト
	memset (buf, 0x20, 14); 
	while (memcmp (buf,"Repeater Call:", 14) != 0)
	{
		numrcv = recv(dstSocket, buf, 64, 0);
		buf[numrcv] = 0x00;
		printf("%s\n",buf);
	}

	//コールサインの送信
	for (n = 0 ; n < 8 ; n++)
	{
		client_call[n] = toupper (client_call[n]);
	}
	send (dstSocket, client_call, 8, 0);

        //パスワード入力のプロンプト
	memset (buf, 0x20, 9);
	while (memcmp (buf, "Password:", 9) != 0)
	{
        	numrcv = recv(dstSocket, buf,  255, 0);
        	buf[numrcv] = 0x00;
		printf ("%s\n",buf);
	}

        //パスワードの送信
        send (dstSocket, client_password, strlen(client_password), 0);

        //ポート番号入力のプロンプト
	memset (buf, 0x20, 12);
        while (memcmp (buf, "Port Number:", 12) != 0)
	{
		numrcv = recv(dstSocket, buf, 255, 0);
       	 	buf[numrcv] = 0x00;
        	printf("%s\n",buf);
	}

	sprintf (buf, "%0d\0", ntohs(client_port));
	//ポート番号の送信
        send (dstSocket, buf, strlen(buf), 0);

        memset (&udpAddr, 0, sizeof(struct sockaddr_in));
        udpAddr.sin_family = PF_INET;
        udpAddr.sin_port = client_port;
        udpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
        ret = bind(udpSocket, (struct sockaddr *)&udpAddr, sizeof(udpAddr));

        FD_ZERO(&Mask);
        FD_SET(dstSocket, &Mask);
	FD_SET(udpSocket, &Mask);
        FD_SET(0,&Mask);
        width = dstSocket;
	if (dstSocket < udpSocket) width = udpSocket;
	width++;
	Send_Connect = FALSE;
	Recv_Connect = FALSE;
	SendBack = FALSE;
	RelaySW = FALSE;
	ConnectWait = FALSE;
	FirstHeaderSend = FALSE;
	AckTimeOut = 0;
	in_dat = NULL;
	dat = NULL;
	NakCount = 0;
	Connected = FALSE;
	DVwait = FALSE;

	while (1){
		readOk = Mask;
		timeout.tv_sec = 0;
		timeout.tv_usec = 5000;
		switch (select (width, (fd_set *)&readOk, NULL, NULL, &timeout))
		{
	
			case -1:
				break;
			case 0:
                                if (ConnectWait)
                                {
                                        AckTimeOut++;
                                        if (AckTimeOut == 20)   /* 100 m sec, */
                                        {
                                                ConSend();
                                        }
                                        if (AckTimeOut > 40)    /* 200 m sec. */
                                        {
                                                RelaySW = TRUE;
                                                ConnectWait = FALSE;
                                                FirstHeaderSend = TRUE;
						AckTimeOut = 0;
                                                printf ("Change to Relay Mode\n");
                                        }
				}
				if (DVwait)
				{
					DSTREQcnt ++;
					if ((DSTREQcnt % 4) == 0) ReqSendSender();
                                }
				if (SendBack)	voice_send();
				break;
			default:
				if(FD_ISSET(dstSocket, &readOk))
				{
                        		//パケットの受信
                        		numrcv = recv(dstSocket, &reqConMsg, sizeof(reqConMsg), 0);
					if (numrcv <= 0)
                                        {
                                                reqConMsg.reqType = ReqEnd;
                                                send (dstSocket, &reqConMsg, sizeof(reqConMsg), 0);
                                                close(dstSocket);
                                                return(0);
                                        }
                        		if (reqConMsg.reqType == Ack)
                        		{
						if (!Connected && !memcmp(&PacketIdSave, &reqConMsg.ackConnect.PacketID, 2))
						{
                                               		dstPort = reqConMsg.ackConnect.port;
                                                	dstIP = reqConMsg.ackConnect.ip;
                                        		udpAddr.sin_family = PF_INET;
                                                	udpAddr.sin_port = dstPort;
                                              		udpAddr.sin_addr = dstIP;
							respFlags = RespNULL;
                                                	sendto (udpSocket, "\0\0\0\0SRCREQ UDP hole punching", 28, 0,
                                                       		(struct sockaddr *)&udpAddr, sizeof(udpAddr));
						}
                        		}
                                        if (reqConMsg.reqType == Nak)
                                        {
                                                printf ("Nak %2.2x %8.8s %s %d\n",
                                                        reqConMsg.reqType, reqConMsg.ackConnect.YourCall,
                                                        inet_ntoa (reqConMsg.ackConnect.ip),
                                                        ntohs(reqConMsg.ackConnect.port));
						usleep(500000);
						ConSend();
						AckTimeOut = 0;
						NakCount++;
						if (NakCount > 20)
						{
							ConnectWait = FALSE;
							FirstHeaderSend = FALSE;
						}
                                        }
                                        else if (reqConMsg.reqType == ReqConnect)
                                        {
                                                printf ("Req %2.2x %2.2x %8.8s %8.8s %s %d\n",
							reqConMsg.reqType,
                                                        reqConMsg.reqConnect.protocol,
                                                        reqConMsg.reqConnect.YourCall,
							reqConMsg.reqConnect.Rpt1Call,
                                                        inet_ntoa(reqConMsg.reqConnect.ip),
                                                        ntohs(reqConMsg.reqConnect.port));
						PacketIdSave[0] = reqConMsg.reqConnect.PacketID[0];
						PacketIdSave[1] = reqConMsg.reqConnect.PacketID[1];
						ReqSendSender();
						DSTREQcnt = 0;
						//dstIP = reqConMsg.reqConnect.ip;
						//dstPort = reqConMsg.reqConnect.port;
						//if (AckSend(reqConMsg.reqConnect.Rpt1Call) == Ack)
						//			ReqSendSender();
						AckSend(reqConMsg.reqConnect.Rpt1Call, reqConMsg.reqConnect.PacketID);
                                        }
				}
				if(FD_ISSET(0, &readOk))
				{
                			fgets(buf, 255, stdin);
                			if (memcmp (buf, "end", 3) == 0)
					{
						reqConMsg.reqType = ReqEnd;
        					send (dstSocket, &reqConMsg, sizeof(reqConMsg), 0);
        					close(dstSocket);
        					return(0);
					}
                			memcpy (reqConMsg.reqConnect.Rpt2Call, buf, 32);
                			reqConMsg.reqConnect.port =  client_port;
                			reqConMsg.reqConnect.protocol = 0;
                			reqConMsg.reqType = ReqConnect;
                			send (dstSocket, &reqConMsg, sizeof(reqConMsg), 0);
					Send_Connect = FALSE;
					Connected = FALSE;
				}
				if (FD_ISSET(udpSocket, &readOk))
				{
			                addrlen = sizeof(struct sockaddr_in);
			                ret=recvfrom (udpSocket, (char *)&send_packet, 56, 0,
                        			(struct sockaddr *)&udpAddr, &addrlen);
					if (memcmp ((char *)&send_packet, "\0\0\0\0DSTREQ", 10) == 0)
					{
						if (!Connected)
						{
                                                	ConnectWait = FALSE;
                                                	FirstHeaderSend = TRUE;
							dstPort = udpAddr.sin_port;
							dstIP = udpAddr.sin_addr;
							Connected = TRUE;
							DVwait = FALSE;
						}
					}
					if (memcmp ((char *)&send_packet, "\0\0\0\0SRCREQ", 10) == 0)
					{
                        			sendto (udpSocket, "\0\0\0\0SRCACK UDP hole punching", 28, 0,
                                			(struct sockaddr *)&udpAddr, sizeof(udpAddr));
					}
        				if (memcmp ((char *)&send_packet, "\0\0\0\0SRCACK", 10) == 0)
        				{
						if (!Connected)
						{
							dstPort = udpAddr.sin_port;
							dstIP = udpAddr.sin_addr;
                					ConnectWait = FALSE;
                					FirstHeaderSend = TRUE;
							Connected = TRUE;
							DVwait = FALSE;
						}
        				}
					if (!memcmp (send_packet.ID, "DSVT", 4)
						&& !memcmp(PacketIdSave, send_packet.B_header.PacketID, 2))
					{
						DVwait = FALSE;
						if (!dat) dat = fopen ("/tmp/dv_echo.bin", "wb");
						if (send_packet.B_header.seq == 0x80)
							memcpy (&CallerCall, &send_packet.header.MyCall, 8);
						fwrite ((char *)&send_packet, 56, 1, dat);
						if (send_packet.B_header.seq & 0x40)
						{
							fclose (dat);
							dat = NULL;
							dat = 0;
							SendBack = TRUE;
							Connected = FALSE;
							ConnectWait = FALSE;
						}
					}
				}
				break;
		}
  	}
}

