/*
 * dumptab.c - handles dumping the database
 */

#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>			/* inet_ntoa */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


#include "defaults.h"
#include "bootp.h"
#include "hash.h"
#include "hwaddr.h"
#include "report.h"
#include "patchlevel.h"
#include "bootpd.h"
#include "dhcpd.h"
#include "dhcp.h"
#include "my_syslog.h"
#include "utils.h"

#ifdef	__STDC__
#define P(args) args
#else
#define P(args) ()
#endif

static void dump_generic P((FILE *, struct shared_bindata *));
static void dump_host P((FILE *, struct host *));
static int list_ipaddresses P((FILE *, struct in_addr_list *));
extern hash_tbl *boundhshtbl; 
static void print_packet_counts P((FILE *));
#undef P

/* let's always provide dumptab and dumpbindings support, instead
   of requiring compilation with DEBUG defined */
#if 0
void
dumptab(filename)
	char *filename;
{
	report(LOG_ERR, "No dumptab support without DEBUG defined");
}

void
dumpbindings(filename)
	char *filename;
{
	report(LOG_ERR, "No dumpbindings support without DEBUG defined");
}

#else 

/*
 * Dump the internal memory database to bootpd_dump.
 */

void
dumptab(filename)
	char *filename;
{
	int n;
	struct host *hp;
	FILE *fp;
	long t;
	/* Print symbols in alphabetical order for reader's convenience. */
	static char legend[] = "#\n# Legend:\t(see bootptab.5)\n\
#\tfirst field -- hostname (not indented)\n\
#\tba -- broadcast address\n\
#\tbe -- bootp exclude\n\
#\tbf -- bootfile\n\
#\tbi -- bootp include\n\
#\tbs -- bootfile size in 512-octet blocks\n\
#\tcf -- client fqdn\n\
#\tcl -- client identifier\n\
#\tcr -- non-local source routing enable/disable\n\
#\tcs -- cookie servers\n\
#\tdd -- deny client dynamic\n\
#\tde -- deny client\n\
#\tdf -- dump file name\n\
#\tdl -- length of leases\n\
#\tdn -- domain name\n\
#\tds -- domain name servers\n\
#\tdy -- dynamic address\n\
#\tef -- extension file\n\
#\ten -- ethernet encapsulation\n\
#\tex -- exec file (YORK_EX_OPTION)\n\
#\tfi -- default finger servers\n\
#\tgw -- gateways\n\
#\tha -- hardware address\n\
#\thd -- home directory for bootfiles\n\
#\thn -- host name set for client\n\
#\tht -- hardware type\n\
#\tif -- IP forwarding enable/disable\n\
#\tim -- impress servers\n\
#\tip -- host IP address\n\
#\tir -- default Internet Relay Chat (IRC) servers\n\
#\tkg -- TCP keepalive garbage\n\
#\tlg -- log servers\n\
#\tlp -- LPR servers\n\
#\tma -- Simple Mail Transport Protocol (SMTP) servers\n\
#\tmd -- perform mask discovery\n\
#\tms -- message size\n\
#\tmu -- mask supplier\n\
#\tmw -- min wait (secs)\n\
#\tnb -- netbios over TCP/IP Datagram Distribution servers\n\
#\tnn -- Network News Transport Protocol (NNTP) servers\n\
#\tno -- netbios node type\n\
#\tnr -- not requested options are sent\n\
#\tns -- IEN-116 name servers\n\
#\tnt -- NTP servers (RFC 1129)\n\
#\tpd -- NIS+ domain\n\
#\tpo -- Post Office Protocol (POP3) servers\n\
#\tps -- NIS+ servers\n\
#\tra -- reply address override\n\
#\trb -- rebind (seconds)\n\
#\trd -- perform router discovery\n\
#\trl -- resource location protocol servers\n\
#\trn -- renew (seconds)\n\
#\tro -- roaming\n\
#\trp -- root path\n\
#\trs -- router solicitation address\n\
#\tsa -- boot server address\n\
#\tsc -- netbios scope\n\
#\tsl -- all subnets are local\n\
#\tsm -- subnet mask\n\
#\tsr -- static route\n\
#\tsw -- swap server\n\
#\ttc -- template host (points to similar host entry)\n\
#\ttd -- TFTP directory\n\
#\tte -- trailer encapsulation\n\
#\ttl -- TCP default time-to-live (TTL)\n\
#\tto -- time offset (seconds)\n\
#\tts -- time servers\n\
#\ttt -- default IP time-to-live (TTL)\n\
#\tvm -- vendor magic number\n\
#\tvs -- vendor size\n\
#\tws -- netbios name servers\n\
#\tww -- default World Wide Web (WWW) servers\n\
#\txd -- X Windows system display manager\n\
#\txf -- X Windows system font servers\n\
#\tyd -- YP (NIS) domain\n\
#\tys -- YP (NIS) servers\n\
#\tTn -- generic option tag n\n\
\n";

	report(LOG_INFO, "Received request to dump bootp server database");

	/*
	 * Open bootpd.dump file.
	 */
	if ((fp = open_for_writing(filename)) == NULL) {
		report(LOG_ERR, "error opening \"%s\", won't dump bootp server database", filename);
		/*  let's stop exiting on this error, it really isn't fatal.  Instead just return.
		exit(1); 
		*/
		return;
	}
	t = time(NULL);
	fprintf(fp, "\n# %s %s.%d\n", progname, VERSION, PATCHLEVEL);
	fprintf(fp, "# %s: dump of bootp server database.\n", filename);
	fprintf(fp, "# Dump taken %s", ctime(&t));
	fwrite(legend, 1, sizeof(legend) - 1, fp);

	n = 0;
	for (hp = (struct host *) hash_FirstEntry(nmhashtable); hp != NULL;
		 hp = (struct host *) hash_NextEntry(nmhashtable)) {
		dump_host(fp, hp);
		fprintf(fp, "\n");
		n++;
	}
	fclose(fp);

	report(LOG_INFO, "dumped %d entries to \"%s\".", n, filename);
}



