view srvrlib/flod.c @ 0:c7f6b056b673

First import of vendor version
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 13:49:58 +0100
parents
children
line wrap: on
line source

/* Distributed Checksum Clearinghouse
 *
 * open, create, and check DCC server output flood mapped file
 *
 * Copyright (c) 2008 by Rhyolite Software, LLC
 *
 * This agreement is not applicable to any entity which sells anti-spam
 * solutions to others or provides an anti-spam solution as part of a
 * security solution sold to other entities, or to a private network
 * which employs the DCC or uses data provided by operation of the DCC
 * but does not provide corresponding data to other users.
 *
 * Permission to use, copy, modify, and distribute this software without
 * changes for any purpose with or without fee is hereby granted, provided
 * that the above copyright notice and this permission notice appear in all
 * copies and any distributed versions or copies are either unchanged
 * or not called anything similar to "DCC" or "Distributed Checksum
 * Clearinghouse".
 *
 * Parties not eligible to receive a license under this agreement can
 * obtain a commercial license to use DCC by contacting Rhyolite Software
 * at sales@rhyolite.com.
 *
 * A commercial license would be for Distributed Checksum and Reputation
 * Clearinghouse software.  That software includes additional features.  This
 * free license for Distributed ChecksumClearinghouse Software does not in any
 * way grant permision to use Distributed Checksum and Reputation Clearinghouse
 * software
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.3.103-1.58 $Revision$
 */

#include "srvr_defs.h"

FLOD_MMAPS *flod_mmaps;
DCC_PATH flod_mmap_path;
DCC_PATH flod_path;

static int mmap_fd = -1;


void
flod_mmap_path_set(void)
{
	fnm2rel_good(flod_mmap_path,
		     grey_on ? GREY_FLOD_NM : DCCD_FLOD_NM,
		     ".map");
	fnm2rel_good(flod_path,
		     grey_on ? GREY_FLOD_NM : DCCD_FLOD_NM,
		     0);
}



u_char
flod_mmap_sync(DCC_EMSG emsg, u_char touch)
{
	u_char result = 1;

	if (flod_mmap_path[0] == '\0')
		flod_mmap_path_set();

	if (flod_mmaps
	    && 0 > MSYNC(flod_mmaps, sizeof(*flod_mmaps), MS_SYNC)) {
		dcc_pemsg(EX_IOERR, emsg, "msync(%s): %s",
			  flod_mmap_path, ERROR_STR());
		result = 0;
		emsg = 0;
	}

	if (touch) {
		if (mmap_fd >= 0
		    && 0 > fsync(mmap_fd)) {
			dcc_pemsg(EX_IOERR, emsg, "fsync(%s): %s",
				  flod_mmap_path, ERROR_STR());
			result = 0;
			emsg = 0;
		}

		/* Ensure that the mtime changes at least occassionally.
		 * Several systems do not update the mtimes of files modified
		 * with mmap().  Some like BSD/OS delay changing the mtime until
		 * the file is accessed with read().  Others including
		 * filesystems on some versions of Linux apparently never change
		 * the mtime. */
		if (!dcc_set_mtime(emsg, flod_mmap_path, mmap_fd, 0)) {
			result = 0;
			emsg = 0;
		}
	}

	return result;
}



u_char					/* 1=no problems, 0=complaints */
flod_unmap(DCC_EMSG emsg, const DCCD_STATS *dccd_stats)
{
	u_char result = 1;

	if (!flod_mmap_sync(emsg, 0)) {
		result = 0;
		emsg = 0;
	}

	if (flod_mmaps) {
		if (dccd_stats)
			memcpy(&flod_mmaps->dccd_stats, dccd_stats,
			       sizeof(flod_mmaps->dccd_stats));
		if (0 > munmap((void *)flod_mmaps, sizeof(*flod_mmaps))) {
			dcc_pemsg(EX_IOERR, emsg, "munmap(%s,%d): %s",
				  flod_mmap_path,
				  ISZ(*flod_mmaps), ERROR_STR());
			result = 0;
			emsg = 0;
		}
		flod_mmaps = 0;
	}

	if (mmap_fd >= 0) {
		if (close(mmap_fd) < 0) {
			dcc_pemsg(EX_IOERR, emsg, "close(%s): %s",
				  flod_mmap_path, ERROR_STR());
			result = 0;
			emsg = 0;
		}
		mmap_fd = -1;
	}

	return result;
}



