/*
 * Copyright (c) 2005 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/param.h>
#include <sys/types.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/queue.h>
#include <sys/socket.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <netinet/in.h>

#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <ctype.h>
#include <getopt.h>
#include <err.h>

#include <pcre.h>
#include <event.h>
#include <evdns.h>
#include <dnet.h>

#include "util.h"
#include "proxy.h"
#include "proxy_messages.h"
#include "smtp.h"
#include "honeyd_overload.h"

extern int debug;

#define DFPRINTF(x, y)	do { \
	if (debug >= x) fprintf y; \
} while (0)

/* globals */

FILE *flog_proxy = NULL;	/* log the proxy transactions somewhere */
static pcre *re_connect;	/* regular expression to match connect */
static pcre *re_hostport;	/* extracts host and port */
static pcre *re_get;		/* generic get request */

/* Generic PROXY related code */

char *
proxy_logline(struct proxy_ta *ta)
{
	static char line[1024];
	char *srcipaddress = kv_find(&ta->dictionary, "$srcipaddress");
	char *cmd = kv_find(&ta->dictionary, "$command");
	char *host = kv_find(&ta->dictionary, "$host");
	char *port = kv_find(&ta->dictionary, "$port");
	char *uri = kv_find(&ta->dictionary, "$rawuri");

	if (!strcasecmp("connect", cmd)) {
		snprintf(line, sizeof(line),
		    "%d %s: CONNECT %s:%s",
		    time(NULL), srcipaddress,
		    host, port);
	} else {
		snprintf(line, sizeof(line),
		    "%d %s: GET %s:%s%s", 
		    time(NULL), srcipaddress,
		    host, port, uri);
	}

	return (line);
}

void
proxy_clear_state(struct proxy_ta *ta)
{
	/* XXX - something here */
}

/* Callbacks for PROXY handling */

char *
proxy_response(struct proxy_ta *ta, struct keyvalue data[]) {
	static char line[1024];
	struct keyvalue *cur;

	for (cur = &data[0]; cur->key != NULL; cur++) {
		if (strcmp(ta->proxy_id, cur->key) == 0)
			break;
	}

	if (cur->key == NULL)
		return (NULL);

	strlcpy(line, cur->value, sizeof(line));

	TAILQ_FOREACH(cur, &ta->dictionary, next) {
		strrpl(line, sizeof(line), cur->key, cur->value);
	}
	
	return (line);
}

int
proxy_allowed_network(const char *host)
{
	const char *error;
	int erroroffset;
	pcre *re_uri;
	int rc;
	int ovector[30];
	char *unusednets[] = {
		"^127\\.[0-9]+\\.[0-9]+\\.[0-9]+$",		/* local */
		"^10\\.[0-9]+\\.[0-9]+\\.[0-9]+$",		/* rfc-1918 */
		"^172\\.(1[6-9]|2[0-9]|3[01])\\.[0-9]+\\.[0-9]+$",
		"^192\\.168\\.[0-9]+\\.[0-9]+$",		/* rfc-1918 */
		"^2(2[4-9]|3[0-9])\\.[0-9]+\\.[0-9]+\\.[0-9]+$",/* rfc-1112 */
		"^2(4[0-9]|5[0-5])\\.[0-9]+\\.[0-9]+\\.[0-9]+$",
		"^0\\.[0-9]+\\.[0-9]+\\.[0-9]+$",
		"^255\\.[0-9]+\\.[0-9]+\\.[0-9]+$",
		NULL
	};

	char **p;

	for (p = &unusednets[0]; *p; ++p) {
		re_uri = pcre_compile(*p, PCRE_CASELESS,
		    &error, &erroroffset, NULL);
		if (re_uri == NULL) {
			/* Default to no match */
			fprintf(stderr, "%s: %s: %s at %d",
			    __func__, *p, error, erroroffset);
			return (0);
		}

		/* Match against the URI */
		rc = pcre_exec(re_uri, NULL, host, strlen(host),
		    0, 0, ovector, 30);
		pcre_free(re_uri);

		if (rc >= 0)
			return (0);
	}

	return (1);
}

