#include "stdhead.h"
#include "clilib.h"
#include "parse.h"


void write_to_log (char * str, int time_flag)
{
    extern int event_log_fd;
    time_t curr_time;
    int time_length;
    char *str_curr_time;
    
    if (time_flag)
    {
	curr_time = time (&curr_time);
	str_curr_time = ctime (&curr_time);
	write (event_log_fd, str_curr_time, strlen(str_curr_time));
	write (event_log_fd, "\t", 1);
    }
    if ((write (event_log_fd, str, strlen (str))) == -1)
	printf ("Failed to log event.\n");
    if (time_flag)
	write (event_log_fd, "\n", 1);
}

// Build message linked-list from character buffer
void build_linked_list (char *m, int len, struct DHCP_MESSAGE *dhcp_message)
{
    int index = 0, i;
    struct OPTIONS *opt;
    
    dhcp_message -> u_msg_type.buffer[0] = m[index++];

    //only 3 bits trans by server, and the code padding '0' into last bytes.... tr by bruce
    for (i = 2; i >= 0; i--){
	    dhcp_message -> u_trans_id.buffer[i] = m[index++];
    }
    dhcp_message -> u_trans_id.buffer[3] = 0;
    opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
    LLOG("LLOG: create opt address: %x\n", opt);
    BLOG("BLOG: pkt index: %d, len: %d",index,len);
    read_option (m, index, len, opt);
    dhcp_message -> opt = opt;
    LLOG("LLOG: dhcp msg opt link address: %x\n",dhcp_message -> opt );
}

void print_linked_list_contents (struct DHCP_MESSAGE *dhcp_message)
{
    struct OPTIONS *q;
    struct DUID *d;
    struct DUID1 *d1;
    struct DUID2 *d2;
    struct DUID3 *d3;
    struct IA *ia;
    struct IA_ADDRESS *ia_addr;
    struct PREFERENCE *preference;
    struct STATUS_CODE *sc_ptr;
    int i;
    char name[64];
    
    printf ("Message type = %d\n", dhcp_message -> u_msg_type.msg_type);
    printf ("Transaction ID = %d\n\n", dhcp_message -> u_trans_id.trans_id);
    q = dhcp_message -> opt;
LLOG("LLOG: dhcp msg opt link address: %x\n", q);
    if (!q)
    {
	printf ("No options defined!\n");
	return;
    }
	
    do
    {
	switch (q -> u_opt_code.opt_code)
	{
	    case OPTION_CLIENTID :
	    case OPTION_SERVERID :
		printf ("Option type = %d\n", q -> u_opt_code.opt_code);
		//printf ("DUID length = %d\n", q -> u_opt_len.opt_len);
		d = q -> opt_data;
		//printf ("DUID type = %d\n", d -> u_duid_type.duid_type);
		
		if (d -> u_duid_type.duid_type == 1)
		{
		    d1 = d -> duid_type;
		    
		    //printf ("Time = %d\n", d1 -> u_time.time);
		    //printf ("Hardware type = %d\nHardware address = ", d1 -> u_haddr_type.haddr_type);
		    for (i = 0; i < 6; i++)
		    {
			printf ("%x", d1 -> link_layer_address[i]);
			if (i != 5)
			    printf (":");
		    }
		    //printf ("\n\n");
		}
		else if (d -> u_duid_type.duid_type == 2)
		{
		    d2 = d -> duid_type;
		    
		    //printf ("Identifier length = %d\nIdentifier = ", d2 -> u_identifier_length.identifier_length);
		    //for (i = 0; i < d2 -> u_identifier_length.identifier_length; i++)
		//	printf ("%d", d2 -> identifier[i]);
	//	    printf ("\nDomain name = ");
		    //for (i = 0; i < d2 -> domain_name_len; i++)
		//	printf ("%c", d2 -> domain_name[i]);
		}
		else if (d -> u_duid_type.duid_type == 3)
		{
		    d3 = d -> duid_type;
		    
		//    printf ("Hardware type = %d\nHardware address = ", d3 -> u_haddr_type.haddr_type);
		    for (i = 0; i < 6; i++)
		    {
			printf ("%x", d3 -> link_layer_address[i]);
			if (i != 5)
			    printf (":");
		    }
		    printf ("\n\n");
		}
    		
		q = d -> opt;
        LLOG("LLOG: print duid next opt address: %x\n",q );
		break;
		
	    case OPTION_IA :
		printf ("Option type = IA option\n");
	//	printf ("option length = %d\n", q -> u_opt_len.opt_len);
		ia = q -> opt_data;

	//	printf ("IAID = %x\n", ia -> u_iaid.iaid);
	//	printf ("T1 = %d\n", ia -> u_t1.t1);
	//	printf ("T2 = %d\n", ia -> u_t2.t2);
	//	printf ("Status code for IA = %d\n\n", ia -> status);
		q = ia -> opt;
		break;
		
	    case OPTION_IAADDR :
		printf ("Option type = IA address option\n");
	//	printf ("option length = %d\n", q -> u_opt_len.opt_len);
		ia_addr = q -> opt_data;

	//	printf ("Temporary address bit = %d\n", ia_addr -> t_bit);
	//	printf ("Address status = %d\n", ia_addr -> addr_status);
	//	printf ("Prefix length of the address = %d\n", ia_addr -> prefix_length);
		printf ("IPv6 Address = %s\n", inet_ntop (AF_INET6, ia_addr -> addr, name, 64));
		printf ("Preferred lifetime = %d\n" , ia_addr -> u_pref_lifetime.pref_lifetime);
		printf ("Valid lifetime = %d\n", ia_addr -> u_valid_lifetime.valid_lifetime);
		q = ia_addr -> opt;
		break;
		
	    case OPTION_PREFERENCE :
		printf ("Option type = Preference Option\n");
	//	printf ("Option length = %d\n", q -> u_opt_len.opt_len);
		preference = q -> opt_data;
		
	//	printf ("Preference value = %d\n\n", preference -> preference_value);
		q = preference -> opt;
		break;
	    
	    case OPTION_STATUS_CODE :
		printf ("Option type = Status code\n");
	//	printf("Option length = %d\n",q->u_opt_len.opt_len);
		sc_ptr = q->opt_data;
		
	//	printf( "Status code = %d\nStatus message =",sc_ptr->u_status_code.status_code);
                //for(i=0; i < q->u_opt_len.opt_len -2; i++) 		
	//	   printf("%c", sc_ptr->message[i]);
	//	printf("\n\n");
		q=sc_ptr->opt;
		break;
		    
	    default :
		printf ("Print!!! Invalid option type\n\n");
		q = 0;
		return;
	}
    }
    while (q);
}

// This function returns the generated random number in between -0.1 and +0.1
float generate_rand ()
{
    time_t t1;
    // call srand with the current time as the seed
    srand (time (&t1));
    // Return a random vakue between -0.1 and +0.1
    return ((float)((rand()%200)-100)/1000.0);
}

// This function returns a random number in between the defined constants.
int calculate_initial_delay ()
{
    time_t t1;
    int random;

    // call srand with the current time as the seed
    srand (time (&t1));
    
    // Calculate and return a random number between MAX_SOL_DELAY and MIN_SOL_DELAY
    random = rand () % (MAX_SOL_DELAY);

    if (random < (MIN_SOL_DELAY))
	random += (MIN_SOL_DELAY);
    return random;
}

/* This function calculates the timeout upon the expiry of which the
retransmission is to be done. */
float calculate_retransmission_timeout (float RTprev, float mrt)
{
    float rt = ((2.0 * RTprev) + (generate_rand () * RTprev));
    if (mrt != 0)
	while (rt > mrt)
	    rt = mrt + generate_rand () * mrt;
    return rt;
}

/* This function checks whether that particular option exists in the linked
 list. It returns 1on success and 0 on failure. */
int check_for_option (int option, struct DHCP_MESSAGE *dhcp_msg_ptr)
{
    struct OPTIONS * opt;
    struct DUID *duid_ptr;
    struct IA *ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    struct STATUS_CODE * status_ptr;
    struct PREFERENCE * preference;
    
    // Move to the first option node
    opt= dhcp_msg_ptr->opt;
    
    // Until there are no moer options in the message
    while ( opt !=0)
    {
	// If the required option is found then return 1
	if (opt->u_opt_code.opt_code == option)
	    return 1;
	
	// Depending on the option code    
	switch(opt->u_opt_code.opt_code)
	{
	    case OPTION_CLIENTID :
	    case OPTION_SERVERID :
		// Move to the next node
		duid_ptr = (struct DUID *) opt -> opt_data;
		opt = duid_ptr->opt;
		break;
		
	    case OPTION_IA : 
		// Move to the next node
		ia_ptr= (struct IA *) opt -> opt_data;
		opt= ia_ptr->opt;
		break;
		
	    case OPTION_IAADDR :
		// Move to the next node
	        iaaddr_ptr=(struct IA_ADDRESS *) opt -> opt_data;
		opt=iaaddr_ptr->opt;
		break;
		
	    case OPTION_STATUS_CODE :
		// Move to the next node
	        status_ptr=(struct STATUS_CODE *) opt -> opt_data;
		opt=status_ptr->opt;
		break;
		
	    case OPTION_PREFERENCE :
		// Move to the next node
		preference = (struct PREFERENCE *) opt -> opt_data;
		opt = preference -> opt;
		break;
		
	    default :
        BLOG("BLOG: unknown option...%d\n",opt->u_opt_code.opt_code);
		opt = 0;
		break;
	}	
    }
    return 0;
}   

