/* * Embedded Ping Program Examples * Copyright 2003 by Floyd L. Davidson, floyd@barrow.com * * 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, 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 details. * * myping.c -- Ping a host within a larger program. * * $Id: myping.c,v 1.1.0.0 2003/11/05 19:29:01 floyd Exp floyd $ */ /* * Demonstrate how to ping a distant host from within a * C program. * * Note that this program must be run as root to work! */ #include #include #include #include #include #include #include #include #include #include jmp_buf env; int in_cksum(unsigned short *, int); int ping(struct sockaddr *); int pinger(char *); void catcher(int); int main(int argc, char *argv[]) { int rtnvalue; if (argc != 2) { printf("Usage: ping host\n"); exit(0); } switch (rtnvalue = pinger(argv[1])) { case 0: printf("%s is alive\n", argv[1]); break; case 1: printf("Invalid response from %s\n", argv[1]); break; case 2: printf("No response from %s\n", argv[1]); break; case 3: printf("Error: unable to send packet to %s\n", argv[1]); break; case 4: printf("Error: unable to create a socket.\n"); break; case 5: printf("Error: icmp protocol is not supported.\n"); break; case 10: printf("Unknown host: %s\n", argv[1]); break; case 11: printf("No IP address for %s\n", argv[1]); break; case 12: printf("DNS server error for %s\n", argv[1]); break; case 13: printf("Transient DNS server error for %s\n", argv[1]); break; case 14: printf("Unknown DNS server error for %s\n", argv[1]); break; default: printf("Error: Unknown response."); break; } return rtnvalue; } /* * call ping to determine if a host is alive * * This function is a wrapper that sort out all * the needed information from the known information. * In this demo, only a name or an IP address is * known, so everything needs to be fetched and * massaged properly. * * Returns 0 on success, otherwise returns * * error codes from ping(), or * * 10 -- Unknown host * 11 -- No ip address for host * 12 -- DNS server error * 13 -- Transient DNS server error * 14 -- Unknown DNS error */ int pinger(char *host) { char hostname[80]; char hostaddr[80]; struct sockaddr_in dest; struct hostent *hp; struct in_addr inaddr; /* zero out host information */ memset(&dest, 0, sizeof dest); /* * Initalize pointer to a hostent struct */ if (NULL == (hp = gethostbyname(host))) { switch (h_errno) { case HOST_NOT_FOUND: return 10; case NO_ADDRESS: return 11; case NO_RECOVERY: return 12; case TRY_AGAIN: return 13; } return 14; } /* init parts of sockaddr_in struct */ dest.sin_family = hp->h_addrtype; memcpy(&dest.sin_addr, hp->h_addr, hp->h_length); sprintf(hostname, "%s", hp->h_name); sprintf(hostaddr, "%s", inet_ntoa(dest.sin_addr)); /* Print Destination host IP address */ printf("Destination Host: %s\n", hostname); printf("Destination Host IP: %s\n", hostaddr); /* try to get an official name for this host */ if (inet_aton(inet_ntoa(dest.sin_addr), &inaddr)) { if (NULL != (hp = gethostbyaddr(&inaddr, 4, AF_INET))) { sprintf(hostname, "%s", hp->h_name); printf("Official Host FQDN: %s\n", hostname); } } return ping((struct sockaddr *) &dest); } /* * ping the host whose address is in struct sockaddr dest * * Silently ping the distant host and return status of * 0 on success, otherwise * 1 -- invalid packet received * 2 -- alarm() timed out waiting for ping * 3 -- sendto() failed to send packet * 4 -- socket() failed to create socket * 5 -- icmp protocol is not supported */ /* actually only needs to be 8 bytes, minimum */ #define PACKETSZ (sizeof(struct icmp)) int ping(struct sockaddr *dest) { unsigned char packet[PACKETSZ]; int sockfd, pid, packetsize, recv_bytes; struct icmp *ic; struct protoent *pp; socklen_t fromlen; pid = getpid() & 0xffff; /* * get protocol information */ if (NULL == (pp = getprotobyname("icmp"))) { return 5; } /* build an ICMP header for the send packet */ ic = (struct icmp *) packet; ic->icmp_type = ICMP_ECHO; /* 1 byte */ ic->icmp_code = 0; /* 1 byte */ ic->icmp_cksum = 0; /* 2 bytes */ ic->icmp_seq = 1; /* 2 bytes */ ic->icmp_id = pid; /* 2 bytes */ /* as above, use only first 8 bytes */ packetsize = 8; /* compute checksum */ ic->icmp_cksum = in_cksum((unsigned short *)ic, packetsize); /* create a raw socket */ if (0 > (sockfd = socket(AF_INET, SOCK_RAW, pp->p_proto))) { return 4; } /* send the echo request */ if (packetsize != sendto(sockfd, packet, packetsize, 0, dest, sizeof *dest)) { return 3; } /* zero out host information */ memset(dest, 0, sizeof *dest); /* catch alarm signal if no answer comes back */ signal(SIGALRM, catcher); fromlen = sizeof *dest; if (setjmp(env)) { return 2; /* no answer, return an error */ } alarm(2); /* wait up to 2 seconds */ recv_bytes = recvfrom(sockfd, packet, sizeof packet, 0, dest, &fromlen); alarm(0); if (recv_bytes != packetsize + (int) sizeof (struct iphdr)) { return 1; } /* if all else fails, we've succeeded! */ return 0; } /* * in_cksum -- A checksum routine for IP family headers * * This is greatfully stolen from the original ping.c * public domain source code by Mike Muuss. */ int in_cksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short *w = addr; unsigned 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) { *(unsigned char *)(&answer) = *(unsigned 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); } /* * catch SIGALRM and continue */ void catcher(int sig) { longjmp(env, sig); return; }