mark pod.py as running on python2
[pingofdeath.git] / pl-poddoit.c
1 /*
2  * EMULAB-COPYRIGHT
3  * Copyright (c) 2000-2002 University of Utah and the Flux Group.
4  * All rights reserved.
5  */
6
7 /*
8  *  Send the magical ping of death ICMP type
9  */
10
11
12 #include <time.h>
13 #include <sys/time.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <sys/types.h>
18 #include <sys/param.h>
19 #include <sys/socket.h>
20 #include <fcntl.h>
21 #include <netdb.h>
22 #include <netinet/in.h>
23 #include <netinet/in_systm.h>
24 #include <netinet/ip.h>
25 #include <netinet/ip_icmp.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <signal.h>
29 #include <arpa/inet.h>
30
31 #define IPOD_ICMPTYPE   6
32 #define IPOD_ICMPCODE   6
33 #define IPOD_IPLEN      666
34 #define IPOD_IDLEN      32
35
36 int icmpid = 0;
37 static char myid[IPOD_IDLEN];
38 static int myidlen = 0;
39
40 u_short in_cksum(u_short *addr, int len);
41 void icmpmap_init();  /* For getting information */
42 void icmp_info(struct icmp *icmp, char *outbuf, int maxlen);
43
44 /*
45  * We perform lookups on the hosts, and then store them in a chain
46  * here.
47  */
48
49 struct hostdesc {
50         char *hostname;
51         struct in_addr hostaddr;
52         struct hostdesc *next;
53 };
54
55 struct hostdesc *hostnames;
56 struct hostdesc *hosttail;
57
58 /*
59  * Set up the list of hosts.  Return the count.
60  */
61
62 int makehosts(char **hostlist)
63 {
64         int i;
65         struct hostent *hp;
66         struct in_addr tmpaddr;
67         int hostcount = 0;
68         
69         for (i = 0; hostlist[i]; i++) {
70 #ifdef DEBUG
71                 printf("Resolving %s\n", hostlist[i]);
72 #endif
73                 if (!hostlist[i] ||
74                     !hostlist[i][0] ||
75                     strlen(hostlist[i]) > MAXHOSTNAMELEN) {
76                     fprintf(stderr, "bad host entry, exiting\n");
77                     exit(-1);
78                 }
79                 if (!inet_aton(hostlist[i], &tmpaddr)) {
80                         if ((hp = gethostbyname(hostlist[i])) == NULL) {
81                                 /* Could not resolve it.  Skip it. */
82                                 fprintf(stderr, "%s: unknown host\n",
83                                         hostlist[i]);
84                                 continue;
85                         }
86                         else {
87                                 memcpy(&tmpaddr.s_addr,
88                                        hp->h_addr_list[0],
89                                        hp->h_length);
90                         }
91                 }
92
93                 /* The host has been resolved.  Put it in the chain */
94                 /* We want to stick it on the end. */
95                 if (hostnames == NULL) {
96                         hostnames = (struct hostdesc *)
97                                 malloc(sizeof(*hostnames));
98                         if (hostnames == NULL) {
99                                 perror("hostnames malloc failed");
100                                 exit(-1);
101                         }
102                         hosttail = hostnames;
103                 } else {
104                         hosttail->next = (struct hostdesc *)
105                                 malloc(sizeof(*hostnames));
106                         if (hosttail->next == NULL) {
107                                 perror("hosttail->next malloc failed");
108                                 exit(-1);
109                         }
110                         hosttail = hosttail->next;
111                 }
112                 hosttail->hostname = strdup(hostlist[i]);
113                 if (hosttail->hostname == NULL) {
114                         perror("strdup failed");
115                         exit(-1);
116                 }
117                 hosttail->hostaddr = tmpaddr;
118                 hosttail->next = NULL;
119                 hostcount++;
120         }
121         return hostcount;
122 }
123
124 void usage(char *prog)
125 {
126    fprintf(stderr, "%s [ -i identityfile ] target [ target ... ]\n", prog);
127 }
128
129 /*
130  * Set up a packet.  Returns the length of the ICMP portion.
131  */
132
133 void initpacket(char *buf, int querytype, struct in_addr fromaddr)
134 {
135    struct ip *ip = (struct ip *)buf;
136    struct icmp *icmp = (struct icmp *)(ip + 1);
137
138    /* things we customize */
139    int icmplen = 0;
140
141    ip->ip_src = fromaddr;       /* if 0,  have kernel fill in */
142    ip->ip_v   = 4;              /* Always use ipv4 for now */
143    ip->ip_hl  = sizeof *ip >> 2;
144    ip->ip_tos = 0;
145    ip->ip_id  = htons(4321);
146    ip->ip_ttl = 255;
147    ip->ip_p   = 1;
148    ip->ip_sum = 0;                 /* kernel fills in */
149
150    icmp->icmp_seq   = 1;
151    icmp->icmp_cksum = 0;           /* We'll compute it later. */
152    icmp->icmp_type  = querytype; 
153    icmp->icmp_code  = IPOD_ICMPCODE;
154    if (myidlen)
155       memcpy(icmp->icmp_data, myid, myidlen);
156
157    ip->ip_len = IPOD_IPLEN;
158    icmplen = IPOD_IPLEN - sizeof(struct ip);
159    icmp->icmp_cksum = in_cksum((u_short *)icmp, icmplen);
160 }
161
162 /*
163  * Send all of the ICMP queries.
164  */
165
166 void sendpings(int s, int querytype, struct hostdesc *head, int delay,
167                struct in_addr fromaddr)
168      
169 {
170         char buf[1500];
171         struct ip *ip = (struct ip *)buf;
172         struct sockaddr_in dst;
173
174         bzero(buf, 1500);
175         initpacket(buf, querytype, fromaddr);
176         dst.sin_family = AF_INET;
177 #ifdef DA_HAS_SIN_LEN
178         dst.sin_len = sizeof(dst);
179 #endif
180
181         while (head != NULL) {
182                 int rc;
183 #ifdef DEBUG
184                 printf("pinging %s\n", head->hostname);
185 #endif
186                 ip->ip_dst.s_addr = head->hostaddr.s_addr;
187                 dst.sin_addr = head->hostaddr;
188                 rc = sendto(s, buf, ip->ip_len, 0,
189                            (struct sockaddr *)&dst,
190                            sizeof(dst));
191                 if (rc != ip->ip_len) {
192                         perror("sendto");
193                 }
194                 /* Don't flood small pipes. */
195                 if (delay)
196                         usleep(delay);
197                 head = head->next;
198         }
199 }
200
201 /*
202  * Handles our timeout for us.  Called by the signal handler
203  * when we get a SIGARLM.
204  */
205
206 void myexit(int whatsig)
207 {
208         exit(0);
209 }
210
211 /*
212  * Open a raw socket for receiving ICMP.  Tell the kernel we want
213  * to supply the IP headers.
214  */
215
216 int get_icmp_socket()
217 {
218         int s;
219         int on = 1;
220         if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
221                 perror("socket");
222                 exit(1);
223         }
224         if (setsockopt(s, IPPROTO_IP, IP_HDRINCL,
225                        (const char *)&on, sizeof(on)) < 0) {
226                 perror("IP_HDRINCL");
227                 exit(1);
228         }
229         return s;
230 }
231
232 int
233 main(int argc, char **argv)
234 {
235    int s;
236
237    char *progname;
238    extern char *optarg;         /* getopt variable declarations */
239    extern int optind;
240    char ch;                     /* Holds the getopt result */
241    int hostcount;
242    int delay = 0;
243    int querytype = ICMP_TSTAMP;
244    struct in_addr fromaddr;
245    int timeout = 5;  /* Default to 5 seconds */
246    int identityfile;
247
248    fromaddr.s_addr = 0;
249
250    progname = argv[0];
251
252    querytype = IPOD_ICMPTYPE;  /* the magical death packet number */
253
254    while ((ch = getopt(argc, argv, "i:")) != -1)
255       switch(ch)
256       {
257       case 'i':
258          if (optarg[0] == '-')
259             identityfile = 0;
260          else if ((identityfile = open(optarg, 0)) < 0)
261          {
262             perror(optarg);
263             exit(1);
264          }
265          myidlen = read(identityfile, myid, IPOD_IDLEN);
266          if (optarg[0] != '-')
267             close(identityfile);
268          if (myidlen != IPOD_IDLEN)
269          {
270             fprintf(stderr, "%s: cannot read %d-byte identity\n",
271                     optarg[0] != '-' ? optarg : "<stdin>", IPOD_IDLEN);
272             exit(2);
273          }
274          break;
275       default:
276          usage(progname);
277          exit(-1);
278       }
279
280    argc -= optind;
281    argv += optind;
282    if (!argv[0] || !strlen(argv[0])) 
283    {
284       usage(progname);
285       exit(-1);
286    }
287
288    hostcount = makehosts(argv);
289
290    s = get_icmp_socket();
291
292    signal(SIGALRM, myexit);
293    alarm(timeout);
294    sendpings(s, querytype, hostnames, delay, fromaddr);
295    exit(0);
296 }
297    
298 /*
299  * in_cksum --
300  *      Checksum routine for Internet Protocol family headers (C Version)
301  *      From FreeBSD's ping.c
302  */
303
304 u_short
305 in_cksum(addr, len)
306         u_short *addr;
307         int len;
308 {
309         register int nleft = len;
310         register u_short *w = addr;
311         register int sum = 0;
312         u_short answer = 0;
313
314         /*
315          * Our algorithm is simple, using a 32 bit accumulator (sum), we add
316          * sequential 16 bit words to it, and at the end, fold back all the
317          * carry bits from the top 16 bits into the lower 16 bits.
318          */
319         while (nleft > 1)  {
320                 sum += *w++;
321                 nleft -= 2;
322         }
323
324         /* mop up an odd byte, if necessary */
325         if (nleft == 1) {
326                 *(u_char *)(&answer) = *(u_char *)w ;
327                 sum += answer;
328         }
329
330         /* add back carry outs from top 16 bits to low 16 bits */
331         sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
332         sum += (sum >> 16);                     /* add carry */
333         answer = ~sum;                          /* truncate to 16 bits */
334         return(answer);
335 }
336