Ping

#include"unp.h"
#include<netinet/in_systm.h>
#include<netinet/ip.h>
#include<netinet/ip_icmp.h>
#define BUFSIZE 1500
/*Globals*/
char sendbuf[BUFSIZE];
int datalen;
char *host;
int nsent; /* add 1 for each sendto() */
pid_t pid; /* out PID */
int sockfd;
int verbose;
/* function prototype */
void init_v6(void);
void proc_v4(char * ,ssize_t,struct msghdr *,struct timeval *);
void proc_v6(char *, ssize_t,struct msghdr *,struct timeval *);
void send_v4(void);
void send_v6(void);
void readloop(void);
void sig_alam(int);
void tv_sub(struct timeval *,struct timeval *);

struct proto
{
 void (*fproc)(char *,ssize_t,struct msghdr *,struct timeval *);
 void (*fsend)(void);
 void (*finit)(void);
 struct sockaddr *sasend; /* sockaddr() for send,from getaddrinfo */
 struct sockaddr *sarecv; /* sockaddr() for receving */
 socklen_t salen;  /* length of sockaddr()s*/
 int icmpproto;   /* IPPROTO_XXX value for ICMP */
 
}*pr;
#ifdef IPV6

#include<netinet/ip6.h>
#include<netinet/icmp6.h>

#endif

#define _GNU_SOURCE
#include <sys/time.h>
#include <sys/un.h>
#include <string.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/types.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>

#define BUFSIZE  1500
//#define IPV6
/* globals */
char sendbuf[BUFSIZE];

int datalen;   /* # bytes of data following ICMP header */
char *host;
int nsent;   /* add 1 for each sendto() */
pid_t pid;   /* our PID */
int sockfd;
int verbose;

struct proto {
 void (*fproc) (char *, ssize_t, struct msghdr *, struct timeval *);
 void (*fsend) (void);
 void (*finit) (void);
 struct sockaddr *sasend; /* sockaddr{} for send, from getaddrinfo */
 struct sockaddr *sarecv; /* sockaddr{} for receiving */
 socklen_t salen; /* length of sockaddr{}s */
 int icmpproto;  /* IPPROTO_xxx value for ICMP */
} *pr;

#define MAXLINE  4096 /* max text line length */
int daemon_proc;  /* set nonzero by daemon_init() */
void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
 int errno_save, n;
 char buf[MAXLINE + 1];

 errno_save = errno; /* value caller might want printed */
#ifdef HAVE_VSNPRINTF
 vsnprintf(buf, MAXLINE, fmt, ap); /* safe */
#else
 vsprintf(buf, fmt, ap); /* not safe */
#endif
 n = strlen(buf);
 if (errnoflag)
  snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
 strcat(buf, " ");

 if (daemon_proc) {
  syslog(level, "%s", buf);
 } else {
  fflush(stdout); /* in case stdout and stderr are the same */
  fputs(buf, stderr);
  fflush(stderr);
 }
 return;
}

void err_quit(const char *fmt, ...)
{
 va_list ap;

 va_start(ap, fmt);
 err_doit(0, LOG_ERR, fmt, ap);
 va_end(ap);
 exit(1);
}

void err_sys(const char *fmt, ...)
{
 va_list ap;

 va_start(ap, fmt);
 err_doit(1, LOG_ERR, fmt, ap);
 va_end(ap);
 exit(1);
}

char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
 static char str[128]; /* Unix domain is largest */

 switch (sa->sa_family) {
 case AF_INET:{
   struct sockaddr_in *sin = (struct sockaddr_in *)sa;

   if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))
       == NULL)
    return (NULL);
   return (str);
  }

#ifdef IPV6
 case AF_INET6:{
   struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;

   if (inet_ntop
       (AF_INET6, &sin6->sin6_addr, str,
        sizeof(str)) == NULL)
    return (NULL);
   return (str);
  }
#endif

#ifdef AF_UNIX
 case AF_UNIX:{
   struct sockaddr_un *unp = (struct sockaddr_un *)sa;

   /* OK to have no pathname bound to the socket: happens on
      every connect() unless client calls bind() first. */
   if (unp->sun_path[0] == 0)
    strcpy(str, "(no pathname bound)");
   else
    snprintf(str, sizeof(str), "%s", unp->sun_path);
   return (str);
  }
