├── .gitignore ├── AUTHORS ├── COPYING.GPL ├── ChangeLog ├── INSTALL ├── LICENSE ├── Makefile.in ├── NEWS ├── README.git ├── README.md ├── acct.c ├── acct.h ├── addr.c ├── addr.h ├── addr_test.c ├── bsd.c ├── bsd.h ├── cap.c ├── cap.h ├── cdefs.h ├── configure.ac ├── contrib ├── LisezMoi.MacOS ├── ReadMe.MacOS ├── cx.ath.darkstat ├── darkproxy.php └── darkstat_export ├── conv.c ├── conv.h ├── darkstat.8.in ├── darkstat.c ├── daylog.c ├── daylog.h ├── db.c ├── db.h ├── decode.c ├── decode.h ├── decode_corpus ├── ether_ipv4_tcp └── ether_ipv4_udp ├── decode_fuzzer.c ├── dev_all.c ├── dev_analyze.sh ├── dev_clang_warns.sh ├── dev_gcc_warns.sh ├── dns.c ├── dns.h ├── err.c ├── err.h ├── export-format.txt ├── graph_db.c ├── graph_db.h ├── hosts_db.c ├── hosts_db.h ├── hosts_sort.c ├── html.c ├── html.h ├── http.c ├── http.h ├── install-sh ├── linktypes.c ├── linktypes.h ├── linktypes_list.h ├── linktypes_test.c ├── localip.c ├── localip.h ├── ncache.c ├── ncache.h ├── now.c ├── now.h ├── opt.h ├── pidfile.c ├── pidfile.h ├── presubmit.sh ├── queue.h ├── release.sh ├── static ├── c-ify.c ├── favicon.png ├── graph.js ├── hex-ify.c └── style.css ├── str.c ├── str.h ├── test_headers.sh ├── tidy_linktypes_list.sh └── tree.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | Makefile 4 | a.out 5 | autom4te.cache 6 | darkstat-* 7 | hex-ify 8 | c-ify 9 | config.h 10 | config.h.in 11 | config.log 12 | config.status 13 | configure 14 | configure.lineno 15 | darkstat 16 | darkstat.8 17 | graphjs.h 18 | stylecss.h 19 | favicon.h 20 | addr_test 21 | linktypes_test 22 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | AUTHORS 2 | ------- 3 | 4 | - Emil Mikulic 5 | Primary maintainer. 6 | 7 | (please read the manpage before sending me an e-mail about how your 8 | graphs are all blank) 9 | 10 | Big thanks to everyone who helped out, in no particular order: 11 | 12 | - Ben Stewart 13 | Web interface design for v3, import/export code and file format design. 14 | 15 | - Chris Kuethe 16 | Security, cool patches, OpenBSD port maintainer. 17 | 18 | - Bartosz Kuzma 19 | DLT_PPP and DLT_PPP_SERIAL decoding, pkgsrc maintainer. 20 | 21 | - Claudio Leite - DLT_PPP_ETHER decoding. 22 | 23 | - Can Erkin Acar - BIOCSETWF patch. 24 | 25 | - Ingo Bressler - DLT_LINUX_SLL decoding. 26 | 27 | - Dennis Jansen 28 | Motivation for keeping memory use down, cool patches. 29 | 30 | - Anton S. Ustyuzhanin - DLT_RAW decoding. 31 | 32 | - Cristian Rodriguez - SUSE package maintainer. 33 | 34 | - Rene Mayorga - Debian package maintainer. 35 | 36 | - Cedric Delfosse - Debian package maintainer (retired). 37 | 38 | - Damian Lozinski - initial implementation of average KB/s on graphs. 39 | 40 | - Damien Clauzel - launchd config and Mac OS X instructions. 41 | 42 | - Mats Erik Andersson - for doing the IPv6 heavy lifting. 43 | 44 | Cyro W. Corte Real Filho, Jean-Edouard Babin, Leif Terrens, Moritz Grimm, 45 | Andreas Reimann, Colin Phipps, Cheng-Lung Sung, Martin Wilke, Piotr Kalina, 46 | Carlo Florendo, Malte S. Stretz, Dirk Koopman, and others. 47 | 48 | My apologies if I missed anyone - please mail any corrections to me (Emil) 49 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | v3.0.721 (12 Jan 2022) 2 | - Replace xxd with hex-ify to reduce build dependencies. 3 | 4 | v3.0.720 (10 Jan 2022) 5 | - Improve graphs on mobile (thanks to Dallen Wilson). 6 | - Add favicon, improve CSS (thanks to Daniel Aleksandersen). 7 | - Export host statistics in Prometheus format on /metrics 8 | (thanks to flowblok). 9 | - Move to github. 10 | 11 | v3.0.719 (24 May 2015) 12 | - Implement tracking of remote ports: shows which ports the host 13 | is making outgoing connections to. Long time feature request. 14 | - Bugfix: when the capture interface goes down, exit instead of 15 | busy-looping forever. 16 | - Fix "clock error" due to machine reboot. 17 | - SIGUSR1 now resets the time and bytes reported on the graphs 18 | page. 19 | - Account for all IP protocols. 20 | - Change the default ports_max to only twice the default 21 | ports_keep. 22 | 23 | v3.0.718 (25 January 2014) 24 | - (SECURITY!) Don't chroot() by default. The user must specify 25 | a --chroot dir for this to happen now. 26 | - Bring back the "--base /path" functionality. 27 | - Add explicit warning about graphs being blank if we can't get 28 | local IPs on an interface. 29 | - Don't crash in timer_stop() if monotonic time stops or goes 30 | backwards. 31 | - Lots of internal cleanups. 32 | - Use time_t instead of "long" for time. This is more correct 33 | and should fix darkstat on OpenBSD 5.5 on 32-bit systems. 34 | 35 | v3.0.717 (14 August 2013) 36 | - (OS X only) Work around lack of clock_gettime(). 37 | - Fix crash due to str_appendf() not understanding %ld. 38 | 39 | v3.0.716 (8 August 2013) 40 | - Implement support for multiple capture interfaces. 41 | - Support multiple local IPs on an interface. 42 | - Only error out if we fail to create all HTTP sockets. 43 | In particular, this helps on IPv6-incapable platforms. 44 | - Use monotonic time over wall time where appropriate. 45 | - Portability fixes for NetBSD and OpenBSD. 46 | 47 | v3.0.715 (January 2012) 48 | - Compatibility fixes for Hurd and Solaris. 49 | - Use link-time optimization and automake-like silent rules. 50 | - Support systems without ifaddrs.h again. 51 | - Continuing fixes for IPv6 support. 52 | - Only update lastseen time for sender, not recipient. 53 | - Implement --local-only: accounting for hosts on the local net. 54 | - Make failure to bind() a socket non-fatal. 55 | - Make failure to get local IP non-fatal. 56 | - Fall back to gethostbyaddr() if getnameinfo() fails. 57 | - Fix detection of IPv4 multicast addresses. 58 | - Fix decoding on OpenBSD DLT_NULL interfaces (e.g. gif(4)) 59 | 60 | v3.0.714 (June 2011) 61 | - IPv6 support! Big ups to Mats Erik Andersson who did most 62 | of this work! 63 | - Allow sort on last-seen, thanks to Dirk Koopman. 64 | - Support multiple bind addresses. 65 | - Add --disable-debug configure flag, thanks to Malte S. Stretz. 66 | - Make it possible to export the database without resetting it: 67 | by sending SIGUSR2. 68 | - Web: Use relative URLs, so darkstat works properly 69 | behind mod_proxy, thanks to Malte S. Stretz. 70 | 71 | v3.0.713 (March 2010) 72 | - Don't require --verbose for pcap_stats. 73 | - Survive interface going down on Linux. 74 | - Support DLT_RAW, implemented by Anton S. Ustyuzhanin. 75 | - Skip accounting for hosts or ports if their max 76 | is set to zero. 77 | - Implement --hexdump for troubleshooting. 78 | - Web: Implement --no-lastseen 79 | - Implement --snaplen manual override. 80 | - Fix snaplen problem on recent (1-2 years?) Linux kernels. 81 | - Implement --syslog 82 | - Implement --wait as a NetworkManager workaround. 83 | 84 | (there were no releases made in 2009) 85 | 86 | v3.0.712 (November 2008) 87 | - Web: Add --no-macs option to hide mac addresses. 88 | Thanks Dennis! 89 | - Web: Make tables prettier. 90 | - Host detail view now triggers a DNS lookup. 91 | - Manpage tweaks, also move from section 1 to section 8. 92 | - Track and show how long ago a host was last seen. 93 | Suggested by: Prof A Olowofoyeku (The African Chief) 94 | - Show pcap_stats (like number of packets dropped) in the web 95 | interface and also upon exit. 96 | 97 | v3.0.711 (August 2008) 98 | - Split --debug into --verbose and --no-daemon 99 | - Include launchd config and instructions for running darkstat 100 | on Mac OS X. Contributed by Damien Clauzel. 101 | - Implement PPPoE decoding on ethernet iface. (--pppoe) 102 | - Web: Add automatic reload button. Thanks Dennis! 103 | - Web: Add a graph legend with min/avg/max. 104 | - Web: Remove hashtable stats pages. 105 | 106 | v3.0.708 (May 2008) 107 | 108 | - Implement limiting of number of ports tracked per host, 109 | configurable on the commandline (--ports-max) 110 | - Optionally don't track high ports (--highest-port) 111 | Thanks Dennis! 112 | - Fix rare use-after-free resulting from hosts table reduction. 113 | - Make hosts limit configurable (--hosts-max) 114 | - Option to read from capfile as alternative to live capture 115 | (really only useful for development, benchmarking) 116 | - Add the sniffed interface name to HTML reports. 117 | Thanks Chris! 118 | 119 | v3.0.707 (Sep 2007) 120 | 121 | - Fix silly bug in formatting hex. 122 | - Check for pcap.h in include/pcap/ for old RedHat-a-likes. 123 | - New commandline parser. 124 | - To stay in foreground, pass --debug instead of -d. 125 | - We can now reset all statistics at runtime (send SIGUSR1) 126 | - Make chroot dir configurable on cmdline (--chroot) 127 | - Make privdrop user configurable on cmdline (--user) 128 | - Implement daylog (brings back a v2 feature) 129 | - Import and export hosts and graphs, this brings back a fairly 130 | major v2 feature. Big ups to Ben for doing a lot of the 131 | design and implementation of this feature! 132 | Note that the v3 database format is, by design, incompatible 133 | with the v2 format. 134 | - Report average KB/s in and out on graphs. 135 | Thanks to Damian Lozinski for suggestion and first cut at the 136 | implementation. 137 | - Fix graph rotation when the delay between rotations is big 138 | enough to clear an entire graph. 139 | - Make ip_proto 8 bits wide, to match the IP header. 140 | - Implement pidfile functionality for people who prefer to 141 | handle daemons in this manner. 142 | 143 | v3.0.619 (Apr 2007) 144 | 145 | - Decode DLT_PPP and DLT_PPP_SERIAL on NetBSD, 146 | patch courtesy of Bartosz Kuzma. 147 | - Don't use pcap_setnonblock(), with help from Colin Phipps. 148 | - Reduce the number of syscalls made. 149 | - Answer FAQ about graph axes / labels / scale. 150 | - Fix build on OpenBSD (thanks Chris!) and Solaris. 151 | - Commandline arg (-n) to disable promiscuous mode when 152 | sniffing, thanks to Chris Kuethe for the implementation. 153 | - Commandline arg (-r) to disable DNS resolver. 154 | - Track and report per-host last seen MAC address. 155 | - Move FAQ into manpage. 156 | - Implement display of start time and running time. 157 | - Web: implement sorting the hosts table by in/out/total. 158 | - Web: implement paging through the hosts table. 159 | - Web: implement full view of hosts table. 160 | - Don't die if the capture interface loses its IP address. 161 | - Make daemonize (previously -d) the default, and make -D the 162 | argument to suppress it. 163 | - Commandline arg (-l) to graph traffic entering/leaving the 164 | local network as opposed to just the local IP. v2 had this. 165 | - Allow configure-time override of CHROOT_DIR and PRIVDROP_USER. 166 | - Web: new color scheme. 167 | 168 | v3.0.540 (Aug 2006) 169 | 170 | - Fix build against old libpcap (thanks Claudio) 171 | - Fix build on AIX (thanks Andreas) 172 | - Fix build warnings on NetBSD (thanks Bartosz) 173 | - Deny writes to BPF socket (thanks Can) 174 | - Reverse-resolve IPs less aggressively. 175 | - Free up the DNS queue as we process it. 176 | - Fix dns_reply silliness. 177 | - Web: tweak the look of the top bar. 178 | - Web: update total packets and bytes as part of graph update. 179 | - Decode DLT_LINUX_SLL (ippp0 on Linux), 180 | patch courtesy of Ingo Bressler 181 | 182 | v3.0.524 (Jul 2006) 183 | 184 | - Fix build on NetBSD. 185 | - Fix shutdown on Linux. 186 | - Performance improvements. 187 | - Free the mallocs. 188 | - Work around BPF being immediate on Linux. 189 | This improves performance. 190 | - Drop privileges when we don't need them. Chroot. Generally 191 | be more paranoid. Thanks to Chris Kuethe for patches and 192 | inspiration. 193 | - Daemonize. (run in the background) 194 | - Graphs: Make the entire bar have the same label (instead of 195 | different labels for in/out), add thousands separators for 196 | legibility, include the position/index (i.e. day 22) 197 | - Instead of reducing the hosts_db based on time, do it based on 198 | its size. 199 | - Implement somewhat better handling of time moving backwards - 200 | we assume that real time is monotonic and just renumber the 201 | graph bars. (time is hard) 202 | - Greatly improve IPC with the DNS child, make it more efficient 203 | and much more robust. 204 | - Decode DLT_PPP_ETHER (pppoe0 on OpenBSD), patch courtesy of 205 | Claudio Leite. 206 | 207 | v3.0.471 (Jun 2006) 208 | 209 | First public release of darkstat 3. Almost a complete rewrite 210 | since v2.6. Architecture much improved, better portability and 211 | stability. Approximate feature parity with v2, missing 212 | loading/saving DB. 213 | 214 | v2.6 (Nov 2003) 215 | 216 | End of the line for darkstat 2. 217 | 218 | vim:set noet ts=8 sts=8 sw=8 tw=72: 219 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation instructions 2 | ------------------------- 3 | 4 | $ ./configure 5 | $ make 6 | $ make install 7 | 8 | 9 | Quickstart 10 | ---------- 11 | 12 | $ darkstat -i eth0 13 | 14 | 15 | Slightly slower start 16 | --------------------- 17 | 18 | $ man darkstat 19 | 20 | 21 | Packaging 22 | --------- 23 | 24 | The install target respects DESTDIR. If you are packaging darkstat or 25 | installing into a chroot, you can: 26 | 27 | $ make install DESTDIR=/chroot/whatever 28 | 29 | 30 | Portability 31 | ----------- 32 | 33 | I, the darkstat maintainer, mostly develop darkstat on Debian GNU/Linux, but 34 | mostly run darkstat on FreeBSD. 35 | 36 | darkstat usually builds out-of-the-box on FreeBSD, although you should probably 37 | install it from ports. 38 | 39 | In the past, darkstat has also been reported to work on: 40 | 41 | - Solaris (with Sun C 5.8, and libpcap installed) 42 | - Fedora Core (with libpcap-devel installed) 43 | - OpenBSD 44 | - NetBSD 45 | - Mac OS X 46 | - AIX 47 | - Ubuntu (you need build-essential, zlib1g-dev, libpcap-dev) 48 | - Mandrake 49 | - OpenSUSE 50 | 51 | Sadly, darkstat doesn't run on GNU/Hurd 0.3 because the BPF there doesn't 52 | support non-blocking operation (FIONBIO). 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Parts of the darkstat source code are covered by a BSD license (actually 2 | closer to the ISC license template favored by the OpenBSD project). 3 | These are usually the more generalized, reusable parts of the code. 4 | 5 | Other parts of the darkstat code are covered by the GPL. These other 6 | parts are usually not generic code, but are specific to darkstat and its 7 | purpose. 8 | 9 | All of the source code is clearly annotated to show which license covers 10 | which parts. 11 | 12 | Due to the viral nature of the GPL, once linked, the entire darkstat 13 | binary is infected with the GPL. 14 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | # vim:set ts=8 sw=8 sts=8 noet: 2 | # 3 | # darkstat 3 4 | # copyright (c) 2001-2016 Emil Mikulic. 5 | # 6 | # You may use, modify and redistribute this file under the terms of the 7 | # GNU General Public License version 2. (see COPYING.GPL) 8 | # 9 | # To rebuild Makefile from Makefile.in, run: ./config.status 10 | # For verbose output, run: make V=1 11 | 12 | CC = @CC@ 13 | CFLAGS = @CFLAGS@ 14 | CPPFLAGS = @CPPFLAGS@ 15 | INSTALL = @INSTALL@ 16 | LDFLAGS = @LDFLAGS@ 17 | LIBS = @LIBS@ 18 | 19 | HOSTCC ?= $(CC) 20 | HOSTCFLAGS ?= $(CFLAGS) 21 | 22 | prefix = @prefix@ 23 | exec_prefix = @exec_prefix@ 24 | sbindir = @sbindir@ 25 | datarootdir = @datarootdir@ 26 | mandir = @mandir@ 27 | 28 | SRCS = \ 29 | acct.c \ 30 | addr.c \ 31 | bsd.c \ 32 | cap.c \ 33 | conv.c \ 34 | darkstat.c \ 35 | daylog.c \ 36 | db.c \ 37 | decode.c \ 38 | dns.c \ 39 | err.c \ 40 | graph_db.c \ 41 | hosts_db.c \ 42 | hosts_sort.c \ 43 | html.c \ 44 | http.c \ 45 | linktypes.c \ 46 | localip.c \ 47 | ncache.c \ 48 | now.c \ 49 | pidfile.c \ 50 | str.c 51 | 52 | TEST_SRCS = \ 53 | addr_test.c \ 54 | linktypes_test.c 55 | 56 | OBJS = $(SRCS:%.c=%.o) 57 | TEST_OBJS = $(TEST_SRCS:%.c=%.o) 58 | 59 | STATICHS = \ 60 | favicon.h \ 61 | stylecss.h \ 62 | graphjs.h 63 | 64 | all: darkstat 65 | 66 | darkstat: $(OBJS) 67 | $(AM_V_LINK) 68 | $(AM_V_at)$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) $(LIBS) -o $@ 69 | 70 | .c.o: 71 | $(AM_V_CC) 72 | $(AM_V_at)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ 73 | 74 | clean: 75 | rm -f darkstat 76 | rm -f $(OBJS) 77 | rm -f $(TEST_OBJS) 78 | rm -f $(STATICHS) 79 | rm -f hex-ify c-ify 80 | rm -f addr_test linktypes_test 81 | 82 | depend: config.status $(STATICHS) 83 | cp Makefile.in Makefile.in.old 84 | sed '/^# Automatically generated dependencies$$/,$$d' \ 85 | Makefile.in 86 | echo "# Automatically generated dependencies" >>Makefile.in 87 | $(CC) $(CPPFLAGS) -MM $(SRCS) $(TEST_SRCS) >>Makefile.in 88 | ./config.status 89 | rm -f Makefile.in.old 90 | 91 | favicon.h: static/favicon.png hex-ify 92 | $(AM_V_HEXIFY) 93 | $(AM_V_at)./hex-ify favicon_png $@ 94 | 95 | graphjs.h: static/graph.js c-ify 96 | $(AM_V_CIFY) 97 | $(AM_V_at)./c-ify graph_js $@ 98 | 99 | stylecss.h: static/style.css c-ify 100 | $(AM_V_CIFY) 101 | $(AM_V_at)./c-ify style_css $@ 102 | 103 | hex-ify: static/hex-ify.c 104 | $(AM_V_HOSTCC) 105 | $(AM_V_at)$(HOSTCC) $(HOSTCFLAGS) static/hex-ify.c -o $@ 106 | 107 | c-ify: static/c-ify.c 108 | $(AM_V_HOSTCC) 109 | $(AM_V_at)$(HOSTCC) $(HOSTCFLAGS) static/c-ify.c -o $@ 110 | 111 | install: darkstat 112 | $(INSTALL) -d $(DESTDIR)$(sbindir) 113 | $(INSTALL) -m 555 darkstat $(DESTDIR)$(sbindir) 114 | $(INSTALL) -d $(DESTDIR)$(mandir)/man8 115 | $(INSTALL) -m 444 darkstat.8 $(DESTDIR)$(mandir)/man8 116 | 117 | # Testing. 118 | 119 | addr_test: addr_test.o addr.o 120 | $(AM_V_LINK) 121 | $(AM_V_at)$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LIBS) -o $@ 122 | 123 | linktypes_test: linktypes_test.o linktypes.o 124 | $(AM_V_LINK) 125 | $(AM_V_at)$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LIBS) -o $@ 126 | 127 | check: addr_test linktypes_test 128 | ./addr_test 129 | ./linktypes_test 130 | @echo All tests pass. 131 | 132 | .PHONY: all install clean depend check 133 | 134 | # silent-rules 135 | AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ 136 | AM_V_CC = $(am__v_CC_$(V)) 137 | am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) 138 | am__v_CC_0 = @echo " CC " $@; 139 | AM_V_LINK = $(am__v_LINK_$(V)) 140 | am__v_LINK_ = $(am__v_LINK_$(AM_DEFAULT_VERBOSITY)) 141 | am__v_LINK_0 = @echo " LINK " $@; 142 | AM_V_HOSTCC = $(am__v_HOSTCC_$(V)) 143 | am__v_HOSTCC_ = $(am__v_HOSTCC_$(AM_DEFAULT_VERBOSITY)) 144 | am__v_HOSTCC_0 = @echo " HOSTCC" $@; 145 | AM_V_CIFY = $(am__v_CIFY_$(V)) 146 | am__v_CIFY_ = $(am__v_CIFY_$(AM_DEFAULT_VERBOSITY)) 147 | am__v_CIFY_0 = @echo " C-IFY " $@; 148 | AM_V_HEXIFY = $(am__v_HEXIFY_$(V)) 149 | am__v_HEXIFY_ = $(am__v_HEXIFY_$(AM_DEFAULT_VERBOSITY)) 150 | am__v_HEXIFY_0 = @echo " HEX-IFY " $@; 151 | AM_V_at = $(am__v_at_$(V)) 152 | am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) 153 | am__v_at_0 = @ 154 | 155 | # Automatically generated dependencies 156 | acct.o: acct.c acct.h decode.h addr.h conv.h daylog.h graph_db.h err.h \ 157 | cdefs.h hosts_db.h localip.h now.h opt.h 158 | addr.o: addr.c addr.h 159 | bsd.o: bsd.c bsd.h config.h cdefs.h 160 | cap.o: cap.c acct.h cdefs.h cap.h config.h conv.h decode.h addr.h err.h \ 161 | hosts_db.h linktypes.h localip.h now.h opt.h queue.h str.h 162 | conv.o: conv.c conv.h err.h cdefs.h 163 | darkstat.o: darkstat.c acct.h cap.h cdefs.h config.h conv.h daylog.h \ 164 | graph_db.h db.h dns.h err.h hosts_db.h addr.h http.h localip.h ncache.h \ 165 | now.h pidfile.h str.h 166 | daylog.o: daylog.c cdefs.h err.h daylog.h graph_db.h str.h now.h 167 | db.o: db.c err.h cdefs.h hosts_db.h addr.h graph_db.h db.h 168 | decode.o: decode.c cdefs.h decode.h addr.h err.h opt.h 169 | dns.o: dns.c cdefs.h cap.h conv.h decode.h addr.h dns.h err.h hosts_db.h \ 170 | queue.h str.h tree.h bsd.h config.h 171 | err.o: err.c cdefs.h err.h opt.h pidfile.h bsd.h config.h 172 | graph_db.o: graph_db.c cap.h conv.h db.h acct.h err.h cdefs.h str.h \ 173 | html.h graph_db.h now.h opt.h 174 | hosts_db.o: hosts_db.c cdefs.h conv.h decode.h addr.h dns.h err.h \ 175 | hosts_db.h db.h html.h ncache.h now.h opt.h str.h 176 | hosts_sort.o: hosts_sort.c cdefs.h err.h hosts_db.h addr.h 177 | html.o: html.c config.h str.h cdefs.h html.h opt.h 178 | http.o: http.c cdefs.h config.h conv.h err.h graph_db.h hosts_db.h addr.h \ 179 | http.h now.h queue.h str.h stylecss.h graphjs.h favicon.h 180 | linktypes.o: linktypes.c linktypes_list.h 181 | localip.o: localip.c addr.h bsd.h config.h conv.h err.h cdefs.h localip.h \ 182 | now.h 183 | ncache.o: ncache.c conv.h err.h cdefs.h ncache.h tree.h bsd.h config.h 184 | now.o: now.c err.h cdefs.h now.h str.h 185 | pidfile.o: pidfile.c err.h cdefs.h str.h pidfile.h 186 | str.o: str.c conv.h err.h cdefs.h str.h 187 | addr_test.o: addr_test.c addr.h 188 | linktypes_test.o: linktypes_test.c linktypes.h 189 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Changes to defaults, most recent first: 2 | 3 | - After v3.0.717, the user must specify a --chroot dir for chroot() to happen. 4 | We don't set a default in the configure script anymore. 5 | 6 | - After v3.0.708, --debug was split into --verbose and --no-daemon. 7 | 8 | - Since v3.0.694, darkstat is able to save its internal database into a file and 9 | reload it on startup. The file format is, by design, incompatible with the 10 | format from darkstat v2. 11 | 12 | - After v3.0.626, daemonizing can be suppressed with the "--debug" commandline 13 | argument, to force darkstat to stay in the foreground for debugging purposes. 14 | 15 | The "-d" argument is no longer recognized, and will prevent darkstat 16 | from starting, so make sure you adjust any startup scripts you may have. 17 | 18 | - v3.0.540 and earlier defaulted to running in the foreground and had a "-d" 19 | commandline argument to get darkstat to daemonize, detach from the controlling 20 | terminal, and run in the background. 21 | 22 | After 540, the default has been inverted. darkstat will daemonize by default. 23 | 24 | vim:set noet ts=8 sw=8 tw=80: 25 | -------------------------------------------------------------------------------- /README.git: -------------------------------------------------------------------------------- 1 | These instructions are for developers and other people building darkstat 2 | from git instead of a release tarball. This file shouldn't end up in a 3 | release tarball or a binary package (like *.deb) 4 | 5 | To build the latest version of darkstat from git, do: 6 | 7 | git clone https://github.com/emikulic/darkstat 8 | cd darkstat 9 | autoconf 10 | autoheader 11 | ./configure --enable-warnings 12 | make 13 | 14 | Test the binary without daemonizing it (running it in the background): 15 | 16 | sudo ./darkstat -i eth0 --no-daemon --verbose 17 | 18 | To view the manpage: 19 | 20 | nroff -man darkstat.8 | less 21 | 22 | To build with sanitizers: 23 | 24 | CFLAGS="-g -fsanitize=address -fsanitize=undefined" ./configure 25 | 26 | To see what make is doing: 27 | 28 | make V=1 29 | 30 | Suggested valgrind invocation: 31 | (note that valgrind doesn't work with a -fsanitize=address build) 32 | 33 | sudo valgrind --leak-check=full --show-reachable=yes ./darkstat -i eth0 --no-daemon --verbose --chroot $PWD --export DB --import DB --daylog DAYLOG --user $USER 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # darkstat 2 | 3 | https://unix4lyfe.org/darkstat/ 4 | 5 | darkstat is a network statistics gatherer. 6 | 7 | It sniffs packets on a specified interface, accumulates statistics, and 8 | serves them up over HTTP. 9 | 10 | See the "AUTHORS" file for credits, and who to e-mail when things break. 11 | See the "LICENSE" file for an explanation of the source code licensing. 12 | See the "INSTALL" file for installation instructions. 13 | See the "darkstat.8" manual page for usage instructions. 14 | 15 | If your system doesn't have enough copies of the full text of the GNU 16 | General Public License already, we have provided another one in the 17 | "COPYING.GPL" file. 18 | -------------------------------------------------------------------------------- /acct.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2012 Emil Mikulic. 3 | * 4 | * acct.c: traffic accounting 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "acct.h" 20 | #include "decode.h" 21 | #include "conv.h" 22 | #include "daylog.h" 23 | #include "err.h" 24 | #include "hosts_db.h" 25 | #include "localip.h" 26 | #include "now.h" 27 | #include "opt.h" 28 | 29 | #define __FAVOR_BSD 30 | #include 31 | #include 32 | #include 33 | #include /* for isdigit */ 34 | #include /* for gai_strerror */ 35 | #include /* for free */ 36 | #include /* for memcpy */ 37 | 38 | uint64_t acct_total_packets = 0, acct_total_bytes = 0; 39 | 40 | static int using_localnet4 = 0, using_localnet6 = 0; 41 | static struct addr localnet4, localmask4, localnet6, localmask6; 42 | 43 | /* Parse the net/mask specification into two IPs or die trying. */ 44 | void 45 | acct_init_localnet(const char *spec) 46 | { 47 | char **tokens; 48 | unsigned int num_tokens; 49 | int isnum, j, ret; 50 | int pfxlen, octets, remainder; 51 | struct addr localnet, localmask; 52 | 53 | tokens = split('/', spec, &num_tokens); 54 | if (num_tokens != 2) 55 | errx(1, "expecting network/netmask, got \"%s\"", spec); 56 | 57 | if ((ret = str_to_addr(tokens[0], &localnet)) != 0) 58 | errx(1, "couldn't parse \"%s\": %s", tokens[0], gai_strerror(ret)); 59 | 60 | /* Detect a purely numeric argument. */ 61 | isnum = 0; 62 | { 63 | const char *p = tokens[1]; 64 | while (*p != '\0') { 65 | if (isdigit(*p)) { 66 | isnum = 1; 67 | ++p; 68 | continue; 69 | } else { 70 | isnum = 0; 71 | break; 72 | } 73 | } 74 | } 75 | 76 | if (!isnum) { 77 | if ((ret = str_to_addr(tokens[1], &localmask)) != 0) 78 | errx(1, "couldn't parse \"%s\": %s", tokens[1], gai_strerror(ret)); 79 | if (localmask.family != localnet.family) 80 | errx(1, "family mismatch between net and mask"); 81 | } else { 82 | uint8_t frac, *p; 83 | char *endptr; 84 | 85 | localmask.family = localnet.family; 86 | 87 | /* Compute the prefix length. */ 88 | pfxlen = (unsigned int)strtol(tokens[1], &endptr, 10); 89 | 90 | if ((pfxlen < 0) || 91 | ((localnet.family == IPv6) && (pfxlen > 128)) || 92 | ((localnet.family == IPv4) && (pfxlen > 32)) || 93 | (tokens[1][0] == '\0') || 94 | (*endptr != '\0')) 95 | errx(1, "invalid network prefix length \"%s\"", tokens[1]); 96 | 97 | /* Construct the network mask. */ 98 | octets = pfxlen / 8; 99 | remainder = pfxlen % 8; 100 | p = (localnet.family == IPv6) ? (localmask.ip.v6.s6_addr) 101 | : ((uint8_t *) &(localmask.ip.v4)); 102 | 103 | if (localnet.family == IPv6) 104 | memset(p, 0, 16); 105 | else 106 | memset(p, 0, 4); 107 | 108 | for (j = 0; j < octets; ++j) 109 | p[j] = 0xff; 110 | 111 | frac = (uint8_t)(0xff << (8 - remainder)); 112 | if (frac) 113 | p[j] = frac; /* Have contribution for next position. */ 114 | } 115 | 116 | free(tokens[0]); 117 | free(tokens[1]); 118 | free(tokens); 119 | 120 | /* Register the correct netmask and calculate the correct net. */ 121 | addr_mask(&localnet, &localmask); 122 | if (localnet.family == IPv6) { 123 | using_localnet6 = 1; 124 | localnet6 = localnet; 125 | localmask6 = localmask; 126 | } else { 127 | using_localnet4 = 1; 128 | localnet4 = localnet; 129 | localmask4 = localmask; 130 | } 131 | 132 | verbosef("local network address: %s", addr_to_str(&localnet)); 133 | verbosef(" local network mask: %s", addr_to_str(&localmask)); 134 | } 135 | 136 | static int addr_is_local(const struct addr * const a, 137 | const struct local_ips *local_ips) { 138 | if (is_localip(a, local_ips)) 139 | return 1; 140 | if (a->family == IPv4 && using_localnet4) { 141 | if (addr_inside(a, &localnet4, &localmask4)) 142 | return 1; 143 | } else if (a->family == IPv6 && using_localnet6) { 144 | if (addr_inside(a, &localnet6, &localmask6)) 145 | return 1; 146 | } 147 | return 0; 148 | } 149 | 150 | /* Account for the given packet summary. */ 151 | void acct_for(const struct pktsummary * const sm, 152 | const struct local_ips * const local_ips) { 153 | struct bucket *hs = NULL; // Source host. 154 | struct bucket *hd = NULL; // Dest host. 155 | int dir_in, dir_out; 156 | 157 | #if 0 /* WANT_CHATTY? */ 158 | printf("%15s > ", addr_to_str(&sm->src)); 159 | printf("%15s ", addr_to_str(&sm->dst)); 160 | printf("len %4d proto %2d", sm->len, sm->proto); 161 | 162 | if (sm->proto == IPPROTO_TCP || sm->proto == IPPROTO_UDP) 163 | printf(" port %5d : %5d", sm->src_port, sm->dst_port); 164 | if (sm->proto == IPPROTO_TCP) 165 | printf(" %s%s%s%s%s%s", 166 | (sm->tcp_flags & TH_FIN)?"F":"", 167 | (sm->tcp_flags & TH_SYN)?"S":"", 168 | (sm->tcp_flags & TH_RST)?"R":"", 169 | (sm->tcp_flags & TH_PUSH)?"P":"", 170 | (sm->tcp_flags & TH_ACK)?"A":"", 171 | (sm->tcp_flags & TH_URG)?"U":"" 172 | ); 173 | printf("\n"); 174 | #endif 175 | 176 | /* Totals. */ 177 | acct_total_packets++; 178 | acct_total_bytes += sm->len; 179 | 180 | /* Graphs. */ 181 | dir_out = addr_is_local(&sm->src, local_ips); 182 | dir_in = addr_is_local(&sm->dst, local_ips); 183 | 184 | /* Traffic staying within the network isn't counted. */ 185 | if (dir_out && !dir_in) { 186 | daylog_acct((uint64_t)sm->len, GRAPH_OUT); 187 | graph_acct((uint64_t)sm->len, GRAPH_OUT); 188 | } 189 | if (dir_in && !dir_out) { 190 | daylog_acct((uint64_t)sm->len, GRAPH_IN); 191 | graph_acct((uint64_t)sm->len, GRAPH_IN); 192 | } 193 | 194 | if (opt_hosts_max == 0) return; /* skip per-host accounting */ 195 | 196 | /* Hosts. */ 197 | hosts_db_reduce(); 198 | if (!opt_want_local_only || dir_out) { 199 | hs = host_get(&(sm->src)); 200 | hs->out += sm->len; 201 | hs->total += sm->len; 202 | memcpy(hs->u.host.mac_addr, sm->src_mac, sizeof(sm->src_mac)); 203 | hs->u.host.last_seen_mono = now_mono(); 204 | } 205 | 206 | if (!opt_want_local_only || dir_in) { 207 | hd = host_get(&(sm->dst)); 208 | hd->in += sm->len; 209 | hd->total += sm->len; 210 | memcpy(hd->u.host.mac_addr, sm->dst_mac, sizeof(sm->dst_mac)); 211 | /* 212 | * Don't update recipient's last seen time, we don't know that 213 | * they received successfully. 214 | */ 215 | } 216 | 217 | /* Protocols. */ 218 | if (sm->proto != IPPROTO_INVALID) { 219 | if (hs) { 220 | struct bucket *ps = host_get_ip_proto(hs, sm->proto); 221 | ps->out += sm->len; 222 | ps->total += sm->len; 223 | } 224 | if (hd) { 225 | struct bucket *pd = host_get_ip_proto(hd, sm->proto); 226 | pd->in += sm->len; 227 | pd->total += sm->len; 228 | } 229 | } 230 | 231 | if (opt_ports_max == 0) return; /* skip ports accounting */ 232 | 233 | /* Ports. */ 234 | switch (sm->proto) { 235 | case IPPROTO_TCP: 236 | // Local ports on host. 237 | if ((sm->src_port <= opt_highest_port) && hs) { 238 | struct bucket *ps = host_get_port_tcp(hs, sm->src_port); 239 | ps->out += sm->len; 240 | ps->total += sm->len; 241 | } 242 | if ((sm->dst_port <= opt_highest_port) && hd) { 243 | struct bucket *pd = host_get_port_tcp(hd, sm->dst_port); 244 | pd->in += sm->len; 245 | pd->total += sm->len; 246 | if (sm->tcp_flags == TH_SYN) 247 | pd->u.port_tcp.syn++; 248 | } 249 | 250 | // Remote ports. 251 | if ((sm->src_port <= opt_highest_port) && hd) { 252 | struct bucket *pdr = host_get_port_tcp_remote(hd, sm->src_port); 253 | pdr->out += sm->len; 254 | pdr->total += sm->len; 255 | } 256 | if ((sm->dst_port <= opt_highest_port) && hs) { 257 | struct bucket *psr = host_get_port_tcp_remote(hs, sm->dst_port); 258 | psr->in += sm->len; 259 | psr->total += sm->len; 260 | if (sm->tcp_flags == TH_SYN) 261 | psr->u.port_tcp.syn++; 262 | } 263 | break; 264 | 265 | case IPPROTO_UDP: 266 | // Local ports on host. 267 | if ((sm->src_port <= opt_highest_port) && hs) { 268 | struct bucket *ps = host_get_port_udp(hs, sm->src_port); 269 | ps->out += sm->len; 270 | ps->total += sm->len; 271 | } 272 | if ((sm->dst_port <= opt_highest_port) && hd) { 273 | struct bucket *pd = host_get_port_udp(hd, sm->dst_port); 274 | pd->in += sm->len; 275 | pd->total += sm->len; 276 | } 277 | 278 | // Remote ports. 279 | if ((sm->src_port <= opt_highest_port) && hd) { 280 | struct bucket *pdr = host_get_port_udp_remote(hd, sm->src_port); 281 | pdr->out += sm->len; 282 | pdr->total += sm->len; 283 | } 284 | if ((sm->dst_port <= opt_highest_port) && hs) { 285 | struct bucket *psr = host_get_port_udp_remote(hs, sm->dst_port); 286 | psr->in += sm->len; 287 | psr->total += sm->len; 288 | } 289 | break; 290 | 291 | case IPPROTO_INVALID: 292 | /* proto decoding failed, don't complain in accounting */ 293 | break; 294 | } 295 | } 296 | 297 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 298 | -------------------------------------------------------------------------------- /acct.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2012 Emil Mikulic. 3 | * 4 | * acct.h: traffic accounting 5 | */ 6 | 7 | #include 8 | 9 | struct pktsummary; 10 | struct local_ips; 11 | 12 | extern uint64_t acct_total_packets, acct_total_bytes; 13 | 14 | void acct_init_localnet(const char *spec); 15 | void acct_for(const struct pktsummary * const sm, 16 | const struct local_ips * const local_ips); 17 | 18 | /* vim:set ts=3 sw=3 tw=80 expandtab: */ 19 | -------------------------------------------------------------------------------- /addr.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2011 Emil Mikulic. 3 | * 4 | * addr.c: compound IPv4/IPv6 address 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | 10 | #include "addr.h" 11 | 12 | #include /* for inet_ntop */ 13 | #include 14 | #include /* for memcmp */ 15 | #include /* for getaddrinfo */ 16 | 17 | int addr_equal(const struct addr * const a, const struct addr * const b) 18 | { 19 | if (a->family != b->family) 20 | return 0; 21 | if (a->family == IPv4) 22 | return (a->ip.v4 == b->ip.v4); 23 | else { 24 | assert(a->family == IPv6); 25 | return (memcmp(&(a->ip.v6), &(b->ip.v6), sizeof(a->ip.v6)) == 0); 26 | } 27 | } 28 | 29 | static char _addrstrbuf[INET6_ADDRSTRLEN]; 30 | const char *addr_to_str(const struct addr * const a) 31 | { 32 | if (a->family == IPv4) { 33 | struct in_addr in; 34 | in.s_addr = a->ip.v4; 35 | return (inet_ntoa(in)); 36 | } else { 37 | assert(a->family == IPv6); 38 | inet_ntop(AF_INET6, &(a->ip.v6), _addrstrbuf, sizeof(_addrstrbuf)); 39 | return (_addrstrbuf); 40 | } 41 | } 42 | 43 | int str_to_addr(const char *s, struct addr *a) 44 | { 45 | struct addrinfo hints, *ai; 46 | int ret; 47 | 48 | memset(&hints, 0, sizeof(hints)); 49 | hints.ai_family = AF_UNSPEC; 50 | hints.ai_flags = AI_NUMERICHOST; 51 | 52 | if ((ret = getaddrinfo(s, NULL, &hints, &ai)) != 0) 53 | return (ret); 54 | 55 | if (ai->ai_family == AF_INET) { 56 | a->family = IPv4; 57 | a->ip.v4 = ((const struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr; 58 | } else if (ai->ai_family == AF_INET6) { 59 | a->family = IPv6; 60 | memcpy(&(a->ip.v6), 61 | ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, 62 | sizeof(a->ip.v6)); 63 | } else { 64 | ret = EAI_FAMILY; 65 | } 66 | 67 | freeaddrinfo(ai); 68 | return (ret); 69 | } 70 | 71 | void addr_mask(struct addr *a, const struct addr * const mask) 72 | { 73 | assert(a->family == mask->family); 74 | if (a->family == IPv4) 75 | a->ip.v4 &= mask->ip.v4; 76 | else { 77 | size_t i; 78 | 79 | assert(a->family == IPv6); 80 | for (i=0; iip.v6.s6_addr); i++) 81 | a->ip.v6.s6_addr[i] &= mask->ip.v6.s6_addr[i]; 82 | } 83 | } 84 | 85 | int addr_inside(const struct addr * const a, 86 | const struct addr * const net, const struct addr * const mask) 87 | { 88 | struct addr masked; 89 | 90 | assert(a->family == net->family); 91 | assert(a->family == mask->family); 92 | 93 | masked = *a; 94 | addr_mask(&masked, mask); 95 | return (addr_equal(&masked, net)); 96 | } 97 | 98 | /* vim:set ts=3 sw=3 tw=78 et: */ 99 | -------------------------------------------------------------------------------- /addr.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2011 Emil Mikulic. 3 | * 4 | * addr.h: compound IPv4/IPv6 address 5 | * (because struct sockaddr_storage stores too much) 6 | * 7 | * You may use, modify and redistribute this file under the terms of the 8 | * GNU General Public License version 2. (see COPYING.GPL) 9 | */ 10 | #ifndef __DARKSTAT_ADDR_H 11 | #define __DARKSTAT_ADDR_H 12 | 13 | #include /* for in_addr_t, at least on OpenBSD */ 14 | #include /* for AF_INET6 */ 15 | #include /* for in6_addr */ 16 | 17 | struct addr { 18 | union { 19 | in_addr_t v4; 20 | struct in6_addr v6; 21 | } ip; 22 | enum { IPv4 = 4, IPv6 = 6 } family; 23 | }; 24 | 25 | int addr_equal(const struct addr * const a, const struct addr * const b); 26 | const char *addr_to_str(const struct addr * const a); 27 | void addr_mask(struct addr *a, const struct addr * const mask); 28 | int addr_inside(const struct addr * const a, 29 | const struct addr * const net, const struct addr * const mask); 30 | 31 | /* Returns 0 on success, gai_strerror() code otherwise. */ 32 | int str_to_addr(const char *s, struct addr *a); 33 | 34 | #endif 35 | /* vim:set ts=3 sw=3 tw=78 et: */ 36 | -------------------------------------------------------------------------------- /addr_test.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2011 Emil Mikulic. 3 | * 4 | * Permission to use, copy, modify, and distribute this file for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | * You may use, modify and redistribute this file under the terms of the 16 | * GNU General Public License version 2. (see COPYING.GPL) 17 | */ 18 | 19 | #include "addr.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | static int retcode = 0; 26 | 27 | static void test(const char *in, const char *expect_out, int expect_result) { 28 | struct addr a; 29 | int success, ret; 30 | const char *out; 31 | 32 | success = 1; 33 | ret = str_to_addr(in, &a); 34 | 35 | if (ret != expect_result) { 36 | success = 0; 37 | } 38 | 39 | if (ret == 0) { 40 | out = addr_to_str(&a); 41 | } else { 42 | out = "(error)"; 43 | } 44 | 45 | if (expect_out && (strcmp(out, expect_out) != 0)) { 46 | success = 0; 47 | } 48 | 49 | printf("%s:", success ? "PASS" : "FAIL"); 50 | 51 | printf(" \"%s\" -> \"%s\"", in, out); 52 | if (expect_out && (strcmp(out, expect_out) != 0)) { 53 | printf(" (expected \"%s\")", expect_out); 54 | } 55 | 56 | if (ret != expect_result) { 57 | printf(" [ret %d, expected %d]", ret, expect_result); 58 | } 59 | 60 | if (ret != 0) { 61 | printf(" [err: %s]", gai_strerror(ret)); 62 | } 63 | 64 | printf("\n"); 65 | 66 | if (!success) { 67 | retcode = 1; 68 | } 69 | } 70 | 71 | void test_inside(const char *a, const char *net, const char *mask, int expect) 72 | { 73 | struct addr aa, anet, amask; 74 | 75 | str_to_addr(a, &aa); 76 | str_to_addr(net, &anet); 77 | str_to_addr(mask, &amask); 78 | 79 | printf("%s: %s in %s/%s\n", 80 | addr_inside(&aa, &anet, &amask) ? "PASS" : "FAIL", 81 | a, net, mask); 82 | } 83 | 84 | int main() { 85 | test("0.0.0.0", "0.0.0.0", 0); 86 | test("192.168.1.2", "192.168.1.2", 0); 87 | 88 | test("::", "::", 0); 89 | test("::0", "::", 0); 90 | test("::00", "::", 0); 91 | test("::000", "::", 0); 92 | test("::0000", "::", 0); 93 | 94 | test("::1", "::1", 0); 95 | test("::01", "::1", 0); 96 | test("::001", "::1", 0); 97 | test("::0001", "::1", 0); 98 | 99 | test("2404:6800:8004::68", "2404:6800:8004::68", 0); 100 | test("2404:6800:8004:0000:0000:0000:0000:0068", "2404:6800:8004::68", 0); 101 | 102 | test(".", NULL, EAI_NONAME); 103 | test(":", NULL, EAI_NONAME); 104 | test("23.75.345.200", NULL, EAI_NONAME); 105 | 106 | test_inside("192.168.1.2", "192.168.0.0", "255.255.0.0", 1); 107 | test_inside("2001:0200::3eff:feb1:44d7", 108 | "2001:0200::", 109 | "ffff:ffff::", 1); 110 | 111 | return retcode; 112 | } 113 | 114 | /* vim:set ts=2 sts=2 sw=2 tw=80 et: */ 115 | -------------------------------------------------------------------------------- /bsd.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2011 Emil Mikulic. 3 | * 4 | * bsd.c: *BSD compatibility. 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "bsd.h" 20 | #include "cdefs.h" 21 | #include "config.h" 22 | 23 | #include /* for strlen */ 24 | 25 | /* strlcpy() and strlcat() are derived from: 26 | * 27 | * $OpenBSD: strlcpy.c,v 1.4 28 | * $FreeBSD: src/lib/libc/string/strlcpy.c,v 1.8 29 | * 30 | * $OpenBSD: strlcat.c,v 1.2 31 | * $FreeBSD: src/lib/libc/string/strlcat.c,v 1.10 32 | * 33 | * under the following license: 34 | * 35 | * Copyright (c) 1998 Todd C. Miller 36 | * All rights reserved. 37 | * 38 | * Redistribution and use in source and binary forms, with or without 39 | * modification, are permitted provided that the following conditions 40 | * are met: 41 | * 1. Redistributions of source code must retain the above copyright 42 | * notice, this list of conditions and the following disclaimer. 43 | * 2. Redistributions in binary form must reproduce the above copyright 44 | * notice, this list of conditions and the following disclaimer in the 45 | * documentation and/or other materials provided with the distribution. 46 | * 3. The name of the author may not be used to endorse or promote products 47 | * derived from this software without specific prior written permission. 48 | * 49 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 50 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 51 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 52 | * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 53 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 54 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 55 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 56 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 57 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 58 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 | */ 60 | 61 | #ifndef HAVE_STRLCPY 62 | /* 63 | * Copy src to string dst of size siz. At most siz-1 characters 64 | * will be copied. Always NUL terminates (unless siz == 0). 65 | * Returns strlen(src); if retval >= siz, truncation occurred. 66 | */ 67 | size_t 68 | strlcpy(char * restrict dst, const char * restrict src, const size_t siz) 69 | { 70 | char *d = dst; 71 | const char *s = src; 72 | size_t n = siz; 73 | 74 | /* Copy as many bytes as will fit */ 75 | if (n != 0 && --n != 0) { 76 | do { 77 | if ((*d++ = *s++) == 0) 78 | break; 79 | } while (--n != 0); 80 | } 81 | 82 | /* Not enough room in dst, add NUL and traverse rest of src */ 83 | if (n == 0) { 84 | if (siz != 0) 85 | *d = '\0'; /* NUL-terminate dst */ 86 | while (*s++) 87 | ; 88 | } 89 | 90 | return (size_t)(s - src - 1); /* count does not include NUL */ 91 | } 92 | #endif 93 | 94 | #ifndef HAVE_STRLCAT 95 | /* 96 | * Appends src to string dst of size siz (unlike strncat, siz is the 97 | * full size of dst, not space left). At most siz-1 characters 98 | * will be copied. Always NUL terminates (unless siz <= strlen(dst)). 99 | * Returns strlen(src) + MIN(siz, strlen(initial dst)). 100 | * If retval >= siz, truncation occurred. 101 | */ 102 | size_t 103 | strlcat(char * restrict dst, const char * restrict src, const size_t siz) 104 | { 105 | char *d = dst; 106 | const char *s = src; 107 | size_t n = siz; 108 | size_t dlen; 109 | 110 | /* Find the end of dst and adjust bytes left but don't go past end */ 111 | while (n-- != 0 && *d != '\0') 112 | d++; 113 | dlen = (size_t)(d - dst); 114 | n = siz - dlen; 115 | 116 | if (n == 0) 117 | return(dlen + strlen(s)); 118 | while (*s != '\0') { 119 | if (n != 1) { 120 | *d++ = *s; 121 | n--; 122 | } 123 | s++; 124 | } 125 | *d = '\0'; 126 | 127 | return (dlen + (size_t)(s - src)); /* count does not include NUL */ 128 | } 129 | #endif 130 | 131 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 132 | -------------------------------------------------------------------------------- /bsd.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2011-2014 Emil Mikulic. 3 | * 4 | * bsd.h: *BSD compatibility. 5 | */ 6 | 7 | #include 8 | #include "config.h" 9 | #ifdef HAVE_BSD_STRING_H 10 | # include 11 | #endif 12 | #ifdef HAVE_BSD_UNISTD_H 13 | # include 14 | #endif 15 | 16 | #ifndef HAVE_STRLCPY 17 | size_t strlcpy(char *dst, const char *src, size_t siz); 18 | #endif 19 | 20 | #ifndef HAVE_STRLCAT 21 | size_t strlcat(char *dst, const char *src, size_t siz); 22 | #endif 23 | 24 | #ifndef HAVE_SETPROCTITLE 25 | #define setproctitle(fmt) /* no-op */ 26 | #endif 27 | 28 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 29 | -------------------------------------------------------------------------------- /cap.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * cap.h: interface to libpcap. 5 | */ 6 | 7 | #include /* OpenBSD needs this before select */ 8 | #include /* FreeBSD 4 needs this for struct timeval */ 9 | #include 10 | 11 | extern unsigned int cap_pkts_recv, cap_pkts_drop; 12 | 13 | void cap_add_ifname(const char *ifname); /* call one or more times */ 14 | void cap_add_filter(const char *filter); /* call zero or more times */ 15 | void cap_start(const int promisc); 16 | void cap_fd_set(fd_set *read_set, int *max_fd, 17 | struct timeval *timeout, int *need_timeout); 18 | int cap_poll(fd_set *read_set); 19 | void cap_stop(void); 20 | void cap_free_args(void); 21 | 22 | void cap_from_file(const char *capfile); 23 | 24 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 25 | -------------------------------------------------------------------------------- /cdefs.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * cdefs.h: compiler-specific defines 5 | * 6 | * This file borrows from FreeBSD's sys/cdefs.h 7 | */ 8 | 9 | #ifdef __GNUC__ 10 | # define _unused_ __attribute__((__unused__)) 11 | # define _noreturn_ __attribute__((__noreturn__)) 12 | # define _printflike_(fmtarg, firstvararg) \ 13 | __attribute__((__format__ (__printf__, fmtarg, firstvararg) )) 14 | #else 15 | # define _unused_ 16 | # define _noreturn_ 17 | # define _printflike_(fmtarg, firstvararg) 18 | #endif 19 | 20 | #ifndef MAX 21 | # define MAX(a,b) ((a) > (b) ? (a) : (b)) 22 | #endif 23 | 24 | #ifndef MIN 25 | # define MIN(a,b) ((a) < (b) ? (a) : (b)) 26 | #endif 27 | 28 | #if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L 29 | # ifdef __COUNTER__ 30 | # define _Static_assert(x, y) __Static_assert(x, __COUNTER__) 31 | # else 32 | # define _Static_assert(x, y) __Static_assert(x, __LINE__) 33 | # endif 34 | # define __Static_assert(x, y) ___Static_assert(x, y) 35 | # define ___Static_assert(x, y) typedef char __assert_ ## y[(x) ? 1 : -1] 36 | #endif 37 | 38 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 39 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.69]) 2 | AC_INIT([darkstat],[3.0.721-git],[],[],[https://unix4lyfe.org/darkstat/]) 3 | AC_CONFIG_SRCDIR([darkstat.c]) 4 | AC_CONFIG_HEADERS([config.h]) 5 | 6 | RULE="------------------------------------------------------------" 7 | 8 | # Allow configure-time override of PRIVDROP_USER. 9 | AC_ARG_WITH(privdrop-user, AS_HELP_STRING([--with-privdrop-user], 10 | [specify which user to drop privileges to (default: nobody)]), 11 | [_pdu="$withval"], 12 | [_pdu="nobody"]) 13 | AC_DEFINE_UNQUOTED(PRIVDROP_USER, "$_pdu", [User to privdrop to.]) 14 | 15 | # Checks for programs. 16 | AC_PROG_INSTALL 17 | AC_PROG_CC 18 | 19 | # Compiler's language features. 20 | AC_C_RESTRICT 21 | 22 | m4_pattern_allow([^AM_DEFAULT_VERBOSITY$]) 23 | AC_ARG_ENABLE([silent-rules], 24 | [ --enable-silent-rules less verbose build output (undo: 'make V=1') 25 | --disable-silent-rules verbose build output (undo: 'make V=0')]) 26 | case $enable_silent_rules in 27 | no) AM_DEFAULT_VERBOSITY=1;; 28 | *) AM_DEFAULT_VERBOSITY=0;; 29 | esac 30 | AC_SUBST([AM_DEFAULT_VERBOSITY]) 31 | 32 | # Let user disable debugging symbols so we create smaller binaries. 33 | AC_MSG_CHECKING(if we want debug code) 34 | AC_ARG_ENABLE(debug, AS_HELP_STRING([--disable-debug], 35 | [turn off debugging code and asserts]), 36 | [if test "x$enableval" = "xno" ; then 37 | CFLAGS="$CFLAGS -DNDEBUG -g0" 38 | AC_MSG_RESULT(nope) 39 | elif test "x$enableval" = "xyes" ; then 40 | AC_MSG_RESULT(sure) 41 | else 42 | CFLAGS="$CFLAGS -g$enableval" 43 | AC_MSG_RESULT(sure ($enableval)) 44 | fi], 45 | [AC_MSG_RESULT(sure)]) 46 | 47 | # Augment CFLAGS for fun. 48 | echo "int main(void){return 1;}" > conftest.$ac_ext 49 | 50 | AC_MSG_CHECKING(if your C compiler wants a hit off the pipe) 51 | save_cflags="$CFLAGS" 52 | CFLAGS="-pipe $CFLAGS" 53 | if (eval $ac_link) 2>/dev/null; then 54 | AC_MSG_RESULT(sure does) 55 | else 56 | AC_MSG_RESULT(no) 57 | CFLAGS="$save_cflags" 58 | fi 59 | 60 | AC_MSG_CHECKING(if your C compiler has a link-time optimizer) 61 | if test x$GCC = xyes; then 62 | save_cflags="$CFLAGS" 63 | CFLAGS="-flto $CFLAGS" 64 | if (eval $ac_link) 2>/dev/null; then 65 | AC_MSG_RESULT(sure does) 66 | else 67 | AC_MSG_RESULT(no) 68 | CFLAGS="$save_cflags" 69 | fi 70 | else 71 | AC_MSG_RESULT(skipped) 72 | fi 73 | 74 | AC_ARG_ENABLE(warnings, AS_HELP_STRING([--enable-warnings], 75 | [turn on lots of compile-time warnings, 76 | these are only useful for development]), 77 | [if test "x$enableval" = "xyes" ; then 78 | AC_MSG_CHECKING(if your C compiler has gcc-like --extra-warnings) 79 | save_cflags="$CFLAGS" 80 | CFLAGS="$CFLAGS -fdiagnostics-show-option --all-warnings --extra-warnings" 81 | if (eval $ac_link) 2>/dev/null; then 82 | AC_MSG_RESULT(yes) 83 | else 84 | AC_MSG_RESULT(no) 85 | CFLAGS="$save_cflags" 86 | fi 87 | 88 | AC_MSG_CHECKING(if your C compiler has clang-like -Weverything) 89 | save_cflags="$CFLAGS" 90 | CFLAGS="$CFLAGS -Weverything" 91 | if (eval $ac_link) 2>/dev/null; then 92 | AC_MSG_RESULT(yes) 93 | else 94 | AC_MSG_RESULT(no) 95 | CFLAGS="$save_cflags" 96 | fi 97 | fi]) 98 | 99 | rm -f conftest.$ac_objext conftest.$ac_ext 100 | 101 | 102 | 103 | # Check for zlib. 104 | AC_CHECK_LIB(z, deflate,, [ 105 | cat < 2 | 3 | 4 | 5 | UserName 6 | root 7 | GroupName 8 | wheel 9 | KeepAlive 10 | 11 | Label 12 | cx.ath.darkstat 13 | Nice 14 | 1 15 | ProgramArguments 16 | 17 | 23 | --no-daemon 24 | -i 25 | en0 26 | -b 27 | 127.0.0.1 28 | 29 | RunAtLoad 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /contrib/darkproxy.php: -------------------------------------------------------------------------------- 1 | 0) 90 | print reason 91 | if (terminate != 0) { 92 | # Any remaining bytes in the file shall be dumped. 93 | for (i=ai; i<=addr; i++) 94 | print i, hex[i], ascii[i] 95 | exit(retval) 96 | } 97 | } 98 | function readIPsection() { 99 | ip_protos_data=read_bytes(ascii, ai, 1) 100 | if (ip_protos_data != "P") 101 | quit("expected ip_protos_data P, found " ip_protos_data, 1, 1) 102 | ai += 1 103 | ip_proto_count=read_number(ai, 1) 104 | ai += 1 105 | for (pi=0; pi 1) { 205 | last_seen=read_number(ai, 4) 206 | # This value is always 0 in our files. 207 | ai += 4 208 | } 209 | mac_address=hex[ai+0] ":" hex[ai+1] ":" hex[ai+2] ":" hex[ai+3] ":" hex[ai+4] ":" hex[ai+5] 210 | ai += 6 211 | # Weird stuff: the host name should be read. 212 | # But there are only 5 bytes of nonsense. 213 | # The first byte should be the length counter, but it isnt. 214 | # The last byte is in fact a 0 byte. 215 | # Probably caused by the missing DNS server. 216 | # ignore 5 bytes with nonsense 217 | nonsense=read_text(ai, 5) 218 | ai += 5 219 | host_bytes_in=read_number(ai, 8) 220 | ai += 8 221 | host_bytes_out=read_number(ai, 8) 222 | ai += 8 223 | readIPsection() 224 | readTCPsection() 225 | readUDPsection() 226 | } else { 227 | quit("host format supported only in version 02: " host_version, 1, 1) 228 | #address_familiy=read_bytes(hex, ai, 1) 229 | #print "address familiy = " address_familiy 230 | } 231 | printf("\"%s\";\"%s\";%d;%d;%s;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%s;%s\n", 232 | ip_address, mac_address, host_bytes_in, host_bytes_out, 233 | IPprotos, ip_proto_in, ip_proto_out, 234 | tcp_proto_count, tcp_proto_in, tcp_proto_out, 235 | udp_proto_count, udp_proto_in, udp_proto_out, 236 | ssh_in, ssh_out, rdp_in, rdp_out, 237 | TCPports, UDPports) 238 | } 239 | section_header=read_bytes(hex, ai, 3) 240 | if (section_header != "da4752") 241 | quit("section header da4752 expected: " section_header, 1, 1) 242 | ai += 3 243 | db_version=read_bytes(hex, ai, 1) 244 | if (db_version != "01") 245 | quit("file format supported only in version 01", 1, 1) 246 | ai += 1 247 | last_time=read_number(ai, 8) 248 | ai += 8 249 | readGraphsection("60 seconds") 250 | readGraphsection("60 minutes") 251 | readGraphsection("24 hours") 252 | readGraphsection("31 days") 253 | # The complete file has been parsed, no bytes should be left over. 254 | # Terminate with return value 0 if the byte numbers match. 255 | quit("", (addr != ai+1) ?0:1, addr != ai+1) 256 | } 257 | ' > ${CSVFILENAME} 258 | -------------------------------------------------------------------------------- /conv.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * conv.c: convenience functions. 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "conv.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include "err.h" 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define PATH_DEVNULL "/dev/null" 37 | 38 | /* malloc() that exits on failure. */ 39 | void * 40 | xmalloc(const size_t size) 41 | { 42 | void *ptr = malloc(size); 43 | 44 | if (ptr == NULL) 45 | errx(1, "malloc(): out of memory"); 46 | return (ptr); 47 | } 48 | 49 | /* calloc() that exits on failure. */ 50 | void * 51 | xcalloc(const size_t num, const size_t size) 52 | { 53 | void *ptr = calloc(num, size); 54 | 55 | if (ptr == NULL) 56 | errx(1, "calloc(): out of memory"); 57 | return (ptr); 58 | } 59 | 60 | /* realloc() that exits on failure. */ 61 | void * 62 | xrealloc(void *original, const size_t size) 63 | { 64 | void *ptr = realloc(original, size); 65 | 66 | if (ptr == NULL) 67 | errx(1, "realloc(): out of memory"); 68 | return (ptr); 69 | } 70 | 71 | /* strdup() that exits on failure. */ 72 | char * 73 | xstrdup(const char *s) 74 | { 75 | char *tmp = strdup(s); 76 | 77 | if (tmp == NULL) 78 | errx(1, "strdup(): out of memory"); 79 | return (tmp); 80 | } 81 | 82 | /* --------------------------------------------------------------------------- 83 | * Split string out of src with range [left:right-1] 84 | */ 85 | char * 86 | split_string(const char *src, const size_t left, const size_t right) 87 | { 88 | char *dest; 89 | assert(left <= right); 90 | assert(left < strlen(src)); /* [left means must be smaller */ 91 | assert(right <= strlen(src)); /* right) means can be equal or smaller */ 92 | 93 | dest = xmalloc(right - left + 1); 94 | memcpy(dest, src+left, right-left); 95 | dest[right-left] = '\0'; 96 | return (dest); 97 | } 98 | 99 | /* --------------------------------------------------------------------------- 100 | * Uppercasify all characters in a string of given length. 101 | */ 102 | void 103 | strntoupper(char *str, const size_t length) 104 | { 105 | size_t i; 106 | 107 | for (i=0; i 2) 285 | close(fd_null); 286 | } 287 | 288 | /* 289 | * For security, chroot (optionally) and drop privileges. 290 | * Pass a NULL chroot_dir to disable chroot() behaviour. 291 | */ 292 | void privdrop(const char *chroot_dir, const char *privdrop_user) { 293 | struct passwd *pw; 294 | 295 | errno = 0; 296 | pw = getpwnam(privdrop_user); 297 | 298 | if (pw == NULL) { 299 | if (errno == 0) 300 | errx(1, "getpwnam(\"%s\") failed: no such user", privdrop_user); 301 | else 302 | err(1, "getpwnam(\"%s\") failed", privdrop_user); 303 | } 304 | if (chroot_dir == NULL) { 305 | verbosef("no --chroot dir specified, darkstat will not chroot()"); 306 | } else { 307 | /* Read /etc/localtime before we chroot. This works on FreeBSD but not 308 | * on Linux / with glibc (as of 2.22) */ 309 | tzset(); 310 | if (chroot(chroot_dir) == -1) 311 | err(1, "chroot(\"%s\") failed", chroot_dir); 312 | if (chdir("/") == -1) 313 | err(1, "chdir(\"/\") failed"); 314 | verbosef("chrooted into: %s", chroot_dir); 315 | } 316 | { 317 | gid_t list[1]; 318 | list[0] = pw->pw_gid; 319 | if (setgroups(1, list) == -1) 320 | err(1, "setgroups"); 321 | } 322 | if (setgid(pw->pw_gid) == -1) 323 | err(1, "setgid"); 324 | if (setuid(pw->pw_uid) == -1) 325 | err(1, "setuid"); 326 | verbosef("set uid/gid to %d/%d", (int)pw->pw_uid, (int)pw->pw_gid); 327 | } 328 | 329 | /* Make the specified file descriptor non-blocking. */ 330 | void 331 | fd_set_nonblock(const int fd) 332 | { 333 | int flags; 334 | 335 | if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 336 | err(1, "fcntl(fd %d) to get flags", fd); 337 | flags |= O_NONBLOCK; 338 | if (fcntl(fd, F_SETFL, flags) == -1) 339 | err(1, "fcntl(fd %d) to set O_NONBLOCK", fd); 340 | assert( (fcntl(fd, F_GETFL, 0) & O_NONBLOCK ) == O_NONBLOCK ); 341 | } 342 | 343 | /* Make the specified file descriptor blocking. */ 344 | void 345 | fd_set_block(const int fd) 346 | { 347 | int flags; 348 | 349 | if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 350 | err(1, "fcntl(fd %d) to get flags", fd); 351 | flags &= ~O_NONBLOCK; 352 | if (fcntl(fd, F_SETFL, flags) == -1) 353 | err(1, "fcntl(fd %d) to unset O_NONBLOCK", fd); 354 | assert( (fcntl(fd, F_GETFL, 0) & O_NONBLOCK ) == 0 ); 355 | } 356 | 357 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 358 | -------------------------------------------------------------------------------- /conv.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2011 Emil Mikulic. 3 | * 4 | * conv.h: convenience functions. 5 | */ 6 | 7 | #include 8 | 9 | void *xmalloc(const size_t size); 10 | void *xcalloc(const size_t num, const size_t size); 11 | void *xrealloc(void *original, const size_t size); 12 | char *xstrdup(const char *s); 13 | char *split_string(const char *src, const size_t left, const size_t right); 14 | void strntoupper(char *str, const size_t length); 15 | int str_starts_with(const char *haystack, const char *needle); 16 | char**split(const char delimiter, const char *str, unsigned int *num_chunks); 17 | char *qs_get(const char *qs, const char *key); 18 | 19 | void daemonize_start(void); 20 | void daemonize_finish(void); 21 | void privdrop(const char *chroot_dir, const char *privdrop_user); 22 | void fd_set_nonblock(const int fd); 23 | void fd_set_block(const int fd); 24 | 25 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 26 | -------------------------------------------------------------------------------- /daylog.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2007-2014 Emil Mikulic. 3 | * 4 | * daylog.c: daily usage log 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | 10 | #define _GNU_SOURCE 1 /* for O_NOFOLLOW on Linux */ 11 | 12 | #include "cdefs.h" 13 | #include "err.h" 14 | #include "daylog.h" 15 | #include "str.h" 16 | #include "now.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | static const char *daylog_fn = NULL; 27 | static time_t today_real, tomorrow_real; 28 | static uint64_t bytes_in, bytes_out, pkts_in, pkts_out; 29 | 30 | #define DAYLOG_DATE_LEN 26 /* strlen("1900-01-01 00:00:00 +1234") + 1 */ 31 | static char datebuf[DAYLOG_DATE_LEN]; 32 | 33 | static char *fmt_date(time_t when) { 34 | if (strftime(datebuf, 35 | DAYLOG_DATE_LEN, 36 | "%Y-%m-%d %H:%M:%S %z", 37 | localtime(&when)) == 0) 38 | errx(1, "strftime() failed in fmt_date()"); 39 | return datebuf; 40 | } 41 | 42 | /* Given some time today, find the first second of tomorrow. */ 43 | static time_t tomorrow(time_t t_before) { 44 | time_t t_after; 45 | struct tm tm, *lt; 46 | 47 | lt = localtime(&t_before); 48 | memcpy(&tm, lt, sizeof(tm)); 49 | tm.tm_sec = 0; 50 | tm.tm_min = 0; 51 | tm.tm_hour = 0; 52 | tm.tm_mday = lt->tm_mday + 1; /* tomorrow */ 53 | t_after = mktime(&tm); 54 | assert(t_after > t_before); 55 | return t_after; 56 | } 57 | 58 | /* Warns on error. */ 59 | static void daylog_write(const char *format, ...) _printflike_(1, 2); 60 | static void daylog_write(const char *format, ...) { 61 | int fd; 62 | ssize_t wr; 63 | va_list va; 64 | struct str *buf; 65 | 66 | assert(daylog_fn != NULL); 67 | fd = open(daylog_fn, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600); 68 | if (fd == -1) { 69 | warn("daylog_write: couldn't open '%s' for append", daylog_fn); 70 | return; 71 | } 72 | 73 | buf = str_make(); 74 | va_start(va, format); 75 | str_vappendf(buf, format, va); 76 | va_end(va); 77 | 78 | wr = str_write(buf, fd); 79 | if (wr == -1) 80 | warn("daylog_write: couldn't write to '%s'", daylog_fn); 81 | else if (wr != (ssize_t)str_len(buf)) 82 | warnx("daylog_write: truncated write to '%s': wrote %d of %d bytes", 83 | daylog_fn, 84 | (int)wr, 85 | (int)str_len(buf)); 86 | close(fd); 87 | str_free(buf); 88 | } 89 | 90 | static void daylog_emit(void) { 91 | daylog_write("%s|%qu|%qu|%qu|%qu|%qu\n", 92 | fmt_date(today_real), 93 | (qu)today_real, 94 | (qu)bytes_in, 95 | (qu)bytes_out, 96 | (qu)pkts_in, 97 | (qu)pkts_out); 98 | } 99 | 100 | void daylog_init(const char *filename) { 101 | daylog_fn = filename; 102 | today_real = now_real(); 103 | tomorrow_real = tomorrow(today_real); 104 | verbosef("today is %llu, tomorrow is %llu", 105 | (llu)today_real, 106 | (llu)tomorrow_real); 107 | bytes_in = bytes_out = pkts_in = pkts_out = 0; 108 | 109 | daylog_write("# logging started at %s (%qu)\n", 110 | fmt_date(today_real), (qu)today_real); 111 | } 112 | 113 | void daylog_free(void) { 114 | today_real = now_real(); 115 | daylog_emit(); /* Emit what's currently accumulated before we exit. */ 116 | daylog_write("# logging stopped at %s (%qu)\n", 117 | fmt_date(today_real), (qu)today_real); 118 | } 119 | 120 | void daylog_acct(uint64_t amount, enum graph_dir dir) { 121 | if (daylog_fn == NULL) 122 | return; /* daylogging disabled */ 123 | 124 | /* Check if we need to update the log. */ 125 | if (now_real() >= tomorrow_real) { 126 | daylog_emit(); 127 | 128 | today_real = now_real(); 129 | tomorrow_real = tomorrow(today_real); 130 | bytes_in = bytes_out = pkts_in = pkts_out = 0; 131 | verbosef("updated daylog, tomorrow = %llu", (llu)tomorrow_real); 132 | } 133 | 134 | /* Accounting. */ 135 | if (dir == GRAPH_IN) { 136 | bytes_in += amount; 137 | pkts_in++; 138 | } else { 139 | assert(dir == GRAPH_OUT); 140 | bytes_out += amount; 141 | pkts_out++; 142 | } 143 | } 144 | 145 | /* vim:set ts=3 sw=3 tw=78 et: */ 146 | -------------------------------------------------------------------------------- /daylog.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2007 Emil Mikulic. 3 | * 4 | * daylog.h: daily usage log 5 | */ 6 | 7 | #include "graph_db.h" /* for graph_dir */ 8 | 9 | void daylog_init(const char *filename); 10 | void daylog_free(void); 11 | void daylog_acct(uint64_t amount, enum graph_dir dir); 12 | 13 | /* vim:set ts=3 sw=3 tw=78 et: */ 14 | -------------------------------------------------------------------------------- /db.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * 3 | * db.c: load and save in-memory database from/to file 4 | * copyright (c) 2007-2012 Ben Stewart, Emil Mikulic. 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | 10 | #define _GNU_SOURCE 1 /* for O_NOFOLLOW in Linux */ 11 | 12 | #include 13 | #include /* for ntohs() and friends */ 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "err.h" 20 | #include "hosts_db.h" 21 | #include "graph_db.h" 22 | #include "db.h" 23 | 24 | static const unsigned char export_file_header[] = {0xDA, 0x31, 0x41, 0x59}; 25 | static const unsigned char export_tag_hosts_ver1[] = {0xDA, 'H', 'S', 0x01}; 26 | static const unsigned char export_tag_graph_ver1[] = {0xDA, 'G', 'R', 0x01}; 27 | 28 | #ifndef swap64 29 | static uint64_t swap64(uint64_t _x) { 30 | /* this is __bswap64 from: 31 | * $FreeBSD: src/sys/i386/include/endian.h,v 1.41$ 32 | */ 33 | return ((_x >> 56) | ((_x >> 40) & 0xff00) | ((_x >> 24) & 0xff0000) | 34 | ((_x >> 8) & 0xff000000) | ((_x << 8) & ((uint64_t)0xff << 32)) | 35 | ((_x << 24) & ((uint64_t)0xff << 40)) | 36 | ((_x << 40) & ((uint64_t)0xff << 48)) | ((_x << 56))); 37 | } 38 | #endif 39 | 40 | #define ntoh64 hton64 41 | static uint64_t hton64(const uint64_t ho) { 42 | if (ntohs(0x1234) == 0x1234) 43 | return ho; 44 | else 45 | return swap64(ho); 46 | } 47 | 48 | void 49 | test_64order(void) 50 | { 51 | static const char str[] = { 0x79,0x74,0x69,0x63,0x6b,0x72,0x65,0x6a }; 52 | uint64_t no, ho; 53 | 54 | assert(sizeof(no) == 8); 55 | memcpy(&no, str, 8); 56 | ho = ntoh64(no); 57 | assert(ho == 8751735851613054314ULL); 58 | assert(hton64(ntoh64(no)) == no); 59 | } 60 | 61 | /* --------------------------------------------------------------------------- 62 | * Read-from-file helpers. They all return 0 on failure, and 1 on success. 63 | */ 64 | 65 | unsigned int 66 | xtell(const int fd) 67 | { 68 | off_t ofs = lseek(fd, 0, SEEK_CUR); 69 | if (ofs == -1) 70 | err(1, "lseek(0, SEEK_CUR) failed"); 71 | return (unsigned int)ofs; 72 | } 73 | 74 | /* Read bytes from , warn() and return 0 on failure, 75 | * or return 1 for success. 76 | */ 77 | int 78 | readn(const int fd, void *dest, const size_t len) 79 | { 80 | ssize_t numread; 81 | 82 | numread = read(fd, dest, len); 83 | if (numread == (ssize_t)len) return 1; 84 | 85 | if (numread == -1) 86 | warn("at pos %u: couldn't read %d bytes", xtell(fd), (int)len); 87 | else 88 | warnx("at pos %u: tried to read %d bytes, got %d", 89 | xtell(fd), (int)len, (int)numread); 90 | return 0; 91 | } 92 | 93 | /* Read a byte. */ 94 | int 95 | read8(const int fd, uint8_t *dest) 96 | { 97 | assert(sizeof(*dest) == 1); 98 | return readn(fd, dest, sizeof(*dest)); 99 | } 100 | 101 | /* Read a byte and compare it to the expected data. 102 | * Returns 0 on failure or mismatch, 1 on success. 103 | */ 104 | int 105 | expect8(const int fd, uint8_t expecting) 106 | { 107 | uint8_t tmp; 108 | 109 | assert(sizeof(tmp) == 1); 110 | if (!readn(fd, &tmp, sizeof(tmp))) return 0; 111 | if (tmp == expecting) return 1; 112 | 113 | warnx("at pos %u: expecting 0x%02x, got 0x%02x", 114 | xtell(fd)-1, expecting, tmp); 115 | return 0; 116 | } 117 | 118 | /* Read a network order uint16_t from a file 119 | * and store it in host order in memory. 120 | */ 121 | int 122 | read16(const int fd, uint16_t *dest) 123 | { 124 | uint16_t tmp; 125 | 126 | assert(sizeof(tmp) == 2); 127 | if (!read(fd, &tmp, sizeof(tmp))) return 0; 128 | *dest = ntohs(tmp); 129 | return 1; 130 | } 131 | 132 | /* Read a network order uint32_t from a file 133 | * and store it in host order in memory. 134 | */ 135 | int 136 | read32(const int fd, uint32_t *dest) 137 | { 138 | uint32_t tmp; 139 | 140 | assert(sizeof(tmp) == 4); 141 | if (!read(fd, &tmp, sizeof(tmp))) return 0; 142 | *dest = ntohl(tmp); 143 | return 1; 144 | } 145 | 146 | /* Read an IPv4 addr from a file. This is for backward compatibility with 147 | * host records version 1 and 2. 148 | */ 149 | int 150 | readaddr_ipv4(const int fd, struct addr *dest) 151 | { 152 | dest->family = IPv4; 153 | return readn(fd, &(dest->ip.v4), sizeof(dest->ip.v4)); 154 | } 155 | 156 | /* Read a struct addr from a file. Addresses are always stored in network 157 | * order, both in the file and in the host's memory (FIXME: is that right?) 158 | */ 159 | int 160 | readaddr(const int fd, struct addr *dest) 161 | { 162 | unsigned char family; 163 | 164 | if (!read8(fd, &family)) 165 | return 0; 166 | 167 | if (family == 4) { 168 | dest->family = IPv4; 169 | return readn(fd, &(dest->ip.v4), sizeof(dest->ip.v4)); 170 | } 171 | else if (family == 6) { 172 | dest->family = IPv6; 173 | return readn(fd, dest->ip.v6.s6_addr, sizeof(dest->ip.v6.s6_addr)); 174 | } 175 | else 176 | return 0; /* no address family I ever heard of */ 177 | } 178 | 179 | /* Read a network order uint64_t from a file 180 | * and store it in host order in memory. 181 | */ 182 | int 183 | read64(const int fd, uint64_t *dest) 184 | { 185 | uint64_t tmp; 186 | 187 | assert(sizeof(tmp) == 8); 188 | if (!read(fd, &tmp, sizeof(tmp))) return 0; 189 | *dest = ntoh64(tmp); 190 | return 1; 191 | } 192 | 193 | /* --------------------------------------------------------------------------- 194 | * Write-to-file helpers. They all return 0 on failure, and 1 on success. 195 | */ 196 | 197 | /* Write bytes to , warn() and return 0 on failure, 198 | * or return 1 for success. 199 | */ 200 | int 201 | writen(const int fd, const void *dest, const size_t len) 202 | { 203 | ssize_t numwr; 204 | 205 | numwr = write(fd, dest, len); 206 | if (numwr == (ssize_t)len) return 1; 207 | 208 | if (numwr == -1) 209 | warn("couldn't write %d bytes", (int)len); 210 | else 211 | warnx("tried to write %d bytes but wrote %d", 212 | (int)len, (int)numwr); 213 | return 0; 214 | } 215 | 216 | int 217 | write8(const int fd, const uint8_t i) 218 | { 219 | assert(sizeof(i) == 1); 220 | return writen(fd, &i, sizeof(i)); 221 | } 222 | 223 | /* Given a uint16_t in host order, write it to a file in network order. 224 | */ 225 | int 226 | write16(const int fd, const uint16_t i) 227 | { 228 | uint16_t tmp = htons(i); 229 | assert(sizeof(tmp) == 2); 230 | return writen(fd, &tmp, sizeof(tmp)); 231 | } 232 | 233 | /* Given a uint32_t in host order, write it to a file in network order. 234 | */ 235 | int 236 | write32(const int fd, const uint32_t i) 237 | { 238 | uint32_t tmp = htonl(i); 239 | assert(sizeof(tmp) == 4); 240 | return writen(fd, &tmp, sizeof(tmp)); 241 | } 242 | 243 | /* Given a uint64_t in host order, write it to a file in network order. 244 | */ 245 | int 246 | write64(const int fd, const uint64_t i) 247 | { 248 | uint64_t tmp = hton64(i); 249 | assert(sizeof(tmp) == 8); 250 | return writen(fd, &tmp, sizeof(tmp)); 251 | } 252 | 253 | 254 | /* Write the active address part in a struct addr to a file. 255 | * Addresses are always stored in network order, both in the file and 256 | * in the host's memory (FIXME: is that right?) 257 | */ 258 | int 259 | writeaddr(const int fd, const struct addr *const a) 260 | { 261 | if (!write8(fd, a->family)) 262 | return 0; 263 | 264 | if (a->family == IPv4) 265 | return writen(fd, &(a->ip.v4), sizeof(a->ip.v4)); 266 | else { 267 | assert(a->family == IPv6); 268 | return writen(fd, a->ip.v6.s6_addr, sizeof(a->ip.v6.s6_addr)); 269 | } 270 | } 271 | 272 | /* --------------------------------------------------------------------------- 273 | * db import/export code follows. 274 | */ 275 | 276 | /* Check that the global file header is correct / supported. */ 277 | int 278 | read_file_header(const int fd, const uint8_t expected[4]) 279 | { 280 | uint8_t got[4]; 281 | 282 | if (!readn(fd, got, sizeof(got))) return 0; 283 | 284 | /* Check the header data */ 285 | if (memcmp(got, expected, sizeof(got)) != 0) { 286 | warnx("bad header: " 287 | "expecting %02x%02x%02x%02x, got %02x%02x%02x%02x", 288 | expected[0], expected[1], expected[2], expected[3], 289 | got[0], got[1], got[2], got[3]); 290 | return 0; 291 | } 292 | return 1; 293 | } 294 | 295 | /* Returns 0 on failure, 1 on success. */ 296 | static int 297 | db_import_from_fd(const int fd) 298 | { 299 | if (!read_file_header(fd, export_file_header)) return 0; 300 | if (!read_file_header(fd, export_tag_hosts_ver1)) return 0; 301 | if (!hosts_db_import(fd)) return 0; 302 | if (!read_file_header(fd, export_tag_graph_ver1)) return 0; 303 | if (!graph_import(fd)) return 0; 304 | return 1; 305 | } 306 | 307 | void 308 | db_import(const char *filename) 309 | { 310 | int fd = open(filename, O_RDONLY | O_NOFOLLOW); 311 | if (fd == -1) { 312 | warn("can't import from \"%s\"", filename); 313 | return; 314 | } 315 | if (!db_import_from_fd(fd)) { 316 | warnx("import failed"); 317 | /* don't stay in an inconsistent state: */ 318 | hosts_db_reset(); 319 | graph_reset(); 320 | } 321 | close(fd); 322 | } 323 | 324 | /* Returns 0 on failure, 1 on success. */ 325 | static int 326 | db_export_to_fd(const int fd) 327 | { 328 | if (!writen(fd, export_file_header, sizeof(export_file_header))) 329 | return 0; 330 | if (!writen(fd, export_tag_hosts_ver1, sizeof(export_tag_hosts_ver1))) 331 | return 0; 332 | if (!hosts_db_export(fd)) 333 | return 0; 334 | if (!writen(fd, export_tag_graph_ver1, sizeof(export_tag_graph_ver1))) 335 | return 0; 336 | if (!graph_export(fd)) 337 | return 0; 338 | return 1; 339 | } 340 | 341 | void 342 | db_export(const char *filename) 343 | { 344 | int fd = open(filename, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, 0600); 345 | if (fd == -1) { 346 | warn("can't export to \"%s\"", filename); 347 | return; 348 | } 349 | verbosef("exporting db to file \"%s\"", filename); 350 | if (!db_export_to_fd(fd)) 351 | warnx("export failed"); 352 | else 353 | verbosef("export successful"); 354 | 355 | /* FIXME: should write to another filename and use the rename() syscall to 356 | * atomically update the output file on success 357 | */ 358 | close(fd); 359 | } 360 | 361 | /* vim:set ts=3 sw=3 tw=78 et: */ 362 | -------------------------------------------------------------------------------- /db.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * 3 | * db.h: load and save in-memory database from/to file 4 | * copyright (c) 2007-2012 Ben Stewart, Emil Mikulic. 5 | */ 6 | 7 | #include /* for size_t */ 8 | #include /* for uint64_t */ 9 | 10 | struct addr; 11 | 12 | void db_import(const char *filename); 13 | void db_export(const char *filename); 14 | void test_64order(void); 15 | 16 | /* read helpers */ 17 | unsigned int xtell(const int fd); 18 | int readn(const int fd, void *dest, const size_t len); 19 | int read8(const int fd, uint8_t *dest); 20 | int expect8(const int fd, uint8_t expecting); 21 | int read16(const int fd, uint16_t *dest); 22 | int read32(const int fd, uint32_t *dest); 23 | int read64(const int fd, uint64_t *dest); 24 | int readaddr_ipv4(const int fd, struct addr *dest); 25 | int readaddr(const int fd, struct addr *dest); 26 | int read_file_header(const int fd, const uint8_t expected[4]); 27 | 28 | /* write helpers */ 29 | int writen(const int fd, const void *dest, const size_t len); 30 | int write8(const int fd, const uint8_t i); 31 | int write16(const int fd, const uint16_t i); 32 | int write32(const int fd, const uint32_t i); 33 | int write64(const int fd, const uint64_t i); 34 | int writeaddr(const int fd, const struct addr *const a); 35 | 36 | /* vim:set ts=3 sw=3 tw=78 et: */ 37 | -------------------------------------------------------------------------------- /decode.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2012 Emil Mikulic. 3 | * 4 | * decode.h: packet decoding. 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | #ifndef __DARKSTAT_DECODE_H 10 | #define __DARKSTAT_DECODE_H 11 | 12 | #include "addr.h" 13 | 14 | #ifndef ETHER_ADDR_LEN 15 | # define ETHER_ADDR_LEN 6 16 | #endif 17 | 18 | #define IPPROTO_INVALID 254 /* special: means don't do proto accounting */ 19 | 20 | #ifndef IPPROTO_OSPF 21 | # define IPPROTO_OSPF 89 22 | #endif 23 | 24 | #define PPPOE_HDR_LEN 8 25 | 26 | /* Decoding creates a summary which is passed to accounting. */ 27 | struct pktsummary { 28 | /* Fields are in host byte order (except IPs) */ 29 | struct addr src, dst; 30 | uint16_t len; 31 | uint8_t proto; /* IPPROTO_INVALID means don't do proto accounting */ 32 | uint8_t tcp_flags; /* only for TCP */ 33 | uint16_t src_port, dst_port; /* only for TCP, UDP */ 34 | uint8_t src_mac[ETHER_ADDR_LEN], /* only for Ethernet */ 35 | dst_mac[ETHER_ADDR_LEN]; /* only for Ethernet */ 36 | }; 37 | 38 | struct pcap_pkthdr; /* from pcap.h */ 39 | 40 | #define DECODER_ARGS const struct pcap_pkthdr *pheader, \ 41 | const u_char *pdata, \ 42 | struct pktsummary *sm 43 | 44 | /* Returns 0 on decode failure (meaning accounting should not be performed) */ 45 | typedef int (decoder_fn)(DECODER_ARGS); 46 | 47 | struct linkhdr { 48 | int linktype; 49 | unsigned int hdrlen; 50 | decoder_fn *decoder; 51 | }; 52 | 53 | const struct linkhdr *getlinkhdr(const int linktype); 54 | int getsnaplen(const struct linkhdr *lh); 55 | 56 | #endif /* __DARKSTAT_DECODE_H */ 57 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 58 | -------------------------------------------------------------------------------- /decode_corpus/ether_ipv4_tcp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emikulic/darkstat/a219027ec8ea6bb75647bef126dd44f56ac9c86e/decode_corpus/ether_ipv4_tcp -------------------------------------------------------------------------------- /decode_corpus/ether_ipv4_udp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emikulic/darkstat/a219027ec8ea6bb75647bef126dd44f56ac9c86e/decode_corpus/ether_ipv4_udp -------------------------------------------------------------------------------- /decode_fuzzer.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2022 Emil Mikulic. 3 | * 4 | * decode_fuzzer.c: fuzzer for the decoders in decode.c 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* Usage: 20 | * clang -g -O2 -fsanitize=fuzzer,address decode_fuzzer.c -o decode_fuzzer 21 | * ./decode_fuzzer decode_corpus 22 | * 23 | * Try also: -print_coverage=1 24 | * 25 | * The first two bytes of the fuzzer input are treated as the linktype, and 26 | * dispatch is done via getlinkhdr(). 27 | */ 28 | 29 | #include "addr.c" 30 | #include "decode.c" 31 | #include "linktypes.c" 32 | 33 | /* This is fine. It means decode_ether won't call into decode_pppoe, 34 | * but linktype DLT_PPP_ETHER will, so we still get coverage. 35 | */ 36 | int opt_want_pppoe = 0; 37 | 38 | /* Only enable verbose if debugging the fuzzer. */ 39 | static const int verbose = 0; 40 | 41 | void verbosef(const char *format, ...) { 42 | if (!verbose) return; 43 | va_list va; 44 | 45 | va_start(va, format); 46 | printf("verbosef: "); 47 | vprintf(format, va); 48 | printf("\n"); 49 | va_end(va); 50 | } 51 | 52 | static void hexdump(const u_char *buf, 53 | const uint32_t len) { 54 | uint32_t i; 55 | uint32_t col = 0; 56 | 57 | printf("packet of %u bytes:\n", len); 58 | for (i=0; i= 72) { 63 | printf("\n"); 64 | col = 0; 65 | } 66 | } 67 | if (col != 0) printf("\n"); 68 | } 69 | 70 | static void print_summary(const struct pktsummary* s) { 71 | printf("pktsummary:\n"); 72 | printf(" src=%s\n", addr_to_str(&s->src)); 73 | printf(" dst=%s\n", addr_to_str(&s->dst)); 74 | printf(" len=0x%04x (%d) proto=0x%02x tcp_flags=0x%02x\n", 75 | s->len, s->len, s->proto, s->tcp_flags); 76 | printf(" src_port=0x%04x (%d) dst_port=0x%04x (%d)\n", 77 | s->src_port, s->src_port, s->dst_port, s->dst_port); 78 | printf(" src_mac=%02x:%02x:%02x:%02x:%02x:%02x\n", 79 | s->src_mac[0], 80 | s->src_mac[1], 81 | s->src_mac[2], 82 | s->src_mac[3], 83 | s->src_mac[4], 84 | s->src_mac[5]); 85 | printf(" dst_mac=%02x:%02x:%02x:%02x:%02x:%02x\n", 86 | s->dst_mac[0], 87 | s->dst_mac[1], 88 | s->dst_mac[2], 89 | s->dst_mac[3], 90 | s->dst_mac[4], 91 | s->dst_mac[5]); 92 | } 93 | 94 | static void decode(int linktype, const uint8_t* data, size_t size) { 95 | const struct linkhdr *lh = getlinkhdr(linktype); 96 | if (verbose) { 97 | printf(">> linktype=%d (%s)\n", linktype, get_linktype_name(linktype)); 98 | hexdump(data, size); 99 | } 100 | 101 | if (lh == NULL) { 102 | return; /* No decoder for this linktype. */ 103 | } 104 | 105 | struct pcap_pkthdr hdr; 106 | hdr.caplen = size; 107 | 108 | struct pktsummary sm; 109 | memset(&sm, 0, sizeof(sm)); 110 | int ret = lh->decoder(&hdr, data, &sm); 111 | 112 | if (verbose) { 113 | printf("ret = %d\n", ret); 114 | if (ret) print_summary(&sm); 115 | } 116 | } 117 | 118 | int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { 119 | int16_t linktype; 120 | const int SZ = sizeof(linktype); 121 | if (size < SZ) { 122 | return 0; /* Too short. */ 123 | } 124 | memcpy(&linktype, data, SZ); 125 | decode(linktype, data + SZ, size - SZ); 126 | return 0; 127 | } 128 | 129 | /* vim:set ts=2 sw=2 sts=2 expandtab tw=78: */ 130 | -------------------------------------------------------------------------------- /dev_all.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2014 Emil Mikulic. 3 | * 4 | * dev_all.c: a single compilation unit of all darkstat code. 5 | * Useful for static analysis. 6 | * 7 | * You may use, modify and redistribute this file under the terms of the 8 | * GNU General Public License version 2. (see COPYING.GPL) 9 | */ 10 | #include "acct.c" 11 | #include "addr.c" 12 | #include "bsd.c" 13 | #include "cap.c" 14 | #include "conv.c" 15 | #include "daylog.c" 16 | #include "db.c" 17 | #include "decode.c" 18 | #include "dns.c" 19 | #include "err.c" 20 | #include "graph_db.c" 21 | #include "hosts_db.c" 22 | #include "hosts_sort.c" 23 | #include "html.c" 24 | #include "http.c" 25 | #include "localip.c" 26 | #include "ncache.c" 27 | #include "now.c" 28 | #include "pidfile.c" 29 | #include "str.c" 30 | 31 | #include "darkstat.c" 32 | -------------------------------------------------------------------------------- /dev_analyze.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | # 3 | # Run the clang static analyzer. 4 | # 5 | time $HOME/llvm/install/bin/scan-build \ 6 | -analyze-headers \ 7 | \ 8 | -enable-checker alpha.core.BoolAssignment \ 9 | -enable-checker alpha.core.CallAndMessageUnInitRefArg \ 10 | -enable-checker alpha.core.CastSize \ 11 | -enable-checker alpha.core.CastToStruct \ 12 | -enable-checker alpha.core.DynamicTypeChecker \ 13 | -enable-checker alpha.core.FixedAddr \ 14 | -enable-checker alpha.core.IdenticalExpr \ 15 | -enable-checker alpha.core.PointerArithm \ 16 | -enable-checker alpha.core.PointerSub \ 17 | -enable-checker alpha.core.SizeofPtr \ 18 | -enable-checker alpha.core.TestAfterDivZero \ 19 | -enable-checker alpha.cplusplus.VirtualCall \ 20 | -enable-checker alpha.deadcode.UnreachableCode \ 21 | -enable-checker alpha.security.ArrayBound \ 22 | -enable-checker alpha.security.ArrayBoundV2 \ 23 | -enable-checker alpha.security.MallocOverflow \ 24 | -enable-checker alpha.security.ReturnPtrRange \ 25 | -enable-checker alpha.security.taint.TaintPropagation \ 26 | -enable-checker alpha.unix.Chroot \ 27 | -enable-checker alpha.unix.PthreadLock \ 28 | -enable-checker alpha.unix.SimpleStream \ 29 | -enable-checker alpha.unix.Stream \ 30 | -enable-checker alpha.unix.cstring.BufferOverlap \ 31 | -enable-checker alpha.unix.cstring.NotNullTerminated \ 32 | -enable-checker alpha.unix.cstring.OutOfBounds \ 33 | -enable-checker llvm.Conventions \ 34 | -enable-checker nullability.NullableDereferenced \ 35 | -enable-checker nullability.NullablePassedToNonnull \ 36 | -enable-checker nullability.NullablePassedToNonnull \ 37 | -enable-checker optin.performance.Padding \ 38 | -enable-checker security.FloatLoopCounter \ 39 | -enable-checker security.insecureAPI.rand \ 40 | -enable-checker security.insecureAPI.strcpy \ 41 | \ 42 | '$CC -c dev_all.c' 43 | -------------------------------------------------------------------------------- /dev_clang_warns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | # 3 | # Build with lots of Clang warnings enabled. 4 | # 5 | TARGET=dev_all.c 6 | 7 | # Adjust to suit: 8 | LLVM=$HOME/llvm 9 | CLANG=$LLVM/install/bin/clang 10 | 11 | $CLANG -Weverything -Wno-padded -Wno-format-non-iso -Wno-cast-align \ 12 | -Wno-disabled-macro-expansion -Wno-used-but-marked-unused \ 13 | -Wno-reserved-id-macro \ 14 | -O -c $TARGET 15 | -------------------------------------------------------------------------------- /dev_gcc_warns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | # 3 | # Build with lots of GCC warnings enabled. 4 | # 5 | TARGET=dev_all.c 6 | 7 | gcc -O -c -fstrict-aliasing --all-warnings --extra-warnings $TARGET 8 | -------------------------------------------------------------------------------- /dns.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * dns.c: synchronous DNS in a child process. 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | 10 | #include "cdefs.h" 11 | #include "cap.h" 12 | #include "conv.h" 13 | #include "decode.h" 14 | #include "dns.h" 15 | #include "err.h" 16 | #include "hosts_db.h" 17 | #include "queue.h" 18 | #include "str.h" 19 | #include "tree.h" 20 | #include "bsd.h" /* for setproctitle, strlcpy */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #ifdef __NetBSD__ 34 | # define gethostbyaddr(addr, len, type) \ 35 | gethostbyaddr((const char *)(addr), len, type) 36 | #endif 37 | 38 | static void dns_main(void) _noreturn_; /* the child process runs this */ 39 | 40 | #define CHILD 0 /* child process uses this socket */ 41 | #define PARENT 1 42 | static int dns_sock[2]; 43 | static pid_t pid = -1; 44 | 45 | struct dns_reply { 46 | struct addr addr; 47 | int error; /* for gai_strerror(), or 0 if no error */ 48 | char name[256]; /* http://tools.ietf.org/html/rfc1034#section-3.1 */ 49 | }; 50 | 51 | void 52 | dns_init(const char *privdrop_user) 53 | { 54 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, dns_sock) == -1) 55 | err(1, "socketpair"); 56 | 57 | pid = fork(); 58 | if (pid == -1) 59 | err(1, "fork"); 60 | 61 | if (pid == 0) { 62 | /* We are the child. */ 63 | privdrop(NULL /* don't chroot */, privdrop_user); 64 | close(dns_sock[PARENT]); 65 | dns_sock[PARENT] = -1; 66 | daemonize_finish(); /* drop our copy of the lifeline! */ 67 | if (signal(SIGUSR1, SIG_IGN) == SIG_ERR) 68 | errx(1, "signal(SIGUSR1, ignore) failed"); 69 | cap_free_args(); 70 | dns_main(); 71 | } else { 72 | /* We are the parent. */ 73 | close(dns_sock[CHILD]); 74 | dns_sock[CHILD] = -1; 75 | fd_set_nonblock(dns_sock[PARENT]); 76 | verbosef("DNS child has PID %d", pid); 77 | } 78 | } 79 | 80 | void 81 | dns_stop(void) 82 | { 83 | if (pid == -1) 84 | return; /* no child was started */ 85 | close(dns_sock[PARENT]); 86 | if (kill(pid, SIGINT) == -1) 87 | err(1, "kill"); 88 | verbosef("dns_stop() waiting for child"); 89 | if (waitpid(pid, NULL, 0) == -1) 90 | err(1, "waitpid"); 91 | verbosef("dns_stop() done waiting for child"); 92 | } 93 | 94 | struct tree_rec { 95 | RB_ENTRY(tree_rec) ptree; 96 | struct addr ip; 97 | }; 98 | 99 | static int 100 | tree_cmp(struct tree_rec *a, struct tree_rec *b) 101 | { 102 | if (a->ip.family != b->ip.family) 103 | /* Sort IPv4 to the left of IPv6. */ 104 | return ((a->ip.family == IPv4) ? -1 : +1); 105 | 106 | if (a->ip.family == IPv4) 107 | return (memcmp(&a->ip.ip.v4, &b->ip.ip.v4, sizeof(a->ip.ip.v4))); 108 | else { 109 | assert(a->ip.family == IPv6); 110 | return (memcmp(&a->ip.ip.v6, &b->ip.ip.v6, sizeof(a->ip.ip.v6))); 111 | } 112 | } 113 | 114 | static RB_HEAD(tree_t, tree_rec) ip_tree = RB_INITIALIZER(&tree_rec); 115 | RB_GENERATE_STATIC(tree_t, tree_rec, ptree, tree_cmp) 116 | 117 | void 118 | dns_queue(const struct addr *const ipaddr) 119 | { 120 | struct tree_rec *rec; 121 | ssize_t num_w; 122 | 123 | if (pid == -1) 124 | return; /* no child was started - we're not doing any DNS */ 125 | 126 | if ((ipaddr->family != IPv4) && (ipaddr->family != IPv6)) { 127 | verbosef("dns_queue() for unknown family %d", ipaddr->family); 128 | return; 129 | } 130 | 131 | rec = xmalloc(sizeof(*rec)); 132 | memcpy(&rec->ip, ipaddr, sizeof(rec->ip)); 133 | 134 | if (RB_INSERT(tree_t, &ip_tree, rec) != NULL) { 135 | /* Already queued - this happens seldom enough that we don't care about 136 | * the performance hit of needlessly malloc()ing. */ 137 | verbosef("already queued %s", addr_to_str(ipaddr)); 138 | free(rec); 139 | return; 140 | } 141 | 142 | num_w = write(dns_sock[PARENT], ipaddr, sizeof(*ipaddr)); /* won't block */ 143 | if (num_w == 0) 144 | warnx("dns_queue: write: ignoring end of file"); 145 | else if (num_w == -1) 146 | warn("dns_queue: ignoring write error"); 147 | else if (num_w != sizeof(*ipaddr)) 148 | err(1, "dns_queue: wrote %zu instead of %zu", num_w, sizeof(*ipaddr)); 149 | } 150 | 151 | static void 152 | dns_unqueue(const struct addr *const ipaddr) 153 | { 154 | struct tree_rec tmp, *rec; 155 | 156 | memcpy(&tmp.ip, ipaddr, sizeof(tmp.ip)); 157 | if ((rec = RB_FIND(tree_t, &ip_tree, &tmp)) != NULL) { 158 | RB_REMOVE(tree_t, &ip_tree, rec); 159 | free(rec); 160 | } 161 | else 162 | verbosef("couldn't unqueue %s - not in queue!", addr_to_str(ipaddr)); 163 | } 164 | 165 | /* 166 | * Returns non-zero if result waiting, stores IP and name into given pointers 167 | * (name buffer is allocated by dns_poll) 168 | */ 169 | static int 170 | dns_get_result(struct addr *ipaddr, char **name) 171 | { 172 | struct dns_reply reply; 173 | ssize_t numread; 174 | 175 | numread = read(dns_sock[PARENT], &reply, sizeof(reply)); 176 | if (numread == -1) { 177 | if (errno == EAGAIN) 178 | return (0); /* no input waiting */ 179 | else 180 | goto error; 181 | } 182 | if (numread == 0) 183 | goto error; /* EOF */ 184 | if (numread != sizeof(reply)) 185 | errx(1, "dns_get_result read got %zu, expected %zu", 186 | numread, sizeof(reply)); 187 | 188 | /* Return successful reply. */ 189 | memcpy(ipaddr, &reply.addr, sizeof(*ipaddr)); 190 | if (reply.error != 0) { 191 | /* Identify common special cases. */ 192 | const char *type = "none"; 193 | 194 | if (reply.addr.family == IPv6) { 195 | if (IN6_IS_ADDR_LINKLOCAL(&reply.addr.ip.v6)) 196 | type = "link-local"; 197 | else if (IN6_IS_ADDR_SITELOCAL(&reply.addr.ip.v6)) 198 | type = "site-local"; 199 | else if (IN6_IS_ADDR_MULTICAST(&reply.addr.ip.v6)) 200 | type = "multicast"; 201 | } else { 202 | assert(reply.addr.family == IPv4); 203 | if (IN_MULTICAST(htonl(reply.addr.ip.v4))) 204 | type = "multicast"; 205 | } 206 | xasprintf(name, "(%s)", type); 207 | } 208 | else /* Correctly resolved name. */ 209 | *name = xstrdup(reply.name); 210 | 211 | dns_unqueue(&reply.addr); 212 | return (1); 213 | 214 | error: 215 | warn("dns_get_result: ignoring read error"); 216 | /* FIXME: re-align to stream? restart dns child? */ 217 | return (0); 218 | } 219 | 220 | void 221 | dns_poll(void) 222 | { 223 | struct addr ip; 224 | char *name; 225 | 226 | if (pid == -1) 227 | return; /* no child was started - we're not doing any DNS */ 228 | 229 | while (dns_get_result(&ip, &name)) { 230 | /* push into hosts_db */ 231 | struct bucket *b = host_find(&ip); 232 | 233 | if (b == NULL) { 234 | verbosef("resolved %s to %s but it's not in the DB!", 235 | addr_to_str(&ip), name); 236 | return; 237 | } 238 | if (b->u.host.dns != NULL) { 239 | verbosef("resolved %s to %s but it's already in the DB!", 240 | addr_to_str(&ip), name); 241 | return; 242 | } 243 | b->u.host.dns = name; 244 | } 245 | } 246 | 247 | /* ------------------------------------------------------------------------ */ 248 | 249 | struct qitem { 250 | STAILQ_ENTRY(qitem) entries; 251 | struct addr ip; 252 | }; 253 | 254 | static STAILQ_HEAD(qhead, qitem) queue = STAILQ_HEAD_INITIALIZER(queue); 255 | 256 | static void 257 | enqueue(const struct addr *const ip) 258 | { 259 | struct qitem *i; 260 | 261 | i = xmalloc(sizeof(*i)); 262 | memcpy(&i->ip, ip, sizeof(i->ip)); 263 | STAILQ_INSERT_TAIL(&queue, i, entries); 264 | verbosef("DNS: enqueued %s", addr_to_str(ip)); 265 | } 266 | 267 | /* Return non-zero and populate pointer if queue isn't empty. */ 268 | static int 269 | dequeue(struct addr *ip) 270 | { 271 | struct qitem *i; 272 | 273 | i = STAILQ_FIRST(&queue); 274 | if (i == NULL) 275 | return (0); 276 | STAILQ_REMOVE_HEAD(&queue, entries); 277 | memcpy(ip, &i->ip, sizeof(*ip)); 278 | free(i); 279 | verbosef("DNS: dequeued %s", addr_to_str(ip)); 280 | return 1; 281 | } 282 | 283 | static void 284 | xwrite(const int d, const void *buf, const size_t nbytes) 285 | { 286 | ssize_t ret = write(d, buf, nbytes); 287 | 288 | if (ret == -1) 289 | err(1, "write"); 290 | if (ret != (ssize_t)nbytes) 291 | err(1, "wrote %d bytes instead of all %d bytes", (int)ret, (int)nbytes); 292 | } 293 | 294 | static void 295 | dns_main(void) 296 | { 297 | struct addr ip; 298 | 299 | setproctitle("DNS child"); 300 | fd_set_nonblock(dns_sock[CHILD]); 301 | verbosef("DNS child entering main DNS loop"); 302 | for (;;) { 303 | int blocking; 304 | 305 | if (STAILQ_EMPTY(&queue)) { 306 | blocking = 1; 307 | fd_set_block(dns_sock[CHILD]); 308 | verbosef("entering blocking read loop"); 309 | } else { 310 | blocking = 0; 311 | fd_set_nonblock(dns_sock[CHILD]); 312 | verbosef("non-blocking poll"); 313 | } 314 | for (;;) { 315 | /* While we have input to process... */ 316 | ssize_t numread = read(dns_sock[CHILD], &ip, sizeof(ip)); 317 | if (numread == 0) 318 | exit(0); /* end of file, nothing more to do here. */ 319 | if (numread == -1) { 320 | if (!blocking && (errno == EAGAIN)) 321 | break; /* ran out of input */ 322 | /* else error */ 323 | err(1, "DNS: read failed"); 324 | } 325 | if (numread != sizeof(ip)) 326 | err(1, "DNS: read got %zu bytes, expecting %zu", 327 | numread, sizeof(ip)); 328 | enqueue(&ip); 329 | if (blocking) { 330 | /* After one blocking read, become non-blocking so that when we 331 | * run out of input we fall through to queue processing. 332 | */ 333 | blocking = 0; 334 | fd_set_nonblock(dns_sock[CHILD]); 335 | } 336 | } 337 | 338 | /* Process queue. */ 339 | if (dequeue(&ip)) { 340 | struct dns_reply reply; 341 | struct sockaddr_in sin; 342 | struct sockaddr_in6 sin6; 343 | struct hostent *he; 344 | char host[NI_MAXHOST]; 345 | int ret, flags; 346 | 347 | reply.addr = ip; 348 | flags = NI_NAMEREQD; 349 | # ifdef NI_IDN 350 | flags |= NI_IDN; 351 | # endif 352 | switch (ip.family) { 353 | case IPv4: 354 | sin.sin_family = AF_INET; 355 | sin.sin_addr.s_addr = ip.ip.v4; 356 | ret = getnameinfo((struct sockaddr *) &sin, sizeof(sin), 357 | host, sizeof(host), NULL, 0, flags); 358 | if (ret == EAI_FAMILY) { 359 | verbosef("getnameinfo error %s, trying gethostbyname", 360 | gai_strerror(ret)); 361 | he = gethostbyaddr(&sin.sin_addr.s_addr, 362 | sizeof(sin.sin_addr.s_addr), sin.sin_family); 363 | if (he == NULL) { 364 | ret = EAI_FAIL; 365 | verbosef("gethostbyname error %s", hstrerror(h_errno)); 366 | } else { 367 | ret = 0; 368 | strlcpy(host, he->h_name, sizeof(host)); 369 | } 370 | } 371 | break; 372 | case IPv6: 373 | sin6.sin6_family = AF_INET6; 374 | memcpy(&sin6.sin6_addr, &ip.ip.v6, sizeof(sin6.sin6_addr)); 375 | ret = getnameinfo((struct sockaddr *) &sin6, sizeof(sin6), 376 | host, sizeof(host), NULL, 0, flags); 377 | break; 378 | default: 379 | errx(1, "unexpected ip.family = %d", ip.family); 380 | } 381 | 382 | if (ret != 0) { 383 | reply.name[0] = '\0'; 384 | reply.error = ret; 385 | } else { 386 | assert(sizeof(reply.name) > sizeof(char *)); /* not just a ptr */ 387 | strlcpy(reply.name, host, sizeof(reply.name)); 388 | reply.error = 0; 389 | } 390 | fd_set_block(dns_sock[CHILD]); 391 | xwrite(dns_sock[CHILD], &reply, sizeof(reply)); 392 | verbosef("DNS: %s is \"%s\".", addr_to_str(&reply.addr), 393 | (ret == 0) ? reply.name : gai_strerror(ret)); 394 | } 395 | } 396 | } 397 | 398 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 399 | -------------------------------------------------------------------------------- /dns.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2011 Emil Mikulic. 3 | * 4 | * dns.h: synchronous DNS in a child process. 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | 10 | struct addr; 11 | 12 | void dns_init(const char *privdrop_user); 13 | void dns_stop(void); 14 | void dns_queue(const struct addr *const ipaddr); 15 | void dns_poll(void); 16 | 17 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 18 | -------------------------------------------------------------------------------- /err.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2012 Emil Mikulic. 3 | * 4 | * err.c: BSD-like err() and warn() functions 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "cdefs.h" 20 | #include "err.h" 21 | #include "opt.h" 22 | #include "pidfile.h" 23 | #include "bsd.h" /* for strlcpy */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | static void to_syslog(const char *type, const int want_err, 34 | const char *format, va_list va) _printflike_(3, 0); 35 | 36 | static void 37 | to_syslog(const char *type, const int want_err, 38 | const char *format, va_list va) 39 | { 40 | char buf[512]; 41 | size_t pos = 0; 42 | int saved_errno = errno; 43 | 44 | if (type != NULL) { 45 | strlcpy(buf, type, sizeof(buf)); 46 | pos = strlen(buf); 47 | } 48 | vsnprintf(buf+pos, sizeof(buf)-pos, format, va); 49 | if (want_err) { 50 | strlcat(buf, ": ", sizeof(buf)); 51 | strlcat(buf, strerror(saved_errno), sizeof(buf)); 52 | } 53 | syslog(LOG_DEBUG, "%s", buf); 54 | } 55 | 56 | void 57 | err(const int code, const char *format, ...) 58 | { 59 | va_list va; 60 | 61 | va_start(va, format); 62 | if (opt_want_syslog) 63 | to_syslog("ERROR: ", 1, format, va); 64 | else { 65 | fprintf(stderr, "%5d: error: ", (int)getpid()); 66 | vfprintf(stderr, format, va); 67 | fprintf(stderr, ": %s\n", strerror(errno)); 68 | } 69 | va_end(va); 70 | pidfile_unlink(); 71 | exit(code); 72 | } 73 | 74 | void 75 | errx(const int code, const char *format, ...) 76 | { 77 | va_list va; 78 | 79 | va_start(va, format); 80 | if (opt_want_syslog) 81 | to_syslog("ERROR: ", 0, format, va); 82 | else { 83 | fprintf(stderr, "%5d: error: ", (int)getpid()); 84 | vfprintf(stderr, format, va); 85 | fprintf(stderr, "\n"); 86 | } 87 | va_end(va); 88 | pidfile_unlink(); 89 | exit(code); 90 | } 91 | 92 | void 93 | warn(const char *format, ...) 94 | { 95 | va_list va; 96 | 97 | va_start(va, format); 98 | if (opt_want_syslog) 99 | to_syslog("WARNING: ", 1, format, va); 100 | else { 101 | fprintf(stderr, "%5d: warning: ", (int)getpid()); 102 | vfprintf(stderr, format, va); 103 | fprintf(stderr, ": %s\n", strerror(errno)); 104 | } 105 | va_end(va); 106 | } 107 | 108 | void 109 | warnx(const char *format, ...) 110 | { 111 | va_list va; 112 | 113 | va_start(va, format); 114 | if (opt_want_syslog) 115 | to_syslog("WARNING: ", 0, format, va); 116 | else { 117 | fprintf(stderr, "%5d: warning: ", (int)getpid()); 118 | vfprintf(stderr, format, va); 119 | fprintf(stderr, "\n"); 120 | } 121 | va_end(va); 122 | } 123 | 124 | /* We interlock verbosef() between processes by using a pipe with a single 125 | * byte in it. This pipe must be initialized before the first fork() in order 126 | * to work. Then, verbosef() will block on a read() until it is able to 127 | * retrieve the byte. After doing its business, it will put a byte back into 128 | * the pipe. 129 | * 130 | * This is completely silly and largely unnecessary. 131 | */ 132 | static int inited = 0; 133 | static int lockpipe[2]; 134 | 135 | static void unlock(void); 136 | 137 | static void 138 | initlock(void) 139 | { 140 | if (pipe(lockpipe) == -1) 141 | err(1, "pipe(lockpipe)"); 142 | inited = 1; 143 | unlock(); 144 | } 145 | 146 | static void 147 | lock(void) 148 | { 149 | char buf[1]; 150 | 151 | if (!inited) initlock(); 152 | if (read(lockpipe[0], buf, 1) != 1) { 153 | fprintf(stderr, "lock failed!\n"); 154 | pidfile_unlink(); 155 | exit(1); 156 | } 157 | } 158 | 159 | static void 160 | unlock(void) 161 | { 162 | char c = 0; 163 | 164 | if (write(lockpipe[1], &c, 1) != 1) { 165 | fprintf(stderr, "unlock failed!\n"); 166 | pidfile_unlink(); 167 | exit(1); 168 | } 169 | } 170 | 171 | void 172 | verbosef(const char *format, ...) 173 | { 174 | va_list va; 175 | 176 | if (!opt_want_verbose) return; 177 | va_start(va, format); 178 | if (opt_want_syslog) 179 | to_syslog(NULL, 0, format, va); 180 | else { 181 | lock(); 182 | fprintf(stderr, "darkstat (%05d): ", (int)getpid()); 183 | vfprintf(stderr, format, va); 184 | fprintf(stderr, "\n"); 185 | unlock(); 186 | } 187 | va_end(va); 188 | } 189 | 190 | void 191 | dverbosef(const char *format _unused_, ...) 192 | { 193 | /* disabled / do-nothing verbosef */ 194 | } 195 | 196 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 197 | -------------------------------------------------------------------------------- /err.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * err.h: BSD-like err() and warn() functions 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "cdefs.h" 20 | 21 | void err(const int code, const char *format, ...) 22 | _noreturn_ _printflike_(2, 3); 23 | void errx(const int code, const char *format, ...) 24 | _noreturn_ _printflike_(2, 3); 25 | 26 | void warn(const char *format, ...) _printflike_(1, 2); 27 | void warnx(const char *format, ...) _printflike_(1, 2); 28 | 29 | void verbosef(const char *format, ...) _printflike_(1, 2); 30 | void dverbosef(const char *format _unused_, ...) _printflike_(1, 2); 31 | 32 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 33 | -------------------------------------------------------------------------------- /export-format.txt: -------------------------------------------------------------------------------- 1 | The darkstat export format was designed by Ben Stewart. 2 | Note that all integers are stored in network order (big-endian). 3 | 4 | FILE HEADER 0xDA314159 darkstat export format 5 | SECTION HEADER 0xDA 'H' 'S' 0x01 hosts_db ver1 6 | HOST COUNT 0x00000001 1 host follows 7 | For each host: 8 | HOST HEADER 'H' 'S' 'T' 0x04 host ver4 9 | ADDRESS FAMILY 0x04 Either 4 or 6. 10 | IPv4 ADDR 0x0A010101 IPv4 10.1.1.1 11 | or for 0x06: 12 | IPv6 ADDR 0x0000 0000 0000 0000 0000 0000 0000 0001 13 | meaning IPv6 ::1 14 | LASTSEEN 0x0000 0000 4800 0123 64-bit time_t meaning: 15 | 2008-04-12 00:24:03 UTC 16 | MACADDR 0x001122334455 00:11:22:33:44:55 17 | HOSTNAME 0x09 "localhost" 9 is the string length 18 | IN 0x0000000000123456 Bytes in: 1193046 19 | OUT 0x0000000000789ABC Bytes out: 7903932 20 | PROTOS DATA 'P' start ip proto data 21 | IP PROTO COUNT 0x03 3 ip_proto entries 22 | IP PROTO 0x06 tcp 23 | IN 0x0000000000123456 Bytes in: 1193046 24 | OUT 0x0000000000789ABC Bytes out: 7903932 25 | IP PROTO 0x11 udp 26 | IN 0x0000000000000444 Bytes in: 1092 27 | OUT 0x0000000000000555 Bytes out: 1365 28 | IP PROTO 0x01 icmp 29 | IN 0x0000000000000001 Bytes in: 1 30 | OUT 0x0000000000000002 Bytes out: 2 31 | TCP DATA 'T' start tcp proto data 32 | TCP PROTO COUNT 0x0001 1 tcp_proto entry 33 | PORT 0x0050 http (port 80) 34 | SYN COUNT 0x0000000000000003 SYNs: 3 35 | IN 0x0000000000000001 Bytes in: 1 36 | OUT 0x0000000000000002 Bytes out: 2 37 | UDP DATA 'U' start udp proto data 38 | UDP PROTO COUNT 0x0001 1 udp_proto entry 39 | PORT 0x0045 tftp (port 69) 40 | IN 0x0000000000000001 Bytes in: 1 41 | OUT 0x0000000000000002 Bytes out: 2 42 | REMOTE TCP DATA 't' (as above) 43 | REMOTE UDP DATA 'u' (as above) 44 | SECTION HEADER 0xDA 'G' 'R' 0x01 graph_db ver1 45 | LAST_TIME (time_t as 64-bit uint) 46 | For each of 4 graphs: (60 seconds, 60 minutes, 24 hours, 31 days) 47 | 8 bits - number of bars in this graph 48 | 8 bits - index of last_time bar, in the range [0:n_bars) 49 | For each bar: 50 | 64 bits - bytes in 51 | 64 bits - bytes out 52 | 53 | Host header version 1 is just version 2 without the lastseen time. 54 | 55 | Host header version 2 is just version 3 without the address family 56 | byte (or the possibility of an IPv6 address). 57 | 58 | Host header version 3 is just version 4 without the remote TCP and UDP ports. 59 | -------------------------------------------------------------------------------- /graph_db.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2006-2011 Emil Mikulic. 3 | * 4 | * graph_db.h: round robin database for graph data 5 | */ 6 | #ifndef __DARKSTAT_GRAPH_DB_H 7 | #define __DARKSTAT_GRAPH_DB_H 8 | 9 | #include /* for uint64_t on Linux and OS X */ 10 | 11 | enum graph_dir { 12 | MIN_GRAPH_DIR = 1, 13 | GRAPH_IN = 1, 14 | GRAPH_OUT = 2, 15 | MAX_GRAPH_DIR = 2 16 | }; 17 | 18 | void graph_init(void); 19 | void graph_reset(void); 20 | void graph_free(void); 21 | void graph_acct(uint64_t amount, enum graph_dir dir); 22 | void graph_rotate(void); 23 | int graph_import(const int fd); 24 | int graph_export(const int fd); 25 | 26 | struct str *html_front_page(void); 27 | struct str *xml_graphs(void); 28 | 29 | #endif 30 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 31 | -------------------------------------------------------------------------------- /hosts_db.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * hosts_db.h: database of hosts, ports, protocols. 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | #ifndef __DARKSTAT_HOSTS_DB_H 10 | #define __DARKSTAT_HOSTS_DB_H 11 | 12 | #include /* for uint64_t */ 13 | 14 | #include "addr.h" 15 | 16 | struct hashtable; 17 | 18 | struct host { 19 | struct addr addr; 20 | char *dns; 21 | uint8_t mac_addr[6]; 22 | /* last_seen_mono is converted to/from time_t in export/import. 23 | * It can be negative (due to machine reboots). 24 | */ 25 | int64_t last_seen_mono; 26 | struct hashtable *ports_tcp; 27 | struct hashtable *ports_tcp_remote; 28 | struct hashtable *ports_udp; 29 | struct hashtable *ports_udp_remote; 30 | struct hashtable *ip_protos; 31 | }; 32 | 33 | struct port_tcp { 34 | uint16_t port; 35 | uint64_t syn; 36 | }; 37 | 38 | struct port_udp { 39 | uint16_t port; 40 | }; 41 | 42 | struct ip_proto { 43 | uint8_t proto; 44 | }; 45 | 46 | struct bucket { 47 | struct bucket *next; 48 | uint64_t in, out, total; 49 | union { 50 | struct host host; 51 | struct port_tcp port_tcp; 52 | struct port_udp port_udp; 53 | struct ip_proto ip_proto; 54 | } u; 55 | }; 56 | 57 | enum sort_dir { IN, OUT, TOTAL, LASTSEEN }; 58 | 59 | extern int hosts_db_show_macs; 60 | 61 | void hosts_db_init(void); 62 | void hosts_db_reduce(void); 63 | void hosts_db_reset(void); 64 | void hosts_db_free(void); 65 | int hosts_db_import(const int fd); 66 | int hosts_db_export(const int fd); 67 | 68 | struct bucket *host_find(const struct addr *const a); /* can return NULL */ 69 | struct bucket *host_get(const struct addr *const a); 70 | struct bucket *host_get_port_tcp(struct bucket *host, const uint16_t port); 71 | struct bucket *host_get_port_tcp_remote(struct bucket *host, 72 | const uint16_t port); 73 | struct bucket *host_get_port_udp(struct bucket *host, const uint16_t port); 74 | struct bucket *host_get_port_udp_remote(struct bucket *host, 75 | const uint16_t port); 76 | struct bucket *host_get_ip_proto(struct bucket *host, const uint8_t proto); 77 | 78 | /* Web pages. */ 79 | struct str *html_hosts(const char *uri, const char *query); 80 | struct str *text_metrics(); 81 | 82 | /* From hosts_sort */ 83 | void qsort_buckets(const struct bucket **a, size_t n, 84 | size_t left, size_t right, const enum sort_dir d); 85 | 86 | #endif /* __DARKSTAT_HOSTS_DB_H */ 87 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 88 | -------------------------------------------------------------------------------- /hosts_sort.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2012 Emil Mikulic. 3 | * 4 | * hosts_sort.c: quicksort a table of buckets. 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | 10 | #include "cdefs.h" 11 | #include "err.h" 12 | #include "hosts_db.h" 13 | 14 | static int cmp_u64(const uint64_t a, const uint64_t b) { 15 | if (a < b) return (1); 16 | if (a > b) return (-1); 17 | return (0); 18 | } 19 | 20 | static int cmp_i64(const int64_t a, const int64_t b) { 21 | if (a < b) return (1); 22 | if (a > b) return (-1); 23 | return (0); 24 | } 25 | 26 | /* Comparator for sorting 'struct bucket' */ 27 | static int cmp(const struct bucket * const *x, const struct bucket * const *y, 28 | const enum sort_dir dir) { 29 | switch (dir) { 30 | case IN: 31 | return cmp_u64((*x)->in, (*y)->in); 32 | case OUT: 33 | return cmp_u64((*x)->out, (*y)->out); 34 | case TOTAL: 35 | return cmp_u64((*x)->total, (*y)->total); 36 | case LASTSEEN: 37 | return cmp_i64((*x)->u.host.last_seen_mono, 38 | (*y)->u.host.last_seen_mono); 39 | default: 40 | errx(1, "cmp: unknown direction: %d", dir); 41 | } 42 | } 43 | 44 | /* 45 | * The quicksort code is derived from FreeBSD's 46 | * src/lib/libc/stdlib/qsort.c v1.12 47 | */ 48 | 49 | /*- 50 | * Copyright (c) 1992, 1993 51 | * The Regents of the University of California. All rights reserved. 52 | * 53 | * Redistribution and use in source and binary forms, with or without 54 | * modification, are permitted provided that the following conditions 55 | * are met: 56 | * 1. Redistributions of source code must retain the above copyright 57 | * notice, this list of conditions and the following disclaimer. 58 | * 2. Redistributions in binary form must reproduce the above copyright 59 | * notice, this list of conditions and the following disclaimer in the 60 | * documentation and/or other materials provided with the distribution. 61 | * 3. All advertising materials mentioning features or use of this software 62 | * must display the following acknowledgement: 63 | * This product includes software developed by the University of 64 | * California, Berkeley and its contributors. 65 | * 4. Neither the name of the University nor the names of its contributors 66 | * may be used to endorse or promote products derived from this software 67 | * without specific prior written permission. 68 | * 69 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 70 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 71 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 72 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 73 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 74 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 75 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 76 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 77 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 78 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 79 | * SUCH DAMAGE. 80 | */ 81 | 82 | static void 83 | vecswap(const struct bucket **pi, const struct bucket **pj, int n) 84 | { 85 | if (n <= 0) 86 | return; 87 | 88 | do { 89 | const struct bucket *t = *pi; 90 | *pi++ = *pj; 91 | *pj++ = t; 92 | } while (--n > 0); 93 | } 94 | 95 | #define swap(a, b) { \ 96 | const struct bucket *t = *(const struct bucket **)(a); \ 97 | *(const struct bucket **)(a) = *(const struct bucket **)(b); \ 98 | *(const struct bucket **)(b) = t; \ 99 | } 100 | 101 | static const struct bucket ** 102 | med3(const struct bucket **a, 103 | const struct bucket **b, 104 | const struct bucket **c, 105 | const enum sort_dir dir) 106 | { 107 | return (cmp(a, b, dir) < 0) 108 | ? (cmp(b, c, dir) < 0 ? b : (cmp(a, c, dir) < 0 ? c : a )) 109 | : (cmp(b, c, dir) > 0 ? b : (cmp(a, c, dir) < 0 ? a : c )); 110 | } 111 | 112 | /* Partial sort - only sort elements in the range [left:right] */ 113 | void 114 | qsort_buckets(const struct bucket **a, size_t n, 115 | size_t left, size_t right, 116 | const enum sort_dir dir) 117 | { 118 | const struct bucket **pa, **pb, **pc, **pd, **pl, **pm, **pn; 119 | int d, r, swap_cnt; 120 | 121 | loop: 122 | swap_cnt = 0; 123 | if (n < 7) { 124 | for (pm = a+1; pm < a+n; pm++) 125 | for (pl = pm; 126 | (pl > a) && (cmp(pl-1, pl, dir) > 0); 127 | pl--) 128 | swap(pl, pl-1); 129 | return; 130 | } 131 | pm = a + (n / 2); 132 | if (n > 7) { 133 | pl = a; 134 | pn = a + (n - 1); 135 | if (n > 40) { 136 | d = (n / 8); 137 | pl = med3(pl, pl + d, pl + 2 * d, dir); 138 | pm = med3(pm - d, pm, pm + d, dir); 139 | pn = med3(pn - 2 * d, pn - d, pn, dir); 140 | } 141 | pm = med3(pl, pm, pn, dir); 142 | } 143 | swap(a, pm); 144 | pa = pb = a + 1; 145 | 146 | pc = pd = a + (n - 1); 147 | for (;;) { 148 | while (pb <= pc && (r = cmp(pb, a, dir)) <= 0) { 149 | if (r == 0) { 150 | swap_cnt = 1; 151 | swap(pa, pb); 152 | pa++; 153 | } 154 | pb++; 155 | } 156 | while (pb <= pc && (r = cmp(pc, a, dir)) >= 0) { 157 | if (r == 0) { 158 | swap_cnt = 1; 159 | swap(pc, pd); 160 | pd--; 161 | } 162 | pc--; 163 | } 164 | if (pb > pc) 165 | break; 166 | swap(pb, pc); 167 | swap_cnt = 1; 168 | pb++; 169 | pc--; 170 | } 171 | if (swap_cnt == 0) { /* Switch to insertion sort */ 172 | for (pm = a + 1; pm < a+n; pm++) 173 | for (pl = pm; 174 | (pl > a) && (cmp(pl-1, pl, dir) > 0); 175 | pl--) 176 | swap(pl, pl-1); 177 | return; 178 | } 179 | 180 | pn = a + n; 181 | r = MIN(pa - a, pb - pa); 182 | vecswap(a, pb - r, r); 183 | r = MIN(pd - pc, pn - pd - 1); 184 | vecswap(pb, pn - r, r); 185 | if (((r = pb - pa) > 1) && ((unsigned)r >= left)) 186 | qsort_buckets(a, r, left, right, dir); 187 | if (((r = pd - pc) > 1) && (n - r <= right)) { 188 | /* Iterate rather than recurse to save stack space */ 189 | if (n - r > left) 190 | left = 0; 191 | else 192 | left -= n - r; 193 | right -= n - r; 194 | a += n - r; 195 | n = r; 196 | goto loop; 197 | } 198 | /* qsort(pn - r, r, cmp);*/ 199 | } 200 | 201 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 202 | -------------------------------------------------------------------------------- /html.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * 3 | * html.c: HTML header/footer templating for web interface. 4 | * copyright (c) 2006 Ben Stewart. 5 | * copyright (c) 2010 Malte S. Stretz. 6 | * 7 | * You may use, modify and redistribute this file under the terms of the 8 | * GNU General Public License version 2. (see COPYING.GPL) 9 | */ 10 | 11 | #include "config.h" 12 | #include "str.h" 13 | #include "html.h" 14 | #include "opt.h" 15 | 16 | #include 17 | 18 | static const char *relpaths[] = { 19 | ".", 20 | "..", 21 | "../.." 22 | }; 23 | 24 | void html_open(struct str *buf, const char *title, 25 | const unsigned int path_depth, const int want_graph_js) 26 | { 27 | const char *root; 28 | assert(path_depth < (sizeof(relpaths)/sizeof(*relpaths))); 29 | root = relpaths[path_depth]; 30 | 31 | str_appendf(buf, 32 | "\n" 33 | "\n" 34 | "\n" 35 | "%s (darkstat %s)\n" 36 | "\n" 37 | "\n" 38 | "\n" 40 | "\n", 41 | title, title_interfaces, root); 42 | 43 | if (want_graph_js) 44 | str_appendf(buf, 45 | "\n" 46 | , root); 47 | 48 | str_appendf(buf, 49 | "\n" 50 | "\n" 51 | "
\n" 52 | "
    " /* no whitespace (newlines) in list */ 53 | "
  • " PACKAGE_STRING "
  • " 54 | "
  • graphs
  • " 55 | "
  • hosts
  • " 56 | "
  • homepage
  • " 57 | "
