Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #if HAVE_CONFIG_H
- # include <config.h>
- #endif
- #if STDC_HEADERS
- # include <stdlib.h>
- # include <stdio.h>
- # include <string.h>
- # include <inttypes.h>
- # include <errno.h>
- # include <assert.h>
- #else
- # error "You don't have the standard C99 header files installed"
- #endif /* STDC_HEADERS */
- #ifdef HAVE_STDINT_H
- # include <stdint.h>
- #endif
- #if HAVE_UNISTD_H
- # include <unistd.h>
- #endif
- #if HAVE_FCNTL_H
- # include <fcntl.h>
- #endif
- #if HAVE_SYS_TYPES_H
- # include <sys/types.h>
- #endif
- #if HAVE_SYS_STAT_H
- # include <sys/stat.h>
- #endif
- #if TIME_WITH_SYS_TIME
- # include <sys/time.h>
- # include <time.h>
- #else
- # if HAVE_SYS_TIME_H
- # include <sys/time.h>
- # else
- # include <time.h>
- # endif
- #endif
- #if HAVE_SYS_SOCKET_H
- # include <sys/socket.h>
- #endif
- #if HAVE_NETDB_H
- # include <netdb.h>
- #endif
- #if HAVE_NETINET_IN_SYSTM_H
- # include <netinet/in_systm.h>
- #endif
- #if HAVE_NETINET_IN_H
- # include <netinet/in.h>
- #endif
- #if HAVE_NETINET_IP_H
- # include <netinet/ip.h>
- #endif
- #if HAVE_NETINET_IP_ICMP_H
- # include <netinet/ip_icmp.h>
- #endif
- #ifdef HAVE_NETINET_IP_VAR_H
- # include <netinet/ip_var.h>
- #endif
- #if HAVE_NETINET_IP6_H
- # include <netinet/ip6.h>
- #endif
- #if HAVE_NETINET_ICMP6_H
- # include <netinet/icmp6.h>
- #endif
- #include "oping.h"
- #if WITH_DEBUG
- # define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__)
- #else
- # define dprintf(...) /**/
- #endif
- #define PING_ERRMSG_LEN 256
- #define PING_TABLE_LEN 5381
- struct pinghost
- {
- /* username: name passed in by the user */
- char *username;
- /* hostname: name returned by the reverse lookup */
- char *hostname;
- struct sockaddr_storage *addr;
- socklen_t addrlen;
- int addrfamily;
- int ident;
- int sequence;
- struct timeval *timer;
- double latency;
- uint32_t dropped;
- int recv_ttl;
- uint8_t recv_qos;
- char *data;
- void *context;
- struct pinghost *next;
- struct pinghost *table_next;
- };
- struct pingobj
- {
- double timeout;
- int ttl;
- int addrfamily;
- uint8_t qos;
- char *data;
- int fd4;
- int fd6;
- struct sockaddr *srcaddr;
- socklen_t srcaddrlen;
- char *device;
- char set_mark;
- int mark;
- char errmsg[PING_ERRMSG_LEN];
- pinghost_t *head;
- pinghost_t *table[PING_TABLE_LEN];
- };
- /*
- * private (static) functions
- */
- /* Even though Posix requires "strerror_r" to return an "int",
- * some systems (e.g. the GNU libc) return a "char *" _and_
- * ignore the second argument ... -tokkee */
- static int ping_timeval_add (struct timeval *tv1, struct timeval *tv2,
- struct timeval *res)
- {
- res->tv_sec = tv1->tv_sec + tv2->tv_sec;
- res->tv_usec = tv1->tv_usec + tv2->tv_usec;
- while (res->tv_usec > 1000000)
- {
- res->tv_usec -= 1000000;
- res->tv_sec++;
- }
- return (0);
- }
- static int ping_timeval_sub (struct timeval *tv1, struct timeval *tv2,
- struct timeval *res)
- {
- if ((tv1->tv_sec < tv2->tv_sec)
- || ((tv1->tv_sec == tv2->tv_sec)
- && (tv1->tv_usec < tv2->tv_usec)))
- return (-1);
- res->tv_sec = tv1->tv_sec - tv2->tv_sec;
- res->tv_usec = tv1->tv_usec - tv2->tv_usec;
- assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec >= 0)));
- while (res->tv_usec < 0)
- {
- res->tv_usec += 1000000;
- res->tv_sec--;
- }
- return (0);
- }
- static uint16_t ping_icmp4_checksum (char *buf, size_t len)
- {
- uint32_t sum = 0;
- uint16_t ret = 0;
- uint16_t *ptr;
- for (ptr = (uint16_t *) buf; len > 1; ptr++, len -= 2)
- sum += *ptr;
- if (len == 1)
- {
- *(char *) &ret = *(char *) ptr;
- sum += ret;
- }
- /* Do this twice to get all possible carries.. */
- sum = (sum >> 16) + (sum & 0xFFFF);
- sum = (sum >> 16) + (sum & 0xFFFF);
- ret = ~sum;
- return (ret);
- }
- static pinghost_t *ping_receive_ipv4 (pingobj_t *obj, char *buffer,
- size_t buffer_len)
- {
- struct ip *ip_hdr;
- struct icmp *icmp_hdr;
- size_t ip_hdr_len;
- uint16_t recv_checksum;
- uint16_t calc_checksum;
- uint16_t ident;
- uint16_t seq;
- pinghost_t *ptr;
- if (buffer_len < sizeof (struct ip))
- return (NULL);
- ip_hdr = (struct ip *) buffer;
- ip_hdr_len = ip_hdr->ip_hl << 2;
- if (buffer_len < ip_hdr_len)
- return (NULL);
- buffer += ip_hdr_len;
- buffer_len -= ip_hdr_len;
- if (buffer_len < ICMP_MINLEN)
- return (NULL);
- icmp_hdr = (struct icmp *) buffer;
- if (icmp_hdr->icmp_type != ICMP_ECHOREPLY)
- {
- dprintf ("Unexpected ICMP type: %"PRIu8"\n", icmp_hdr->icmp_type);
- return (NULL);
- }
- recv_checksum = icmp_hdr->icmp_cksum;
- /* This writes to buffer. */
- icmp_hdr->icmp_cksum = 0;
- calc_checksum = ping_icmp4_checksum (buffer, buffer_len);
- if (recv_checksum != calc_checksum)
- {
- dprintf ("Checksum missmatch: Got 0x%04"PRIx16", "
- "calculated 0x%04"PRIx16"\n",
- recv_checksum, calc_checksum);
- return (NULL);
- }
- ident = ntohs (icmp_hdr->icmp_id);
- seq = ntohs (icmp_hdr->icmp_seq);
- if (ptr != NULL){
- ptr->recv_ttl = (int) ip_hdr->ip_ttl;
- ptr->recv_qos = (uint8_t) ip_hdr->ip_tos;
- }
- return (ptr);
- }
- static int ping_receive_one (pingobj_t *obj, struct timeval *now, int addrfam)
- {
- int fd = obj->fd4; //Obj is used here
- struct timeval diff, pkt_now = *now;
- pinghost_t *host = NULL;
- int recv_ttl;
- /*
- * Set up the receive buffer..
- */
- struct msghdr msghdr;
- struct cmsghdr *cmsg;
- char payload_buffer[4096];
- ssize_t payload_buffer_len;
- char control_buffer[4096];
- struct iovec payload_iovec;
- memset (&payload_iovec, 0, sizeof (payload_iovec));
- payload_iovec.iov_base = payload_buffer;
- payload_iovec.iov_len = sizeof (payload_buffer);
- memset (&msghdr, 0, sizeof (msghdr));
- /* unspecified source address */
- msghdr.msg_name = NULL;
- msghdr.msg_namelen = 0;
- /* output buffer vector, see readv(2) */
- msghdr.msg_iov = &payload_iovec;
- msghdr.msg_iovlen = 1;
- /* output buffer for control messages */
- msghdr.msg_control = control_buffer;
- msghdr.msg_controllen = sizeof (control_buffer);
- /* flags; this is an output only field.. */
- msghdr.msg_flags = 0;
- payload_buffer_len = recvmsg (fd, &msghdr, /* flags = */ 0);
- if (payload_buffer_len < 0)
- {
- return (-1);
- }
- dprintf ("Read %zi bytes from fd = %i\n", payload_buffer_len, fd);
- /* Iterate over all auxiliary data in msghdr */
- recv_ttl = -1;
- for (cmsg = CMSG_FIRSTHDR (&msghdr); cmsg != NULL; cmsg = CMSG_NXTHDR (&msghdr, cmsg))
- {
- if (cmsg->cmsg_level == SOL_SOCKET)
- {
- #ifdef SO_TIMESTAMP
- if (cmsg->cmsg_type == SO_TIMESTAMP)
- memcpy (&pkt_now, CMSG_DATA (cmsg), sizeof (pkt_now)); //Suspicious; likely can be cut
- #endif /* SO_TIMESTAMP */
- }
- if (cmsg->cmsg_level != IPPROTO_IP)
- continue;
- if (cmsg->cmsg_type == IP_TTL)
- {
- memcpy (&recv_ttl, CMSG_DATA (cmsg),
- sizeof (recv_ttl));
- dprintf ("TTLv4 = %i;\n", recv_ttl);
- }
- } /* }}} for (cmsg) */
- host = ping_receive_ipv4 (obj, payload_buffer, payload_buffer_len);
- if (host == NULL)
- return (-1);
- dprintf ("rcvd: %12i.%06i\n",
- (int) pkt_now.tv_sec,
- (int) pkt_now.tv_usec);
- dprintf ("sent: %12i.%06i\n",
- (int) host->timer->tv_sec,
- (int) host->timer->tv_usec);
- if (ping_timeval_sub (&pkt_now, host->timer, &diff) < 0)
- {
- timerclear (host->timer);
- return (-1);
- }
- dprintf ("diff: %12i.%06i\n",
- (int) diff.tv_sec,
- (int) diff.tv_usec);
- if (recv_ttl >= 0)
- host->recv_ttl = recv_ttl;
- host->latency = ((double) diff.tv_usec) / 1000.0;
- host->latency += ((double) diff.tv_sec) * 1000.0;
- timerclear (host->timer);
- return (0);
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Sending functions: *
- * *
- * ping_send_all *
- * +-> ping_send_one_ipv4 *
- * `-> ping_send_one_ipv6 *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph,
- const void *buf, size_t buflen, int fd)
- {
- ssize_t ret;
- if (gettimeofday (ph->timer, NULL) == -1)
- {
- timerclear (ph->timer);
- return (-1);
- }
- ret = sendto (fd, buf, buflen, 0,
- (struct sockaddr *) ph->addr, ph->addrlen);
- if (ret < 0)
- {
- ping_set_errno (obj, errno);
- }
- return (ret);
- }
- static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph, int fd)
- {
- struct icmp *icmp4;
- int status;
- char buf[4096] = {0};
- size_t buflen;
- char *data;
- size_t datalen;
- dprintf ("ph->hostname = %s\n", ph->hostname);
- icmp4 = (struct icmp *) buf;
- *icmp4 = (struct icmp) {
- .icmp_type = ICMP_ECHO,
- .icmp_id = htons (ph->ident),
- .icmp_seq = htons (ph->sequence),
- };
- datalen = strlen (ph->data);
- buflen = ICMP_MINLEN + datalen;
- if (sizeof (buf) < buflen)
- return (EINVAL);
- data = buf + ICMP_MINLEN;
- memcpy (data, ph->data, datalen);
- icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen);
- dprintf ("Sending ICMPv4 package with ID 0x%04x\n", ph->ident);
- status = ping_sendto (obj, ph, buf, buflen, fd);
- if (status < 0)
- {
- perror ("ping_sendto");
- return (-1);
- }
- dprintf ("sendto: status = %i\n", status);
- return (0);
- }
- static int ping_send_one (pingobj_t *obj, pinghost_t *ptr, int fd)
- {
- if (gettimeofday (ptr->timer, NULL) == -1)
- {
- /* start timer.. The GNU `ping6' starts the timer before
- * sending the packet, so I will do that too */
- timerclear (ptr->timer);
- return (-1);
- }
- else
- {
- dprintf ("timer set for hostname = %s\n", ptr->hostname);
- }
- dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname);
- if (ping_send_one_ipv4 (obj, ptr, fd) != 0)
- {
- timerclear (ptr->timer);
- return (-1);
- }
- ptr->sequence++;
- return (0);
- }
- /*
- * Set the TTL of a socket protocol independently.
- */
- static int ping_set_ttl (pingobj_t *obj, int ttl)
- {
- int ret = 0;
- char errbuf[PING_ERRMSG_LEN];
- if (setsockopt (obj->fd4, IPPROTO_IP, IP_TTL,
- &ttl, sizeof (ttl)))
- {
- ret = errno;
- ping_set_error (obj, "ping_set_ttl",
- sstrerror (ret, errbuf, sizeof (errbuf)));
- dprintf ("Setting TTLv4 failed: %s\n", errbuf);
- }
- return (ret);
- }
- static pinghost_t *ping_alloc (void)
- {
- pinghost_t *ph;
- size_t ph_size;
- ph_size = sizeof (pinghost_t)
- + sizeof (struct sockaddr_storage)
- + sizeof (struct timeval);
- ph = (pinghost_t *) malloc (ph_size);
- if (ph == NULL)
- return (NULL);
- memset (ph, '\0', ph_size);
- ph->timer = (struct timeval *) (ph + 1);
- ph->addr = (struct sockaddr_storage *) (ph->timer + 1);
- ph->addrlen = sizeof (struct sockaddr_storage);
- ph->latency = -1.0;
- ph->dropped = 0;
- ph->ident = ping_get_ident () & 0xFFFF;
- return (ph);
- }
- static void ping_free (pinghost_t *ph)
- {
- if (ph == NULL)
- return;
- free (ph->username);
- free (ph->hostname);
- free (ph->data);
- free (ph);
- }
- /* ping_open_socket opens, initializes and returns a new raw socket to use for
- * ICMPv4 or ICMPv6 packets. addrfam must be either AF_INET or AF_INET6. On
- * error, -1 is returned and obj->errmsg is set appropriately. */
- static int ping_open_socket(pingobj_t *obj, int addrfam)
- {
- int fd;
- fd = socket(addrfam, SOCK_RAW, IPPROTO_ICMP);
- if (fd == -1)
- {
- ping_set_errno (obj, errno);
- return -1;
- }
- if (obj->srcaddr != NULL)
- {
- assert (obj->srcaddrlen > 0);
- assert (obj->srcaddrlen <= sizeof (struct sockaddr_storage));
- if (bind (fd, obj->srcaddr, obj->srcaddrlen) == -1)
- {
- ping_set_errno (obj, errno);
- close (fd);
- return -1;
- }
- }
- #ifdef SO_TIMESTAMP
- if (1) /* {{{ */
- {
- int status = setsockopt (fd, SOL_SOCKET, SO_TIMESTAMP,
- &(int){1}, sizeof(int));
- if (status != 0)
- {
- ping_set_errno (obj, errno);
- close (fd);
- return -1;
- }
- } /* }}} if (1) */
- #endif /* SO_TIMESTAMP */
- /* Enable receiving the TTL field */
- setsockopt (fd, IPPROTO_IP, IP_RECVTTL, &(int){1}, sizeof(int));
- return fd;
- }
- /*
- * public methods
- */
- const char *ping_get_error (pingobj_t *obj)
- {
- if (obj == NULL)
- return (NULL);
- return (obj->errmsg);
- }
- pingobj_t *ping_construct (void)
- {
- pingobj_t *obj;
- if ((obj = malloc (sizeof (*obj))) == NULL)
- return (NULL);
- memset (obj, 0, sizeof (*obj));
- obj->timeout = PING_DEF_TIMEOUT;
- obj->ttl = PING_DEF_TTL;
- obj->addrfamily = PING_DEF_AF;
- obj->data = strdup (PING_DEF_DATA);
- obj->qos = 0;
- obj->fd4 = -1;
- obj->fd6 = -1;
- return (obj);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement