/*
 * Copyright (c) 2002, 2003, 2004 Niels Provos <provos@citi.umich.edu>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <sys/types.h>
#include <sys/param.h>

#include "config.h"
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#include <sys/stat.h>
#include <sys/tree.h>
#include <sys/queue.h>
#include <sys/socket.h>

#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <dnet.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <fnmatch.h>

#include <pcap.h>

#undef timeout_pending
#undef timeout_initialized

#include <event.h>

#include "honeyd.h"
#include "personality.h"
#include "template.h"
#include "subsystem.h"
#include "condition.h"
#include "interface.h"
#include "parser.h"
#include "ethernet.h"
#include "arp.h"
#include "pool.h"
#include "dhcpclient.h"
#include "util.h"
#include "log.h"

/* Tailq that holds all subsystems */
struct subsystemqueue subsystems;

/* Tree that contains all templates */
struct templtree templates;

/* Counter for addresses in 169.254/16 that we assign for DHCP */
static uint16_t privip_counter = 1;

int
templ_compare(struct template *a, struct template *b)
{
	return (strcmp(a->name, b->name));
}

SPLAY_GENERATE(templtree, template, node, templ_compare);

int
port_compare(struct port *a, struct port *b)
{
	int diff;

	diff = a->proto - b->proto;
	if (diff) {
		return (diff);
	} else {
		/* safe because number in uint16_t */
		return ((int)a->number - (int)b->number);
	}
}

SPLAY_PROTOTYPE(porttree, port, node, port_compare);

SPLAY_GENERATE(porttree, port, node, port_compare);

void
config_init(void)
{
	TAILQ_INIT(&subsystems);
	SPLAY_INIT(&templates);

	no_spoof.new_src.addr_type = ADDR_TYPE_NONE;	/* default is no source spoofing... */
	no_spoof.new_dst.addr_type = ADDR_TYPE_NONE;	/* ... and no destination spoofing */
}

void
config_read(char *config)
{
	extern int honeyd_ignore_parse_errors;
	FILE *fp;

	if ((fp = fopen(config, "r")) == NULL)
		err(1, "fopen(%s)", config);
	if (parse_configuration(fp, config) == -1 &&
	    !honeyd_ignore_parse_errors)
		errx(1, "parsing configuration file failed");

	fclose(fp);
}

struct template *
template_find(const char *name)
{
	struct template tmp;

	tmp.name = (char *)name;
	return (SPLAY_FIND(templtree, &templates, &tmp));
}

void
template_list_glob(struct evbuffer *buffer, const char *pattern)
{
	struct template *tmpl, *last = NULL;
	struct evbuffer *tmp = NULL;
	int count = 0;

	tmpl = template_find(pattern);
	if (tmpl != NULL) {
		template_print(buffer, tmpl);
		return;
	}	

	if ((tmp = evbuffer_new()) == NULL)
		err(1, "%s: malloc");

	SPLAY_FOREACH(tmpl, templtree, &templates) {
		/* Ignore it if it does not match */
		if (fnmatch(pattern, tmpl->name, 0))
			continue;
		count++;
		evbuffer_add_printf(tmp, "%4d. %s (%s)\n",
		    count,
		    tmpl->name,
		    tmpl->person != NULL ? tmpl->person->name : "undefined");

		last = tmpl;
	}

	if (count == 1) {
		template_print(buffer, last);
	} else {
		evbuffer_add_buffer(buffer, tmp);
	}
	evbuffer_free(tmp);
}

/*
 * Checks if condition for each template in the list is matched.
 * Return the first template that we match.
 */

struct template *
template_dynamic(const struct template *tmpl, const struct ip_hdr *ip,
    u_short iplen)
{
	struct template *save = NULL;
	struct condition *cond;

	TAILQ_FOREACH(cond, &tmpl->dynamic, next) {
		if (save == NULL)
			save = cond->tmpl;

		/* See if we match this template and return it on success */
		if (cond->match == NULL ||
		    cond->match(cond->tmpl, ip, iplen, cond->match_arg))
			return (cond->tmpl);
	}

	/* We need to return something, so return the first non-NULL */
	return (save);
}