/*
 * Checks if we are allowed to retrieve a URL from here.
 */

int
proxy_allowed_get(struct proxy_ta *ta, struct keyvalue data[])
{
	const char *error;
	int erroroffset;
	char *host, *uri;
	struct keyvalue *cur;
	pcre *re_uri;
	int rc;
	int ovector[30];

	host = kv_find(&ta->dictionary, "$host");
	uri = kv_find(&ta->dictionary, "$rawuri");

	for (cur = &data[0]; cur->key != NULL; cur++) {
		if (strcmp(host, cur->key) == 0)
			break;
	}

	/* Host is not allowed if we do not find it */
	if (cur->key == NULL)
		return (0);

	re_uri = pcre_compile(cur->value, PCRE_CASELESS,
	    &error, &erroroffset, NULL);
	if (re_uri == NULL) {
		/* Default to no match */
		fprintf(stderr, "%s: %s: %s at %d",
		    __func__, cur->value, error, erroroffset);
		return (0);
	}

	/* Match against the URI */
	rc = pcre_exec(re_uri, NULL, uri, strlen(uri), 0, 0, ovector, 30);

	pcre_free(re_uri);

	return (rc >= 0);
}

int
proxy_bad_connection(struct proxy_ta *ta)
{
	char *response = proxy_response(ta, badconnection);
	bufferevent_write(ta->bev, response, strlen(response));
	ta->wantclose = 1;
	return (0);
}

void
proxy_remote_readcb(struct bufferevent *bev, void *arg)
{
	struct proxy_ta *ta = arg;
	struct evbuffer *buffer = EVBUFFER_INPUT(bev);
	unsigned char *data = EVBUFFER_DATA(buffer);
	size_t len = EVBUFFER_LENGTH(buffer);

	bufferevent_write(ta->bev, data, len);
	evbuffer_drain(buffer, len);
}

void
proxy_remote_writecb(struct bufferevent *bev, void *arg)
{
}

void
proxy_remote_errorcb(struct bufferevent *bev, short what, void *arg)
{
	struct proxy_ta *ta = arg;
	struct evbuffer *buffer = EVBUFFER_OUTPUT(ta->bev);
	fprintf(stderr, "%s: called with %p, freeing\n", __func__, arg);

	/* If we still have data to write; we just wait for the flush */
	if (EVBUFFER_LENGTH(buffer)) {
		/* Shutdown this site at least - XXX: maybe call shutdown */
		bufferevent_disable(bev, EV_READ|EV_WRITE);

		ta->wantclose = 1;
	} else {
		proxy_ta_free(ta);
	}
}

char *
proxy_corrupt(char *data, size_t len)
{
	static char buffer[4096];
	int corruptions = len / CORRUPT_SPACE + 1;
	int i;

	if (len > sizeof(buffer) || len <= 1)
		return (data);

	memcpy(buffer, data, len);
	for (i = 0; i < corruptions; i++) {
		int off = rand() % (len - 1);
		buffer[off] = rand();
	}

	return (buffer);
}

