/*
	Date	01/18/2014
	Auther	Satoshi Yasuda	7M3TJZ/AD6GZ
	Mod.
	2014.01.21 V00.00
	2014.01.22 V00.01 Bug fixed for from_rig.c
	2014.01.22 V00.02 Bug fixed for beep.c
	2014.01.23 V00.03 drop "htons"
	2014.01.23 V00.04 Added RnRoute messages routine
	2014.01.28 V00.05 Changed "hole punching" routine & bug fixed
	2014.01.29 V00.06 Added PacketID check
	2014.01.30 V00.07 Added ProductID & Vendor ID set routine
*/

#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 <linux/ipc.h>
#include <linux/msg.h>
#include <signal.h>
#include <mqueue.h>
#include <pthread.h>
#include <errno.h>
#include "dsgwd_pi.h"
#include "node.h"

void	exit (int status);
void	*node_adapter (void *arg);
void	send_dsvt(void);
void	receive_dsvt(void);

int	dstSocket;
int	udpSocket;

struct	sockaddr_in	udpAddr;
struct	sockaddr_in	sndAddr;

mqd_t	rf_qid;
mqd_t	inet_qid;
char	ConnectWait;
char	FirstHeaderSend;

struct	in_addr trustIP;
struct	in_addr	dstIP;
struct	in_addr	dbIP;

in_port_t	dstPort;

struct	mq_attr	rf_mattr;
struct	mq_attr	inet_mattr;
struct  repMsg  reqConMsg;

pthread_t	usb_ID;

char	SendConnect;
char	RecvConnect;
struct  packet  	send_pkt;
struct	shm_form	send_msg;
int	AckTimeOut;
int	DSTREQcnt;
char	RelaySW;
char	respFlags;
char	Connected;
char	DVwait;
unsigned char	PacketIdSave[2];

char	PTT_On;
char	COS_On;
void CloseSocket()
{
        if (dstSocket != 0){
                close(dstSocket);
                dstSocket=0;
        }
        if (udpSocket != 0){
                close(dstSocket);
                dstSocket=0;
        }
	mq_close (rf_qid);
	mq_close (inet_qid);
	mq_unlink ("/dstar_rf");
	mq_unlink ("/dstar_inet");

//	pthread_join (usb_ID, NULL);
        exit(0);
}

void	send_dsvt(void)
{
	int	length;
	int	n;
	int	ret;
        struct  repMsg  ConMsg;

	if (ConnectWait) return;
	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 = trust_db_port;
        		sndAddr.sin_addr = dbIP;
        		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;
                	sendto (udpSocket, &send_pkt, 56, 0,
                        	(struct sockaddr *)&sndAddr, sizeof(sndAddr));
		}
		FirstHeaderSend = FALSE;
	}
	length = mq_receive (rf_qid, (char *)&send_pkt, sizeof(send_pkt), NULL); 
	if (length > 0)
	{
		if (memcmp (send_pkt.ID, STREAM_ID, 4) == 0)
		{
			if (send_pkt.B_header.seq == 0x80)
			{
				ConSend();
				ConnectWait = TRUE;
				AckTimeOut = 0;
				DSTREQcnt = 0;
				Connected = 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 = trust_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;
			}
		}
	}
}

