/*
 * Copyright (c) 2003 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>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/queue.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/tree.h>

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

#undef timeout_pending
#undef timeout_initialized

#include <event.h>

#include <Python.h>

#include "honeyd.h"
#include "template.h"
#include "personality.h"
#include "interface.h"
#include "log.h"
#include "pyextend.h"
#include "histogram.h"
#include "osfp.h"
#include "debug.h"

int make_socket(int (*f)(int, const struct sockaddr *, socklen_t), int type,
    char *, uint16_t);

/*
 * Can be used externally to share Python dictionaries with the user interface
 */
PyObject *pyextend_dict_global;
PyObject *pyextend_dict_local;

/* 
 * Functions that we need to call for this script.
 * This is stateless and shared among connections.
 */

struct pyextend {
	SPLAY_ENTRY(pyextend) node;
	char *name;
	PyObject *pFuncInit;
	PyObject *pFuncReadData;
	PyObject *pFuncWriteData;
	PyObject *pFuncEnd;
};

SPLAY_HEAD(pyetree, pyextend) pyextends;

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

SPLAY_PROTOTYPE(pyetree, pyextend, node, pye_compare);
SPLAY_GENERATE(pyetree, pyextend, node, pye_compare);

struct pywrite {
	TAILQ_ENTRY(pywrite) next;

	u_char *buf;
	size_t size;
};

struct pystate {
	PyObject *state;

	struct pyextend *pye;

	int fd;

	struct event pread;
	struct event pwrite;
	
	int wantwrite;

	TAILQ_HEAD(pywbufs, pywrite) writebuffers;

	struct command *cmd;
	void *con;
};

static PyObject *pyextend_readselector(PyObject *, PyObject *);
static PyObject *pyextend_writeselector(PyObject *, PyObject *);
static PyObject *pyextend_log(PyObject *, PyObject *);
static PyObject *pyextend_raw_log(PyObject *, PyObject *);
static PyObject *pyextend_uptime(PyObject *, PyObject *);
static PyObject *pyextend_interfaces(PyObject *, PyObject *);
static PyObject *pyextend_stats_network(PyObject *, PyObject *);
static PyObject *pyextend_status_connections(PyObject *, PyObject *);
static PyObject *pyextend_config(PyObject *, PyObject *);
static PyObject *pyextend_config_ips(PyObject *, PyObject *);
static PyObject *pyextend_delete_template(PyObject *, PyObject *);
static PyObject *pyextend_delete_connection(PyObject *, PyObject *);
static PyObject *pyextend_security_info(PyObject *, PyObject *);

static PyMethodDef HoneydMethods[] = {
    {"read_selector", pyextend_readselector, METH_VARARGS,
     "Tells Honeyd if the embedded Python application wants to read or not."},
    {"write_selector", pyextend_writeselector, METH_VARARGS,
     "Tells Honeyd if the embedded Python application wants to write or not."},
    {"log", pyextend_log, METH_VARARGS,
     "Allows a python script to pass a string to generate service logs."},
    {"raw_log", pyextend_raw_log, METH_VARARGS,
     "Allows a python script to log directly to syslog."},
    {"uptime", pyextend_uptime, METH_VARARGS,
     "Returns the number of seconds that Honeyd has been running."},
    {"interfaces", pyextend_interfaces, METH_VARARGS,
     "Returns an array of configured interfaces."},
    {"stats_network", pyextend_stats_network, METH_VARARGS,
     "Returns a dictionary with network statistics."},
    {"status_connections", pyextend_status_connections, METH_VARARGS,
     "Returns a list of active UDP or TCP connections."},
    {"config", pyextend_config, METH_VARARGS,
     "Returns an associative array with config information."},
    {"config_ips", pyextend_config_ips, METH_VARARGS,
     "Returns an array with bound IP addresses."},
    {"delete_template", pyextend_delete_template, METH_VARARGS,
     "Deletes the specified template."},
    {"delete_connection", pyextend_delete_connection, METH_VARARGS,
     "Deletes the specified connection."},
    {"security_info", pyextend_security_info, METH_VARARGS,
     "Returns a string with pertinent security information."},
    {NULL, NULL, 0, NULL}
};

static struct pystate *current_state;

struct pyextend_count {
	int offset;
	PyObject *pArgs;
};

static int
pyextend_populate_connections(struct tuple *hdr, void *arg)
{
	PyObject *pArgs = arg, *pValue;
	struct addr src, dst;
	
	addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS, &hdr->ip_src, IP_ADDR_LEN);
	addr_pack(&dst, ADDR_TYPE_IP, IP_ADDR_BITS, &hdr->ip_dst, IP_ADDR_LEN);

	pValue = Py_BuildValue("{sssssisisisi}",
	    "src", addr_ntoa(&src),
	    "dst", addr_ntoa(&dst),
	    "sport", hdr->sport,
	    "dport", hdr->dport,
	    "received", hdr->received,
	    "sent", hdr->sent);
	if (pValue == NULL) {
		PyErr_Print();
		errx(1, "%s: failed to build argument list", __func__);
	}

	/* pValue reference stolen here */
	PyList_Append(pArgs, pValue);

	return (0);
}

