From 737caf932e7eb9c5c36d95df14bd2529b551a384 Mon Sep 17 00:00:00 2001 From: Emil Mikulic Date: Thu, 15 Oct 2015 23:42:45 +1100 Subject: [PATCH] Start upgrade to darkstat-3.0.719. wget https://unix4lyfe.org/darkstat/darkstat-3.0.719.tar.bz2 uupdate darkstat-3.0.719.tar.bz2 git rm -r * mv ../darkstat-3.0.719/* . git add . --- COPYING.GPL | 41 +++++----- ChangeLog | 14 +++- INSTALL | 11 ++- Makefile.in | 14 ++-- acct.c | 47 +++++++++--- cap.c | 27 ++++++- cap.h | 5 +- cdefs.h | 12 --- config.h.in | 14 ++++ configure | 81 +++++++++++++++----- configure.ac | 12 +-- conv.c | 9 ++- darkstat.8.in | 6 +- darkstat.c | 13 +++- db.c | 3 +- debian/changelog | 6 ++ decode.c | 35 ++------- dns.c | 6 +- export-format.txt | 11 ++- graph_db.c | 19 +++-- hosts_db.c | 188 +++++++++++++++++++++++++++++++--------------- hosts_db.h | 15 +++- hosts_sort.c | 55 ++++++-------- http.c | 3 +- now.c | 72 +++++++++--------- now.h | 6 +- 26 files changed, 458 insertions(+), 267 deletions(-) diff --git a/COPYING.GPL b/COPYING.GPL index d60c31a..d159169 100644 --- a/COPYING.GPL +++ b/COPYING.GPL @@ -1,12 +1,12 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to +the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not @@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - - GNU GENERAL PUBLIC LICENSE + + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -225,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN @@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it @@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - Gnomovision version 69, Copyright (C) year name of author + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names: This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General +library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. diff --git a/ChangeLog b/ChangeLog index 1a6e982..c59bd8e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +v3.0.719 (24 May 2015) + - Implement tracking of remote ports: shows which ports the host + is making outgoing connections to. Long time feature request. + - Bugfix: when the capture interface goes down, exit instead of + busy-looping forever. + - Fix "clock error" due to machine reboot. + - SIGUSR1 now resets the time and bytes reported on the graphs + page. + - Account for all IP protocols. + - Change the default ports_max to only twice the default + ports_keep. + v3.0.718 (25 January 2014) - (SECURITY!) Don't chroot() by default. The user must specify a --chroot dir for this to happen now. @@ -193,4 +205,4 @@ v2.6 (Nov 2003) End of the line for darkstat 2. -vim:set noet ts=8 sw=8 tw=72: +vim:set noet ts=8 sts=8 sw=8 tw=72: diff --git a/INSTALL b/INSTALL index fb269e6..6e8f7e3 100644 --- a/INSTALL +++ b/INSTALL @@ -30,11 +30,13 @@ $ make install DESTDIR=/chroot/whatever Portability ----------- -Builds out of the box on: +I, the darkstat maintainer, mostly develop darkstat on Debian GNU/Linux, but +mostly run darkstat on FreeBSD. - - FreeBSD 7.0 +darkstat usually builds out-of-the-box on FreeBSD, although you should probably +install it from ports. -Reported to also work on: +In the past, darkstat has also been reported to work on: - Solaris (with Sun C 5.8, and libpcap installed) - Fedora Core (with libpcap-devel installed) @@ -45,3 +47,6 @@ Reported to also work on: - Ubuntu (you need build-essential, zlib1g-dev, libpcap-dev) - Mandrake - OpenSUSE + +Sadly, darkstat doesn't run on GNU/Hurd 0.3 because the BPF there doesn't +support non-blocking operation (FIONBIO). diff --git a/Makefile.in b/Makefile.in index 6aa57d1..0e16ce5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,3 +1,5 @@ +# vim:set ts=8 sw=8 sts=8 noet: +# # darkstat 3 # copyright (c) 2001-2014 Emil Mikulic. # @@ -21,9 +23,6 @@ sbindir = @sbindir@ datarootdir = @datarootdir@ mandir = @mandir@ -# Optimizations FIXME: dead code. push into autoconf? -#CPPFLAGS += -D__OPTIMIZE__ - SRCS = \ acct.c \ addr.c \ @@ -78,9 +77,6 @@ depend: config.status $(STATICHS) ./config.status rm -f Makefile.in.old -show-dep: - @echo $(CPP) $(CPPFLAGS) -MM $(SRCS) - graphjs.h: static/graph.js $(AM_V_CIFY) $(AM_V_at)./c-ify graph_js $@ @@ -100,7 +96,7 @@ install: darkstat $(INSTALL) -d $(DESTDIR)$(mandir)/man8 $(INSTALL) -m 444 darkstat.8 $(DESTDIR)$(mandir)/man8 -.PHONY: all install clean depend show-dep +.PHONY: all install clean depend # silent-rules AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ @@ -132,9 +128,9 @@ darkstat.o: darkstat.c acct.h cap.h cdefs.h config.h conv.h daylog.h \ graph_db.h db.h dns.h err.h hosts_db.h addr.h http.h localip.h ncache.h \ now.h pidfile.h str.h daylog.o: daylog.c cdefs.h err.h daylog.h graph_db.h str.h now.h -db.o: db.c cdefs.h err.h hosts_db.h addr.h graph_db.h db.h +db.o: db.c err.h cdefs.h hosts_db.h addr.h graph_db.h db.h decode.o: decode.c cdefs.h decode.h addr.h err.h opt.h -dns.o: dns.c cdefs.h conv.h decode.h addr.h dns.h err.h hosts_db.h \ +dns.o: dns.c cdefs.h cap.h conv.h decode.h addr.h dns.h err.h hosts_db.h \ queue.h str.h tree.h bsd.h config.h err.o: err.c cdefs.h err.h opt.h pidfile.h bsd.h config.h graph_db.o: graph_db.c cap.h conv.h db.h acct.h err.h cdefs.h str.h \ diff --git a/acct.c b/acct.c index 3605674..540bf35 100644 --- a/acct.c +++ b/acct.c @@ -150,8 +150,8 @@ static int addr_is_local(const struct addr * const a, /* Account for the given packet summary. */ void acct_for(const struct pktsummary * const sm, const struct local_ips * const local_ips) { - struct bucket *hs = NULL, *hd = NULL; - struct bucket *ps, *pd; + struct bucket *hs = NULL; // Source host. + struct bucket *hd = NULL; // Dest host. int dir_in, dir_out; #if 0 /* WANT_CHATTY? */ @@ -217,12 +217,12 @@ void acct_for(const struct pktsummary * const sm, /* Protocols. */ if (sm->proto != IPPROTO_INVALID) { if (hs) { - ps = host_get_ip_proto(hs, sm->proto); + struct bucket *ps = host_get_ip_proto(hs, sm->proto); ps->out += sm->len; ps->total += sm->len; } if (hd) { - pd = host_get_ip_proto(hd, sm->proto); + struct bucket *pd = host_get_ip_proto(hd, sm->proto); pd->in += sm->len; pd->total += sm->len; } @@ -233,39 +233,64 @@ void acct_for(const struct pktsummary * const sm, /* Ports. */ switch (sm->proto) { case IPPROTO_TCP: + // Local ports on host. if ((sm->src_port <= opt_highest_port) && hs) { - ps = host_get_port_tcp(hs, sm->src_port); + struct bucket *ps = host_get_port_tcp(hs, sm->src_port); ps->out += sm->len; ps->total += sm->len; } if ((sm->dst_port <= opt_highest_port) && hd) { - pd = host_get_port_tcp(hd, sm->dst_port); + struct bucket *pd = host_get_port_tcp(hd, sm->dst_port); pd->in += sm->len; pd->total += sm->len; if (sm->tcp_flags == TH_SYN) pd->u.port_tcp.syn++; } + + // Remote ports. + if ((sm->src_port <= opt_highest_port) && hd) { + struct bucket *pdr = host_get_port_tcp_remote(hd, sm->src_port); + pdr->out += sm->len; + pdr->total += sm->len; + } + if ((sm->dst_port <= opt_highest_port) && hs) { + struct bucket *psr = host_get_port_tcp_remote(hs, sm->dst_port); + psr->in += sm->len; + psr->total += sm->len; + if (sm->tcp_flags == TH_SYN) + psr->u.port_tcp.syn++; + } break; case IPPROTO_UDP: + // Local ports on host. if ((sm->src_port <= opt_highest_port) && hs) { - ps = host_get_port_udp(hs, sm->src_port); + struct bucket *ps = host_get_port_udp(hs, sm->src_port); ps->out += sm->len; ps->total += sm->len; } if ((sm->dst_port <= opt_highest_port) && hd) { - pd = host_get_port_udp(hd, sm->dst_port); + struct bucket *pd = host_get_port_udp(hd, sm->dst_port); pd->in += sm->len; pd->total += sm->len; } + + // Remote ports. + if ((sm->src_port <= opt_highest_port) && hd) { + struct bucket *pdr = host_get_port_udp_remote(hd, sm->src_port); + pdr->out += sm->len; + pdr->total += sm->len; + } + if ((sm->dst_port <= opt_highest_port) && hs) { + struct bucket *psr = host_get_port_udp_remote(hs, sm->dst_port); + psr->in += sm->len; + psr->total += sm->len; + } break; case IPPROTO_INVALID: /* proto decoding failed, don't complain in accounting */ break; - - default: - verbosef("acct_for: unknown IP protocol 0x%02x", sm->proto); } } diff --git a/cap.c b/cap.c index 62da77d..e5f3a3e 100644 --- a/cap.c +++ b/cap.c @@ -112,6 +112,7 @@ static void cap_set_filter(pcap_t *pcap, const char *filter) { free(tmp_filter); } +/* Start capturing on just one interface. Called from cap_start(). */ static void cap_start_one(struct cap_iface *iface, const int promisc) { char errbuf[PCAP_ERRBUF_SIZE], *tmp_device; int linktype, snaplen, waited; @@ -395,8 +396,10 @@ static void callback(u_char *user, acct_for(&sm, &iface->local_ips); } -/* Process any packets currently in the capture buffer. */ -void cap_poll(fd_set *read_set _unused_on_linux_) { +/* Process any packets currently in the capture buffer. + * Returns 0 on error (usually means the interface went down). + */ +int cap_poll(fd_set *read_set _unused_on_linux_) { struct cap_iface *iface; static int told = 0; @@ -431,7 +434,7 @@ void cap_poll(fd_set *read_set _unused_on_linux_) { if (ret < 0) { warnx("pcap_dispatch('%s'): %s", iface->name, pcap_geterr(iface->pcap)); - continue; + return 0; } #if 0 /* debugging */ @@ -449,6 +452,7 @@ void cap_poll(fd_set *read_set _unused_on_linux_) { } } cap_stats_update(); + return 1; } void cap_stop(void) { @@ -464,6 +468,23 @@ void cap_stop(void) { title_interfaces = NULL; } +/* This is only needed by the DNS child. In the main process, the deallocation + * happens in cap_start(). + */ +void cap_free_args(void) { + while (!STAILQ_EMPTY(&cli_ifnames)) { + struct strnode *ifname = STAILQ_FIRST(&cli_ifnames); + STAILQ_REMOVE_HEAD(&cli_ifnames, entries); + free(ifname); + } + + while (!STAILQ_EMPTY(&cli_filters)) { + struct strnode *filter = STAILQ_FIRST(&cli_filters); + STAILQ_REMOVE_HEAD(&cli_filters, entries); + free(filter); + } +} + /* Run through entire capfile. */ void cap_from_file(const char *capfile) { char errbuf[PCAP_ERRBUF_SIZE]; diff --git a/cap.h b/cap.h index f9f455a..74a68de 100644 --- a/cap.h +++ b/cap.h @@ -1,5 +1,5 @@ /* darkstat 3 - * copyright (c) 2001-2012 Emil Mikulic. + * copyright (c) 2001-2014 Emil Mikulic. * * cap.h: interface to libpcap. */ @@ -15,8 +15,9 @@ void cap_add_filter(const char *filter); /* call zero or more times */ void cap_start(const int promisc); void cap_fd_set(fd_set *read_set, int *max_fd, struct timeval *timeout, int *need_timeout); -void cap_poll(fd_set *read_set); +int cap_poll(fd_set *read_set); void cap_stop(void); +void cap_free_args(void); void cap_from_file(const char *capfile); diff --git a/cdefs.h b/cdefs.h index 2bcdf31..05c8172 100644 --- a/cdefs.h +++ b/cdefs.h @@ -17,18 +17,6 @@ # define _printflike_(fmtarg, firstvararg) #endif -#if __GNUC__ == 2 -# define inline __inline__ -#else -# ifdef __TenDRA__ -# define inline __inline -# endif -#endif - -#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901 -# define restrict __restrict -#endif - #ifndef MAX # define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif diff --git a/config.h.in b/config.h.in index f0e3e7f..cc73d8e 100644 --- a/config.h.in +++ b/config.h.in @@ -83,3 +83,17 @@ /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS + +/* Define to the equivalent of the C99 'restrict' keyword, or to + nothing if this is not supported. Do not define if restrict is + supported directly. */ +#undef restrict +/* Work around a bug in Sun C++: it does not support _Restrict or + __restrict__, even though the corresponding Sun C compiler ends up with + "#define restrict _Restrict" or "#define restrict __restrict__" in the + previous line. Perhaps some future version of Sun C++ will work with + restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ +#if defined __SUNPRO_CC && !defined __RESTRICT +# define _Restrict +# define __restrict__ +#endif diff --git a/configure b/configure index dd5e415..dd1e288 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for darkstat 3.0.718. +# Generated by GNU Autoconf 2.69 for darkstat 3.0.719. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,10 +577,10 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='darkstat' PACKAGE_TARNAME='darkstat' -PACKAGE_VERSION='3.0.718' -PACKAGE_STRING='darkstat 3.0.718' +PACKAGE_VERSION='3.0.719' +PACKAGE_STRING='darkstat 3.0.719' PACKAGE_BUGREPORT='' -PACKAGE_URL='http://unix4lyfe.org/darkstat/' +PACKAGE_URL='https://unix4lyfe.org/darkstat/' ac_unique_file="darkstat.c" # Factoring default headers for most tests. @@ -1231,7 +1231,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures darkstat 3.0.718 to adapt to many kinds of systems. +\`configure' configures darkstat 3.0.719 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1292,7 +1292,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of darkstat 3.0.718:";; + short | recursive ) echo "Configuration of darkstat 3.0.719:";; esac cat <<\_ACEOF @@ -1327,7 +1327,7 @@ Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. -darkstat home page: . +darkstat home page: . _ACEOF ac_status=$? fi @@ -1390,7 +1390,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -darkstat configure 3.0.718 +darkstat configure 3.0.719 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1688,7 +1688,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by darkstat $as_me 3.0.718, which was +It was created by darkstat $as_me 3.0.719, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2970,6 +2970,55 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu +# Compiler's language features. + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5 +$as_echo_n "checking for C/C++ restrict keyword... " >&6; } +if ${ac_cv_c_restrict+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_restrict=no + # The order here caters to the fact that C++ does not require restrict. + for ac_kw in __restrict __restrict__ _Restrict restrict; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +typedef int * int_ptr; + int foo (int_ptr $ac_kw ip) { + return ip[0]; + } +int +main () +{ +int s[1]; + int * $ac_kw t = s; + t[0] = 0; + return foo(t) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_restrict=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_restrict" != no && break + done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5 +$as_echo "$ac_cv_c_restrict" >&6; } + + case $ac_cv_c_restrict in + restrict) ;; + no) $as_echo "#define restrict /**/" >>confdefs.h + ;; + *) cat >>confdefs.h <<_ACEOF +#define restrict $ac_cv_c_restrict +_ACEOF + ;; + esac + + # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : @@ -3076,7 +3125,6 @@ rm -f conftest.$ac_objext conftest.$ac_ext # Check for zlib. - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for deflate in -lz" >&5 $as_echo_n "checking for deflate in -lz... " >&6; } if ${ac_cv_lib_z_deflate+:} false; then : @@ -3128,10 +3176,9 @@ $RULE I can't link to zlib. You really can't have a modern operating system without zlib. -If you are using a package-based operating system (like -something with RPMs), see if there is a zlib-devel package -that you can install, to provide the zlib headers and -libraries. +On Debian or a derivative, try: sudo apt-get install zlib1g-dev + +On an RPM system, see if there is a zlib-devel package. $RULE END @@ -4705,7 +4752,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by darkstat $as_me 3.0.718, which was +This file was extended by darkstat $as_me 3.0.719, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -4762,13 +4809,13 @@ Configuration headers: $config_headers Report bugs to the package provider. -darkstat home page: ." +darkstat home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -darkstat config.status 3.0.718 +darkstat config.status 3.0.719 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 2c22002..dd237e4 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # Need at least 2.64 for PACKAGE_URL AC_PREREQ([2.64]) -AC_INIT(darkstat, 3.0.718, , , http://unix4lyfe.org/darkstat/) +AC_INIT(darkstat, 3.0.719, , , https://unix4lyfe.org/darkstat/) AC_CONFIG_SRCDIR([darkstat.c]) AC_CONFIG_HEADER([config.h]) @@ -17,6 +17,9 @@ AC_DEFINE_UNQUOTED(PRIVDROP_USER, "$_pdu", [User to privdrop to.]) AC_PROG_INSTALL AC_PROG_CC +# Compiler's language features. +AC_C_RESTRICT + m4_pattern_allow([^AM_DEFAULT_VERBOSITY$]) AC_ARG_ENABLE([silent-rules], [ --enable-silent-rules less verbose build output (undo: 'make V=1') @@ -106,10 +109,9 @@ $RULE I can't link to zlib. You really can't have a modern operating system without zlib. -If you are using a package-based operating system (like -something with RPMs), see if there is a zlib-devel package -that you can install, to provide the zlib headers and -libraries. +On Debian or a derivative, try: sudo apt-get install zlib1g-dev + +On an RPM system, see if there is a zlib-devel package. $RULE END diff --git a/conv.c b/conv.c index 265f864..e1ff414 100644 --- a/conv.c +++ b/conv.c @@ -24,13 +24,14 @@ #include "err.h" #include #include +#include +#include #include #include #include #include #include #include -#include #define PATH_DEVNULL "/dev/null" @@ -310,6 +311,12 @@ void privdrop(const char *chroot_dir, const char *privdrop_user) { err(1, "chroot(\"%s\") failed", chroot_dir); verbosef("chrooted into: %s", chroot_dir); } + { + gid_t list[1]; + list[0] = pw->pw_gid; + if (setgroups(1, list) == -1) + err(1, "setgroups"); + } if (setgid(pw->pw_gid) == -1) err(1, "setgid"); if (setuid(pw->pw_uid) == -1) diff --git a/darkstat.8.in b/darkstat.8.in index 9b5a0b4..40b957d 100644 --- a/darkstat.8.in +++ b/darkstat.8.in @@ -194,7 +194,7 @@ The default is \fI/\fR (i.e. the root). Use the specified filter expression when capturing traffic. The filter syntax is beyond the scope of this manual page; please refer to the -.BR tcpdump (8) +.BR tcpdump (1) documentation. .\" .TP @@ -402,7 +402,7 @@ darkstat \-i fxp0 \-f "not (src net 192.168.0 and dst net 192.168.0)" .PP .\" (For a full reference on filter syntax, refer to the -.BR tcpdump (8) +.BR tcpdump (1) manpage) .PP .\" @@ -460,7 +460,7 @@ You can also use it to do accounting for a whole subnet by specifying an appropriate netmask. .\" .SH SEE ALSO -.BR tcpdump (8) +.BR tcpdump (1) .\" .SH HISTORY .I darkstat diff --git a/darkstat.c b/darkstat.c index c65631e..47a81ad 100644 --- a/darkstat.c +++ b/darkstat.c @@ -153,7 +153,7 @@ unsigned int opt_hosts_keep = 500; static void cb_hosts_keep(const char *arg) { opt_hosts_keep = parsenum(arg, 0); } -unsigned int opt_ports_max = 200; +unsigned int opt_ports_max = 60; static void cb_ports_max(const char *arg) { opt_ports_max = parsenum(arg, 65536); } @@ -412,7 +412,10 @@ main(int argc, char **argv) daemonize_finish(); while (running) { - int select_ret, max_fd = -1, use_timeout = 0; + int select_ret; + int max_fd = -1; + int use_timeout = 0; + int cap_ret; struct timeval timeout; struct timespec t; fd_set rs, ws; @@ -451,10 +454,14 @@ main(int argc, char **argv) } graph_rotate(); - cap_poll(&rs); + cap_ret = cap_poll(&rs); dns_poll(); http_poll(&rs, &ws); timer_stop(&t, 1000000000, "event processing took longer than a second"); + + if (!cap_ret) { + running = 0; + } } verbosef("shutting down"); diff --git a/db.c b/db.c index cadd26e..201d110 100644 --- a/db.c +++ b/db.c @@ -16,7 +16,6 @@ #include #include -#include "cdefs.h" #include "err.h" #include "hosts_db.h" #include "graph_db.h" @@ -39,7 +38,7 @@ static uint64_t swap64(uint64_t _x) { #endif #define ntoh64 hton64 -static inline uint64_t hton64(const uint64_t ho) { +static uint64_t hton64(const uint64_t ho) { if (ntohs(0x1234) == 0x1234) return ho; else diff --git a/debian/changelog b/debian/changelog index ca4bc75..74e915d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +darkstat (3.0.719-1) UNRELEASED; urgency=medium + + * New upstream release + + -- emil Thu, 15 Oct 2015 23:41:16 +1100 + darkstat (3.0.718-2) unstable; urgency=low * Fix FTBFS on Hurd diff --git a/decode.c b/decode.c index efa6b00..af1b0b0 100644 --- a/decode.c +++ b/decode.c @@ -349,28 +349,16 @@ static int helper_ipv6(HELPER_ARGS) { sm->dst.family = IPv6; memcpy(&sm->dst.ip.v6, &hdr->ip6_dst, sizeof(sm->dst.ip.v6)); - /* Don't do proto accounting if this packet uses extension headers. */ - switch (sm->proto) { - case 0: /* Hop-by-Hop Options */ - case IPPROTO_NONE: - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - case IPPROTO_FRAGMENT: - case IPPROTO_AH: - case IPPROTO_ESP: - case 135: /* Mobility */ - sm->proto = IPPROTO_INVALID; - return 1; /* but we have addresses, so host accounting is ok */ - - default: - helper_ip_deeper(pdata + IPV6_HDR_LEN, len - IPV6_HDR_LEN, sm); - return 1; - } + helper_ip_deeper(pdata + IPV6_HDR_LEN, len - IPV6_HDR_LEN, sm); + return 1; } static void helper_ip_deeper(HELPER_ARGS) { /* At this stage we have IP addresses so we can do host accounting. - * If proto decode fails, we set IPPROTO_INVALID to skip proto accounting. + * + * If proto decode fails, we set IPPROTO_INVALID to skip accounting of port + * numbers. + * * We don't need to "return 0" like other helpers. */ switch (sm->proto) { @@ -399,17 +387,6 @@ static void helper_ip_deeper(HELPER_ARGS) { sm->dst_port = ntohs(uhdr->uh_dport); return; } - - case IPPROTO_ICMP: - case IPPROTO_IGMP: - case IPPROTO_ICMPV6: - case IPPROTO_OSPF: - /* known protocol, don't complain about it */ - sm->proto = IPPROTO_INVALID; /* also don't do accounting */ - break; - - default: - verbosef("ip_deeper: unknown protocol 0x%02x", sm->proto); } } diff --git a/dns.c b/dns.c index 5b6d95b..163aa56 100644 --- a/dns.c +++ b/dns.c @@ -8,6 +8,7 @@ */ #include "cdefs.h" +#include "cap.h" #include "conv.h" #include "decode.h" #include "dns.h" @@ -65,8 +66,9 @@ dns_init(const char *privdrop_user) daemonize_finish(); /* drop our copy of the lifeline! */ if (signal(SIGUSR1, SIG_IGN) == SIG_ERR) errx(1, "signal(SIGUSR1, ignore) failed"); + cap_free_args(); dns_main(); - exit(0); + errx(1, "DNS child fell out of dns_main()"); } else { /* We are the parent. */ close(dns_sock[CHILD]); @@ -375,7 +377,7 @@ dns_main(void) host, sizeof(host), NULL, 0, flags); break; default: - ret = EAI_FAMILY; + errx(1, "unexpected ip.family = %d", ip.family); } if (ret != 0) { diff --git a/export-format.txt b/export-format.txt index ca89f01..fbc2cd4 100644 --- a/export-format.txt +++ b/export-format.txt @@ -5,14 +5,15 @@ FILE HEADER 0xDA314159 darkstat export format SECTION HEADER 0xDA 'H' 'S' 0x01 hosts_db ver1 HOST COUNT 0x00000001 1 host follows For each host: - HOST HEADER 'H' 'S' 'T' 0x03 host ver3 + HOST HEADER 'H' 'S' 'T' 0x04 host ver4 ADDRESS FAMILY 0x04 Either 4 or 6. IPv4 ADDR 0x0A010101 IPv4 10.1.1.1 - or for 0x06: + or for 0x06: IPv6 ADDR 0x0000 0000 0000 0000 0000 0000 0000 0001 meaning IPv6 ::1 + LASTSEEN 0x0000 0000 4800 0123 64-bit time_t meaning: + 2008-04-12 00:24:03 UTC MACADDR 0x001122334455 00:11:22:33:44:55 - LASTSEEN 0x0000000048000123 (time_t) 2008-04-12 00:24:03 UTC HOSTNAME 0x09 "localhost" 9 is the string length IN 0x0000000000123456 Bytes in: 1193046 OUT 0x0000000000789ABC Bytes out: 7903932 @@ -38,6 +39,8 @@ FILE HEADER 0xDA314159 darkstat export format PORT 0x0045 tftp (port 69) IN 0x0000000000000001 Bytes in: 1 OUT 0x0000000000000002 Bytes out: 2 + REMOTE TCP DATA 't' (as above) + REMOTE UDP DATA 'u' (as above) SECTION HEADER 0xDA 'G' 'R' 0x01 graph_db ver1 LAST_TIME (time_t as 64-bit uint) For each of 4 graphs: (60 seconds, 60 minutes, 24 hours, 31 days) @@ -51,3 +54,5 @@ Host header version 1 is just version 2 without the lastseen time. Host header version 2 is just version 3 without the address family byte (or the possibility of an IPv6 address). + +Host header version 3 is just version 4 without the remote TCP and UDP ports. diff --git a/graph_db.c b/graph_db.c index 4485d45..8884e9d 100644 --- a/graph_db.c +++ b/graph_db.c @@ -55,9 +55,6 @@ void graph_init(void) { graph_db[i]->in = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars); graph_db[i]->out = xmalloc(sizeof(uint64_t) * graph_db[i]->num_bars); } - start_mono = now_mono(); - start_real = now_real(); - last_real = 0; graph_reset(); } @@ -71,7 +68,15 @@ void graph_reset(void) { for (i=0; i\n"); - str_append(buf, "Running for "); + str_append(buf, "Measuring for "); rf = length_of_time(d_mono); str_appendstr(buf, rf); str_free(rf); str_append(buf, ""); - if (abs(d_real - d_mono) > 1) + if (labs((long)(d_real - d_mono)) > 1) str_appendf(buf, " (real time is off by %qd sec)", - (qd)d_real - (qd)d_mono); + (qd)(d_real - d_mono)); if (strftime(start_when, sizeof(start_when), "%Y-%m-%d %H:%M:%S %Z%z", localtime(&start_real)) != 0) str_appendf(buf, ", since %s", start_when); str_appendf(buf,".
\n" - "Total %'qu bytes, " + "Seen %'qu bytes, " "in %'qu packets. " "(%'u captured, " "%'u dropped)
\n" diff --git a/hosts_db.c b/hosts_db.c index 3058d65..c1193a1 100644 --- a/hosts_db.c +++ b/hosts_db.c @@ -91,9 +91,7 @@ static const double phi_1 = 0.61803398874989490252573887119069695472717285156250; /* Co-prime of u, using phi^-1 */ -inline static uint32_t -coprime(const uint32_t u) -{ +static uint32_t coprime(const uint32_t u) { return ( (uint32_t)( (double)(u) * phi_1 ) | 1U ); } @@ -101,9 +99,7 @@ coprime(const uint32_t u) * This is the "recommended" IPv4 hash function, as seen in FreeBSD's * src/sys/netinet/tcp_hostcache.c 1.1 */ -inline static uint32_t -ipv4_hash(const struct addr *const a) -{ +static uint32_t ipv4_hash(const struct addr *const a) { uint32_t ip = a->ip.v4; return ( (ip) ^ ((ip) >> 7) ^ ((ip) >> 17) ); } @@ -125,9 +121,7 @@ ipv4_hash(const struct addr *const a) * This is the IPv6 hash function used by FreeBSD in the same file as above, * svn rev 122922. */ -inline static uint32_t -ipv6_hash(const struct addr *const a) -{ +static uint32_t ipv6_hash(const struct addr *const a) { const struct in6_addr *const ip6 = &(a->ip.v6); return ( ip6->s6_addr32[0] ^ ip6->s6_addr32[1] ^ ip6->s6_addr32[2] ^ ip6->s6_addr32[3] ); @@ -240,7 +234,9 @@ make_func_host(const void *key) h->last_seen_mono = 0; memset(&h->mac_addr, 0, sizeof(h->mac_addr)); h->ports_tcp = NULL; + h->ports_tcp_remote = NULL; h->ports_udp = NULL; + h->ports_udp_remote = NULL; h->ip_protos = NULL; return (b); } @@ -251,7 +247,9 @@ free_func_host(struct bucket *b) struct host *h = &(b->u.host); if (h->dns != NULL) free(h->dns); hashtable_free(h->ports_tcp); + hashtable_free(h->ports_tcp_remote); hashtable_free(h->ports_udp); + hashtable_free(h->ports_udp_remote); hashtable_free(h->ip_protos); } @@ -347,20 +345,21 @@ format_row_host(struct str *buf, const struct bucket *b, (qu)b->total); if (opt_want_lastseen) { - time_t last = b->u.host.last_seen_mono; + int64_t last = b->u.host.last_seen_mono; + int64_t now = (int64_t)now_mono(); struct str *last_str = NULL; - if ((now_mono() >= last) && (last > 0)) - last_str = length_of_time(now_mono() - last); + if ((now >= last) && (last != 0)) + last_str = length_of_time(now - last); str_append(buf, " "); if (last_str == NULL) { if (last == 0) str_append(buf, "(never)"); else - str_appendf(buf, "(clock error: now = %qu, last = %qu)", - (qu)now_mono(), - (qu)last); + str_appendf(buf, "(clock error: last = %qd, now = %qu)", + (qd)last, + (qu)now); } else { str_appendstr(buf, last_str); str_free(last_str); @@ -826,7 +825,6 @@ struct bucket * host_get_port_tcp(struct bucket *host, const uint16_t port) { struct host *h = &host->u.host; - assert(h != NULL); if (h->ports_tcp == NULL) h->ports_tcp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short, free_func_simple, key_func_port_tcp, @@ -835,6 +833,18 @@ host_get_port_tcp(struct bucket *host, const uint16_t port) return (hashtable_find_or_insert(h->ports_tcp, &port, ALLOW_REDUCE)); } +struct bucket * +host_get_port_tcp_remote(struct bucket *host, const uint16_t port) +{ + struct host *h = &host->u.host; + if (h->ports_tcp_remote == NULL) + h->ports_tcp_remote = hashtable_make( + PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short, + free_func_simple, key_func_port_tcp, find_func_port_tcp, + make_func_port_tcp, format_cols_port_tcp, format_row_port_tcp); + return (hashtable_find_or_insert(h->ports_tcp_remote, &port, ALLOW_REDUCE)); +} + /* --------------------------------------------------------------------------- * Find or create a port_udp inside a host. */ @@ -842,7 +852,6 @@ struct bucket * host_get_port_udp(struct bucket *host, const uint16_t port) { struct host *h = &host->u.host; - assert(h != NULL); if (h->ports_udp == NULL) h->ports_udp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short, free_func_simple, key_func_port_udp, @@ -851,6 +860,18 @@ host_get_port_udp(struct bucket *host, const uint16_t port) return (hashtable_find_or_insert(h->ports_udp, &port, ALLOW_REDUCE)); } +struct bucket * +host_get_port_udp_remote(struct bucket *host, const uint16_t port) +{ + struct host *h = &host->u.host; + if (h->ports_udp_remote == NULL) + h->ports_udp_remote = hashtable_make( + PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short, + free_func_simple, key_func_port_udp, find_func_port_udp, + make_func_port_udp, format_cols_port_udp, format_row_port_udp); + return (hashtable_find_or_insert(h->ports_udp_remote, &port, ALLOW_REDUCE)); +} + /* --------------------------------------------------------------------------- * Find or create an ip_proto inside a host. */ @@ -1081,20 +1102,28 @@ static struct str *html_hosts_detail(const char *ip) { "