void	receive_dsvt(void)
{
	socklen_t addrlen;
	struct	packet	inet_recv;

	addrlen = sizeof(struct sockaddr_in);
	recvfrom (udpSocket, (char *)&inet_recv, sizeof(inet_recv), 0, 
		(struct sockaddr *)&sndAddr, &addrlen);
        if (memcmp ((char *)&inet_recv, "\0\0\0\0SRCREQ", 10) == 0)
        {
		sendto (udpSocket, "\0\0\0\0SRCACK UDP hole punching", 28, 0,
               		(struct sockaddr *)&sndAddr, sizeof(sndAddr));
        }
        if (memcmp ((char *)&inet_recv, "\0\0\0\0DSTREQ", 10) == 0)
        {
		if (!Connected)
		{	
                	ConnectWait = FALSE;
                	FirstHeaderSend = TRUE;
			dstPort = sndAddr.sin_port;
			dstIP = sndAddr.sin_addr;
			Connected = TRUE;
		}
        }
        if (memcmp ((char *)&inet_recv, "\0\0\0\0SRCACK", 10) == 0)
        {
		if (!Connected)
		{
			dstPort = sndAddr.sin_port;
			dstIP = sndAddr.sin_addr;
			ConnectWait = FALSE;
			FirstHeaderSend = TRUE;
			Connected = TRUE;
		}
        }

	if (!memcmp ((char *)&inet_recv.ID, STREAM_ID, 4) 
		&& !memcmp(&PacketIdSave, &inet_recv.B_header.PacketID, 2))
	{
		mq_send (inet_qid, (char *)&inet_recv, sizeof(inet_recv), 1);
		DVwait = FALSE;
	}
}

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

	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 = trust_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    NakSendTrust(char srcRptCall[8])
{
        struct  shm_form        ack_msg;

        ack_msg.type = Nak;
        memcpy (ack_msg.Rep1Call, srcRptCall, 8);
        memcpy (ack_msg.dstCall, client_call, 8);
        sndAddr.sin_family = PF_INET;
        sndAddr.sin_port = trust_db_port;
        sndAddr.sin_addr = dbIP;
        sendto (udpSocket, (char *)&ack_msg, sizeof(ack_msg), 0,
                 (struct sockaddr *)&sndAddr, sizeof(sndAddr));
        return;
}

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

void	ConSend(void)
{
        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;
	send_msg.PacketID[0] = send_pkt.B_header.PacketID[0];
        send_msg.PacketID[1] = send_pkt.B_header.PacketID[1];
	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];
	sndAddr.sin_family = PF_INET;
	sndAddr.sin_port = trust_db_port;
	sndAddr.sin_addr = dbIP;
	sendto (udpSocket, (char *)&send_msg, sizeof (struct shm_form), 0,
		(struct sockaddr *)&sndAddr, sizeof(sndAddr));
}

