Commit Diff


commit - 906d82cc46339e4ceae4f40df4050e3953d666b2
commit + 05982e1a011d4b6bfa4caadde9817a0e5380afc9
blob - 2cf8d3a13c1792a1c4ee6e99d0daaaf7a5222db8
blob + b619932c623292b68eeb94c8dc7e6df034a1b88b
--- Makefile
+++ Makefile
@@ -1,13 +1,13 @@
-#	$OpenBSD: Makefile,v 1.4 2007/01/09 02:32:58 reyk Exp $
+#	$OpenBSD: Makefile,v 1.5 2007/01/29 14:23:31 pyr Exp $
 
 PROG=		hoststated
 SRCS=		parse.y log.c control.c buffer.c imsg.c hoststated.c 	\
-		pfe.c pfe_filter.c hce.c 				\
+		ssl.c pfe.c pfe_filter.c hce.c 				\
 		check_icmp.c check_tcp.c check_http.c check_send_expect.c
 MAN=		hoststated.8 hoststated.conf.5
 
-LDADD=		-levent
-DPADD=		${LIBEVENT}
+LDADD=		-levent -lcrypto -lssl
+DPADD=		${LIBEVENT} ${LIBCRYPTO} ${LIBSSL}
 CFLAGS+=	-Wall -Werror -I${.CURDIR}
 CFLAGS+=	-Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+=	-Wmissing-declarations