int check_for_status (struct DHCP_MESSAGE *dhcp_msg_ptr)
{
    struct OPTIONS *options_ptr;
    struct DUID *duid_ptr;
    struct IA *ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    struct STATUS_CODE * status_ptr;
    struct PREFERENCE * preference_ptr;
    
    // Move to the first option node
    options_ptr= dhcp_msg_ptr->opt;
    
    // Until there are no moer options in the message
    while (options_ptr !=0)
    {
	// Depending on the option code    
	switch(options_ptr -> u_opt_code.opt_code)
	{
	    case OPTION_CLIENTID :
	    case OPTION_SERVERID :
		// Move to the next node
		duid_ptr = (struct DUID *) options_ptr -> opt_data;
		options_ptr = duid_ptr->opt;
		break;
		
	    case OPTION_IA : 
		// Move to the next node
		ia_ptr= (struct IA *) options_ptr -> opt_data;
		options_ptr = ia_ptr->opt;
		break;
		
	    case OPTION_IAADDR :
		// Move to the next node
	        iaaddr_ptr=(struct IA_ADDRESS *) options_ptr -> opt_data;
		options_ptr = iaaddr_ptr->opt;
		break;
		
	    case OPTION_STATUS_CODE :
		// Move to the next node
	        status_ptr=(struct STATUS_CODE *) options_ptr -> opt_data;
		return (status_ptr -> u_status_code.status_code);
		
	    case OPTION_PREFERENCE :
		// Move to the next node
		preference_ptr = (struct PREFERENCE *) options_ptr -> opt_data;
		options_ptr = preference_ptr -> opt;
		break;

	    default :
		options_ptr = 0;
		break;
	}	
    }
    //original code return -1 if the reply message do no have state code payload,
    //but i do not see state code in ZyXel ADV message, and i don't send it ether,
    //so i pass this when i get ADV reply message       //bruce  2009/07/16
    //return -1;
    return 0;
}

/* This function checks the validity of the message being passed to it.
It return 1 on success and 0 on failure */
int check_message (struct DHCP_MESSAGE *dhcp_message_ptr, int recv_type, int sent_type)
{
    struct OPTIONS *options_ptr;
    extern int g_trans_id;
    int status;
    // Check whether the received message is of the right type
    if (dhcp_message_ptr->u_msg_type.msg_type != recv_type){
        BLOG("BLOG: type error\n");
        BLOG("BLOG: %d, %d\n",dhcp_message_ptr->u_msg_type.msg_type, recv_type);
        return 0;
    }
    // Checks whether the transaction id of the received message has the same
    // transaction is as that sent to same server before a reply is obtained
    // from the server
    if (dhcp_message_ptr->u_trans_id.trans_id != g_trans_id){
       BLOG("BLOG: trans id error\n");
       BLOG("BLOG: %x, %x\n",dhcp_message_ptr->u_trans_id.trans_id, g_trans_id);       
       return 0;	
    }
    BLOG("BLOG: message type %d\n",dhcp_message_ptr->u_msg_type.msg_type);
    BLOG("BLOG: trans_id %x\n",dhcp_message_ptr->u_trans_id.trans_id);   
    switch (dhcp_message_ptr->u_msg_type.msg_type)
    {
	case ADVERTISE :
        BLOG("BLOG: ADVERTISE message\n"); 
	    if ((status = check_for_status (dhcp_message_ptr)) != -1)
		if (status != Success)
		    return 0;
        //I skip status check...   //bruce
	    if (!check_for_option (OPTION_SERVERID, dhcp_message_ptr)){
            BLOG("BLOG: SERVERID error\n");
            return 0;
        }
	    if (!check_for_option (OPTION_IA, dhcp_message_ptr)){
            BLOG("BLOG: OPTION_IA error\n");
		    return 0;
        }
	    if (!check_for_option (OPTION_IAADDR, dhcp_message_ptr)){
            BLOG("BLOG: IAADDR error\n");
		    return 0;
        }
	    break;
	    
	case REPLY :
	    if ((status = check_reply_message (dhcp_message_ptr, sent_type)) == 0)
		return 0;
	    break;
    }
    return 1;
}

// This function waits for a response from the server after the initial transmission
// of a message has been done. The function 
struct DHCP_MESSAGE ** wait_until_response (int sfd, int cfd, float irt, float mrt, int mrc, int sent_type, int recv_type, char *buff, int size, int *msg_count)
{
    char name [64];
    // Stores the client state
    extern int g_state;
    // Server and Client socket structures
    extern struct sockaddr_in6 sa, ca;
    
    extern struct DHCP_MESSAGE * reply_for_request;
    // count keeps track of message retransmissions
    // valid is used for checking message validity
    int count = 0, n, message_valid;
    // variable for storing message received from the server
    struct DHCP_MESSAGE **msg_arr_ptr = 0, **p; // pointer to the message array
    // Calculate the initial retransmission time
    float rt = calculate_retransmission_timeout (SOL_IRT, mrt);
    // Used for detecting activity on a socket
    fd_set readfds, testfds;
    // Used to set the retransmission timeout
    struct timeval timeout;
    // Used to receive the response from the server
    char reply[MIN_MESSAGE_SIZE];
    // pointer to a DHCP_MESSAGE
    struct DHCP_MESSAGE *dhcp_message_ptr;
    
    u_int32_t renewal_timer, rebind_timer, valid_lifetime_timer;
    time_t start, end;
    // Set the length of the server and client socket structures
    socklen_t sl = sizeof (sa);
    socklen_t cl = sizeof (ca);
    // Set the server response message count to zero    
    *msg_count = 0;
    // Clear the file descriptor set variables
    FD_ZERO (&readfds);
    // Set the client socket in the file descriptor set
    FD_SET (cfd, &readfds);
    // If Maximum retransmission count is zero then set mrc to infinity.
    if (!mrc)
	mrc = INFINITY;
    // Set the retransmission time
    timeout.tv_sec = 0;
    timeout.tv_usec = rt * MEGA;
	
    if (g_state == RENEWING || g_state == REBINDING)
    {
	get_timers (reply_for_request, &renewal_timer, &rebind_timer, &valid_lifetime_timer);
	time (&start);
    }
    
    while (count < mrc)
    {
	// Initialize the file descriptor set
        testfds = readfds;	   
	// Start the retransmission timeout and wait for activity on the scokets
	write_to_log ("Waiting for server response", 1);
        select (cfd+1, &testfds, 0, 0, &timeout);	   
	// If it is time to retrasmit   
        if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
        {
	    if (g_state == RENEWING)
	    {
		time (&end);
		if (end-start >= rebind_timer-renewal_timer)
		    return 0;
	    }
	    else if (g_state == REBINDING)
	    {
		time (&end);
		if (end-start >= valid_lifetime_timer-rebind_timer)
		    return 0;
	    }
		
	    // If there are no replies from the server then break out and return a null pointer
	    write_to_log ("Wait for server response timed out.", 1);
	    if (*msg_count > 0)
	    {
		write_to_log ("\tResponse messages have been received. Exiting retransmission scheme.", 0);
		break;
	    }
	    write_to_log ("\tNo response messages have been received. Continuing in the retransmission scheme...", 0);
	    // Increment the retransmission count
	    count++;
	    printf ("Sending %d %d time\n", sent_type, count);
	    // Retransmit the client message
	    write_to_log ("Retransmitting message...", 1);
	    sendto (sfd, buff, size, 0, (struct sockaddr *) &sa, sl);
	    // Calculate the new Retransmission timeout
	    rt = calculate_retransmission_timeout (rt, mrt);
	    // Set the new retransmission timeout
	    timeout.tv_sec = 0;
	    timeout.tv_usec = rt * MEGA;
	    printf ("The new retransmission timeout is %f\n", rt);
	}
	// If activity is detected on the client receiving scoket
	else if (FD_ISSET (cfd, &testfds))
	{
	    // Receive the server message into the reply buffer
	    n = recvfrom (cfd, reply, MIN_MESSAGE_SIZE, 0, (struct sockaddr *) &ca, &cl);
	    write_to_log ("Server response received from ", 1);
	    write_to_log ((char *) inet_ntop (AF_INET6, &ca.sin6_addr, name, 64), 0);
	    // Allocate memory for a DHCP_MESSAGE
	    dhcp_message_ptr = (struct DHCP_MESSAGE *) malloc (sizeof (struct DHCP_MESSAGE));
	    // Build the linked list for the server response message
	    build_linked_list (reply, n, dhcp_message_ptr);
	    // Check for validity of the server response message
	    write_to_log ("Checking message validity ....", 1);
	    message_valid = check_message (dhcp_message_ptr, recv_type, sent_type);
	    // Announce arrival of server response message
	    printf ("%d message received.\n", recv_type);
	    // Check for message validity
            if (message_valid)   	
	    {
		// Allocate an array of pointers for the dhcp message.
		(*msg_count)++;
	    	msg_arr_ptr= (struct DHCP_MESSAGE **) realloc (msg_arr_ptr, sizeof (struct DHCP_MESSAGE *) * (*msg_count));
		p = msg_arr_ptr + (*msg_count) - 1;
		*p = dhcp_message_ptr;
		
		printf ("Message valid.\n");
		write_to_log ("Message is valid", 1);
		// If state is not selecting then break on receiving a valid server reply
		// Else wait for more replies according to the retransmission scheme
		printf ("BLOG: STATE: %d\n",g_state);
		if (g_state != SELECTING){
		    break;
        }
		if (count > 0)
		    break;
	     }
	    else 
	    {
		printf ("\n\t\tMessage invalid.\n");
		print_linked_list_contents (dhcp_message_ptr);
	        free_message_mem (dhcp_message_ptr);
		write_to_log ("Message is invalid. Discarding message", 1);
            }	
	}
    }
    return msg_arr_ptr;    
}