static PyObject*
pyextend_status_connections(PyObject *self, PyObject *args)
{
	PyObject *pArgs;
	char *string;
	extern struct conlru tcplru;
	extern struct conlru udplru;
	struct conlru *head;

	if (!PyArg_ParseTuple(args, "s", &string))
		return NULL;

	/* Check that we are asking for either UDP or TCP */
	if (!strcmp(string, "udp"))
		head = &udplru;
	else if (!strcmp(string, "tcp"))
		head = &tcplru;
	else
		return NULL;

	pArgs = PyList_New(0);

	/* Populate tuple with per IP address information */
	tuple_iterate(head, pyextend_populate_connections, pArgs);

	return (pArgs);
}

/*
 * Returns 1 if the template name corresponds to an IP address
 */

int
pyextend_is_ipaddress(struct template *tmpl)
{
	ip_addr_t addr;

	return (ip_pton(tmpl->name, &addr) != -1);
}

int
pyextend_count_ips(struct template *tmpl, void *arg)
{
	int *num_ips = arg;

	if (!pyextend_is_ipaddress(tmpl))
		return (0);

	(*num_ips)++;

	return (0);
}

void
pyextend_humanreadable_action(struct action *action, char *buffer, size_t len)
{
	char *flags = NULL;
	if (action->flags & PORT_TARPIT) {
		flags = "tarpit ";
	}

	switch (action->status) {
	case PORT_BLOCK:
		snprintf(buffer, len, "block");
		break;

	case PORT_RESET:
		snprintf(buffer, len, "reset");
		break;

	case PORT_OPEN:
		snprintf(buffer, len, "%s%s",
		    flags != NULL ? flags : "",
		    action->action != NULL ? action->action : "open");
		break;
	case PORT_PYTHON:
		snprintf(buffer, len, "%sinternal %s",
		    flags != NULL ? flags : "",
		    action->action);
		break;
	case PORT_PROXY:
		if (action->action != NULL) {
			snprintf(buffer, len, "%sproxy %s",
			    flags != NULL ? flags : "",
			    action->action);
		} else if (action->aitop != NULL) {
			struct addrinfo *ai = action->aitop;
			char addr[NI_MAXHOST];
			char port[NI_MAXSERV];

			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
				addr, sizeof(addr), port, sizeof(port),
				NI_NUMERICHOST|NI_NUMERICSERV) != 0)
				err(1, "%s: getnameinfo", __func__);

			snprintf(buffer, len, "%sproxy %s:%s",
			    flags != NULL ? flags : "",
			    addr, port);
		} else {
			snprintf(buffer, len, "proxy UNKNOWN");
		}
		break;
	default:
		snprintf(buffer, len, "UNKNOWN");
		break;
	}
}

int
pyextend_populate_ips(struct template *tmpl, void *arg)
{
	struct pyextend_count *count = arg;
	char icmp_action[1024];
	char tcp_action[1024];
	char udp_action[1024];
	PyObject *pArgs = count->pArgs;
	PyObject *pValue;

	if (!pyextend_is_ipaddress(tmpl))
		return (0);

	/* Fill in the default actions that we take */
	pyextend_humanreadable_action(&tmpl->icmp,
	    icmp_action, sizeof(icmp_action));
	pyextend_humanreadable_action(&tmpl->tcp,
	    tcp_action, sizeof(tcp_action));
	pyextend_humanreadable_action(&tmpl->udp,
	    udp_action, sizeof(udp_action));

	pValue = Py_BuildValue("{ssssssssssss}",
	    "address", tmpl->name,
	    "personality", tmpl->person != NULL ? tmpl->person->name : NULL,
	    "icmp_action", icmp_action,
	    "tcp_action", tcp_action,
	    "udp_action", udp_action,
	    "ethernet", tmpl->ethernet_addr != NULL ?
	    addr_ntoa(tmpl->ethernet_addr) : NULL);
	if (pValue == NULL) {
		PyErr_Print();
		errx(1, "%s: failed to build argument list", __func__);
	}
	/* pValue reference stolen here */
	PyList_SetItem(pArgs, count->offset++, pValue);

	return (0);
}

static PyObject*
pyextend_config_ips(PyObject *self, PyObject *args)
{
	PyObject *pArgs;
	int num_ips = 0;
	struct pyextend_count count;

	/* Count all IP addresses */
	template_iterate(pyextend_count_ips, &num_ips);

	pArgs = PyList_New(num_ips);

	/* Populate tuple with per IP address information */
	memset(&count, 0, sizeof(count));
	count.pArgs = pArgs;
	template_iterate(pyextend_populate_ips, &count);

	return (pArgs);
}

