#include "stdhead.h"
#include "lib.h"

void write_to_log (char * str, int time_flag)
{
   time_t curr_time;
   int time_length;
   char *str_curr_time;
   int log_fd; 
   if ((log_fd= open (DEFAULT_EVENT_LOG_FILE, O_CREAT|O_APPEND|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH)) == -1)
   {
	printf ("Failed to open event log file.\n");
	return;
   }
   
   if (time_flag)
   {
	curr_time = time (&curr_time);
	str_curr_time = ctime (&curr_time);
	write (log_fd, str_curr_time, strlen (str_curr_time));
	write (log_fd, "\t", 1);
   }
   if ((write (log_fd, str, strlen (str))) == -1)
	printf ("Failed to log event.\n");
   if (time_flag)
	write (log_fd, "\n", 1);
   close (log_fd);
}
	

// Safe free function
void safe_free (void *ptr)
{
   if (ptr)
	free (ptr);
}

// Join an IPv6 multicast group
int mcast_join (int socket, char *interface_name, char *mcast_addr)
{
   struct ipv6_mreq mreq6;
   
   mreq6.ipv6mr_interface = if_nametoindex (interface_name);
   inet_pton (AF_INET6, mcast_addr, &mreq6.ipv6mr_multiaddr);
   return (setsockopt (socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof (mreq6)));
}

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++];
    BLOG("BLOG: message type: %d\n",dhcp_message ->u_msg_type.buffer[0]);
    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));
    read_option (m, index, len, opt);
    dhcp_message -> opt = 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 *pref_ptr;
    struct STATUS_CODE *sc_ptr;
    struct ELAPSED_TIME *et_ptr;
    struct UN_SUPPORT *un_ptr;
    int i;
    int loop_protect=0;
    char name[64];
    
    printf ("\nMessage 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;

    if (!q)
    {
	printf ("No options defined!\n");
	return;
   }
	
    do
    {
        loop_protect++;
        if(loop_protect>30){        //loop protection, to avoid loop...   //bruce hsu
            printf ("Maybe loop happend...\n");    
            break;
        }
	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]);
            //may do not have domain name...
            //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 addr_len = %d\n", d3->haddr_len);
		    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;
		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", ia_addr -> u_pref_lifetime.pref_lifetime);
		printf ("\nValid lifetime = %d", ia_addr -> u_valid_lifetime.valid_lifetime);
		printf ("\n\n");
		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);
		pref_ptr = q -> opt_data;
		
		printf ("Preference value = %d\n\n", pref_ptr -> preference_value);
		q = pref_ptr -> 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;
        
        case OPTION_ELAPSED:
        printf ("Option type = elapsed time\n");
		printf ("option length = %d\n", q -> u_opt_len.opt_len);
		et_ptr = q ->opt_data;
		
		printf ("Elapsed time = %d\n", et_ptr ->u_elapsed_time.elapsed_time);
		
		q = et_ptr -> opt;
		break;
        
	   default :
		printf ("Invalid option type\n\n");
        printf ("option length = %d\n", q -> u_opt_len.opt_len);
        un_ptr=q->opt_data;
        q=un_ptr->opt;
        printf ("option ptr = %x\n", q);
		//q = 0;
		break;
	}
    }
    while (q != 0);
    printf ("print ok...\n ");
}