#endif

#ifdef HAVE_SOCKADDR_DL_STRUCT
 case AF_LINK:{
   struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;

   if (sdl->sdl_nlen > 0)
    snprintf(str, sizeof(str), "%*s",
      sdl->sdl_nlen, &sdl->sdl_data[0]);
   else
    snprintf(str, sizeof(str), "AF_LINK, index=%d",
      sdl->sdl_index);
   return (str);
  }
#endif
 default:
  snprintf(str, sizeof(str),
    "sock_ntop_host: unknown AF_xxx: %d, len %d",
    sa->sa_family, salen);
  return (str);
 }
 return (NULL);
}

char *Sock_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
 char *ptr;

 if ((ptr = sock_ntop_host(sa, salen)) == NULL)
  err_sys("sock_ntop_host error"); /* inet_ntop() sets errno */
 return (ptr);
}

void tv_sub(struct timeval *out, struct timeval *in)
{
 if ((out->tv_usec -= in->tv_usec) < 0) { /* out -= in */
  --out->tv_sec;
  out->tv_usec += 1000000;
 }
 out->tv_sec -= in->tv_sec;
}

void proc_v4(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)
{
 int hlen1, icmplen;
 double rtt;
 struct ip *ip;
 struct icmp *icmp;
 struct timeval *tvsend;

 ip = (struct ip *)ptr; /* start of IP header */
 hlen1 = ip->ip_hl << 2; /* length of IP header */
 if (ip->ip_p != IPPROTO_ICMP)
  return;  /* not ICMP */

 icmp = (struct icmp *)(ptr + hlen1); /* start of ICMP header */
 if ((icmplen = len - hlen1) < 8)
  return;  /* malformed packet */

 if (icmp->icmp_type == ICMP_ECHOREPLY) {
  if (icmp->icmp_id != pid)
   return; /* not a response to our ECHO_REQUEST */
  if (icmplen < 16)
   return; /* not enough data to use */

  tvsend = (struct timeval *)icmp->icmp_data;
  tv_sub(tvrecv, tvsend);
  rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;

  printf("%d bytes from %s: seq=%u, ttl=%d, rtt=%.3f ms ",
         icmplen, Sock_ntop_host(pr->sarecv, pr->salen),
         icmp->icmp_seq, ip->ip_ttl, rtt);

 } else if (verbose) {
  printf("  %d bytes from %s: type = %d, code = %d ",
         icmplen, Sock_ntop_host(pr->sarecv, pr->salen),
         icmp->icmp_type, icmp->icmp_code);
 }
}

void Gettimeofday(struct timeval *tv, void *foo)
{
 if (gettimeofday(tv, foo) == -1)
  err_sys("gettimeofday error");
 return;
}

uint16_t in_cksum(uint16_t * addr, int len)
{
 int nleft = len;
 uint32_t sum = 0;
 uint16_t *w = addr;
 uint16_t 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;
 }

 /* 4mop up an odd byte, if necessary */
 if (nleft == 1) {
  *(unsigned char *)(&answer) = *(unsigned char *)w;
  sum += answer;
 }

 /* 4add 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);
}

void
Sendto(int fd, const void *ptr, size_t nbytes, int flags,
       const struct sockaddr *sa, socklen_t salen)
{
 if (sendto(fd, ptr, nbytes, flags, sa, salen) != (ssize_t) nbytes)
  err_sys("sendto error");
}

void send_v4(void)
{
 int len;
 struct icmp *icmp;

 icmp = (struct icmp *)sendbuf;
 icmp->icmp_type = ICMP_ECHO;
 icmp->icmp_code = 0;
 icmp->icmp_id = pid;
 icmp->icmp_seq = nsent++;
 memset(icmp->icmp_data, 0xa5, datalen); /* fill with pattern */
 Gettimeofday((struct timeval *)icmp->icmp_data, NULL);

 len = 8 + datalen; /* checksum ICMP header and data */
 icmp->icmp_cksum = 0;
 icmp->icmp_cksum = in_cksum((u_short *) icmp, len);

 Sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen);
}