static PyObject*
pyextend_stats_network(PyObject *self, PyObject *args)
{
	PyObject *pValue;
	extern struct stats_network stats_network;
	
	pValue = Py_BuildValue("{s:(d,d,d),s:(d,d,d)}",
	    "Input Bytes", 
	    (double)count_get_minute(stats_network.input_bytes)/60.0,
	    (double)count_get_hour(stats_network.input_bytes)/3600.0,
	    (double)count_get_day(stats_network.input_bytes)/86400.0,
	    "Output Bytes",
	    (double)count_get_minute(stats_network.output_bytes)/60.0,
	    (double)count_get_hour(stats_network.output_bytes)/3600.0,
	    (double)count_get_day(stats_network.output_bytes)/86400.0);

	if (pValue == NULL) {
		PyErr_Print();
		errx(1, "%s: failed to build argument list", __func__);
	}

	return (pValue);
}

static PyObject*
pyextend_config(PyObject *self, PyObject *args)
{
	PyObject *pValue;
	extern struct config config;

	pValue = Py_BuildValue("{ssssssssssss}",
	    "version", VERSION,
	    "config", config.config,
	    "personality", config.pers,
	    "xprobe", config.xprobe,
	    "assoc", config.assoc,
	    "osfp", config.osfp);
	if (pValue == NULL) {
		PyErr_Print();
		errx(1, "%s: failed to build argument list", __func__);
	}

	return (pValue);
}

static PyObject*
pyextend_interfaces(PyObject *self, PyObject *args)
{
	PyObject *pArgs;
	int i;

	pArgs = PyTuple_New(interface_count());
	for (i = 0; i < interface_count(); i++) {
		struct interface *inter = interface_get(i);
		struct intf_entry *if_ent = &inter->if_ent;
		PyObject *pValue;

		pValue = Py_BuildValue("{sssssiss}",
		    "name", if_ent->intf_name,
		    "address", addr_ntoa(&if_ent->intf_addr),
		    "mtu", if_ent->intf_mtu,
		    "link", addr_ntoa(&if_ent->intf_link_addr));
		if (pValue == NULL) {
			PyErr_Print();
			errx(1, "%s: failed to build argument list", __func__);
		}
		/* pValue reference stolen here */
		PyTuple_SetItem(pArgs, i, pValue);
	}

	return (pArgs);
}

static PyObject*
pyextend_uptime(PyObject *self, PyObject *args)
{
	extern struct timeval honeyd_uptime;
	struct timeval tv;

	gettimeofday(&tv, NULL);
	timersub(&tv, &honeyd_uptime, &tv);

	return (Py_BuildValue("i", tv.tv_sec));
}

static PyObject*
pyextend_delete_template(PyObject *self, PyObject *args)
{
	char *string;
	struct template *tmpl;
	int result = 0;

	if (!PyArg_ParseTuple(args, "s", &string))
		goto done;

	if ((tmpl = template_find(string)) == NULL)
		goto done;
	
	result = 1;
	template_remove(tmpl);

 done:
	return (Py_BuildValue("i", result));
}

static PyObject*
pyextend_delete_connection(PyObject *self, PyObject *args)
{
	extern struct tree tcpcons;
	extern struct tree udpcons;
	struct tuple tmp, *hdr;
	char *protocol;
	char *asrc, *adst, *asport, *adport;
	struct addr src, dst;
	int result = 0;

	if (!PyArg_ParseTuple(args, "sssss", &protocol,
		&asrc, &asport, &adst, &adport))
		goto done;

	if (addr_aton(asrc, &src) == -1)
		goto done;
	if (addr_aton(adst, &dst) == -1)
		goto done;

	tmp.ip_src = src.addr_ip;
	tmp.ip_dst = dst.addr_ip;
	tmp.sport = atoi(asport);
	tmp.dport = atoi(adport);

	if (!strcmp(protocol, "tcp")) {
		hdr = tuple_find(&tcpcons, &tmp);
		if (hdr == NULL)
			goto done;
		tcp_free((struct tcp_con *)hdr);
	} else if (!strcmp(protocol, "udp")) {
		hdr = tuple_find(&udpcons, &tmp);
		if (hdr == NULL)
			goto done;
		udp_free((struct udp_con *)hdr);
	}
	
	result = 1;

 done:
	return (Py_BuildValue("i", result));
}

static PyObject*
pyextend_security_info(PyObject *self, PyObject *args)
{
	extern char *security_update;
	PyObject *pValue;

	if (security_update == NULL || strlen(security_update) == 0)
		return Py_None;

	pValue = Py_BuildValue("s", security_update);

	return (pValue);
}

static PyObject*
pyextend_log(PyObject *self, PyObject *args)
{
	extern FILE *honeyd_servicefp;
	struct tuple *hdr;
	char *string;

	if (current_state == NULL)
		return (Py_BuildValue("i", -1));
	
	hdr = current_state->con;
	
	if(!PyArg_ParseTuple(args, "s:read_selector", &string))
		return (NULL);

	honeyd_log_service(honeyd_servicefp,
	    hdr->type == SOCK_STREAM ? IP_PROTO_TCP : IP_PROTO_UDP,
	    hdr, string);

	return (Py_BuildValue("i", 0));
}

