/* 
 experience sur le DNS
 P. Langevin,  01/2004.
*/

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

#define MAX 1024

typedef struct  {
  short int id;
  short int flag;
  short int qrcount;
  short int ancount;
  short int nscount;
  short int arcount;;
  char data[MAX];
} paquet;


void affiche( char * bfr, int nb) 
{ 
 paquet *z;
 int i;
 z = ( paquet *) bfr;   
 printf("\nTaille du paquet %d", nb); 
 printf("\nNumero Id : %u", htons(z->id) );
 printf("\nFlag : %d", z->flag); 
 printf("\nNombre de questions : %d", htons( z->qrcount) );   
 printf("\nNombre de reponses  : %d", htons( z->ancount) );   
 printf("\n%d champs serveurs", htons( z->nscount) );
 printf("\n%d champs additionnels.", htons(z->arcount));
 printf("\nDATA:\n");
 nb = nb - 2*7;
 for( i =0; i < nb ; i++)
    if ( isprint( z->data[i] ))
        printf("%c", z->data[i]);
    else printf("<%d>", z->data[i]);
 printf("\n");
}

void erreur( int num)
{
  printf("\nErreur %d...\n", num);
  exit(1);
}

int main(int argc, char *argv[]) {
  
  int sd, lngr;
  char nom[32];
  int prtl, prtd;
  int nbl;

  char bfr[MAX];
  
  struct sockaddr_in cliAddr, servAddr, dnsAddr;
 
  struct hostent *hote;
 
  // Trois arguments : port local, nom du dns, port dns.
  if ( argc != 4 ) erreur(-1);
  prtl = atoi( argv[1] );
  prtd = atoi( argv[3] );
  // Parametre du serveur local
  if ( gethostname( nom, 32) < 0 ) 
      erreur(0);
  if ( ! gethostbyname(nom) )
    erreur(1);
 
  // Creation socket serveur local
  sd = socket(AF_INET, SOCK_DGRAM, 0);
  if ( sd < 0)  erreur(2);

  // Bind serveur sur port local
  servAddr.sin_family = AF_INET;
  servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  servAddr.sin_port = htons( prtl );
  if ( 0 > bind (sd, (struct sockaddr *) &servAddr,sizeof(servAddr)) )
    erreur(3);

  // Attente d'un paquet.
  memset(bfr, 0 , MAX);
  lngr = sizeof(cliAddr);
  nbl  = recvfrom(sd, bfr, MAX, 0, 
		 (struct sockaddr *) &cliAddr, & lngr);

  if( nbl < 0 ) erreur( 4);
      
  // paquet recu :
  printf("\n%d octets udp de %s port %u", nbl, 
	   inet_ntoa(cliAddr.sin_addr),
	   ntohs(cliAddr.sin_port));
  affiche( bfr, nbl);


  /*** Redirection du paquet vers le serveur dns ***/

  // parametres serveur distant.
  hote = gethostbyname(argv[2]);
  if ( ! hote ) erreur(5);
  
  printf("\redirection vers %s (IP : %s)", hote->h_name,
	 inet_ntoa(*(struct in_addr *) hote->h_addr_list[0]));

  dnsAddr.sin_family = hote->h_addrtype;
  memcpy((char *) &dnsAddr.sin_addr.s_addr, 
	 hote->h_addr_list[0], hote->h_length);
  dnsAddr.sin_port = htons(prtd);
 
  // rebond vers le serveur.
  nbl = sendto(sd, bfr, nbl, 0, 
	      (struct sockaddr *) & dnsAddr, 
		sizeof(dnsAddr));

  if ( nbl < 0 ) erreur( 6);


  // Attente de la reponse .

  memset(bfr, 0 , MAX);
  lngr  = sizeof(cliAddr);
  nbl   = recvfrom(sd, bfr, MAX, 0, 
		 (struct sockaddr *) &cliAddr, & lngr);

  if( nbl < 0 ) erreur( 7 );
      
  // paquet recu :
  printf("\n%d octets udp de %s port %u", nbl, 
	   inet_ntoa(cliAddr.sin_addr),
	   ntohs(cliAddr.sin_port));
  affiche( bfr, nbl);

  close(sd);
  printf("\n");
  return 0;
}
