/***************CS244a PROJECT-2****************/

/***************JATINDER PAL SINGH****************/
/**************jatinder@stanford.edu****************/


#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>  
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>


// Constant definitions
#define NUM_ARGUMENTS       3         /*ARGUMENT TO main() COUNT*/
#define MAX_NEIGHBORS       16        /*MAX # NEIGHBORS A ROUTER CAN HAVE*/
#define MAX_ROUTERS         64        /*MAX # ROUTERS IN TOPOLOGY*/
#define MAX_HOSTNAME_LEN    64        /*MAX LENGTH OF A ROUTER*/
#define SUCCESS             0         /*DEFINED FOR exit() CALL*/
#define FAILURE             1         /*      "      "         */
#define MAX_WAIT_TIME       10        /*MAX TIME A ROUTER SHOULD WAIT
					TILL IT RECEIVES NO LSP*/


// Type definitions
/********STRUCTURE OF A ROUTER NEIGHBOR********/
typedef struct
{
  int                cost;   /*LINK COST*/
  struct _routerT   *router; /*POINTER TO PAREN ROUTER STRUCTURE*/
} neighborT;


/********STRUCTURE OF A ROUTER ENTRY IN TOPOLOGY********/
typedef struct _routerT
{
  char         name[MAX_HOSTNAME_LEN];    /*ROUTER NAME*/
  int          num_neighbors;             /* # OF NEIGHBORS OF THE ROUTER*/
  neighborT    neighbors[MAX_NEIGHBORS];  /* ARRAY OF ENIGHBORS*/
} routerT;


/********NETWORK TOPOLOGY********/
typedef struct
{
  int       num_routers;                 /* # ROUTERS IN TOPOLOGY */
  routerT   routers[MAX_ROUTERS];        /* ARRAY OF ROUTERS IN TOPOLOGY*/
  unsigned short  port;                  /* TCP PORT # */
} topologyT;
// Note: routers[0] is the router on which this program is running


/********STRUCTURE OF ENTRY IN ROUTING TABLE********/
typedef struct
{
  char name[MAX_HOSTNAME_LEN];
  char prev_hop[MAX_HOSTNAME_LEN];  /*PREV HOP FROM DEST*/
  char next_hop[MAX_HOSTNAME_LEN];  /*NEXT HOP FROM SRC TO REACH DEST*/
  int cost;
  char status;                      /*STATUS INDICATES IF THE RT ENTRY HAS BEEN
			            UPDATED OF NOT*/
} RoutingTableEntry;


/* function prototypes; documentation with & in function bodies*/

void LoadStartupFile(topologyT *topology, char *startup_filename);
u_long resolveHostAddr(char *hostname);
int bindMyAddr(u_long servInAddr, unsigned short TCP_PORT);
u_long GetPeer (int sockfd);
int acceptActive(u_long servInAddr, unsigned short TCP_PORT);
int connectPassive(u_long servInAddr, unsigned short TCP_PORT);
void RunConnectPhase(topologyT *topology, int *sockfds);
void constructWriteString( routerT *me, char *linkStateInfo, char
			   *tempbuff);
void setFdsForSelect(fd_set *fd_set_ptr, struct timeval *timeoutptr, int
		     *sockfds, int *maxfdptr, int num_fds, int waittime);
char *retrieveInfo(char *destination, char *buffer, char c, int maxbufflen);
void RunAlgorithmPhase(topologyT *topology, int *sockfds);
int UpdateRoutingT(topologyT *topology, RoutingTableEntry *RT);
void copyframe(RoutingTableEntry *dest, RoutingTableEntry *src);
void WriteRoutingTable(RoutingTableEntry *RT, int num_entries);
void SortRoutingTable(RoutingTableEntry *RT, int n);
void WriteTableToFile(RoutingTableEntry* RT, int num_entries, char
		      *TableFile, char *routername);
void PrintTopology(topologyT *topology);
void PrintRouter(routerT *router);



