#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <ifaddrs.h>

#define PORT_NUMBER_LOCAL 31110
#define PORT_NUMBER_TARGET 32110


void badUsage(void)
{
puts("Usage: BroadcastTester [-r] [-d] [-l localAddress] [-b broadcastAddress] [-t interval]");
puts("\t-r -> reply to broadcasts");
puts("\t-d -> daemon mode");
puts("\t-l -> set the local addrress, default is 0.0.0.0");
puts("\t-b -> set the broadcast addrress, default is 255.255.255.255");
puts("\t-t -> set the interval between broadcasts, in seconds, default is 5 seconds");
exit(1);
}


int main(int argc, char **argv)
{
struct sockaddr_in me, target, reply;
int sockSnd, sockRcv, adl, res, i, j, k, p, bnum, interval;
char buff[300], buff1[300];
char localIP[40];
char bcastIP[40];
char *lastIP;
char daemon=0, doReply=0;
char msgList[50][100];
char msgFresh[50];
unsigned char msgListLen;

char header[100];
char *header0="\n\n\n\n\n\n       BroadcastTester.11.2016.asc@isep.ipp.pt\n\n\n\n\n";
char localList[50][50];
unsigned char localListLen;
struct ifaddrs *ifAddrs, *ifAux;

strcpy(localIP,"0.0.0.0");
strcpy(bcastIP,"255.255.255.255");
interval=5;

puts(header0);
for(i=1;i<argc;i++)
	{
	if(!strcmp(argv[i],"-l")) {
		i++;strcpy(localIP,argv[i]); }
	else
	if(!strcmp(argv[i],"-b")) {
		i++;strcpy(bcastIP,argv[i]); }
	else
	if(!strcmp(argv[i],"-t")) {
		i++;interval=atoi(argv[i]); }
	else
	if(!strcmp(argv[i],"-r")) { doReply=1; }
	else
	if(!strcmp(argv[i],"-d")) { daemon=1; }
	else badUsage();
	
	}
if(!interval) badUsage();

sockSnd=socket(AF_INET,SOCK_DGRAM,0);
sockRcv=socket(AF_INET,SOCK_DGRAM,0);
adl=1; ioctl(sockSnd,FIONBIO,&adl);
adl=1; ioctl(sockRcv,FIONBIO,&adl);
adl=1; setsockopt(sockSnd,SOL_SOCKET, SO_BROADCAST, &adl, sizeof(adl));
adl=1; setsockopt(sockRcv,SOL_SOCKET, SO_BROADCAST, &adl, sizeof(adl));
adl=1; setsockopt(sockSnd,SOL_SOCKET, SO_REUSEADDR, &adl, sizeof(adl));
adl=1; setsockopt(sockRcv,SOL_SOCKET, SO_REUSEADDR, &adl, sizeof(adl));
adl=sizeof(me);
bzero(&me,adl);
me.sin_family=AF_INET;
me.sin_addr.s_addr=inet_addr(localIP);
me.sin_port=htons(PORT_NUMBER_LOCAL);
if(bind(sockSnd,(struct sockaddr *)&me,adl)==-1) {
		printf("Bind to local address %s:%i failed\n",localIP,PORT_NUMBER_LOCAL);
		close(sockSnd);close(sockRcv);
		exit(1); }
bzero(&me,adl);
me.sin_family=AF_INET;
me.sin_addr.s_addr=inet_addr(localIP);
me.sin_port=htons(PORT_NUMBER_TARGET);
if(bind(sockRcv,(struct sockaddr *)&me,adl)==-1) {
		printf("Bind to local address %s:%i failed\n",localIP,PORT_NUMBER_TARGET);
		close(sockSnd);close(sockRcv);
		exit(1); }

bzero(&target,adl);
target.sin_family=AF_INET;
target.sin_addr.s_addr=inet_addr(bcastIP);
target.sin_port=htons(PORT_NUMBER_TARGET);

getifaddrs(&ifAddrs); localListLen=0;
ifAux=ifAddrs;
while(ifAux) 
	{
	if (ifAux->ifa_addr && ifAux->ifa_addr->sa_family == AF_INET)
		{
		strcpy(localList[localListLen],
			inet_ntoa(((struct sockaddr_in *)ifAux->ifa_addr)->sin_addr));
		localListLen++;
		}
	ifAux=ifAux->ifa_next;
	}

freeifaddrs(ifAddrs);

bnum=0;
msgListLen=0;


if(daemon)
	{
	if(fork()) {
		close(sockSnd);close(sockRcv);exit(0);}
	close(0);close(1);close(2);
	}


while(1) {
	  bnum++; if(bnum==10000) bnum=0;
	  sprintf(buff,"BCAST %i",bnum);
	  sprintf(header,"Sent broadcast number %i to %s:%i\n, from local address %s:%i\n",bnum, 
		bcastIP,PORT_NUMBER_TARGET,localIP,PORT_NUMBER_LOCAL);
	  sendto(sockSnd,buff,strlen(buff),0,(struct sockaddr *)&target,adl);
	  for(j=0;j<msgListLen;j++) if(msgFresh[j]>0) msgFresh[j]--;
          sleep(interval);
	  while(1)
	        {
		// receive broadcasts
		res=recvfrom(sockRcv,buff,300,0,(struct sockaddr *)&reply,(socklen_t *)&adl);
		if(res<1) break;
		buff[res]=0; lastIP=inet_ntoa(reply.sin_addr);
		sprintf(buff1,"%s %s",lastIP,buff);
		p=0; while(buff1[p] && buff1[p]!=32) p++;
		p++; while(buff1[p] && buff1[p]!=32) p++;
		for(j=0;j<msgListLen;j++)
			{
			if(!strncmp(msgList[j],buff1,p)) break;
			}
		strcpy(msgList[j],buff1);
		msgFresh[j]=5;
		for(k=0;k<localListLen;k++) if(!strcmp(localList[k],lastIP))
			{
			strcat(msgList[j]," (LOCAL)");
			break;
			}
		if(j==msgListLen) msgListLen++;
		if(doReply) {
			sprintf(buff1,"REPLY to %s from %s",buff,inet_ntoa(reply.sin_addr));
	  		sendto(sockRcv,buff1,strlen(buff1),0,(struct sockaddr *)&reply,adl);
			}
		}
	  while(1)
	        {
		// receive replies
		res=recvfrom(sockSnd,buff,300,0,(struct sockaddr *)&reply,(socklen_t *)&adl);
		if(res<1) break;
		buff[res]=0; lastIP=inet_ntoa(reply.sin_addr);
		sprintf(buff1,"%s %s",lastIP,buff);
		p=0; while(buff1[p] && buff1[p]!=32) p++;
		p++; while(buff1[p] && buff1[p]!=32) p++;
		for(j=0;j<msgListLen;j++)
			{
			if(!strncmp(msgList[j],buff1,p)) break;
			}
		strcpy(msgList[j],buff1);
		msgFresh[j]=5;
		for(k=0;k<localListLen;k++) if(!strcmp(localList[k],lastIP))
			{
			strcat(msgList[j]," (LOCAL)");
			break;
			}
		if(j==msgListLen) msgListLen++;
		}
	if(!daemon) {
		puts(header0);puts(header);
		puts("CURRENT ----------------------------------------------------------------");
		for(j=0;j<msgListLen;j++) if(msgFresh[j]) puts(msgList[j]);
		puts("HISTORIC ---------------------------------------------------------------");
		for(j=0;j<msgListLen;j++) if(!msgFresh[j]) puts(msgList[j]);
		puts("------------------------------------------------------------------------");
		}
	}
close(sockSnd);close(sockRcv);
}