struct template *
template_find_best(const char *addr, const struct ip_hdr *ip, u_short iplen)
{
	struct template *tmpl;

	tmpl = template_find(addr);
	if (tmpl == NULL)
		tmpl = template_find("default");
	
	if (tmpl != NULL && tmpl->flags & TEMPLATE_DYNAMIC)
		tmpl = template_dynamic(tmpl, ip, iplen);

	return (tmpl);
}

struct template *
template_create(const char *name)
{
	extern rand_t *honeyd_rand;
	struct template *tmpl;

	if (template_find(name))
		return (NULL);

	if ((tmpl = calloc(1, sizeof(struct template))) == NULL)
		err(1, "%s: calloc", __func__);

	tmpl->name = strdup(name);

	/* UDP ports are closed by default */
	tmpl->udp.status = PORT_RESET;

	SPLAY_INIT(&tmpl->ports);
	SPLAY_INSERT(templtree, &templates, tmpl);

	/* Configured subsystems */
	TAILQ_INIT(&tmpl->subsystems);
	TAILQ_INIT(&tmpl->dynamic);

	/* No spoofing, by default */
	tmpl->spoof = no_spoof;

	/* Crank ref counter */
	tmpl->refcnt++;

	/* Create a drift */
	tmpl->drift = 1.0 + (rand_uint16(honeyd_rand) % 140 - 70)/100000.0;

	return (tmpl);
}

int
template_iterate(int (*f)(struct template *, void *), void *arg)
{
	struct template *tmpl;

	SPLAY_FOREACH(tmpl, templtree, &templates) {
		if ((*f)(tmpl, arg) == -1)
			return (-1);
	}

	return (0);
}

/*
 * Removes all configured templates from the system, so that the
 * configuration file can be re-read.
 */

void
template_free_all(int how)
{
	struct template *tmpl;

	while ((tmpl = SPLAY_ROOT(&templates)) != NULL) {
		SPLAY_REMOVE(templtree, &templates, tmpl);
		if (how == TEMPLATE_FREE_REGULAR)
			template_free(tmpl);
		else if (!(tmpl->flags & TEMPLATE_DYNAMIC_CHILD))
			template_deallocate(tmpl);
	}
}

/* Remove a template from the system */

void
template_remove(struct template *tmpl)
{
	/* Remove ourselves from the searchable index */
	if (template_find(tmpl->name) == tmpl)
		SPLAY_REMOVE(templtree, &templates, tmpl);
}

/* Insert a template into the system */

int
template_insert(struct template *tmpl)
{
	/* Insert ourselves into the searchable index */
	if (template_find(tmpl->name) != NULL)
		return (-1);
	SPLAY_INSERT(templtree, &templates, tmpl);

	return (0);
}

void
template_deallocate(struct template *tmpl)
{
	struct condition *cond;
	struct port *port;

	/* Remove ourselves from the searchable index */
	if (template_find(tmpl->name) == tmpl)
		SPLAY_REMOVE(templtree, &templates, tmpl);

	/* Free conditions for dynamic templates */
	for (cond = TAILQ_FIRST(&tmpl->dynamic); cond != NULL;
	    cond = TAILQ_FIRST(&tmpl->dynamic)) {
		TAILQ_REMOVE(&tmpl->dynamic, cond, next);

		template_free(cond->tmpl);
		if (cond->match_arg)
			free(cond->match_arg);
		free(cond);
	}
	
	/* Remove ports from template */
	while ((port = SPLAY_ROOT(&tmpl->ports)) != NULL)
		port_free(tmpl, port);

	if (tmpl->person != NULL)
		personality_declone(tmpl->person);

	if (tmpl->ethernet_addr != NULL) {
		struct arp_req *req = arp_find(tmpl->ethernet_addr);
		/*
		 * Templates that are not bound to IP addresses do not
		 * have an associated arp request object.
		 */
		if (req != NULL)
			arp_free(req);
		free(tmpl->ethernet_addr);
	}

	if (tmpl->dhcp_req != NULL) {
		dhcp_release(tmpl);
		dhcp_abort(tmpl);
		free(tmpl->dhcp_req);
	}

	free(tmpl->name);
	free(tmpl);
}