/* 
    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 PREFERENCE *pref_ptr;
    struct STATUS_CODE *sc_ptr;

    // MESSAGE TYPE
    *buff = dhcp_message->u_msg_type.buffer[0];
    buff++;
    
    // TRANSACTION ID
    for (i = 2; i >= 0; i--)
    {
	*buff = dhcp_message->u_trans_id.buffer[i];
	buff++;
    }
    
    options_ptr = dhcp_message->opt;
    while (options_ptr)
    {
	    // 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++;
    
	    switch (options_ptr -> u_opt_code.opt_code)
        {
    	    case OPTION_CLIENTID :
	        case OPTION_SERVERID :
		        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 :		
			            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
			            for (i = 3; i >= 0; i--)
			            {
			                *buff = duid1_ptr->u_time.buffer[i];		
			                buff++;
		                }
			
			            // 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 = duid2_ptr -> domain_name_len; i >= 0; 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 :
		        // IA OPTION DATA
		        ia_ptr = (struct IA *)options_ptr->opt_data;
   
    		    // IAID
    		    for (i = 3; i >= 0; i--)
		        {
	    	        *buff = ia_ptr->u_iaid.buffer[i];
	    	        buff++;
		        }	
    
    		    //  T1
    		    for (i = 3; i >= 0; i--)
    		    {
	    	        *buff = ia_ptr->u_t1.buffer[i];
	    	        buff++;
		        }	
    
		        // T2
		        for (i = 3; i >= 0; i--)
		        {
	    	        *buff = ia_ptr->u_t2.buffer[i];
	    	        buff++;
	        	}

    		    // STATUS
    		    //*buff = ia_ptr->status;
    		    //buff++;
    
   		        options_ptr = ia_ptr->opt;
		        break;	    

	        case OPTION_IAADDR :
		        ia_addr_ptr = (struct IA_ADDRESS *) options_ptr->opt_data;
   
		        // ADDRESS STATUS
		        *buff = ia_addr_ptr->addr_status;

                //no these field...
		        // 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_PREFERENCE :
		        pref_ptr = (struct PREFERENCE *) options_ptr -> opt_data;
		
		        // Preference value
		        *buff = pref_ptr -> preference_value;
		        buff++;
		
		        // Next option
		        options_ptr = pref_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
		        //why should i decrease 2? because we use option length,
		        //so we should decrease status code length   //bruce hsu
		        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;
	    }
    }
	
    // RETURN THE TOTAL NUMBER OF BYTES
    return (abs (start-buff));
}

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)
{
    time_t t;
    srand (time (&t));
    *trans_id = 0;
    *trans_id = rand();
    // Make the upper byte of trans id = 0
    *trans_id &= 0x00ffffff;
}

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 PREFERENCE *pref_ptr;
    struct STATUS_CODE *sc_ptr;
    struct ELAPSED_TIME *et_ptr;
    struct UN_SUPPORT *un_ptr;
    struct OPTIONS *next_opt;
    u_int8_t *l_ptr;
    union
    {
	char buffer[2];
	u_int16_t type_of_duid;
    } u_type_of_duid;
    
    for (i = 1; i >= 0; i--)
	opt ->u_opt_code.buffer[i] = m[index++];
	
    for (i = 1; i >= 0; i--)
      opt ->u_opt_len.buffer[i] = m[index++];

    switch (opt ->u_opt_code.opt_code)
    {
	    case OPTION_CLIENTID :
        case OPTION_SERVERID :
            BLOG("BLOG: opt c_id/s_id\n");
	        d = (struct DUID *) malloc (sizeof (struct DUID));
	        opt ->opt_data = d;
	    
	        for (i = 1; i >= 0; i--)
		    u_type_of_duid.buffer[i] = m[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++];
		            for (i = 3; i >= 0; i--)
			            d1 ->u_time.buffer[i] = m[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);
		            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 :
		            d3 = (struct DUID3 *) malloc (sizeof (struct DUID3));
		            d ->u_duid_type.duid_type = 3;
		    
		            for (i = 1; i >= 0; i--)
			            d3 ->u_haddr_type.buffer[i] = m[index++];

		            // DUID Type = 2 octets
		            // Hardware type field = 2 octets
		            d3 ->haddr_len = opt ->u_opt_len.opt_len - 2 - 2;
                    BLOG("D3 length: %d\n",d3 ->haddr_len);
		            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;
		            break;

		        default :
              #if DEBUG == 2
		        printf ("Unrecognized DUID type\n");
              #endif
		        index += opt ->u_opt_len.opt_len;
		        break;
	        }

	        next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
	        if (index == len){
		        d -> opt = 0;
	        }else{
		        read_option (m, index, len, next_opt);
		        d -> opt = next_opt;
	        }
	    break;
	    
	    case OPTION_IA :
            BLOG("BLOG: opt ia\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++];
	        //ia ->status = m[index++];
            next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
            if (index == len){
                BLOG("BLOG: Bruce check!!!  %d\n",len);
		        ia -> opt = 0;
	        }else{
		        read_option (m, index, len, next_opt);
		        ia -> opt = next_opt;
	        }
	    break;
	    
	    case OPTION_IAADDR :
            BLOG("BLOG: opt ia address\n");
	        ia_addr = (struct IA_ADDRESS *) malloc (sizeof (struct IA_ADDRESS));
	        opt ->opt_data = ia_addr;
		    ia_addr -> t_bit = 0;   // set temp bit to 0 
	        //ia_addr -> addr_status = temp % 128;
	        //ia_addr -> prefix_length = m[index++];    //no prefix length...
	        for (i = 0; i <16; i++)
		        ia_addr -> addr[i] = m[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_PREFERENCE :
            BLOG("BLOG: opt preference\n");
	        pref_ptr = (struct PREFERENCE *) malloc (sizeof (struct PREFERENCE));
	        opt ->opt_data = pref_ptr;
	        pref_ptr ->preference_value = m[index++];
	    
	        next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
	        if (index == len){
		        pref_ptr -> opt = 0;
	        }else{
		        read_option (m, index, len, next_opt);
		    pref_ptr -> opt = next_opt;
	        }
	    break;
	    
	    case OPTION_STATUS_CODE :
            BLOG("BLOG: opt status code\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));
	        for (i = opt ->u_opt_len.opt_len - 2; i >= 0; i--)
		        sc_ptr ->message[i] = m[index++];

	        next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
	        if (index == len){
		        sc_ptr -> opt = 0;
	        }else{
		        read_option (m, index, len, next_opt);
		        sc_ptr -> opt = next_opt;
	        }
	    break;
            
        case OPTION_ELAPSED:
            //we do not support elapsed time check now, i pass this option (only save information)
            //but i think this should be added.   //Bruce Hsu

            BLOG("BLOG: opt elapsed time\n");
            et_ptr= (struct ELAPSED_TIME*)malloc(sizeof(struct ELAPSED_TIME));
            opt->opt_data=et_ptr;
            for (i = 1; i >= 0; i--){
		        et_ptr ->u_elapsed_time.buffer[i] = m[index++];
            }
            next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
	        if (index == len){
		        et_ptr -> opt = 0;
	        }else{
		        read_option (m, index, len, next_opt);
		        et_ptr -> opt = next_opt;
	        }
            
        break;            
	    default:
            //if we can not support this option, skip this option    //bruce hsu
	        printf ("Invalid option type: %d\n",opt ->u_opt_code.opt_code);
            un_ptr=(struct UN_SUPPORT*)malloc(sizeof(struct UN_SUPPORT));
            opt->opt_data=un_ptr;
            BLOG("BLOG: ori index: %d\n",index);
            index+=opt->u_opt_len.opt_len;
            BLOG("BLOG: new index: %d\n",index);
            next_opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
            BLOG("BLOG: LEN: %d\n",len);
	        if (index == len){
                printf("parser ok...\n");
		        un_ptr -> opt = 0;
	        }else{
		        read_option (m, index, len, next_opt);
		        un_ptr -> opt = next_opt;
	        }
	    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 *pref_ptr;
    struct STATUS_CODE *sc_ptr;
    
    options_ptr = dhcp_message->opt;

    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;
		safe_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;
			safe_free (duid1_ptr -> link_layer_address);
			safe_free (duid1_ptr);
			break;
			
		    case 2 :
			duid2_ptr = (struct DUID2 *) duid_ptr->duid_type;
			safe_free (duid2_ptr->identifier);
			safe_free (duid2_ptr->domain_name);
			safe_free (duid2_ptr);
			break;

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

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

/* 
    This function checks whether that particular option exists in the linked
    list. It returns 1 on 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;
    struct ELAPSED_TIME* et_ptr;
    
    // Move to the first option node
    opt= dhcp_msg_ptr->opt;
    
    // Until there are no more options in the message
    while (opt)
    {
	// 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;

        case OPTION_ELAPSED:
        // Move to the next node
        et_ptr=(struct ELAPSED_TIME*)opt->opt_data;
        opt = et_ptr->opt;
        break;
	
	   default :
		opt = 0;
		break;
	}	
    }
    return 0;
}   

int check_duid (int option, struct DHCP_MESSAGE *dhcp_msg_ptr, struct lease_details *lease_struct)
{
    extern struct DUID * server_duid;
    struct OPTIONS * opt;
    struct DUID * duid_ptr;
    struct DUID1 * server_duid1_ptr, * duid1_ptr;
    struct DUID2 * server_duid2_ptr, * duid2_ptr;
    struct DUID3 * server_duid3_ptr, * duid3_ptr;
    struct IA *ia_ptr;
    struct IA_ADDRESS * iaaddr_ptr;
    struct STATUS_CODE * status_ptr;
    struct PREFERENCE * preference;
    struct ELAPSED_TIME* et_ptr;
    u_int8_t * server_link_layer_address, * link_layer_address;
    u_int8_t * server_identifier, * identifier;
    char * server_domain_name, * domain_name;

    // Move to the first option node
    opt = dhcp_msg_ptr->opt;
    
    // Until there are no more options in the message
    while (opt)
    {
	switch(opt->u_opt_code.opt_code)
	{
	    case OPTION_CLIENTID :
		if (option != OPTION_CLIENTID)
		{
		    // Move to the next node
		    duid_ptr = (struct DUID *) opt -> opt_data;
		    opt = duid_ptr->opt;
		}else if (check_for_duid_match (dhcp_msg_ptr, lease_struct)){
		    BLOG("check C_ID ok\n ");
		    return 1;
		}else{
		    BLOG("check C_ID fail\n ");
		    return 0;
        }
		break;
		
	    case OPTION_SERVERID :
		if (option != OPTION_SERVERID)
		{
		    // Move to the next node
		    duid_ptr = (struct DUID *) opt -> opt_data;
		    opt = duid_ptr->opt;
		}
		else
		{
		    BLOG("4444\n ");
		    duid_ptr = (struct DUID *) opt -> opt_data;
		    switch (server_duid -> u_duid_type.duid_type)
		    {
			case 1 :
			    if (duid_ptr -> u_duid_type.duid_type != 1)
				return 0;
			    server_duid1_ptr = (struct DUID1 *) server_duid -> duid_type;
			    duid1_ptr = (struct DUID1 *) duid_ptr -> duid_type;
			    if (server_duid1_ptr -> u_haddr_type.haddr_type != duid1_ptr -> u_haddr_type.haddr_type)
				return 0;
			    if (server_duid1_ptr -> u_time.time  != duid1_ptr -> u_time.time)
				return 0;
			    if (server_duid1_ptr -> haddr_len != duid1_ptr -> haddr_len)
				return 0;
			    server_link_layer_address = server_duid1_ptr -> link_layer_address;
			    link_layer_address = duid1_ptr -> link_layer_address;
			    if (memcmp (server_link_layer_address, link_layer_address, duid1_ptr -> haddr_len))
				return 0;
			    break;
		
			case 2 :
			    if (duid_ptr -> u_duid_type.duid_type != 2)
				return 0;
			    server_duid2_ptr = (struct DUID2 *) server_duid -> duid_type;
			    duid2_ptr = (struct DUID2 *) duid_ptr -> duid_type;
			    if (server_duid2_ptr -> u_identifier_length.identifier_length != duid2_ptr -> u_identifier_length.identifier_length)
				return 0;
    			    server_identifier = server_duid2_ptr -> identifier;
    			    identifier = duid2_ptr -> identifier;
			    if (memcmp (server_identifier, identifier, duid2_ptr -> u_identifier_length.identifier_length))
				return 0;
			    if (server_duid2_ptr -> domain_name_len != duid2_ptr -> domain_name_len)
				return 0;
			    server_domain_name = server_duid2_ptr -> domain_name;
			    domain_name = duid2_ptr -> domain_name;
			    if (memcmp (server_domain_name, domain_name, duid2_ptr -> domain_name_len))
				return 0;
			    break;
		    
			case 3 :
			    if (duid_ptr -> u_duid_type.duid_type != 3)
				return 0;
			    server_duid3_ptr = (struct DUID3 *) server_duid -> duid_type;
			    duid3_ptr = (struct DUID3 *) duid_ptr -> duid_type;
			    if (server_duid3_ptr -> u_haddr_type.haddr_type != duid3_ptr -> u_haddr_type.haddr_type)
				return 0;
			    if (server_duid3_ptr -> haddr_len != duid3_ptr -> haddr_len)
				return 0;
			    server_link_layer_address = server_duid3_ptr -> link_layer_address;
			    link_layer_address = duid3_ptr -> link_layer_address;
			    if (memcmp (server_link_layer_address, link_layer_address, duid3_ptr -> haddr_len))
				return 0;
			    break;
		    }
		    return 1;
		}
		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;

        case OPTION_ELAPSED:        //add by bruce
            // Move to the next node
            et_ptr=(struct ELAPSED_TIME*)opt->opt_data;
            opt=et_ptr->opt;
	        break;
            
	   default :
		opt = 0;
		break;
	}	
    }
    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)
{
    extern struct lease_details *partial_lease, *lease;
    struct OPTIONS *options_ptr;
    extern int g_trans_id;

    // Check whether the received message is of the right type
    if (dhcp_message_ptr->u_msg_type.msg_type != recv_type)
	    return 0;

    switch (dhcp_message_ptr->u_msg_type.msg_type)
    {
	// Check for validity of the solicit message
	case SOLICIT :
	    if (!check_for_option (OPTION_CLIENTID, dhcp_message_ptr))
		return 0;
	    if (!check_for_option (OPTION_IA, dhcp_message_ptr))
		return 0;
        //if no ia_addr for solicit msg, it's ok...
	    //if (!check_for_option (OPTION_IAADDR, dhcp_message_ptr))
		//return 0;
	    break;
	    
	case REQUEST :
	    if (!check_for_option (OPTION_CLIENTID, dhcp_message_ptr))
		return 0;
        BLOG("1\n ");
	    if (!check_for_option (OPTION_SERVERID, dhcp_message_ptr))
		return 0;
        BLOG("2\n ");
	    if (!check_for_option (OPTION_IA, dhcp_message_ptr))
		return 0;
        BLOG("3\n ");
	    if (!check_for_option (OPTION_IAADDR, dhcp_message_ptr))
		return 0;
        BLOG("4\n ");
	    if (!check_duid (OPTION_SERVERID, dhcp_message_ptr, partial_lease))
		return 0;
        BLOG("5\n ");
        if (!check_duid (OPTION_CLIENTID, dhcp_message_ptr, partial_lease))
		return 0;
        BLOG("6\n ");
	    if (!check_for_match (dhcp_message_ptr, partial_lease)){
            //i pass this check, we should check rfc to modify it...   //bruce hsu
	        //return 0;
        }
        BLOG("7\n ");
	    break;
	
	case RELEASE :
	case DECLINE :
        BLOG("r1\n ");
	    if (!check_for_option (OPTION_CLIENTID, dhcp_message_ptr))
		return 0;
        BLOG("r2\n ");
	    if (!check_for_option (OPTION_SERVERID, dhcp_message_ptr))
		return 0;
        BLOG("r3\n ");
	    if (!check_for_option (OPTION_IA, dhcp_message_ptr))
		return 0;
        BLOG("4\n ");
	    if (!check_for_option (OPTION_IAADDR, dhcp_message_ptr))
		return 0;
        BLOG("r5\n ");
	    if (!check_duid (OPTION_SERVERID, dhcp_message_ptr, lease))
		return 0;
        BLOG("r6\n ");
	    if (!check_duid (OPTION_CLIENTID, dhcp_message_ptr, lease))
		return 0;
        BLOG("r7\n ");
	    if (!check_for_match (dhcp_message_ptr, lease)){
            //i pass this check, because valid time is 0 in release msg,
            // but we should review the code later...      //bruce hsu
	        //return 0;
        }
        BLOG("r8\n ");
	    break;
	    
	case RENEW :
	    if (!check_for_option (OPTION_CLIENTID, dhcp_message_ptr))
		return 0;
	    if (!check_for_option (OPTION_SERVERID, dhcp_message_ptr))
		return 0;
	    if (!check_for_option (OPTION_IA, dhcp_message_ptr))
		return 0;
	    if (!check_for_option (OPTION_IAADDR, dhcp_message_ptr))
		return 0;
	    break;

	case REBIND :
	    if (!check_for_option (OPTION_CLIENTID, dhcp_message_ptr))
		return 0;
	    if (!check_for_option (OPTION_IA, dhcp_message_ptr))
		return 0;
	    if (!check_for_option (OPTION_IAADDR, dhcp_message_ptr))
		return 0;
	    break;
    }
    BLOG("check message ok...\n");
    return 1;
}

int check_duid1 (struct DUID1 *d1, struct DUID1 *duid)
{
    int i;
    BLOG("check duid1\n");
    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;
    BLOG("check duid2\n");
    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;
    char *bruce;
    BLOG("duid3 address: %x\n",d3);
    bruce=(char*)d3;
    for(i=0;i<10;i++){
        BLOG(" %x",*bruce); 
        bruce++;
    }
    BLOG("\n",d3);
    if (duid -> u_haddr_type.haddr_type != d3 -> u_haddr_type.haddr_type){
        BLOG("check duid3-1\n");
      return 0;
    }
    if (duid -> haddr_len != d3 -> haddr_len){
        BLOG("check duid3-2 %d , %d\n",d3->haddr_len, duid->haddr_len );
      return 0;
    }
    for (i = 0; i < d3 -> haddr_len; i++){
        BLOG("check duid3-3\n");
      if (duid -> link_layer_address[i] != d3 -> link_layer_address[i]){
	      return 0;
      }
    }
    BLOG("check duid3 ok\n");
    return 1;
}

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_for_duplicate_packet (struct DHCP_MESSAGE *dhcp_message)
{
   extern struct lease_details *partial_lease, *lease;
   
   switch (dhcp_message -> u_msg_type.msg_type)
   {
	case SOLICIT :
	    if (!partial_lease)
		return 0;

	   if (check_duid (OPTION_CLIENTID, dhcp_message, partial_lease))
		if (check_duid (OPTION_SERVERID, dhcp_message, partial_lease))
		   if (check_for_match (dhcp_message, partial_lease))
			return 1;
	   break;
	   
	case REQUEST :
	case RENEW :
	   if (!lease)
		return 0;
		
	   if (check_duid (OPTION_CLIENTID, dhcp_message, lease))
		if (check_duid (OPTION_SERVERID, dhcp_message, lease))
		   if (check_for_match (dhcp_message, lease))
			return 1;
	   break;
	
	case REBIND :
	   if (!lease)
		return 0;
		
	   if (check_duid (OPTION_CLIENTID, dhcp_message, lease))
		   if (check_for_match (dhcp_message, lease))
			return 1;
	   break;
	
	case DECLINE :
        if (!lease)
		return 0;
		
	   if (check_duid (OPTION_CLIENTID, dhcp_message, lease))
		if (check_duid (OPTION_SERVERID, dhcp_message, lease))
		   if (check_for_match (dhcp_message, lease))
			return 0;
	   // here, non existence of lease node indicates duplicate DECLINE packet
	   return 1;
       break;
	case RELEASE :  //i feel confused in judge release message...
	   if (!lease)
		return 0;
		
	   if (check_duid (OPTION_CLIENTID, dhcp_message, lease))
		if (check_duid (OPTION_SERVERID, dhcp_message, lease))
		   if (check_for_match (dhcp_message, lease))
			return 0;
       break;
   }
   
   return 0;
}

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;
    struct ELAPSED_TIME *et_ptr;
    struct UN_SUPPORT* un_ptr;

    while (options_ptr != NULL)
    {
        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;
                
            case OPTION_ELAPSED:
                et_ptr = (struct ELAPSED_TIME*) options_ptr -> opt_data;
                options_ptr = et_ptr -> opt;
                break;
                
		    default :
                un_ptr = (struct UN_SUPPORT *) options_ptr -> opt_data;
                options_ptr = un_ptr -> opt;
		        //options_ptr = 0;
		        BLOG("OPT PTR %x\n",options_ptr);
		        break;
        }
    }
    return 0;
}

struct OPTIONS * send_server_id (struct OPTIONS * opt_ptr)
{
    extern struct DUID *server_duid;
    struct DUID *duid_ptr;
    struct DUID1 *duid1_ptr;
    struct DUID2 *duid2_ptr;
    struct DUID3 *duid3_ptr;
    
    opt_ptr -> u_opt_code.opt_code = OPTION_SERVERID;
    opt_ptr -> opt_data = (struct DUID *) malloc (sizeof (struct DUID));
    duid_ptr = (struct DUID *) opt_ptr -> opt_data;    
    duid_ptr -> u_duid_type.duid_type = server_duid -> u_duid_type.duid_type;

    BLOG("Server DUID type %d\n",duid_ptr ->u_duid_type.duid_type);
    switch (duid_ptr -> u_duid_type.duid_type)
    {
	case 1 :
	    duid_ptr -> duid_type = (struct DUID1 *) malloc (sizeof (struct DUID1));
	    duid1_ptr = (struct DUID1 *) duid_ptr -> duid_type;
	    copy_duid1 ((struct DUID1 *) server_duid -> duid_type, duid1_ptr);
	    
	    // DUID type -> 2 octets
	    // Time -> 4 octets
	    // Hardware type -> 2 octets
	    opt_ptr -> u_opt_len.opt_len = 2 + 4 + 2 + duid1_ptr -> haddr_len;
    	    break;
	
	case 2 :
	    duid_ptr -> duid_type = (struct DUID2 *) malloc (sizeof (struct DUID2));
	    duid2_ptr = (struct DUID2 *) duid_ptr -> duid_type;
	    copy_duid2 ((struct DUID2 *) server_duid -> duid_type, duid2_ptr);
	    
	    // DUID type -> 2 octets
	    // Identifier length -> 2 octets
	    opt_ptr -> u_opt_len.opt_len = 2 + 2 + duid2_ptr -> u_identifier_length.identifier_length + duid2_ptr -> domain_name_len;
    	    break;
	
	case 3 :
	    duid_ptr -> duid_type = (struct DUID3 *) malloc (sizeof (struct DUID3));
	    duid3_ptr = (struct DUID3 *) duid_ptr -> duid_type;
	    copy_duid3 ((struct DUID3 *) server_duid -> duid_type, duid3_ptr);
	    
	    // DUID type -> 2 octets
	    // Hardware type -> 2 octets
	    opt_ptr -> u_opt_len.opt_len = 2 + 2 + duid3_ptr -> haddr_len;
    	    break;
    }
    
    opt_ptr = duid_ptr -> opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
    return opt_ptr;
}

struct DHCP_MESSAGE * create_message_with_status_code (struct DHCP_MESSAGE *message, int message_type, int status_code, char *status_message)
{
    struct DHCP_MESSAGE *dhcp_message_ptr = (struct DHCP_MESSAGE *) malloc (sizeof (struct DHCP_MESSAGE));
    struct OPTIONS *opt_ptr;
    struct STATUS_CODE *sc_ptr;
    int i;
    
    // MESSAGE TYPE
    dhcp_message_ptr->u_msg_type.msg_type = message_type;

    // Copy TRANSACTION ID from solicit message
    dhcp_message_ptr -> u_trans_id.trans_id = message -> u_trans_id.trans_id;

    // ASSIGN MEMORY FOR OPTIONS
    opt_ptr = dhcp_message_ptr->opt = (struct OPTIONS *) malloc (sizeof (struct OPTIONS));
    
    sc_ptr = (struct STATUS_CODE *) malloc (sizeof (struct STATUS_CODE));
    sc_ptr -> u_status_code.status_code = status_code;
    sc_ptr -> message = (char *) malloc (strlen (status_message) * sizeof (char));
    for (i = 0; i < strlen (status_message); i++)
	sc_ptr -> message[i] = status_message[i];
    sc_ptr -> opt = 0;

    opt_ptr -> u_opt_code.opt_code = OPTION_STATUS_CODE;
    // Status code = 2 octets
    opt_ptr -> u_opt_len.opt_len = strlen (status_message) + 2;
    opt_ptr -> opt_data = (struct STATUS_CODE *) sc_ptr;
    return dhcp_message_ptr;
}
