/* Calcul de la distance d'un host en utilisant une reponse ICMP */
/* Principe: envoyer un paquet UDP au port PORT (par defaut 25)  */
/* Analyser le paquet de reponse ICMP PORT UNREACHABLE           */
/* Qui contient le TTL du paquet UDP au moment ou il a ete recu  */
/* Usage: idist [ -g intermediaire] [ host [ port]]              */
/* pour calculer la distance A B                                 */
/* idist -g A B  moins idist A  moins 1                          */
/*                                                               */
#include <stdio.h>
/* void perror(const char *s); */
#include <sys/signal.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
/* #include <netinet/ip_icmp.h> */
#include <sys/time.h>
#include <string.h>
#define PORT 25
#define ICMP_UNREACH 3
#define ICMP_PARAM 12
#define PORT_UNREACH 3
#define TIMEOUT 10
#define MAXRETRY 5
#define ECHO 8
#define ECHO_REPLY 0
#define QUENCH 4
/* sous SunOs 60 sous solaris 255 */
#define MAXTTL 255
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN  64
#endif
#define Sprintf (void)sprintf
int nflag =0;
#include "lsr.h"
struct sockaddr_in distant;
struct sockaddr   reponse;
#include <errno.h>
extern char *sys_errlist[];
char *inetname();
int errno;
char buf[80]; /* buffer de lecture depuis le socket */

main(argc,argv)
int argc;
char *argv[];
{
int sd;   /* numero de descripteur de socket */
int sdicmp; /* numero de socket ICMP */
int n,i;
int len,fromlen;
char buffer[256]; /* buffer lecture icmp */
char c='.';
int r =0 ;
int port;    /* numero du port UDP a appeler  */
struct hostent host , *phost;
int source = 0; /* addresse du noeud source si Source Routing 0 = pas SR */
struct servent  *pservent , servent ;

fd_set  inputset , outputset, exceptset; /* listes de file descriptor a tester */
struct timeval timeout;   /* duree du time out pour le select */
struct in_addr in;
char icmp_type ;
	if (argc <2)
	{
		/* lecture du nom de host s'il n'est pas precise dans la commande */
		printf("nom du host:");
		scanf("%s",buf);
	}
	else 	
	{
	if (strcmp(argv[1],"-g")==0)
	{	
		strcpy(buf,argv[2]);
		if ((phost=gethostbyname(buf))== NULL)
       	 {
       	         perror("host inconnu");
       	         exit(-1);
       	 };
		source= * (int *) phost->h_addr;
		argv++;
		argv++;
		argc -=2 ;
	}	
	strcpy(buf,argv[1]);
	}
	if ((phost=gethostbyname(buf))== NULL)
	{
		perror("host inconnu");
		exit(-1);
	};
	distant.sin_addr= * (struct in_addr *) phost->h_addr;
	distant.sin_family=AF_INET;
	if ( argc == 3)
	{
		/* le numero de port a ete donne a la commande */
		sscanf(argv[2],"%d",&port);
		if (( pservent = getservbyport(port, 0)) ==NULL)
			printf("Port non repertorie\n");
		else    printf("Service: %s\n", pservent->s_name );
		distant.sin_port= port;
	}
	else distant.sin_port=PORT;
	sd=socket(AF_INET,SOCK_DGRAM,0);
	sdicmp=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
	if (sdicmp < 0) perror("Erreur ouverture socket ICMP\n");
	set_lsrr(sd,source,* (int * )phost->h_addr);
do 
{
	sendto(sd,&c,1,0,&distant,sizeof(struct sockaddr));
	FD_ZERO  (&exceptset);
        FD_ZERO  (&inputset);
	FD_ZERO  (&outputset);
	FD_SET(sdicmp, &inputset ); /* reponse ICMP */
	FD_SET(sd, &exceptset); /* exception sur le socket */
	FD_SET(sdicmp, &exceptset);
	timeout.tv_sec= TIMEOUT;
	timeout.tv_usec= 0;
	n = select ( 256, &inputset, &outputset , &exceptset, &timeout) ;
	if ( n < 0 ) { perror("erreur sur le select"); r++ ; }
	else 
   {
	if ( n == 0 ) 
	{ printf("Pas de reponse\n");
	r++; }
	else
	{
	if ( FD_ISSET (sd, &exceptset) ) 
		{	 perror("erreur socket\n"); break ; 
		}
	if ( FD_ISSET (sdicmp, &inputset) )
		{
		        len=256;
			fromlen=256;
			n=recvfrom(sdicmp,buffer,len,0,&reponse,& fromlen);
			if ( n < 0) perror("Lecture ICMP\n");
/*
			for (i=0; i< n ; i++  )
				printf("%2x",buffer[i] & 0xff);
			printf("\n");
			printf("type de icmp:%d,ttl de la reponse:%d\n",buffer[20],buffer[36] & 0xff);
*/
/* calcul de la reponse  */
			/* la reponse vient elle du destinataire */
			if ( * ( long *)(buffer+12 )== (* (long * ) &distant.sin_addr))
			{ if ( buffer[20] == ICMP_UNREACH  | buffer[20] == ICMP_PARAM ) 
				{	
				printf("Distance= %d\n", MAXTTL - (buffer[36] & 0xff) + 1 );
				exit(0);
				}  else { printf("ICMP %d, code %d\n", buffer[20]  , buffer[21]  ) ; exit(0) ;}
			; }
			 else 
				{ printf("ICMP %d, code %d  ", buffer[20] , buffer[21]  );
				  icmp_type=buffer[20];
				  in = * ( struct in_addr *)(buffer+12 ) ;
				  printf("%s\n", inetname (in ));
				  if ( icmp_type == ECHO | icmp_type == ECHO_REPLY | icmp_type == QUENCH )
					{ /* icmp parasite, recommencer  */
					  r++ ;}
					else break ; }
		}
	if ( FD_ISSET (sdicmp, &exceptset) )
                {        printf("anomalie icmp\n"); break ;
                }
	}
   }
}
while (r < MAXRETRY) ;
	n=close(sd);
	if ( n < 0) perror("Fermeture socket UDP");
	n=close(sdicmp);
	if ( n < 0) perror("Fermeture socket ICMP");	
}
/*
 * Construct an Internet address representation.
 * If the nflag has been supplied, give
 * numeric value, otherwise try for symbolic name.
 */
char *
inetname(in)
        struct in_addr in;
{
        register char *cp;
        static char line[50];
        struct hostent *hp;
	struct hostent result ;
	int h_errnop;
	char buffer[1000];
        static char domain[MAXHOSTNAMELEN + 1];
        static int first = 1;

        if (first && !nflag) {
                first = 0;
                if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
                    (cp = strchr(domain, '.')))
                        (void) strcpy(domain, cp + 1);
                else
                        domain[0] = 0;
        }
        cp = 0;
        if (!nflag && in.s_addr != INADDR_ANY) {
                hp = gethostbyaddr_r((char *)&in, sizeof (in), AF_INET,&result,
          buffer,  1000 , &h_errnop);
                if (hp) {
                        if ((cp = strchr(hp->h_name, '.')) &&
                            !strcmp(cp + 1, domain))
                                *cp = 0;
                        cp = hp->h_name;
                }
        }
        if (cp)
                (void) strcpy(line, cp);
        else {
                in.s_addr = ntohl(in.s_addr);
#define C(x)    ((x) & 0xff)
                Sprintf(line, "%lu.%lu.%lu.%lu", C(in.s_addr >> 24),
                        C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
        }
        return (line);
}