\n" 58 | "
\n" 59 | "
\n" 60 | "

%s

\n" 61 | , root, root, title); 62 | } 63 | 64 | void html_close(struct str *buf) 65 | { 66 | str_append(buf, 67 | "
\n" 68 | "\n" 69 | "\n"); 70 | } 71 | 72 | /* vim:set ts=4 sw=4 tw=80 et: */ 73 | -------------------------------------------------------------------------------- /html.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * 3 | * html.h: HTML header/footer templating for web interface. 4 | * copyright (c) 2006 Ben Stewart. 5 | * copyright (c) 2010 Malte S. Stretz. 6 | */ 7 | 8 | struct str; 9 | 10 | void html_open(struct str *buf, const char *title, 11 | const unsigned int path_depth, const int want_graph_js); 12 | void html_close(struct str *buf); 13 | 14 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 15 | -------------------------------------------------------------------------------- /http.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2016 Emil Mikulic. 3 | * 4 | * http.h: embedded webserver. 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | void http_init_base(const char *url); 11 | void http_add_bindaddr(const char *bindaddr); 12 | void http_listen(const unsigned short bindport); 13 | void http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd, 14 | struct timeval *timeout, int *need_timeout); 15 | void http_poll(fd_set *read_set, fd_set *write_set); 16 | void http_stop(void); 17 | 18 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 19 | -------------------------------------------------------------------------------- /install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # install - install a program, script, or datafile 3 | 4 | scriptversion=2005-05-14.22 5 | 6 | # This originates from X11R5 (mit/util/scripts/install.sh), which was 7 | # later released in X11R6 (xc/config/util/install.sh) with the 8 | # following copyright and license. 9 | # 10 | # Copyright (C) 1994 X Consortium 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documentation files (the "Software"), to 14 | # deal in the Software without restriction, including without limitation the 15 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | # sell copies of the Software, and to permit persons to whom the Software is 17 | # furnished to do so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in 20 | # all copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 26 | # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- 27 | # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # Except as contained in this notice, the name of the X Consortium shall not 30 | # be used in advertising or otherwise to promote the sale, use or other deal- 31 | # ings in this Software without prior written authorization from the X Consor- 32 | # tium. 33 | # 34 | # 35 | # FSF changes to this file are in the public domain. 36 | # 37 | # Calling this script install-sh is preferred over install.sh, to prevent 38 | # `make' implicit rules from creating a file called install from it 39 | # when there is no Makefile. 40 | # 41 | # This script is compatible with the BSD install script, but was written 42 | # from scratch. It can only install one file at a time, a restriction 43 | # shared with many OS's install programs. 44 | 45 | # set DOITPROG to echo to test this script 46 | 47 | # Don't use :- since 4.3BSD and earlier shells don't like it. 48 | doit="${DOITPROG-}" 49 | 50 | # put in absolute paths if you don't have them in your path; or use env. vars. 51 | 52 | mvprog="${MVPROG-mv}" 53 | cpprog="${CPPROG-cp}" 54 | chmodprog="${CHMODPROG-chmod}" 55 | chownprog="${CHOWNPROG-chown}" 56 | chgrpprog="${CHGRPPROG-chgrp}" 57 | stripprog="${STRIPPROG-strip}" 58 | rmprog="${RMPROG-rm}" 59 | mkdirprog="${MKDIRPROG-mkdir}" 60 | 61 | chmodcmd="$chmodprog 0755" 62 | chowncmd= 63 | chgrpcmd= 64 | stripcmd= 65 | rmcmd="$rmprog -f" 66 | mvcmd="$mvprog" 67 | src= 68 | dst= 69 | dir_arg= 70 | dstarg= 71 | no_target_directory= 72 | 73 | usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE 74 | or: $0 [OPTION]... SRCFILES... DIRECTORY 75 | or: $0 [OPTION]... -t DIRECTORY SRCFILES... 76 | or: $0 [OPTION]... -d DIRECTORIES... 77 | 78 | In the 1st form, copy SRCFILE to DSTFILE. 79 | In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. 80 | In the 4th, create DIRECTORIES. 81 | 82 | Options: 83 | -c (ignored) 84 | -d create directories instead of installing files. 85 | -g GROUP $chgrpprog installed files to GROUP. 86 | -m MODE $chmodprog installed files to MODE. 87 | -o USER $chownprog installed files to USER. 88 | -s $stripprog installed files. 89 | -t DIRECTORY install into DIRECTORY. 90 | -T report an error if DSTFILE is a directory. 91 | --help display this help and exit. 92 | --version display version info and exit. 93 | 94 | Environment variables override the default commands: 95 | CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG 96 | " 97 | 98 | while test -n "$1"; do 99 | case $1 in 100 | -c) shift 101 | continue;; 102 | 103 | -d) dir_arg=true 104 | shift 105 | continue;; 106 | 107 | -g) chgrpcmd="$chgrpprog $2" 108 | shift 109 | shift 110 | continue;; 111 | 112 | --help) echo "$usage"; exit $?;; 113 | 114 | -m) chmodcmd="$chmodprog $2" 115 | shift 116 | shift 117 | continue;; 118 | 119 | -o) chowncmd="$chownprog $2" 120 | shift 121 | shift 122 | continue;; 123 | 124 | -s) stripcmd=$stripprog 125 | shift 126 | continue;; 127 | 128 | -t) dstarg=$2 129 | shift 130 | shift 131 | continue;; 132 | 133 | -T) no_target_directory=true 134 | shift 135 | continue;; 136 | 137 | --version) echo "$0 $scriptversion"; exit $?;; 138 | 139 | *) # When -d is used, all remaining arguments are directories to create. 140 | # When -t is used, the destination is already specified. 141 | test -n "$dir_arg$dstarg" && break 142 | # Otherwise, the last argument is the destination. Remove it from $@. 143 | for arg 144 | do 145 | if test -n "$dstarg"; then 146 | # $@ is not empty: it contains at least $arg. 147 | set fnord "$@" "$dstarg" 148 | shift # fnord 149 | fi 150 | shift # arg 151 | dstarg=$arg 152 | done 153 | break;; 154 | esac 155 | done 156 | 157 | if test -z "$1"; then 158 | if test -z "$dir_arg"; then 159 | echo "$0: no input file specified." >&2 160 | exit 1 161 | fi 162 | # It's OK to call `install-sh -d' without argument. 163 | # This can happen when creating conditional directories. 164 | exit 0 165 | fi 166 | 167 | for src 168 | do 169 | # Protect names starting with `-'. 170 | case $src in 171 | -*) src=./$src ;; 172 | esac 173 | 174 | if test -n "$dir_arg"; then 175 | dst=$src 176 | src= 177 | 178 | if test -d "$dst"; then 179 | mkdircmd=: 180 | chmodcmd= 181 | else 182 | mkdircmd=$mkdirprog 183 | fi 184 | else 185 | # Waiting for this to be detected by the "$cpprog $src $dsttmp" command 186 | # might cause directories to be created, which would be especially bad 187 | # if $src (and thus $dsttmp) contains '*'. 188 | if test ! -f "$src" && test ! -d "$src"; then 189 | echo "$0: $src does not exist." >&2 190 | exit 1 191 | fi 192 | 193 | if test -z "$dstarg"; then 194 | echo "$0: no destination specified." >&2 195 | exit 1 196 | fi 197 | 198 | dst=$dstarg 199 | # Protect names starting with `-'. 200 | case $dst in 201 | -*) dst=./$dst ;; 202 | esac 203 | 204 | # If destination is a directory, append the input filename; won't work 205 | # if double slashes aren't ignored. 206 | if test -d "$dst"; then 207 | if test -n "$no_target_directory"; then 208 | echo "$0: $dstarg: Is a directory" >&2 209 | exit 1 210 | fi 211 | dst=$dst/`basename "$src"` 212 | fi 213 | fi 214 | 215 | # This sed command emulates the dirname command. 216 | dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` 217 | 218 | # Make sure that the destination directory exists. 219 | 220 | # Skip lots of stat calls in the usual case. 221 | if test ! -d "$dstdir"; then 222 | defaultIFS=' 223 | ' 224 | IFS="${IFS-$defaultIFS}" 225 | 226 | oIFS=$IFS 227 | # Some sh's can't handle IFS=/ for some reason. 228 | IFS='%' 229 | set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` 230 | shift 231 | IFS=$oIFS 232 | 233 | pathcomp= 234 | 235 | while test $# -ne 0 ; do 236 | pathcomp=$pathcomp$1 237 | shift 238 | if test ! -d "$pathcomp"; then 239 | $mkdirprog "$pathcomp" 240 | # mkdir can fail with a `File exist' error in case several 241 | # install-sh are creating the directory concurrently. This 242 | # is OK. 243 | test -d "$pathcomp" || exit 244 | fi 245 | pathcomp=$pathcomp/ 246 | done 247 | fi 248 | 249 | if test -n "$dir_arg"; then 250 | $doit $mkdircmd "$dst" \ 251 | && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ 252 | && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ 253 | && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ 254 | && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } 255 | 256 | else 257 | dstfile=`basename "$dst"` 258 | 259 | # Make a couple of temp file names in the proper directory. 260 | dsttmp=$dstdir/_inst.$$_ 261 | rmtmp=$dstdir/_rm.$$_ 262 | 263 | # Trap to clean up those temp files at exit. 264 | trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 265 | trap '(exit $?); exit' 1 2 13 15 266 | 267 | # Copy the file name to the temp name. 268 | $doit $cpprog "$src" "$dsttmp" && 269 | 270 | # and set any options; do chmod last to preserve setuid bits. 271 | # 272 | # If any of these fail, we abort the whole thing. If we want to 273 | # ignore errors from any of these, just make sure not to ignore 274 | # errors from the above "$doit $cpprog $src $dsttmp" command. 275 | # 276 | { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ 277 | && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ 278 | && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ 279 | && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && 280 | 281 | # Now rename the file to the real destination. 282 | { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ 283 | || { 284 | # The rename failed, perhaps because mv can't rename something else 285 | # to itself, or perhaps because mv is so ancient that it does not 286 | # support -f. 287 | 288 | # Now remove or move aside any old file at destination location. 289 | # We try this two ways since rm can't unlink itself on some 290 | # systems and the destination file might be busy for other 291 | # reasons. In this case, the final cleanup might fail but the new 292 | # file should still install successfully. 293 | { 294 | if test -f "$dstdir/$dstfile"; then 295 | $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ 296 | || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ 297 | || { 298 | echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 299 | (exit 1); exit 1 300 | } 301 | else 302 | : 303 | fi 304 | } && 305 | 306 | # Now rename the file to the real destination. 307 | $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" 308 | } 309 | } 310 | fi || { (exit 1); exit 1; } 311 | done 312 | 313 | # The final little trick to "correctly" pass the exit status to the exit trap. 314 | { 315 | (exit 0); exit 0 316 | } 317 | 318 | # Local variables: 319 | # eval: (add-hook 'write-file-hooks 'time-stamp) 320 | # time-stamp-start: "scriptversion=" 321 | # time-stamp-format: "%:y-%02m-%02d.%02H" 322 | # time-stamp-end: "$" 323 | # End: 324 | -------------------------------------------------------------------------------- /linktypes.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2022 Emil Mikulic. 3 | * 4 | * linktypes.c: convert pcap linktype to a string name. 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | 21 | struct linktype_pair { 22 | int linktype; /* Returned by pcap_datalink(). */ 23 | const char* name; 24 | }; 25 | 26 | #define LT(name) { name, #name }, 27 | static const struct linktype_pair linktypes[] = { 28 | #include "linktypes_list.h" 29 | }; 30 | #undef LT 31 | 32 | const char* get_linktype_name(int linktype) { 33 | const int n = sizeof(linktypes) / sizeof(*linktypes); 34 | for (int i = 0; i < n; i++) { 35 | if (linktypes[i].linktype == linktype) return linktypes[i].name; 36 | } 37 | return "unknown"; 38 | } 39 | 40 | /* vim:set ts=2 sts=2 sw=2 tw=80 et: */ 41 | -------------------------------------------------------------------------------- /linktypes.h: -------------------------------------------------------------------------------- 1 | const char* get_linktype_name(int linktype); 2 | -------------------------------------------------------------------------------- /linktypes_test.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2022 Emil Mikulic. 3 | * 4 | * Permission to use, copy, modify, and distribute this file for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "linktypes.h" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | static int retcode = 0; 24 | 25 | static void test(int linktype, const char* expected) { 26 | const char* actual = get_linktype_name(linktype); 27 | if (strcmp(expected, actual) == 0) { 28 | printf("PASS: get_linktype_name(%d) = \"%s\"\n", 29 | linktype, expected); 30 | } else { 31 | printf("FAIL: get_linktype_name(%d) = \"%s\" (expected \"%s\")\n", 32 | linktype, actual, expected); 33 | retcode = 1; 34 | } 35 | } 36 | 37 | int main() { 38 | test(DLT_NULL, "DLT_NULL"); 39 | test(DLT_EN10MB, "DLT_EN10MB"); 40 | test(-123, "unknown"); 41 | return retcode; 42 | } 43 | 44 | /* vim:set ts=2 sts=2 sw=2 tw=80 et: */ 45 | -------------------------------------------------------------------------------- /localip.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2012 Emil Mikulic. 3 | * 4 | * localip.c: determine local IPs of an interface 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | 10 | #include "addr.h" 11 | #include "bsd.h" /* for strlcpy */ 12 | #include "config.h" /* for HAVE_IFADDRS_H */ 13 | #include "conv.h" 14 | #include "err.h" 15 | #include "localip.h" 16 | #include "now.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #ifdef HAVE_IFADDRS_H 28 | # include 29 | #else 30 | # ifdef HAVE_SYS_SOCKIO_H 31 | # include /* for SIOCGIFADDR, especially on Solaris */ 32 | # endif 33 | # include 34 | #endif 35 | 36 | void localip_init(struct local_ips *ips) { 37 | ips->is_valid = 0; 38 | ips->last_update_mono = 0; 39 | ips->num_addrs = 0; 40 | ips->addrs = NULL; 41 | } 42 | 43 | void localip_free(struct local_ips *ips) { 44 | if (ips->addrs != NULL) 45 | free(ips->addrs); 46 | } 47 | 48 | static void add_ip(const char *iface, 49 | struct local_ips *ips, 50 | int *idx, 51 | struct addr *a) { 52 | if (ips->num_addrs <= *idx) { 53 | /* Grow. */ 54 | ips->addrs = xrealloc(ips->addrs, sizeof(*(ips->addrs)) * (*idx + 1)); 55 | ips->num_addrs++; 56 | assert(ips->num_addrs > *idx); 57 | verbosef("interface '%s' gained new address %s", iface, addr_to_str(a)); 58 | } else { 59 | /* Warn about changed address. */ 60 | if (!addr_equal(ips->addrs + *idx, a)) { 61 | static char before[INET6_ADDRSTRLEN]; 62 | strncpy(before, addr_to_str(ips->addrs + *idx), INET6_ADDRSTRLEN); 63 | verbosef("interface '%s' address %d/%d changed from %s to %s", 64 | iface, *idx+1, ips->num_addrs, before, addr_to_str(a)); 65 | } 66 | } 67 | ips->addrs[*idx] = *a; 68 | (*idx)++; 69 | } 70 | 71 | /* Returns 0 on failure. */ 72 | void localip_update(const char *iface, struct local_ips *ips) { 73 | struct addr a; 74 | int new_addrs = 0; 75 | 76 | if (iface == NULL) { 77 | /* reading from capfile */ 78 | ips->is_valid = 0; 79 | return; 80 | } 81 | 82 | if (ips->last_update_mono == now_mono()) { 83 | /* Too soon, bail out. */ 84 | return; 85 | } 86 | ips->last_update_mono = now_mono(); 87 | 88 | #ifdef HAVE_IFADDRS_H 89 | { 90 | struct ifaddrs *ifas, *ifa; 91 | 92 | if (getifaddrs(&ifas) < 0) 93 | err(1, "getifaddrs() failed"); 94 | 95 | for (ifa=ifas; ifa; ifa=ifa->ifa_next) { 96 | if (strncmp(ifa->ifa_name, iface, IFNAMSIZ)) 97 | continue; /* Wrong interface. */ 98 | 99 | if (!ifa->ifa_addr) 100 | continue; /* This can be NULL, e.g. for ppp0. */ 101 | 102 | if (ifa->ifa_addr->sa_family == AF_INET) { 103 | a.family = IPv4; 104 | a.ip.v4 = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; 105 | add_ip(iface, ips, &new_addrs, &a); 106 | } 107 | if (ifa->ifa_addr->sa_family == AF_INET6) { 108 | struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ifa->ifa_addr; 109 | # if 0 110 | if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr)) 111 | || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) ) 112 | continue; 113 | # endif 114 | a.family = IPv6; 115 | memcpy(&(a.ip.v6), &sa6->sin6_addr, sizeof(a.ip.v6)); 116 | add_ip(iface, ips, &new_addrs, &a); 117 | } 118 | } 119 | freeifaddrs(ifas); 120 | } 121 | #else /* don't HAVE_IFADDRS_H */ 122 | { 123 | int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 124 | struct ifreq ifr; 125 | struct sockaddr sa; 126 | 127 | strlcpy(ifr.ifr_name, iface, IFNAMSIZ); 128 | ifr.ifr_addr.sa_family = AF_INET; 129 | if (ioctl(tmp, SIOCGIFADDR, &ifr) != -1) { 130 | sa = ifr.ifr_addr; 131 | a.family = IPv4; 132 | a.ip.v4 = ((struct sockaddr_in*)(&ifr.ifr_addr))->sin_addr.s_addr; 133 | add_ip(iface, ips, &new_addrs, &a); 134 | } 135 | close(tmp); 136 | } 137 | #endif 138 | if (new_addrs == 0) { 139 | if (ips->is_valid) 140 | verbosef("interface '%s' no longer has any addresses", iface); 141 | ips->is_valid = 0; 142 | } else { 143 | if (!ips->is_valid) 144 | verbosef("interface '%s' now has addresses", iface); 145 | ips->is_valid = 1; 146 | if (ips->num_addrs != new_addrs) 147 | verbosef("interface '%s' number of addresses decreased from %d to %d", 148 | iface, ips->num_addrs, new_addrs); 149 | ips->num_addrs = new_addrs; 150 | } 151 | } 152 | 153 | int is_localip(const struct addr * const a, 154 | const struct local_ips * const ips) { 155 | int i; 156 | 157 | for (i=0; inum_addrs; i++) { 158 | if (addr_equal(a, ips->addrs+i)) 159 | return 1; 160 | } 161 | return 0; 162 | } 163 | 164 | /* vim:set ts=3 sw=3 tw=80 et: */ 165 | -------------------------------------------------------------------------------- /localip.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * localip.h: determine the local IPs of an interface 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | #ifndef __DARKSTAT_LOCALIP_H 10 | #define __DARKSTAT_LOCALIP_H 11 | 12 | #include 13 | 14 | struct local_ips { 15 | int is_valid; 16 | time_t last_update_mono; 17 | int num_addrs; 18 | struct addr *addrs; 19 | }; 20 | 21 | void localip_init(struct local_ips *ips); 22 | void localip_free(struct local_ips *ips); 23 | 24 | void localip_update(const char *iface, struct local_ips *ips); 25 | int is_localip(const struct addr * const a, 26 | const struct local_ips * const ips); 27 | 28 | #endif 29 | /* vim:set ts=3 sw=3 tw=80 et: */ 30 | -------------------------------------------------------------------------------- /ncache.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * ncache.c: cache of protocol and service names. 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | 10 | #include "conv.h" 11 | #include "err.h" 12 | #include "ncache.h" 13 | #include "tree.h" 14 | #include "bsd.h" /* for strlcpy */ 15 | 16 | #include /* ntohs */ 17 | #include 18 | #include 19 | #include 20 | 21 | struct name_rec { 22 | RB_ENTRY(name_rec) ptree; 23 | int num; 24 | char *name; 25 | }; 26 | 27 | static int 28 | rec_cmp(struct name_rec *a, struct name_rec *b) 29 | { 30 | if (a->num < b->num) return (-1); else 31 | if (a->num > b->num) return (+1); else 32 | return (0); 33 | } 34 | 35 | RB_HEAD(nc_tree, name_rec); 36 | RB_GENERATE_STATIC(nc_tree, name_rec, ptree, rec_cmp) 37 | 38 | static struct nc_tree 39 | t_proto = RB_INITIALIZER(&name_rec), 40 | t_servtcp = RB_INITIALIZER(&name_rec), 41 | t_servudp = RB_INITIALIZER(&name_rec); 42 | 43 | static void 44 | add_rec(struct nc_tree *tree, const int num, const char *name) 45 | { 46 | struct name_rec *e, *r = xmalloc(sizeof(*r)); 47 | 48 | r->num = num; 49 | e = RB_INSERT(nc_tree, tree, r); 50 | 51 | if (e != NULL) { 52 | size_t newlen; 53 | 54 | /* record exists: append service name, free record */ 55 | newlen = strlen(e->name) + strlen(name) + 2; 56 | e->name = xrealloc(e->name, newlen); 57 | strlcat(e->name, " ", newlen); 58 | strlcat(e->name, name, newlen); 59 | free(r); 60 | } 61 | else { 62 | /* record added: fill out name field */ 63 | r->name = xstrdup(name); 64 | } 65 | } 66 | 67 | void 68 | ncache_init(void) 69 | { 70 | struct protoent *pe; 71 | struct servent *se; 72 | int count, ctcp, cudp; 73 | 74 | count = 0; 75 | setprotoent(0); 76 | while ((pe = getprotoent()) != NULL) { 77 | add_rec(&t_proto, pe->p_proto, pe->p_name); 78 | count++; 79 | } 80 | endprotoent(); 81 | verbosef("loaded %d protos", count); 82 | 83 | count = ctcp = cudp = 0; 84 | setservent(0); 85 | while ((se = getservent()) != NULL) { 86 | if (strcmp(se->s_proto, "tcp") == 0) { 87 | add_rec(&t_servtcp, ntohs(se->s_port), se->s_name); 88 | ctcp++; 89 | } 90 | else if (strcmp(se->s_proto, "udp") == 0) { 91 | add_rec(&t_servudp, ntohs(se->s_port), se->s_name); 92 | cudp++; 93 | } 94 | count++; 95 | } 96 | endservent(); 97 | verbosef("loaded %d tcp and %d udp servs, from total %d", 98 | ctcp, cudp, count); 99 | } 100 | 101 | static void 102 | tree_free(struct nc_tree *tree) 103 | { 104 | struct name_rec *curr, *next; 105 | 106 | for (curr = RB_MIN(nc_tree, tree); curr != NULL; curr = next) { 107 | next = RB_NEXT(nc_tree, tree, curr); 108 | RB_REMOVE(nc_tree, tree, curr); 109 | free(curr->name); 110 | free(curr); 111 | } 112 | } 113 | 114 | void 115 | ncache_free(void) 116 | { 117 | tree_free(&t_proto); 118 | tree_free(&t_servtcp); 119 | tree_free(&t_servudp); 120 | } 121 | 122 | #define FIND(tree,n) { \ 123 | struct name_rec r, *f; \ 124 | r.num = n; \ 125 | f = RB_FIND(nc_tree, &tree, &r); \ 126 | if (f == NULL) \ 127 | return (""); \ 128 | else \ 129 | return (f->name); \ 130 | } 131 | 132 | const char * 133 | getproto(const int proto) 134 | FIND(t_proto, proto) 135 | 136 | const char * 137 | getservtcp(const int port) 138 | FIND(t_servtcp, port) 139 | 140 | const char * 141 | getservudp(const int port) 142 | FIND(t_servudp, port) 143 | 144 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 145 | -------------------------------------------------------------------------------- /ncache.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2006 Emil Mikulic. 3 | * 4 | * ncache.h: cache of protocol and service names. 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | */ 9 | 10 | void ncache_init(void); 11 | void ncache_free(void); 12 | const char *getproto(const int proto); 13 | const char *getservtcp(const int port); 14 | const char *getservudp(const int port); 15 | 16 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 17 | -------------------------------------------------------------------------------- /now.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2012-2014 Emil Mikulic. 3 | * 4 | * now.c: a cache of the current time. 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #include "err.h" 19 | #include "now.h" 20 | #include "str.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #if defined(__MACH__) && !defined(__gnu_hurd__) 27 | /* Fake up clock_gettime() on OS X. */ 28 | # include 29 | # include 30 | # include 31 | # include 32 | 33 | typedef int clockid_t; 34 | # define CLOCK_REALTIME 0 35 | # define CLOCK_MONOTONIC 1 36 | 37 | static uint64_t mono_first = 0; 38 | 39 | int clock_gettime(clockid_t clk_id, struct timespec *tp) { 40 | if (clk_id == CLOCK_REALTIME) { 41 | struct timeval tv; 42 | gettimeofday(&tv, NULL); 43 | tp->tv_sec = tv.tv_sec; 44 | tp->tv_nsec = tv.tv_usec * 1000; 45 | return 0; 46 | } 47 | if (clk_id == CLOCK_MONOTONIC) { 48 | uint64_t t = mach_absolute_time(); 49 | mach_timebase_info_data_t timebase; 50 | mach_timebase_info(&timebase); 51 | if (!mono_first) { 52 | mono_first = t; 53 | } 54 | uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom; 55 | tp->tv_sec = tdiff / 1000000000; 56 | tp->tv_nsec = tdiff % 1000000000; 57 | return 0; 58 | } 59 | return -1; 60 | } 61 | #endif /* __MACH__ */ 62 | 63 | static struct timespec clock_real, clock_mono; 64 | static int now_initialized = 0; 65 | 66 | time_t now_real(void) { 67 | assert(now_initialized); 68 | return clock_real.tv_sec; 69 | } 70 | 71 | time_t now_mono(void) { 72 | assert(now_initialized); 73 | return clock_mono.tv_sec; 74 | } 75 | 76 | static int before(const struct timespec *a, const struct timespec *b) { 77 | if (a->tv_sec < b->tv_sec) 78 | return 1; 79 | if (a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec) 80 | return 1; 81 | return 0; 82 | } 83 | 84 | static void warn_backwards(const char *name, 85 | const struct timespec * const t0, 86 | const struct timespec * const t1) { 87 | verbosef("%s clock went backwards from %lld.%09lld to %lld.%09lld", 88 | name, 89 | (lld)t0->tv_sec, 90 | (lld)t0->tv_nsec, 91 | (lld)t1->tv_sec, 92 | (lld)t1->tv_nsec); 93 | } 94 | 95 | static void clock_update(const clockid_t clk_id, 96 | struct timespec *dest, 97 | const char *name) { 98 | struct timespec t; 99 | 100 | clock_gettime(clk_id, &t); 101 | if (now_initialized && before(&t, dest)) { 102 | warn_backwards(name, &t, dest); 103 | } 104 | memcpy(dest, &t, sizeof(t)); 105 | } 106 | 107 | static void all_clocks_update(void) { 108 | clock_update(CLOCK_REALTIME, &clock_real, "realtime"); 109 | clock_update(CLOCK_MONOTONIC, &clock_mono, "monotonic"); 110 | } 111 | 112 | void now_init(void) { 113 | assert(!now_initialized); 114 | all_clocks_update(); 115 | now_initialized = 1; 116 | } 117 | 118 | void now_update(void) { 119 | assert(now_initialized); 120 | all_clocks_update(); 121 | } 122 | 123 | time_t mono_to_real(const int64_t t) { 124 | assert(now_initialized); 125 | return (time_t)(t - (int64_t)clock_mono.tv_sec + (int64_t)clock_real.tv_sec); 126 | } 127 | 128 | int64_t real_to_mono(const time_t t) { 129 | assert(now_initialized); 130 | return (int64_t)(t - clock_real.tv_sec + clock_mono.tv_sec); 131 | } 132 | 133 | void timer_start(struct timespec *t) { 134 | clock_gettime(CLOCK_MONOTONIC, t); 135 | } 136 | 137 | static int64_t ts_diff(const struct timespec * const a, 138 | const struct timespec * const b) { 139 | return (int64_t)(a->tv_sec - b->tv_sec) * 1000000000 + 140 | a->tv_nsec - b->tv_nsec; 141 | } 142 | 143 | void timer_stop(const struct timespec * const t0, 144 | const int64_t nsec, 145 | const char *warning) { 146 | struct timespec t1; 147 | int64_t diff; 148 | 149 | clock_gettime(CLOCK_MONOTONIC, &t1); 150 | if (before(&t1, t0)) { 151 | warn_backwards("monotonic timer", t0, &t1); 152 | return; 153 | } 154 | diff = ts_diff(&t1, t0); 155 | if (diff > nsec) { 156 | warnx("%s (took %lld nsec, over threshold of %lld nsec)", 157 | warning, 158 | (lld)diff, 159 | (lld)nsec); 160 | } 161 | } 162 | 163 | /* vim:set ts=3 sw=3 tw=80 et: */ 164 | -------------------------------------------------------------------------------- /now.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * now.h: a cache of the current time. 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #include 19 | 20 | void now_init(void); 21 | void now_update(void); /* once per event loop (in darkstat.c) */ 22 | 23 | time_t now_real(void); 24 | time_t now_mono(void); 25 | 26 | /* Monotonic times can be negative (a time from before the machine booted) so 27 | * treat them as signed. */ 28 | time_t mono_to_real(const int64_t t); 29 | int64_t real_to_mono(const time_t t); 30 | 31 | /* Emits warnings if a call is too slow. */ 32 | struct timespec; 33 | void timer_start(struct timespec *t); 34 | void timer_stop(const struct timespec * const t, 35 | const int64_t nsec, 36 | const char *warning); 37 | 38 | /* vim:set ts=3 sw=3 tw=80 et: */ 39 | -------------------------------------------------------------------------------- /opt.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2012 Emil Mikulic. 3 | * 4 | * opt.h: global options 5 | */ 6 | 7 | /* Capture options. */ 8 | extern int opt_want_pppoe; 9 | extern int opt_want_macs; 10 | extern int opt_want_hexdump; 11 | extern int opt_want_snaplen; 12 | extern int opt_wait_secs; 13 | 14 | /* Error/logging options. */ 15 | extern int opt_want_verbose; 16 | extern int opt_want_syslog; 17 | 18 | /* Accounting options. */ 19 | extern unsigned int opt_highest_port; 20 | extern int opt_want_local_only; 21 | 22 | /* Hosts table reduction - when the number of entries is about to exceed 23 | * , we reduce the table to the top entries. 24 | */ 25 | extern unsigned int opt_hosts_max; 26 | extern unsigned int opt_hosts_keep; 27 | extern unsigned int opt_ports_max; 28 | extern unsigned int opt_ports_keep; 29 | 30 | /* Hosts output options. */ 31 | extern int opt_want_lastseen; 32 | 33 | /* Initialized in cap.c, added to */ 34 | extern char *title_interfaces; 35 | 36 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 37 | -------------------------------------------------------------------------------- /pidfile.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2007-2014 Emil Mikulic. 3 | * 4 | * pidfile.h: pidfile manglement 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "err.h" 20 | #include "str.h" 21 | #include "pidfile.h" 22 | 23 | #include <errno.h> 24 | #include <fcntl.h> 25 | #include <pwd.h> 26 | #include <stdlib.h> 27 | #include <unistd.h> 28 | 29 | static int pidfd = -1; 30 | static const char *pidname = NULL; 31 | 32 | void pidfile_create(const char *chroot_dir, 33 | const char *filename, 34 | const char *privdrop_user) { 35 | struct passwd *pw; 36 | 37 | if (pidfd != -1) 38 | errx(1, "pidfile already created"); 39 | 40 | errno = 0; 41 | pw = getpwnam(privdrop_user); 42 | 43 | if (pw == NULL) { 44 | if (errno == 0) 45 | errx(1, "getpwnam(\"%s\") failed: no such user", privdrop_user); 46 | else 47 | err(1, "getpwnam(\"%s\") failed", privdrop_user); 48 | } 49 | 50 | if (chroot_dir != NULL) { 51 | if (chdir(chroot_dir) == -1) { 52 | err(1, "chdir(\"%s\") failed", chroot_dir); 53 | } 54 | } 55 | pidname = filename; 56 | pidfd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600); 57 | if (pidfd == -1) 58 | err(1, "couldn't not create pidfile"); 59 | if (chown(filename, pw->pw_uid, pw->pw_gid) == -1) 60 | err(1, "couldn't chown pidfile"); 61 | } 62 | 63 | void 64 | pidfile_write_close(void) 65 | { 66 | struct str *s; 67 | size_t len; 68 | char *buf; 69 | 70 | if (pidfd == -1) 71 | errx(1, "cannot write pidfile: not created"); 72 | 73 | s = str_make(); 74 | str_appendf(s, "%u\n", (unsigned int)getpid()); 75 | str_extract(s, &len, &buf); 76 | 77 | if (write(pidfd, buf, len) != (int)len) 78 | err(1, "couldn't write to pidfile"); 79 | free(buf); 80 | if (close(pidfd) == -1) 81 | warn("problem closing pidfile"); 82 | } 83 | 84 | void 85 | pidfile_unlink(void) 86 | { 87 | if (pidname == NULL) 88 | return; /* pidfile wasn't created */ 89 | if (unlink(pidname) == -1) 90 | warn("problem unlinking pidfile"); 91 | } 92 | 93 | /* vim:set ts=3 sw=3 tw=78 et: */ 94 | -------------------------------------------------------------------------------- /pidfile.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2007 Emil Mikulic. 3 | * 4 | * pidfile.h: pidfile manglement 5 | */ 6 | 7 | void pidfile_create(const char *chroot_dir, const char *filename, 8 | const char *privdrop_user); 9 | void pidfile_write_close(void); 10 | void pidfile_unlink(void); 11 | 12 | /* vim:set ts=3 sw=3 tw=78 et: */ 13 | -------------------------------------------------------------------------------- /presubmit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # darkstat 3 3 | # copyright (c) 2022 Emil Mikulic. 4 | # 5 | # Run ./presubmit.sh before committing code. 6 | 7 | say() { 8 | echo ==\> "$@" >&2 9 | } 10 | 11 | run() { 12 | say "$@" 13 | "$@" || { say ERROR!; exit 1; } 14 | } 15 | 16 | run ./config.status 17 | run ./tidy_linktypes_list.sh 18 | run make depend 19 | run ./test_headers.sh 20 | run make clean 21 | run make check 22 | -------------------------------------------------------------------------------- /queue.h: -------------------------------------------------------------------------------- 1 | /* This is a stripped down version of FreeBSD's 2 | * src/sys/sys/queue.h,v 1.60.2.1 3 | * 4 | * The original file's license: 5 | * 6 | * Copyright (c) 1991, 1993 7 | * The Regents of the University of California. All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions 11 | * are met: 12 | * 1. Redistributions of source code must retain the above copyright 13 | * notice, this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 4. Neither the name of the University nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software 19 | * without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | * SUCH DAMAGE. 32 | */ 33 | 34 | #define STAILQ_HEAD(name, type) \ 35 | struct name { \ 36 | struct type *stqh_first;/* first element */ \ 37 | struct type **stqh_last;/* addr of last next element */ \ 38 | } 39 | 40 | #define STAILQ_HEAD_INITIALIZER(head) \ 41 | { NULL, &(head).stqh_first } 42 | 43 | #define STAILQ_ENTRY(type) \ 44 | struct { \ 45 | struct type *stqe_next; /* next element */ \ 46 | } 47 | 48 | #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) 49 | 50 | #define STAILQ_FIRST(head) ((head)->stqh_first) 51 | 52 | #define STAILQ_FOREACH(var, head, field) \ 53 | for((var) = STAILQ_FIRST((head)); \ 54 | (var); \ 55 | (var) = STAILQ_NEXT((var), field)) 56 | 57 | #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) 58 | 59 | #ifdef STAILQ_INSERT_TAIL 60 | #undef STAILQ_INSERT_TAIL 61 | #endif 62 | 63 | #define STAILQ_INSERT_TAIL(head, elm, field) do { \ 64 | STAILQ_NEXT((elm), field) = NULL; \ 65 | *(head)->stqh_last = (elm); \ 66 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 67 | } while (0) 68 | 69 | #ifdef STAILQ_REMOVE_HEAD 70 | #undef STAILQ_REMOVE_HEAD 71 | #endif 72 | 73 | #define STAILQ_REMOVE_HEAD(head, field) do { \ 74 | if ((STAILQ_FIRST((head)) = \ 75 | STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ 76 | (head)->stqh_last = &STAILQ_FIRST((head)); \ 77 | } while (0) 78 | 79 | #undef LIST_HEAD 80 | #define LIST_HEAD(name, type) \ 81 | struct name { \ 82 | struct type *lh_first; /* first element */ \ 83 | } 84 | 85 | #undef LIST_HEAD_INITIALIZER 86 | #define LIST_HEAD_INITIALIZER(head) \ 87 | { NULL } 88 | 89 | #undef LIST_ENTRY 90 | #define LIST_ENTRY(type) \ 91 | struct { \ 92 | struct type *le_next; /* next element */ \ 93 | struct type **le_prev; /* address of previous next element */ \ 94 | } 95 | 96 | #undef LIST_FIRST 97 | #define LIST_FIRST(head) ((head)->lh_first) 98 | 99 | #undef LIST_FOREACH 100 | #define LIST_FOREACH(var, head, field) \ 101 | for ((var) = LIST_FIRST((head)); \ 102 | (var); \ 103 | (var) = LIST_NEXT((var), field)) 104 | 105 | #undef LIST_FOREACH_SAFE 106 | #define LIST_FOREACH_SAFE(var, head, field, tvar) \ 107 | for ((var) = LIST_FIRST((head)); \ 108 | (var) && ((tvar) = LIST_NEXT((var), field), 1); \ 109 | (var) = (tvar)) 110 | 111 | #undef LIST_INIT 112 | #define LIST_INIT(head) do { \ 113 | LIST_FIRST((head)) = NULL; \ 114 | } while (0) 115 | 116 | #undef LIST_INSERT_HEAD 117 | #define LIST_INSERT_HEAD(head, elm, field) do { \ 118 | if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ 119 | LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ 120 | LIST_FIRST((head)) = (elm); \ 121 | (elm)->field.le_prev = &LIST_FIRST((head)); \ 122 | } while (0) 123 | 124 | #undef LIST_NEXT 125 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) 126 | 127 | #undef LIST_REMOVE 128 | #define LIST_REMOVE(elm, field) do { \ 129 | if (LIST_NEXT((elm), field) != NULL) \ 130 | LIST_NEXT((elm), field)->field.le_prev = \ 131 | (elm)->field.le_prev; \ 132 | *(elm)->field.le_prev = LIST_NEXT((elm), field); \ 133 | } while (0) 134 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # release.sh: script to roll a release tarball of darkstat. 4 | # copyright (c) 2006-2016 Emil Mikulic. 5 | # 6 | # This is for developer use only and lives in the repo but 7 | # shouldn't end up in a tarball. 8 | # 9 | # Release checklist: 10 | # - git tag 3.0.xxx 11 | # - git push --tags 12 | # - Update website 13 | # - Mail announcement to darkstat-announce@googlegroups.com 14 | # - Update FreeBSD port, e.g.: 15 | # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=200425 16 | # 17 | if [ $# -ne 1 ]; then 18 | echo "usage: $0 3.0.0rc0" >&2 19 | exit 1 20 | fi 21 | 22 | NAME=darkstat 23 | VERSION="$1" 24 | 25 | files="\ 26 | AUTHORS \ 27 | ChangeLog \ 28 | COPYING.GPL \ 29 | INSTALL \ 30 | LICENSE \ 31 | Makefile.in \ 32 | NEWS \ 33 | README.md \ 34 | acct.c \ 35 | acct.h \ 36 | addr.c \ 37 | addr.h \ 38 | bsd.c \ 39 | bsd.h \ 40 | cap.c \ 41 | cap.h \ 42 | cdefs.h \ 43 | contrib \ 44 | conv.c \ 45 | conv.h \ 46 | darkstat.8.in \ 47 | darkstat.c \ 48 | daylog.c \ 49 | daylog.h \ 50 | db.c \ 51 | db.h \ 52 | decode.c \ 53 | decode.h \ 54 | dns.c \ 55 | dns.h \ 56 | err.c \ 57 | err.h \ 58 | export-format.txt \ 59 | favicon.h \ 60 | graph_db.c \ 61 | graph_db.h \ 62 | graphjs.h \ 63 | hosts_db.c \ 64 | hosts_db.h \ 65 | hosts_sort.c \ 66 | html.c \ 67 | html.h \ 68 | http.c \ 69 | http.h \ 70 | install-sh \ 71 | localip.c \ 72 | localip.h \ 73 | ncache.c \ 74 | ncache.h \ 75 | now.c \ 76 | now.h \ 77 | opt.h \ 78 | pidfile.c \ 79 | pidfile.h \ 80 | queue.h \ 81 | static \ 82 | str.c \ 83 | str.h \ 84 | stylecss.h \ 85 | tree.h \ 86 | " 87 | # end packing list 88 | 89 | say() { 90 | echo ==\> "$@" >&2 91 | } 92 | 93 | run() { 94 | say "$@" 95 | "$@" || { say ERROR!; exit 1; } 96 | } 97 | 98 | PKG=$NAME-$VERSION 99 | say releasing $PKG 100 | run make depend 101 | run make graphjs.h stylecss.h 102 | run autoconf 103 | run autoheader 104 | run ./config.status 105 | run ./test_headers.sh 106 | if git status --porcelain | egrep -v '^\?\?' -q; then 107 | say ERROR: uncommitted changes: 108 | git status 109 | exit 1 110 | fi 111 | run mkdir $PKG 112 | run cp -r $files $PKG/. 113 | run sed -e "/AC_INIT/s/3.0.0-git/$VERSION/" configure.ac > $PKG/configure.ac 114 | say version set to: $(grep '^AC_INIT' $PKG/configure.ac) 115 | (cd $PKG 116 | run autoconf 117 | run autoheader 118 | run rm -r autom4te.cache 119 | ) || exit 1 120 | 121 | # package it up 122 | run tar chof $PKG.tar $PKG 123 | run bzip2 -9vv $PKG.tar 124 | say output: 125 | ls -l $PKG.tar.bz2 126 | say FINISHED! 127 | -------------------------------------------------------------------------------- /static/c-ify.c: -------------------------------------------------------------------------------- 1 | /* Converts a textfile to a const char array with characters escaped. */ 2 | #include <stdio.h> 3 | #include <stdlib.h> 4 | 5 | int 6 | main(int argc, char **argv) 7 | { 8 | int c, eol; 9 | if (argc != 2) { 10 | fprintf(stderr, "usage: %s name <infile >outfile.h\n", 11 | argv[0]); 12 | exit(EXIT_FAILURE); 13 | } 14 | printf("/* this file was automatically generated with c-ify */\n" 15 | "static const char %s[] =", argv[1]); 16 | eol = 1; 17 | while ((c = getchar()) != EOF) { 18 | if (eol) { 19 | printf("\n\""); 20 | eol = 0; 21 | } 22 | switch (c) { 23 | case '\n': printf("\\n\""); eol = 1; break; 24 | case '"': printf("\\\""); break; 25 | case '\\': printf("\\\\"); break; 26 | default: putchar(c); 27 | } 28 | } 29 | printf(";\n" 30 | "static const size_t %s_len = sizeof(%s) - 1;\n", 31 | argv[1], argv[1]); 32 | return (0); 33 | } 34 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emikulic/darkstat/a219027ec8ea6bb75647bef126dd44f56ac9c86e/static/favicon.png -------------------------------------------------------------------------------- /static/graph.js: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2006-2008 Emil Mikulic. 3 | * 4 | * graph.js: graph renderer 5 | * 6 | * You may use, modify and redistribute this file under the terms of the 7 | * GNU General Public License version 2. (see COPYING.GPL) 8 | * 9 | * At some point, this script worked correctly in: 10 | * - Firefox 1.5.0.4, 2.0.0.1, 3.0 11 | * - IE 6.0 12 | * - Opera 8.53, 9.50 13 | * - Konqueror 3.5.9, 4.0.80, 4.0.83 14 | * 15 | * Consumer needs to supply the following variables: 16 | * - graph_width 17 | * - graph_height 18 | * - bar_gap 19 | * 20 | * - graphs [ {id, name, title, bar_secs} ] 21 | * - graphs_uri 22 | * 23 | * - window.onload = graphs_init 24 | */ 25 | 26 | function killChildren(elem) { 27 | while (elem.childNodes.length > 0) 28 | elem.removeChild( elem.childNodes.item(0) ); 29 | } 30 | 31 | function setClass(elem, c) { 32 | elem.setAttribute("class", c); 33 | elem.setAttribute("className", c); /* for MSIE */ 34 | } 35 | 36 | function setStyle(elem, s) { 37 | elem.setAttribute("style", s); 38 | elem.style.cssText = s; /* for MSIE */ 39 | } 40 | 41 | function makeElemClass(e, c) { 42 | var r = document.createElement(e); 43 | setClass(r, c); 44 | return r; 45 | } 46 | 47 | function makeClear() { 48 | var r = document.createElement("div"); 49 | setStyle(r, "clear:both"); 50 | return r; 51 | } 52 | 53 | function thousands(n) { 54 | var s = String(n); 55 | var out = ""; 56 | while (s.length > 3) { 57 | out = "," + s.substr(s.length - 3, 3) + out; 58 | s = s.substr(0, s.length - 3); 59 | } 60 | return s+out; 61 | } 62 | 63 | function fkbps(bps) { 64 | bps /= 1024; 65 | return bps.toFixed(1); 66 | } 67 | 68 | function kbps(bps) { 69 | bps /= 1024; 70 | if (bps < 1) return bps.toPrecision(2); 71 | else return bps.toFixed(1); 72 | } 73 | 74 | function min(a,b) { return (a<b)?a:b; } 75 | function max(a,b) { return (a>b)?a:b; } 76 | 77 | var xh, autoreload=false; 78 | 79 | function graphs_init() { 80 | var gr = document.getElementById("graphs"); 81 | 82 | /* update message */ 83 | var msg = document.createElement("div"); 84 | msg.appendChild(document.createTextNode("Graphs are being loaded...")); 85 | msg.appendChild(document.createElement("br")); 86 | msg.appendChild(document.createElement("br")); 87 | killChildren(gr); 88 | gr.appendChild(msg); 89 | graphs.msg = msg; 90 | 91 | for (var i=0; i<graphs.length; i++) { 92 | var g = makeElemClass("div", "outergraph"); 93 | gr.appendChild(g); 94 | graphs[i].graph = g; 95 | if (i % 2 == 1) gr.appendChild(makeClear()); 96 | } 97 | 98 | /* create buttons */ 99 | var b_reload = document.createElement("a"); 100 | b_reload.setAttribute("id", "graph_reload"); 101 | b_reload.setAttribute("href", "javascript:graph_reload()"); 102 | b_reload.appendChild(document.createTextNode("reload graphs")); 103 | 104 | var b_autoreload = document.createElement("a"); 105 | b_autoreload.setAttribute("id", "graph_autoreload"); 106 | b_autoreload.setAttribute("href", "javascript:graph_autoreload()"); 107 | b_autoreload.appendChild(document.createTextNode("off")); 108 | 109 | var b = document.createElement("div"); 110 | b.appendChild(b_reload); 111 | b.appendChild(document.createTextNode(" - automatic reload is: ")); 112 | b.appendChild(b_autoreload); 113 | gr.appendChild(b); 114 | 115 | graph_reload(); 116 | } 117 | 118 | function graph_reload() { 119 | if (!autoreload) 120 | document.getElementById("graph_reload").innerHTML = "loading..."; 121 | xh = (window.ActiveXObject) 122 | ? new ActiveXObject("Microsoft.XMLHTTP") 123 | : new XMLHttpRequest(); 124 | var asyncFlag = true; 125 | xh.open("GET", graphs_uri, asyncFlag); 126 | // try to nerf caching: 127 | xh.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"); 128 | xh.onreadystatechange = poll; 129 | xh.send(null); 130 | } 131 | 132 | function graph_autoreload() { 133 | // toggle 134 | autoreload = !autoreload; 135 | document.getElementById("graph_autoreload").innerHTML = 136 | autoreload ? "on" : "off"; 137 | if (autoreload) reload_loop(); 138 | } 139 | 140 | function reload_loop() { 141 | if (autoreload) { 142 | graph_reload(); 143 | setTimeout("reload_loop()", 1000); 144 | } 145 | } 146 | 147 | function poll() { 148 | var STATE_COMPLETE = 4; 149 | if (xh && xh.readyState == STATE_COMPLETE) { 150 | for (var i=0; i<graphs.length; i++) 151 | { 152 | g = xh.responseXML.getElementsByTagName(graphs[i].name); 153 | buildGraph(graphs[i].graph, graphs[i].title, graphs[i].bar_secs, 154 | g[0].getElementsByTagName("e")); 155 | } 156 | document.getElementById("graph_reload").innerHTML = "reload graphs"; 157 | killChildren(graphs.msg); 158 | head = xh.responseXML.childNodes[0]; 159 | for (var n in {"tb":0, "tp":0, "pc":0, "pd":0}) 160 | document.getElementById(n).innerHTML = thousands(head.getAttribute(n)); 161 | document.getElementById("rf").innerHTML = head.getAttribute("rf"); 162 | } 163 | } 164 | 165 | function addBar(graph, title, barclass, width, height, left, bottom) { 166 | if (height == 0) return; /* not visible */ 167 | var bar = makeElemClass("div", barclass); 168 | bar.setAttribute("title", title); 169 | setStyle(bar, 170 | "width:"+width+"px; "+ 171 | "height:"+height+"px; "+ 172 | "position: absolute; "+ 173 | "left:"+left+"px; "+ 174 | "bottom:"+bottom+"px;"); 175 | graph.appendChild(bar); 176 | } 177 | 178 | function buildGraph(graph, title, bar_secs, elems) { 179 | var total_max = 0; 180 | var data = []; /* list of [in, out] */ 181 | for (var i=0; i<elems.length; i++) { 182 | var elem = elems.item(i); 183 | var b_pos = Number( elem.getAttribute("p") ); 184 | var b_in = Number( elem.getAttribute("i") ); 185 | var b_out = Number( elem.getAttribute("o") ); 186 | var b_total = b_in + b_out; 187 | /* FIXME: what happens when a bar's value is >4G? */ 188 | if (b_total > total_max) 189 | total_max = b_total; 190 | data.push( [b_pos, b_in, b_out] ); 191 | } 192 | 193 | var igraph = makeElemClass("div", "graph"); // inner graph 194 | setStyle(igraph, 195 | "width:"+graph_width+"px; "+ 196 | "height:"+graph_height+"px; "+ 197 | "position:relative;"); 198 | 199 | var nbars = data.length; 200 | var b_width = (graph_width - bar_gap * (nbars-1)) / nbars; 201 | var next_xofs = 0; 202 | 203 | var min_i = 0, min_o = 0, 204 | max_i = 0, max_o = 0, 205 | tot_i = 0, tot_o = 0; 206 | 207 | for (var i=0; i<nbars; i++) { 208 | var b_p = data[i][0]; 209 | var b_i = data[i][1]; 210 | var b_o = data[i][2]; 211 | 212 | if (b_i>0) { if (min_i == 0) min_i = b_i; else min_i = min(min_i, b_i); } 213 | max_i = max(max_i, b_i); 214 | tot_i += b_i; 215 | 216 | if (b_o>0) { if (min_o == 0) min_o = b_o; else min_o = min(min_o, b_o); } 217 | max_o = max(max_o, b_o); 218 | tot_o += b_o; 219 | 220 | var xofs = next_xofs; 221 | 222 | next_xofs = Math.round((b_width + bar_gap) * (i+1)); 223 | var curr_w = next_xofs - xofs - bar_gap; 224 | 225 | var h_i = Math.round( b_i * graph_height / total_max ); 226 | var h_o = Math.round( b_o * graph_height / total_max ); 227 | 228 | var label = b_p+": "+ 229 | thousands(b_i)+" bytes in, "+ 230 | thousands(b_o)+" bytes out | "+ 231 | kbps(b_i/bar_secs)+" KB/s in, "+ 232 | kbps(b_o/bar_secs)+" KB/s out"; 233 | 234 | addBar(igraph, label, "bar_in", curr_w, h_i, xofs, 0); 235 | addBar(igraph, label, "bar_out", curr_w, h_o, xofs, h_i); 236 | } 237 | 238 | function legendRow(dir_str, minb, avgb, maxb) { 239 | function makeTD(c, str) { 240 | var r = makeElemClass("td", c); 241 | r.appendChild(document.createTextNode(str)); 242 | return r; 243 | } 244 | function addToRow(row, type_str, bytes, trail) { 245 | row.appendChild( makeTD("type", type_str) ); 246 | row.appendChild( makeTD("rate", fkbps(bytes/bar_secs)+" KB/s"+trail) ); 247 | } 248 | var row = document.createElement("tr"); 249 | row.appendChild( makeTD("dir", dir_str) ); 250 | var cell = makeElemClass("td", "swatch"); 251 | var swatch = makeElemClass("div", "bar_"+dir_str); 252 | setStyle(swatch, "width:6px; height:6px;"); 253 | cell.appendChild(swatch); 254 | row.appendChild(cell); 255 | addToRow(row, "min:", minb, ","); 256 | addToRow(row, "avg:", avgb, ","); 257 | addToRow(row, "max:", maxb, ""); 258 | return row; 259 | } 260 | 261 | var glegend = makeElemClass("div", "legend"); 262 | var avg_i = tot_i / nbars, 263 | avg_o = tot_o / nbars; 264 | var tbl = document.createElement("table"); 265 | var tb = document.createElement("tbody"); /* for MSIE */ 266 | tb.appendChild( legendRow("in", min_i, avg_i, max_i) ); 267 | tb.appendChild( legendRow("out", min_o, avg_o, max_o) ); 268 | tbl.appendChild(tb); 269 | glegend.appendChild(tbl); 270 | setStyle(glegend, "width:"+graph_width+"px;"); 271 | 272 | var gtitle = makeElemClass("div", "graphtitle"); 273 | setStyle(gtitle, "width:"+graph_width+"px;"); 274 | gtitle.appendChild(document.createTextNode(title)); 275 | 276 | killChildren(graph); 277 | graph.appendChild(igraph); 278 | graph.appendChild(glegend); 279 | graph.appendChild(gtitle); 280 | } 281 | -------------------------------------------------------------------------------- /static/hex-ify.c: -------------------------------------------------------------------------------- 1 | /* Convert a binary file to a const char array of hex. */ 2 | #include <stdio.h> 3 | #include <stdlib.h> 4 | 5 | int 6 | main(int argc, char **argv) 7 | { 8 | int c; 9 | if (argc != 2) { 10 | fprintf(stderr, "usage: %s name <infile >outfile.h\n", 11 | argv[0]); 12 | exit(EXIT_FAILURE); 13 | } 14 | printf("/* this file was automatically generated with hex-ify */\n" 15 | "static const unsigned char %s[] = {\n", argv[1]); 16 | int start_of_line = 1; 17 | int first = 1; 18 | int bytes = 0; 19 | while ((c = getchar()) != EOF) { 20 | if (start_of_line) { 21 | printf(" "); 22 | start_of_line = 0; 23 | } 24 | if (first) { 25 | first = 0; 26 | } else { 27 | printf(", "); 28 | } 29 | printf("0x%02x", (unsigned char)c); 30 | bytes++; 31 | if (bytes == 12) { 32 | printf(",\n"); 33 | first = 1; 34 | start_of_line = 1; 35 | bytes = 0; 36 | } 37 | } 38 | printf("\n};\n"); 39 | return (0); 40 | } 41 | -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * 3 | * style.css: CSS stylesheet for web interface. 4 | * copyright (c) 2006 Ben Stewart. 5 | * colors broken in 2007 by Emil Mikulic. 6 | * 7 | * You may use, modify and redistribute this file under the terms of the 8 | * GNU General Public License version 2. (see COPYING.GPL) 9 | */ 10 | 11 | body { background-color: #fff; z-index: 0; } 12 | .content { z-index: 1; 13 | position: absolute; top:15px; left:10px; } 14 | div.menu { z-index: 2; 15 | position: absolute; top:0; left:0; 16 | width: 100%; background-color: #789; 17 | border-bottom: 1px solid black; 18 | font-size:11px; } 19 | ul.menu { list-style: none; margin:0; padding:2px 0 3px 0; } 20 | ul.menu li { list-style: none; display: inline; margin:0; 21 | padding:2px 0 3px 0; 22 | border-right:1px solid white; } 23 | ul.menu li.label 24 | { padding-left:10px; padding-right:10px; color:#000; 25 | text-shadow: 0px 1px 0px #9ab; } 26 | ul.menu li a { color: white; text-decoration: none; 27 | text-shadow: 0px 1px 0px #456; 28 | border-bottom: none; padding:2px 15px 3px 15px; } 29 | ul.menu li a:hover 30 | { background-color: #9ab; } 31 | h1, h2, h3, h4, h5, h6 32 | { margin-top:10px; margin-bottom:5px; color: #000000; 33 | font-family: Arial, sans-serif; font-weight:bold; } 34 | .pageheader { border-bottom: 2px dotted black; } 35 | table { border-collapse: collapse; } 36 | td, th { border:1px solid #C0C0C0; padding:1px 5px 1px 5px; } 37 | td.num { text-align:right; } 38 | th { background-color:#EFEFEF; font-weight: bold; 39 | padding-top:2px; padding-bottom:2px; } 40 | th a { color:black; border-bottom:1px dotted; } 41 | tr:nth-child(odd) { background:#FFFFFF; } 42 | tr:nth-child(even) { background:#FAFAFA; } 43 | tr:hover { background:#EFEFEF; } 44 | body, td, th, p, input, textarea 45 | { font-family: Tahoma, Verdana, sans-serif; 46 | font-size: small; } 47 | tt { font-family: Courier New, monospace; 48 | font-size: small; } 49 | a:hover { border-bottom: 1px dotted #666; } 50 | a { text-decoration: none; color: #666; } 51 | div.outergraph { float:left; margin-right:10px; margin-bottom:20px; } 52 | div.graph { border: 1px solid black; } 53 | div.graphtitle { text-align:center; font-weight:bold; } 54 | div.bar_in { background: #678; } 55 | div.bar_out { background: #abc; } 56 | 57 | #graph_reload,#graph_autoreload { border:1px solid black; 58 | padding:2px 10px 2px 10px; margin-left:5px; color:black; } 59 | 60 | #graph_reload:hover,#graph_autoreload:hover { background:#9ab; color:black; } 61 | 62 | div.legend table { margin-left:auto; margin-right:auto; /* center */ 63 | border:0; } 64 | div.legend td { border:0; padding:0 0.2em 0 0.2em; font-size:11px; 65 | color:#444; } 66 | div.legend td.dir { text-align:right; } 67 | div.legend td.rate { text-align:right; white-space: nowrap; } 68 | -------------------------------------------------------------------------------- /str.c: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2012 Emil Mikulic. 3 | * 4 | * str.c: string buffer with pool-based reallocation 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "conv.h" 20 | #include "err.h" 21 | #include "str.h" 22 | 23 | #include <assert.h> 24 | #include <stdlib.h> 25 | #include <string.h> 26 | #include <stdint.h> /* for uint32_t on Linux and OS X */ 27 | #include <unistd.h> 28 | 29 | #define INITIAL_LEN 1024 30 | 31 | struct str { 32 | char *buf; 33 | size_t len, pool; 34 | }; 35 | 36 | struct str * 37 | str_make(void) 38 | { 39 | struct str *s = xmalloc(sizeof(*s)); 40 | s->len = 0; 41 | s->pool = INITIAL_LEN; 42 | s->buf = xmalloc(s->pool); 43 | return (s); 44 | } 45 | 46 | void 47 | str_free(struct str *s) 48 | { 49 | free(s->buf); 50 | free(s); 51 | } 52 | 53 | /* 54 | * Extract struct str into buffer and length, freeing the struct in the 55 | * process. 56 | */ 57 | void 58 | str_extract(struct str *s, size_t *len, char **str) 59 | { 60 | *len = s->len; 61 | *str = s->buf; 62 | free(s); 63 | } 64 | 65 | void 66 | str_appendn(struct str *buf, const char *s, const size_t len) 67 | { 68 | if (buf->pool < buf->len + len) { 69 | /* pool has dried up */ 70 | while (buf->pool < buf->len + len) 71 | buf->pool *= 2; 72 | buf->buf = xrealloc(buf->buf, buf->pool); 73 | } 74 | memcpy(buf->buf + buf->len, s, len); 75 | buf->len += len; 76 | } 77 | 78 | void 79 | str_appendstr(struct str *buf, const struct str *s) 80 | { 81 | str_appendn(buf, s->buf, s->len); 82 | } 83 | 84 | #ifndef str_append 85 | void 86 | str_append(struct str *buf, const char *s) 87 | { 88 | str_appendn(buf, s, strlen(s)); 89 | } 90 | #endif 91 | 92 | /* 93 | * Apparently, some wacky locales use periods, or another character that isn't 94 | * a comma, to separate thousands. If you are afflicted by such a locale, 95 | * change this macro: 96 | */ 97 | #define COMMA ',' 98 | 99 | /* 2^32 = 4,294,967,296 (10 digits, 13 chars) */ 100 | #define I32_MAXLEN 13 101 | 102 | /* 2^64 = 18,446,744,073,709,551,616 (20 digits, 26 chars) */ 103 | #define I64_MAXLEN 26 104 | 105 | static void 106 | str_append_u32(struct str *s, const uint32_t i, const int mod_sep) 107 | { 108 | char out[I32_MAXLEN]; 109 | int pos; 110 | unsigned int len; 111 | uint32_t rem, next; 112 | 113 | if (i == 0) { 114 | str_append(s, "0"); 115 | return; 116 | } 117 | 118 | pos = sizeof(out)-1; 119 | len = 0; 120 | rem = i; 121 | 122 | while (rem > 0) { 123 | assert(pos >= 0); 124 | next = rem / 10; 125 | rem = rem - next * 10; 126 | assert(rem < 10); 127 | out[pos] = '0' + rem; 128 | pos--; 129 | len++; 130 | rem = next; 131 | if (mod_sep && (rem > 0) && (len > 0) && (len % 3 == 0)) { 132 | out[pos] = COMMA; 133 | pos--; 134 | } 135 | } 136 | str_appendn(s, out+pos+1, sizeof(out)-1-pos); 137 | } 138 | 139 | static void 140 | str_append_i32(struct str *s, int32_t i, const int mod_sep) 141 | { 142 | if (i < 0) { 143 | str_append(s, "-"); 144 | i = -i; 145 | } 146 | str_append_u32(s, (uint32_t)i, mod_sep); 147 | } 148 | 149 | static void 150 | str_append_u64(struct str *s, const uint64_t i, const int mod_sep) 151 | { 152 | char out[I64_MAXLEN]; 153 | int pos; 154 | unsigned int len; 155 | uint64_t rem, next; 156 | uint32_t rem32, next32; 157 | 158 | if (i == 0) { 159 | str_append(s, "0"); 160 | return; 161 | } 162 | 163 | pos = sizeof(out)-1; 164 | len = 0; 165 | rem = i; 166 | 167 | while (rem >= 4294967295U) { 168 | assert(pos >= 0); 169 | next = rem / 10; 170 | rem = rem - next * 10; 171 | assert(rem < 10); 172 | out[pos] = '0' + rem; 173 | pos--; 174 | len++; 175 | rem = next; 176 | if (mod_sep && (rem > 0) && (len > 0) && (len % 3 == 0)) { 177 | out[pos] = COMMA; 178 | pos--; 179 | } 180 | } 181 | 182 | /* 183 | * Stick to 32-bit math when we can as it's faster on 32-bit platforms. 184 | * FIXME: a tunable way to switch this off? 185 | */ 186 | rem32 = (uint32_t)rem; 187 | while (rem32 > 0) { 188 | assert(pos >= 0); 189 | next32 = rem32 / 10; 190 | rem32 = rem32 - next32 * 10; 191 | assert(rem32 < 10); 192 | out[pos] = '0' + rem32; 193 | pos--; 194 | len++; 195 | rem32 = next32; 196 | if (mod_sep && (rem32 > 0) && (len > 0) && (len % 3 == 0)) { 197 | out[pos] = COMMA; 198 | pos--; 199 | } 200 | } 201 | str_appendn(s, out+pos+1, sizeof(out)-1-pos); 202 | } 203 | 204 | static void 205 | str_append_i64(struct str *s, int64_t i, const int mod_sep) 206 | { 207 | if (i < 0) { 208 | str_append(s, "-"); 209 | i = -i; 210 | } 211 | str_append_u64(s, (uint64_t)i, mod_sep); 212 | } 213 | 214 | static void 215 | str_append_hex8(struct str *s, const uint8_t b) 216 | { 217 | char out[2]; 218 | static const char hexset[] = "0123456789abcdef"; 219 | 220 | out[0] = hexset[ ((b >> 4) & 15) ]; 221 | out[1] = hexset[ (b & 15) ]; 222 | str_appendn(s, out, 2); 223 | } 224 | 225 | /* accepted formats: %s %d %u %x 226 | * accepted modifiers: q and ' 227 | * 228 | * %x is equivalent to %02x and expects a uint8_t 229 | */ 230 | void str_vappendf(struct str *s, const char *format, va_list va) { 231 | size_t pos, len; 232 | len = strlen(format); 233 | 234 | for (pos=0; pos<len; pos++) { 235 | size_t span_start = pos, span_len = 0; 236 | 237 | while ((format[pos] != '\0') && (format[pos] != '%')) { 238 | span_len++; 239 | pos++; 240 | } 241 | if (span_len > 0) 242 | str_appendn(s, format+span_start, span_len); 243 | 244 | if (format[pos] == '%') { 245 | int mod_quad = 0, mod_sep = 0; 246 | char *arg_str; 247 | FORMAT: 248 | pos++; 249 | switch (format[pos]) { 250 | case '%': 251 | str_append(s, "%"); 252 | break; 253 | case 'q': 254 | mod_quad = 1; 255 | goto FORMAT; 256 | case '\'': 257 | mod_sep = 1; 258 | goto FORMAT; 259 | case 's': 260 | arg_str = va_arg(va, char*); 261 | str_append(s, arg_str); 262 | /* str_append can be a macro! passing it va_arg can result in 263 | * va_arg being called twice 264 | */ 265 | break; 266 | case 'd': 267 | if (mod_quad) 268 | str_append_i64(s, va_arg(va, int64_t), mod_sep); 269 | else 270 | str_append_i32(s, (int32_t)va_arg(va, int), mod_sep); 271 | break; 272 | case 'u': 273 | if (mod_quad) 274 | str_append_u64(s, va_arg(va, uint64_t), mod_sep); 275 | else 276 | str_append_u32(s, (uint32_t)va_arg(va, unsigned int), mod_sep); 277 | break; 278 | case 'x': 279 | str_append_hex8(s, (uint8_t)va_arg(va, int)); 280 | break; 281 | default: 282 | errx(1, "format string is \"%s\", unknown format '%c' at %u", 283 | format, format[pos], (unsigned int)pos); 284 | } 285 | } 286 | } 287 | } 288 | 289 | void 290 | str_appendf(struct str *s, const char *format, ...) 291 | { 292 | va_list va; 293 | va_start(va, format); 294 | str_vappendf(s, format, va); 295 | va_end(va); 296 | } 297 | 298 | size_t 299 | xvasprintf(char **result, const char *format, va_list va) 300 | { 301 | size_t len; 302 | struct str *s = str_make(); 303 | str_vappendf(s, format, va); 304 | str_appendn(s, "", 1); /* "" still contains \0 */ 305 | str_extract(s, &len, result); 306 | return (len-1); 307 | } 308 | 309 | size_t 310 | xasprintf(char **result, const char *format, ...) 311 | { 312 | va_list va; 313 | size_t ret; 314 | va_start(va, format); 315 | ret = xvasprintf(result, format, va); 316 | va_end(va); 317 | return (ret); 318 | } 319 | 320 | /* 321 | * Format a length of time in seconds to "n days, n hrs, n mins, n secs". 322 | * Returns a newly allocated str. 323 | */ 324 | struct str * 325 | length_of_time(const time_t t) 326 | { 327 | struct str *buf = str_make(); 328 | int secs = t % 60; 329 | int mins = (t / 60) % 60; 330 | int hours = (t / 3600) % 24; 331 | int days = t / 86400; 332 | 333 | int show_zeroes = 0; 334 | 335 | if (days > 0) { 336 | str_appendf(buf, "%d %s", days, (days==1)?"day":"days"); 337 | show_zeroes = 1; 338 | } 339 | 340 | if (show_zeroes || (hours > 0)) { 341 | if (show_zeroes) str_append(buf, ", "); 342 | str_appendf(buf, "%d %s", hours, (hours==1)?"hr":"hrs"); 343 | show_zeroes = 1; 344 | } 345 | 346 | if (show_zeroes || (mins > 0)) { 347 | if (show_zeroes) str_append(buf, ", "); 348 | str_appendf(buf, "%d %s", mins, (mins==1)?"min":"mins"); 349 | show_zeroes = 1; 350 | } 351 | 352 | if (show_zeroes) str_append(buf, ", "); 353 | str_appendf(buf, "%d %s", secs, (secs==1)?"sec":"secs"); 354 | 355 | return buf; 356 | } 357 | 358 | ssize_t str_write(const struct str * const buf, const int fd) { 359 | return write(fd, buf->buf, buf->len); 360 | } 361 | 362 | size_t str_len(const struct str * const buf) { 363 | return buf->len; 364 | } 365 | 366 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 367 | -------------------------------------------------------------------------------- /str.h: -------------------------------------------------------------------------------- 1 | /* darkstat 3 2 | * copyright (c) 2001-2014 Emil Mikulic. 3 | * 4 | * str.h: string buffer with pool-based reallocation 5 | * 6 | * Permission to use, copy, modify, and distribute this file for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #ifndef __DARKSTAT_STR_H 19 | #define __DARKSTAT_STR_H 20 | 21 | #include "cdefs.h" 22 | 23 | #include <sys/types.h> 24 | #include <stdarg.h> 25 | #include <stdint.h> /* for uint64_t */ 26 | 27 | typedef long long signed int qd; /* as in appendf("%qd") */ 28 | typedef long long unsigned int qu; /* as in appendf("%qu") */ 29 | typedef long long unsigned int lld; /* as in printf("%lld") */ 30 | typedef long long unsigned int llu; /* as in printf("%llu") */ 31 | 32 | _Static_assert(sizeof(qd) == sizeof(int64_t), "qd must be int64_t sized"); 33 | _Static_assert(sizeof(qu) == sizeof(uint64_t), "qu must be uint64_t sized"); 34 | _Static_assert(sizeof(lld) == sizeof(int64_t), "lld must be int64_t sized"); 35 | _Static_assert(sizeof(llu) == sizeof(uint64_t), "llu must be uint64_t sized"); 36 | 37 | /* Note: the contents are 8-bit clean and not zero terminated! */ 38 | 39 | struct str; 40 | 41 | struct str *str_make(void); 42 | void str_free(struct str *s); 43 | void str_extract(struct str *buf, size_t *len, char **str); 44 | void str_appendn(struct str *buf, const char *s, const size_t len); 45 | void str_appendstr(struct str *buf, const struct str *s); 46 | 47 | #ifdef __GNUC__ 48 | /* amusing efficiency hack */ 49 | # include <string.h> 50 | # define str_append(buf, s) \ 51 | str_appendn(buf, s, \ 52 | (__builtin_constant_p(s) ? sizeof(s)-1 : strlen(s)) ) 53 | #else 54 | void str_append(struct str *buf, const char *s); 55 | #endif 56 | 57 | size_t xvasprintf(char **result, const char *format, va_list va) 58 | _printflike_(2, 0); 59 | size_t xasprintf(char **result, const char *format, ...) _printflike_(2, 3); 60 | void str_vappendf(struct str *s, const char *format, va_list va) 61 | _printflike_(2, 0); 62 | void str_appendf(struct str *s, const char *format, ...) _printflike_(2, 3); 63 | 64 | struct str *length_of_time(const time_t t); 65 | ssize_t str_write(const struct str * const buf, const int fd); 66 | size_t str_len(const struct str * const buf); 67 | 68 | #endif /* __DARKSTAT_STR_H */ 69 | /* vim:set ts=3 sw=3 tw=78 expandtab: */ 70 | -------------------------------------------------------------------------------- /test_headers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # copyright (c) 2011-2012 Emil Mikulic. 3 | 4 | a="\033[33;1m" 5 | z="\033[m" 6 | problem=0 7 | 8 | check_deps() { 9 | echo checking header dependencies... 10 | # Except for the c-ify output, every header should bring in all of its 11 | # dependencies, and be able to be included multiple times. 12 | src=_test_hdr.c 13 | obj=_test_hdr.o 14 | files=`ls *.h | fgrep -v -e graphjs.h -e stylecss.h -e favicon.h` 15 | 16 | for f in $files; do 17 | echo " * $f" 18 | cat >$src <<EOF 19 | #include "$f" 20 | void test_hdr_do_nothing(void) { } 21 | EOF 22 | if ! gcc -c $src 2>/dev/null; then 23 | echo "${a}===> $f can't be included by itself${z}" 24 | problem=1 25 | gcc -c $src 26 | else 27 | cat >$src <<EOF 28 | #include "$f" 29 | #include "$f" 30 | void test_hdr_do_nothing(void) { } 31 | EOF 32 | if ! gcc -c $src 2>/dev/null; then 33 | echo "${a}===> $f can't be included twice${z}" 34 | problem=1 35 | gcc -c $src 36 | fi 37 | fi 38 | done 39 | 40 | rm $src $obj 41 | } 42 | 43 | check_defines() { 44 | header=$1 45 | defines="(^|[^a-zA-Z_])($2)" 46 | files=$3 47 | 48 | echo checking $header users... 49 | 50 | # Check that files expecting defines include it. 51 | for file in `egrep -l "$defines" $files`; do 52 | if ! fgrep -q '#include "'$header'"' $file; then 53 | echo "${a}===> $file should include $header${z}" 54 | problem=1 55 | egrep --color=always "$defines" $file 56 | fi 57 | done 58 | 59 | # And that others don't. 60 | for file in `fgrep -l '#include "'$header'"' *.[ch]`; do 61 | if ! egrep -q "$defines" $file; then 62 | echo "${a}===> $file should not include $header${z}" 63 | problem=1 64 | fi 65 | done 66 | } 67 | 68 | # -=- 69 | 70 | check_deps 71 | 72 | # Check config.h: build a list of macros that are or could be defined. 73 | defines=`egrep '#define|#undef' config.h | cut -d# -f2 | cut -d' ' -f2 | 74 | sort -u | tr '\n' '|' | sed -e 's/|$//'` 75 | # Check against all files except itself. 76 | files=`ls *.[ch] | fgrep -v config.h` 77 | #echo "config.h defines: ($defines)" 78 | check_defines config.h "$defines" "$files" 79 | 80 | defines=`sed -e 's/# \+/#/;' < cdefs.h | grep '#define' | cut -d' ' -f2 | 81 | sed -e 's/(.\+/\\\\(/' | tr '\n' '|' | sed -e 's/|$//'` 82 | files=`ls *.[ch] | fgrep -v -e cdefs.h -e graphjs.h -e stylecss.h` 83 | check_defines cdefs.h "$defines" "$files" 84 | 85 | exit $problem 86 | 87 | # vim:set ts=2 sw=2 et: 88 | -------------------------------------------------------------------------------- /tidy_linktypes_list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "generating linktypes_list.h" >&2 3 | ( 4 | echo "/* autogenerated by $0 */" 5 | egrep '^#define DLT_' /usr/include/pcap/dlt.h | \ 6 | awk '{print $2}' | \ 7 | grep -v \( | \ 8 | while read dlt; do 9 | echo "#ifdef $dlt" 10 | echo "LT($dlt)" 11 | echo "#endif" 12 | done 13 | ) > linktypes_list.h 14 | # vim:set ts=2 sts=2 sw=2 tw=80 et: 15 | --------------------------------------------------------------------------------