struct port *
port_find(struct template *tmpl, int proto, int number)
{
	struct port tmpport;
	
	tmpport.proto = proto;
	tmpport.number = number;
	
	return (SPLAY_FIND(porttree, &tmpl->ports, &tmpport));
}

void
port_action_clone(struct action *dst, const struct action *src)
{
	*dst = *src;
	if (src->action) {
		dst->action = strdup(src->action);
		if (dst->action == NULL)
			err(1, "%s: strdup", __func__);
	}

	if (src->aitop != NULL) {
		struct addrinfo *ai = src->aitop;
		char addr[NI_MAXHOST];
		char port[NI_MAXSERV];
		short nport;

		if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
			addr, sizeof(addr), port, sizeof(port),
			NI_NUMERICHOST|NI_NUMERICSERV) != 0)
			err(1, "%s: getnameinfo", __func__);
		nport = atoi(port);
		dst->aitop = cmd_proxy_getinfo(addr, ai->ai_socktype, nport);
		if (dst->aitop == NULL)
			errx(1, "%s: cmd_proxy_getinfo failed", __func__);
	}
}

void
port_encapsulation_free(struct port_encapsulate *tmp)
{
	struct port *port = tmp->port;

	TAILQ_REMOVE(&port->pending, tmp, next);
	event_del(&tmp->ev);
	
	/* Remove the reference to the pending connection */
	if (tmp->hdr != NULL)
		tmp->hdr->pending = NULL;

	free(tmp);
}

void
port_free(struct template *tmpl, struct port *port)
{
	struct port_encapsulate *tmp;

	/* Remove pending connections */
	while ((tmp = TAILQ_FIRST(&port->pending)) != NULL) {
		/* This might not be the correct way to clean this up */
		if (tmp->hdr != NULL && tmp->hdr->type == SOCK_STREAM)
			tcp_connectfail(tmp->con);
	
		port_encapsulation_free(tmp);
	}

	SPLAY_REMOVE(porttree, &tmpl->ports, port);

	

	if (port->sub_conport != NULL) {
		/* Back pointer to connection object.
		 * It allows us to remove the reference to this object
		 * in the connection.
		 * However, at this point we really need to tear down
		 * that connection, too.
		 */
		*port->sub_conport = NULL;
	}

	if (port->sub != NULL)
		TAILQ_REMOVE(&port->sub->ports, port, next);
	if (port->sub_fd != -1)
		fdshare_close(port->sub_fd);
	if (port->action.action != NULL)
		free (port->action.action);
	if (port->action.aitop != NULL)
		freeaddrinfo(port->action.aitop);
	free(port);
}

struct port *
port_insert(struct template *tmpl, int proto, int number,
    struct action *action)
{
	struct port *port, tmpport;
	
	tmpport.proto = proto;
	tmpport.number = number;
	
	if (SPLAY_FIND(porttree, &tmpl->ports, &tmpport) != NULL)
		return (NULL);
	
	if ((port = calloc(1, sizeof(struct port))) == NULL)
		err(1, "%s: calloc", __func__);

	TAILQ_INIT(&port->pending);
	port->sub = NULL;
	port->sub_fd = -1;
	port->proto = proto;
	port->number = number;
	port_action_clone(&port->action, action);
	    
	SPLAY_INSERT(porttree, &tmpl->ports, port);

	return (port);
}

/* Create a random port in a certain range */