static int				/* 1=success, 0=retry, -1=fatal */
flod_mmap_try(DCC_EMSG emsg,
	      const DB_SN *sn,
	      u_char rw)		/* 0=rdonly/unlocked, 1=write/locked */
{
	int flags;
	struct stat sb;
	union {
	    FLOD_MMAPS	m;
	    u_char	b[1];
	} init;
	void *p;
	int i;

	if (flod_mmap_path[0] == '\0')
		flod_mmap_path_set();

	if (rw)
		mmap_fd = dcc_lock_open(emsg, flod_mmap_path, O_RDWR|O_CREAT,
					DCC_LOCK_OPEN_NOWAIT,
					DCC_LOCK_ALL_FILE, 0);
	else
		mmap_fd = dcc_lock_open(emsg, flod_mmap_path, O_RDONLY,
					DCC_LOCK_OPEN_NOLOCK, 0, 0);
	if (mmap_fd == -1)
		return (errno == EWOULDBLOCK) ? -1 : 0;

	if (fstat(mmap_fd, &sb) < 0) {
		dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
			  flod_mmap_path, ERROR_STR());
		flod_unmap(0, 0);
		return 0;
	}
	if (0 > fcntl(mmap_fd, F_SETFD, FD_CLOEXEC)) {
		dcc_pemsg(EX_IOERR, emsg, "fcntl(%s, FD_CLOEXEC): %s",
			  flod_mmap_path, ERROR_STR());
		flod_unmap(0, 0);
		return 0;
	}

	if (sb.st_size == 0 && rw) {
		memset(&init, 0, sizeof(init));
		strcpy(init.m.magic, FLOD_MMAP_MAGIC);
		if (sn)
			init.m.sn = *sn;
		i = write(mmap_fd, &init, sizeof(init));
		if (i < 0) {
			dcc_pemsg(EX_IOERR, emsg, "write(%s, init): %s",
				  flod_mmap_path, ERROR_STR());
			flod_unmap(0, 0);
			return -1;
		}
		if (i != ISZ(init)) {
			dcc_pemsg(EX_IOERR, emsg,
				  "write(%s, init)=%d instead of %d",
				  flod_mmap_path, i, ISZ(init));
			flod_unmap(0, 0);
			return -1;
		}
	} else {
		i = read(mmap_fd, &init, sizeof(init));
		if (i < 0) {
			dcc_pemsg(EX_IOERR, emsg, "read(%s, init): %s",
				  flod_mmap_path, ERROR_STR());
			flod_unmap(0, 0);
			return -1;
		}
		if (i < ISZ(init)) {
			if (i < sb.st_size) {
				dcc_pemsg(EX_IOERR, emsg, "read(%s, init)=%d",
					  flod_mmap_path, i);
				flod_unmap(0, 0);
				return -1;
			}
		}
	}

	if (sb.st_size >= (off_t)sizeof(init.m.magic)
	    && strcmp(init.m.magic, FLOD_MMAP_MAGIC)) {
		dcc_pemsg(EX_IOERR, emsg, "wrong magic in %s;"
			  " \"%s\" instead of \"%s\"",
			  flod_mmap_path, init.m.magic, FLOD_MMAP_MAGIC);
		flod_unmap(0, 0);
		return 0;
	}

	flags = rw ? (PROT_READ|PROT_WRITE) : PROT_READ;
	p = mmap(0, sizeof(*flod_mmaps), flags, MAP_SHARED, mmap_fd, 0);
	if (p == MAP_FAILED) {
		dcc_pemsg(EX_IOERR, emsg, "mmap(%s): %s",
			  flod_mmap_path, ERROR_STR());
		flod_unmap(0, 0);
		return 0;
	}
	flod_mmaps = p;

	if (sb.st_size == 0) {
		dcc_pemsg(EX_IOERR, emsg, "%s (re)created", flod_mmap_path);
	} else if (sb.st_size != sizeof(FLOD_MMAPS)) {
		dcc_pemsg(EX_IOERR, emsg, "%s has size %d instead of %d",
			  flod_mmap_path, (int)sb.st_size, ISZ(FLOD_MMAPS));
		flod_unmap(0, 0);
		return 0;
	}

	if (sn
	    && memcmp(&flod_mmaps->sn, sn, sizeof(flod_mmaps->sn))) {
		char sn1_buf[30], sn2_buf[32];
		dcc_pemsg(EX_IOERR, emsg, "s/n %s instead of %s in %s",
			  ts2str(sn1_buf, sizeof(sn1_buf), &flod_mmaps->sn),
			  ts2str(sn2_buf, sizeof(sn2_buf), sn),
			  flod_mmap_path);
		flod_unmap(0, 0);
		return 0;
	}

	return 1;
}



u_char					/* 0=failed, 1=mapped */
flod_mmap(DCC_EMSG emsg,
	  const DB_SN *sn,
	  const DCCD_STATS *dccd_stats,
	  u_char rw,			/* 0=rdonly/unlocked, 1=write/locked */
	  u_char trace)
{
	int i;

	if (!flod_unmap(emsg, dccd_stats) && emsg) {
		if (trace)
			dcc_trace_msg("%s", emsg);
		*emsg = '\0';
	}

	/* try to open the existing file */
	i = flod_mmap_try(emsg, sn, rw);

	/* finished if that went well or we are not allowed to fix things */
	if (i != 0 || !rw)
		return i > 0;

	/* delete the file if it is broken */
	if (emsg && *emsg != '\0') {
		if (trace)
			dcc_trace_msg("%s", emsg);
		*emsg = '\0';
	}
	if (0 > unlink(flod_mmap_path)
	    && errno != ENOENT) {
		dcc_pemsg(EX_IOERR, emsg, "unlink(%s): %s",
			  flod_mmap_path, ERROR_STR());
		flod_unmap(0, 0);
		return 0;
	}
	flod_unmap(0, 0);

	/* try to recreate the file */
	if (flod_mmap_try(emsg, sn, 1) > 0) {
		if (emsg) {
			if (trace)
				dcc_trace_msg("%s", emsg);
			*emsg = '\0';
		}
		return 1;
	}

	flod_unmap(0, 0);
	return 0;
}