u_int8_t convert_character_to_hex (char ch)
{
    u_int8_t hex;
    
    if (ch >= '0' && ch <= '9')
    	hex = ch - '0';
    else
    {
        switch (ch)
        {
    	    case 'A' :
	    case 'a' :
		hex = 10;
		break;
		    
	    case 'B' :
	    case 'b' :
		hex = 11;
		break;
		    
	    case 'C' :
	    case 'c' :
		hex = 12;
		break;
		    
	    case 'D' :
	    case 'd' :
		hex = 13;
		break;
		    
	    case 'E' :
	    case 'e' :
		hex = 14;
		break;
		    
	    case 'F' :
	    case 'f' :
		hex = 15;
		break;
	}
    }
    
    return hex;
}

void generate_trans_id (u_int32_t *trans_id)
{
    extern int g_trans_id;
    time_t t;
    srand (time (&t));
    *trans_id = 0;
    *trans_id = rand();
    // Make the upper byte of transaction id equal to zero
    *trans_id &= 0x00ffffff;
    g_trans_id = *trans_id;
}

/* 
    This function converts the message from the linked list into a character
    buffer. It returns the length of the buffer.
*/
int store_in_buffer (struct DHCP_MESSAGE *dhcp_message, char *buff)
{
    int i;
    char *start = buff;
    struct OPTIONS *options_ptr;
    struct DUID *duid_ptr;
    struct DUID1 *duid1_ptr;
    struct DUID2 *duid2_ptr;
    struct DUID3 *duid3_ptr;
    struct IA *ia_ptr;
    struct IA_ADDRESS *ia_addr_ptr;
    struct STATUS_CODE *sc_ptr;

    // MESSAGE TYPE
    *buff = dhcp_message->u_msg_type.buffer[0];
    buff++;
    
    // TRANSACTION ID
    printf("T_ID: %x\n",dhcp_message->u_trans_id.trans_id);
    for (i = 2; i >= 0; i--)
    {
	*buff = dhcp_message->u_trans_id.buffer[i];
	buff++;
    }
    
    options_ptr = dhcp_message->opt;
    
    while (options_ptr != 0)
    {
    BLOG("BLOG: buffer address: %x\n",buff);
	// OPTIONS CODE
	*buff = options_ptr->u_opt_code.buffer[1];
	buff++;
    	*buff = options_ptr->u_opt_code.buffer[0];
	buff++;
	    
	// OPTIONS LEN
        *buff = options_ptr->u_opt_len.buffer[1];
        buff++;
        *buff = options_ptr->u_opt_len.buffer[0];
        buff++;
    BLOG("BLOG: store option type: %d\n",options_ptr -> u_opt_code.opt_code);
	switch (options_ptr -> u_opt_code.opt_code)
        {
    	case OPTION_CLIENTID :
	    case OPTION_SERVERID :
            BLOG("store C_ID/S_ID into buffer\n");
		duid_ptr = (struct DUID *) options_ptr->opt_data;
    		
		// DUID TYPE
		*buff = duid_ptr->u_duid_type.buffer[1];
    		buff++;
    		*buff = duid_ptr->u_duid_type.buffer[0];
    		buff++;
		
		switch (duid_ptr -> u_duid_type.duid_type)
		{
		    case 1 :		
                BLOG("store DUID type 1\n");
			duid1_ptr = (struct DUID1 *) duid_ptr->duid_type;

    			// DUID HARDWARE ADDRESS TYPE
    			*buff = duid1_ptr->u_haddr_type.buffer[1];
        		buff++;
	   		*buff = duid1_ptr->u_haddr_type.buffer[0];
			buff++;

			// Time field
			printf("Time: %x\n",duid1_ptr->u_time.time);
			for (i = 3; i >= 0; i--)
			{
			    *buff = duid1_ptr->u_time.buffer[i];		
			    buff++;
		    }
			
            printf("==> %d", duid1_ptr -> haddr_len);
            
			// DUID HARDWARE ADDRESS
			for (i =0; i< duid1_ptr -> haddr_len; i++)
			{
			    *buff = duid1_ptr->link_layer_address[i];		
			    buff++;
		    }
			break;
			
		    case 2 :    
			duid2_ptr = (struct DUID2 *) duid_ptr->duid_type;

    			// Identifier length
    			*buff = duid2_ptr->u_identifier_length.buffer[1];
        		buff++;
	   		*buff = duid2_ptr->u_identifier_length.buffer[0];
			buff++;

			// Identifier
			for (i = 0; i<duid2_ptr->u_identifier_length.identifier_length; i++)
			{
			    *buff = duid2_ptr-> identifier[i];
			    buff++;
		        }
			
			// Domain name
			for (i =0; i< duid2_ptr -> domain_name_len; i++)
			{
			    *buff = duid2_ptr-> domain_name[i];		
			    buff++;
		        }
			break;
			
		    case 3 :
			    duid3_ptr = (struct DUID3 *) duid_ptr->duid_type;
        
    			// DUID HARDWARE ADDRESS TYPE
    			*buff = duid3_ptr->u_haddr_type.buffer[1];
        		buff++;
	   		    *buff = duid3_ptr->u_haddr_type.buffer[0];
			    buff++;

			    // DUID HARDWARE ADDRESS
			    for (i = 0; i< duid3_ptr->haddr_len; i++)
			    {
			        *buff = duid3_ptr->link_layer_address[i];		
			        buff++;
		            }
			    break;
		}
			    
		// GO TO THE NEXT OPTIONS HEADER
		options_ptr = duid_ptr->opt;
		break;
		
	    case OPTION_IA :
            BLOG("store IA into buffer\n");
		// IA OPTION DATA
		ia_ptr = (struct IA *)options_ptr->opt_data;
   
    		// IAID
    		printf("IAID: %x\n",ia_ptr->u_iaid.iaid);
    		for (i = 3; i >= 0; i--)
		{
	    	    *buff = ia_ptr->u_iaid.buffer[i];
	    	    buff++;
		}	
    
    		//  T1
    		printf("T1: %x\n",ia_ptr->u_t1.t1);
    		for (i = 3; i >= 0; i--)
    		{
	    	    *buff = ia_ptr->u_t1.buffer[i];
	    	    buff++;
		}	
    
		// T2
		printf("T2: %x\n",ia_ptr->u_t2.t2);
		for (i = 3; i >= 0; i--)
		{
	    	    *buff = ia_ptr->u_t2.buffer[i];
	    	    buff++;
		}

            //do not send state....  //bruce
    		// STATUS
    		//*buff = ia_ptr->status;
    		//buff++;
    
   		options_ptr = ia_ptr->opt;
		break;	    

	    case OPTION_IAADDR :
            BLOG("store IAADDR into buffer\n");
		ia_addr_ptr = (struct IA_ADDRESS *) options_ptr->opt_data;
   
		// ADDRESS STATUS
		//*buff = ia_addr_ptr->addr_status;

        //no these flag, rfc changed...  bruce
        
		// TEMPORARY ADDRESS BIT
		//if (ia_addr_ptr -> t_bit)
		//    *buff = *buff & (1 << 7);
		//else
		//    *buff = *buff & (0 << 7);
		//buff++;
    
		//  PREFIX LENGTH
        //*buff = ia_addr_ptr->prefix_length;
		//buff++;
    
    		// IPv6 ADDRESS
		for (i = 0; i < 16; i++)
		{
	    	    *buff = ia_addr_ptr->addr[i];
	    	    buff++;
		}    

		// PREFERRED LIFETIME
		for (i = 3; i >= 0; i--)
		{
	    	    *buff = ia_addr_ptr->u_pref_lifetime.buffer[i];
	    	    buff++;
		}

		// VALID LIFETIME
		for (i = 3; i >= 0; i--)
		{
	    	    *buff = ia_addr_ptr->u_valid_lifetime.buffer[i];
	    	    buff++;
		}

    		// OPTIONS FIELD OF THE IA ADDRESS OPTION
		// Not considered yet

		// NEXT OPTION
		options_ptr = ia_addr_ptr->opt;
		break;
	
	    case OPTION_STATUS_CODE :
		sc_ptr = (struct STATUS_CODE *) options_ptr -> opt_data;
	    
	    // Status code
	    for (i = 1; i >= 0; i--)
	    {
		*buff = sc_ptr -> u_status_code.buffer[i];
		buff++;
	    }
	    
	    // Status message
	    for (i = 0; i<options_ptr -> u_opt_len.opt_len - 2; i++)
	    {
		*buff = sc_ptr -> message[i];
		buff++;
	    }
	    
	    // Next option
	    options_ptr = sc_ptr -> opt;
	    break;	
        default:
            options_ptr=NULL;
        printf("what the fuck...\n");
        break;
	}
 printf("option parserd......\n");
    
    }
	BLOG("BLOG:  start: %x, buff: %x, len:%d \n",start, buff, (abs (start-buff)) );
    // RETURN THE TOTAL NUMBER OF BYTES
    return (abs (start-buff));
}

