Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
maalvikabhat committed Mar 6, 2020
2 parents b2bfe85 + 125b49b commit be77575
Show file tree
Hide file tree
Showing 9 changed files with 784 additions and 0 deletions.
1 change: 1 addition & 0 deletions exercises/ex04/trout/.#util.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
downey@rocky.colby.edu.25374:1
47 changes: 47 additions & 0 deletions exercises/ex04/trout/COPYRIGHT
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
TROUT is a simple version of traceroute.

It is based on code written by W. Richard Stevens and published in
"UNIX Network Programming Volume 1, Second Edition Networking APIs:
Sockets and XTI," Prentice Hall PTR, Upper Saddle River, NJ 07458.

It has been modified a fair amount by Allen Downey
([email protected]). Among other things, I did the following

1) I collected all the procedures from Stevens's extensive library
and put them in util.c, and put their prototypes in trout.h

2) I killed all the IPv6 support, because I didn't need it and
because I hate ifdefs and pointers to functions.

3) For the same reason, I took out some of the configuration
ifdefs. As a result, I don't know if this will still run
on anything other than Linux.

4) I fixed a race condition that may or may not have been the
cause of some early problems I had. Anyway, the fix also
came from Stevens, in Section 18.5 of Unix Network Programming.

5) I split things up into more procedures.

The copyright for the original code is held by Prentice Hall,
but they make it available under a license that is more or
less identical to the GNU GPL.

This version is Copyright (C) 1999 Allen B. Downey.

I am making it available under the GNU General Public License
(which does not, I think, violate the original copyright).

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2 changes: 2 additions & 0 deletions exercises/ex04/trout/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
trout: trout.h trout.c util.h util.c main.c
gcc -Wall -g -o trout trout.c main.c util.c
45 changes: 45 additions & 0 deletions exercises/ex04/trout/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "trout.h"

int main (int argc, char **argv)
{
int c;
struct addrinfo *ai;
char *host;

opterr = 0;
while ( (c = getopt (argc, argv, "m:")) != -1) {
switch (c) {
case 'm':
if ( (max_ttl = atoi(optarg)) <= 1) {
err_quit ("invalid -m value");
}
break;
default:
err_quit ("unrecognizd option: %c", c);
}
}

if (optind != argc - 1) {
err_quit ("usage: trout [ -m <maxttl>] <hostname>");
}
host = argv[optind];
ai = Host_serv (host, NULL, 0, 0);

printf ("trout to %s (%s): %d hops max, %d data bytes\n",
ai->ai_canonname,
Sock_ntop_host (ai->ai_addr, ai->ai_addrlen),
max_ttl, datalen);

if (ai->ai_family != AF_INET) {
err_quit ("unknown address family %d", ai->ai_family);
}

sasend = ai->ai_addr;
salen = ai->ai_addrlen;
sarecv = Calloc (1, salen);
salast = Calloc (1, salen);
sabind = Calloc (1, salen);

loop_ttl ();
exit (0);
}
Binary file added exercises/ex04/trout/trout (1)
Binary file not shown.
261 changes: 261 additions & 0 deletions exercises/ex04/trout/trout.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
#include "trout.h"

/* variables we might want to configure */
Rec *rec = (Rec *) sendbuf;

void sig_alrm (int signo)
{
Write (pipefd[1], "", 1); /* write 1 null byte to pipe */
return;
}

int process_ip (struct ip *ip, int len)
{
int hlen1, hlen2, icmplen;
struct icmp *icmp;
struct ip *hip;
struct udphdr *udp;
seq = 0;
u_short dport = 32768 + 668; /* destination port -- hopefully unused */

hlen1 = ip->ip_hl << 2; /* length of IP header */
icmp = (struct icmp *) (recvbuf + hlen1);
icmplen = len - hlen1;

if (icmplen < 8 + 20 + 8) return 0;

if (icmp->icmp_type != ICMP_TIME_EXCEEDED &&
icmp->icmp_type != ICMP_DEST_UNREACH)
return 0;

/* hip is the header of the enclosed IP packets, supposedly
the header of the packet that caused the error */

hip = (struct ip *) (recvbuf + hlen1 + 8);
if (hip->ip_p != IPPROTO_UDP) return 0;

hlen2 = hip->ip_hl << 2;
udp = (struct udphdr *) (recvbuf + hlen1 + 8 + hlen2);

if (udp->source != htons (sport)) return 0;
if (udp->dest != htons (dport + seq)) return 0;

if (icmp->icmp_type == ICMP_TIME_EXCEEDED) {
if (icmp->icmp_code == ICMP_EXC_TTL) {
return -2;
} else {
return 0;
}
}

if (icmp->icmp_type == ICMP_DEST_UNREACH) {
if (icmp->icmp_code == ICMP_PORT_UNREACH) {
return -1;
} else {
return 0;
}
}
return 0;
}