/*********************************MAIN****************************************/
int main(int argc, char *argv[])
{
  char *startup_filename;              /*INPUT FILE TO BE READ BY THE ROUTER*/
  topologyT topology;          /*NETWORK TOPOLOGY CONTAINING ROUTERS, NEIGHBORS
			       AND LINK COST INFOS*/
  int sockfds[MAX_NEIGHBORS];  /*SOCKFDS FOR TCP CONNECTIONS TO THE NEIGHBORS*/
  int num_entries_RT;                             /*#ENTRIES IN ROUTING TABLE*/
  RoutingTableEntry RoutingT[MAX_ROUTERS];    /*THE ROUEING TABLE*/
  char TableFile[MAX_HOSTNAME_LEN + 6];/*THE FILE TO WHICH RT WILL BE WRITTEN*/


   if(argc != NUM_ARGUMENTS)
   {
      printf("Usage: router <startup file> <port>\n");
      exit(FAILURE);
   }
   startup_filename = argv[1];                    /*READ THE IPUT FILE NAME*/
   topology.port = (unsigned short)atoi(argv[2]); /*READ PORT #*/
   
   /*RETRIVE TOP[OLOGY INFO FROM THE TOPOLOGY SPECIFIED IN INPUT FILE*/
   LoadStartupFile(&topology, startup_filename);
   printf("\tROUTER: %s\n\n", topology.routers[0].name);
   printf("\tPRINTING INITIAL TOPOLOGY\n");
   PrintTopology(&topology);
   printf("\n");

   /*UPDATE ROUTING TABLE AND PRINT*/
   num_entries_RT = UpdateRoutingT(&topology, RoutingT);
   printf("\tPRINTING INITIAL ROUTING TABLE\n");
   WriteRoutingTable(RoutingT, num_entries_RT);
   printf("-------------------------------------------------------------------------\n");

   /*CONNECTION PHASE*/
   RunConnectPhase(&topology, sockfds);

   /*ALGORITHM PHASE*/
   RunAlgorithmPhase(&topology, sockfds);

   /*PRINT FINAL TOPOLOGY*/
   printf("\tTHE FINAL TOPOLOGY IS:\n");
   PrintTopology(&topology);
printf("\n\n");

   /*UPDATE, SORT AND PRITNT THE FINAL RT*/
   num_entries_RT = UpdateRoutingT(&topology, RoutingT);
   SortRoutingTable(RoutingT, num_entries_RT);
   printf("\tPRINTING FINAL ROUTING TABLE\n");
   WriteRoutingTable(RoutingT, num_entries_RT);

   /*WRITE THE RT TO FILE <HOSTNAME>.table*/
   strcpy(TableFile, topology.routers[0].name);
   strcat(TableFile, ".table");
   WriteTableToFile(RoutingT, num_entries_RT, TableFile, 
		    topology.routers[0].name);

   return 0;
}
/************************END OF MAIN********************************/



/****************FUNCTION BODIES************************/



/********READS THE INPUT FILE CONTAINING DESCRIPTION OF ROUTER'S NEIGHBORS
	 AND LINK COSTS********/

void LoadStartupFile(topologyT *topology, char *startup_filename)
{
   FILE *startup_file = fopen(startup_filename, "r");
   int cost;
   int num_routers;
   routerT *me, *neighbor;

   if(startup_file == NULL)
   {
      printf("Error: Couldn't load startup file!\n");
      exit(FAILURE);
   }
   
   me = &(topology->routers[0]); /*POINTER TO ROUTER STRUCTURE*/
   me->num_neighbors = 0;

   fscanf(startup_file, "%s\n", me->name);
   topology->num_routers = 1;
   num_routers = 1;
   
   
   while(fscanf(startup_file, "%s %d\n",
		topology->routers[num_routers].name, &cost) != EOF)
   {
     
     neighbor = &(topology->routers[num_routers]); /*POINTER TO NEIGHBOR*/
     
     /*UPDATE THE ROUTER STRUCTURE*/
     me->neighbors[me->num_neighbors].router = neighbor;
     me->neighbors[me->num_neighbors].cost = cost;
     me->num_neighbors++;
     
     /*UPDATE THE NEIGHBOR STRUCTURE*/
     neighbor->num_neighbors = 1;
     neighbor->neighbors[0].router = me;
     neighbor->neighbors[0].cost = cost;
      
     num_routers++;
   }
   topology->num_routers = num_routers;
}



/********DOES DNS LOOKUP: CONVERTS NAME STRING TO U_LONG ADDRESS********/
u_long resolveHostAddr( char *hostname )
{
  struct hostent *hostentPtr;   /* used with gethostbyname() */
  struct in_addr inaddr;
  u_long hostInAddr;

  hostentPtr = gethostbyname(hostname);
  if ( hostentPtr == NULL ) {
    printf("%s%s\n", "Failed to resolve router name: ", hostname);
    exit(FAILURE);
  }
  bcopy( hostentPtr->h_addr, (char *) &inaddr, hostentPtr->h_length );
  hostInAddr = inaddr.s_addr;

  return hostInAddr;
}

/********OPENS A SOCKET; BINDS THE ROUTER ADDR. TO THE SOCKET AND LISTENS
	 ON THE SOCKET********/