struct port *
port_random(struct template *tmpl, int proto, struct action *action,
    int min, int max)
{
	extern rand_t *honeyd_rand;
	struct port *port = NULL;
	int count = 100;
	int number;

	while (count-- && port == NULL) {
		number = rand_uint16(honeyd_rand) % (max - min) + min;
		port = port_insert(tmpl, proto, number, action);
	}

	return (port);
}

int
template_add(struct template *tmpl, int proto, int number,
    struct action *action)
{
	return (port_insert(tmpl, proto, number, action) == NULL ? -1 : 0);
}

void
template_insert_subsystem(struct template *tmpl, struct subsystem *sub)
{
	struct subsystem_container *container;

	if ((container = malloc(sizeof(struct subsystem_container))) == NULL)
		err(1, "%s: malloc", __func__);

	container->sub = sub;
	TAILQ_INSERT_TAIL(&tmpl->subsystems, container, next);
}

/* This function is slow, but should only called on SIGHUP */

void
template_remove_subsystem(struct template *tmpl, struct subsystem *sub)
{
	struct subsystem_container *container;

	TAILQ_FOREACH(container, &tmpl->subsystems, next) {
		if (container->sub == sub)
			break;
	}

	if (container == NULL)
		errx(1, "%s: could not remove subsystem %p from %s",
		    sub, tmpl->name);

	TAILQ_REMOVE(&tmpl->subsystems, container, next);

	free(container);
}

void
template_post_arp(struct template *tmpl, struct addr *ipaddr)
{
	struct arp_req *req;

	/* Register this mac address as our own */
	req = arp_new(tmpl->inter, NULL, NULL, ipaddr, tmpl->ethernet_addr);
	if (req == NULL)
		errx(1, "%s: cannot create arp entry");
		
	req->flags |= ARP_INTERNAL;
	req->owner = tmpl;
}

void
template_remove_arp(struct template *tmpl)
{
	struct arp_req *arp;

	if (tmpl->ethernet_addr == NULL)
		return;

	/* Check if we have an ARP entry */
	arp = arp_find(tmpl->ethernet_addr);
	assert(arp != NULL);
	arp_free(arp);
}

/*
 * When the interface is specified, we do not need to do an address lookup
 * for a corresponding interface.  We use the interface for DHCP.
 */

struct template *
template_clone(const char *newname, const struct template *tmpl, 
    struct interface *inter, int start)
{
	struct subsystem_container *container;
	struct condition *condition;
	struct template *newtmpl;
	struct port *port;
	struct addr addr;
	int isipaddr = 0;

	if ((newtmpl = template_create(newname)) == NULL)
		return (NULL);

	SPLAY_FOREACH(port, porttree, (struct porttree *)&tmpl->ports) {
		if (port_insert(newtmpl, port->proto, port->number,
			&port->action) == NULL)
			return (NULL);
	}

	if (tmpl->person)
		newtmpl->person = personality_clone(tmpl->person);

	/* Keeps track of the type of template */
	isipaddr = addr_aton(newtmpl->name, &addr) != -1;

	if (tmpl->ethernet_addr) {
		newtmpl->ethernet_addr = ethernetcode_clone(tmpl->ethernet_addr);
		/* 
		 * This template wants to have its own ethernet address,
		 * so we need to register it with our arp code.
		 *
		 * DHCP templates get a temporary IP address assigned.
		 */
		if (isipaddr) {
			if (inter == NULL) {
				inter = interface_find_responsible(&addr);
				if (inter == NULL)
					errx(1, "%s: cannot find interface");
			}
			newtmpl->inter = inter;

			/* Register this mac address as our own */
			template_post_arp(newtmpl, &addr);
		}
	}

	port_action_clone(&newtmpl->tcp, &tmpl->tcp);
	port_action_clone(&newtmpl->udp, &tmpl->udp);
	port_action_clone(&newtmpl->icmp, &tmpl->icmp);