void read_option (char *m, int index, int len, struct OPTIONS *opt)
{
    int i, temp;
    struct DUID *d;
    struct DUID1 *d1;
    struct DUID2 *d2;
    struct DUID3 *d3;
    struct IA *ia;
    struct IA_ADDRESS *ia_addr;
    struct OPTIONS *next_opt;
    struct STATUS_CODE *sc_ptr;    
    struct PREFERENCE * preference_ptr;
    u_int8_t *l_ptr;
    int ggg=0;
    union
    {
	char buffer[2];
	u_int16_t type_of_duid;
    } u_type_of_duid;

    BLOG("\nBLOG: pkt:");
    for(ggg=0;ggg<len;ggg++){
        BLOG("%x ",m[ggg]);
    }
    BLOG("\nBLOG: pkt end\n");
    BLOG("BLOG: pkt index: %d, len: %d, opt_addr: %x\n",index,len,opt);
    
    for (i = 1; i>= 0; i--){
	    opt -> u_opt_code.buffer[i] = m[index];
        index++;
        BLOG("BLOG: m:%x, index: %d\n",m, index);
        BLOG("BLOG: op_code:%d\n",opt -> u_opt_code.buffer[i]);
        
    }
    for (i = 1; i >= 0; i--){
      opt -> u_opt_len.buffer[i] = m[index];
      index++;
    }
    BLOG("BLOG: opt number: %d\n",opt -> u_opt_code.opt_code);
    switch (opt -> u_opt_code.opt_code)
    {
	case OPTION_CLIENTID :
    case OPTION_SERVERID :
        BLOG("BLOG: C_ID/S_ID opt\n");
        d = (struct DUID *) malloc (sizeof (struct DUID));
        LLOG("LLOG: create opt C_ID/S_ID address: %x",d );
	    opt -> opt_data = d;
	    
	    for (i = 1; i >=0 ; i--){
	        u_type_of_duid.buffer[i] = m[index];
            index++;
        }
	    switch (u_type_of_duid.type_of_duid)
	    {
		case 1 :
		    d1 = (struct DUID1 *) malloc (sizeof (struct DUID1));
		    d -> u_duid_type.duid_type = 1;
		    
		    for (i = 1; i >= 0; i--){
			    d1 -> u_haddr_type.buffer[i] = m[index];
                index++;
            }
		    for (i = 3; i >= 0; i--){
			    d1 -> u_time.buffer[i] = m[index];
                index++;
            }
BLOG("BLOG: time: %x\n",d1 -> u_time.time);
BLOG("BLOG: index: %d\n",index);
		    // DUID Type = 2 octets
		    // Hardware type field = 2 octets
		    // Time field = 4 octets
		    d1 -> haddr_len = opt -> u_opt_len.opt_len - 2 - 2 - 4;
		    d1 -> link_layer_address = (u_int8_t *) malloc (sizeof (u_int8_t) * d1->haddr_len);
            BLOG("BLOG: d1 -> haddr_len: %d \n",d1 -> haddr_len);
            for (i = 0; i<d1 -> haddr_len; i++)
	                d1 -> link_layer_address[i] = m[index++];
		    d -> duid_type = d1;
		    break;
		    
		case 2 :
		    d2 = (struct DUID2 *) malloc (sizeof (struct DUID2));
		    d -> u_duid_type.duid_type = 2;
		    
		    for (i = 1; i >= 0; i--)
			d2 -> u_identifier_length.buffer[i] = m[index++];

		    d2 -> identifier = (u_int8_t *) malloc (sizeof (u_int8_t) * d2 -> u_identifier_length.identifier_length);
		    for (i = d2 -> u_identifier_length.identifier_length; i >= 0; i--)
	                d2 -> identifier[i] = m[index++];
		    
		    // DUID Type = 2 octets
		    // Identifier length field = 2 octets
		    temp = opt -> u_opt_len.opt_len - 2 - 2 - d2 -> u_identifier_length.identifier_length;
		    d2 -> domain_name = (u_char *) malloc (sizeof (char) * temp);
		    for (i = temp; i >= 0; i--)
	                d2 -> domain_name[i] = m[index++];
		    d2 -> domain_name_len = temp;
		    d -> duid_type = d2;
		    break;

		case 3 :
            BLOG("DUID3\n");
		    d3 = (struct DUID3 *) malloc (sizeof (struct DUID3));
		    d -> u_duid_type.duid_type = 3;
		    BLOG("BLOG: index3-1: %d\n",index);
		    for (i = 1; i >= 0; i--)
			d3 -> u_haddr_type.buffer[i] = m[index++];
            BLOG("BLOG: index3-2: %d\n",index);
		    // DUID Type = 2 octets
		    // Hardware type field = 2 octets
		    d3 -> haddr_len = opt -> u_opt_len.opt_len - 2 - 2;
		    d3->link_layer_address = (u_int8_t *) malloc (sizeof (u_int8_t) * d3->haddr_len);
		    for (i = 0 ; i< d3 -> haddr_len; i++)
	                d3 -> link_layer_address[i] = m[index++];
		    d -> duid_type = d3;
            BLOG("BLOG: index3-3: %d\n",index);
		    break;

		default :
		    printf ("Unrecognized DUID type\n");
	    }

	    next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
        LLOG("LLOG: X: create next opt address: %x\n",next_opt );
	    if (index == len){
		    d->opt = 0;
	    }else
	    {
	        BLOG("BLOG: index2: %d\n",index);
		    read_option (m, index, len, next_opt);
		    d->opt = next_opt;
            LLOG("LLOG: X: duid next opt address: %x\n",d->opt );
	    }
	    break;
	    
	case OPTION_IA :
        BLOG("BLOG: IA opt\n");
	    ia = (struct IA *) malloc (sizeof (struct IA));
	    opt -> opt_data = ia;
	    
	    for (i = 3; i >= 0; i--)
		ia -> u_iaid.buffer[i] = m[index++];
	    for (i = 3; i >= 0; i--)
		ia -> u_t1.buffer[i] = m[index++];
	    for (i = 3; i >= 0; i--)
		ia -> u_t2.buffer[i] = m[index++];
        //i mark status bytes... bruce
	    //ia -> status = m[index++];
	    next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
	    read_option (m, index, len, next_opt);
	    ia -> opt = next_opt;
	    break;
	    
	case OPTION_IAADDR :
        BLOG("BLOG: IAADDR opt\n");
        //the format is changed in rfc3315
        //
	    ia_addr = (struct IA_ADDRESS *) malloc (sizeof (struct IA_ADDRESS));
	    opt -> opt_data = ia_addr;
	    //i use IA as defaule   //bruce
		ia_addr -> t_bit = 0;
		
	    //ia_addr -> addr_status = temp % 128;
	    //i guess...
	    ia_addr -> prefix_length = 128;

        //save ipv6 address 
	    for (i = 0; i <16; i++){
		    ia_addr -> addr[i] = m[index];
            index++;
        }
        BLOG("BLOG: index_ia_1: %d\n",index);
	    for (i = 3; i >= 0; i--)
		ia_addr -> u_pref_lifetime.buffer[i] = m[index++];
	    for (i = 3; i >= 0; i--)
		ia_addr -> u_valid_lifetime.buffer[i] = m[index++];
		
	    ia_addr -> ia_addr_opt = 0;
	    next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
	    if (index == len){
		    ia_addr -> opt = 0;
	    }else
	    {
		read_option (m, index, len, next_opt);
		ia_addr -> opt = next_opt;
	    }
	    break;

    case OPTION_STATUS_CODE :
        BLOG("BLOG: Status opt\n");
    	    sc_ptr = (struct STATUS_CODE *) malloc (sizeof (struct STATUS_CODE));
	    opt -> opt_data = sc_ptr;
	    for (i = 1; i >= 0; i--)
	        sc_ptr -> u_status_code.buffer[i] = m[index++];
	    sc_ptr -> message = (char *) malloc (sizeof (char) * (opt -> u_opt_len.opt_len -2));
        //save message
        for (i = 0; i< opt -> u_opt_len.opt_len - 2; i++){
	        sc_ptr -> message[i] = m[index++];
        }
	    next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
        BLOG("BLOG: index_st_1: %d\n",index);
        if (index == len){
	        sc_ptr -> opt = 0;
	    }else
	    {
	        read_option (m, index, len, next_opt);
	        sc_ptr -> opt = next_opt;
	    }
	    break;
	
	case OPTION_PREFERENCE :
        BLOG("BLOG: PREFERENCE opt\n"); 
	    preference_ptr = (struct PREFERENCE *) malloc (sizeof (struct PREFERENCE));
	    opt -> opt_data = preference_ptr;
	    preference_ptr -> preference_value = m[index];
        index++;
	    next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
	    if (index == len){
	        preference_ptr -> opt = 0;
            BLOG("BLOG: Parser done...\n");    
	    }else
	    {
	         BLOG("BLOG: index:%d  len:%d\n",index,len); 
	        read_option (m, index, len, next_opt);
	        preference_ptr -> opt = next_opt;
	    }
	    break;
	
	default :
	    BLOG("BLOG: Invalid option type\n");
	    opt = 0;
	    index = len; 
	    return;
    }
}