int main(int argc, const char **argv)
{
        int     ret;
        int     width;
        int     n;
	int	k;
        fd_set  readOk, Mask;
        struct  timeval timeout;

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

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

	FILE	*PidFile;
	int	cmdin;

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

	srand ((unsigned) time (NULL));

	/* PID の書き出し　/var/run/dsgwd_pi.pid  */
	PidFile = fopen ("/var/run/dsgwd_pi.pid","w");
	if (PidFile == NULL)
	{
		printf ("must run on supper user\n");
		exit(-1);
	}
	fprintf (PidFile, "%d\n",(int)getpid());
	fclose (PidFile);

	cmdmode = 0;
	ConnectWait = FALSE;
	FirstHeaderSend = FALSE;
	SendConnect = FALSE;
	RecvConnect = FALSE;
	AckTimeOut = 0;
	RelaySW = FALSE;
	respFlags = RespNULL;
	repeater_mode = 0;
	COS_On = FALSE;
	Connected = FALSE;
	DSTREQcnt = 0;
	DVwait = FALSE;
	NodeVendorID =  VendorID;
	NodeProductID = ProductID;

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

	if (cmdmode == 0)
	{
		cmdin = 0;
	}
	else
	{
		/* コマンド入力の名前付きのパイプ(FIFO)のオープン */
		cmdin = open ("/tmp/dsgwd_pi-cmdin", O_NONBLOCK | O_RDONLY);
		if (cmdin < 0)
		{
			printf ("ERROR FIFO file not open /tmp/dsgwd_pi-cmdin\n");
			cmdin = 0;
		}
	}

        hp = gethostbyname(TrustDomainName);
        bcopy(hp->h_addr, &trustIP, hp->h_length);

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

	/* define mq attribute */
	rf_mattr.mq_flags = O_NONBLOCK;
	rf_mattr.mq_maxmsg = 100;
	rf_mattr.mq_msgsize = sizeof(struct packet);
	rf_mattr.mq_curmsgs = 0;

	rf_qid = mq_open ("/dstar_rf", O_RDWR | O_CREAT, 
		S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &rf_mattr);

        /* define mq attribute */
        inet_mattr.mq_flags = O_NONBLOCK;
        inet_mattr.mq_maxmsg = 100;
        inet_mattr.mq_msgsize = sizeof(struct packet);
        inet_mattr.mq_curmsgs = 0;

        inet_qid = mq_open ("/dstar_inet", O_RDWR | O_CREAT,
                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &inet_mattr);

	status = pthread_create (&usb_ID, NULL, node_adapter, (void *)NULL);
	if (status)
	{
		printf ("pthread error %s\n", strerror(status));
	}

	//sockaddr_in 構造体のセット
	bzero((char *)&dstAddr, sizeof(dstAddr));
	dstAddr.sin_family = PF_INET;
	dstAddr.sin_port = trust_port;
	dstAddr.sin_addr = trustIP;

	//ソケットの生成
	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);
		CloseSocket();	
	}

	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,  128, 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, 128, 0);
       	 	buf[numrcv] = 0x00;
        	printf("%s\n",buf);
	}

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

        udpAddr.sin_family = PF_INET;
        udpAddr.sin_port = htons(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(cmdin, &Mask);
	FD_SET(rf_qid, &Mask);
        width = dstSocket;
	if (width < rf_qid) width = rf_qid;
	if (width < udpSocket) width = udpSocket;
	if (width < cmdin) width = cmdin;
	width++;
	while (1){
		readOk = Mask;
		if (ConnectWait) FD_CLR(rf_qid, &readOk);
		timeout.tv_sec = 0;
		timeout.tv_usec = 5000;		/* wait 5 m sec. */

		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;
						Connected = TRUE;
						printf ("Change to Relay Mode\n");
					}
				}
				if (DVwait)
				{
					DSTREQcnt++;
					if ((DSTREQcnt % 4) == 0)	/* 20 m sec. interval */
					{
						ReqSendSender();
					}
				}
				break;
			default:
                                if (FD_ISSET(udpSocket, &readOk))
                                {
                                        receive_dsvt();
                                }
				if(FD_ISSET(dstSocket, &readOk))
				{
                        		//パケットの受信
                        		numrcv = recv(dstSocket, (char *)&reqConMsg, sizeof(reqConMsg), 0);
					if (numrcv <= 0)
                                        {
                                                reqConMsg.reqType = ReqEnd;
                                                send (dstSocket, (char *)&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;
                        				sndAddr.sin_family = PF_INET;
                        				sndAddr.sin_port = dstPort;
                        				sndAddr.sin_addr = dstIP;
							respFlags = RespNULL;
                        				sendto (udpSocket, "\0\0\0\0SRCREQ UDP hole punching", 28, 0,
                                				(struct sockaddr *)&sndAddr, sizeof(sndAddr));
						}
                        		}
                                        else 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));
							respFlags = reqConMsg.ackConnect.RespFlags;
                                        }
                        		else if (reqConMsg.reqType == ReqConnect)
                        		{
                                		printf ("Req %2.2x %2.2x %8.8s %s %d\n", 
							reqConMsg.reqType,
							reqConMsg.reqConnect.protocol, 
							reqConMsg.reqConnect.YourCall, 
							inet_ntoa(reqConMsg.reqConnect.ip),
							ntohs(reqConMsg.reqConnect.port));
						if (PTT_On || COS_On)
						{
							NakSendTrust(reqConMsg.reqConnect.Rpt1Call);
						}
						else
						{
							PacketIdSave[0] = reqConMsg.reqConnect.PacketID[0];
							PacketIdSave[1] = reqConMsg.reqConnect.PacketID[1];
							ReqSendSender();
							DVwait = TRUE;
							DSTREQcnt = 0;
							AckSendTrust (reqConMsg.reqConnect.Rpt1Call, reqConMsg.reqConnect.PacketID);
						}
                        		}
				}
				if (FD_ISSET(rf_qid, &readOk))
				{
					send_dsvt();
				}
				if(FD_ISSET(cmdin, &readOk))
				{
                			read (cmdin, buf, 64);
                			if (memcmp (buf, "end", 3) == 0)
					{
						reqConMsg.reqType = ReqEnd;
        					send (dstSocket, (char *)&reqConMsg, sizeof(reqConMsg), 0);
        					CloseSocket();
					}
				}
				break;
		}
  	}
}

