/*
 * Example of client using TCP protocol, adapted from
 *   "Unix Network Programming", 1st ed. 
 */

#include  <stdio.h>
#include  <sys/types.h>
#include  <sys/socket.h>
#include  <netinet/in.h>
#include  <arpa/inet.h>

#include  <stdio.h>
#include  <strings.h> // bzero
#include  <string.h>  // strncpy
#include  <stdlib.h>  // exit
#include  <unistd.h>  // close

#include <stdint.h>   // uint8_t

int print_dns_name(char *);

#define SERV_TCP_PORT   53
#define SERV_HOST_ADDR  "129.170.170.2"

char req[] = { 
     0x00, 0x27, 0x1d, 0x62, 0x01, 0x00, 0x00, 0x01, 
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x63, 
     0x73, 0x36, 0x30, 0x02, 0x63, 0x73, 0x09, 0x64, 
     0x61, 0x72, 0x74, 0x6d, 0x6f, 0x75, 0x74, 0x68, 
     0x03, 0x65, 0x64, 0x75, 0x00, 0x00, 0x01, 0x00, 
     0x01 };

#define MAX_MSG 1500

typedef struct tcp_dns_resp_hdr {
     uint16_t len;
     uint16_t tx_id;
     uint8_t  flags[2];
     uint16_t n_q;
     uint16_t n_a;
     uint16_t n_auth;
     uint16_t n_add;
} tcp_dns_resp_hdr_t;

int main(argc, argv)
int	argc;
char	*argv[];
{
     int sockfd;
     struct sockaddr_in	serv_addr;

     char buff[1500];
     int n, i, len; 

     uint16_t type, class;

     tcp_dns_resp_hdr_t *r; 

     char *p;

     /*
      * Fill in the structure "serv_addr" with the address of the
      * server that we want to connect with.
      */
     
     bzero((char *) &serv_addr, sizeof(serv_addr));
     serv_addr.sin_family      = AF_INET;
     serv_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR);
     serv_addr.sin_port        = htons(SERV_TCP_PORT);
     
     /*
      * Open a TCP socket (an Internet stream socket).
      */
     
     if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
          perror("tcp client: can't open stream socket");
          exit(1);
     }

     /*
      * Connect to the server.
      */
     
     if (connect(sockfd, (struct sockaddr *) &serv_addr,
                 sizeof(serv_addr)) < 0){
          perror("tcp client: can't connect to server");
          exit(1);
     }
     
     //send(sockfd, (void*) msg, MSG_LEN, 0);
     write(sockfd, (void*) req, sizeof(req));
     
     n = recv(sockfd, (void*) buff, MAX_MSG, 0);
     if( n < 0 ){
          perror("Truncated response received.");
          goto out;
     }

     r = (tcp_dns_resp_hdr_t *) buff;
     printf( "Response length: %d\n", ntohs(r->len) );

     /* print packet */
     for (i = 0; i < n; i++) {
          printf("%02X%s", (uint8_t)buff[i], (i + 1)%16 ? " " : "\n");
     }
     printf("\n");

     printf("tx_id 0x%x, flags %02X %02X, questions %d, answers %d, authorities %d, additional %d\n",
            ntohs(r->tx_id), r->flags[0], r->flags[1], ntohs(r->n_q), ntohs(r->n_a), ntohs(r->n_auth), ntohs(r->n_add) );
     
     p = buff + sizeof(tcp_dns_resp_hdr_t);

     // print queries
     for( i =0 ; i < ntohs(r->n_q); i++ ){
          // parse query
          len = *(int8_t*)p;
          if( len > 0 ){ // name follows
               p += print_dns_name( p );
               // then print type and class
               type = ntohs( *(uint16_t *)p );
               p += 2;
               class = ntohs (*(uint16_t *)p );
               p += 2;
               printf( "  type %d, class %d\n", type, class );
          }
     }
     // print answers 
     for( i =0 ; i < ntohs(r->n_a); i++ ){
          // parse query
          len = *(int8_t*)p;
          if( len > 0 ){ // name follows
               p += print_dns_name( p );
               // then print type and class
               type = ntohs( *(uint16_t *)p );
               p += 2;
               class = ntohs (*(uint16_t *)p );
               p += 2;
               printf( "  type %d, class %d\n", type, class );
          }
          else if( len < 0 ){ // backpointer 
               uint16_t offset, data_len;
               uint32_t ttl;
               offset = ntohs(*(uint16_t *)p) & ~0xc000;
               printf( "offset %d\n", offset );
               print_dns_name( buff+2+offset ); 
               printf("\n");
               p += 2; // skip offset
               type = ntohs( *(uint16_t *)p );
               p += 2;
               class = ntohs (*(uint16_t *)p );
               p += 2;
               printf( "  type %d, class %d ", type, class );
               ttl = ntohs( *(uint32_t *)p ); 
               p += 4;
               data_len = ntohs( *(uint16_t *)p ); 
               p += 2;
               printf( "  ttl %d, data_len %d ", ttl, data_len );
               printf( " %s\n", inet_ntoa( *(struct in_addr*) p ));
          }
     }
          


     
out:
     close(sockfd);
     return 0;
}

int print_dns_name(char *p){
     char *p0 = p;
     int len  = *(uint8_t*)p;
     p++;
     printf( "name: " );    
     while( len > 0 ){
          fwrite( p, len, 1, stdout );
          printf(".");
          p += len; 
          len = *(uint8_t*)p;
          p++;
     }
     return p-p0;
}