static PyObject*
pyextend_raw_log(PyObject *self, PyObject *args)
{
	char *string;

	if(!PyArg_ParseTuple(args, "s:read_selector", &string))
		return (NULL);

	syslog(LOG_NOTICE, "%s", string);

	return Py_BuildValue("i", 0);;
}

static PyObject*
pyextend_selector(PyObject *args, struct event *ev, const char *name)
{
	int on = 0;

	if(!PyArg_ParseTuple(args, "i:read_selector", &on))
		return (NULL);
	DFPRINTF(1, (stderr, "%s: called selector with %d\n", name, on));

	if (on)
		event_add(ev, NULL);
	else
		event_del(ev);

	return Py_BuildValue("i", 0);;
}

static PyObject*
pyextend_readselector(PyObject *self, PyObject *args)
{
	if (current_state == NULL)
		return (NULL);

	return (pyextend_selector(args, &current_state->pread, __func__));
}

static PyObject*
pyextend_writeselector(PyObject *self, PyObject *args)
{
	struct pystate *state = current_state;

	PyObject *pValue;
	if (state == NULL)
		return (NULL);

	pValue = pyextend_selector(args, &state->pwrite, __func__);
	if (pValue == NULL)
		return (NULL);

	/* 
	 * We need to keep track of this, so that in case we have buffered
	 * data to write, we know if we should schedule the python script.
	 */
	state->wantwrite = event_pending(&state->pwrite, EV_WRITE, NULL);

	return (pValue);
}

static void
pyextend_cbread(int fd, short what, void *arg)
{
	static char buf[4096];
	PyObject *pArgs, *pValue;
	struct pystate *state = arg;
	struct pyextend *pye = state->pye;
	int n;

	n = read(fd, buf, sizeof(buf));

	if (n <= 0)
		goto error;

	pArgs = Py_BuildValue("(O,s#)", state->state, buf, n);
	if (pArgs == NULL) {
		fprintf(stderr, "Failed to build value\n");
		goto error;
	}

	current_state = state;
	pValue = PyObject_CallObject(pye->pFuncReadData, pArgs);
	current_state = NULL;

	Py_DECREF(pArgs);

	if (pValue == NULL) {
		PyErr_Print();
		goto error;
	}
	Py_DECREF(pValue);

	return;

 error:
	pyextend_connection_end(state);
	return;
}

static int
pyextend_addbuffer(struct pystate *state, u_char *buf, size_t size)
{
	struct pywrite *write;

	if ((write = malloc(sizeof(struct pywrite))) == NULL)
		return (-1);

	if ((write->buf = malloc(size)) == NULL) {
		free(write);
		return (-1);
	}

	memcpy(write->buf, buf, size);
	write->size = size;

	TAILQ_INSERT_TAIL(&state->writebuffers, write, next);

	return (0);
}

static void
pyextend_cbwrite(int fd, short what, void *arg)
{
	PyObject *pArgs, *pValue;
	struct pystate *state = arg;
	struct pyextend *pye = state->pye;
	struct pywrite *writebuf;
	char *buf;
	int size, res;

	/* If we still have buffered data from before, we are going
	 * to send it now and reschedule us if necessary.
	 */
	if ((writebuf = TAILQ_FIRST(&state->writebuffers)) != NULL) {
		res = write(fd, writebuf->buf, writebuf->size);
		if (res <= 0)
			goto error;
		if (res < writebuf->size) {
			writebuf->size -= res;
			memmove(writebuf->buf, writebuf->buf + res,
			    writebuf->size);
			event_add(&state->pwrite, NULL);
		} else {
			TAILQ_REMOVE(&state->writebuffers, writebuf, next);
			free(writebuf->buf);
			free(writebuf);
			if (state->wantwrite ||
			    TAILQ_FIRST(&state->writebuffers) != NULL)
				event_add(&state->pwrite, NULL);
		}

		return;
	}
	

	pArgs = Py_BuildValue("(O)", state->state);
	if (pArgs == NULL) {
		fprintf(stderr, "Failed to build value\n");
		goto error;
	}

	current_state = state;
	pValue = PyObject_CallObject(pye->pFuncWriteData, pArgs);
	current_state = NULL;

	Py_DECREF(pArgs);

	if (pValue == NULL) {
		PyErr_Print();
		goto error;
	}

	/* 
	 * Addition to support closing connections from the server
	 * side. - AJ 2.4.2004
	 */
	if (pValue == Py_None) {
		Py_DECREF(pValue);
		goto error;
	}

	res = PyString_AsStringAndSize(pValue, &buf, &size);

	if (res == -1) {
		Py_DECREF(pValue);
		goto error;
	}

	/* XXX - What to do about left over data */
	res = write(fd, buf, size);

	if (res <= 0) {
		Py_DECREF(pValue);
		goto error;
	}

	if (res != size) {
		pyextend_addbuffer(state, buf + res, size - res);
		event_add(&state->pwrite, NULL);
	}

	Py_DECREF(pValue);
		
	return;

 error:
	pyextend_connection_end(state);
	return;
}