\n" "Last seen: "); - last_seen_real = mono_to_real(h->u.host.last_seen_mono); - if (strftime(ls_when, sizeof(ls_when), - "%Y-%m-%d %H:%M:%S %Z%z", localtime(&last_seen_real)) != 0) - str_append(buf, ls_when); - - if (h->u.host.last_seen_mono <= now_mono()) { - ls_len = length_of_time(now_mono() - h->u.host.last_seen_mono); - str_append(buf, " ("); - str_appendstr(buf, ls_len); - str_free(ls_len); - str_append(buf, " ago)"); + if (h->u.host.last_seen_mono == 0) { + str_append(buf, "(never)"); } else { - str_append(buf, " (in the future, possible clock problem)"); - } + last_seen_real = mono_to_real(h->u.host.last_seen_mono); + if (strftime(ls_when, sizeof(ls_when), + "%Y-%m-%d %H:%M:%S %Z%z", localtime(&last_seen_real)) != 0) + str_append(buf, ls_when); + + if (h->u.host.last_seen_mono <= now_mono()) { + ls_len = + length_of_time((int64_t)now_mono() - h->u.host.last_seen_mono); + str_append(buf, " ("); + str_appendstr(buf, ls_len); + str_free(ls_len); + str_append(buf, " ago)"); + } else { + str_appendf(buf, " (in the future, possible clock problem, " + "last = %qd, now = %qu)", + (qd)h->u.host.last_seen_mono, + (qu)now_mono()); + } + } str_appendf(buf, "

