From 6995e2cfd517af456904db26b7781b4953feeacc Mon Sep 17 00:00:00 2001 From: octo Date: Mon, 8 May 2006 14:16:34 +0000 Subject: [PATCH] Created standard subversion directories. Imported everything from the collectd repository. --- AUTHORS | 0 ChangeLog | 2 + Makefile.am | 7 + NEWS | 0 README | 28 + ToDo | 0 configure.ac | 143 +++ debian/README | 6 + debian/changelog | 5 + debian/compat | 1 + debian/control | 36 + debian/copyright | 25 + debian/liboping-dev.dirs | 3 + debian/liboping-dev.docs | 6 + debian/liboping-dev.manpages | 8 + debian/liboping.dirs | 1 + debian/liboping.docs | 6 + debian/oping.dirs | 1 + debian/oping.docs | 6 + debian/oping.manpages | 1 + debian/rules | 120 +++ src/Makefile.am | 26 + src/liboping.c | 1172 ++++++++++++++++++++++++ src/mans/Makefile.am | 28 + src/mans/liboping.pod | 86 ++ src/mans/oping.pod | 58 ++ src/mans/ping_construct.pod | 38 + src/mans/ping_get_error.pod | 30 + src/mans/ping_host_add.pod | 49 + src/mans/ping_iterator_get.pod | 45 + src/mans/ping_iterator_get_context.pod | 45 + src/mans/ping_iterator_get_info.pod | 96 ++ src/mans/ping_send.pod | 42 + src/mans/ping_setopt.pod | 63 ++ src/oping.c | 403 ++++++++ src/oping.h | 86 ++ 36 files changed, 2672 insertions(+) create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 ToDo create mode 100644 configure.ac create mode 100644 debian/README create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/liboping-dev.dirs create mode 100644 debian/liboping-dev.docs create mode 100644 debian/liboping-dev.manpages create mode 100644 debian/liboping.dirs create mode 100644 debian/liboping.docs create mode 100644 debian/oping.dirs create mode 100644 debian/oping.docs create mode 100644 debian/oping.manpages create mode 100755 debian/rules create mode 100644 src/Makefile.am create mode 100644 src/liboping.c create mode 100644 src/mans/Makefile.am create mode 100644 src/mans/liboping.pod create mode 100644 src/mans/oping.pod create mode 100644 src/mans/ping_construct.pod create mode 100644 src/mans/ping_get_error.pod create mode 100644 src/mans/ping_host_add.pod create mode 100644 src/mans/ping_iterator_get.pod create mode 100644 src/mans/ping_iterator_get_context.pod create mode 100644 src/mans/ping_iterator_get_info.pod create mode 100644 src/mans/ping_send.pod create mode 100644 src/mans/ping_setopt.pod create mode 100644 src/oping.c create mode 100644 src/oping.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..bc96f7a --- /dev/null +++ b/ChangeLog @@ -0,0 +1,2 @@ +2006-05-08, Version 0.1.0 + * Initial release. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..4c53dab --- /dev/null +++ b/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = libltdl src +INCLUDES = $(LTDLINCL) + +EXTRA_DIST = debian + +dist-hook: + find $(distdir) -type d -name '.svn' | xargs rm -rf diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..7d2e8d4 --- /dev/null +++ b/README @@ -0,0 +1,28 @@ + liboping - Library to ping IPv4 and IPv6 hosts in parallel +============================================================ +http://verplant.org/liboping/ + +About +----- + + liboping was inspired by ping, libping and fping: It differs from these + existing solutions in that it can `ping' multiple hosts in parallel using + IPv4 or IPv6 transparently. Other design principles were an object oriented + interface, simplicity and extensibility. + + +Features +-------- + + * Support for multiple hosts. + + * Support for IPv4 and IPv6. + + * Object oriented interface. + + +Author +------ + + Florian octo Forster + diff --git a/ToDo b/ToDo new file mode 100644 index 0000000..e69de29 diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..ab94506 --- /dev/null +++ b/configure.ac @@ -0,0 +1,143 @@ +AC_INIT(liboping, 0.1.0) +AC_CONFIG_SRCDIR(src/liboping.c) +AC_CONFIG_HEADERS(src/config.h) +AM_INIT_AUTOMAKE(dist-bzip2) +AC_LANG(C) + +AC_PREFIX_DEFAULT("/opt/oping") + +# +# Check for programs/utilities +# +AC_PROG_CC +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AM_CONDITIONAL(COMPILER_IS_GCC, test "x$GCC" = "xyes") + +# +# configure libtool +# +AC_LIBLTDL_CONVENIENCE +AC_SUBST(LTDLINCL) +AC_SUBST(LIBLTDL) +AC_LIBTOOL_DLOPEN +AC_PROG_LIBTOOL +#AC_PROG_RANLIB +AC_CONFIG_SUBDIRS(libltdl src) + +# +# Checks for header files. +# +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h) +AC_CHECK_HEADERS(math.h) +AC_CHECK_HEADERS(fcntl.h) +AC_CHECK_HEADERS(sys/types.h) +AC_CHECK_HEADERS(sys/stat.h) +AC_HEADER_TIME +AC_CHECK_HEADERS(sys/socket.h) +AC_CHECK_HEADERS(netdb.h) +AC_CHECK_HEADERS(signal.h) + +# This sucks, but what can I do..? +AC_CHECK_HEADERS(netinet/in_systm.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/in.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/ip.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/ip_icmp.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_NETINET_IP_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/ip_var.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_NETINET_IP_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/ip6.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +]) +AC_CHECK_HEADERS(netinet/icmp6.h, [], [], +[#if HAVE_STDINT_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_NETINET_IP6_H +# include +#endif +]) + +socket_needs_socket="no" +AC_CHECK_FUNCS(socket, [], + AC_CHECK_LIB(socket, socket, + [socket_needs_socket="yes"], + AC_MSG_ERROR(cannot find socket))) +AM_CONDITIONAL(BUILD_WITH_LIBSOCKET, test "x$socket_needs_socket" = "xyes") + +nanosleep_needs_rt="no" +AC_CHECK_FUNCS(nanosleep, [], + AC_CHECK_LIB(rt, nanosleep, + [nanosleep_needs_rt="yes"], + AC_MSG_ERROR(cannot find nanosleep))) +AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes") + +AC_OUTPUT(Makefile src/Makefile src/mans/Makefile) diff --git a/debian/README b/debian/README new file mode 100644 index 0000000..252cfc2 --- /dev/null +++ b/debian/README @@ -0,0 +1,6 @@ +The Debian Package liboping +---------------------------- + +Comments regarding the Package + +Florian Forster , Sun, 30 Apr 2006 18:46:29 +0200 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..e5f3994 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +liboping (0.1.0) unstable; urgency=low + + * Initial Release. + + -- Florian Forster Sun, 30 Apr 2006 18:46:29 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +4 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..de3d2c9 --- /dev/null +++ b/debian/control @@ -0,0 +1,36 @@ +Source: liboping +Section: libs +Priority: optional +Maintainer: Florian Forster +Build-Depends: debhelper (>= 4.0.0), autotools-dev +Standards-Version: 3.6.2 + +Package: liboping +Section: libs +Architecture: any +Depends: ${shlibs:Depends} +Description: Library to send ICMPv4/ICMPv6 echo packets to multiple hosts + liboping was inspired by ping, libping and fping: It differs from these + existing solutions in that it can `ping' multiple hosts in parallel using IPv4 + or IPv6 transparently. Other design principles were an object oriented + interface, simplicity and extensibility. + +Package: liboping-dev +Section: libdevel +Architecture: any +Depends: liboping (= ${Source-Version}) +Description: Development files for the liboping library + liboping was inspired by ping, libping and fping: It differs from these + existing solutions in that it can `ping' multiple hosts in parallel using IPv4 + or IPv6 transparently. Other design principles were an object oriented + interface, simplicity and extensibility. + +Package: oping +Section: net +Architecture: any +Depends: liboping (= ${Source-Version}), ${shlibs:Depends} +Description: Sends ICMPv4/ICMPv6 echo packets to multiple remote hosts + liboping was inspired by ping, libping and fping: It differs from these + existing solutions in that it can `ping' multiple hosts in parallel using IPv4 + or IPv6 transparently. Other design principles were an object oriented + interface, simplicity and extensibility. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..1cf8421 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,25 @@ +This is liboping, written and maintained by Florian Forster +on Sun, 30 Apr 2006 18:46:29 +0200. + +The original source can always be found at: + http://verplant.org/ + +Copyright (C) 2006 Florian Forster + +License: + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, as published + by the Free Software Foundation. + + 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 more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. diff --git a/debian/liboping-dev.dirs b/debian/liboping-dev.dirs new file mode 100644 index 0000000..c7147dc --- /dev/null +++ b/debian/liboping-dev.dirs @@ -0,0 +1,3 @@ +usr/lib +usr/include +usr/share/man diff --git a/debian/liboping-dev.docs b/debian/liboping-dev.docs new file mode 100644 index 0000000..dc63960 --- /dev/null +++ b/debian/liboping-dev.docs @@ -0,0 +1,6 @@ +AUTHORS +COPYING +ChangeLog +INSTALL +NEWS +README diff --git a/debian/liboping-dev.manpages b/debian/liboping-dev.manpages new file mode 100644 index 0000000..c1e9e83 --- /dev/null +++ b/debian/liboping-dev.manpages @@ -0,0 +1,8 @@ +src/mans/liboping.3 +src/mans/ping_construct.3 +src/mans/ping_get_error.3 +src/mans/ping_host_add.3 +src/mans/ping_iterator_get.3 +src/mans/ping_iterator_get_context.3 +src/mans/ping_iterator_get_info.3 +src/mans/ping_send.3 diff --git a/debian/liboping.dirs b/debian/liboping.dirs new file mode 100644 index 0000000..6845771 --- /dev/null +++ b/debian/liboping.dirs @@ -0,0 +1 @@ +usr/lib diff --git a/debian/liboping.docs b/debian/liboping.docs new file mode 100644 index 0000000..dc63960 --- /dev/null +++ b/debian/liboping.docs @@ -0,0 +1,6 @@ +AUTHORS +COPYING +ChangeLog +INSTALL +NEWS +README diff --git a/debian/oping.dirs b/debian/oping.dirs new file mode 100644 index 0000000..e772481 --- /dev/null +++ b/debian/oping.dirs @@ -0,0 +1 @@ +usr/bin diff --git a/debian/oping.docs b/debian/oping.docs new file mode 100644 index 0000000..dc63960 --- /dev/null +++ b/debian/oping.docs @@ -0,0 +1,6 @@ +AUTHORS +COPYING +ChangeLog +INSTALL +NEWS +README diff --git a/debian/oping.manpages b/debian/oping.manpages new file mode 100644 index 0000000..06d379e --- /dev/null +++ b/debian/oping.manpages @@ -0,0 +1 @@ +src/mans/oping.8 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..d5280eb --- /dev/null +++ b/debian/rules @@ -0,0 +1,120 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. +# +# Modified to make a template file for a multi-binary package with separated +# build-arch and build-indep targets by Bill Allombert 2001 + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This has to be exported to make some magic below work. +export DH_OPTIONS + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +version=0.1.0 +major=0 + +config.status: configure + dh_testdir + CFLAGS="$(CFLAGS) -Wl,-z,defs" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info + + +#Architecture +build: build-arch + +build-arch: build-arch-stamp + +build-arch-stamp: config.status + $(MAKE) + touch build-arch-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-arch-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean +ifneq "$(wildcard /usr/share/misc/config.sub)" "" + cp -f /usr/share/misc/config.sub config.sub +endif +ifneq "$(wildcard /usr/share/misc/config.guess)" "" + cp -f /usr/share/misc/config.guess config.guess +endif + rm -rf $(CURDIR)/debian/tmp || true + + dh_clean + +install: install-arch + +install-arch: + dh_testdir + dh_testroot + dh_clean -k -s + dh_installdirs --all + + $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp + mv -v $(CURDIR)/debian/tmp/usr/lib/*.so $(CURDIR)/debian/liboping/usr/lib/ + mv -v $(CURDIR)/debian/tmp/usr/lib/*.{a,la} $(CURDIR)/debian/liboping-dev/usr/lib/ + mv -v $(CURDIR)/debian/tmp/usr/include/*.h $(CURDIR)/debian/liboping-dev/usr/include/ + mv -v $(CURDIR)/debian/tmp/usr/bin/* $(CURDIR)/debian/oping/usr/bin/ + + dh_install -s + +# Must not depend on anything. This is to be called by +# binary-arch/binary-indep +# in another 'make' thread. +binary-common: + dh_testdir + dh_testroot + dh_installchangelogs ChangeLog + dh_installdocs + dh_installexamples +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_python + dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture dependant packages using the common target. +binary-arch: build-arch install-arch + $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common + +binary: binary-arch +.PHONY: build clean binary-arch binary install install-arch diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..1b440d4 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,26 @@ +AUTOMAKE_OPTIONS = foreign no-dependencies + +SUBDIRS = mans + +if COMPILER_IS_GCC +AM_CFLAGS = -Wall -Werror +endif + +include_HEADERS = oping.h +lib_LTLIBRARIES = liboping.la + +#liboping_la_CFLAGS = +liboping_la_LDFLAGS = -avoid-version +if BUILD_WITH_LIBSOCKET +liboping_la_LDFLAGS += -lsocket +endif +liboping_la_SOURCES = oping.h liboping.c + +bin_PROGRAMS = oping + +oping_SOURCES = oping.c +oping_LDADD = liboping.la +oping_LDFLAGS = -lm +if BUILD_WITH_LIBRT +oping_LDFLAGS += -lrt +endif diff --git a/src/liboping.c b/src/liboping.c new file mode 100644 index 0000000..9e9d6c2 --- /dev/null +++ b/src/liboping.c @@ -0,0 +1,1172 @@ +/** + * Object oriented C module to send ICMP and ICMPv6 `echo's. + * Copyright (C) 2006 Florian octo Forster + * + * 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 of the License, 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 more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#if STDC_HEADERS +# include +# include +# include +# include +# include +#else +# error "You don't have the standard C99 header files installed" +#endif /* STDC_HEADERS */ + +#if HAVE_UNISTD_H +# include +#endif + +#ifndef __USE_BSD +# define __USE_BSD +#endif + +#if HAVE_FCNTL_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_SYS_SOCKET_H +# include +#endif +#if HAVE_NETDB_H +# include +#endif + +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#if HAVE_NETINET_IP_H +# include +#endif +#if HAVE_NETINET_IP_ICMP_H +# include +#endif +#ifdef HAVE_NETINET_IP_VAR_H +# include +#endif +#if HAVE_NETINET_IP6_H +# include +#endif +#if HAVE_NETINET_ICMP6_H +# include +#endif + +#include "oping.h" + +#if DEBUG +# define dprintf(...) printf ("%s[%4i]: %-20s: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__) +#else +# define dprintf(...) /**/ +#endif + +#define PING_ERRMSG_LEN 256 + +#define PING_DATA "Florian Forster http://verplant.org/" + +struct pinghost +{ + char *hostname; + struct sockaddr_storage *addr; + socklen_t addrlen; + int addrfamily; + int fd; + int ident; + int sequence; + struct timeval *timer; + double latency; + + void *context; + + struct pinghost *next; +}; + +struct pingobj +{ + double timeout; + int ttl; + int addrfamily; + + char errmsg[PING_ERRMSG_LEN]; + + pinghost_t *head; +}; + +/* + * private (static) functions + */ +static void ping_set_error (pingobj_t *obj, const char *function, + const char *message) +{ + snprintf (obj->errmsg, PING_ERRMSG_LEN, "%s: %s", function, message); + obj->errmsg[PING_ERRMSG_LEN - 1] = '\0'; +} + +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 (pinghost_t *ph, 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 < sizeof (struct icmp)) + return (NULL); + + icmp_hdr = (struct icmp *) buffer; + buffer += sizeof (struct icmp); + buffer_len -= sizeof (struct icmp); + + if (icmp_hdr->icmp_type != ICMP_ECHOREPLY) + { + dprintf ("Unexpected ICMP type: %i\n", icmp_hdr->icmp_type); + return (NULL); + } + + recv_checksum = icmp_hdr->icmp_cksum; + icmp_hdr->icmp_cksum = 0; + calc_checksum = ping_icmp4_checksum ((char *) icmp_hdr, + sizeof (struct icmp) + buffer_len); + + if (recv_checksum != calc_checksum) + { + dprintf ("Checksum missmatch: Got 0x%04x, calculated 0x%04x\n", + recv_checksum, calc_checksum); + return (NULL); + } + + ident = ntohs (icmp_hdr->icmp_id); + seq = ntohs (icmp_hdr->icmp_seq); + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n", + ptr->hostname, ptr->ident, ptr->sequence - 1); + + if (ptr->addrfamily != AF_INET) + continue; + + if (!timerisset (ptr->timer)) + continue; + + if (ptr->ident != ident) + continue; + + if ((ptr->sequence - 1) != seq) + continue; + + dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n", + ptr->hostname, ident, seq); + + break; + } + + if (ptr == NULL) + { + dprintf ("No match found for ident = 0x%04x, seq = %i\n", + ident, seq); + } + + return (ptr); +} + +static pinghost_t *ping_receive_ipv6 (pinghost_t *ph, char *buffer, size_t buffer_len) +{ + struct icmp6_hdr *icmp_hdr; + + uint16_t ident; + uint16_t seq; + + pinghost_t *ptr; + + if (buffer_len < sizeof (struct icmp6_hdr)) + return (NULL); + + icmp_hdr = (struct icmp6_hdr *) buffer; + buffer += sizeof (struct icmp); + buffer_len -= sizeof (struct icmp); + + if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY) + { + dprintf ("Unexpected ICMP type: %02x\n", icmp_hdr->icmp6_type); + return (NULL); + } + + if (icmp_hdr->icmp6_code != 0) + { + dprintf ("Unexpected ICMP code: %02x\n", icmp_hdr->icmp6_code); + return (NULL); + } + + ident = ntohs (icmp_hdr->icmp6_id); + seq = ntohs (icmp_hdr->icmp6_seq); + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + dprintf ("hostname = %s, ident = 0x%04x, seq = %i\n", + ptr->hostname, ptr->ident, ptr->sequence - 1); + + if (ptr->addrfamily != AF_INET6) + continue; + + if (!timerisset (ptr->timer)) + continue; + + if (ptr->ident != ident) + continue; + + if ((ptr->sequence - 1) != seq) + continue; + + dprintf ("Match found: hostname = %s, ident = 0x%04x, seq = %i\n", + ptr->hostname, ident, seq); + + break; + } + + if (ptr == NULL) + { + dprintf ("No match found for ident = 0x%04x, seq = %i\n", + ident, seq); + } + + return (ptr); +} + +static int ping_receive_one (int fd, pinghost_t *ph, struct timeval *now) +{ + char buffer[4096]; + size_t buffer_len; + + struct timeval diff; + + pinghost_t *host = NULL; + + struct sockaddr_storage sa; + socklen_t sa_len; + + sa_len = sizeof (sa); + + buffer_len = recvfrom (fd, buffer, sizeof (buffer), 0, + (struct sockaddr *) &sa, &sa_len); + if (buffer_len == -1) + { + dprintf ("recvfrom: %s\n", strerror (errno)); + return (-1); + } + + dprintf ("Read %i bytes from fd = %i\n", buffer_len, fd); + + if (sa.ss_family == AF_INET) + { + if ((host = ping_receive_ipv4 (ph, buffer, buffer_len)) == NULL) + return (-1); + } + else if (sa.ss_family == AF_INET6) + { + if ((host = ping_receive_ipv6 (ph, buffer, buffer_len)) == NULL) + return (-1); + } + + dprintf ("rcvd: %12i.%06i\n", + (int) now->tv_sec, + (int) now->tv_usec); + dprintf ("sent: %12i.%06i\n", + (int) host->timer->tv_sec, + (int) host->timer->tv_usec); + + if (ping_timeval_sub (now, host->timer, &diff) < 0) + { + timerclear (host->timer); + return (-1); + } + + dprintf ("diff: %12i.%06i\n", + (int) diff.tv_sec, + (int) diff.tv_usec); + + host->latency = ((double) diff.tv_usec) / 1000.0; + host->latency += ((double) diff.tv_sec) * 1000.0; + + timerclear (host->timer); + + return (0); +} + +static int ping_receive_all (pingobj_t *obj) +{ + fd_set readfds; + int num_readfds; + int max_readfds; + + pinghost_t *ph; + pinghost_t *ptr; + + struct timeval endtime; + struct timeval nowtime; + struct timeval timeout; + int status; + + int ret; + + ph = obj->head; + ret = 0; + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + ptr->latency = -1.0; + + if (gettimeofday (&nowtime, NULL) == -1) + { + ping_set_error (obj, "gettimeofday", strerror (errno)); + return (-1); + } + + /* Set up timeout */ + timeout.tv_sec = (time_t) obj->timeout; + timeout.tv_usec = (suseconds_t) (1000000 * (obj->timeout - ((double) timeout.tv_sec))); + + dprintf ("Set timeout to %i.%06i seconds\n", + (int) timeout.tv_sec, + (int) timeout.tv_usec); + + ping_timeval_add (&nowtime, &timeout, &endtime); + + while (1) + { + FD_ZERO (&readfds); + num_readfds = 0; + max_readfds = -1; + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + if (!timerisset (ptr->timer)) + continue; + + FD_SET (ptr->fd, &readfds); + num_readfds++; + + if (max_readfds < ptr->fd) + max_readfds = ptr->fd; + } + + if (num_readfds == 0) + break; + + if (gettimeofday (&nowtime, NULL) == -1) + { + ping_set_error (obj, "gettimeofday", strerror (errno)); + return (-1); + } + + if (ping_timeval_sub (&endtime, &nowtime, &timeout) == -1) + break; + + dprintf ("Waiting on %i sockets for %i.%06i seconds\n", num_readfds, + (int) timeout.tv_sec, + (int) timeout.tv_usec); + + status = select (max_readfds + 1, &readfds, NULL, NULL, &timeout); + + if (gettimeofday (&nowtime, NULL) == -1) + { + ping_set_error (obj, "gettimeofday", strerror (errno)); + return (-1); + } + + if ((status == -1) && (errno == EINTR)) + { + dprintf ("select was interrupted by signal..\n"); + continue; + } + else if (status < 0) + { + dprintf ("select: %s\n", strerror (errno)); + break; + } + else if (status == 0) + { + dprintf ("select timed out\n"); + break; + } + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + if (FD_ISSET (ptr->fd, &readfds)) + if (ping_receive_one (ptr->fd, ph, &nowtime) == 0) + ret++; + } + } /* while (1) */ + + return (ret); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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) +{ + ssize_t ret; + + if (gettimeofday (ph->timer, NULL) == -1) + { + timerclear (ph->timer); + return (-1); + } + + ret = sendto (ph->fd, buf, buflen, 0, + (struct sockaddr *) ph->addr, ph->addrlen); + + if (ret < 0) + ping_set_error (obj, "sendto", strerror (errno)); + + return (ret); +} + +static int ping_send_one_ipv4 (pingobj_t *obj, pinghost_t *ph) +{ + struct icmp *icmp4; + int status; + + char buf[4096]; + int buflen; + + char *data; + int datalen; + + dprintf ("ph->hostname = %s\n", ph->hostname); + + memset (buf, '\0', sizeof (buf)); + icmp4 = (struct icmp *) buf; + data = (char *) (icmp4 + 1); + + icmp4->icmp_type = ICMP_ECHO; + icmp4->icmp_code = 0; + icmp4->icmp_cksum = 0; + icmp4->icmp_id = htons (ph->ident); + icmp4->icmp_seq = htons (ph->sequence); + + strcpy (data, PING_DATA); + datalen = strlen (data); + + buflen = datalen + sizeof (struct icmp); + + 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); + if (status < 0) + { + perror ("ping_sendto"); + return (-1); + } + + dprintf ("sendto: status = %i\n", status); + + return (0); +} + +static int ping_send_one_ipv6 (pingobj_t *obj, pinghost_t *ph) +{ + struct icmp6_hdr *icmp6; + int status; + + char buf[4096]; + int buflen; + + char *data; + int datalen; + + dprintf ("ph->hostname = %s\n", ph->hostname); + + memset (buf, '\0', sizeof (buf)); + icmp6 = (struct icmp6_hdr *) buf; + data = (char *) (icmp6 + 1); + + icmp6->icmp6_type = ICMP6_ECHO_REQUEST; + icmp6->icmp6_code = 0; + /* The checksum will be calculated by the TCP/IP stack. */ + icmp6->icmp6_cksum = 0; + icmp6->icmp6_id = htons (ph->ident); + icmp6->icmp6_seq = htons (ph->sequence); + + strcpy (data, PING_DATA); + datalen = strlen (data); + + buflen = datalen + sizeof (struct icmp6_hdr); + + dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident); + + status = ping_sendto (obj, ph, buf, buflen); + if (status < 0) + { + perror ("ping_sendto"); + return (-1); + } + + dprintf ("sendto: status = %i\n", status); + + return (0); +} + +static int ping_send_all (pingobj_t *obj) +{ + pinghost_t *ph; + pinghost_t *ptr; + + int ret; + + ret = 0; + ph = obj->head; + + for (ptr = ph; ptr != NULL; ptr = ptr->next) + { + /* start timer.. The GNU `ping6' starts the timer before + * sending the packet, so I will do that too */ + if (gettimeofday (ptr->timer, NULL) == -1) + { + dprintf ("gettimeofday: %s\n", strerror (errno)); + timerclear (ptr->timer); + ret--; + continue; + } + else + { + dprintf ("timer set for hostname = %s\n", ptr->hostname); + } + + if (ptr->addrfamily == AF_INET6) + { + dprintf ("Sending ICMPv6 echo request to `%s'\n", ptr->hostname); + if (ping_send_one_ipv6 (obj, ptr) != 0) + { + timerclear (ptr->timer); + ret--; + continue; + } + } + else if (ptr->addrfamily == AF_INET) + { + dprintf ("Sending ICMPv4 echo request to `%s'\n", ptr->hostname); + if (ping_send_one_ipv4 (obj, ptr) != 0) + { + timerclear (ptr->timer); + ret--; + continue; + } + } + else /* this should not happen */ + { + dprintf ("Unknown address family: %i\n", ptr->addrfamily); + timerclear (ptr->timer); + ret--; + continue; + } + + ptr->sequence++; + } + + return (ret); +} + +/* + * Set the TTL of a socket protocol independently. + */ +static int ping_set_ttl (pinghost_t *ph, int ttl) +{ + int ret = -2; + + if (ph->addrfamily == AF_INET) + { + ret = setsockopt (ph->fd, IPPROTO_IP, IP_TTL, &ttl, sizeof (ttl)); + } + else if (ph->addrfamily == AF_INET6) + { + ret = setsockopt (ph->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof (ttl)); + } + + return (ret); +} + +static int ping_get_ident (void) +{ + int fd; + static int did_seed = 0; + + int retval; + + if (did_seed == 0) + { + if ((fd = open ("/dev/urandom", O_RDONLY)) != -1) + { + unsigned int seed; + + if (read (fd, &seed, sizeof (seed)) != -1) + { + did_seed = 1; + dprintf ("Random seed: %i\n", seed); + srandom (seed); + } + + close (fd); + } + else + { + dprintf ("open (/dev/urandom): %s\n", strerror (errno)); + } + } + + retval = (int) random (); + + dprintf ("Random number: %i\n", retval); + + return (retval); +} + +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->ident = ping_get_ident () & 0xFFFF; + + return (ph); +} + +static void ping_free (pinghost_t *ph) +{ + if (ph->hostname != NULL) + free (ph->hostname); + + free (ph); +} + +/* + * public methods + */ +const char *ping_get_error (pingobj_t *obj) +{ + return (obj->errmsg); +} + +pingobj_t *ping_construct (void) +{ + pingobj_t *obj; + + if ((obj = (pingobj_t *) malloc (sizeof (pingobj_t))) == NULL) + return (NULL); + memset (obj, '\0', sizeof (pingobj_t)); + + obj->timeout = PING_DEF_TIMEOUT; + obj->ttl = PING_DEF_TTL; + obj->addrfamily = PING_DEF_AF; + + return (obj); +} + +void ping_destroy (pingobj_t *obj) +{ + pinghost_t *current; + pinghost_t *next; + + current = obj->head; + next = NULL; + + while (current != NULL) + { + next = current->next; + ping_free (current); + current = next; + } + + free (obj); + + return; +} + +int ping_setopt (pingobj_t *obj, int option, void *value) +{ + int ret = 0; + + switch (option) + { + case PING_OPT_TIMEOUT: + obj->timeout = *((double *) value); + if (obj->timeout < 0.0) + { + obj->timeout = PING_DEF_TIMEOUT; + ret = -1; + } + break; + + case PING_OPT_TTL: + obj->ttl = *((int *) value); + if ((obj->ttl < 1) || (obj->ttl > 255)) + { + obj->ttl = PING_DEF_TTL; + ret = -1; + } + break; + + case PING_OPT_AF: + obj->addrfamily = *((int *) value); + if ((obj->addrfamily != AF_UNSPEC) + && (obj->addrfamily != AF_INET) + && (obj->addrfamily != AF_INET6)) + { + obj->addrfamily = PING_DEF_AF; + ret = -1; + } + break; + + default: + ret = -2; + } /* switch (option) */ + + return (ret); +} /* int ping_setopt */ + + +int ping_send (pingobj_t *obj) +{ + int ret; + + if (ping_send_all (obj) < 0) + return (-1); + + if ((ret = ping_receive_all (obj)) < 0) + return (-2); + + return (ret); +} + +static pinghost_t *ping_host_search (pinghost_t *ph, const char *host) +{ + while (ph != NULL) + { + if (strcasecmp (ph->hostname, host) == 0) + break; + + ph = ph->next; + } + + return (ph); +} + +int ping_host_add (pingobj_t *obj, const char *host) +{ + pinghost_t *ph; + + struct sockaddr_storage sockaddr; + socklen_t sockaddr_len; + + struct addrinfo ai_hints; + struct addrinfo *ai_list, *ai_ptr; + int ai_return; + + dprintf ("host = %s\n", host); + + if (ping_host_search (obj->head, host) != NULL) + return (0); + + memset (&ai_hints, '\0', sizeof (ai_hints)); + ai_hints.ai_flags = 0; +#ifdef AI_ADDRCONFIG + ai_hints.ai_flags |= AI_ADDRCONFIG; +#endif + ai_hints.ai_family = obj->addrfamily; + ai_hints.ai_socktype = SOCK_RAW; + + if ((ph = ping_alloc ()) == NULL) + { + dprintf ("Out of memory!\n"); + return (-1); + } + + if ((ph->hostname = strdup (host)) == NULL) + { + dprintf ("Out of memory!\n"); + ping_set_error (obj, "strdup", strerror (errno)); + ping_free (ph); + return (-1); + } + + if ((ai_return = getaddrinfo (host, NULL, &ai_hints, &ai_list)) != 0) + { + dprintf ("getaddrinfo failed\n"); + ping_set_error (obj, "getaddrinfo", + (ai_return == EAI_SYSTEM) + ? strerror (errno) + : gai_strerror (ai_return)); + ping_free (ph); + return (-1); + } + + if (ai_list == NULL) + ping_set_error (obj, "getaddrinfo", "No hosts returned"); + + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) + { + ph->fd = -1; + + sockaddr_len = sizeof (sockaddr); + memset (&sockaddr, '\0', sockaddr_len); + + if (ai_ptr->ai_family == AF_INET) + { + struct sockaddr_in *si; + + si = (struct sockaddr_in *) &sockaddr; + si->sin_family = AF_INET; + si->sin_port = htons (ph->ident); + si->sin_addr.s_addr = htonl (INADDR_ANY); + + ai_ptr->ai_socktype = SOCK_RAW; + ai_ptr->ai_protocol = IPPROTO_ICMP; + } + else if (ai_ptr->ai_family == AF_INET6) + { + struct sockaddr_in6 *si; + + si = (struct sockaddr_in6 *) &sockaddr; + si->sin6_family = AF_INET6; + si->sin6_port = htons (ph->ident); + si->sin6_addr = in6addr_any; + + ai_ptr->ai_socktype = SOCK_RAW; + ai_ptr->ai_protocol = IPPROTO_ICMPV6; + } + else + { + char errmsg[PING_ERRMSG_LEN]; + + snprintf (errmsg, PING_ERRMSG_LEN, "Unknown `ai_family': %i", ai_ptr->ai_family); + errmsg[PING_ERRMSG_LEN - 1] = '\0'; + + dprintf (errmsg); + ping_set_error (obj, "getaddrinfo", errmsg); + continue; + } + + /* TODO: Move this to a static function `ping_open_socket' and + * call it whenever the socket dies. */ + ph->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); + if (ph->fd == -1) + { + dprintf ("socket: %s\n", strerror (errno)); + ping_set_error (obj, "socket", strerror (errno)); + continue; + } + + if (bind (ph->fd, (struct sockaddr *) &sockaddr, sockaddr_len) == -1) + { + dprintf ("bind: %s\n", strerror (errno)); + ping_set_error (obj, "bind", strerror (errno)); + close (ph->fd); + ph->fd = -1; + continue; + } + + assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen); + memset (ph->addr, '\0', sizeof (struct sockaddr_storage)); + memcpy (ph->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen); + ph->addrlen = ai_ptr->ai_addrlen; + ph->addrfamily = ai_ptr->ai_family; + + break; + } + + freeaddrinfo (ai_list); + + if (ph->fd < 0) + { + free (ph->hostname); + free (ph); + return (-1); + } + + ph->next = obj->head; + obj->head = ph; + + ping_set_ttl (ph, obj->ttl); + + return (0); +} + +int ping_host_remove (pingobj_t *obj, const char *host) +{ + pinghost_t *pre, *cur; + + pre = NULL; + cur = obj->head; + + while (cur != NULL) + { + if (strcasecmp (host, cur->hostname)) + break; + + pre = cur; + cur = cur->next; + } + + if (cur == NULL) + { + ping_set_error (obj, "ping_host_remove", "Host not found"); + return (-1); + } + + if (pre == NULL) + obj->head = cur->next; + else + pre->next = cur->next; + + if (cur->fd >= 0) + close (cur->fd); + + ping_free (cur); + + return (0); +} + +pingobj_iter_t *ping_iterator_get (pingobj_t *obj) +{ + return ((pingobj_iter_t *) obj->head); +} + +pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter) +{ + return ((pingobj_iter_t *) iter->next); +} + +int ping_iterator_get_info (pingobj_iter_t *iter, int info, + void *buffer, size_t *buffer_len) +{ + int ret = EINVAL; + + size_t orig_buffer_len = *buffer_len; + + switch (info) + { + case PING_INFO_HOSTNAME: + ret = ENOMEM; + *buffer_len = strlen (iter->hostname); + if (orig_buffer_len <= *buffer_len) + break; + /* Since (orig_buffer_len > *buffer_len) `strncpy' + * will copy `*buffer_len' and pad the rest of + * `buffer' with null-bytes */ + strncpy (buffer, iter->hostname, orig_buffer_len); + ret = 0; + break; + + case PING_INFO_ADDRESS: + ret = getnameinfo ((struct sockaddr *) iter->addr, + iter->addrlen, + (char *) buffer, + *buffer_len, + NULL, 0, + NI_NUMERICHOST); + if (ret != 0) + { + if ((ret == EAI_MEMORY) +#ifdef EAI_OVERFLOW + || (ret == EAI_OVERFLOW) +#endif + ) + ret = ENOMEM; + else if (ret == EAI_SYSTEM) + /* XXX: Not thread-safe! */ + ret = errno; + else + ret = EINVAL; + } + break; + + case PING_INFO_FAMILY: + ret = ENOMEM; + *buffer_len = sizeof (int); + if (orig_buffer_len < sizeof (int)) + break; + *((int *) buffer) = iter->addrfamily; + ret = 0; + break; + + case PING_INFO_LATENCY: + ret = ENOMEM; + *buffer_len = sizeof (double); + if (orig_buffer_len < sizeof (double)) + break; + *((double *) buffer) = iter->latency; + ret = 0; + break; + + case PING_INFO_SEQUENCE: + ret = ENOMEM; + *buffer_len = sizeof (uint16_t); + if (orig_buffer_len < sizeof (uint16_t)) + break; + *((uint16_t *) buffer) = (uint16_t) iter->sequence; + ret = 0; + break; + + case PING_INFO_IDENT: + ret = ENOMEM; + *buffer_len = sizeof (uint16_t); + if (orig_buffer_len < sizeof (uint16_t)) + break; + *((uint16_t *) buffer) = (uint16_t) iter->ident; + ret = 0; + break; + } + + return (ret); +} + +void *ping_iterator_get_context (pingobj_iter_t *iter) +{ + return (iter->context); +} + +void ping_iterator_set_context (pingobj_iter_t *iter, void *context) +{ + iter->context = context; +} diff --git a/src/mans/Makefile.am b/src/mans/Makefile.am new file mode 100644 index 0000000..9e398a9 --- /dev/null +++ b/src/mans/Makefile.am @@ -0,0 +1,28 @@ +man_PODS = liboping.pod ping_construct.pod ping_setopt.pod ping_host_add.pod \ + ping_send.pod ping_get_error.pod ping_iterator_get.pod \ + ping_iterator_get_info.pod ping_iterator_get_context.pod oping.pod +man_MANS = liboping.3 ping_construct.3 ping_setopt.3 ping_host_add.3 \ + ping_send.3 ping_get_error.3 ping_iterator_get.3 \ + ping_iterator_get_info.3 ping_iterator_get_context.3 oping.8 + +EXTRA_DIST = $(man_MANS) $(man_PODS) + +#liboping_3_SOURCES = liboping.pod +#ping_construct_3_SOURCES = ping_construct.pod +#ping_setopt_3_SOURCES = ping_setopt.pod +#ping_host_add_3_SOURCES = ping_host_add.pod +#ping_send_3_SOURCES = ping_send.pod +#ping_get_error_3_SOURCES = ping_get_error.pod +#ping_iterator_get_3_SOURCES = ping_iterator_get.pod +#ping_iterator_get_info_3_SOURCES = ping_iterator_get_info.pod +#ping_iterator_get_context_3_SOURCES = ping_iterator_get_context.pod +#oping_8_SOURCES = oping.pod + +.pod.1: + pod2man --section=1 --release=$(VERSION) --center=$(PACKAGE) $< >$@ + +.pod.3: + pod2man --section=3 --release=$(VERSION) --center=$(PACKAGE) $< >$@ + +.pod.8: + pod2man --section=8 --release=$(VERSION) --center=$(PACKAGE) $< >$@ diff --git a/src/mans/liboping.pod b/src/mans/liboping.pod new file mode 100644 index 0000000..496ad35 --- /dev/null +++ b/src/mans/liboping.pod @@ -0,0 +1,86 @@ +=head1 NAME + +liboping - Library to send ICMPv4/ICMPv6 echo packets to multiple hosts + +=head1 DESCRIPTION + +This is an overview of liboping, a C library to send ICMP ECHO_REQUEST packets +to remote hosts and measure the time it takes for replies to be received. This +method, often simply called "ping", is a common way to measure network latency +and/or host reachability. + +The goals of this library are to provide the above functionality in a platform +and protocol independent manner. The interface is simple, object oriented and +(hopefully) ANSI-C compliant. + +=head1 GENERAL USAGE + +There are two main types that are used by applications. Both are "opaque +types", meaning they are structures that are B completely defined in the +header file, so you cannot access the structures' members. You don't need to, +don't do it. These structures are object to change without notice. + +=over 4 + +=item C + +A ping-object. You can set specific options for this object, add and remove +hosts to/from it and send ICMP packets to all associated hosts. This is often +called a "handle". + +=item C + +An iterator over the hosts associated with a C object. This iterator +can be used to query more information about a host, for example the hostname, +the measured latency or the current ICMP sequence. + +=back + +Upon startup you usually create one or more C objects and add hosts +to it using the C method (see below). You periodically send +"echo requests" using the C method, iterate over all hosts using +C and C. For each host you call +C to read the current latency and do something with it. + +If an error occurs you can use C so get information on what +failed. + +=head1 LINKING WITH LIBOPING + +Depending on you platform you don't need any extra libraries (e.g. GNU/Linux) +or C (using C<-lsocket>) if the C function is not in the +C-library. The latter is the case for the Solaris operating system. + +=head1 SYMBOL NAMES + +All "official" function or method names are prefixed with "ping_". Don't use +any other functions or methods. Although no such functions should exist. + +=head1 THREAD SAFETY + +liboping has been designed to be as thread safe a possible. However, this has +not been tested and may need some additional work. Use at your own risk and +please report back any problems or success messages. Thank you :) + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L, +L, +L + +=head1 LICENSE + +liboping is licensed under the GPLv2. No other version of the license is +applicable. + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/oping.pod b/src/mans/oping.pod new file mode 100644 index 0000000..e6ef195 --- /dev/null +++ b/src/mans/oping.pod @@ -0,0 +1,58 @@ +=head1 NAME + +oping - send ICMP ECHO_REQUEST to network hosts + +=head1 SYNOPSIS + +B [B<-4> | B<-6>] [B<-c> I] [B<-i> I] I [I [I ...]] + +=head1 DESCRIPTION + +oping uses ICMPv4 or ICMPv6 ECHO_REQUEST packets to measure a hosts +reachability and the network latency. In contrast to the original L +utility B can send ICMP packets to multiple hosts in parallel and wait +for all ECHO_RESPONSE packets to arrive. In contrast to the +B utility (URL is listed in L<"SEE ALSO">) B can use both, IPv4 +and IPv6 transparently and side by side. + +=head1 OPTIONS + +=over 4 + +=item B<-4> + +Force the use of IPv4. + +=item B<-6> + +Force the use of IPv6 + +=item B<-c> I + +Send (and receive) I ICMP packets, then stop and exit. + +=item B<-i> I + +Send one ICMP packet (per host) each I seconds. This can be a +floating-point number to specify sub-second precision. + +=back + +=head1 BUGS + +=over 4 + +=item The TTL cannot be set + +=back + +=head1 SEE ALSO + +L, L, L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_construct.pod b/src/mans/ping_construct.pod new file mode 100644 index 0000000..4fadf70 --- /dev/null +++ b/src/mans/ping_construct.pod @@ -0,0 +1,38 @@ +=head1 NAME + +ping_construct - Constructor for the liboping class + +=head1 SYNOPSIS + + #include + + pingobj_t *ping_construct (void); + void ping_destroy (pingobj_t *obj); + +=head1 DESCRIPTION + +The B constructor allocates the memory neccessary for a +liboping object, initializes that memory and returns a pointer to it. + +The B iterates over all hosts associated with the liboping object +I, closes the sockets, removes the hosts and frees I's memory. + +=head1 RETURN VALUE + +The B constructor returns a pointer to the allocated memory or NULL if no memory could be allocated. + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_get_error.pod b/src/mans/ping_get_error.pod new file mode 100644 index 0000000..4f67511 --- /dev/null +++ b/src/mans/ping_get_error.pod @@ -0,0 +1,30 @@ +=head1 NAME + +ping_get_error - Return the last error message + +=head1 SYNOPSIS + + #include + + const char *ping_get_error (pingobj_t *obj); + +=head1 DESCRIPTION + +The B method returns an error message indicating the last error +encountered. B + +=head1 RETURN VALUE + +A C string representing the last error or an empty string if no error +was encountered yet. + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_host_add.pod b/src/mans/ping_host_add.pod new file mode 100644 index 0000000..c768be3 --- /dev/null +++ b/src/mans/ping_host_add.pod @@ -0,0 +1,49 @@ +=head1 NAME + +ping_host_add - Add a host to a liboping object + +=head1 SYNOPSIS + + #include + + int ping_host_add (pingobj_t *obj, const char *host); + int ping_host_remove (pingobj_t *obj, const char *host); + +=head1 DESCRIPTION + +The B method tries to resolve the I argument, open a +socket and associate everything with the liboping object I. + +The I argument is a pointer to an liboping object, as returned by +L. + +The I parameter is a '\0' terminated string which is interpreted as a +hostname or an IP address. Depending on the address family setting, set with +L, the hostname is resolved to an IPv4 or IPv6 address. + +The B method looks for I within I and remove it if +found. It will close the socket and deallocate the memory, too. + +=head1 RETURN VALUE + +If B succeeds it returns zero. If an error occurs a value less +than zero is returned and the last error is saved internally. You can receive +the error message using L. + +B returns zero upon success and less than zero if it failed. +Currently the only reason for failure is that the host isn't found, but this is +subject to change. Use L to receive the error message. + +=head1 SEE ALSO + +L, +L, +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_iterator_get.pod b/src/mans/ping_iterator_get.pod new file mode 100644 index 0000000..ae73539 --- /dev/null +++ b/src/mans/ping_iterator_get.pod @@ -0,0 +1,45 @@ +=head1 NAME + +ping_iterator_get, ping_iterator_next - Iterate over all hosts of a liboping object + +=head1 SYNOPSIS + + #include + + pingobj_iter_t *ping_iterator_get (pingobj_t *obj); + pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter) + +=head1 DESCRIPTION + +These two functions can be used to iterate over all hosts associated with a +liboping object. You can use these methods as follows: + + pingobj_iter_t *iter; + + for (iter = ping_iterator_get (obj); + iter != NULL; + iter = ping_iterator_next (iter)) + { + ...; + } + +To get usable information from an iterator object (which is also an opaque data +type, just like the liboping object itself) use L and +L. + +=head1 RETURN VALUE + +The B returns an iterator for I or NULL if no host is +associated with I. + +The B returns an iterator for the host following I or +NULL if the last host has been reached. + +=head1 SEE ALSO + +L, +L, +L, +L + +=head1 AUTHOR diff --git a/src/mans/ping_iterator_get_context.pod b/src/mans/ping_iterator_get_context.pod new file mode 100644 index 0000000..2ada86a --- /dev/null +++ b/src/mans/ping_iterator_get_context.pod @@ -0,0 +1,45 @@ +=head1 NAME + +ping_iterator_get_context, ping_iterator_set_context - Store host-dependent data + +=head1 SYNOPSIS + + #include + + void *ping_iterator_get_context (pingobj_iter_t *iter); + void ping_iterator_set_context (pingobj_iter_t *iter, void *context); + +=head1 DESCRIPTION + +B can be used to store host-specific data within the +liboping structures. This data can be received again by calling +B. The data itself is never touched by liboping. If +you call ping_host_remove (see L) or ping_destroy (see +L) and the context is not NULL liboping will assume you know +what you're doing and simply ignore the fact this might be a memory leak. + +The I argument is an iterator object as returned by +L and ping_iterator_next. + +The I argument of B is a pointer to +anything and may be NULL. + +=head1 RETURN VALUE + +B returns the same pointer previously passed to +B or NULL if B has never +been called before. + +=head1 SEE ALSO + +L, +L, +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_iterator_get_info.pod b/src/mans/ping_iterator_get_info.pod new file mode 100644 index 0000000..d84c111 --- /dev/null +++ b/src/mans/ping_iterator_get_info.pod @@ -0,0 +1,96 @@ +=head1 NAME + +ping_iterator_get_info - Constructor for the liboping class + +=head1 SYNOPSIS + + #include + + int ping_iterator_get_info (pingobj_iter_t *iter, + int info, + void *buffer, + size_t *buffer_len); + +=head1 DESCRIPTION + +The B method can be used on an host iterator to return +various information about the current host. + +The I argument is an iterator as returned by L or +L. + +The I argument specifies the type of information returned. Use the +following defines: + +=over 4 + +=item B + +Return the hostname of the host the iterator points to. Since the name is +looked up using the socket address this may differ from the hostname passed to +L. The hostname is actually looked up every time you call +this method, no cache is involved within liboping. + +It is recommended to include C and allocate B bytes of +buffer. + +=item B + +Return the address used in ASCII (i.e. human readable) format. The address is +looked up every time you call this method. 40 bytes should be sufficient for +the buffer (16 octets in hex format, seven colons and one null byte), but more +won't hurt. + +=item B + +Returns the address family of the host. The buffer should be ig enough to hold +an integer. The value is either B or B. + +=item B + +Return the last measured latency or less than zero if the timeout occured +before a echo response was received. The buffer should be big enough to hold a +double value. + +=item B + +Return the last sequence number sent. This number is increased regardless of +echo responses being received or not. The buffer should hold an integer. + +=item B + +Return the ident that is put into every ICMP packet sent to this host. Per +convention this usually is the PID of the sending process, but since liboping +can handle several hosts in parallel it uses a (pseudo-)random number here. The +buffer should be big enough to hold an integer value. + +=back + +The I argument is a pointer to an appropriately sized area of memory +where the result of the call will be stored. The I value is used as +input and output: When calling B it reports the size of +the memory region pointed to by I. The method will write the number of +bytes actually written to the memory into I before returning. + +=head1 RETURN VALUE + +B returns zero if it succeeds. + +B is returned if the value passed as I is unknown. Both, +I and I, will be left untouched in this case. + +If the requested information didn't fit into I then the size that would +have been needed is written into I; I itself is left +untouched. The return value is B in this case. + +=head1 SEE ALSO + +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_send.pod b/src/mans/ping_send.pod new file mode 100644 index 0000000..b149cfe --- /dev/null +++ b/src/mans/ping_send.pod @@ -0,0 +1,42 @@ +=head1 NAME + +ping_send - Send ICMP echo requests to all associated hosts and wait for ICMP echo responses to arrive + +=head1 SYNOPSIS + + #include + + int ping_send (pingobj_t *obj); + +=head1 DESCRIPTION + +The B method is the actual workhorse of this library. It crafts ICMP +packets for the hosts associated with I and sends them via the +corresponding sockets. It then waits for echo responses and receives them, +writing latency information for each host. The method returns after all echo +replies have been read or the timeout (set with L) is reached. + +After this function returns you will most likely iterate over all hosts using +L and ping_iterator_next (described in the same manpage) +and call L on each host. + +=head1 RETURN VALUE + +B returns the value of echo replies received or a value less than +zero if an error occured. Use L to receive an error message. + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/mans/ping_setopt.pod b/src/mans/ping_setopt.pod new file mode 100644 index 0000000..7f64b04 --- /dev/null +++ b/src/mans/ping_setopt.pod @@ -0,0 +1,63 @@ +=head1 NAME + +ping_setopt - Set options for a liboping object + +=head1 SYNOPSIS + + #include + + int ping_setopt (pingobj_t *obj, int opt, void *val); + +=head1 DESCRIPTION + +The B method sets options that effect all hosts associated with +the object I and hosts that are yet to be added to the object. + +The I argument is a pointer to an liboping object, as returned by +L. + +The I argument specifies the option to set. Use one of the following +constants: + +=over 4 + +=item B + +The time to wait for a "echo reply" to be received; in seconds. In this case +the memory pointed to by I is interpreted as a double value and must be +greater than zero. The default is B. + +=item B + +The value written into the time-to-live (= TTL) field of generated ICMP +packets. The memory pointed to by I is interpreted as an integer. Valid +values are 1 through 255. Default is B. + +=item B + +The address family to use. The memory pointed to by I is interpreted as an +integer and must be either B, B, or B. This +option only effects hosts that are being added B this option has been +set. Default is B. + +=back + +The I argument is a pointer to the new value. It must not be NULL. It is +dereferences depending on the value of the I argument, see above. The +memory pointed to by I is not changed. + +=head1 RETURN VALUE + +B returns zero upon success or less than zero upon failure. + +=head1 SEE ALSO + +L, +L + +=head1 AUTHOR + +liboping is written by Florian octo Forster Eocto at verplant.orgE. +It's homepage can be found at L. + +(c) 2005, 2006 by Florian octo Forster. diff --git a/src/oping.c b/src/oping.c new file mode 100644 index 0000000..cc115f2 --- /dev/null +++ b/src/oping.c @@ -0,0 +1,403 @@ +/** + * Object oriented C module to send ICMP and ICMPv6 `echo's. + * Copyright (C) 2006 Florian octo Forster + * + * 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 of the License, 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 more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#if STDC_HEADERS +# include +# include +# include +# include +# include +#else +# error "You don't have the standard C99 header files installed" +#endif /* STDC_HEADERS */ + +#if HAVE_MATH_H +# include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_NETDB_H +# include /* NI_MAXHOST */ +#endif + +#if HAVE_SIGNAL_H +# include +#endif + +#include "oping.h" + +typedef struct ping_context +{ + char host[NI_MAXHOST]; + char addr[NI_MAXHOST]; + + int req_sent; + int req_rcvd; + + double latency_min; + double latency_max; + double latency_total; +} ping_context_t; + +static double opt_interval = 1.0; +static int opt_addrfamily = PING_DEF_AF; +static int opt_count = -1; + +void sigint_handler (int signal) +{ + /* Exit the loop */ + opt_count = 0; +} + +ping_context_t *context_create (void) +{ + ping_context_t *ret; + + if ((ret = malloc (sizeof (ping_context_t))) == NULL) + return (NULL); + + memset (ret, '\0', sizeof (ping_context_t)); + + ret->latency_min = -1.0; + ret->latency_max = -1.0; + ret->latency_total = 0.0; + + return (ret); +} + +void context_destroy (ping_context_t *context) +{ + free (context); +} + +void usage_exit (const char *name) +{ + fprintf (stderr, "Usage: %s [-46] [-c count] [-i interval] host [host [host ...]]\n", + name); + exit (1); +} + +int read_options (int argc, char **argv) +{ + int optchar; + + while (1) + { + optchar = getopt (argc, argv, "46c:i:"); + + if (optchar == -1) + break; + + switch (optchar) + { + case '4': + case '6': + opt_addrfamily = (optchar == '4') ? AF_INET : AF_INET6; + break; + + case 'c': + { + int new_count; + new_count = atoi (optarg); + if (new_count > 0) + opt_count = new_count; + } + break; + + case 'i': + { + double new_interval; + new_interval = atof (optarg); + if (new_interval >= 0.2) + opt_interval = new_interval; + } + break; + + default: + usage_exit (argv[0]); + } + } + + return (optind); +} + +void print_host (pingobj_iter_t *iter) +{ + double latency; + uint16_t sequence; + size_t buffer_len; + ping_context_t *context; + + buffer_len = sizeof (latency); + ping_iterator_get_info (iter, PING_INFO_LATENCY, + &latency, &buffer_len); + + buffer_len = sizeof (sequence); + ping_iterator_get_info (iter, PING_INFO_SEQUENCE, + &sequence, &buffer_len); + + context = (ping_context_t *) ping_iterator_get_context (iter); + + context->req_sent++; + if (latency > 0.0) + { + context->req_rcvd++; + context->latency_total += latency; + + if ((context->latency_max < 0.0) || (context->latency_max < latency)) + context->latency_max = latency; + if ((context->latency_min < 0.0) || (context->latency_min > latency)) + context->latency_min = latency; + + printf ("echo reply from %s (%s): icmp_seq=%u time=%.1f ms\n", + context->host, context->addr, + (unsigned int) sequence, latency); + } + else + { + printf ("echo reply from %s (%s): icmp_seq=%u timeout\n", + context->host, context->addr, + (unsigned int) sequence); + } +} + +void time_normalize (struct timespec *ts) +{ + while (ts->tv_nsec < 0) + { + if (ts->tv_sec == 0) + { + ts->tv_nsec = 0; + return; + } + + ts->tv_sec -= 1; + ts->tv_nsec += 1000000000; + } + + while (ts->tv_nsec >= 1000000000) + { + ts->tv_sec += 1; + ts->tv_nsec -= 1000000000; + } +} + +void time_calc (struct timespec *ts_dest, + const struct timespec *ts_int, + const struct timeval *tv_begin, + const struct timeval *tv_end) +{ + ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec; + ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec; + time_normalize (ts_dest); + + /* Assure that `(begin + interval) > end'. + * This may seem overly complicated, but `tv_sec' is of type `time_t' + * which may be `unsigned. *sigh* */ + if ((tv_end->tv_sec > ts_dest->tv_sec) + || ((tv_end->tv_sec == ts_dest->tv_sec) + && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec))) + { + ts_dest->tv_sec = 0; + ts_dest->tv_nsec = 0; + return; + } + + ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec; + ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000); + time_normalize (ts_dest); +} + +int main (int argc, char **argv) +{ + pingobj_t *ping; + pingobj_iter_t *iter; + + struct sigaction sigint_action; + + struct timeval tv_begin; + struct timeval tv_end; + struct timespec ts_wait; + struct timespec ts_int; + + int optind; + int i; + + if (geteuid () != 0) + { + fprintf (stderr, "Need superuser privileges to open a RAW socket. Sorry.\n"); + return (1); + } + + optind = read_options (argc, argv); + + if (optind >= argc) + usage_exit (argv[0]); + + if ((ping = ping_construct ()) == NULL) + { + fprintf (stderr, "ping_construct failed\n"); + return (1); + } + + { + double temp_sec; + double temp_nsec; + + temp_nsec = modf (opt_interval, &temp_sec); + ts_int.tv_sec = (time_t) temp_sec; + ts_int.tv_nsec = (long) (temp_nsec * 1000000000L); + + /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */ + } + + if (opt_addrfamily != PING_DEF_AF) + ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily); + + for (i = optind; i < argc; i++) + { + if (ping_host_add (ping, argv[i]) > 0) + { + fprintf (stderr, "ping_host_add (%s) failed\n", argv[i]); + continue; + } + } + + for (iter = ping_iterator_get (ping); + iter != NULL; + iter = ping_iterator_next (iter)) + { + ping_context_t *context; + size_t buffer_size; + + context = context_create (); + + buffer_size = sizeof (context->host); + ping_iterator_get_info (iter, PING_INFO_HOSTNAME, context->host, &buffer_size); + + buffer_size = sizeof (context->addr); + ping_iterator_get_info (iter, PING_INFO_ADDRESS, context->addr, &buffer_size); + + ping_iterator_set_context (iter, (void *) context); + } + + memset (&sigint_action, '\0', sizeof (sigint_action)); + sigint_action.sa_handler = sigint_handler; + if (sigaction (SIGINT, &sigint_action, NULL) < 0) + { + perror ("sigaction"); + return (1); + } + + while (opt_count != 0) + { + int status; + + if (gettimeofday (&tv_begin, NULL) < 0) + { + perror ("gettimeofday"); + return (1); + } + + if (ping_send (ping) < 0) + { + fprintf (stderr, "ping_send failed\n"); + return (1); + } + + for (iter = ping_iterator_get (ping); + iter != NULL; + iter = ping_iterator_next (iter)) + { + print_host (iter); + } + fflush (stdout); + + /* Don't sleep in the last iteration */ + if (opt_count == 1) + break; + + if (gettimeofday (&tv_end, NULL) < 0) + { + perror ("gettimeofday"); + return (1); + } + + time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end); + + /* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */ + while ((status = nanosleep (&ts_wait, &ts_wait)) != 0) + { + if (errno != EINTR) + { + perror ("nanosleep"); + break; + } + else if (opt_count == 0) + { + /* sigint */ + break; + } + } + + if (opt_count > 0) + opt_count--; + } /* while (opt_count != 0) */ + + for (iter = ping_iterator_get (ping); + iter != NULL; + iter = ping_iterator_next (iter)) + { + ping_context_t *context; + + context = ping_iterator_get_context (iter); + + printf ("\n--- %s ping statistics ---\n" + "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n" + "rtt min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms\n", + context->host, context->req_sent, context->req_rcvd, + 100.0 * (context->req_sent - context->req_rcvd) / ((double) context->req_sent), + context->latency_total, + context->latency_min, + context->latency_total / ((double) context->req_rcvd), + context->latency_max, + 0.00); + + ping_iterator_set_context (iter, NULL); + free (context); + } + + ping_destroy (ping); + + return (0); +} diff --git a/src/oping.h b/src/oping.h new file mode 100644 index 0000000..5214947 --- /dev/null +++ b/src/oping.h @@ -0,0 +1,86 @@ +/** + * Object oriented C module to send ICMP and ICMPv6 `echo's. + * Copyright (C) 2006 Florian octo Forster + * + * 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 of the License, 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 more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OCTO_PING_H +#define OCTO_PING_H 1 + +#if HAVE_CONFIG_H +# include +#endif + +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif + +/* + * Type definitions + */ +struct pinghost; +typedef struct pinghost pinghost_t; + +typedef pinghost_t pingobj_iter_t; + +struct pingobj; +typedef struct pingobj pingobj_t; + +#define PING_OPT_TIMEOUT 0x01 +#define PING_OPT_TTL 0x02 +#define PING_OPT_AF 0x04 + +#define PING_DEF_TIMEOUT 1.0 +#define PING_DEF_TTL 255 +#define PING_DEF_AF AF_UNSPEC + +/* + * Method definitions + */ +pingobj_t *ping_construct (void); +void ping_destroy (pingobj_t *obj); + +int ping_setopt (pingobj_t *obj, int option, void *value); + +int ping_send (pingobj_t *obj); + +int ping_host_add (pingobj_t *obj, const char *host); +int ping_host_remove (pingobj_t *obj, const char *host); + +pingobj_iter_t *ping_iterator_get (pingobj_t *obj); +pingobj_iter_t *ping_iterator_next (pingobj_iter_t *iter); + +#define PING_INFO_HOSTNAME 1 +#define PING_INFO_ADDRESS 2 +#define PING_INFO_FAMILY 3 +#define PING_INFO_LATENCY 4 +#define PING_INFO_SEQUENCE 5 +#define PING_INFO_IDENT 6 +int ping_iterator_get_info (pingobj_iter_t *iter, int info, + void *buffer, size_t *buffer_len); + +const char *ping_get_error (pingobj_t *obj); + +void *ping_iterator_get_context (pingobj_iter_t *iter); +void ping_iterator_set_context (pingobj_iter_t *iter, void *context); + +#endif /* OCTO_PING_H */ -- 2.30.2