/* Initializes our Python extension support */

void
pyextend_init(void)
{
	PyObject *pModule;
	char path[1024], singlepath[1024];
	extern char *honeyd_webserver_root;
	char *p;

	SPLAY_INIT(&pyextends);

	Py_Initialize();
	strlcpy(path, Py_GetPath(), sizeof(path));
	/* Append the current path */
	strlcat(path, ":.", sizeof(path));
	strlcat(path, ":webserver", sizeof(path));

	/* Append the webserver root directory */
	snprintf(singlepath, sizeof(singlepath), ":%s", honeyd_webserver_root);
	if ((p = strstr(singlepath, "/htdocs")) != NULL) {
		*p = '\0';
		strlcat(path, singlepath, sizeof(path));
	}

	/* Append the Honeyd shared data directory */ 
	snprintf(singlepath, sizeof(singlepath),
	    ":%s", PATH_HONEYDDATA);
	strlcat(path, singlepath, sizeof(path));
	snprintf(singlepath, sizeof(singlepath),
	    ":%s/webserver", PATH_HONEYDDATA);
	strlcat(path, singlepath, sizeof(path));
	PySys_SetPath(path);

	pModule = Py_InitModule("honeyd", HoneydMethods);
	PyModule_AddIntConstant(pModule, "EVENT_ON", 1);
	PyModule_AddIntConstant(pModule, "EVENT_OFF", 0);
	PyModule_AddStringConstant(pModule, "version", VERSION);
}

/* Cleans up all Python stuff when we exit */

void
pyextend_exit(void)
{
	Py_Finalize();
}

void
pyextend_run(struct evbuffer *output, char *command)
{
	PyObject *res = NULL, *compiled_code;
	char *data;
	int datlen;

	char *preamble = "import StringIO\n"
	    "import sys\n"
	    "myout = StringIO.StringIO()\n"
	    "myerr = StringIO.StringIO()\n"
	    "saveout = sys.stdout\n"
	    "saveerr = sys.stderr\n"
	    "try:\n"
	    "  sys.stdout = myout\n"
	    "  sys.stderr = myerr\n"
	    "  try:\n"
	    "    %s\n"
	    "  except:\n"
	    "    import traceback\n"
	    "    traceback.print_exc()\n"
	    "finally:\n"
	    "  sys.stdout = saveerr\n"
	    "  sys.stderr = saveerr\n"
	    "output = \"%%s%%s\" %% (myout.getvalue(), myerr.getvalue())";

	char *code = NULL;

	if (asprintf(&code, preamble, command) == -1)
		err(1, "%s: asprintf", __func__);

	compiled_code = Py_CompileStringFlags(code, "<filter>",
	    Py_file_input, 0);

	free(code);
	
	if (compiled_code == NULL) {
		const char *err = "Compilation of Python code failed.\n";
		evbuffer_add(output, (char *)err, strlen(err));
		PyErr_Print();
		return;
	}

	if (pyextend_dict_local == NULL) {
		pyextend_dict_local = PyDict_New();
		assert(pyextend_dict_local != NULL);
	}
	
	if (pyextend_dict_global == NULL) {
		PyObject *m;

		/* Extract the global dictionary object */
		if ((m = PyImport_AddModule("__main__")) == NULL) {
			PyErr_Print();
			return;
		}

		if ((pyextend_dict_global = PyModule_GetDict(m)) == NULL) {
			PyErr_Print();
			return;
		}
		Py_INCREF(pyextend_dict_global);

		if (PyDict_GetItemString(pyextend_dict_global, "__builtins__") == NULL&&
		    PyDict_SetItemString(pyextend_dict_global, "__builtins__", PyEval_GetBuiltins()) == 0) {
			Py_DECREF(pyextend_dict_global);
			pyextend_dict_global = NULL;
			return;
		}
	}

	res = PyEval_EvalCode((PyCodeObject *)compiled_code,
	    pyextend_dict_global, pyextend_dict_local);
	Py_DECREF(compiled_code);

	if (res == NULL) {
		PyErr_Print();
		return;
	}
	Py_DECREF(res);

	res = PyDict_GetItemString(pyextend_dict_local, "output");
	assert(res != NULL);

	if (PyString_AsStringAndSize(res, &data, &datlen) == 0) 
		evbuffer_add(output, data, datlen);
}

#define CHECK_FUNC(f, x) do { \
	f = PyDict_GetItemString(pDict, x); \
	if ((f) == NULL || !PyCallable_Check(f)) { \
		warnx("%s: Cannot find function \"%s\"", \
			__func__, x); \
		goto error; \
	} \
} while (0)