/* 
    This function deallocates the memory space consumed by the dhcp message.
    It returns void.
*/
void free_message_mem (struct DHCP_MESSAGE *dhcp_message)
{
    struct OPTIONS *options_ptr;
    struct DUID *duid_ptr;
    struct DUID1 *duid1_ptr;
    struct DUID2 *duid2_ptr;
    struct DUID3 *duid3_ptr;
    struct IA *ia_ptr;
    struct IA_ADDRESS *ia_addr_ptr;
    struct PREFERENCE *preference;
    struct STATUS_CODE *sc_ptr;

    options_ptr = dhcp_message->opt;
    free (dhcp_message);

    while (options_ptr) // loop until the end of the linked list.
    {
	switch (options_ptr->u_opt_code.opt_code)  // determines the option code.
        {
	    case OPTION_CLIENTID :
	    case OPTION_SERVERID :
	    	duid_ptr = (struct DUID *) options_ptr->opt_data;
		free (options_ptr);
	    
		switch (duid_ptr -> u_duid_type.duid_type) // determines the duid type.
		{
		    case 1 :
			duid1_ptr = (struct DUID1 *) duid_ptr->duid_type;
			free (duid1_ptr -> link_layer_address);
			free (duid1_ptr);
			break;
			
		    case 2 :
			duid2_ptr = (struct DUID2 *) duid_ptr->duid_type;
			free (duid2_ptr->identifier);
			free (duid2_ptr->domain_name);
			free (duid2_ptr);
			break;

		    case 3 :
			duid3_ptr = (struct DUID3 *) duid_ptr->duid_type;
			free (duid3_ptr->link_layer_address);
			free (duid3_ptr);
			break;
		}

		options_ptr = duid_ptr->opt;
		free (duid_ptr);
		break;
		
	    case OPTION_IA :
		ia_ptr = (struct IA *) options_ptr->opt_data; 
		free (options_ptr);
		options_ptr = ia_ptr->opt;
		free (ia_ptr);
		break;
		
	    case OPTION_IAADDR :
		ia_addr_ptr = (struct IA_ADDRESS *) options_ptr->opt_data;
		free (options_ptr);
		options_ptr = ia_addr_ptr->opt;
		free (ia_addr_ptr);
		break;
		
	    case OPTION_PREFERENCE :
		preference = (struct PREFERENCE *) options_ptr -> opt_data;
		free (options_ptr);
		options_ptr = preference -> opt;
		free (preference);
		break;
		
	    case OPTION_STATUS_CODE :
		sc_ptr = (struct STATUS_CODE *) options_ptr -> opt_data;
		free (options_ptr);
		options_ptr = sc_ptr -> opt;
		free (sc_ptr->message);
		free (sc_ptr);
		break;
		
	    default :
		options_ptr = 0;
		break;
	}
    }
}

// This function returns the preference value of the message being sent as
// parameter. If there is no PREFERENCE option then 0 is returned
u_int8_t get_preference_value (struct DHCP_MESSAGE *message)
{
    struct OPTIONS * opt;
    struct DUID *duid_ptr;
    struct IA *ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    struct STATUS_CODE * status_ptr;
    struct PREFERENCE * preference;
    
    if (!message)
	return 0;
	
        // Move to the first option of the message
    opt = message->opt;
    
    // Until no options are left
    while (opt)
    {
	switch(opt->u_opt_code.opt_code)
	{
	    case OPTION_CLIENTID :
	    case OPTION_SERVERID :
		// Move onto the next node
		duid_ptr = (struct DUID *) opt -> opt_data;
		opt = duid_ptr -> opt;
		break;
		
	    case OPTION_IA : 
		// Move onto the next node
		ia_ptr= (struct IA *) opt -> opt_data;
		opt = ia_ptr->opt;
		break;
		
	    case OPTION_IAADDR :
		// Move onto the next node
	        iaaddr_ptr=(struct IA_ADDRESS *) opt -> opt_data;
		opt = iaaddr_ptr->opt;
		break;
		
	    case OPTION_STATUS_CODE :
		// Move onto the next node
	        status_ptr=(struct STATUS_CODE *) opt -> opt_data;
		opt = status_ptr->opt;
		break;
		
	    case OPTION_PREFERENCE :
		// Return the preference value
		preference = (struct PREFERENCE *) opt -> opt_data;
		return preference->preference_value;
		
	    default :
		opt = 0;
		break;
	}	
    }
    return 0;
}

// This function returns the preferred IPv6 address of the message being sent as
// parameter. If there is no IAADDR option then 0 is returned
u_int8_t * get_pref_ipv6_address (struct DHCP_MESSAGE *message)
{
    struct OPTIONS * opt;
    struct DUID *duid_ptr;
    struct IA *ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    struct STATUS_CODE * status_ptr;
    struct PREFERENCE * preference;
    
    if (!message)
	return 0;
	
    // Move to the first option of the message
    opt = message->opt;
    
    // Until no options are left
    while (opt)
    {
	switch(opt->u_opt_code.opt_code)
	{
	    case OPTION_CLIENTID :
	    case OPTION_SERVERID :
		// Move onto the next node
		duid_ptr = (struct DUID *) opt -> opt_data;
		opt = duid_ptr -> opt;
		break;
		
	    case OPTION_IA : 
		// Move onto the next node
		ia_ptr= (struct IA *) opt -> opt_data;
		opt = ia_ptr->opt;
		break;
		
	    case OPTION_IAADDR :
		// Return the preferred address
	        iaaddr_ptr=(struct IA_ADDRESS *) opt -> opt_data;
		return iaaddr_ptr -> addr;
		//opt = iaaddr_ptr->opt;
		break;
		
	    case OPTION_STATUS_CODE :
		// Move onto the next node
	        status_ptr=(struct STATUS_CODE *) opt -> opt_data;
		opt = status_ptr->opt;
		break;
		
	    case OPTION_PREFERENCE :
		// Move onto the next node
		preference = (struct PREFERENCE *) opt -> opt_data;
		opt = preference->opt;
		break;
		
	    default :
		opt = 0;
		break;
	}	
    }
    return 0;
}


// This function returns the prefereed lifetime of the message that has been sent
// to it as parameter.It the message does not have a preferred lifetime then 
// the function returns 0.*/

u_int32_t get_pref_lifetime (struct DHCP_MESSAGE *message)
{
    struct OPTIONS * opt;
    struct DUID *duid_ptr;
    struct IA *ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    struct STATUS_CODE * status_ptr;
    struct PREFERENCE * preference_ptr;
    
    if (!message)
	return 0;
	
    // Move to the first option of the message
    opt = message->opt;
    
    // Until no options are left
    while (opt)
    {
	switch(opt->u_opt_code.opt_code)
	{
	    case OPTION_CLIENTID :
	    case OPTION_SERVERID :
		// Move onto the next node
		duid_ptr = (struct DUID *) opt -> opt_data;
		opt = duid_ptr -> opt;
		break;
		
	    case OPTION_IA : 
		// Move onto the next node
		ia_ptr= (struct IA *) opt -> opt_data;
		opt = ia_ptr -> opt;
		break;
		
	    case OPTION_IAADDR :
		// Return the preferred lifetime
		iaaddr_ptr = (struct IA_ADDRESS *) opt->opt_data;
		return iaaddr_ptr -> u_pref_lifetime.pref_lifetime;
		break;
		
	    case OPTION_PREFERENCE :
		// Move onto the next node
		preference_ptr = (struct PREFERENCE *) opt -> opt_data;
		opt = preference_ptr -> opt;
		break;
		
	    default :
		opt = 0;
		break;
	}
    }
    return 0;
}
		
struct DHCP_MESSAGE * select_server (struct DHCP_MESSAGE ** server_reply, int msg_count, char * interface_name)
{
    struct DHCP_MESSAGE **p;
    // POinters to the list of messages
    struct DHCP_MESSAGE *best = 0, *curr = 0;
    
    // Pointer to Interface Details
    struct interface * interface_details;
    
    // Pointer to IPv6 addresses
    u_int8_t *best_pref_ipv6_addr = 0, *curr_pref_ipv6_addr = 0;
    
    // Variables to stores the best and current nodes Prefereed lifetimes
    u_int32_t best_pref_lifetime = 0, curr_pref_lifetime = 0;
    
    // Message index in the array of pointers to messages
    int index = 0;
    
