This commit was generated by cvs2svn to compensate for changes in r45,
[pingofdeath.git] / pl-poddoit.c
diff --git a/pl-poddoit.c b/pl-poddoit.c
new file mode 100644 (file)
index 0000000..20e72c2
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * EMULAB-COPYRIGHT
+ * Copyright (c) 2000-2002 University of Utah and the Flux Group.
+ * All rights reserved.
+ */
+
+/*
+ *  Send the magical ping of death ICMP type
+ */
+
+
+#include <time.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <arpa/inet.h>
+
+#define IPOD_ICMPTYPE  6
+#define IPOD_ICMPCODE  6
+#define IPOD_IPLEN     666
+#define IPOD_IDLEN     32
+
+int icmpid = 0;
+static char myid[IPOD_IDLEN];
+static int myidlen = 0;
+
+u_short in_cksum(u_short *addr, int len);
+void icmpmap_init();  /* For getting information */
+void icmp_info(struct icmp *icmp, char *outbuf, int maxlen);
+
+/*
+ * We perform lookups on the hosts, and then store them in a chain
+ * here.
+ */
+
+struct hostdesc {
+       char *hostname;
+       struct in_addr hostaddr;
+       struct hostdesc *next;
+};
+
+struct hostdesc *hostnames;
+struct hostdesc *hosttail;
+
+/*
+ * Set up the list of hosts.  Return the count.
+ */
+
+int makehosts(char **hostlist)
+{
+       int i;
+       struct hostent *hp;
+       struct in_addr tmpaddr;
+       int hostcount = 0;
+       
+       for (i = 0; hostlist[i]; i++) {
+#ifdef DEBUG
+               printf("Resolving %s\n", hostlist[i]);
+#endif
+               if (!hostlist[i] ||
+                   !hostlist[i][0] ||
+                   strlen(hostlist[i]) > MAXHOSTNAMELEN) {
+                   fprintf(stderr, "bad host entry, exiting\n");
+                   exit(-1);
+               }
+               if (!inet_aton(hostlist[i], &tmpaddr)) {
+                       if ((hp = gethostbyname(hostlist[i])) == NULL) {
+                               /* Could not resolve it.  Skip it. */
+                               fprintf(stderr, "%s: unknown host\n",
+                                       hostlist[i]);
+                               continue;
+                       }
+                       else {
+                               memcpy(&tmpaddr.s_addr,
+                                      hp->h_addr_list[0],
+                                      hp->h_length);
+                       }
+               }
+
+               /* The host has been resolved.  Put it in the chain */
+               /* We want to stick it on the end. */
+               if (hostnames == NULL) {
+                       hostnames = (struct hostdesc *)
+                               malloc(sizeof(*hostnames));
+                       if (hostnames == NULL) {
+                               perror("hostnames malloc failed");
+                               exit(-1);
+                       }
+                       hosttail = hostnames;
+               } else {
+                       hosttail->next = (struct hostdesc *)
+                               malloc(sizeof(*hostnames));
+                       if (hosttail->next == NULL) {
+                               perror("hosttail->next malloc failed");
+                               exit(-1);
+                       }
+                       hosttail = hosttail->next;
+               }
+               hosttail->hostname = strdup(hostlist[i]);
+               if (hosttail->hostname == NULL) {
+                       perror("strdup failed");
+                       exit(-1);
+               }
+               hosttail->hostaddr = tmpaddr;
+               hosttail->next = NULL;
+               hostcount++;
+       }
+       return hostcount;
+}
+
+void usage(char *prog)
+{
+   fprintf(stderr, "%s [ -i identityfile ] target [ target ... ]\n", prog);
+}
+
+/*
+ * Set up a packet.  Returns the length of the ICMP portion.
+ */
+
+void initpacket(char *buf, int querytype, struct in_addr fromaddr)
+{
+   struct ip *ip = (struct ip *)buf;
+   struct icmp *icmp = (struct icmp *)(ip + 1);
+
+   /* things we customize */
+   int icmplen = 0;
+
+   ip->ip_src = fromaddr;      /* if 0,  have kernel fill in */
+   ip->ip_v   = 4;             /* Always use ipv4 for now */
+   ip->ip_hl  = sizeof *ip >> 2;
+   ip->ip_tos = 0;
+   ip->ip_id  = htons(4321);
+   ip->ip_ttl = 255;
+   ip->ip_p   = 1;
+   ip->ip_sum = 0;                 /* kernel fills in */
+
+   icmp->icmp_seq   = 1;
+   icmp->icmp_cksum = 0;           /* We'll compute it later. */
+   icmp->icmp_type  = querytype; 
+   icmp->icmp_code  = IPOD_ICMPCODE;
+   if (myidlen)
+      memcpy(icmp->icmp_data, myid, myidlen);
+
+   ip->ip_len = IPOD_IPLEN;
+   icmplen = IPOD_IPLEN - sizeof(struct ip);
+   icmp->icmp_cksum = in_cksum((u_short *)icmp, icmplen);
+}
+
+/*
+ * Send all of the ICMP queries.
+ */
+
+void sendpings(int s, int querytype, struct hostdesc *head, int delay,
+              struct in_addr fromaddr)
+     
+{
+       char buf[1500];
+       struct ip *ip = (struct ip *)buf;
+       struct sockaddr_in dst;
+
+       bzero(buf, 1500);
+       initpacket(buf, querytype, fromaddr);
+       dst.sin_family = AF_INET;
+#ifdef DA_HAS_SIN_LEN
+       dst.sin_len = sizeof(dst);
+#endif
+
+       while (head != NULL) {
+               int rc;
+#ifdef DEBUG
+               printf("pinging %s\n", head->hostname);
+#endif
+               ip->ip_dst.s_addr = head->hostaddr.s_addr;
+               dst.sin_addr = head->hostaddr;
+               rc = sendto(s, buf, ip->ip_len, 0,
+                          (struct sockaddr *)&dst,
+                          sizeof(dst));
+                if (rc != ip->ip_len) {
+                       perror("sendto");
+               }
+               /* Don't flood small pipes. */
+               if (delay)
+                       usleep(delay);
+               head = head->next;
+       }
+}
+
+/*
+ * Handles our timeout for us.  Called by the signal handler
+ * when we get a SIGARLM.
+ */
+
+void myexit(int whatsig)
+{
+       exit(0);
+}
+
+/*
+ * Open a raw socket for receiving ICMP.  Tell the kernel we want
+ * to supply the IP headers.
+ */
+
+int get_icmp_socket()
+{
+       int s;
+       int on = 1;
+       if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
+               perror("socket");
+               exit(1);
+       }
+       if (setsockopt(s, IPPROTO_IP, IP_HDRINCL,
+                      (const char *)&on, sizeof(on)) < 0) {
+               perror("IP_HDRINCL");
+               exit(1);
+       }
+       return s;
+}
+
+int
+main(int argc, char **argv)
+{
+   int s;
+
+   char *progname;
+   extern char *optarg;         /* getopt variable declarations */
+   extern int optind;
+   char ch;                     /* Holds the getopt result */
+   int hostcount;
+   int delay = 0;
+   int querytype = ICMP_TSTAMP;
+   struct in_addr fromaddr;
+   int timeout = 5;  /* Default to 5 seconds */
+   int identityfile;
+
+   fromaddr.s_addr = 0;
+
+   progname = argv[0];
+
+   querytype = IPOD_ICMPTYPE;  /* the magical death packet number */
+
+   while ((ch = getopt(argc, argv, "i:")) != -1)
+      switch(ch)
+      {
+      case 'i':
+        if (optarg[0] == '-')
+           identityfile = 0;
+        else if ((identityfile = open(optarg, 0)) < 0)
+        {
+           perror(optarg);
+           exit(1);
+        }
+        myidlen = read(identityfile, myid, IPOD_IDLEN);
+        if (optarg[0] != '-')
+           close(identityfile);
+         if (myidlen != IPOD_IDLEN)
+        {
+           fprintf(stderr, "%s: cannot read %d-byte identity\n",
+                   optarg[0] != '-' ? optarg : "<stdin>", IPOD_IDLEN);
+           exit(2);
+        }
+         break;
+      default:
+         usage(progname);
+        exit(-1);
+      }
+
+   argc -= optind;
+   argv += optind;
+   if (!argv[0] || !strlen(argv[0])) 
+   {
+      usage(progname);
+      exit(-1);
+   }
+
+   hostcount = makehosts(argv);
+
+   s = get_icmp_socket();
+
+   signal(SIGALRM, myexit);
+   alarm(timeout);
+   sendpings(s, querytype, hostnames, delay, fromaddr);
+   exit(0);
+}
+   
+/*
+ * in_cksum --
+ *     Checksum routine for Internet Protocol family headers (C Version)
+ *      From FreeBSD's ping.c
+ */
+
+u_short
+in_cksum(addr, len)
+       u_short *addr;
+       int len;
+{
+       register int nleft = len;
+       register u_short *w = addr;
+       register int sum = 0;
+       u_short answer = 0;
+
+       /*
+        * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+        * sequential 16 bit words to it, and at the end, fold back all the
+        * carry bits from the top 16 bits into the lower 16 bits.
+        */
+       while (nleft > 1)  {
+               sum += *w++;
+               nleft -= 2;
+       }
+
+       /* mop up an odd byte, if necessary */
+       if (nleft == 1) {
+               *(u_char *)(&answer) = *(u_char *)w ;
+               sum += answer;
+       }
+
+       /* add back carry outs from top 16 bits to low 16 bits */
+       sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
+       sum += (sum >> 16);                     /* add carry */
+       answer = ~sum;                          /* truncate to 16 bits */
+       return(answer);
+}
+