	newtmpl->timestamp = tmpl->timestamp;
	newtmpl->uid = tmpl->uid;
	newtmpl->gid = tmpl->gid;
	newtmpl->max_nofiles = tmpl->max_nofiles;
	newtmpl->drop_inrate = tmpl->drop_inrate;
	newtmpl->drop_synrate = tmpl->drop_synrate;
	newtmpl->flags = tmpl->flags;
	newtmpl->spoof = tmpl->spoof;

	/* We need to remove this when cloning */
	newtmpl->flags &= ~TEMPLATE_DYNAMIC_CHILD;

	/* Clone dynamics */
	TAILQ_FOREACH(condition, &tmpl->dynamic, next) {
		if (template_insert_dynamic(newtmpl,
			condition->tmpl, condition) == -1)
			warn("%s: couldn't insert dynamic cond %s into %s",
			    condition->tmpl->name, tmpl->name);
	}

	/* Clone subsystems */
	TAILQ_FOREACH(container, &tmpl->subsystems, next) {
		struct subsystem *sub = container->sub;

		/* 
		 * Create a new subsystem structure only if the
		 * subsystem has not been specified as shared in the
		 * configuration.
		 */
		if (!(sub->flags & SUBSYSTEM_SHARED)) {
			template_subsystem(newtmpl, sub->cmdstring,sub->flags);
			continue;
		}

		/* Otherwise, just create references */
		subsystem_insert_template(sub, newtmpl);

		template_insert_subsystem(newtmpl, sub);
	}

	/* Start subsystems if we are the master template for a subsystem */
	if (!start)
		return (newtmpl);

	/* Start background processes */
	TAILQ_FOREACH(container, &newtmpl->subsystems, next) {
		template_subsystem_start(newtmpl, container->sub);
	}

	return (newtmpl);
}

int
template_subsystem(struct template *tmpl, char *subsystem, int flags)
{
	struct subsystem *sub;
	
	if ((sub = calloc(1, sizeof(struct subsystem))) == NULL)
		err(1, "%s: calloc", __func__);

	if ((sub->cmdstring = strdup(subsystem)) == NULL)
		err(1, "%s: strdup", __func__);

	/* Initializes subsystem data structures */
	TAILQ_INIT(&sub->ports);
	SPLAY_INIT(&sub->root);
	TAILQ_INIT(&sub->templates);

	subsystem_insert_template(sub, tmpl);

	sub->cmd.pid = -1;
	sub->flags |= flags;

	template_insert_subsystem(tmpl, sub);

	/* Remember this subsystem for lookup through the UI */
	TAILQ_INSERT_TAIL(&subsystems, sub, next);

	return (0);
}

void
template_subsystem_free_ports(struct subsystem *sub)
{
	struct port *port;

	for (port = TAILQ_FIRST(&sub->ports); port;
	    port = TAILQ_FIRST(&sub->ports)) {
		
		/* Free all ports for this subsystem */
		port_free(port->subtmpl, port);
	}
}

struct subsystem *
template_subsystem_find(const char *name)
{
	struct subsystem *sub;

	TAILQ_FOREACH(sub, &subsystems, next) {
		if (!strcmp(name, sub->cmdstring))
			return (sub);
	}

	return (NULL);
}

void
template_subsystem_list_glob(struct evbuffer *buffer, const char *pattern)
{
	struct subsystem *sub, *last = NULL;
	struct evbuffer *tmp = NULL;
	int count = 0;

	sub = template_subsystem_find(pattern);
	if (sub != NULL) {
		subsystem_print(buffer, sub);
		return;
	}

	if ((tmp = evbuffer_new()) == NULL)
		err(1, "%s: malloc");

	TAILQ_FOREACH(sub, &subsystems, next) {
		/* Ignore it if it does not match */
		if (fnmatch(pattern, sub->cmdstring, 0))
			continue;

		count++;
		evbuffer_add_printf(tmp, "%4d. %s (%d)\n",
		    count, sub->cmdstring, sub->cmd.pid);

		last = sub;
	}

	if (count == 1) {
		subsystem_print(buffer, last);
	} else {
		evbuffer_add_buffer(buffer, tmp);
	}
	evbuffer_free(tmp);
}