    // Best and current node values of nodes
    u_int8_t max_preference_value = 0, curr_preference_value;
    
    // Get the Interface details for a particular interface
    interface_details = get_interface_details (interface_name);
    
    // Set current pointer to the first mesage
    curr = *server_reply;
    
    // Until all messages have been checked or best message has been found    
    while (index < msg_count)
    {
	// get the preference value for the current node
        curr_preference_value = get_preference_value (curr);
	
	// If the current preference value is equal to 255 return current pointer
	// as the best message
	if (curr_preference_value == MAX_PREFERENCE_VALUE)
	    return curr;
	    
	// If the current preference value is better than the previously best
	// preference value, assign the current message as the best message
	// and the current preference value as the maximum preference value    
	else if (curr_preference_value > max_preference_value)
	{
	    best = curr;
	    max_preference_value = curr_preference_value;
	}
	
	// If the current preference value if equal to the previously best
	// preference value then ccompare the two message and decide which is
	// the best
	else if (curr_preference_value == max_preference_value)
	{
	    if (!best)
	    {
		best = curr;
		// Increment the message index
		index++;
		// Move to the next message
		p = server_reply + index;
		curr = *p;
		continue;
	    }
		
	    // get the preferered IPv6 address for the best node
	    best_pref_ipv6_addr = get_pref_ipv6_address (best);
	    
	    // If the best messages preferred IPv6 address is not equal to
	    // the preferred IPv6 address in the interface configuartion
	    if (interface_details->pref_ipv6_addr && memcmp (best_pref_ipv6_addr, interface_details->pref_ipv6_addr->s6_addr, 16))
	    {
		// Get the prefereed IPv6 address of the current node
		curr_pref_ipv6_addr = get_pref_ipv6_address (curr);
		
		// If the preferred IPv6 address of the current node is equal
		// to the preferred IPv6 address of the particular interface,
		// assign the current message as the best message
		if (interface_details->pref_ipv6_addr && !memcmp (curr_pref_ipv6_addr, interface_details->pref_ipv6_addr->s6_addr, 16))
		    best = curr;
		else
		{
		    // Get the preferred lifetime for the best node
		    best_pref_lifetime = get_pref_lifetime (best);
		    // Get the prefereed lifetime for the current node
		    curr_pref_lifetime = get_pref_lifetime (curr);
		    // if the best nodes preferred lifetime is less than the
		    // current nodes lifetime then assgin the best node as the
		    // current node
		    if (best_pref_lifetime < curr_pref_lifetime )
			best = curr;
		}
	    }
	}
	// Increment the message index
	index++;
	// Move to the next message
	p = server_reply + index;
	curr = *p;
    }
    // Return the pointer to the best message
    return best;
}

struct DHCP_MESSAGE ** purge_message (struct DHCP_MESSAGE ** server_reply, struct DHCP_MESSAGE * target, int * msg_count)
{
    struct DHCP_MESSAGE * curr;
    int i, index = 0;
    curr = *server_reply;
    while (index < *msg_count)
    {
	if (curr == target)
	{
	    free_message_mem (curr);
	    for (i = index; i < (*msg_count) - 1; i++)
		server_reply [i] = server_reply [i+1];
	    (*msg_count)--;
	    break;
	}
	index++;
    }
    
    server_reply = (struct DHCP_MESSAGE ** ) realloc (server_reply, sizeof (struct DHCP_MESSAGE *) * (*msg_count));
    return server_reply;
}

struct OPTIONS * get_options_ptr (struct DHCP_MESSAGE * dhcp_msg_ptr, int option)
{
    struct OPTIONS * options_ptr = dhcp_msg_ptr -> opt;
    struct DUID * duid_ptr;
    struct IA * ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    struct PREFERENCE * preference_ptr;
    struct STATUS_CODE * status_ptr;

    while (options_ptr)
    {
        if (options_ptr -> u_opt_code.opt_code == option)
            return options_ptr;

        switch (options_ptr -> u_opt_code.opt_code)
        {
            case OPTION_CLIENTID :
            case OPTION_SERVERID :
                duid_ptr = (struct DUID *) options_ptr -> opt_data;
                options_ptr = duid_ptr -> opt;
                break;

            case OPTION_IA :

                ia_ptr = (struct IA *) options_ptr -> opt_data;
                options_ptr = ia_ptr -> opt;
                break;

            case OPTION_IAADDR :
                iaaddr_ptr = (struct IA_ADDRESS *) options_ptr -> opt_data;
                options_ptr = iaaddr_ptr -> opt;
                break;

            case OPTION_PREFERENCE :
                preference_ptr = (struct PREFERENCE *) options_ptr -> opt_data;
                options_ptr = preference_ptr -> opt;
                break;

            case OPTION_STATUS_CODE :
                status_ptr = (struct STATUS_CODE *) options_ptr -> opt_data;
                options_ptr = status_ptr -> opt;
                break;
		
	    default :
		options_ptr = 0;
		break;
        }
    }
    return 0;
}