void
proxy_connect_cb(int fd, short what, void *arg)
{
	char line[1024], *data;
	struct proxy_ta *ta = arg;
	int error;
	socklen_t errsz = sizeof(error);
	char *uri;

	/* Check if the connection completed */
	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &errsz) == -1 ||
	    error) {
		char *response;
		fprintf(stderr, "%s: connection failed: %s\n",
		    __func__, strerror(error));
		close(fd);

		/* Give them a connect error message */
		kv_replace(&ta->dictionary, "$reason", strerror(error));
		response = proxy_response(ta, badconnect);
		bufferevent_write(ta->bev, response, strlen(response));

		ta->wantclose = 1;
		return;
	}

	ta->remote_bev = bufferevent_new(ta->remote_fd,
	    proxy_remote_readcb, proxy_remote_writecb,
	    proxy_remote_errorcb, ta);
	if (ta->bev == NULL) {
		close(fd);
		proxy_ta_free(ta);
		return;
	}

	/* If this get is not allowed, we are going to corrupt the data */
	if (!proxy_allowed_get(ta, allowedhosts))
		ta->corrupt = 1;

	uri = kv_find(&ta->dictionary, "$rawuri");
	snprintf(line, sizeof(line), "GET %s HTTP/1.0\r\n",
	    ta->corrupt ? proxy_corrupt(uri, strlen(uri)) : uri);
	bufferevent_write(ta->remote_bev, line, strlen(line));

	/* Forward all the headers */
	while ((data = kv_find(&ta->dictionary, "data")) != NULL) {
		/* We do not propagate X-Forwarded-For headers */
		if (strncasecmp(X_FORWARDED, data, strlen(X_FORWARDED))) {
			bufferevent_write(ta->remote_bev,
			    ta->corrupt ? proxy_corrupt(data, strlen(data)) :
			    data, strlen(data)); 
			bufferevent_write(ta->remote_bev, "\r\n", 2); 
		}

		/* Do not invalidate this data until we used it */
		kv_remove(&ta->dictionary, "data");
	}
	bufferevent_write(ta->remote_bev, "\r\n", 2); 

	/* Allow the remote site to send us data */
	bufferevent_enable(ta->remote_bev, EV_READ);

	ta->justforward = 1;
}

void
proxy_connect(struct proxy_ta *ta, char *host, int port)
{
	fprintf(stderr, "Connecting to %s port %d\n", host, port);

	ta->remote_fd = -1;
	if (proxy_allowed_network(host)) {
		char *local_ip = kv_find(&ta->dictionary, "$dstipaddress");

		if (local_ip != NULL) {
			ta->remote_fd = make_bound_connect(
				SOCK_STREAM, host, port, local_ip);
		} else {
			ta->remote_fd = make_socket(
				connect, SOCK_STREAM, host, port);
		}
	}
	if (ta->remote_fd == -1) {
		char *response;
		fprintf(stderr, "%s: failed to connect: %s\n",
		    __func__, strerror(errno));
		kv_replace(&ta->dictionary, "$reason", strerror(errno));
		response = proxy_response(ta, badconnect);
		bufferevent_write(ta->bev, response, strlen(response));
		ta->wantclose = 1;
		return;
	}

	/* One handy event to get called back on this */
	event_once(ta->remote_fd, EV_WRITE, proxy_connect_cb, ta, NULL);
}

void
proxy_handle_get_cb(int result, char type, int count, int ttl,
    void *addresses, void *arg)
{
	struct proxy_ta *ta = arg;
	struct addr addr;
	struct in_addr *in_addrs = addresses;
	int port = atoi(kv_find(&ta->dictionary, "$port"));
	char *response;

	if (ta->dns_canceled) {
		proxy_ta_free(ta);
		return;
	}
	ta->dns_pending = 0;

	if (result != DNS_ERR_NONE || type != DNS_IPv4_A || count == 0) {
		response = proxy_response(ta, baddomain);
		bufferevent_write(ta->bev, response, strlen(response));
		ta->wantclose = 1;
		return;
	}

	/* Need to make a connection here */
	bufferevent_disable(ta->bev, EV_READ);

	addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS, &in_addrs[0], IP_ADDR_LEN);
	proxy_connect(ta, addr_ntoa(&addr), port);
}

int
proxy_handle_get(struct proxy_ta *ta)
{
	char *host = kv_find(&ta->dictionary, "$rawhost");
	int rc;
	int ovector[30];

	kv_replace(&ta->dictionary, "$command", "GET");

	rc = pcre_exec(re_hostport, NULL, host, strlen(host), 0, 0,
	    ovector, 30);
	if (rc >= 0) {
		char *strport = proxy_pcre_group(host, 2, ovector);
		char *real_host = proxy_pcre_group(host, 1, ovector);

		kv_add(&ta->dictionary, "$host", real_host);
		kv_add(&ta->dictionary, "$port", strport);

		free(real_host);
		free(strport);
	} else {
		kv_add(&ta->dictionary, "$host", host);
		kv_add(&ta->dictionary, "$port", "80");
	}

	if (flog_proxy != NULL) {
		char *line = proxy_logline(ta);
		fprintf(flog_proxy, "%s\n", line);
		fflush(flog_proxy);
	}

	/* Try to resolve the domain name */
	evdns_resolve_ipv4(kv_find(&ta->dictionary, "$host"), 0,
	    proxy_handle_get_cb, ta);
	ta->dns_pending = 1;
	return (0);
}