blob - 7133094ddfcf73ddae5fefd2ac77ca12473d5550
blob + 4d85f78ea57b018c56825802ba917289ab59e01a
--- buffer.c
+++ buffer.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: buffer.c,v 1.4 2007/01/09 00:45:32 deraadt Exp $	*/
+/*	$OpenBSD: buffer.c,v 1.5 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -30,6 +30,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 int	buf_realloc(struct buf *, size_t);
blob - f4167359a0ee034b98c16ded17676138b5cf759a
blob + ce83f7de2141c7755548a2ac74a88cc559c8faff
--- check_icmp.c
+++ check_icmp.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: check_icmp.c,v 1.11 2007/01/12 17:12:58 pyr Exp $	*/
+/*	$OpenBSD: check_icmp.c,v 1.12 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -35,6 +35,8 @@
 #include <stdlib.h>
 #include <err.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 void	icmp_setup(struct hoststated *, struct ctl_icmp_event *, int);
blob - 7195a216710a7dabb57d1e9bfc743af444fe9523
blob + a3ebc23c56377ecc1417a05ec9b65799865a7ed9
--- check_tcp.c
+++ check_tcp.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: check_tcp.c,v 1.11 2007/01/20 16:32:10 pyr Exp $	*/
+/*	$OpenBSD: check_tcp.c,v 1.12 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -30,6 +30,8 @@
 #include <stdlib.h>
 #include <errno.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 void	tcp_write(int, short, void *);
@@ -108,6 +110,7 @@ tcp_write(int s, short event, void *arg)
 		else
 			cte->host->up = HOST_UP;
 	}
+
 	if (cte->host->up == HOST_UP)
 		tcp_host_up(s, cte);
 	else {
@@ -123,8 +126,10 @@ tcp_host_up(int s, struct ctl_tcp_event *cte)
 
 	switch (cte->table->check) {
 	case CHECK_TCP:
+		if (cte->table->flags & F_SSL)
+			break;
 		close(s);
-		hce_notify_done(cte->host, "tcp_host_up: success");
+		hce_notify_done(cte->host, "tcp_host_up: connect successfull");
 		return;
 	case CHECK_HTTP_CODE:
 		cte->validate_read = NULL;
@@ -140,6 +145,11 @@ tcp_host_up(int s, struct ctl_tcp_event *cte)
 		break;
 	}
 
+	if (cte->table->flags & F_SSL) {
+		ssl_transaction(cte);
+		return;
+	}
+
 	if (cte->table->sendbuf != NULL) {
 		cte->req = cte->table->sendbuf;
 		event_again(&cte->ev, s, EV_TIMEOUT|EV_WRITE, tcp_send_req,
blob - 46ac0e87ec86b488130b2e03624f5973d780e9ba
blob + b7f78b2434e0dc4ad0145a5d5aa1831a927f2fec
--- control.c
+++ control.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: control.c,v 1.9 2007/01/23 17:43:36 claudio Exp $	*/
+/*	$OpenBSD: control.c,v 1.10 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -31,6 +31,8 @@
 #include <unistd.h>
 #include <signal.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 #define	CONTROL_BACKLOG	5
blob - aceef6f8e88459750539701f52065e5c976eb372
blob + ac88731116499633b925f4ddb35138be4cc73ab9
--- hce.c
+++ hce.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: hce.c,v 1.11 2007/01/24 10:26:00 claudio Exp $	*/
+/*	$OpenBSD: hce.c,v 1.12 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -36,6 +36,8 @@
 #include <err.h>
 #include <pwd.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 void	hce_sig_handler(int sig, short, void *);
@@ -70,6 +72,7 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], 
 	struct timeval	 tv;
 	struct event	 ev_sigint;
 	struct event	 ev_sigterm;
+	struct table	*table;
 
 	switch (pid = fork()) {
 	case -1:
@@ -135,6 +138,15 @@ hce(struct hoststated *x_env, int pipe_parent2pfe[2], 
 	bzero(&tv, sizeof(tv));
 	evtimer_add(&env->ev, &tv);
 
+	if (env->flags & F_SSL) {
+		ssl_init(env);
+		TAILQ_FOREACH(table, &env->tables, entry) {
+			if (!(table->flags & F_SSL))
+				continue;
+			table->ssl_ctx = ssl_ctx_create(env);
+		}
+	}
+
 	event_dispatch();
 
 	hce_shutdown();
blob - fb110a45fb80427da160004fe37a0cea8b0935e5
blob + 83d633e2dc1f5b4dea84fc8421e6bf22b8909d45
--- imsg.c
+++ imsg.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: imsg.c,v 1.4 2007/01/09 00:45:32 deraadt Exp $	*/
+/*	$OpenBSD: imsg.c,v 1.5 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -28,6 +28,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 void
blob - 7dc5648ea6fd57aa218ba8295990d599036479d3
blob + 195823f2aa38210d2a35ac0d247cd45aa7ced3bf
--- parse.y
+++ parse.y
@@ -1,4 +1,4 @@
-/*	$OpenBSD: parse.y,v 1.18 2007/01/25 19:40:08 niallo Exp $	*/
+/*	$OpenBSD: parse.y,v 1.19 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -41,6 +41,8 @@
 #include <netdb.h>
 #include <string.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 struct hoststated			*conf = NULL;
@@ -98,14 +100,14 @@ typedef struct {
 %}
 
 %token	SERVICE TABLE BACKUP HOST REAL
-%token  CHECK HTTP TCP ICMP EXTERNAL
+%token  CHECK HTTP HTTPS TCP ICMP EXTERNAL
 %token  TIMEOUT CODE DIGEST PORT TAG INTERFACE
 %token	VIRTUAL IP INTERVAL DISABLE STICKYADDR
-%token	SEND EXPECT NOTHING
+%token	SEND EXPECT NOTHING USE SSL
 %token	ERROR
 %token	<v.string>	STRING
 %type	<v.string>	interface
-%type	<v.number>	number port
+%type	<v.number>	number port http_type
 %type	<v.host>	host
 %type	<v.tv>		timeout
 
@@ -134,6 +136,10 @@ number		: STRING	{
 		}
 		;
 
+http_type	: HTTP		{ $$ = 0; }
+		| HTTPS		{ $$ = 1; }
+		;
+
 port		: PORT STRING	{
 			const char	*estr;
 			struct servent	*servent;
@@ -156,12 +162,22 @@ port		: PORT STRING	{
 				$$ = htons($$);
 			free($2);
 		}
-		| PORT HTTP {
+		| PORT http_type {
 			struct servent	*servent;
+			int		 port;
+			const char	*sport;
 
-			servent = getservbyname("http", "tcp");
+			if ($2) {
+				port = 443;
+				sport = "https";
+			} else {
+				port = 80;
+				sport = "http";
+			}
+
+			servent = getservbyname(sport, "tcp");
 			if (servent == NULL)
-				$$ = htons(80);
+				$$ = htons(port);
 			else
 				$$ = servent->s_port;
 		}
@@ -380,7 +396,16 @@ tableoptsl	: host			{
 		| CHECK TCP		{
 			table->check = CHECK_TCP;
 		}
-		| CHECK HTTP STRING CODE number {
+		| CHECK SSL		{
+			table->check = CHECK_TCP;
+			conf->flags |= F_SSL;
+			table->flags |= F_SSL;
+		}
+		| CHECK http_type STRING CODE number {
+			if ($2) {
+				conf->flags |= F_SSL;
+				table->flags |= F_SSL;
+			}
 			table->check = CHECK_HTTP_CODE;
 			table->retcode = $5;
 			asprintf(&table->sendbuf, "HEAD %s HTTP/1.0\r\n\r\n",
@@ -389,7 +414,11 @@ tableoptsl	: host			{
 			if (table->sendbuf == NULL)
 				fatal("out of memory");
 		}
-		| CHECK HTTP STRING DIGEST STRING {
+		| CHECK http_type STRING DIGEST STRING {
+			if ($2) {
+				conf->flags |= F_SSL;
+				table->flags |= F_SSL;
+			}
 			table->check = CHECK_HTTP_DIGEST;
 			asprintf(&table->sendbuf, "GET %s HTTP/1.0\r\n\r\n",
 			    $3);
@@ -418,6 +447,10 @@ tableoptsl	: host			{
 			table->port = $2;
 		}
 		| DISABLE			{ table->flags |= F_DISABLE; }
+		| USE SSL			{
+			table->flags |= F_SSL;
+			conf->flags |= F_SSL;
+		}
 		;
 
 interface	: /*empty*/		{ $$ = NULL; }