int datalen = 56;  /* data that goes with ICMP echo request */

typedef void Sigfunc(int); /* for signal handlers */
Sigfunc *signal(int signo, Sigfunc * func)
{
 struct sigaction act, oact;

 act.sa_handler = func;
 sigemptyset(&act.sa_mask);
 act.sa_flags = 0;
 if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
  act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */
#endif
 } else {
#ifdef SA_RESTART
  act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */
#endif
 }
 if (sigaction(signo, &act, &oact) < 0)
  return (SIG_ERR);
 return (oact.sa_handler);
}

Sigfunc *Signal(int signo, Sigfunc * func)
{    /* for our signal() function */
 Sigfunc *sigfunc;

 if ((sigfunc = signal(signo, func)) == SIG_ERR)
  err_sys("signal error");
 return (sigfunc);
}

struct addrinfo *Host_serv(const char *host, const char *serv, int family,
      int socktype)
{
 int n;
 struct addrinfo hints, *res;

 bzero(&hints, sizeof(struct addrinfo));
 hints.ai_flags = AI_CANONNAME; /* always return canonical name */
 hints.ai_family = family; /* 0, AF_INET, AF_INET6, etc. */
 hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */

 if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
  err_quit("host_serv error for %s, %s: %s",
    (host == NULL) ? "(no hostname)" : host,
    (serv == NULL) ? "(no service name)" : serv,
    gai_strerror(n));

 return (res);  /* return pointer to first on linked list */
}

void *Calloc(size_t n, size_t size)
{
 void *ptr;

 if ((ptr = calloc(n, size)) == NULL)
  err_sys("calloc error");
 return (ptr);
}

void sig_alrm(int signo)
{
 (*pr->fsend) ();

 alarm(1);
 return;
}

int Socket(int family, int type, int protocol)
{
 int n;

 if ((n = socket(family, type, protocol)) < 0)
  err_sys("socket error");
 return (n);
}

void readloop(void)
{
 int size;
 char recvbuf[BUFSIZE];
 char controlbuf[BUFSIZE];
 struct msghdr msg;
 struct iovec iov;
 ssize_t n;
 struct timeval tval;

 sockfd = Socket(pr->sasend->sa_family, SOCK_RAW, pr->icmpproto);
 setuid(getuid()); /* don't need special permissions any more */
 if (pr->finit)
  (*pr->finit) ();

 size = 60 * 1024; /* OK if setsockopt fails */
 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));

 sig_alrm(SIGALRM); /* send first packet */

 iov.iov_base = recvbuf;
 iov.iov_len = sizeof(recvbuf);
 msg.msg_name = pr->sarecv;
 msg.msg_iov = &iov;
 msg.msg_iovlen = 1;
 msg.msg_control = controlbuf;
 for (;;) {
  msg.msg_namelen = pr->salen;
  msg.msg_controllen = sizeof(controlbuf);
  n = recvmsg(sockfd, &msg, 0);
  if (n < 0) {
   if (errno == EINTR)
    continue;
   else
    err_sys("recvmsg error");
  }

  Gettimeofday(&tval, NULL);
  (*pr->fproc) (recvbuf, n, &msg, &tval);
 }
}