void
proxy_handle_connect_cb(int result, char type, int count, int ttl,
    void *addresses, void *arg)
{
	struct proxy_ta *ta = arg;
	char *host = kv_find(&ta->dictionary, "$host");
	int port = atoi(kv_find(&ta->dictionary, "$port"));
	char *response;
	fprintf(stderr, "Connecting to %s port %d\n", host, port);

	if (ta->dns_canceled) {
		proxy_ta_free(ta);
		return;
	}
	ta->dns_pending = 0;

	if (result != DNS_ERR_NONE) {
		response = proxy_response(ta, baddomain);
		bufferevent_write(ta->bev, response, strlen(response));
		ta->wantclose = 1;
		return;
	}

	if (port != 25 || !proxy_allowed_network(host)) {
		response = proxy_response(ta, badport);
		bufferevent_write(ta->bev, response, strlen(response));
		ta->wantclose = 1;
	} else {
		struct smtp_ta *smtp_ta = NULL;
		int fd = dup(ta->fd);

		if (fd != -1)
			smtp_ta = smtp_ta_new(fd,
			    (struct sockaddr *)&ta->sa, ta->salen, 
			    NULL, 0, 0);
		if (smtp_ta != NULL) {
			response = proxy_response(ta, goodport);
			bufferevent_write(smtp_ta->bev,
			    response, strlen(response));
			smtp_greeting(smtp_ta);

			proxy_ta_free(ta);
		} else {
			kv_add(&ta->dictionary, "$host", host);
			response = proxy_response(ta, badport);
			bufferevent_write(ta->bev, response, strlen(response));
			ta->wantclose = 1;
		}
	}
}

int
proxy_handle_connect(struct proxy_ta *ta)
{
	char *host = kv_find(&ta->dictionary, "$rawhost");
	int rc;
	int ovector[30];

	kv_replace(&ta->dictionary, "$command", "CONNECT");

	rc = pcre_exec(re_hostport, NULL, host, strlen(host), 0, 0,
	    ovector, 30);
	if (rc >= 0) {
		char *strport = proxy_pcre_group(host, 2, ovector);
		char *real_host = proxy_pcre_group(host, 1, ovector);

		kv_add(&ta->dictionary, "$host", real_host);
		kv_add(&ta->dictionary, "$port", strport);

		free(real_host);
		free(strport);
	} else {
		kv_add(&ta->dictionary, "$host", host);
		kv_add(&ta->dictionary, "$port", "80");
	}

	if (flog_proxy != NULL) {
		char *line = proxy_logline(ta);
		fprintf(flog_proxy, "%s\n", line);
		fflush(flog_proxy);
	}

	/* Try to resolve the domain name */
	evdns_resolve_ipv4(kv_find(&ta->dictionary, "$host"), 0,
	    proxy_handle_connect_cb, ta);
	ta->dns_pending = 1;
	return (0);
}

char *
proxy_pcre_group(char *line, int groupnr, int ovector[])
{
	int start = ovector[2*groupnr];
	int end = ovector[2*groupnr + 1];
	char *group = malloc(end - start + 1);
	if (group == NULL)
		err(1, "%s: malloc", __func__);
	memcpy(group, line + start, end - start);
	group[end-start] = '\0';

	return (group);
}