@@ -515,6 +548,7 @@ lookup(char *s)
 		{ "external",		EXTERNAL },
 		{ "host",		HOST },
 		{ "http",		HTTP },
+		{ "https",		HTTPS },
 		{ "icmp",		ICMP },
 		{ "interface",		INTERFACE },
 		{ "interval",		INTERVAL },
@@ -524,11 +558,13 @@ lookup(char *s)
 		{ "real",		REAL },
 		{ "send",		SEND },
 		{ "service",		SERVICE },
+		{ "ssl",		SSL },
 		{ "sticky-address",	STICKYADDR },
 		{ "table",		TABLE },
 		{ "tag",		TAG },
 		{ "tcp",		TCP },
 		{ "timeout",		TIMEOUT },
+		{ "use",		USE },
 		{ "virtual",		VIRTUAL }
 	};
 	const struct keywords	*p;
blob - 1077928d1efe6be86ce287b26ab12beb44859362
blob + 43c6f358c375336b9401b1e71668060af46f19f1
--- pfe.c
+++ pfe.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pfe.c,v 1.8 2007/01/24 10:26:00 claudio Exp $	*/
+/*	$OpenBSD: pfe.c,v 1.9 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -31,6 +31,8 @@
 #include <unistd.h>
 #include <pwd.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 void	pfe_sig_handler(int sig, short, void *);
blob - 18e0af927e39e89914666e5bc643b99030713fd7
blob + 22c95844271de3faf9379e2dfb85fd3e0f2bdc6b
--- pfe_filter.c
+++ pfe_filter.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pfe_filter.c,v 1.9 2007/01/09 13:50:11 pyr Exp $	*/
+/*	$OpenBSD: pfe_filter.c,v 1.10 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -35,6 +35,8 @@
 #include <stdlib.h>
 #include <errno.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 struct pfdata {
blob - 2a0b12336205b7a318a72cb3b6b8b529f1b63a81
blob + ae31748ae37a27b87735c326c85ecf9e2d23b9fb
--- relayd.c
+++ relayd.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relayd.c,v 1.12 2007/01/24 10:26:00 claudio Exp $	*/
+/*	$OpenBSD: relayd.c,v 1.13 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -33,6 +33,8 @@
 #include <unistd.h>
 #include <pwd.h>
 
+#include <openssl/ssl.h>
+
 #include "hoststated.h"
 
 __dead void	 usage(void);
blob - ae3f6ec209f407d0db5b72d7df63059b65a8269a
blob + ce0d6b760e844618383f5f1fef83286153438cd2
--- relayd.conf.5
+++ relayd.conf.5
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: relayd.conf.5,v 1.14 2007/01/10 13:42:19 jmc Exp $
+.\"	$OpenBSD: relayd.conf.5,v 1.15 2007/01/29 14:23:31 pyr Exp $
 .\"
 .\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
 .\"
@@ -109,6 +109,12 @@ For each host in the table, verify that retrieving the
 .Ar path
 gives the HTTP return code
 .Ar number .
+If
+.Ic use ssl
+is specified, HTTPS will be used to contact the host.
+.It Ic check https Ar path Ic code Ar number
+This has the same effect than above but also implies 
+.Ic use ssl .
 .It Ic check http Ar path Ic digest Ar string
 For each host in the table, verify that retrieving the URL
 .Ar path
@@ -125,6 +131,12 @@ that can be used as is in a digest statement:
 .Bd -literal -offset indent
 a9993e36476816aba3e25717850c26c9cd0d89d
 .Ed
+If
+.Ic use ssl
+is specified, HTTPS will be used to contact the host.
+.It Ic check https Ar path Ic digest Ar string
+This has the same effect than above but also implies 
+.Ic use ssl .
 .It Ic check icmp
 Ping hosts in this table to determine whether they are up or not.
 This method will automatically use ICMP or ICMPV6 depending on the
@@ -145,8 +157,17 @@ then nothing is sent on the connection and data is imm
 read.
 This can be useful with protocols that output a banner like
 SMTP, NNTP and FTP.
+If
+.Ic use ssl
+is specified, the data will be sent and/or received inside a SSL tunnel.
 .It Ic check tcp
 Use a simple TCP connect to check that hosts are up.
+If
+.Ic use ssl
+is specified, a complete SSL handshake will also be performed.
+.It Ic check ssl
+This has the same effect than above but also implies 
+.Ic use ssl .
 .It Ic disable
 Start the table disabled \(en no hosts will be checked in this table.
 The table can be later enabled through
@@ -166,6 +187,8 @@ Main and backup tables need to have the same real port
 Set the timeout in milliseconds for each host that is checked using
 TCP as the transport.
 This will override the global timeout, which is 200 milliseconds by default.
+.It Ic use ssl
+When the table uses a TCP check, wrap it into SSL.
 .El
 .Sh SERVICES
 Services represent a
blob - b76c4e68e9bfdb9da0dedc79b8b3e8c88ce860d4
blob + f375d5724c677e9463db0e5178dc5e7aab48b1d8
--- relayd.h
+++ relayd.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relayd.h,v 1.16 2007/01/12 17:05:18 pyr Exp $	*/
+/*	$OpenBSD: relayd.h,v 1.17 2007/01/29 14:23:31 pyr Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
@@ -143,6 +143,8 @@ struct ctl_tcp_event {
 	struct event	 	 ev;
 	int			(*validate_read)(struct ctl_tcp_event *);
 	int			(*validate_close)(struct ctl_tcp_event *);
+	SSL			*ssl;
+	char			 rbuf[SMALL_READ_BUF_SIZE];
 };
 
 struct address {
@@ -164,6 +166,7 @@ TAILQ_HEAD(addresslist, address);
 #define F_CHECK_DONE		0x0100
 #define F_ACTIVE_RULESET	0x0200
 #define F_CHECK_SENT		0x0400
+#define F_SSL			0x0800
 
 struct host {
 	u_int16_t		 flags;
@@ -197,6 +200,7 @@ struct table {
 	char			*sendbuf;
 	char			 exbuf[64];
 	char			 digest[41]; /* length of sha1 digest * 2 */