const char *
flod_stats_printf(char *buf, int buf_len,
		  int st,		/* 0=off, 1=restarting, 2=on */
		  int oflods_total,
		  int oflods_active,
		  int iflods_active)
{
	snprintf(buf, buf_len,
		 "flood %s %3d streams  %d out active %d in",
		 st == 0 ? "off" : st == 1 ? "restarting" : "on",
		 oflods_total, oflods_active, iflods_active);
	return buf;
}



static void
mmap_fg_sub(char **bufp, int *buf_lenp,
	    const char **prefix, const char *str)
{
	int i;

	i = snprintf(*bufp, *buf_lenp, "%s%s", *prefix, str);
	if (*buf_lenp <= i) {
		*buf_lenp = 0;
	} else {
		*bufp += i;
		*buf_lenp -= i;
	}
	*prefix = "  ";
}


const char *
flodmap_fg(char *buf, int buf_len,
	     const char *prefix, const FLOD_MMAP *mp)
{
	char *buf0 = buf;

	if (!buf_len)
		return "";
	*buf = '\0';
	if (!mp)
		return buf0;

	if ((mp->flags & FLODMAP_FG_IN_OFF)
	    && (mp->flags & FLODMAP_FG_OUT_OFF)) {
		mmap_fg_sub(&buf, &buf_len, &prefix, "off");
	} else {
		if (mp->flags & FLODMAP_FG_IN_OFF)
			mmap_fg_sub(&buf, &buf_len, &prefix, "in off");
		if (mp->flags & FLODMAP_FG_OUT_OFF)
			mmap_fg_sub(&buf, &buf_len, &prefix, "out off");
	}

	if (mp->flags & FLODMAP_FG_ROGUE)
		mmap_fg_sub(&buf, &buf_len, &prefix, "crazy");

	if (mp->flags & FLODMAP_FG_REWINDING)
		mmap_fg_sub(&buf, &buf_len, &prefix, "rewinding");

	if (mp->flags & FLODMAP_FG_NEED_REWIND)
		mmap_fg_sub(&buf, &buf_len, &prefix, "need rewind");
	else if (mp->flags & FLODMAP_FG_FFWD_IN)
		mmap_fg_sub(&buf, &buf_len, &prefix, "need FFWD");

	if (mp->flags & FLODMAP_FG_PASSIVE)
		mmap_fg_sub(&buf, &buf_len, &prefix, "passive");
	if ((mp->flags & FLODMAP_FG_OUT_SRVR)
	    && !(mp->flags & FLODMAP_FG_PASSIVE))
		mmap_fg_sub(&buf, &buf_len, &prefix, "forced passive");

	if (!(mp->flags & FLODMAP_FG_IN_SRVR)) {
		if (mp->flags & FLODMAP_FG_SOCKS)
			mmap_fg_sub(&buf, &buf_len, &prefix, "SOCKS");
		if (mp->flags & FLODMAP_FG_NAT)
			mmap_fg_sub(&buf, &buf_len, &prefix, "NAT");
	} else {
		if (mp->flags & FLODMAP_FG_SOCKS)
			mmap_fg_sub(&buf, &buf_len, &prefix, "rejected SOCKS");
		if (mp->flags & FLODMAP_FG_NAT)
			mmap_fg_sub(&buf, &buf_len, &prefix, "rejected NAT");
	}

	if (mp->flags & FLODMAP_FG_LEAF)
		mmap_fg_sub(&buf, &buf_len, &prefix, "leaf");

	if (mp->flags & FLODMAP_FG_MAPPED)
		mmap_fg_sub(&buf, &buf_len, &prefix, "IDs mapped");

	if (mp->flags & FLODMAP_FG_FFWD_IN)
		mmap_fg_sub(&buf, &buf_len, &prefix, "want fastforward");

	if (mp->flags & FLODMAP_FG_USE_2PASSWD)
		mmap_fg_sub(&buf, &buf_len, &prefix, "2nd password");

	if (mp->flags & FLODMAP_FG_IPv4)
		mmap_fg_sub(&buf, &buf_len, &prefix, "IPv4");

	if (mp->flags & FLODMAP_FG_IPv6)
		mmap_fg_sub(&buf, &buf_len, &prefix, "IPv6");

	return buf0;
}



/* this function lets clients such as dbclean know when flooding is quiet */
int					/* -1=sick, 0=off, 1=not off */
flod_running(const char *st)
{
	char off_buf[11];
	int out, active, in;

	if (4 != sscanf(st, "flood %10s %d streams %d out active %d in",
			off_buf, &out, &active, &in))
		return 1;

	if (strcmp(off_buf, "off"))
		return 1;
	return active != 0 || in != 0;
}