/*
 * Dump all the available information on the host pointed to by "hp".
 * The output is sent to the file pointed to by "fp".
 */

static void
dump_host(fp, hp)
	FILE *fp;
	struct host *hp;
{
	unsigned int ui;
	/* Print symbols in alphabetical order for reader's convenience. */
	if (hp) {
		fprintf(fp, "%s:", (hp->hostname ?
							hp->hostname->string : "?"));
		if (hp->flags.broadcast_addr) {
			fprintf(fp, "\\\n\t:ba=%s:", inet_ntoa(hp->broadcast_addr));
		}
		if (hp->flags.bootp_exclude) {
			fprintf(fp, "\\\n\t:be=\"%s\":", hp->bootp_exclude->string);
		}
		if (hp->flags.bootp_include) {
			fprintf(fp, "\\\n\t:bi=\"%s\":", hp->bootp_include->string);
		}
		if (hp->flags.bootfile) {
			fprintf(fp, "\\\n\t:bf=%s:", hp->bootfile->string);
		}
		if (hp->flags.bootsize) {
			fprintf(fp, "\\\n\t:bs=");
			if (hp->flags.bootsize_auto) {
				fprintf(fp, "auto:");
			} else {
				fprintf(fp, "%d:", hp->bootsize);
			}
		}
		if (hp->flags.client_fqdn) {
			fprintf(fp, "\\\n\t:cf=0x%02X:", hp->client_fqdn);
		}
		if(hp->flags.cliid){
			fprintf(fp,"\\\n\t:cl=");
			for(ui=0;ui+1<hp->cliid->length;ui++)
				fprintf(fp,"%02X.",hp->cliid->data[ui]);
			fprintf(fp,"%02X",hp->cliid->data[ui]);
		}
		if (hp->flags.ip_source_routing) {
			fprintf(fp, "\\\n\t:cr=%d:", hp->ip_source_routing);
		}
		if (hp->flags.cookie_server) {
			fprintf(fp, "\\\n\t:cs=");
			if (! list_ipaddresses(fp, hp->cookie_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.deny_client_dynamic) {
			fprintf(fp, "\\\n\t:dd:");
		}
		if (hp->flags.deny_client) {
			fprintf(fp, "\\\n\t:de:");
		}
		if (hp->flags.dump_file) {
			fprintf(fp, "\\\n\t:df=%s:", hp->dump_file->string);
		}
		if (hp->flags.dynamic) {
			fprintf(fp, "\\\n\t:dy:");
		}
		if (hp->flags.lease) {
			fprintf(fp, "\\\n\t:dl=%d:", hp->lease);
		}
		if (hp->flags.domain_name) {
			fprintf(fp, "\\\n\t:dn=%s:", hp->domain_name->string);
		}
		if (hp->flags.domain_server) {
			fprintf(fp, "\\\n\t:ds=");
			if (! list_ipaddresses(fp, hp->domain_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.exten_file) {
			fprintf(fp, "\\\n\t:ef=%s:", hp->exten_file->string);
		}
		if (hp->flags.ethernet_encapsulation) {
			fprintf(fp, "\\\n\t:en=%d:", hp->ethernet_encapsulation);
		}
		if (hp->flags.exec_file) {
			fprintf(fp, "\\\n\t:ex=%s:", hp->exec_file->string);
		}
		if (hp->flags.finger_server) {
			fprintf(fp, "\\\n\t:fi=");
			if (! list_ipaddresses(fp, hp->finger_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.gateway) {
			fprintf(fp, "\\\n\t:gw=");
			if (! list_ipaddresses(fp, hp->gateway)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		/* FdC: swap_server (see below) */
		if (hp->flags.homedir) {
			fprintf(fp, "\\\n\t:hd=%s:", hp->homedir->string);
		}
		/* FdC: dump_file (see above) */
		/* FdC: domain_name (see above) */
		/* FdC: root_path (see below) */
		if (hp->flags.name_switch && hp->flags.send_name) {
			fprintf(fp, "\\\n\t:hn:");
		}
		if (hp->flags.htype) {
			int hlen = haddrlength(hp->htype);
			fprintf(fp, "\\\n\t:ht=%u:", (unsigned) hp->htype);
			if (hp->flags.haddr) {
				fprintf(fp, "ha=\"%s\":",
						haddrtoa(hp->haddr, hlen));
			}
		}
		if (hp->flags.ip_forwarding) {
			fprintf(fp, "\\\n\t:if=%d:", hp->ip_forwarding);
		}
		if (hp->flags.impress_server) {
			fprintf(fp, "\\\n\t:im=");
			if (! list_ipaddresses(fp, hp->impress_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		/* NetBSD: swap_server (see below) */
		if (hp->flags.iaddr) {
			fprintf(fp, "\\\n\t:ip=%s:", inet_ntoa(hp->iaddr));
		}
		if (hp->flags.irc_server) {
			fprintf(fp, "\\\n\t:ir=");
			if (! list_ipaddresses(fp, hp->irc_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.tcp_keepalive_garbage) {
			fprintf(fp, "\\\n\t:kg=%d:", hp->tcp_keepalive_garbage);
		}
		if (hp->flags.log_server) {
			fprintf(fp, "\\\n\t:lg=");
			if (! list_ipaddresses(fp, hp->log_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.lpr_server) {
			fprintf(fp, "\\\n\t:lp=");
			if (! list_ipaddresses(fp, hp->lpr_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.smtp_server) {
			fprintf(fp, "\\\n\t:ma=");
			if (! list_ipaddresses(fp, hp->smtp_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.perform_mask_discovery) {
			fprintf(fp, "\\\n\t:md=%d:", hp->perform_mask_discovery);
		}
		if (hp->flags.msg_size) {
			fprintf(fp, "\\\n\t:ms=%d:", hp->msg_size);
		}
		if (hp->flags.mask_supplier) {
			fprintf(fp, "\\\n\t:mu=%d:", hp->mask_supplier);
		}
		if (hp->flags.min_wait) {
			fprintf(fp, "\\\n\t:mw=%d:", hp->min_wait);
		}
		if (hp->flags.nbdd_server) {
			fprintf(fp, "\\\n\t:nd=");
			if (! list_ipaddresses(fp, hp->nbdd_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.nntp_server) {
			fprintf(fp, "\\\n\t:nn=");
			if (! list_ipaddresses(fp, hp->nntp_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if(hp->flags.node_type){
			fprintf(fp,"\\\n\t:no=");
			switch(hp->node_type){
			case 1:fprintf(fp,"b:");break;
			case 2:fprintf(fp,"p:");break;
			case 4:fprintf(fp,"m:");break;
			case 8:fprintf(fp,"h:");break;
			default:fprintf(fp,"%d:",hp->node_type);
			}
		}
		if(hp->flags.not_rq)
			fprintf(fp,"\\\n\t:nr:");
		if (hp->flags.name_server) {
			fprintf(fp, "\\\n\t:ns=");
			if (! list_ipaddresses(fp, hp->name_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.ntp_server) {
			fprintf(fp, "\\\n\t:nt=");
			if (! list_ipaddresses(fp, hp->ntp_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.nisplus_domain) {
			fprintf(fp, "\\\n\t:pd=%s:",
					hp->nisplus_domain->string);
		}
		if (hp->flags.pop3_server) {
			fprintf(fp, "\\\n\t:po=");
			if (! list_ipaddresses(fp, hp->pop3_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.nisplus_server) {
			fprintf(fp, "\\\n\t:ps=");
			if (! list_ipaddresses(fp, hp->nisplus_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.reply_addr) {
			fprintf(fp, "\\\n\t:ra=%s:", inet_ntoa(hp->reply_addr));
		}
		if (hp->flags.rebind) {
			fprintf(fp, "\\\n\t:rb=%d:", hp->rebind);
		}
		if (hp->flags.perform_router_discovery) {
			fprintf(fp, "\\\n\t:rd=%d:", hp->perform_router_discovery);
		}
		if (hp->flags.rlp_server) {
			fprintf(fp, "\\\n\t:rl=");
			if (! list_ipaddresses(fp, hp->rlp_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.renew) {
			fprintf(fp, "\\\n\t:rn=%d:", hp->renew);
		}
		if (hp->flags.roaming) {
			fprintf(fp, "\\\n\t:ro:");
		}
		if (hp->flags.root_path) {
			fprintf(fp, "\\\n\t:rp=%s:", hp->root_path->string);
		}
		if (hp->flags.router_solicitation_addr) {
			fprintf(fp, "\\\n\t:rs=%s:", inet_ntoa(hp->router_solicitation_addr));
		}
		if (hp->flags.bootserver) {
			fprintf(fp, "\\\n\t:sa=%s:", inet_ntoa(hp->bootserver));
		}
		if (hp->flags.nb_scope){
			fprintf(fp, "\\\n\t:sc=%s:", hp->nb_scope->string);
		}
		if (hp->flags.nbns_server) {
			fprintf(fp, "\\\n\t:ws=");
			if (! list_ipaddresses(fp, hp->nbns_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.subnet_mask) {
			fprintf(fp, "\\\n\t:sm=%s:", inet_ntoa(hp->subnet_mask));
		}
		if (hp->flags.static_route) {
			fprintf(fp, "\\\n\t:sr=");
			if (! list_ipaddresses(fp, hp->static_route)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.all_subnets_are_local) {
			fprintf(fp, "\\\n\t:sl=%d:", hp->all_subnets_are_local);
		}
		if (hp->flags.swap_server) {
			fprintf(fp, "\\\n\t:sw=%s:", inet_ntoa(hp->subnet_mask));
		}
		if (hp->flags.tftpdir) {
			fprintf(fp, "\\\n\t:td=%s:", hp->tftpdir->string);
		}
		if (hp->flags.trailer_encapsulation) {
			fprintf(fp, "\\\n\t:te=%d:", hp->trailer_encapsulation);
		}
		if (hp->flags.tcp_ttl) {
			fprintf(fp, "\\\n\t:tl=%d:", hp->tcp_ttl);
		}
		/* NetBSD: rootpath (see above) */
		/* NetBSD: domainname (see above) */
		/* NetBSD: dumpfile (see above) */
		if (hp->flags.time_offset) {
			fprintf(fp, "\\\n\t:to=%ld:", hp->time_offset);
		}
		if (hp->flags.time_server) {
			fprintf(fp, "\\\n\t:ts=");
			if (! list_ipaddresses(fp, hp->time_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.ip_ttl) {
			fprintf(fp, "\\\n\t:tt=%d:", hp->ip_ttl);
		}
		if (hp->flags.vm_cookie) {
			fprintf(fp, "\\\n\t:vm=");
			if (!bcmp(hp->vm_cookie, vm_rfc1048, 4)) {
				fprintf(fp, "rfc1048:");
			} else if (!bcmp(hp->vm_cookie, vm_cmu, 4)) {
				fprintf(fp, "cmu:");
			} else {
				fprintf(fp, "%d.%d.%d.%d:",
						(int) ((hp->vm_cookie)[0]),
						(int) ((hp->vm_cookie)[1]),
						(int) ((hp->vm_cookie)[2]),
						(int) ((hp->vm_cookie)[3]));
			}
		}
		if (hp->flags.www_server) {
			fprintf(fp, "\\\n\t:ww=");
			if (! list_ipaddresses(fp, hp->www_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.xwindows_display_manager) {
			fprintf(fp, "\\\n\t:xd=");
			if (! list_ipaddresses(fp, hp->xwindows_display_manager)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.xwindows_font_server) {
			fprintf(fp, "\\\n\t:xf=");
			if (! list_ipaddresses(fp, hp->xwindows_font_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		if (hp->flags.nis_domain) {
			fprintf(fp, "\\\n\t:yd=%s:",
					hp->nis_domain->string);
		}
		if (hp->flags.nis_server) {
			fprintf(fp, "\\\n\t:ys=");
			if (! list_ipaddresses(fp, hp->nis_server)) {
				report(LOG_ERR, "  hostname was %s", (hp->hostname ? hp->hostname->string : "unknown"));
			}
			fprintf(fp, ":");
		}
		/* XXX - Add new tags here (or above,
		 * so they print in alphabetical order).
		 */
		if (hp->flags.generic) {
			dump_generic(fp, hp->generic);
		}
		if (hp->flags.cable) {
			fprintf(fp, "\n\t# cable=%s",
					hp->cable->string);
		}
		fprintf(fp, "\n\t# code=%s",
			((hp->code < 6) ? codename[hp->code] : "UNKNOWN"));
		if (hp->flags.expire) {
			fprintf(fp, "\n\t# expires_time_t=%d",
				hp->expire); 
			fprintf(fp, "\n\t# expires=%s",
				ctime((time_t*)&hp->expire)); /* ctime() adds a newline at end */
		} else
			fprintf(fp, "\n\t# expires=never\n");
		/* note we finished the last item we printed with a newline */
	}
}


static void
dump_generic(fp, generic)
	FILE *fp;
	struct shared_bindata *generic;
{
	u_char *bp = generic->data;
	u_char *ep = bp + generic->length;
	u_char tag;
	int len;

	while (bp < ep) {
		tag = *bp++;
		if (tag == TAG_PAD)
			continue;
		if (tag == TAG_END)
			return;
		len = *bp++;
		if (bp + len > ep) {
			fprintf(fp, " #junk in generic! :");
			return;
		}
		fprintf(fp, "\\\n\t:T%d=", tag);
		while (len) {
			fprintf(fp, "%02X", *bp);
			bp++;
			len--;
			if (len)
				fprintf(fp, ".");
		}
		fprintf(fp, ":");
	}
}



/*
 * Dump an entire struct in_addr_list of IP addresses to the indicated file.
 *
 * The addresses are printed in standard ASCII "dot" notation and separated
 * from one another by a single space.  A single leading space is also
 * printed before the first adddress.
 *
 * Null lists produce no output (and no error).
 * Return nonzero on success, zero on failure
 */

static int
list_ipaddresses(fp, ipptr)
	FILE *fp;
	struct in_addr_list *ipptr;
{
	unsigned count;
	struct in_addr *addrptr;
	int result = 1;

	if (ipptr) {
		count = ipptr->addrcount;
		addrptr = ipptr->addr;
		while (count > 0) {
			/* trying to find a bug - sometimes addrptr is null, though it should never be - ist 10/5/97
			fprintf(fp, "%s", inet_ntoa(*addrptr++));
			*/
			if (addrptr) {
				fprintf(fp, "%s", inet_ntoa(*addrptr++));
			} else {
				fprintf(fp, "(unknown)");
				addrptr++;
				/* we don't have 'hp' here, so we can't print the hostname here */
				report(LOG_ERR, " list_ipaddress(): error: addrptr==NULL.  Hostname on next line...");
				result = 0; /* indicate failure */
			}
			count--;
			if (count)
				fprintf(fp, ", ");
		}
	}
	return result;
}

void dumpbindings(filename)
char *filename;
{
    FILE *fp;
    time_t t;
    struct host *hp;
    struct order_list *p;
    unsigned int n;

	report(LOG_INFO, "Received request to dump current bindings database");

    if ((fp = open_for_writing(filename)) == NULL) {
		report(LOG_ERR, "error opening \"%s\", won't dump current bindings database", filename);
		/*  let's stop exiting on this error, it really isn't fatal.  Instead just return.
		exit(1);
		*/
		return;
    }
    t = time(NULL);
    fprintf(fp, "# dhcpd Current Bindings Dump taken %s\n", ctime(&t));

	print_packet_counts(fp);

	/* summarize current finite leases -- they in boundhshtbl */
    fprintf(fp,"# Summary of Current Bindings\n");
    fprintf(fp,"#\n");
	fprintf(fp,"#   Current bindings are defined as all unexpired DHCP leases, and\n");
	fprintf(fp,"#   all BootP assignment for *dynamic* addresses, but not *static* addresses.\n");
    fprintf(fp,"#   Expired leases are included until they are reclaimed by garbage collection.\n");
    fprintf(fp,"#   DHCP offers that were not (yet) accepted by the client are included (as very short\n");
    fprintf(fp,"#   leases) until they are reclaimed by garbage collection.\n");
    fprintf(fp,"#\n");
    n = 0;
    for (hp=(struct host*)hash_FirstEntry(boundhshtbl);hp;
		hp=(struct host*)hash_NextEntry(boundhshtbl)){
        fprintf(fp,"# %s %s expires: %s",hp->hostname->string,
					inet_ntoa(hp->iaddr), 
					hp->flags.expire ? ctime((time_t*)&hp->expire) : "never\n");
#ifdef BRNO
		/* Also print haddr.
		   For Petr Lampa <lampa@fee.vutbr.cz>, Technical University of Brno.
		   Since this could break existing folks who parse this output, it is not the default behavior.
		*/
		if (hp->flags.haddr && hp->flags.htype) {
			int hlen = haddrlength(hp->htype);
			fprintf(fp, "ha=\"%s\":", haddrtoa(hp->haddr, hlen));
		}
#endif
        n++;
    }
    fprintf(fp,"#\n# Total Current Bindings=%u\n",n);
	
	if (leases != n)
		fprintf(fp,"\n# ERROR: Internal 'leases' counter says there are %u current bindings\n", leases);
	
    fprintf(fp,"\n\n# Detailed Dump of each Current Binding\n\n");
    n=0;
    for (hp = (struct host *) hash_FirstEntry(boundhshtbl); hp != NULL;
         hp = (struct host *) hash_NextEntry(boundhshtbl)) {
         dump_host(fp,hp);
         fprintf(fp,"\n");
         n++;
    }
    /* fprintf(fp,"# Total Current Bindings=%u\n\n",n); */
    fclose(fp);

	report(LOG_INFO, "Finished dumping current bindings database to \"%s\".", filename);
}

#endif 

static void
print_packet_counts(fp)
	FILE *fp;
{
	if (!fp) {
		report(LOG_ERR, "print_packet_counts: fp==NULL");
		return;
	}

	/* stats of types of packets received and sent */
	fprintf(fp,"# Packet Counts\n");
	fprintf(fp,"#   bootp requests received=%u\n",bootp_requests_received);
	fprintf(fp,"#   bootp replies sent=%u\n",bootp_replys_sent);
	fprintf(fp,"#   dhcp discovers received=%u\n",discovers_received);
	fprintf(fp,"#   dhcp offers sent=%u\n",offers_sent);
	fprintf(fp,"#   dhcp requests received=%u\n",requests_received);
	fprintf(fp,"#   dhcp acks sent=%u\n",acks_sent);
	fprintf(fp,"#   dhcp nacks sent=%u\n",nacks_sent);
	fprintf(fp,"#   dhcp declines received=%u\n",declines_received);
	fprintf(fp,"#   dhcp informs received=%u\n",informs_received);
	fprintf(fp,"#   dhcp releases received=%u\n\n\n",releases_received);

	return;
}

void dumpstats(filename)
char *filename;
{
    FILE *fp;
	long t;

	report(LOG_INFO, "Received request to dump statistics");

    if ((fp = open_for_writing(filename)) == NULL) {
		report(LOG_ERR, "error opening \"%s\", won't dump statistics", filename);
		return;
    }

    t = time(NULL);

    fprintf(fp, "+++ dhcpd statistics dump +++ (%ld)\n", t);
    /* 
	fprintf(fp, "# dhcpd Current Statistics Dump taken %s", ctime(&t));
    fprintf(fp, "# time=%ld\n", t);
	*/

	print_packet_counts(fp);

    fprintf(fp, "--- dhcpd statistics dump --- (%ld)\n", t);

    fclose(fp);
	report(LOG_INFO, "Finished dumping statistics to \"%s\".", filename);

	return;
}