\n" @@ -1107,15 +1136,22 @@ static struct str *html_hosts_detail(const char *ip) { (qu)h->out, (qu)h->total); - str_append(buf, "

TCP ports

\n"); + str_append(buf, "

TCP ports on this host

\n"); format_table(buf, h->u.host.ports_tcp, 0,TOTAL,0); - str_append(buf, "

UDP ports

\n"); + str_append(buf, "

TCP ports on remote hosts

\n"); + format_table(buf, h->u.host.ports_tcp_remote, 0,TOTAL,0); + + str_append(buf, "

UDP ports on this host

\n"); format_table(buf, h->u.host.ports_udp, 0,TOTAL,0); + str_append(buf, "

UDP ports on remote hosts

\n"); + format_table(buf, h->u.host.ports_udp_remote, 0,TOTAL,0); + str_append(buf, "

IP protocols

\n"); format_table(buf, h->u.host.ip_protos, 0,TOTAL,0); + str_append(buf, "
\n"); html_close(buf); return buf; } @@ -1123,21 +1159,26 @@ static struct str *html_hosts_detail(const char *ip) { /* --------------------------------------------------------------------------- * Database import and export code: * Initially written and contributed by Ben Stewart. - * copyright (c) 2007-2011 Ben Stewart, Emil Mikulic. + * copyright (c) 2007-2014 Ben Stewart, Emil Mikulic. */ static int hosts_db_export_ip(const struct hashtable *h, const int fd); -static int hosts_db_export_tcp(const struct hashtable *h, const int fd); -static int hosts_db_export_udp(const struct hashtable *h, const int fd); +static int hosts_db_export_tcp(const char magic, const struct hashtable *h, + const int fd); +static int hosts_db_export_udp(const char magic, const struct hashtable *h, + const int fd); static const char - export_proto_ip = 'P', - export_proto_tcp = 'T', - export_proto_udp = 'U'; + export_proto_ip = 'P', + export_proto_tcp = 'T', + export_proto_tcp_remote = 't', + export_proto_udp = 'U', + export_proto_udp_remote = 'u'; static const unsigned char export_tag_host_ver1[] = {'H', 'S', 'T', 0x01}, export_tag_host_ver2[] = {'H', 'S', 'T', 0x02}, - export_tag_host_ver3[] = {'H', 'S', 'T', 0x03}; + export_tag_host_ver3[] = {'H', 'S', 'T', 0x03}, + export_tag_host_ver4[] = {'H', 'S', 'T', 0x04}; /* --------------------------------------------------------------------------- * Load a host's ip_proto table from a file. @@ -1171,15 +1212,16 @@ hosts_db_import_ip(const int fd, struct bucket *host) } /* --------------------------------------------------------------------------- - * Load a host's port_tcp table from a file. + * Load a host's port_tcp{,_remote} table from a file. * Returns 0 on failure, 1 on success. */ -static int -hosts_db_import_tcp(const int fd, struct bucket *host) -{ +static int hosts_db_import_tcp(const int fd, const char magic, + struct bucket *host, + struct bucket *(get_port_fn)(struct bucket *host, + uint16_t port)) { uint16_t count, i; - if (!expect8(fd, export_proto_tcp)) return 0; + if (!expect8(fd, magic)) return 0; if (!read16(fd, &count)) return 0; for (i=0; iin = in; b->out = out; b->total = in + out; @@ -1207,12 +1249,13 @@ hosts_db_import_tcp(const int fd, struct bucket *host) * Load a host's port_tcp table from a file. * Returns 0 on failure, 1 on success. */ -static int -hosts_db_import_udp(const int fd, struct bucket *host) -{ +static int hosts_db_import_udp(const int fd, const char magic, + struct bucket *host, + struct bucket *(get_port_fn)(struct bucket *host, + uint16_t port)) { uint16_t count, i; - if (!expect8(fd, export_proto_udp)) return 0; + if (!expect8(fd, magic)) return 0; if (!read16(fd, &count)) return 0; for (i=0; iin = in; b->out = out; b->total = in + out; @@ -1250,7 +1293,9 @@ hosts_db_import_host(const int fd) int ver = 0; if (!readn(fd, hdr, sizeof(hdr))) return 0; - if (memcmp(hdr, export_tag_host_ver3, sizeof(hdr)) == 0) + if (memcmp(hdr, export_tag_host_ver4, sizeof(hdr)) == 0) + ver = 4; + else if (memcmp(hdr, export_tag_host_ver3, sizeof(hdr)) == 0) ver = 3; else if (memcmp(hdr, export_tag_host_ver2, sizeof(hdr)) == 0) ver = 2; @@ -1262,7 +1307,7 @@ hosts_db_import_host(const int fd) return 0; } - if (ver == 3) { + if (ver >= 3) { if (!readaddr(fd, &a)) return 0; } else { @@ -1309,8 +1354,19 @@ hosts_db_import_host(const int fd) /* Host's port and proto subtables: */ if (!hosts_db_import_ip(fd, host)) return 0; - if (!hosts_db_import_tcp(fd, host)) return 0; - if (!hosts_db_import_udp(fd, host)) return 0; + if (!hosts_db_import_tcp(fd, export_proto_tcp, host, host_get_port_tcp)) + return 0; + if (!hosts_db_import_udp(fd, export_proto_udp, host, host_get_port_udp)) + return 0; + + if (ver == 4) { + if (!hosts_db_import_tcp(fd, export_proto_tcp_remote, host, + host_get_port_tcp_remote)) + return 0; + if (!hosts_db_import_udp(fd, export_proto_udp_remote, host, + host_get_port_udp_remote)) + return 0; + } return 1; } @@ -1347,7 +1403,7 @@ int hosts_db_export(const int fd) for (i = 0; isize; i++) for (b = hosts_db->table[i]; b != NULL; b = b->next) { /* For each host: */ - if (!writen(fd, export_tag_host_ver3, sizeof(export_tag_host_ver3))) + if (!writen(fd, export_tag_host_ver4, sizeof(export_tag_host_ver4))) return 0; if (!writeaddr(fd, &(b->u.host.addr))) @@ -1381,8 +1437,16 @@ int hosts_db_export(const int fd) if (!write64(fd, b->out)) return 0; if (!hosts_db_export_ip(b->u.host.ip_protos, fd)) return 0; - if (!hosts_db_export_tcp(b->u.host.ports_tcp, fd)) return 0; - if (!hosts_db_export_udp(b->u.host.ports_udp, fd)) return 0; + if (!hosts_db_export_tcp(export_proto_tcp, b->u.host.ports_tcp, fd)) + return 0; + if (!hosts_db_export_udp(export_proto_udp, b->u.host.ports_udp, fd)) + return 0; + if (!hosts_db_export_tcp(export_proto_tcp_remote, + b->u.host.ports_tcp_remote, fd)) + return 0; + if (!hosts_db_export_udp(export_proto_udp_remote, + b->u.host.ports_udp_remote, fd)) + return 0; } return 1; } @@ -1425,13 +1489,13 @@ hosts_db_export_ip(const struct hashtable *h, const int fd) * Dump the port_tcp table of a host. */ static int -hosts_db_export_tcp(const struct hashtable *h, const int fd) +hosts_db_export_tcp(const char magic, const struct hashtable *h, const int fd) { struct bucket *b; uint32_t i, written = 0; /* TCP DATA */ - if (!write8(fd, export_proto_tcp)) return 0; + if (!write8(fd, magic)) return 0; /* If no data, write a count of 0 and we're done. */ if (h == NULL) { @@ -1458,13 +1522,13 @@ hosts_db_export_tcp(const struct hashtable *h, const int fd) * Dump the port_udp table of a host. */ static int -hosts_db_export_udp(const struct hashtable *h, const int fd) +hosts_db_export_udp(const char magic, const struct hashtable *h, const int fd) { struct bucket *b; uint32_t i, written = 0; /* UDP DATA */ - if (!write8(fd, export_proto_udp)) return 0; + if (!write8(fd, magic)) return 0; /* If no data, write a count of 0 and we're done. */ if (h == NULL) { @@ -1486,4 +1550,4 @@ hosts_db_export_udp(const struct hashtable *h, const int fd) return 1; } -/* vim:set ts=3 sw=3 tw=78 expandtab: */ +/* vim:set ts=3 sw=3 tw=80 expandtab: */ diff --git a/hosts_db.h b/hosts_db.h index 109f373..6abb777 100644 --- a/hosts_db.h +++ b/hosts_db.h @@ -19,8 +19,15 @@ struct host { struct addr addr; char *dns; uint8_t mac_addr[6]; - time_t last_seen_mono; - struct hashtable *ports_tcp, *ports_udp, *ip_protos; + /* last_seen_mono is converted to/from time_t in export/import. + * It can be negative (due to machine reboots). + */ + int64_t last_seen_mono; + struct hashtable *ports_tcp; + struct hashtable *ports_tcp_remote; + struct hashtable *ports_udp; + struct hashtable *ports_udp_remote; + struct hashtable *ip_protos; }; struct port_tcp { @@ -61,7 +68,11 @@ int hosts_db_export(const int fd); struct bucket *host_find(const struct addr *const a); /* can return NULL */ struct bucket *host_get(const struct addr *const a); struct bucket *host_get_port_tcp(struct bucket *host, const uint16_t port); +struct bucket *host_get_port_tcp_remote(struct bucket *host, + const uint16_t port); struct bucket *host_get_port_udp(struct bucket *host, const uint16_t port); +struct bucket *host_get_port_udp_remote(struct bucket *host, + const uint16_t port); struct bucket *host_get_ip_proto(struct bucket *host, const uint8_t proto); /* Web pages. */ diff --git a/hosts_sort.c b/hosts_sort.c index bb88581..110fbfb 100644 --- a/hosts_sort.c +++ b/hosts_sort.c @@ -11,39 +11,34 @@ #include "err.h" #include "hosts_db.h" -/* --------------------------------------------------------------------------- - * comparator for sorting (biggest first) - */ -static int -cmp(const struct bucket * const *x, const struct bucket * const *y, - const enum sort_dir dir) -{ - uint64_t a, b; +static int cmp_u64(const uint64_t a, const uint64_t b) { + if (a < b) return (1); + if (a > b) return (-1); + return (0); +} +static int cmp_i64(const int64_t a, const int64_t b) { + if (a < b) return (1); + if (a > b) return (-1); + return (0); +} + +/* Comparator for sorting 'struct bucket' */ +static int cmp(const struct bucket * const *x, const struct bucket * const *y, + const enum sort_dir dir) { switch (dir) { - case IN: - a = (*x)->in; - b = (*y)->in; - break; - case OUT: - a = (*x)->out; - b = (*y)->out; - break; - case TOTAL: - a = (*x)->total; - b = (*y)->total; - break; - case LASTSEEN: - a = (*x)->u.host.last_seen_mono; - b = (*y)->u.host.last_seen_mono; - break; - default: - errx(1, "cmp: unknown direction: %d", dir); + case IN: + return cmp_u64((*x)->in, (*y)->in); + case OUT: + return cmp_u64((*x)->out, (*y)->out); + case TOTAL: + return cmp_u64((*x)->total, (*y)->total); + case LASTSEEN: + return cmp_i64((*x)->u.host.last_seen_mono, + (*y)->u.host.last_seen_mono); + default: + errx(1, "cmp: unknown direction: %d", dir); } - - if (a < b) return (1); - else if (a > b) return (-1); - else return (0); } /* diff --git a/http.c b/http.c index 0cd6133..bd44ff7 100644 --- a/http.c +++ b/http.c @@ -1181,6 +1181,7 @@ void http_poll(fd_set *recv_set, fd_set *send_set) void http_stop(void) { struct connection *conn; + struct connection *next; unsigned int i; free(http_base_url); @@ -1192,7 +1193,7 @@ void http_stop(void) { insocks = NULL; /* Close in-flight connections. */ - LIST_FOREACH(conn, &connlist, entries) { + LIST_FOREACH_SAFE(conn, &connlist, entries, next) { LIST_REMOVE(conn, entries); free_connection(conn); free(conn); diff --git a/now.c b/now.c index 95ab2a2..b1a43cb 100644 --- a/now.c +++ b/now.c @@ -23,41 +23,41 @@ #include #include -#ifdef __APPLE__ +#if defined(__MACH__) && !defined(__gnu_hurd__) /* Fake up clock_gettime() on OS X. */ -# include -# include -# include -# include - -typedef int clockid_t; -#define CLOCK_REALTIME 0 -#define CLOCK_MONOTONIC 1 - -static uint64_t mono_first = 0; - -int clock_gettime(clockid_t clk_id, struct timespec *tp) { - if (clk_id == CLOCK_REALTIME) { - struct timeval tv; - gettimeofday(&tv, NULL); - tp->tv_sec = tv.tv_sec; - tp->tv_nsec = tv.tv_usec * 1000; - return 0; - } - if (clk_id == CLOCK_MONOTONIC) { - uint64_t t = mach_absolute_time(); - mach_timebase_info_data_t timebase; - mach_timebase_info(&timebase); - if (!mono_first) { - mono_first = t; +# include +# include +# include +# include + + typedef int clockid_t; +# define CLOCK_REALTIME 0 +# define CLOCK_MONOTONIC 1 + + static uint64_t mono_first = 0; + + int clock_gettime(clockid_t clk_id, struct timespec *tp) { + if (clk_id == CLOCK_REALTIME) { + struct timeval tv; + gettimeofday(&tv, NULL); + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * 1000; + return 0; + } + if (clk_id == CLOCK_MONOTONIC) { + uint64_t t = mach_absolute_time(); + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + if (!mono_first) { + mono_first = t; + } + uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom; + tp->tv_sec = tdiff / 1000000000; + tp->tv_nsec = tdiff % 1000000000; + return 0; } - uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom; - tp->tv_sec = tdiff / 1000000000; - tp->tv_nsec = tdiff % 1000000000; - return 0; + return -1; } - return -1; -} #endif /* __MACH__ */ static struct timespec clock_real, clock_mono; @@ -120,14 +120,14 @@ void now_update(void) { all_clocks_update(); } -time_t mono_to_real(const time_t t) { +time_t mono_to_real(const int64_t t) { assert(now_initialized); - return t - clock_mono.tv_sec + clock_real.tv_sec; + return (time_t)(t - (int64_t)clock_mono.tv_sec + (int64_t)clock_real.tv_sec); } -time_t real_to_mono(const time_t t) { +int64_t real_to_mono(const time_t t) { assert(now_initialized); - return t - clock_real.tv_sec + clock_mono.tv_sec; + return (int64_t)(t - clock_real.tv_sec + clock_mono.tv_sec); } void timer_start(struct timespec *t) { diff --git a/now.h b/now.h index 03366e7..12231a4 100644 --- a/now.h +++ b/now.h @@ -23,8 +23,10 @@ void now_update(void); /* once per event loop (in darkstat.c) */ time_t now_real(void); time_t now_mono(void); -time_t mono_to_real(const time_t t); -time_t real_to_mono(const time_t t); +/* Monotonic times can be negative (a time from before the machine booted) so + * treat them as signed. */ +time_t mono_to_real(const int64_t t); +int64_t real_to_mono(const time_t t); /* Emits warnings if a call is too slow. */ struct timespec; -- 2.17.1