void
template_subsystem_free(struct subsystem *sub)
{
	struct template_container *cont;
	struct template *tmpl;

	TAILQ_REMOVE(&subsystems, sub, next);

	template_subsystem_free_ports(sub);
		
	/* 
	 * As we are removing all templates for this subsystem, we
	 * actually do not need to remove them from the Splay.
	 */
	for (cont = TAILQ_FIRST(&sub->templates); cont;
	    cont = TAILQ_FIRST(&sub->templates)) {
		TAILQ_REMOVE(&sub->templates, cont, next);
		tmpl = cont->tmpl;
		free(cont);

		template_remove_subsystem(tmpl, sub);

		template_free(tmpl);
	}

	cmd_free(&sub->cmd);

	free(sub->cmdstring);
	free(sub);
}

/* 
 * Inserts a new conditional template into a dynamic template.
 * The condition gets cloned in the process.
 */

int
template_insert_dynamic(struct template *tmpl, struct template *child,
    struct condition *condition)
{
	char newname[1024];
	struct condition *cond;

	if ((cond = calloc(1, sizeof(struct condition))) == NULL)
		err(1, "%s: calloc", __func__);

	if (condition != NULL)
		*cond = *condition;

	/* Con up a new template name */
	snprintf(newname, sizeof(newname), "%s_%d_%s",
	    tmpl->name, tmpl->dynamic_rulenr++, child->name);
	if ((cond->tmpl = template_clone(newname, child, NULL, 0)) == NULL) {
		fprintf(stderr, "Failed to clone %s from %s\n",
		    newname, child->name);
		free(cond);
		return (-1);
	}
	cond->tmpl->flags |= TEMPLATE_DYNAMIC_CHILD;

	TAILQ_INSERT_TAIL(&tmpl->dynamic, cond, next);

	/* Do we need to copy the match arg, too? */
	if (condition == NULL || condition->match_arg == NULL)
		return (0);

	if ((cond->match_arg = malloc(cond->match_arglen)) == NULL)
		err(1, "%s: malloc", __func__);

	memcpy(cond->match_arg, condition->match_arg, cond->match_arglen);

	return (0);
}

int
template_get_dhcp_address(struct addr *addr)
{
	static char address[16];

	snprintf(address, sizeof(address),
	    "169.254.%d.%d", privip_counter / 256 + 1, privip_counter % 256);

	if (++privip_counter > 255 * 255)
		errx(1, "%s: out of temporary IP addresses", __func__);

	return (addr_aton(address, addr));
}

void
template_subsystem_start(struct template *tmpl, struct subsystem *sub)
{
	extern int honeyd_verify_config;
	char *p;
	char *argv[4];
	char line[512];	/* for command replacement */
	char *name = tmpl->name;
	struct addr addr;
	int isipaddr = addr_aton(name, &addr) != -1;


	/* Has the subsystem started already? */
	if (sub->cmd.pid != -1 || honeyd_verify_config)
		return;

	gettimeofday(&sub->tv_restart, NULL);

	argv[0] = "/bin/sh";
	argv[1] = "-c";
	argv[3] = NULL;

	strlcpy(line, sub->cmdstring, sizeof(line));
	if (isipaddr) {
		/*
		 * Provide a subsystem with it's own IP address.
		 * For a shared subsystem, this is going to fail.
		 */
		while (strrpl(line, sizeof(line), "$ipsrc", name) != NULL)
			;
	}
	p = honeyd_logdate();
        while (strrpl(line, sizeof(line), "$date", p) != NULL)
                ;

	argv[2] = line;
	if (cmd_subsystem(tmpl, sub, "/bin/sh", argv) == -1)
		errx(1, "%s: can not start subsystem \"%s\" for %s",
		    sub->cmdstring, name);

}