int check_address_in_use (struct OPTIONS * options_ptr)
{
    int icmp_fd, size, n, hlen, icmp6len, msg_count = 0;
    char buff[1500], reply[1500];
    struct sockaddr_in6 sa;
    char name[64];
    struct icmp6_hdr * icmp6, * icmp6_reply;
    struct ip6_hdr * ip6;
    socklen_t len;
    struct IA_ADDRESS * iaaddr_ptr;
    struct timeval timeout;
    fd_set readfds, tempfds;
    
    return 0;
    
    timeout.tv_sec = 2;
    timeout.tv_usec = 0;
    iaaddr_ptr = (struct IA_ADDRESS *) options_ptr -> opt_data;
    FD_ZERO (&readfds);
    
    INITIALIZE_SOCKADDR (sa);
    inet_pton (AF_INET6, inet_ntop (AF_INET6, iaaddr_ptr -> addr, name, 64), &sa.sin6_addr);
    
    icmp_fd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
    size = 60 * 1024;
    setsockopt (icmp_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
    icmp6 = (struct icmp6_hdr *) buff;
    icmp6 -> icmp6_type = ICMP6_ECHO_REQUEST;
    icmp6 -> icmp6_code = 0;
    srand (time(NULL));
    icmp6 -> icmp6_id = rand ();
    icmp6 -> icmp6_seq = 0;
    icmp6 -> icmp6_cksum = 0;
    gettimeofday ((struct timeval *) (icmp6 + 1), 0);
    size = 8 + 56;
    sendto (icmp_fd, buff, size, 0, (struct sockaddr * ) &sa, sizeof (sa));
    
    FD_SET (icmp_fd, &readfds);

    while (msg_count < 10)
    {
	timeout.tv_sec = 2;
	timeout.tv_usec = 0;
	tempfds = readfds;
        select (icmp_fd + 1, &tempfds, 0, 0, &timeout);
	if (FD_ISSET (icmp_fd, &tempfds))
	{
	    msg_count++;
	    n = recvfrom (icmp_fd, reply, 1500, 0, 0, 0);
	    ip6 = (struct ip6_hdr *) reply;
	    hlen = sizeof (struct ip6_hdr);
	    if (ip6 -> ip6_nxt != IPPROTO_ICMPV6)
	    {
		continue;
	        return 0;
	    }
	    icmp6_reply = (struct icmp6_hdr *) (reply+hlen);
	    if ((icmp6len = n - hlen) < 8)
	    {
		continue;
	        return 0;
	    }
	    if (icmp6_reply -> icmp6_type == ICMP6_ECHO_REPLY)
	    {
	        if (icmp6_reply -> icmp6_id != icmp6 -> icmp6_id)
		{
		    continue;
	    	    return 0;
		}
	        if (icmp6len < 16)
		{
		    continue;
		    return 0;
		}
	    }
	}
        else
	{
	    msg_count++;
	    continue;
	    return 0;
	}
    }
    return 1;
}

void set_ipv6_address (struct OPTIONS * options_ptr, char * interface_name)
{
    struct IA_ADDRESS * iaaddr_ptr;
    char command [150];
    char addr[64];
        
    if (!options_ptr)
	return;
	
    iaaddr_ptr = (struct IA_ADDRESS *) options_ptr -> opt_data;
    inet_ntop (AF_INET6, iaaddr_ptr -> addr, addr, 64);
    strcpy (command, "/sbin/ifconfig ");
    strcat (command, interface_name);
    strcat (command, " add ");
    strcat (command, addr);
    strcat (command, "\0");
    
    system (command);
}

void unset_ipv6_address (struct OPTIONS * options_ptr, char * interface_name)
{
    struct IA_ADDRESS * iaaddr_ptr;
    char command [150];
    char addr[64];
        
    if (!options_ptr)
	return;
	
    iaaddr_ptr = (struct IA_ADDRESS *) options_ptr -> opt_data;
    inet_ntop (AF_INET6, iaaddr_ptr -> addr, addr, 64);
    strcpy (command, "/sbin/ifconfig ");
    strcat (command, interface_name);
    strcat (command, " del ");
    strcat (command, addr);
    strcat (command, "\0");
    
    system (command);
}

// Create the option node and the option data node which are linked and returns
// a pointer to the option node
struct OPTIONS * copy_message_option (struct DHCP_MESSAGE * src, int option_type)
{
    // Create an Options type node
    struct OPTIONS * dest_options_ptr = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
    struct OPTIONS * src_options_ptr = src -> opt;
    struct DUID * src_duid_ptr, * dest_duid_ptr;
    struct DUID1 * src_duid1_ptr, * dest_duid1_ptr;
    struct DUID2 * src_duid2_ptr, * dest_duid2_ptr;
    struct DUID3 * src_duid3_ptr, * dest_duid3_ptr;
    struct IA * src_ia_ptr, * dest_ia_ptr;
    struct IA_ADDRESS * src_iaaddr_ptr, * dest_iaaddr_ptr;
    struct PREFERENCE * src_preference_ptr;
    struct STATUS_CODE *src_status_code_ptr;
    int i;
        
    while (src_options_ptr)
    {
    
	if (src_options_ptr -> u_opt_code.opt_code == option_type)
	{
	    switch (option_type)
	    {
		case OPTION_CLIENTID :
		case OPTION_SERVERID :
            BLOG("BLOG: cp C_ID/S_ID\n");
		    // Assign the option type
		    dest_options_ptr -> u_opt_code.opt_code = option_type;
		    // Assign to the new option node len, the option len field of the advertise
		    dest_options_ptr -> u_opt_len.opt_len = src_options_ptr -> u_opt_len.opt_len;
		    // Allocate memory for the new option's duid
		    dest_duid_ptr = (struct DUID *) malloc (sizeof (struct DUID));
		    // Link the options node to the duid node
		    dest_options_ptr -> opt_data = dest_duid_ptr;
		    // Move on the duid node in the advertise message
		    src_duid_ptr = src_options_ptr -> opt_data;
		    // Assign the duid type field in the advertise message to the duid type field of the request message
		    dest_duid_ptr -> u_duid_type.duid_type = src_duid_ptr -> u_duid_type.duid_type;
		    // Assign zero to the next option pointer of the reply's duid node
		    dest_duid_ptr -> opt = 0;
		    switch (src_duid_ptr -> u_duid_type.duid_type)
		    {
			case 1 :
                BLOG("BLOG: cp DUID type1\n");
			    // Allocate space for the DUID1 node type
			    dest_duid1_ptr = (struct DUID1 *) malloc (sizeof (struct DUID1));
			    // Link the duid node to the duid1 node
			    dest_duid_ptr -> duid_type = dest_duid1_ptr;
			    // Move on the advertise message's duid1 node
			    src_duid1_ptr = (struct DUID1 *) src_duid_ptr -> duid_type;
			    // Copy the hardware type from the advertise message to the reply message
			    dest_duid1_ptr -> u_haddr_type.haddr_type = src_duid1_ptr -> u_haddr_type.haddr_type;
			    // Copy the time field from advertise to the request message
			    dest_duid1_ptr -> u_time.time = src_duid1_ptr -> u_time.time;
			    // Copy the hardware address length 
			    dest_duid1_ptr -> haddr_len = src_duid1_ptr -> haddr_len;
			    // Allocate space for the link layer address
			    dest_duid1_ptr -> link_layer_address = (u_int8_t *) malloc ((sizeof (u_int8_t)) * dest_duid1_ptr -> haddr_len);
			    // Memory copy the link layer address from the advertise to the request
			    memcpy (&dest_duid1_ptr -> link_layer_address, &src_duid1_ptr -> link_layer_address, dest_duid1_ptr -> haddr_len);
			    break;
			    
			case 2 :
                BLOG("BLOG: cp DUID type2\n");
			    dest_duid2_ptr = (struct DUID2 *) malloc (sizeof (struct DUID2));
			    dest_duid_ptr -> duid_type = dest_duid2_ptr;
			    src_duid2_ptr = (struct DUID2 *) src_duid_ptr -> duid_type;
			    dest_duid2_ptr -> u_identifier_length.identifier_length = src_duid2_ptr -> u_identifier_length.identifier_length;
			    dest_duid2_ptr -> identifier = (u_int8_t *) malloc ((sizeof (u_int8_t)) * dest_duid2_ptr -> u_identifier_length.identifier_length);
			    memcpy (&dest_duid2_ptr -> identifier, &src_duid2_ptr -> identifier, dest_duid2_ptr -> u_identifier_length.identifier_length);
			    dest_duid2_ptr -> domain_name_len = src_duid2_ptr -> domain_name_len;
			    dest_duid2_ptr -> domain_name = (char *) malloc ((sizeof (char)) * dest_duid2_ptr -> domain_name_len);
			    memcpy (&dest_duid2_ptr -> domain_name, &src_duid2_ptr -> domain_name, dest_duid2_ptr -> domain_name_len);
			    break;
			    
			case 3 :
                BLOG("BLOG: cp DUID type3\n");
			    dest_duid3_ptr = (struct DUID3 *) malloc (sizeof (struct DUID3));
			    dest_duid_ptr -> duid_type = dest_duid3_ptr;
			    src_duid3_ptr = (struct DUID3 *) src_duid_ptr -> duid_type;
			    dest_duid3_ptr -> u_haddr_type.haddr_type = src_duid3_ptr -> u_haddr_type.haddr_type;
			    dest_duid3_ptr -> haddr_len = src_duid3_ptr -> haddr_len;
			    dest_duid3_ptr -> link_layer_address = (u_int8_t *) malloc ((sizeof (u_int8_t)) * dest_duid3_ptr -> haddr_len);
			    memcpy (&dest_duid3_ptr -> link_layer_address, &src_duid3_ptr -> link_layer_address, dest_duid3_ptr -> haddr_len);
			    break;
            default:
                BLOG("BLOG: unknown DUID type3\n");
                break;
		    }
		    break;
		    
		case OPTION_IA :
             BLOG("BLOG: cp IA\n");
		    dest_options_ptr -> u_opt_code.opt_code = option_type;
		    dest_options_ptr -> u_opt_len.opt_len = src_options_ptr -> u_opt_len.opt_len;
		    dest_ia_ptr = (struct IA *) malloc (sizeof (struct IA));
		    dest_options_ptr -> opt_data = dest_ia_ptr;
		    src_ia_ptr = (struct IA *) src_options_ptr -> opt_data;
		    dest_ia_ptr -> u_iaid.iaid = src_ia_ptr -> u_iaid.iaid;
		    dest_ia_ptr -> u_t1.t1 = src_ia_ptr -> u_t1.t1;
		    dest_ia_ptr -> u_t2.t2 = src_ia_ptr -> u_t2.t2;
		    dest_ia_ptr -> status = src_ia_ptr -> status;
		    dest_ia_ptr -> opt = 0;
		    break;
		    
		case OPTION_IAADDR :
             BLOG("BLOG: cp IAADDR\n");
		    dest_options_ptr -> u_opt_code.opt_code = option_type;
		    dest_options_ptr -> u_opt_len.opt_len = src_options_ptr -> u_opt_len.opt_len;
		    dest_iaaddr_ptr = (struct IA_ADDRESS *) malloc (sizeof (struct IA_ADDRESS));
		    dest_options_ptr -> opt_data = dest_iaaddr_ptr;
		    src_iaaddr_ptr = (struct IA_ADDRESS *) src_options_ptr -> opt_data;
		    dest_iaaddr_ptr -> t_bit = src_iaaddr_ptr -> t_bit;
		    dest_iaaddr_ptr -> addr_status = src_iaaddr_ptr -> addr_status;
		    dest_iaaddr_ptr -> prefix_length = src_iaaddr_ptr -> prefix_length;
		    for (i = 0; i <= 15; i++)
			dest_iaaddr_ptr -> addr[i] = src_iaaddr_ptr -> addr[i];
		    dest_iaaddr_ptr -> u_pref_lifetime.pref_lifetime = src_iaaddr_ptr -> u_pref_lifetime.pref_lifetime;
		    dest_iaaddr_ptr -> u_valid_lifetime.valid_lifetime = src_iaaddr_ptr -> u_valid_lifetime.valid_lifetime;
		    dest_iaaddr_ptr -> ia_addr_opt = 0;
		    dest_iaaddr_ptr -> opt = 0;
		    break;
         default:
            BLOG("unknown type to copy... %d\n",option_type);
	    }
        BLOG("should return\n");
	    return dest_options_ptr;
	}
	else
	{
	    switch (src_options_ptr -> u_opt_code.opt_code)
	    {
		case OPTION_CLIENTID :
		case OPTION_SERVERID :
            BLOG("BLOG: walk C_ID/ S_ID\n");
		    src_duid_ptr = src_options_ptr -> opt_data;
		    src_options_ptr = src_duid_ptr -> opt;
		    break;
		
		case OPTION_IA :
            BLOG("BLOG: walk IA\n");
		    src_ia_ptr = src_options_ptr -> opt_data;
		    src_options_ptr = src_ia_ptr -> opt;
		    break;
		
		case OPTION_IAADDR :
            BLOG("BLOG: walk IA_ADDR\n");
		    src_iaaddr_ptr = src_options_ptr -> opt_data;
		    src_options_ptr = src_iaaddr_ptr -> opt;
		    break;
		    
		case OPTION_PREFERENCE :
            BLOG("BLOG: walk PREFERENCE\n");
		    src_preference_ptr = src_options_ptr -> opt_data;
		    src_options_ptr = src_preference_ptr -> opt;
		    break;

        case OPTION_STATUS_CODE:
            BLOG("BLOG: walk OPTION_STATUS_CODE\n");
            src_status_code_ptr=src_options_ptr -> opt_data;
            src_options_ptr = src_status_code_ptr -> opt;
            break;
            
        default:
            BLOG("unknown option code, wht???? code type: %d\n",src_options_ptr -> u_opt_code.opt_code);
            break;
	    }
	}
    }
    return 0;
}

void get_timers (struct DHCP_MESSAGE * server_reply, u_int32_t * renew_timer, u_int32_t * rebind_timer, u_int32_t * valid_lifetime_timer)
{
    struct OPTIONS * options_ptr;
    struct IA * ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    options_ptr = get_options_ptr (server_reply, OPTION_IA);
    if (!options_ptr)
	return;
    ia_ptr = (struct IA *) options_ptr -> opt_data;
    * renew_timer = ia_ptr -> u_t1.t1;
    * rebind_timer = ia_ptr -> u_t2.t2;
    options_ptr = get_options_ptr (server_reply, OPTION_IAADDR);
    if (!options_ptr)
	return;
    iaaddr_ptr = (struct IA_ADDRESS *) options_ptr -> opt_data;
    * valid_lifetime_timer = iaaddr_ptr -> u_valid_lifetime.valid_lifetime;
}

int check_for_duid_match (struct OPTIONS * options_ptr)
{
   extern struct DUID * server_duid_ptr;
   struct DUID * duid_ptr;
   struct DUID1 * duid1_ptr;
   struct DUID2 * duid2_ptr;
   struct DUID3 * duid3_ptr;
   
   if (!options_ptr)
	return 0;
   
   duid_ptr = (struct DUID *) options_ptr -> opt_data;
   if (server_duid_ptr -> u_duid_type.duid_type != duid_ptr -> u_duid_type.duid_type)
	return 0;

   switch (duid_ptr -> u_duid_type.duid_type)
   {
	case 1 :
	   duid1_ptr = (struct DUID1 *) duid_ptr -> duid_type;
	   if (!check_duid1 ((struct DUID1 *) server_duid_ptr -> duid_type, duid1_ptr))
		return 0;
	   break;
	case 2 :
	   duid2_ptr = (struct DUID2 *) duid_ptr -> duid_type;
	   if (!check_duid2 ((struct DUID2 *) server_duid_ptr -> duid_type, duid2_ptr))
		return 0;
	   break;
	case 3 :
	   duid3_ptr = (struct DUID3 *) duid_ptr -> duid_type;
	   if (!check_duid3 ((struct DUID3 *) server_duid_ptr -> duid_type, duid3_ptr))
		return 0;
	   break;
   }
   return 1;
}
   
int check_reply_message (struct DHCP_MESSAGE * reply_message, int sent_type)
{
    extern int g_trans_id;
    int status;
    struct OPTIONS *opt_ptr = get_options_ptr (reply_message, OPTION_IA);
    struct IA *ia_ptr;
    
    if (opt_ptr)
      ia_ptr = (struct IA *) opt_ptr -> opt_data;
    
    if (reply_message->u_msg_type.msg_type != REPLY)
	return 0;
	
    if (reply_message->u_trans_id.trans_id != g_trans_id)
       return 0;	
    
   switch (sent_type)
   {
	case REQUEST :
	   if ((status = check_for_status (reply_message)) != -1)
		if (status != Success)
		   return 0;
	   if (!check_for_option (OPTION_SERVERID, reply_message))
		return 0;
	   if (!check_for_option (OPTION_IA, reply_message))
		return 0;
	   if (!check_for_option (OPTION_IAADDR, reply_message))
		return 0;
	   break;

	case DECLINE :
	case RELEASE :
	   if (!check_for_duid_match (get_options_ptr (reply_message, OPTION_SERVERID)))
		return 0;
	   if ((status = check_for_status (reply_message)) != -1)
		if (status != Success)
		    return 0;
	   if (!check_for_option (OPTION_SERVERID, reply_message))
		return 0;
	   break;

	case RENEW :
	case REBIND :
	   if (check_for_duid_match (get_options_ptr (reply_message, OPTION_SERVERID)))
		return 1;
	   else
		return 0;
	   break;
   }
    return 1;
}

char ** get_all_interfaces (char * file_name, int * count)
{
    char ** interface = 0 , ** p = 0;
    char * buff = 0, * curr_ptr = 0, *token = 0;
    read_config_file (&buff, DEFAULT_SOLICIT_CONFIG_FILE);
    curr_ptr = buff;
    *count = 0;
    
    while (move_across_substring (&curr_ptr, "interface"))
    {
	skip_whitespace (&curr_ptr);
	read_token (&token, &curr_ptr);
	interface = (char **) realloc (interface, ++(*count) * sizeof (char *));
	p = interface + (*count) -1;
	*p = (char *) malloc (strlen (token) * sizeof (char));
	strcpy (*p, token);
    }
    return interface;
}

int check_ia_status (struct DHCP_MESSAGE * message)
{
    struct OPTIONS * options_ptr = get_options_ptr (message, OPTION_IA);
    struct IA * ia_ptr;
    if (!options_ptr)
	return 0;
    ia_ptr = (struct IA *) options_ptr -> opt_data;
    if (ia_ptr -> status == RenwNoMatch || ia_ptr -> status == RebdNoMatch || ia_ptr -> status == AddrUnavail || ia_ptr -> status == NoBinding)
	return ia_ptr -> status;
    return 0;
}

void copy_duid1 (struct DUID1 *d1, struct DUID1 *duid)
{
    int i;
    
    duid -> u_haddr_type.haddr_type = d1 -> u_haddr_type.haddr_type;
    duid -> haddr_len = d1 -> haddr_len;
    duid -> u_time.time = d1 -> u_time.time;
    duid -> link_layer_address = (u_int8_t *) malloc (d1 -> haddr_len * sizeof (u_int8_t));
    for (i = 0; i < d1 -> haddr_len; i++)
	duid -> link_layer_address[i] = d1 -> link_layer_address[i];
}

void copy_duid2 (struct DUID2 *d2, struct DUID2 *duid)
{
    int i;
    
    duid -> u_identifier_length.identifier_length = d2 -> u_identifier_length.identifier_length;
    duid -> domain_name_len = d2 -> domain_name_len;
    duid -> identifier = (u_int8_t *) malloc (d2 -> u_identifier_length.identifier_length * sizeof (u_int8_t));
    for (i = 0; i < d2 -> u_identifier_length.identifier_length; i++)
	duid -> identifier[i] = d2 -> identifier[i];
    duid -> domain_name = (char *) malloc (d2 -> domain_name_len * sizeof (char));
    for (i = 0; i < d2 -> domain_name_len; i++)
	duid -> domain_name[i] = d2 -> domain_name[i];
}

void copy_duid3 (struct DUID3 *d3, struct DUID3 *duid)
{
    int i;
    
    duid -> u_haddr_type.haddr_type = d3 -> u_haddr_type.haddr_type;
    duid -> haddr_len = d3 -> haddr_len;
    duid -> link_layer_address = (u_int8_t *) malloc (d3 -> haddr_len * sizeof (u_int8_t));
    for (i = 0; i < d3 -> haddr_len; i++)
	duid -> link_layer_address[i] = d3 -> link_layer_address[i];
}

int check_duid1 (struct DUID1 *d1, struct DUID1 *duid)
{
    int i;
    
    if (duid -> u_haddr_type.haddr_type != d1 -> u_haddr_type.haddr_type)
      return 0;
    if (duid -> u_time.time != d1 -> u_time.time)
      return 0;
    if (duid -> haddr_len != d1 -> haddr_len)
      return 0;
    for (i = 0; i < d1 -> haddr_len; i++)
      if (duid -> link_layer_address[i] != d1 -> link_layer_address[i])
	   return 0;
    return 1;
}

int check_duid2 (struct DUID2 *d2, struct DUID2 *duid)
{
    int i;
    
    if (duid -> u_identifier_length.identifier_length != d2 -> u_identifier_length.identifier_length)
      return 0;
    if (duid -> domain_name_len != d2 -> domain_name_len)
      return 0;
    for (i = 0; i < d2 -> u_identifier_length.identifier_length; i++)
      if (duid -> identifier[i] != d2 -> identifier[i])
	   return 0;
    for (i = 0; i < d2 -> domain_name_len; i++)
      if (duid -> domain_name[i] != d2 -> domain_name[i])
	   return 0;
    return 1;
}

int check_duid3 (struct DUID3 *d3, struct DUID3 *duid)
{
    int i;
    
    if (duid -> u_haddr_type.haddr_type != d3 -> u_haddr_type.haddr_type)
      return 0;
    if (duid -> haddr_len != d3 -> haddr_len)
      return 0;
    for (i = 0; i < d3 -> haddr_len; i++)
      if (duid -> link_layer_address[i] != d3 -> link_layer_address[i])
	   return 0;
    return 1;
}