void *
pyextend_load_module(const char *name)
{
	PyObject *pName, *pModule, *pDict, *pFunc;
	struct pyextend *pye, tmp;

	char line[1024];
	char *script, *p;
	
	if (strlcpy(line, name, sizeof(line)) >= sizeof(line))
		return (NULL);
	p = line;

	script = strsep(&p, " ");

	tmp.name = script;
	if ((pye = SPLAY_FIND(pyetree, &pyextends, &tmp)) != NULL)
		return (pye);

	pName = PyString_FromString(script);
	pModule = PyImport_Import(pName);
	Py_DECREF(pName);

	if (pModule == NULL) {
		PyErr_Print();
		warn("%s: could not load python module: %s",
		    __func__, script);
		return (NULL);
	}

	pDict = PyModule_GetDict(pModule); /* Borrowed */

	CHECK_FUNC(pFunc, "honeyd_init");
	CHECK_FUNC(pFunc, "honeyd_readdata");
	CHECK_FUNC(pFunc, "honeyd_writedata");
	CHECK_FUNC(pFunc, "honeyd_end");

	if ((pye = calloc(1, sizeof(struct pyextend))) == NULL)
		err(1, "calloc");

	CHECK_FUNC(pye->pFuncInit, "honeyd_init");
	CHECK_FUNC(pye->pFuncReadData, "honeyd_readdata");
	CHECK_FUNC(pye->pFuncWriteData, "honeyd_writedata");
	CHECK_FUNC(pye->pFuncEnd, "honeyd_end");

	if ((pye->name = strdup(script)) == NULL)
		err(1, "%s: strdup", __func__);

	SPLAY_INSERT(pyetree, &pyextends, pye);
	  
	return (pye);

 error:
	Py_DECREF(pModule);
	return (NULL);
}

static struct pystate *
pyextend_newstate(struct command *cmd, void *con, struct pyextend *pye)
{
	struct pystate *state;

	if ((state = calloc(1, sizeof(struct pystate))) == NULL)
		return (NULL);

	/* Initialize structure */
	state->fd = -1;
	state->cmd = cmd;
	state->con = con;
	state->pye = pye;

	TAILQ_INIT(&state->writebuffers);

	return (state);
}

static void
pyextend_freestate(struct pystate *state)
{
	struct pywrite *writes;

	while ((writes = TAILQ_FIRST(&state->writebuffers)) != NULL) {
		TAILQ_REMOVE(&state->writebuffers, writes, next);
		free(writes->buf);
		free(writes);
	}

	/* Cleanup our state */
	event_del(&state->pread);
	event_del(&state->pwrite);

	if (state->fd != -1)
		close(state->fd);
	free(state);
}

int
pyextend_connection_start(struct tuple *hdr, struct command *cmd,
    void *con, void *pye_generic)
{
	struct pyextend *pye = pye_generic;
	struct pystate *state;
	PyObject *pArgs, *pValue;
	struct addr src, dst;
	struct ip_hdr ip;
	char *os_name = NULL;

	if ((state = pyextend_newstate(cmd, con, pye)) == NULL)
		return (-1);

	if ((state->fd = cmd_python(hdr, cmd, con)) == -1) {
		free(state);
		return (-1);
	}

	/* Set up state with event callbacks */
	event_set(&state->pread, state->fd, EV_READ, pyextend_cbread, state);
	event_set(&state->pwrite, state->fd, EV_WRITE, pyextend_cbwrite,state);

	addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS, &hdr->ip_src,IP_ADDR_LEN);
	addr_pack(&dst, ADDR_TYPE_IP, IP_ADDR_BITS, &hdr->ip_dst,IP_ADDR_LEN);

	/* Determine the remote operating system */
	ip.ip_src = hdr->ip_src;
	os_name = honeyd_osfp_name(&ip);

	pArgs = PyTuple_New(1);
	pValue = Py_BuildValue("{sssssisiss}",
	    "HONEYD_IP_SRC", addr_ntoa(&src),
	    "HONEYD_IP_DST", addr_ntoa(&dst),
	    "HONEYD_SRC_PORT", hdr->sport,
	    "HONEYD_DST_PORT", hdr->dport,
	    "HONEYD_REMOTE_OS", os_name);
	if (pValue == NULL) {
		fprintf(stderr, "Failed to build value\n");
		Py_DECREF(pArgs);
		goto error;
	}

	/* Set up the current state for Python */
	current_state = state;

	/* pValue reference stolen here: */
	PyTuple_SetItem(pArgs, 0, pValue);

	pValue = PyObject_CallObject(pye->pFuncInit, pArgs);
	Py_DECREF(pArgs);

	/* Take away the current state */
	current_state = NULL;

	if (pValue == NULL) {
		PyErr_Print();
		goto error;
	}

	state->state = pValue;

	/* 
	 * Registers state with command structure so that we can do
	 * proper cleanup if things go wrong.
	 */
	cmd->state = state;
	return (0);
	
 error:
	pyextend_freestate(state);
	return (-1);
}

void
pyextend_connection_end(struct pystate *state)
{
	struct command *cmd = state->cmd;
	struct pyextend *pye = state->pye;
	PyObject *pArgs;

	pArgs = PyTuple_New(1);

	/* state->state reference stolen here: */
	PyTuple_SetItem(pArgs, 0, state->state);

	PyObject_CallObject(pye->pFuncEnd, pArgs);
	Py_DECREF(pArgs);

	pyextend_freestate(state);

	cmd->state = NULL;

	return;
}