int
proxy_handle(struct proxy_ta *ta, char *line)
{
	int rc;
	int ovector[30];

	/* Execute regular expressions to match the command */

	rc = pcre_exec(re_connect, NULL, line, strlen(line), 0, 0,
	    ovector, 30);
	if (rc >= 0) {
		char *host = proxy_pcre_group(line, 1, ovector);
		kv_replace(&ta->dictionary, "$rawhost", host);
		free(host);

		ta->empty_cb = proxy_handle_connect;
		return (0);
	}

	rc = pcre_exec(re_get, NULL, line, strlen(line), 0, 0, ovector, 30);
	if (rc >= 0) {
		char *host = proxy_pcre_group(line, 1, ovector);
		char *uri = proxy_pcre_group(line, 2, ovector);
		kv_replace(&ta->dictionary, "$rawhost", host);
		kv_replace(&ta->dictionary, "$rawuri", uri);
		free(host);
		free(uri);

		ta->empty_cb = proxy_handle_get;
		return (0);
	}

	return proxy_bad_connection(ta);
}

char *
proxy_readline(struct bufferevent *bev)
{
	struct evbuffer *buffer = EVBUFFER_INPUT(bev);
	char *data = EVBUFFER_DATA(buffer);
	size_t len = EVBUFFER_LENGTH(buffer);
	char *line;
	int i;

	for (i = 0; i < len; i++) {
		if (data[i] == '\r' || data[i] == '\n')
			break;
	}
	
	if (i == len)
		return (NULL);

	if ((line = malloc(i + 1)) == NULL) {
		fprintf(stderr, "%s: out of memory\n", __func__);
		evbuffer_drain(buffer, i);
		return (NULL);
	}

	memcpy(line, data, i);
	line[i] = '\0';

	if ( i < len - 1 ) {
		char fch = data[i], sch = data[i+1];

		/* Drain one more character if needed */
		if ( (sch == '\r' || sch == '\n') && sch != fch )
			i += 1;
	}

	evbuffer_drain(buffer, i + 1);

	return (line);
}

void
proxy_readcb(struct bufferevent *bev, void *arg)
{
	struct proxy_ta *ta = arg;
	char *line;

	if (ta->justforward) {
		struct evbuffer *input = EVBUFFER_INPUT(bev);
		char *data = EVBUFFER_DATA(input);
		size_t len = EVBUFFER_LENGTH(input);
		if (ta->corrupt) {
			bufferevent_write(ta->remote_bev,
			    proxy_corrupt(data, len), len);
		} else {
			bufferevent_write(ta->remote_bev, data, len);
		}
		evbuffer_drain(input, len);
		return;
	}

	while ((line = proxy_readline(bev)) != NULL) {
		int res = 0;
		/* If we are ready to close on the bugger, just eat it */
		if (ta->wantclose) {
			free(line);
			continue;
		}
		if (ta->empty_cb) {
			/* eat the input until we get a return */
			if (strlen(line)) {
				kv_add(&ta->dictionary, "data", line);
				free(line);
				continue;
			} else {
				res = (*ta->empty_cb)(ta);
				ta->empty_cb = NULL;
			}
		} else {
			res = proxy_handle(ta, line);
		}
		free(line);

		/* Destroy the state machine on error */
		if (res == -1) {
			proxy_ta_free(ta);
			return;
		}
	}
}

void
proxy_writecb(struct bufferevent *bev, void *arg)
{
	struct proxy_ta *ta = arg;
	
	if (ta->wantclose)
		proxy_ta_free(ta);
}

void
proxy_errorcb(struct bufferevent *bev, short what, void *arg)
{
	fprintf(stderr, "%s: called with %p, freeing\n", __func__, arg);

	proxy_ta_free(arg);
}

/* Tear down a connection */
void
proxy_ta_free(struct proxy_ta *ta)
{
	struct keyvalue *entry;

	if (ta->dns_pending && !ta->dns_canceled) {
		/* if we have a pending dns lookup, tell it to cancel */
		ta->dns_canceled = 1;
		return;
	}

	while ((entry = TAILQ_FIRST(&ta->dictionary)) != NULL) {
		TAILQ_REMOVE(&ta->dictionary, entry, next);
		free(entry->key);
		free(entry->value);
		free(entry);
	}

	bufferevent_free(ta->bev);
	close(ta->fd);

	if (ta->remote_bev) {
		bufferevent_free(ta->remote_bev);
		close(ta->remote_fd);
	}

	free(ta);
	
}

/* Create a new PROXY transaction */