int recv_dgram ()
{
int err;
socklen_t len;
ssize_t n;
struct ip *ip;
int maxfdp1 = max (recvfd, pipefd[0]) + 1;
fd_set rset[1];
FD_ZERO (rset);

alarm(3); /* set the timeout alarm to handle dropped packets */

while (1) {
FD_SET (recvfd, rset);
FD_SET (pipefd[0], rset);

n = select (maxfdp1, rset, NULL, NULL, NULL);
if (n < 0 && errno != EINTR) {
err_sys ("select error");
}

if (FD_ISSET (recvfd, rset)) {
len = salen;
n = recvfrom (recvfd, recvbuf, sizeof(recvbuf), 0, sarecv, &len);
err = errno;
Gettimeofday (recvtv, NULL); /* get time of packet arrival */
if (n < 0 && err != EAGAIN) {
err_sys ("recvfrom error");
}
}

if (FD_ISSET (pipefd[0], rset)) {
Read (pipefd[0], &n, 1);
return -3; /* timeout */
}

ip = (struct ip *) recvbuf;
return process_ip (ip, n);
}
}

/* sub_tv: subtract minus from plus and put the result in res */

void sub_tv (Timeval *plus, Timeval *minus, Timeval *res)
{
res->tv_sec = plus->tv_sec - minus->tv_sec;
res->tv_usec = plus->tv_usec - minus->tv_usec;

if (res->tv_usec < 0) {
res->tv_sec--;
res->tv_usec += 1000000;
}
}

/* time_to_double: convert a Timeval to a double. This only
works with Timevals that are small (like the difference between
two real Timevals) */

double time_to_double (Timeval *time)
{
return time->tv_sec * 1000.0 + time->tv_usec / 1000.0;
}

/* print_report: prints all the information about a successful round trip */

void print_report ()
{
int stat;
char str[NI_MAXHOST];

stat = sock_cmp_addr (sarecv, salast, salen);

/* if this reply comes from source different from the previous
one, print the full host information */

if (stat != 0) {
stat = getnameinfo (sarecv, salen, str, sizeof(str), NULL, 0, 0);
if (stat == 0) {
printf (" %s (%s)", str, Sock_ntop_host (sarecv, salen));
} else {
printf (" %s", Sock_ntop_host (sarecv, salen));
}
memcpy (salast, sarecv, salen);
}

/* calculate and print the round trip time using user-level timestamps */

sub_tv (recvtv, sendtv, difftv);

printf (" %.3f", time_to_double (difftv));
}

/* send_dgram: generate an outgoing UDP packet */

/* the second effort send is a kludge to handle a funny
thing, which is that the last host seems to refuse the
second or third connection consistently, which might
might mean that something breaks when we get the
ICMP_DEST_UNREACH error. The second attempt seems
to succeed consistently. */

void send_dgram (int ttl)
{
int n;
datalen = sizeof (Rec); /* length of the data in a datagram */
seq = 0;
u_short dport = 32768 + 668; /* destination port -- hopefully unused */

rec->seq = seq++;
sock_set_port (sasend, salen, htons(dport+seq));

Gettimeofday (sendtv, NULL);
n = sendto(sendfd, sendbuf, datalen, 0, sasend, salen);

if (n==-1 && errno == ECONNREFUSED) {
Gettimeofday (sendtv, NULL);
n = sendto(sendfd, sendbuf, datalen, 0, sasend, salen);
}

if (n != datalen) {
err_sys("sendto error");
}
}

/* send_probes: sends a set of probes with the given ttl and
then waits for the replies. The weird TOS options are there
as a signal to the kernel to identify clink packets so it can
fill in the timestamps. I am assuming that they don't have
any actual effect. */

int send_probes (int ttl)
{
int probe, code, done;

Setsockopt (sendfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(int));
bzero (salast, salen);
int nprobes = 2;

printf ("%2d ", ttl);
fflush (stdout);

done = 0; /* count the number of probes that generate an ICMP_DEST_UNREACH */

for (probe = 0; probe < nprobes; probe++) {
send_dgram (ttl);
code = recv_dgram ();

if (code == -3) {
printf (" *");
} else {
print_report ();
}

if (code == -1) done++;
fflush (stdout);
}
printf ("ms\n");
return done;
}

/* loop_ttl: starting with ttl=1, gradually increase ttl until
we start getting ICMP_DEST_UNREACH instead of ICMP_TIME_EXCEEDED */

void loop_ttl ()
{
int ttl, done;

Pipe (pipefd); /* the pipe for the alarm handler */
max_ttl = 30;


recvfd = socket (sasend->sa_family, SOCK_RAW, IPPROTO_ICMP);
if (recvfd == -1) {
if (errno == EPERM) {
printf ("\nclink was unable to open a raw socket. The most\n");
printf ("likely cause is that you are not running it as root.\n");
exit (1);
} else {
err_sys ("opening raw socket in clink");
}
}

fcntl (recvfd, F_SETFL, O_NONBLOCK);
setuid (getuid ());

sendfd = socket (sasend->sa_family, SOCK_DGRAM, 0);

sabind->sa_family = sasend->sa_family;
sport = (getpid() & 0xffff) | 0x8000; /* source UDP port # */
sock_set_port (sabind, salen, htons(sport));
Bind (sendfd, sabind, salen);

Signal (SIGALRM, sig_alrm);

for (ttl = 1; ttl <= max_ttl; ttl++) {
done = send_probes (ttl);
if (done > 0) break;
}
}
Loading

0 comments on commit be77575

Please sign in to comment.