void proc_v6(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)
{
#ifdef IPV6
 double rtt;
 struct icmp6_hdr *icmp6;
 struct timeval *tvsend;
 struct cmsghdr *cmsg;
 int hlim;

 icmp6 = (struct icmp6_hdr *)ptr;
 if (len < 8)
  return;  /* malformed packet */

 if (icmp6->icmp6_type == ICMP6_ECHO_REPLY) {
  if (icmp6->icmp6_id != pid)
   return; /* not a response to our ECHO_REQUEST */
  if (len < 16)
   return; /* not enough data to use */

  tvsend = (struct timeval *)(icmp6 + 1);
  tv_sub(tvrecv, tvsend);
  rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;

  hlim = -1;
  for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
       cmsg = CMSG_NXTHDR(msg, cmsg)) {
   if (cmsg->cmsg_level == IPPROTO_IPV6
       && cmsg->cmsg_type == IPV6_HOPLIMIT) {
    hlim = *(u_int32_t *) CMSG_DATA(cmsg);
    break;
   }
  }
  printf("%d bytes from %s: seq=%u, hlim=",
         len, Sock_ntop_host(pr->sarecv, pr->salen),
         icmp6->icmp6_seq);
  if (hlim == -1)
   printf("???"); /* ancillary data missing */
  else
   printf("%d", hlim);
  printf(", rtt=%.3f ms ", rtt);
 } else if (verbose) {
  printf("  %d bytes from %s: type = %d, code = %d ",
         len, Sock_ntop_host(pr->sarecv, pr->salen),
         icmp6->icmp6_type, icmp6->icmp6_code);
 }
#endif    /* IPV6 */
}

void send_v6()
{
#ifdef IPV6
 int len;
 struct icmp6_hdr *icmp6;

 icmp6 = (struct icmp6_hdr *)sendbuf;
 icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
 icmp6->icmp6_code = 0;
 icmp6->icmp6_id = pid;
 icmp6->icmp6_seq = nsent++;
 memset((icmp6 + 1), 0xa5, datalen); /* fill with pattern */
 Gettimeofday((struct timeval *)(icmp6 + 1), NULL);

 len = 8 + datalen; /* 8-byte ICMPv6 header */

 Sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen);
 /* 4kernel calculates and stores checksum for us */
#endif    /* IPV6 */
}

void init_v6()
{
#ifdef IPV6
 int on = 1;

 if (verbose == 0) {
  /* install a filter that only passes ICMP6_ECHO_REPLY unless verbose */
  struct icmp6_filter myfilt;
  ICMP6_FILTER_SETBLOCKALL(&myfilt);
  ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &myfilt);
  setsockopt(sockfd, IPPROTO_IPV6, ICMP6_FILTER, &myfilt,
      sizeof(myfilt));
  /* ignore error return; the filter is an optimization */
 }

 /* ignore error returned below; we just won't receive the hop limit */
#ifdef IPV6_RECVHOPLIMIT
 /* RFC 3542 */
 setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
#else
 /* RFC 2292 */
 setsockopt(sockfd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));
#endif
#endif
}
struct proto proto_v4 = { proc_v4, send_v4, NULL, NULL, NULL, 0, IPPROTO_ICMP };

#ifdef IPV6
struct proto proto_v6 =
    { proc_v6, send_v6, init_v6, NULL, NULL, 0, IPPROTO_ICMPV6 };
#endif
int main(int argc, char **argv)
{
 int c;
 struct addrinfo *ai;
 char *h;

 opterr = 0;  /* don't want getopt() writing to stderr */
 while ((c = getopt(argc, argv, "v")) != -1) {
  switch (c) {
  case 'v':
   verbose++;
   break;

  case '?':
   err_quit("unrecognized option: %c", c);
  }
 }

 if (optind != argc - 1)
  err_quit("usage: ping [ -v ] <hostname>");
 host = argv[optind];

 pid = getpid() & 0xffff; /* ICMP ID field is 16 bits */
 Signal(SIGALRM, sig_alrm);

 ai = Host_serv(host, NULL, 0, 0);

 h = Sock_ntop_host(ai->ai_addr, ai->ai_addrlen);
 printf("PING %s (%s): %d data bytes ",
        ai->ai_canonname ? ai->ai_canonname : h, h, datalen);

 /* 4initialize according to protocol */
 if (ai->ai_family == AF_INET) {
  pr = &proto_v4;
#ifdef IPV6
 } else if (ai->ai_family == AF_INET6) {
  pr = &proto_v6;
  if (IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6 *)
         ai->ai_addr)->sin6_addr)))
   err_quit("cannot ping IPv4-mapped IPv6 address");
#endif
 } else
  err_quit("unknown address family %d", ai->ai_family);

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

 readloop();

 exit(0);
}

原文地址:https://www.cnblogs.com/liuhg/p/Ping.html