/* If you use this code, I must be attributed
 * It is released under the GPL. if this is unclear you are REQUIRED to
 * ask me for clarification.
 * If used commercially, I *must* be asked for permission first.
 * 
 * This code is *entirely* (c) Myself, however, I read the code of
 * Van Jacobson (van@helios.ee.lbl.gov) when learning about this stuff.
 *
 * --Ian Molton (spyro@f2s.com)
 */

#include <sys/param.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>

#include <arpa/inet.h>

#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct opacket {
        struct iphdr ip;
        struct udphdr udp;
        u_char seq;             /* sequence number of this packet */
        u_char ttl;             /* ttl packet left with */
        struct timeval tv;      /* time packet left */
};
	
int ident;

int get_ip_by_name(char *name, struct in_addr *addr){
	struct hostent *hp;

	if(!addr || !name)
		return 0;

	if(!inet_aton(name, addr)){
                hp = gethostbyname(name);
                if(hp)
                        addr->s_addr = *(int*)(hp->h_addr_list[0]);
                else
                        return 0;
        }
	
	return 1;
}

void send_packet(int sock, struct opacket *packet, struct in_addr *addr, int ttl){
	struct iphdr *ip = &packet->ip;
        struct udphdr *up = &packet->udp;
	struct sockaddr_in dest;
	char *a;
	
	dest.sin_addr = *addr;
	dest.sin_family = AF_INET;

	++packet->seq;
	packet->ttl = ttl;
	a = (char*)&packet->tv;
	a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4;
	a[4] = 1; a[5] = 2; a[6] = 3; a[7] = 4;

	ip->ihl = sizeof(struct iphdr) >> 2;
	ip->version = IPVERSION;
	ip->tos = 0;
	ip->tot_len = sizeof(struct opacket);
	ip->id =htons(ident+packet->seq);
	ip->frag_off = 0;
	ip->ttl = ttl;
	ip->protocol = IPPROTO_UDP;
	//ip->saddr = 0; Filled in by OS
	ip->daddr = addr->s_addr;
	
	up->source = htons(ident);
	up->dest = htons(37666);
	up->len = htons((u_short)(sizeof(struct opacket) - sizeof(struct ip)));
	
	sendto(sock, (char *)packet, sizeof(struct opacket) + packet->seq, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr));

}

int wait_for_packet(int sock, struct in_addr *returns){
	struct sockaddr_in from;
	struct timeval wait;
	int fromlen = sizeof(from);
	u_char  packet[512];
	fd_set fds;
	int cc;

	FD_ZERO(&fds);
        FD_SET(sock, &fds);
	wait.tv_sec = 1; wait.tv_usec = 0;
	
	if (select(sock+1, &fds, NULL, NULL, &wait) > 0){
                cc=recvfrom(sock, (char *)packet, sizeof(packet), 0,
                            (struct sockaddr *)&from, &fromlen);
		returns[31-(packet[31]-0x28)] = from.sin_addr;
		return 1;
	}
	return 0;
}
	
int main(int argc, char **argv){
	struct in_addr dest_addr;
	char *dest = argv[1];
	int send_sock, recv_sock;
	struct protoent *pe;
	int data_len = 0;
	int on = 1;
	struct opacket packet;
	int i, n;
	struct in_addr returns[30], last;

	memset(&packet, 0, sizeof(packet));

	ident = (getpid() & 0xffff) | 0x8000;

	if(get_ip_by_name(dest, &dest_addr))	
		printf("Tracing %s: ", inet_ntoa(dest_addr)); fflush(0);

	pe = getprotobyname("icmp");
	
	recv_sock = socket(AF_INET, SOCK_RAW, pe->p_proto);
	send_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);

	setsockopt(send_sock, SOL_SOCKET, SO_SNDBUF, (char *)&data_len,sizeof(int));
	setsockopt(send_sock, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on));

	for(i = 30; i >=0 ; i--){
		memset(&returns[i], 0, sizeof(struct in_addr));
		send_packet(send_sock, &packet, &dest_addr, i+1);
	}
		
	while(wait_for_packet(recv_sock, (struct in_addr*)&returns))
		printf("-");fflush(0);

	printf("\n");

	last.s_addr = 0; n = 1;
	for(i = 0 ; i < 30 ; i++){
		if(returns[i].s_addr != last.s_addr && returns[i].s_addr != 0)
			printf("%d: %s\n", n++, inet_ntoa(returns[i]));
		last = returns[i]; 
	}
	
		
	return 0;
}
	