struct proxy_ta *
proxy_ta_new(int fd, struct sockaddr *sa, socklen_t salen,
    struct sockaddr *lsa, socklen_t lsalen)
{
	struct proxy_ta *ta = calloc(1, sizeof(struct proxy_ta));
	char *srcipname, *srcportname;
	char *dstipname, *dstportname;

	if (ta == NULL)
		goto error;

	ta->proxy_id = "junkbuster";

	TAILQ_INIT(&ta->dictionary);

	memcpy(&ta->sa, sa, salen);
	ta->salen = salen;

	ta->fd = fd;
	ta->bev = bufferevent_new(fd,
	    proxy_readcb, proxy_writecb, proxy_errorcb, ta);
	if (ta->bev == NULL)
		goto error;

	/* Create our tiny dictionary */
	if (lsa != NULL) {
		name_from_addr(lsa, lsalen, &dstipname, &dstportname);
		kv_add(&ta->dictionary, "$dstipaddress", dstipname);
	}

	name_from_addr(sa, salen, &srcipname, &srcportname);
	kv_add(&ta->dictionary, "$srcipaddress", srcipname);

	bufferevent_enable(ta->bev, EV_READ);

	fprintf(stderr, "%s: new proxy instance to %s complete.\n",
	    __func__, srcipname);

	return (ta);

 error:
	if (ta != NULL)
		free(ta);
	fprintf(stderr, "%s: out of memory\n", __func__);
	close(fd);

	return (NULL);
}

static void
accept_socket(int fd, short what, void *arg)
{
	struct sockaddr_storage ss, lss;
	socklen_t addrlen = sizeof(ss), laddrlen = sizeof(lss);
	int nfd, res;

	if ((nfd = accept(fd, (struct sockaddr *)&ss, &addrlen)) == -1) {
		fprintf(stderr, "%s: bad accept\n", __func__);
		return;
	}

	/* Test our special subsystem magic */
	res = fcntl(fd, F_XXX_GETSOCK, &lss, &laddrlen);

	if (res != -1) {
		/*
		 * We are running under honeyd and could figure out
		 * who we are.  That's great.
		 */
		proxy_ta_new(nfd, (struct sockaddr *)&ss, addrlen,
		    (struct sockaddr *)&lss, laddrlen);
	} else {
		proxy_ta_new(nfd, (struct sockaddr *)&ss, addrlen,
		    NULL, 0);
	}
}

void
proxy_bind_socket(struct event *ev, u_short port)
{
	int fd;

	if ((fd = make_socket(bind, SOCK_STREAM, "0.0.0.0", port)) == -1)
		err(1, "%s: cannot bind socket: %d", __func__, port);

	if (listen(fd, 10) == -1)
		err(1, "%s: listen failed: %d", __func__, port);

	/* Schedule the socket for accepting */
	event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, NULL);
	event_add(ev, NULL);

	fprintf(stderr, 
	    "Bound to port %d\n"
	    "Awaiting connections ... \n",
	    port);
}

void
proxy_init(void)
{
	const char *error;
	int erroroffset;
	const char *exp_connect = "^connect\\s+(.*)\\s+http";
	const char *exp_hostport = "^(.*):([0-9]+)$";
	const char *exp_get = "^GET\\s+http://([^/ ]*)(/?[^ ]*)\\s+HTTP";

	/* Compile regular expressions for command parsing */
	re_connect = pcre_compile(exp_connect, PCRE_CASELESS,
	    &error, &erroroffset, NULL);
	if (re_connect == NULL)
		err(1, "%s: %s at %d", __func__, error, erroroffset);

	re_hostport = pcre_compile(exp_hostport, PCRE_CASELESS,
	    &error, &erroroffset, NULL);
	if (re_connect == NULL)
		err(1, "%s: %s at %d", __func__, error, erroroffset);

	re_get = pcre_compile(exp_get, PCRE_CASELESS,
	    &error, &erroroffset, NULL);
	if (re_connect == NULL)
		err(1, "%s: %s at %d", __func__, error, erroroffset);
}


syntax highlighted by Code2HTML, v. 0.9.1