/*
 * 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);
		}
		if (hp->flags.offerred_but_not_yet_accepted_by_client) {
			fprintf(fp, "\n\t# offerred_but_not_yet_accepted_by_client=1");
		}
		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",
				cctime((time_t)hp->expire)); 
			if (hp->flags.expire_prior_to_offer) {
				/* This is a lease that was converted to an offer, but has not (yet) been accepted by the client.
				   We should also show the expiration time for the original unexpired lease prior to when we 
				   converted it to an offer.
				*/
				fprintf(fp, "\n\t# expires_prior_to_offer_time_t=%d",
					hp->expire_prior_to_offer); 
				fprintf(fp, "\n\t# expires_prior_to_offer=%s",
					cctime((time_t)hp->expire_prior_to_offer));
			}
		} else
			fprintf(fp, "\n\t# expires=never");
		fprintf(fp,"\n");
	}
}


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,"#   Unexpired DHCP leases that were converted to DHCP offers that were not (yet)\n");
    fprintf(fp,"#   accepted by the client are included showing the expiration time of the original\n");
    fprintf(fp,"#   unexpired lease, not the expiration time of the offer.\n");

    fprintf(fp,"#\n");
    n = 0;
    for (hp=(struct host*)hash_FirstEntry(boundhshtbl);hp;
		hp=(struct host*)hash_NextEntry(boundhshtbl)){
        fprintf(fp,"# %s %s",hp->hostname->string, inet_ntoa(hp->iaddr));
		if (hp->flags.expire_prior_to_offer) 
        	fprintf(fp," expires: %s", ctime((time_t*)&hp->expire_prior_to_offer));
		else
        	fprintf(fp," expires: %s", 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;
}
