Thread: DNS message format

  1. #1
    Registered User
    Join Date
    Sep 2003
    Posts
    224

    DNS message format

    Hello,
    I have some questions about the DNS message format:
    1. What goes in the "identification" field? Can you put the socket id?
    2. In the question section, is the domain name stored in a multiple of 32 bits?
    3. How do you represent the message format in a structure or what is the best way to do so? I have the following:
    Code:
    typedef struct header_t
    {
     ushort id;
     ushort parameter;
     ushort num_questions;
     ushort num_answers;  
     ushort num_authority;
     ushort num_additional;
    
    } header;
    But I don't know what to do for the others.

    Thanks,
    Yasir
    Last edited by Yasir_Malik; 02-14-2005 at 02:51 PM. Reason: Wrong struct name

  2. #2
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    I think the following code creates the properly formatted question section:
    Code:
    /*
     * domain_name: domain name
     *
     * returns label formatted question
     */
    char *format_question(const char *domain_name)
    {
     char length = strlen(domain_name);
    
     // hold length octet (1) and 4 octet query type and name
     char *formatted_name = (char *) malloc(length + 5);
    
     int query_length = length + 5;
    
     formatted_name[0] = length;
     strcat(formatted_name, domain_name);
    
     // query type
     formatted_name[query_length - 4] = 0x00;
     formatted_name[query_length - 3] = 0x01;
    
     // query class
     formatted_name[query_length - 2] = 0x00;
     formatted_name[query_length - 1] = 0x01;
    
     return formatted_name;
    }
    Is that right? Is the query domain name supposed to be null terminated? Also, for an answer resource record, does the resource data contain the IP address?

    Thanks,
    Yasir
    Last edited by Yasir_Malik; 02-14-2005 at 03:09 PM. Reason: strcpy -> strcat

  3. #3
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >> 1. What goes in the "identification" field? Can you put the socket id? <<

    You use this field to match queries with replies. A commonly used value is the process ID.

    >> 2. In the question section, is the domain name stored in a multiple of 32 bits? <<

    No, I don't think so.

    >> 3. How do you represent the message format in a structure or what is the best way to do so? I have the following: <<

    That looks correct. For maximum readability and portability, you should use a width specified type like uint16_t rather than ushort. Remember, that each field must be in network byte order. This means you should use the htons function to convert from host to network byte order:
    Code:
    header.num_questions = htons(1);
    >> I think the following code creates the properly formatted question section: <<

    Not quite. The domain name must be in this format:
    Code:
    	/* Domain names are written in labels
    	 * Each label comprises of a length byte followed by the label
    	 * The domain is terminated with a zero byte
    	 * eg. www.test.com becomes 0x3www0x4test0x3com0x0 */
    This is then followed by the query type and class. However, these values must also be in network byte order.

    These threads may help you:

    http://cboard.cprogramming.com/showthread.php?t=49730

    In hindsight, this code could be simpler, but the extensive comments should help you.
    http://cboard.cprogramming.com/showthread.php?t=31059

  4. #4
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Much thanks.
    Here is my function that encodes the domain name. I think I'm very close.
    Code:
    /*
     * domain_name: a domain name
     *
     * return an encoded domain name
     */
    char *encode_domain_name(char *domain_name)
    {
     char *node;
     char *encoded_domain = (char *) malloc(512);
     int old_length;
    
     node = strtok(domain_name, ".");
     encoded_domain[0] = 0;
    
     while(node)
     {
      old_length = strlen(encoded_domain);
      encoded_domain[old_length] = strlen(node);
      encoded_domain[old_length + 1] = 0;
      strcat(encoded_domain, node);
      node = strtok(NULL, "."); // <---
     }
    
     if(!encoded_domain)
      return 0;
    
     return encoded_domain;
    }
    If I put "guinsess.cs.stevens.edu" as input, and put a break point at the commented arrow and display the values of node, strlen(encoded_domain), and encoded_domain, everything goes fine until the last call to strtok(): only guinness is displayed.

  5. #5
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    If I wanted a standard, recursive query, would I pass this to the parameter field:
    htons(0x0100)?

  6. #6
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Your encoding function works for me. Are you sure you are not using it with a constant string or string literal. For example, this would be wrong:
    Code:
    char* enc = encode_domain_name("www.test.com");
    >> If I wanted a standard, recursive query, would I pass this to the parameter field:
    htons(0x0100)? <<

    Yes, that should work.
    Last edited by anonytmouse; 02-15-2005 at 07:32 PM.

  7. #7
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Ok, I'm able to send queries to server and get back successful replies. I think I can handle extracting the query response after going through all this.
    Much thanks.

  8. #8
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Ok, I'm getting responses from the DNS server. It seems that if any of the nodes in the IP address is greater than 2, I get the wrong IP address. For example:
    www.netbsd.org 204.152.190.12 65484465432265470012
    www.reuters.com 64.94.180.107 64.94.654600107
    www.mit.edu 18.7.22.83 18.7.22.83

    where the second column is the real IP address and ther third column is what I'm getting. Can anyone gander about what's going on?

  9. #9
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Here's the function
    Code:
    /*
     * response: a DNS response; must point to resource data and be 4 octets
     *
     * returns resource data and points to next resource
     */
    char *extract_resource_data(char **response)
    {
     char *ip_addr = (char *) malloc(16);
     int i;
     char period[] = ".\0", node[4];
    
     sprintf(node, "%hu", **response);
     strcpy(ip_addr, node);
     ++*response;
    
     for(i = 1; i < 4; i++)
     {
      strcat(ip_addr, period);
      sprintf(node, "%hu", **response);
      strcat(ip_addr, node);
      ++*response;
     }
    
     return ip_addr;
    }
    The data buffer is defined as char *response = (char *) malloc(1024). I call the function as extract_resource_data(&response)

  10. #10
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    This is caused by the fact that a char on your compiler is signed. Therefore, byte values over 127 are represented as negative. When these are converted to int and cast to unsigned in the call to sprintf, the result is a very large number. For byte data, you should be using unsigned char. Better, yet would be to typedef a type such as byte_t.

    However, this function could be made a lot cleaner by using inet_ntoa:
    Code:
    char *extract_resource_data(char **response)
    {
     struct in_addr in;
     char *ip_addr = calloc(16);
     char *temp;
    
     memcpy(&in.S_un.S_addr, *response, 4);
    
     if (temp = inet_ntoa(in))
          strcpy(ip_addr, temp);
    
     *response += 4;
    
     return ip_addr;
    }
    As a matter of style, for fixed size buffers, you should allocate an array in the caller and pass it to the callee to use, rather than allocating in the callee. See this thread. You should also check the return value of malloc and calloc.

  11. #11
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Much thanks, anonytmouse, but I figured out the exact same thing last night when I was stepping through the debugger: when I would print out the value of one of the bytes in the resource data section, I would negative nodes along with correct postive values for the other nodes. Here's what I came up with. I didn't know about inet_ntoa(), so I had to write my own version.

    Code:
    /* 
     * response: a DNS response; must point to resource data and be 4 octets
     *
     * returns resource data and points to next resource
     */
    char *extract_resource_data(char **response)
    {  
     char *ip_addr = (char *) malloc(16);
     int ip;
     
     memcpy(&ip, *response, 4);
     strcpy(ip_addr, decimal_ip_to_ip_addr(ip));
     *response += 4;
     
     return ip_addr;
    }
    I'm a real stickler on coding style, and I know about allocating fixed buffer before passing to a function, but I'm always debating with myself of what looks more aesthetically pleasing.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. GradeInfo
    By kirksson in forum C Programming
    Replies: 23
    Last Post: 07-16-2008, 03:27 PM
  2. C or C++
    By AcerN30 in forum Game Programming
    Replies: 41
    Last Post: 05-30-2008, 06:57 PM
  3. COM message pump
    By George2 in forum Windows Programming
    Replies: 2
    Last Post: 03-29-2008, 02:52 AM
  4. Character arrays in a Structure
    By vsriharsha in forum C Programming
    Replies: 7
    Last Post: 07-11-2004, 05:36 PM
  5. Message printing problem
    By robert_sun in forum C Programming
    Replies: 1
    Last Post: 05-18-2004, 05:05 AM