void
template_print(struct evbuffer *buffer, struct template *tmpl)
{
	struct port *port;

	evbuffer_add_printf(buffer, "template %s:\n", tmpl->name);
	evbuffer_add_printf(buffer, "  personality: %s\n",
	    tmpl->person != NULL ? tmpl->person->name : "undefined");
	if (tmpl->ethernet_addr != NULL)
		evbuffer_add_printf(buffer, "  ethernet address: %s\n",
		    addr_ntoa(tmpl->ethernet_addr));
	evbuffer_add_printf(buffer, "  IP id: %u\n", tmpl->id);
	evbuffer_add_printf(buffer, "  TCP seq: %lx\n", tmpl->seq);
	evbuffer_add_printf(buffer, "  TCP drop: in: %d syn: %d\n",
	    tmpl->drop_inrate, tmpl->drop_synrate);
	evbuffer_add_printf(buffer, "  refcnt: %d\n", tmpl->refcnt);
	evbuffer_add_printf(buffer, "  ports:\n");

	SPLAY_FOREACH(port, porttree, &tmpl->ports) {
		char *type;
		switch (port->action.status) {
		case PORT_OPEN:
			type = "open";
			break;
		case PORT_PROXY:
			type = "proxy";
			break;
		case PORT_BLOCK:
			type = "block";
			break;
		case PORT_RESET:
			type = "reset";
			break;
		case PORT_SUBSYSTEM:
			type = "subsystem";
			break;
		case PORT_PYTHON:
			type = "python";
			break;
		default:
			type = "reserved";
			break;
		}
		evbuffer_add_printf(buffer, "    %s %5d %s",
		    port->proto == IP_PROTO_TCP ? "tcp" : "udp",
		    port->number, type);
		evbuffer_add_printf(buffer, "\n");
		if (port->action.status == PORT_SUBSYSTEM) {
			evbuffer_add_printf(buffer, "\t%s\n",
			    port->sub->cmdstring);
		} else if (port->action.status == PORT_OPEN &&
		    port->action.action != NULL) {
			evbuffer_add_printf(buffer, "\t%s\n",
			    port->action.action);
		}
	}
}

/***************************************************************************
 * Everything is unittest related below this
 ***************************************************************************/

void
template_delay_cb(int fd, short which, void *arg)
{
	extern struct pool *pool_pkt;
	extern struct pool *pool_delay;
	struct delay *delay = arg;
	struct ip_hdr *ip = delay->ip;
	struct template *tmpl = delay->tmpl;
	u_int iplen = delay->iplen;

	if (ip->ip_ttl &&
	    !(delay->flags & (DELAY_UNREACH|DELAY_EXTERNAL|DELAY_TUNNEL|DELAY_ETHERNET))) {
		struct addr addr;

		template_free(tmpl);

		addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS,
		    &ip->ip_dst, IP_ADDR_LEN);

		/* Internal delivery */
		tmpl = template_find_best(addr_ntoa(&addr), ip, iplen);
		tmpl = template_ref(tmpl);

		/* No Check for fragmentation */
		honeyd_dispatch(tmpl, ip, iplen);

	}

	if (delay->flags & DELAY_FREEPKT)
		pool_free(pool_pkt, ip);
	template_free(tmpl);

	if (delay->flags & DELAY_NEEDFREE)
		pool_free(pool_delay, delay);
}

void
template_test_parse_error(char *line, struct evbuffer *evbuf)
{
	char *p = EVBUFFER_DATA(evbuf);
	size_t off = EVBUFFER_LENGTH(evbuf);
	p[off - 1] = '\0';
	errx(1, "parse_line \"%s\" failed: %s", line, p);
}

#define MAKE_CONFIG(x)	do { \
	char *p = (x); \
	if (parse_line(evbuf, p) == -1) \
		template_test_parse_error(p, evbuf); \
} while (0)
	