int bindMyAddr(u_long servInAddr, unsigned short TCP_PORT)
{
  int sockfd;
  struct sockaddr_in serv_addr;
  	
  if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {
      printf("%s\n", "ERROR: failed to open socket");
      exit(FAILURE);
    }
  
  bzero((char *) &serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = htonl(servInAddr);
  serv_addr.sin_port = htons(TCP_PORT);

  if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
    {
      printf("%s\n", "ERROR: failed to bind socket");
      exit(FAILURE);
    }
  
  listen(sockfd, 5);
  return sockfd;
} 


/********RETURNS THE U_LONG IP ADDRESS OF A PEER TO THE ROUTER********/
u_long GetPeer (int sockfd)
{
 
  struct sockaddr peer_addr;
  struct sockaddr_in peer_addr_in ;
  socklen_t peer_addr_len = sizeof (peer_addr) ;


  if (getpeername (sockfd, &peer_addr, &peer_addr_len) == -1)
    {
      perror ("getpeername() failed") ;
      exit (FAILURE) ;
    }

  memcpy (&peer_addr_in, &peer_addr, sizeof(peer_addr_in)) ;

  return (peer_addr_in.sin_addr.s_addr);
}



/********ACCEPTS CONNECTION FROM ACIVE NEIGHBORS; THE NEIGHBORS HAVING
	 LOWER IP ADDRS THAN THE ROUTER ARE ACTIVE NEIGHBORS********/

int acceptActive( int sockfd, unsigned short TCP_PORT)
{
  int clilen, newsockfd;
  struct sockaddr_in cli_addr;

  clilen = sizeof(cli_addr);

  /*ACCEPTS CONNECTION FROM A ACTIVE NEIGHBOR*/
  newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
  if (newsockfd < 0)
    {
      perror("ERROR: router.cc: acceptActive() failed to accept connection");      
      exit(FAILURE);
    }

  return newsockfd;
}
      


/********CONNECTS TO PASSIVE NEIGHBORS; THE NEIGHBORS HAVING HIGHER IP
	 ADDR THAN THE ROUTER ARE PASSIVE NEIGHBORS********/
int connectPassive(u_long servInAddr, unsigned short TCP_PORT)
{
  int sockfd;
  struct sockaddr_in serv_addr;
  int retval ;
  //struct sockaddr_in peer_addr_in ;

  /*TRY TO ESTABLISH CONNECTION TILL THE PASSIVE END ACCEPTS CONNECTION*/
  do
    {
      if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
	{
	  printf("%s\n", "ERROR: router.cc: socket() failed");
	  exit(FAILURE);
	}
    
      bzero((char *) &serv_addr, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_addr.s_addr = htonl(servInAddr);
      serv_addr.sin_port = htons(TCP_PORT);

      retval = connect(sockfd, (struct sockaddr *) &serv_addr,
		       sizeof(serv_addr)) ;

      if (retval == 0)
	{
	  break ;
	}

      if ((retval != -1) || (errno != ECONNREFUSED))
	{
	  perror("ERROR: router.cc: connect() failed");
	  exit (FAILURE) ;
	}
      close (sockfd) ;

      sleep(1); /*SLEEP FOR A SECOND AND TRY AGAIN, IF CONNECT FAILS*/
    } while (retval == -1) ;

  //  memcpy (&peer_addr_in, &serv_addr, sizeof(peer_addr_in)) ;
  //  GetPeer (sockfd) ;
  return sockfd;
}



/********ESTABLISHES CONNECTIONS BETWEEN ROUTER AND NEIGHBORS********/
void RunConnectPhase(topologyT *topology, int *sockfds)
{
  int i;
  int sockfd;
  u_long myInAddr, currInAddr; 
  routerT *me;
  u_short activeNb = 0;
  u_long *neighborInAddr;
  u_long passiveInAddr;
  int currsockfd;
  int j;

 
  me = &(topology->routers[0]);  /*POINTER TO ROUTERS STRUCTURE*/
  neighborInAddr = (u_long *)malloc (sizeof(u_long) * me->num_neighbors);
  myInAddr = resolveHostAddr( me->name ); /*ROUTER'S U_LONG IP ADDR*/

  /********OPEN SOCKET, BIND AND LISTEN********/
  sockfd = bindMyAddr( myInAddr, topology->port );

  /********RESOLVE ADDRS OF NEIGHBORS********/
  for (i=0; i < me->num_neighbors; i++)
    {
      neighborInAddr[i] = resolveHostAddr( me->neighbors[i].router->name );
      if ( neighborInAddr[i] < myInAddr )   
	activeNb ++;
    }
	
  /********ACCEPT CONNECTIONS FROM ALL ACTIVE NEIGHBORS********/
  for (i=0; i < activeNb; i++)
    {
      currsockfd = acceptActive(sockfd, topology->port);
      currInAddr = GetPeer (currsockfd) ;
      j = 0;
      while (neighborInAddr[j] != currInAddr) 
	j++;
      if ( j >= me->num_neighbors )
	{
	  printf("Neighbor not found in the list\n");
	  exit(FAILURE);
	}
      sockfds[j] = currsockfd;

    }
 


  /********now begin connecting to passive ends********/
  for (i=0; i < me->num_neighbors; i++)
    {
      passiveInAddr = resolveHostAddr( me->neighbors[i].router->name );
      if ( passiveInAddr > myInAddr )   
	{
	  sockfds[i] = connectPassive(passiveInAddr, topology->port);

	} 
    }
} 



/********CONTRUCTS THE LSP FOR WRITING TO NEIGHBORS********/
void constructWriteString( routerT *me, char *linkStateInfo, char *tempbuff)
{
  int i;
  sprintf( linkStateInfo, "%s:", me->name );
  for(i = 0; i < me->num_neighbors; i++)
    {
      sprintf( tempbuff, "(%s;%d)", me->neighbors[i].router->name, me->neighbors[i].cost );
      strcat(linkStateInfo, tempbuff);
    }
  sprintf(tempbuff, "%s", "$");
  strcat(linkStateInfo, tempbuff);
  
}


/********SETS THE SOCKFDS TO BE MONITORED FOR READING********/
void setFdsForSelect(fd_set *fd_set_ptr, struct timeval *timeoutptr, int
		     *sockfds, int *maxfdptr, int num_fds, int waittime)
{
  int i;

  timeoutptr->tv_sec = waittime; 
  timeoutptr->tv_usec = 0;
  *maxfdptr = 0;

  FD_ZERO(fd_set_ptr);
  for(i = 0; i < num_fds; i++)
    {
      FD_SET(sockfds[i], fd_set_ptr);
      if (*maxfdptr < sockfds[i]) *maxfdptr = sockfds[i];
    }  

  (*maxfdptr)++;
}



/********PARSES THE LSP FOR RELEVANT INFORMATION********/
char *retrieveInfo(char *destination, char *buffer, char c, int maxbufflen)
{
  char *p;
  char *temp;
  char *ret;
  int n;

  temp = (char *)malloc(sizeof(char)*maxbufflen);

  p = strchr(buffer, (int)c);  /*SEARCH FOR CHAR 'c' IN THE STRING*/
  n = p - buffer;              /*LENGTH OF THE REQUIRED FIELD*/
  memcpy(temp, buffer, n);     /*COPY REQUIRED FIELD*/
  *(temp + n) = '\0';          /*TERMINATE WITH NULL CHARACTER*/
  strcpy(destination, temp);   /*STORE IN DESTINATION*/
  ret = buffer+n;              /*BEGINNING OF NEW INFORMATION*/

  if ( (c==':')||(c==')') ) 
    {
      ret ++;
      if ( *ret != '$' ) ret++;    //there are neighbors to be read
    }
  else 
    if (c==';') 
      ret++;                         //link cost 

  return ret;	 
}

  

/********UPDATES TOPOLOGY AFTER RECEIVING LSP********/
int UpdateTopology( topologyT *topology, char *linkStateInfo, int
		    maxbufflen, int *inforecvdb4)
{
   char *tempbuff;
   char *begin = linkStateInfo; /*POINTER TO LSP*/
   char *buffer = linkStateInfo;/*POINTER TO LSP*/
   int LSPseenb4;               /*BOOLEAN INDICATING IF THE LSP HAS BEEN
				  SEEN B4*/
   int num_neighbors = 0;       /*NUMBER OF NEIGHBORS OF THE PRINCIPAL
				  ROUTER IN LSP*/
   int i = 0;                   /*INDEX OF PRINCIPAL ROUTER IN TOPOLOGY*/
   int rtrindex = 0;            /*INDEX OF NEIGHBOR IN TOPOLOGY*/
   int nbrindex = 0;            /*INDEX OF LISTING OF PRINCIPAL ROUTER OF
				  LSP IN THE LIST OF NEIGHBORS OF A NEIGHBOR*/
   routerT *neighbor;           /*NEIGHBOR OF PRINCIPAL LSP ROUTER*/   
   

   tempbuff = (char *)(malloc(sizeof(char)*(MAX_HOSTNAME_LEN + 1)));

   /*RETIRIEVE ROUTER NAME*/
   begin = retrieveInfo(tempbuff, buffer, ':', maxbufflen);
   buffer = begin;
   LSPseenb4 = 1;

   /*SEARCH FOR THE ROUTER IN THE TOPOLOGY*/
   i = 0;
   while ( (i < topology->num_routers)&&(strcmp(tempbuff,
					     topology->routers[i].name)!= 0) ) 
     i++;

   if (inforecvdb4[i] == 0)   /*IF AN LSP CONTAINING THE ROUTER INDEXED-i
				IN TOPOLOGY HAS NOT YET ARRIVED THEN..*/
     {

       if (i == topology->num_routers) 
	 {
	   /* ROUTER NOT FOUND IN TOPOLOGY*/	   
	   strcpy( topology->routers[i].name, tempbuff );
	   topology->num_routers++;
	 }
               
       /*UPDATE THE NEIGHBORS of THE HOST*/
       topology->routers[i].num_neighbors = 0;
       num_neighbors = 0;
       while (*buffer != '$' )
	 {
	   /* retrieve neighbor name*/
	   begin = retrieveInfo(tempbuff, buffer, ';', maxbufflen);
	   buffer = begin;  
	   
	   /* locate the neighbor name in topology*/
	   rtrindex = 0;                  /*index of neighbor in topology*/
	   while ((rtrindex < topology->num_routers) && 
		  (strcmp(tempbuff, topology->routers[rtrindex].name) != 0))
	     rtrindex++;
	   
	   if (rtrindex == topology->num_routers)   
	     {
              /* neighbor not found in list; so add it to the end of the list*/
	       strcpy( topology->routers[rtrindex].name, tempbuff );
	       topology->routers[rtrindex].neighbors[0].router =
		 &(topology->routers[i]);
	       topology->routers[rtrindex].num_neighbors = 1;	       
	       topology->num_routers++;
	     }
	   
	   neighbor = &(topology->routers[rtrindex]); /* address of
							 neighbor in the list*/
	   
	   /**CREATE A LINK TO THE NEIGHBOR IN THE HOST NEIGHBOR LIST*/
	   topology->routers[i].neighbors[num_neighbors].router = neighbor; 
	   nbrindex = 0;

	   /****UPDATE THE NEIGHBOR LIST OF THE NEIGHBOR**/
	   while ( (nbrindex < neighbor->num_neighbors) && 
		  (strcmp(neighbor->neighbors[nbrindex].router->name,
			  topology->routers[i].name) != 0) )
	     {
	       nbrindex++;
	     }

	   if (nbrindex == neighbor->num_neighbors)   /*host not found in 
						     neighbor's neighbor list*/
	     {
	       neighbor->neighbors[nbrindex].router = &(topology->routers[i]);
	       neighbor->num_neighbors++;
	     }
	   
	   /* retrieve neighbor cost*/
	   begin = retrieveInfo(tempbuff, buffer, ')', maxbufflen);
	   buffer = begin;    
	   topology->routers[i].neighbors[num_neighbors].cost = atoi(tempbuff);
	   neighbor->neighbors[nbrindex].cost = atoi(tempbuff);
	   
	   /* increment no of neighbors*/
	   num_neighbors++;
	   topology->routers[i].num_neighbors++;
	 }
       inforecvdb4[i] = 1;   /*THE LSP CONTAING ROUTER INDEXED-i IN
			       TOPOLOGY HAS NOW BEEN SEEN & HENCE SHOULD
			       BE DROPPED IF RECEIVED IN FUTURE*/
       LSPseenb4 = 0;        /*THIS LSP WAS NOT SEEN BEFORE*/ 
     }
   
   return LSPseenb4;
}



/********WRITE LSP, READ LSPs & RUN THE DIJKSTRA'S ALGORITHM ON CURRENT
	 TOPOLOGY********/
void RunAlgorithmPhase(topologyT *topology, int *sockfds)
{
  char *linkStateInfo, *tempbuff, *costbuff;
  char **readbuff;                      /*CHARACTER BUFFER FOR EACH NEIGHBOR*/
  routerT *me;                          /*POINTER TO ROUTER STRUCTURE*/
  int maxbufflen;                       /*MAX LENGTH OF AN LSP*/
  int nwritten, nread;
  int buffsize;
  int i,j;
  fd_set fdvar;
  int selectval;
  int maxfd = 0;
  struct timeval timeout;                /*USED FOR SELECT() CALL*/
  int LSPseenb4;   /*BOOLEAN INDICATING IF THE LSP HAS BEEN SEEN B4*/
  int *readbufflen ; /*LENGTH OF DATA THAT HAS BEEN READ FROM EACH NEIGHBOR*/
  int num_entries_RT;                     /*# OF ENTRIES IN RT*/
  RoutingTableEntry RoutingT[MAX_ROUTERS]; /*THE ROUITNG TABLE*/
  int inforecvdb4[MAX_ROUTERS];          /*INDICATES IF LSP WITH PRINCIPAL
					   ROUTER INDEXED-i IN TOPOLOGY
					   HAS BEEN RECEIVED B4*/
  int fds_for_select[MAX_NEIGHBORS];     /*ARRAY OF SOCKFDS THAT SHOULD BE
					   MONITORED FOR READ*/
  int num_fds;                           /* # FDS THAT SHOULD BE MONITORED
					    FOR READING*/
  struct timeval time;                   /* USED TO GET TIME OF DAY*/
  long currtime;                         /* CURRENT TIME */
  int waittime;                          /* TIME WAITED IN SELECT CALL*/
  long difftime;                         /* TIME BY WHICH WAITIME OF
					    SELECT CALL SHOULD BE ADJUSTED*/
  int waittimeupdated;                  /*INDICATES IF WAITIME FOR SELECT()
					  CALL HAS BEEN UPDATED*/
  
  me = &(topology->routers[0]);
  maxbufflen = (me->num_neighbors + 1)*(MAX_HOSTNAME_LEN + 5); /*BUFFER LENGTH
								 FOR LSP*/
  linkStateInfo = (char *)(malloc(sizeof(char)*maxbufflen));  /*STORES LSP*/

  /*BUFFER FOR NEIGHBORS OF ROUTER; FOR READING LSP*/
  readbuff = (char **)(malloc(sizeof(char *) * me->num_neighbors));

  /*ALLOCATE BUFFERS FOR STORING LSP; TO EACH NEIGHBOR*/
  for (i=0; i< maxbufflen; i++)
    readbuff[i] = (char *)malloc(sizeof(char) * maxbufflen);

  tempbuff = (char *)(malloc(sizeof(char)*(MAX_HOSTNAME_LEN + 8)));
  /*BUFFER FOR EACH FIELD IN LSP*/

  costbuff = (char *)(malloc(sizeof(char)*5)); /*BUFFER FOR STORING LINK COST*/    readbufflen = (int *) malloc (sizeof (int) * me ->  num_neighbors) ;

  /*INITIALIZING READBUFFER*/
  for (i=0; i<me->num_neighbors; i++)
    for (j=0; j<maxbufflen; j++)
      readbuff[i][j] = '\0';

  /*INITIALIZING LENGTH OF DATA READ FORM EACH NEIGHBOR*/
  for (i=0 ; i < me->num_neighbors ; i++)
      readbufflen [i] = 0 ;

  /*CONTRUCT LSP*/
  constructWriteString(me, linkStateInfo, tempbuff);
  buffsize = strlen(linkStateInfo);

  // write to the file descriptors; dispatch the link state info
  for(i = 0; i < me->num_neighbors; i++)
    {
      nwritten = write(sockfds[i], linkStateInfo, buffsize);
      if (nwritten <= 0)
	{
	  perror("ERROR: router.cc: failed to write");
	  exit(FAILURE);
	}	

    }
 
/*********READING-LSP PHASE********/

  /*SET THE FDS_TO_BE_MONITORED_LIST TO ALL NEIGHBOR FDS*/
  num_fds = me->num_neighbors;
  for (i = 0; i<MAX_NEIGHBORS; i++)
    {
      if (i < num_fds) 
	fds_for_select[i] = sockfds[i];
      else
	fds_for_select[i] = 0;
    }	  
  /*INITIALIZE SELECT() WAITTIME TO 10 SECS*/
  waittime = MAX_WAIT_TIME;
 
  /*CALL SELECT TO CHECK IF READ DATA IS AVAILABLE*/
      setFdsForSelect(&fdvar, &timeout, fds_for_select, &maxfd, num_fds, 
		      waittime);
      selectval = select(maxfd, &fdvar, (fd_set *) 0, (fd_set *) 0, &timeout);

      if (selectval == -1)
	{
	  perror ("ERROR: router.cc: select() failed") ;
	  exit (FAILURE) ;
	}

      if (selectval == 0)
	{
	  /*UNABLE TO READ ANY LSP*/
	  printf ("WARNING: router.cc: select() timedout: no data recvd.\n") ;
	  exit(FAILURE) ;
	}

      /*NO INFO RECEIVED FROM ANY ROUTER IN TOPOLOGY*/ 
      for (i=0; i<MAX_ROUTERS; i++)
	inforecvdb4[i] = 0;   
      /*ROUTER ALREADY HAS INFO ABOUT LINK STATES TO NEIGHBORS*/
      inforecvdb4[0] = 1;	

      /*READ DATA TILL SELECT() TIMES OUT*/

  do
    {
      waittimeupdated = 0; /*TO BE SET TO 1 WHEN WAITIME IS UPDATED*/

      for(i = 0; i < me->num_neighbors; i++) /*READ FROM EACH NEIGHBOR*/
	{
	  if (readbufflen[i] >=0 )    /*IF THE PEER NEIGHBOR QUITS
					readbufflen IS SET TO -1*/ 
	    {	      
	      if (FD_ISSET(sockfds[i], &fdvar)!=0) 
		{
		  /*SOCKFD FOR NEIGHBOR-i IS READY FOR READING; READ A
		    CHARACTER FROM THE CORRESPONDING SOCKET*/
		  nread = read(sockfds[i], &readbuff[i][readbufflen[i]], 1);
		  if (nread < 0)
		    {
		      perror("ERROR: router.cc: read() failed");
		      exit(FAILURE);
		    }
		  if (nread == 0)
		    {
		      /*SINCE READ IS A BLOCKING CALL RETURN VALUE OF 0
			IMPLIES THAT THE CONNECTION HAS ALREADY BEEN
			TERMINATED BY PEER; SO CLOSE THE SOCKET*/
		      close(sockfds[i]);
		      readbufflen[i] = -1;
		      num_fds--;
		      if (waittimeupdated == 0)
			{
			  waittime -= (int)difftime; /*UPDATE THE WAITIME
						       IF EXIT FROM SELECT
						       WAS CAUSED BY A
						       PEER CLOSING THE 
						       CONNECTION*/
			  waittimeupdated = 1; /*WAITIME HAS BEEN UPDATED*/
			}
		    }
		  else
		    {
		      /*READ RETURNS +VE VAULE*/
		      readbufflen[i] ++ ;      /*ONE MORE CHARACTER READ*/     
		      if (readbuff[i][readbufflen[i]-1] == '$')
			{
			  /*COMPLETE LSP READ; PARSE INFO AND UPDATE TOPOLOGY*/
			  LSPseenb4 = UpdateTopology ( topology, readbuff[i],
					   maxbufflen, inforecvdb4  );
			  
			  if (!LSPseenb4)
			    {
			      /*THE LSP HAS NOT ARRIVED B4 SO SEND IT TO
				ALL NEIGHBORS*/

			      for(j = 0; j < me->num_neighbors; j++)
				{
				  /*SEND LSP TO ALL NEIGHBORS EXPECT FOR
				    THE NEIGHBOR FROM WHICH IT WAS RECEIVED*/
				  if (j != i)
				    {
				      nwritten = write(sockfds[j],readbuff[i],
						       strlen(readbuff[i]));				      
				      if (nwritten <= 0)
					{
					  printf("%s\n", 
						 "ERROR: failed to write");
					  exit(FAILURE);
					}	
				    }
				}
			      /*PRINT TOPOLOGY*/
			      printf("\n");
			      printf("\t PRINTING TOPOLOGY AFTER RECEINVING LSP:\n%s \n\tFROM %s\n\n", readbuff[i], me->neighbors[i].router->name);
			      PrintTopology(topology);

			      /*UPDATE ROUTING TABLE BY INFO RECVD THRU LSP*/
			      num_entries_RT = UpdateRoutingT(topology,
							      RoutingT);

			      /*PRINT ROUTING TABLE*/
			      printf("\n\t PRINTING ROUTING TABLE\n");
			      WriteRoutingTable(RoutingT, num_entries_RT);
			      printf("------------------------------------------------------------------------\n");
			    }
			  /*INITIALIZE BUFFER PARAMETERS FOR NEXT NEIGHBOR*/
			  readbufflen[i] = 0;		  
			  for (j=0; j<maxbufflen; j++)
			    readbuff[i][j] = '\0';
			  
			}
	      
		    }
		}
	    }
	}

      /*PREPARE THE NEW LIST TO BE MONITORED FOR READ; NOTE THAT SOME
	PEERS HAVE CLOAED CONNECTIONS*/
      i = j = 0;
      while (i<num_fds)
	{
	  if (readbufflen[j] >= 0)
	    {
	      fds_for_select[i] = sockfds[j];
	      i++;
	    }
	  j++;
	}
 
      setFdsForSelect(&fdvar, &timeout, fds_for_select, &maxfd, num_fds, 
		      waittime);
            
      if ( gettimeofday(&time, NULL) < 0 )
	{
	  perror("ERROR: router.cc: failed to get time of day");
	  exit(FAILURE);
	}
      currtime = time.tv_sec;
      selectval = select(maxfd, &fdvar, (fd_set *) 0,
			 (fd_set *) 0, &timeout);
      
      if ( gettimeofday(&time, NULL) < 0 )
	{
	  perror("ERROR: router.cc: failed to get time of day");
	  exit(FAILURE);
	}
      difftime = (time.tv_sec - currtime);/*GIVES THE TIME WAITED IN SELECT()*/
      
      if (selectval == -1)
	{
	  perror ("ERROR: router.cc: select() failed") ;
	  exit (FAILURE) ;
	}

    } while (selectval > 0) ; /*RUN THE LOOP TILL SELECT RETURNS A +VE VALUE*/

  /*CLOSE ALL SOCKFDS; SOME MAY HAVE ALREADY BEEN CLOSED*/
  for(i = 0; i < me->num_neighbors; i++)
    close(sockfds[i]);

  return;
}

	  
/********COPIES ROUTING TABLE ENTRIES FOR UPDATING RT********/
void copyframe(RoutingTableEntry *dest, RoutingTableEntry *src)
  {
    strcpy(dest->name, src->name);
    strcpy(dest->prev_hop, src->prev_hop);
    strcpy(dest->next_hop, src->next_hop);
    dest->cost = src->cost;
    dest->status = src->status;
  
    return;
  }



/********UPDATES RT BY RUNNING DIJKSTRA'S ALGO ON TOPOLOGY********/
int UpdateRoutingT(topologyT *topology, RoutingTableEntry *RT)
{
  int i,j; 
  routerT *me;
  int entries_updated = 0;
  int min_cost;
  int INFcost = 100000;  /*INFINITE COST VALUE*/
  int min_index;
  int n = topology->num_routers - 1; /*#ENTRIES IN RT*/
  char cmpstring[MAX_HOSTNAME_LEN];
  

  me = &(topology->routers[0]);  
  for (i=0; i<n; i++)
    {
      strcpy(RT[i].name, topology->routers[i+1].name); /*COPY THE NEIGHBORS 
							 OF SOURCE TO THE RT*/
      RT[i].status = 'u'; /*INITIALIZE STATUS TO UNDONE*/
      if ( i < me->num_neighbors )
	{
	  /*NEIGHBORS OF SOURCE ROUTER*/
	  strcpy(RT[i].prev_hop, me->name); /*PREV HOP FOR REACHING
					      NEIGHBORS IS THE SOURCE ROUTER*/
	  RT[i].cost = me->neighbors[i].cost;
	}
      else
	RT[i].cost = INFcost;     /*COST INFO NOT KNOWN; SET COST TO INFINITY*/
    }
  
  while (entries_updated < n)
    {
      min_index = 0;
      min_cost = INFcost;
      for (i = 0; i<n; i++)
	{
        /*FIND THE MINIMUM COST ROUTER AMONGST THE ROUTERS TO WHICH MIN
	  COST PATH HAS NOT YET BEEN FOUND, ie, WITH UNDONE STATUS*/
	  if ( (RT[i].cost < min_cost) && (RT[i].status != 'd') )
	    {
	      min_cost = RT[i].cost;
	      min_index = i;
	    }
	}
      /*MIN COST PATH TO THIS ROUTER FOUND; SET STATUS TO DONE*/
      RT[min_index].status = 'd';
      entries_updated++;

      /*UPDATE THE COSTS TO ALL NEIGHBORS OF THE MIN COST ROUTER FOUND ABOVE*/
      for (j = 0; j<topology->routers[min_index+1].num_neighbors; j++)
	{
	  i = 0;
	  if (strcmp(me->name,
	        topology->routers[min_index+1].neighbors[j].router->name) != 0)
	    {
	      /*THE NEIGHBOR OF THE ROUTER IS NOT THE SOURCE ROUTER*/

	      while (strcmp(RT[i].name,
		topology->routers[min_index+1].neighbors[j].router->name) != 0)
		i++;                    /* LOCATE THE NEIGHBOR IN RT*/
	      if (RT[i].status != 'd')
		{
		  /*THE STATUS IS UNDONE*/
		  if ( (RT[min_index].cost +
			topology->routers[min_index+1].neighbors[j].cost) 
		       < RT[i].cost )
		    {
		      /*UPDATE THE COST*/
		      strcpy(RT[i].prev_hop, RT[min_index].name);
		      RT[i].cost = (RT[min_index].cost +
			     topology->routers[min_index+1].neighbors[j].cost);
		    }

		}
	    }
	}
    }
   
  /*THE RT CONTAINS PREV HOP FROM THE DESTINATION INFORMATION; RETRIEVE THE
    NEXT HOP TO THE SOURCE INFORMATION*/
  for (i = 0; i<n; i++)
    {
      cmpstring = RT[i].prev_hop;
      j = i;
      /*WRAP BACK TILL THE SOURCE IS ENCOUNTERED*/
      while( (strcmp(me->name, cmpstring) != 0) )
	{
	  j = 0;
	  while( (strcmp(cmpstring, RT[j].name)) != 0 )
	    j++;
	  strcpy(cmpstring, RT[j].prev_hop);
	}
      /*UPDATE THE NEXT HOP INFORMATION*/
      strcpy(RT[i].next_hop, RT[j].name);
    }
   
  return n;   /*RETURN THE # ENTRIES IN RT*/
}


/*SORTS THE RT FIRST ACCORDING TO THE COST AND ACCORDING TO DESTINATION
  ROUTER NAME*/
void SortRoutingTable(RoutingTableEntry *RT, int n)
{
  int i,j;
  int minindex;  /*index used for sorting RT*/
  RoutingTableEntry temp;  /*used in sorting RT on cost basis*/
  int begin, end; /*used for sorting RT om destination hostaddr basis*/
  
 //****** Sorting the routing table on total cost basis******
  for (i=0; i<n; i++)
    {
      minindex = i;
      /*LOCATE THE ENTRY WITH MIN COST*/
      for (j=i+1; j<n; j++)
	{
	 if (RT[minindex].cost > RT[j].cost) 
	   minindex = j;	 
	}  
      copyframe (&temp, &RT[i]);
      copyframe (&RT[i], &RT[minindex]);
      copyframe (&RT[minindex], &temp);  
    }
  
  
//****** Sorting the routing table on destination hostname basis******
  
  begin = 0;
  end = 0;
  while (end < n-1)
  {
    while ( RT[begin].cost == RT[end].cost )
      end++;
    end--;
    for (i=begin; i<=end; i++)
      {
	minindex = i;
	for (j=i+1; j<=end; j++)
	  {
	    if ( strcmp(RT[minindex].name, RT[j].name) > 0 )
	      minindex = j;
	  }
	copyframe (&temp, &RT[i]);
	copyframe (&RT[i], &RT[minindex]);
	copyframe (&RT[minindex], &temp);
      }
    begin = end + 1;
    end = begin;
  }
  
return;
}


/*********PRINTS THE ROUTING TABLE********/
void WriteRoutingTable(RoutingTableEntry *RT, int num_entries)
{
  int i;
  for (i=0; i<num_entries; i++)
     printf("%s;%s;%d\n", RT[i].name, RT[i].next_hop, RT[i].cost); 
  printf("\n");
  return;
}


/*********WRITES THE ROUTING TABLE TO FILE <HOSTNAME>.TABLE********/
void WriteTableToFile(RoutingTableEntry *RT, int num_entries, char
		      *TableFile, char *routername)
{
  int i;
  FILE *OutFile = fopen(TableFile, "w");

  if (OutFile == NULL)
    {
      perror("ERROR: router.cc : Failed to open file for writing\n");
      exit(FAILURE);
    }

  fprintf(OutFile, "\tROUTING TABLE FOR ROUTER: %s\n\n", routername);
  /*WRITE THE COMPLETE RT*/
  for (i=0; i<num_entries; i++)
    {
      fprintf(OutFile, "%s;%s;%d\n", RT[i].name, RT[i].next_hop,
	     RT[i].cost); 
    }
  
  return;
}



/*PRINTS THE TOPOLOGY*/
void PrintTopology(topologyT *topology)
{
   int i;

   for(i = 0; i < topology->num_routers; i++)
   {
      PrintRouter(&(topology->routers[i]));
   }
   return;
}



/*PRINTS ROUTER, ITS NEIGHBORS AND LINK COST INFO*/
void PrintRouter(routerT *router)
{
   int i;
   neighborT *neighbor;

   printf("*** Router Name: %s\n", router->name);
   printf("--- Neighbors:\n");
   for(i = 0; i < router->num_neighbors; i++)
   {
      neighbor = &router->neighbors[i];
      printf("    + %s (%d)\n", neighbor->router->name, neighbor->cost);
   }
   return;
}





