/*
Program: TCP TraceRoute
Porgram by: lockdown
Date: December 15, 2001

This program requires libnet and libpcap to compile.  This program sends a TCP packet to your destination increasing the ttl each time.  It uses the TTL exceeded messages to determine each hop.  The destination port is set to 80 since most firewalls let that through.
Compile: gcc `libnet-config --defines` ttr.c -o ttr `libnet-config --libs` -lpcap
*/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <libnet.h>
#include <time.h>
#include <sys/param.h>
#include <pcap.h>

void alarm_handler(int);
void usage(char *);

struct iph {
  u_char  ihl;
  u_char  tos;
  short   tot_len;
  u_short id;
  short   off;
  u_char  ttl;
  u_char  protocol;
  u_short check;
  u_long  saddr; 
  u_long  daddr;
};

struct icmph {
  u_char type;
  u_char code;
};

struct tcphd
{
  unsigned short th_sport;
  unsigned short th_dport;
};

int timeout,ttl;

int main(int argc, char *argv[])
{
  int tcp_socket,datalink,id,seq,ack,window,offset;
  unsigned short destp,srcp;
  unsigned long srcip,host;
  char errbuf[PCAP_ERRBUF_SIZE],*device;
  u_char *ptr;
  unsigned char *packet;
  uint32_t localnet,netmask;
  struct pcap *pd;
  struct iph *ip;
  struct tcphd *tcph;
  struct icmph *icmp;
  struct pcap_pkthdr hdr;
  struct bpf_program fp;
  struct in_addr sh, dh;

  if(argc !=2)
  {
    usage(argv[0]);
    return 1;
  }

  if(geteuid()!=0)
  {
    fprintf(stderr,"Must be root to open a raw socket!\n");
    return 1;
  }

  host=libnet_name_resolve(argv[1],1);

  srand(time(NULL) ^ getpid() ^ getgid());
  id=rand();
  srcp=(rand()% 65535);
  destp=80;
  /* If you want to use a random destination port then uncomment the next line
     I recomend using 80 to get through firewalls.
  */
/*  destp=(rand()% 65535);*/
  seq=rand();
  ack=rand();
  window=rand();

  if((device=pcap_lookupdev(errbuf))==NULL)
  {
    fprintf(stderr,"pcap_lookup, %s\n",errbuf);
    return 1;
  }
  printf("Using interface: %s\n",device);

  if((srcip=htonl(libnet_get_ipaddr(NULL,device,errbuf)))==-1)
  {
    fprintf(stderr,"libnet_get_ipaddr: %s\n",errbuf);
    return 1;
  }

  if((pd=pcap_open_live(device,8024,0,1000,errbuf))==NULL)
  {
    fprintf(stderr,"pcap_open_live: %s\n",errbuf);
    return 1;
  }

  switch(pcap_datalink(pd))
  {
    case DLT_NULL: offset=4;break;
    case DLT_EN10MB:
    case DLT_EN3MB: offset=14;break;
    case DLT_RAW: offset=0;break;
    default:
      fprintf(stderr,"Device not supported\n");
      return 1;
  }

  if(pcap_lookupnet(device,&localnet,&netmask,errbuf)<0)
  {
    fprintf(stderr,"pcap_lookupnet: %s\n",errbuf);
    return 1;
  }

  if((datalink=pcap_datalink(pd))<0)
  {
    fprintf(stderr,"pcap_datalink: %s\n",errbuf);
    return 1;
  }

  if(pcap_compile(pd,&fp,"ip",0,netmask)<0)
  {
    fprintf(stderr,"ERROR: pcap_compile\n");
    return 1;
  }

  if(pcap_setfilter(pd,&fp)<0)
  {
    fprintf(stderr,"ERROR: pcap_setfilter\n");
    return 1;
  }

  for(ttl=1;ttl<=32;ttl++)
  {
    signal(SIGALRM,alarm_handler);
    timeout=0;
    libnet_init_packet(LIBNET_MAX_PACKET,&packet);
    if((tcp_socket = libnet_open_raw_sock(IPPROTO_RAW))<0)
    {
      fprintf(stderr,"ERROR: libnet_open_raw_sock\n");
      return 1;
    }

    libnet_build_ip(LIBNET_TCP_H, IPTOS_LOWDELAY|IPTOS_THROUGHPUT,id,0,ttl,
                    IPPROTO_TCP,srcip,host,NULL,0,packet);
    libnet_build_tcp(srcp,destp,seq,ack,TH_SYN,window,0,NULL,0,
                     packet+LIBNET_IP_H);
    if(libnet_do_checksum(packet,IPPROTO_TCP,LIBNET_TCP_H)==-1)
      fprintf(stderr,"ERROR: checksum\n");

    libnet_write_ip(tcp_socket,packet,LIBNET_IP_H+LIBNET_TCP_H);
    alarm(3);

    while(1)
    {
      while((ptr=(u_char *)pcap_next(pd,&hdr))==NULL);
      ip=(struct iph *)(ptr +offset);
      sh.s_addr=ip->saddr;
      dh.s_addr=ip->daddr;
      if(ip->protocol == IPPROTO_ICMP)
      {
        icmp=(struct icmph *)(ptr + offset + sizeof(struct iph));
	if(icmp->type==11 && icmp->code==0)
        {
          printf("%d. %s\n",ttl,inet_ntoa(sh));
	  alarm(0);
	  break;
        }
      }
      if(ip->protocol == IPPROTO_TCP)
      {
        tcph=(struct tcphd *)(ptr + offset + sizeof(struct iph));
        if(destp == ntohs(tcph->th_sport) && srcp == ntohs(tcph->th_dport))
        {
          printf("destination: %s\n",inet_ntoa(sh));
	  alarm(0);
          return 0;
        }
      }
      if(timeout == 1)
	break;
    }/* while */
    free(packet);
  }/* for */

  return 0;
}/*main*/

void alarm_handler(int signal)
{
  printf("%d. No response\n",ttl);
  alarm(0);
  timeout=1;
  return;
}

void usage(char *name)
{
  fprintf(stderr,"Usage: %s <host>\n",name);
  fprintf(stderr,"\t host - name/ip of host to traceroute\n");
  return;
}