+	SSL_CTX			*ssl_ctx;
 	struct hostlist		 hosts;
 	TAILQ_ENTRY(table)	 entry;
 };
@@ -230,6 +234,7 @@ enum {
 
 struct hoststated {
 	u_int8_t		 opts;
+	u_int16_t		 flags;
 	struct pfdata		*pf;
 	int			 tablecount;
 	int			 servicecount;
@@ -356,6 +361,11 @@ int	check_http_digest(struct ctl_tcp_event *);
 /* check_send_expect.c */
 int	check_send_expect(struct ctl_tcp_event *);
 
+/* ssl.c */
+void	 ssl_init(struct hoststated *);
+void	 ssl_transaction(struct ctl_tcp_event *);
+SSL_CTX	*ssl_ctx_create(struct hoststated *);
+
 /* hoststated.c */
 struct host	*host_find(struct hoststated *, objid_t);
 struct table	*table_find(struct hoststated *, objid_t);
blob - /dev/null
blob + 9cedfb9b37d2efa0ab449355965dd29bd4ec353d (mode 644)
--- /dev/null
+++ ssl.c
@@ -0,0 +1,309 @@
+/*	$OpenBSD: ssl.c,v 1.1 2007/01/29 14:23:31 pyr Exp $	*/
+
+/*
+ * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@spootnik.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <limits.h>
+#include <event.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include "hoststated.h"
+
+void	ssl_read(int, short, void *);
+void	ssl_write(int, short, void *);
+void	ssl_connect(int, short, void *);
+void	ssl_cleanup(struct ctl_tcp_event *);
+void	ssl_error(const char *);
+
+void
+ssl_read(int s, short event, void *arg)
+{
+	struct ctl_tcp_event	*cte = arg;
+	int			 ret;
+	int			 ssl_err;
+	int			 retry_flag;
+	char			 rbuf[SMALL_READ_BUF_SIZE];
+
+	if (event == EV_TIMEOUT) {
+		cte->host->up = HOST_DOWN;
+		ssl_cleanup(cte);
+		hce_notify_done(cte->host, "ssl_read: timeout");
+		return;
+	}
+	log_debug("ssl_read: event occurred");
+
+	bzero(rbuf, sizeof(rbuf));
+	ssl_err = 0;
+	retry_flag = EV_READ;
+
+	ret = SSL_read(cte->ssl, rbuf, sizeof(rbuf));
+
+	if (ret <= 0) {
+		ssl_err = SSL_get_error(cte->ssl, ret);
+		switch (ssl_err) {
+		case SSL_ERROR_WANT_READ:
+			log_debug("ssl_read: want read");
+			retry_flag = EV_READ;
+			goto retry;
+		case SSL_ERROR_WANT_WRITE:
+			log_debug("ssl_read: want read");
+			retry_flag = EV_WRITE;
+			goto retry;
+		case SSL_ERROR_ZERO_RETURN: /* FALLTHROUGH */
+		case SSL_ERROR_SYSCALL:
+			if (ret == 0) {
+				cte->host->up = HOST_DOWN;
+				(void)cte->validate_close(cte);
+				ssl_cleanup(cte);
+				if (cte->host->up == HOST_UP)
+					hce_notify_done(cte->host,
+					    "ssl_read: check succeeded");
+				else
+					hce_notify_done(cte->host,
+					    "ssl_read: check failed");
+				return;
+			}
+			/* FALLTHROUGH */
+		default:
+			cte->host->up = HOST_DOWN;
+			ssl_error("cannot read");
+			ssl_cleanup(cte);
+			hce_notify_done(cte->host, "ssl_read: SSL error");
+			break;
+		}
+		return;
+	} 
+	buf_add(cte->buf, rbuf, ret);
+
+	if (cte->validate_read != NULL) {
+		if (cte->validate_read(cte) != 0)
+			goto retry;
+
+		ssl_cleanup(cte);
+		if (cte->host->up == HOST_UP)
+			hce_notify_done(cte->host, "ssl_read: check succeeded");
+		else
+			hce_notify_done(cte->host, "ssl_read: check failed");
+		return;
+	}
+
+retry:
+	log_debug("ssl_read: scheduling ssl_read on %s",
+	    (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE");
+	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_read,
+	    &cte->tv_start, &cte->table->timeout, cte);
+	return;
+}
+
+void
+ssl_write(int s, short event, void *arg)
+{
+	struct ctl_tcp_event	*cte = arg;
+	int			 len;
+	int		 	 ret;
+	int			 ssl_err;
+	int			 retry_flag;
+
+	if (event == EV_TIMEOUT) {
+		cte->host->up = HOST_DOWN;
+		ssl_cleanup(cte);
+		hce_notify_done(cte->host, "ssl_write: timeout");
+		return;
+	}
+
+	log_debug("ssl_write: event occurred");
+	len = strlen(cte->table->sendbuf);
+	retry_flag = EV_WRITE;
+
+	ret = SSL_write(cte->ssl, cte->table->sendbuf, len); 
+
+	if (ret <= 0) {
+		ssl_err = SSL_get_error(cte->ssl, ret);
+		switch (ssl_err) {
+		case SSL_ERROR_WANT_READ:
+			log_debug("ssl_write: want read");
+			retry_flag = EV_READ;
+			goto retry;
+		case SSL_ERROR_WANT_WRITE:
+			log_debug("ssl_write: want write");
+			retry_flag = EV_WRITE;
+			goto retry;
+		default:
+			cte->host->up = HOST_DOWN;
+			ssl_error("cannot write");
+			ssl_cleanup(cte);
+			hce_notify_done(cte->host, "ssl_write: SSL error");
+			return;
+		}
+	}
+	if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
+		fatalx("ssl_write: cannot create dynamic buffer");
+
+	log_debug("ssl_write: scheduling ssl_read on EV_READ");
+	event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, ssl_read,
+	    &cte->tv_start, &cte->table->timeout, cte);
+	return;
+retry:
+	log_debug("ssl_write: scheduling ssl_write on %s",
+	    (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE");
+	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_write,
+	    &cte->tv_start, &cte->table->timeout, cte);
+}
+
+void
+ssl_connect(int s, short event, void *arg)
+{
+	struct ctl_tcp_event	*cte = arg;
+	int		 	 ret;
+	int			 ssl_err;
+	int			 retry_flag;
+
+	if (event == EV_TIMEOUT) {
+		cte->host->up = HOST_DOWN;
+		hce_notify_done(cte->host, "ssl_connect: timeout");
+		return;
+	}
+
+	retry_flag = ssl_err = 0;
+
+	ret = SSL_connect(cte->ssl); 
+
+	if (ret <= 0) {
+		ssl_err = SSL_get_error(cte->ssl, ret);
+		switch (ssl_err) {
+		case SSL_ERROR_WANT_READ:
+			log_debug("ssl_connect: want read");
+			retry_flag = EV_READ;
+			goto retry;
+		case SSL_ERROR_WANT_WRITE:
+			log_debug("ssl_connect: want write");
+			retry_flag = EV_WRITE;
+			goto retry;
+		default:
+			cte->host->up = HOST_DOWN;
+			ssl_error("ssl_connect: cannot connect");
+			hce_notify_done(cte->host, "ssl_connect: SSL error");
+			ssl_cleanup(cte);
+			return;
+		}
+	}
+
+	if (cte->table->check == CHECK_TCP) {
+		cte->host->up = HOST_UP;
+		hce_notify_done(cte->host, "ssl_connect: connect successful");
+		ssl_cleanup(cte);
+		return;
+	}
+	log_debug("ssl_connect: connect succeeded");
+	if (cte->table->sendbuf != NULL) {
+		log_debug("ssl_connect: scheduling ssl_write on EV_WRITE");
+		event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_write,
+		    &cte->tv_start, &cte->table->timeout, cte);
+		return;
+	}
+
+	if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
+		fatalx("ssl_connect: cannot create dynamic buffer");
+	log_debug("ssl_connect: scheduling ssl_read on EV_READ");
+	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, ssl_read,
+	    &cte->tv_start, &cte->table->timeout, cte);
+	return;
+
+retry:
+	log_debug("ssl_write: scheduling ssl_write on %s",
+	    (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE");
+	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_connect,
+	    &cte->tv_start, &cte->table->timeout, cte);
+}
+
+void
+ssl_cleanup(struct ctl_tcp_event *cte)
+{
+	log_debug("ssl_cleanup: cleaning for %s", cte->host->name);
+	close(cte->s);
+	if (cte->ssl != NULL)
+		SSL_free(cte->ssl);
+	if (cte->buf != NULL)
+		buf_free(cte->buf);
+}
+
+void
+ssl_error(const char *msg)
+{
+	unsigned long	code;
+	char		errbuf[128];
+	
+
+	for (; (code = ERR_get_error()) != 0 ;) {
+		ERR_error_string_n(code, errbuf, sizeof(errbuf));
+		log_debug("ssl_error: %s: %s", msg, errbuf);
+	}
+}
+
+void
+ssl_init(struct hoststated *env)
+{
+	SSL_library_init();
+	SSL_load_error_strings();
+}
+
+void
+ssl_transaction(struct ctl_tcp_event *cte)
+{
+	cte->ssl = SSL_new(cte->table->ssl_ctx);
+	if (cte->ssl == NULL) {
+		ssl_error("ssl_transaction: cannot create object");
+		fatal("cannot create SSL object");
+	}
+
+	if (SSL_set_fd(cte->ssl, cte->s) == 0) {
+		cte->host->up = HOST_UNKNOWN;
+		ssl_error("ssl_transaction: cannot set fd"); 
+		ssl_cleanup(cte);
+		hce_notify_done(cte->host, "cannot set SSL fd");
+		return;
+	}
+	SSL_set_connect_state(cte->ssl);
+
+	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_connect,
+	    &cte->tv_start, &cte->table->timeout, cte);
+}
+
+SSL_CTX *
+ssl_ctx_create(struct hoststated *env)
+{
+	SSL_CTX	*ctx;
+
+	ctx = SSL_CTX_new(SSLv23_client_method());
+	if (ctx == NULL) {
+		ssl_error("ssl_ctx_create: cannot create context");
+		fatal("could not create SSL context");
+	}
+	return (ctx);
+}