/*
 * We register our own web server so that we can get some stats reporting
 * via a web browser.
 */

static PyObject *pWebServer;	/* web server instance */
static PyObject *pFuncRequest;	/* handle request function */

static int pyserver_fd = -1;
static struct event ev_accept;

void pyextend_request_free(struct pyextend_request *);

void
pyextend_evb_readcb(struct bufferevent *bev, void *parameter)
{
	struct pyextend_request *req = parameter;
	PyObject *pArgs, *pValue;
	char *client_address = addr_ntoa(&req->src);
	char *buf;
	int size, res;

	/* Check if we have received the complete request */
	if (evbuffer_find(bev->input, "\r\n\r\n", 4) == NULL) {
		/* 
		 * If we did not receive the complete request and we have
		 * waited for too long already, then we drop the request.
		 */
		if (EVBUFFER_LENGTH(bev->input) > PYEXTEND_MAX_REQUEST_SIZE) {
			syslog(LOG_NOTICE,
			    "Dropping request from %s with size %d",
			    client_address, EVBUFFER_LENGTH(bev->input));
			pyextend_request_free(req);
		}
		return;
	}

	pArgs = Py_BuildValue("(O,s#,s#)", pWebServer,
	    EVBUFFER_DATA(bev->input), EVBUFFER_LENGTH(bev->input),
	    client_address, strlen(client_address));
	if (pArgs == NULL)
		goto error;

	pValue = PyObject_CallObject(pFuncRequest, pArgs);
	Py_DECREF(pArgs);

	if (pValue == NULL)
		goto error;

	res = PyString_AsStringAndSize(pValue, &buf, &size);

	if (res == -1) {
		Py_DECREF(pValue);
		goto error;
	}

	/* Write the data to the network stream and be done with it */
	bufferevent_write(req->evb, buf, size);

	return;

 error:
	PyErr_Print();
	pyextend_request_free(req);
}

void
pyextend_evb_writecb(struct bufferevent *bev, void *parameter)
{
	/* 
	 * At this point, we have written all of our result data, so
	 * we just close the connection.
	 */
	struct pyextend_request *req = parameter;
	pyextend_request_free(req);
}

void
pyextend_evb_errcb(struct bufferevent *bev, short what, void *parameter)
{
	struct pyextend_request *req = parameter;
	pyextend_request_free(req);
}

/* Frees a request object and closes the connection */

void
pyextend_request_free(struct pyextend_request *req)
{
	bufferevent_free(req->evb);
	close(req->fd);
	free(req);
}

/* Creates a request object that can be used for streaming data */

struct pyextend_request *
pyextend_request_new(int fd, struct addr *src)
{
	struct pyextend_request *req = NULL;

	if ((req = calloc(1, sizeof(struct pyextend_request))) == NULL)
		return (NULL);

	req->fd = fd;
	req->src = *src;

	if ((req->evb = bufferevent_new(fd,
		 pyextend_evb_readcb, pyextend_evb_writecb, pyextend_evb_errcb,
		 req)) == NULL) {
		free(req);
		return (NULL);
	}

	/* Highest priority to UI requests */
	bufferevent_priority_set(req->evb, 0);

	bufferevent_enable(req->evb, EV_READ);	
	return (req);
}

void
pyextend_accept(int fd, short what, void *arg)
{
	struct sockaddr_storage ss;
	socklen_t socklen = sizeof(ss);
	struct addr src;
	struct pyextend_request *req = NULL;
	int newfd;

	if ((newfd = accept(fd, (struct sockaddr *)&ss, &socklen)) == -1) {
		warn("%s: accept", __func__);
		return;
	}

	addr_ston((struct sockaddr *)&ss, &src);
	syslog(LOG_DEBUG, "%s: new request from %s",
	    __func__, addr_ntoa(&src));

	/* Create a new request structure and dispatch the request */
	if ((req = pyextend_request_new(newfd, &src)) == NULL) {
		warn("%s: calloc", __func__);
		close(newfd);
		return;
	}
}