int
template_test_add(struct evbuffer *evbuf, struct addr *addr, int count)
{
	char line[128];
	int i;

	for (i = 0; i < count; i++) {
		evbuffer_drain(evbuf, -1);
		
		addr->addr_ip = htonl(ntohl(addr->addr_ip) + 1);
		snprintf(line, sizeof(line), "bind %s template",
		    addr_ntoa(addr));
		MAKE_CONFIG(line);
	}

	return (count);
}

void
template_test_measure(int count)
{
	extern rand_t *honeyd_rand;
	extern void
	  honeyd_recv_cb(u_char *, const struct pcap_pkthdr *, const u_char *);
	u_char pkt[1500];
	struct interface inter;
	struct pcap_pkthdr pkthdr;
	struct ip_hdr *ip = (struct ip_hdr *)pkt;
	struct tcp_hdr *tcp = (struct tcp_hdr *)(ip + 1);
	struct addr src, dst;
	uint16_t *snib;
	struct timeval tv_start, tv_end;
	double msperpkt;
	int j;
	uint16_t iplen = sizeof(*ip) + sizeof(*tcp);

	memset(&inter, 0, sizeof(struct interface));
	memset(&pkthdr, 0, sizeof(struct pcap_pkthdr));
	pkthdr.caplen = sizeof(pkt);
		    
	addr_pton("10.0.0.0", &dst);
	addr_pton("10.1.0.0", &src);

	snib = (uint16_t *)(&src.addr_data8[2]);

	gettimeofday(&tv_start, NULL);
	for (j = 0; j < 80000; j++) {
		*snib = rand_uint16(honeyd_rand);
		dst.addr_ip  = htonl(rand_uint32(honeyd_rand) % count);
		dst.addr_data8[0] = 10;

		tcp_pack_hdr(tcp,
		    rand_uint16(honeyd_rand), 23,
		    0x100000, 0x00000, TH_SYN, 32768, 0);
		ip_pack_hdr(pkt, 0, iplen, rand_uint16(honeyd_rand),
		    0, 64,
		    IP_PROTO_TCP, src.addr_ip, dst.addr_ip);
		ip_checksum(pkt, iplen);

		honeyd_recv_cb((u_char *)&inter, &pkthdr, pkt);
	}
	gettimeofday(&tv_end, NULL);
	timersub(&tv_end, &tv_start, &tv_end);
	msperpkt = (double)(tv_end.tv_sec * 1000 + tv_end.tv_usec / 1000)
	    / (double) j;
	fprintf(stderr, "\t\t%7d templates: %.4f ms per packet\n",
	    count, msperpkt);
}

void
template_packet_test(void)
{
	extern void (*honeyd_delay_callback)(int, short, void *);
	void (*old)(int, short, void *) = honeyd_delay_callback;
	struct evbuffer *evbuf = evbuffer_new();
	struct addr addr;
	int i, count;

	/* Set our own delay callback */
	honeyd_delay_callback = template_delay_cb;

	/* Create configuration */
	MAKE_CONFIG("create template");
	MAKE_CONFIG("add template tcp port 23 reset");

	addr_pton("10.0.0.0", &addr);

	count = template_test_add(evbuf, &addr, 1);
	template_test_measure(count);
	for (i = 0; i < 5; i++) {
		count += template_test_add(evbuf, &addr,
		    count == 1 ? 999 : 1000);
		template_test_measure(count);
	}
	for (i = 0; i < 50; i++) {
		count += template_test_add(evbuf, &addr, 5000);
		template_test_measure(count);
	}

	honeyd_delay_callback = old;

	evbuffer_free(evbuf);

	fprintf(stderr, "\t%s: OK\n", __func__);
}

void
template_test(void)
{
	setlogmask(LOG_UPTO(LOG_NOTICE));

	template_packet_test();
}