void
pyextend_webserver_fix_permissions(const char *path, uid_t uid, gid_t gid)
{
	static int created_dirs;
	DIR *dir;
	struct dirent *file;
	struct stat sb;
	char fullname[PATH_MAX];
	int off;

	/* Create special directories */
	if (!created_dirs) {
		created_dirs = 1;
		if (snprintf(fullname, sizeof(fullname), "%s/graphs", path) >=
		    sizeof(fullname))
			errx(1, "Path too long: %s/graphs", path);
		if (lstat(fullname, &sb) == -1 && errno == ENOENT) {
			syslog(LOG_INFO, "Creating directory %s", fullname);
			if (mkdir(fullname, 0722) == -1)
				err(1, "mkdir(%s)", fullname);
		}
	}

	/* Fix permissions */
	if (strlen(path) >= sizeof (fullname) - 2)
		errx(1, "directory name too long");

	dir = opendir(path);
	if (dir == NULL)
		err(1, "opendir(%s)", path);

	strlcpy(fullname, path, sizeof (fullname));
	off = strlen(fullname);
	if (fullname[off - 1] != '/') {
		strlcat(fullname, "/", sizeof(fullname));
		off++;
	}

	while ((file = readdir(dir)) != NULL) {
		char *filename = file->d_name;
		if (!strcmp(filename, "..") || !strcmp(filename, "CVS"))
			continue;

		strlcpy(fullname + off, filename, sizeof(fullname) - off);

		if (lstat(fullname, &sb) == -1)
			err(1, "lstat(%s)", fullname);

		/* We ignore symbolic links - shoot yourself in the foot */
		if (sb.st_mode & S_IFLNK)
			continue;

		/* Change owner ship to us */
		if (sb.st_uid != uid || sb.st_gid != gid) {
			syslog(LOG_INFO, "Fixing ownership: %s", fullname);
			if (chown(fullname, uid, gid) == -1)
				err(1, "chown(%s)", fullname);
		}

		if ((sb.st_mode & (S_IRUSR|S_IWUSR)) != (S_IRUSR|S_IWUSR) ||
		    (sb.st_mode & S_IWOTH)) {
			int mode = (sb.st_mode & 0777);
			mode |= (S_IRUSR|S_IWUSR);
			/* No write access for others */
			mode &= ~S_IWOTH;

			syslog(LOG_INFO, "Fixing modes: %s", fullname);
			if (chmod(fullname, mode) == -1)
				err(1, "chmod(%s)", fullname);
		}

		if ((sb.st_mode & S_IFDIR) && filename[0] != '.')
			pyextend_webserver_fix_permissions(fullname, uid, gid);
	}
	closedir(dir);
}

void
pyextend_webserver_verify_setup(const char *root_dir)
{
	char filename[1024];
	struct _dirs {
		const char *path;
		int mode;
	} dirs[] = { 
		{ "styles", R_OK },
		{ "images", R_OK },
		{ ".", W_OK|R_OK },
		{ "graphs", W_OK|R_OK },
		{ "templates", W_OK|R_OK },
		{ NULL, 0 }
	};
	struct _dirs *p;

	for (p = &dirs[0]; p->path != NULL; p++) {
		snprintf(filename, sizeof(filename), "%s/%s",
		    root_dir, p->path);
		if (access(filename, p->mode) == -1) {
			syslog(LOG_ERR,
			    "webserver: require%s%s access to %s: %m",
			    p->mode & W_OK ? " write" : "",
			    p->mode & R_OK ? " read" : "",
			    filename);
			exit(1);
		}
	}
}

/*
 * Intializes the Python webserver.  It listens on the specified port
 * and serves documents from the specified directory.
 */

void
pyextend_webserver_init(char *address, int port, char *root_dir)
{
	PyObject *pArgs, *pName, *pModule, *pDict, *pFuncWebInit;
	char *script = "server";

	pName = PyString_FromString(script);
	pModule = PyImport_Import(pName);
	Py_DECREF(pName);

	if (pModule == NULL) {
		PyErr_Print();
		err(1, "%s: could not load python module: %s",
		    __func__, script);
	}

	pDict = PyModule_GetDict(pModule); /* Borrowed */

	CHECK_FUNC(pFuncWebInit, "make_server");
	CHECK_FUNC(pFuncRequest, "handle_request");
	pArgs = Py_BuildValue("(s)", root_dir);
	if (pArgs == NULL) {
		PyErr_Print();
		errx(1, "%s: Failed to build value", __func__);
	}
	pWebServer = PyObject_CallObject(pFuncWebInit, pArgs);
	Py_DECREF(pArgs);
	if (pWebServer == NULL) {
		PyErr_Print();
		errx(1, "%s: make_server function returned error.", __func__);
	}

	pyserver_fd = make_socket(bind, SOCK_STREAM, address, port);

	if (pyserver_fd == -1) {
		fprintf(stderr,
		    "\nA web server might already be running on port %d.\n"
		    "Choose another port via --webserver-port or disable\n"
		    "the built in webserver via --disable-webserver.\n", port);
		exit(1);
	}

	if (listen(pyserver_fd, 10) == -1)
		err(1, "%s: listen", __func__);

	syslog(LOG_NOTICE, "HTTP server listening on %s:%d", address, port);
	syslog(LOG_NOTICE, "HTTP server root at %s", root_dir);
	
	/* Accept connections */
	event_set(&ev_accept, pyserver_fd, EV_READ | EV_PERSIST,
	    pyextend_accept, NULL);

	/* Give the highest priority to the accept */
	event_priority_set(&ev_accept, 0);
	event_add(&ev_accept, NULL);
	return;

 error:
	Py_DECREF(pModule);
	errx(1, "Cannot initialize module.");
}

void
pyextend_webserver_exit(void)
{
	event_del(&ev_accept);
	close(pyserver_fd);
}


syntax highlighted by Code2HTML, v. 0.9.1