/* ==========================================================================
 * libevnet/src/tls.c - OpenSSL interface for libevnet.
 * --------------------------------------------------------------------------
 * Copyright (c) 2003  William Ahern
 * Copyright (c) 2004  Barracuda Networks, Inc.
 * Copyright (c) 2006  William Ahern
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ==========================================================================
 */
#ifdef USE_OPENSSL
#include <stdio.h>		/* snprintf(3) */

#include <errno.h>		/* EINVAL EAGAIN EPIPE */
#include <string.h>		/* memset(3), strrchr(3) strerror(3) strcmp(3) */

#include <assert.h>		/* assert(3) */

#include <windows.h>		/* GetLastError SetLastError */

#include <sys/param.h>		/* MAX */

#include <sys/queue.h>		/* SLIST */

#include <sys/time.h>		/* struct timeval timersub gettimeofday(2) */

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <event.h>

#include <arena/proto.h>

#include "tls.h"


#ifndef MARK
#define MARK	fprintf(stderr, "@%s:%d\n", __FILE__, __LINE__);
#endif

#define OPENSSL_WARN do {						\
	unsigned long e;						\
	while ((e = ERR_get_error())) {					\
		warnx("openssl: %s", ERR_error_string(e, 0));		\
	}								\
} while(0)


const char *tls_errlist[] = {
	[TLS_ESUCCESS]		= "Success",
	[TLS_ESYSTEM]		= "System error",
	[TLS_EOPENSSL]		= "OpenSSL error",
	[TLS_ETIMEDOUT]		= "Operation timed out",
	[TLS_ECANCELLED]	= "Operation cancelled",
}; /* tls_errlist */

const int tls_nerr	= sizeof tls_errlist / sizeof *tls_errlist;


const struct tls_options tls_options_initializer = {
	.versions	= 0,
	.require_trust	= 0,
	.is_trusted	= 0,
}; /* tls_options_initializer */


const struct tls_options tls_defaults = {
	.versions	= TLS_VERSION_1,
	.require_trust	= 0,
	.is_trusted	= 0,
}; /* tls_defaults */


struct stack_note {
	struct tls **tp;

	SLIST_ENTRY(stack_note) sle;
}; /* struct stack_note */


static const struct tls_identity {
	struct tls_options opts;

	const struct arena_prototype *ap;

	SSL_CTX *context;
} tls_identity_initializer;


static const struct tls {
	int fd;

	struct tls_options opts;

	struct event_base *evbase;
	const struct arena_prototype *ap;

	struct event event;
	short event_pending;

	struct timeval event_deadline;
	int deadline_pending;

	struct {
		tls_read_cb cb;
		void *arg;
	} read;

	struct {
		tls_write_cb cb;
		void *arg;
	} write;

	struct {
		tls_accept_cb cb;
		void *arg;
	} accept;

	/*
	 * Not currently used. tls_accept() is overloaded for this purpose.
	 */
	struct {
		tls_connect_cb cb;
		void *arg;
	} connect;

	SSL *socket;

	enum tls_state state;

	struct tls_info info;

	enum tls_errno tls_errno;
	int sys_errno;

	SLIST_HEAD(, stack_note) stack;
} tls_initializer = {
	.fd	= -1,
	.info	= { .cipher = { .name = "UNKNOWN" }, .version = { .name = "UNKNOWN" } },
};


enum tls_errno tls_reset(void) {
	return TLS_ESUCCESS;
} /* tls_reset() */


enum tls_errno tls_init(void) {
	SSL_load_error_strings();

	(void)SSL_library_init();

	return TLS_ESUCCESS;
} /* tls_init() */


static void tls_options_copy(struct tls_options *dst, const struct tls_options *src) {
	*dst	= tls_defaults;

	if (src->versions != tls_options_initializer.versions)
		dst->versions	= src->versions;

	if (src->require_trust != tls_options_initializer.require_trust)
		dst->require_trust	= src->require_trust;

	if (src->is_trusted != tls_options_initializer.is_trusted)
		dst->is_trusted	= src->is_trusted;

	return /* void */;
} /* tls_options_copy() */


struct tls_identity *tls_identity_open(const struct tls_options *opts, const struct arena_prototype *ap, enum tls_errno *ep) {
	struct tls_identity *c		= 0;
	SSL_METHOD *openssl_method	= 0;
	enum tls_errno tls_errno	= TLS_ESYSTEM;
	int sys_errno;

	if (!ap)
		ap	= ARENA_STDLIB;

	if (!(c = ap->malloc(ap, sizeof *c, 0)))
		goto failed;

	*c	= tls_identity_initializer;

	tls_options_copy(&c->opts, (opts)? opts : &tls_defaults);

	c->ap	= ap;

	/*
	 * TODO: Investigate whether using the SSLv3_method() allows TLSv1
	 * connections.
	 */
	switch (c->opts.versions) {
	case SSL_VERSION_2:
		openssl_method	= SSLv2_method();

		break;
	case SSL_VERSION_3:
		openssl_method	= SSLv3_method();

		break;
	case SSL_VERSION_2|SSL_VERSION_3:
		openssl_method	= SSLv23_method();

		break;
	case TLS_VERSION_1:
		openssl_method	= TLSv1_method();

		break;
	case TLS_VERSION_1|SSL_VERSION_3:
		openssl_method	= SSLv3_method();

		break;
	case TLS_VERSION_1|SSL_VERSION_2|SSL_VERSION_3:
		openssl_method	= SSLv23_method();

		break;
	default:
		SetLastError(EINVAL);

		goto failed;
	}

	if (!openssl_method) {
		tls_errno	= TLS_EOPENSSL;

		goto failed;
	}

	if (!(c->context = SSL_CTX_new(openssl_method))) {
		tls_errno	= TLS_EOPENSSL;

		goto failed;
	}

	return c;
failed:
	sys_errno	= GetLastError();

	if (ep)
		*ep	= tls_errno;

	if (c) {
		if (c->context)
			SSL_CTX_free(c->context);

		ap->free(ap, memset(c, 0, sizeof *c));
	}

	SetLastError(sys_errno);

	return 0;
} /* tls_identity_open() */


void tls_identity_close(struct tls_identity *c) {
	const struct arena_prototype *ap;

	if (c == 0)
		return /* void */;

	ap	= c->ap;

	if (c->context)
		SSL_CTX_free(c->context);

	ap->free(ap, memset(c, 0, sizeof *c));

	return /* void */;
} /* tls_identity_close() */


enum tls_errno tls_identity_certify(struct tls_identity *c, const char *cert_path, const char *key_path) {
	if (!cert_path || !key_path)
		return SetLastError(EINVAL), TLS_ESYSTEM;

	if (1 != SSL_CTX_use_certificate_file(c->context, cert_path, SSL_FILETYPE_PEM)
	&&  1 != SSL_CTX_use_certificate_file(c->context, cert_path, SSL_FILETYPE_ASN1))
		return TLS_EOPENSSL;

	if (1 != SSL_CTX_use_PrivateKey_file(c->context, key_path, SSL_FILETYPE_PEM)
	&&  1 != SSL_CTX_use_PrivateKey_file(c->context, key_path, SSL_FILETYPE_ASN1))
		return TLS_EOPENSSL;

	return 0;
} /* tls_identity_certify() */


static int tls_identity_trust_path_syntax_isdir(const char *path) {
	const char *c;

	if (!(c = strrchr(path, '/')))
		return 0;

	return (*(c + 1) == '\0' || (*(c + 1) == '.' && *(c + 2) == '\0'));
} /* tls_identity_trust_path_syntax_isdir() */


enum tls_errno tls_identity_trust(struct tls_identity *c, const char *path) {
	const char *file_path	= 0;
	const char *dir_path	= 0;

	if (tls_identity_trust_path_syntax_isdir(path))
		dir_path	= path;
	else
		file_path	= path;

	if (1 != SSL_CTX_load_verify_locations(c->context, file_path, dir_path))
		return TLS_EOPENSSL;

	return 0;		
} /* tls_identity_trust */


SSL_CTX *tls_identity_peek(struct tls_identity *c) {
	return c->context;
} /* tls_identity_peek() */


struct tls *tls_open(int fd, struct tls_identity *c, struct event_base *evbase, const struct arena_prototype *ap, enum tls_errno *ep) {
	struct tls *t			= 0;
	enum tls_errno tls_errno	= TLS_ESYSTEM;
	int sys_errno;

	if (!ap)
		ap	= ARENA_STDLIB;

	if (!(t = ap->malloc(ap, sizeof *t, 0)))
		goto failed;

	*t	= tls_initializer;

	tls_options_copy(&t->opts, &c->opts);

	t->fd		= fd;
	t->evbase	= evbase;
	t->ap		= ap;

	SLIST_INIT(&t->stack);

	if (!(t->socket = SSL_new(c->context))
	||  1 != SSL_set_fd(t->socket, fd)) {
		tls_errno	= TLS_EOPENSSL;

		goto failed;
	}

	return t;
failed:
	sys_errno	= GetLastError();

	if (ep)
		*ep	= tls_errno;

	if (t) {
		if (t->socket)
			SSL_free(t->socket);

		ap->free(ap, memset(t, 0, sizeof *t));
	}

	SetLastError(sys_errno);

	return 0;
} /* tls_open() */


void tls_close(struct tls *t) {
	struct stack_note *np;

	if (t == 0)
		return /* void */;

	if (tls_pending(t)) {
		struct stack_note n	= { .tp = &t };

		SLIST_INSERT_HEAD(&t->stack, &n, sle);

		tls_cancel(t, 0);

		if (t == 0)
			return /* void */;

		assert(SLIST_FIRST(&t->stack) == &n);

		SLIST_REMOVE_HEAD(&t->stack, sle);
	}

	if (t->socket)
		SSL_free(t->socket), t->socket = 0;

	SLIST_FOREACH(np, &t->stack, sle)
		*np->tp	= 0;

	SLIST_INIT(&t->stack);

	t->ap->free(t->ap, t);

	return /* void */;	
} /* tls_close() */


int tls_pending(struct tls *t) {
	return t->event_pending;
} /* tls_pending() */


static void tls_event_del(struct tls *t) {
	if (t->event_pending) {
		(void)event_del(&t->event);

		t->event_pending	= 0;
	}

	return /* void */;
} /* tls_event_del() */


static enum tls_errno tls_event_add(struct tls *t, void (*cb)(int, short, void *)) {
	short events	= ((t->state & TLS_S_NEED_READ)? EV_READ : 0)
			| ((t->state & TLS_S_NEED_WRITE)? EV_WRITE : 0);
	struct timeval now, tvbuf, *timeout = 0;

	assert((t->state & (TLS_S_NEED_READ | TLS_S_NEED_WRITE)));

	if (t->deadline_pending) {
		if (0 != gettimeofday(&now, 0)) {
			t->sys_errno	= GetLastError();
			t->tls_errno	= TLS_ESYSTEM;

			goto failed;
		}

		if (timercmp(&t->event_deadline, &now, <=)) {
			t->tls_errno	= TLS_ETIMEDOUT;

			goto failed;
		}

		timersub(&t->event_deadline, &now, &tvbuf);

		timeout	= &tvbuf;
	}

	if (t->event_pending == events && !timeout)
		return 0;

	tls_event_del(t);

	event_set(&t->event, t->fd, events | EV_PERSIST, cb, t);

	if (t->evbase)
		event_base_set(t->evbase, &t->event);

	if (0 != event_add(&t->event, timeout)) {
		t->sys_errno	= GetLastError();
		t->tls_errno	= TLS_ESYSTEM;	

		goto failed;
	}

	t->event_pending	= events;

	return 0;
failed:
	tls_event_del(t);

	SetLastError(t->sys_errno);

	return t->tls_errno;
} /* tls_event_add() */


void tls_cancel(struct tls *t, int notify) {
	struct stack_note n	= { .tp = &t };

	SLIST_INSERT_HEAD(&t->stack, &n, sle);

	tls_event_del(t);

	if (notify) {
		if (t->read.cb)
			t->read.cb(t, 0, 0, TLS_ECANCELLED, t->read.arg);

		if (t == 0)
			return /* void */;

		if (t->write.cb)
			t->write.cb(t, 0, 0, TLS_ECANCELLED, t->write.arg);

		if (t == 0)
			return /* void */;

		if (t->accept.cb)
			t->accept.cb(t, TLS_ECANCELLED, t->accept.arg);

		if (t == 0)
			return /* void */;

		if (t->connect.cb)
			t->connect.cb(t, TLS_ECANCELLED, t->connect.arg);

		if (t == 0)
			return /* void */;
	}

	assert(SLIST_FIRST(&t->stack) == &n);

	SLIST_REMOVE_HEAD(&t->stack, sle);

	return /* void */;
} /* tls_cancel() */


SSL *tls_peek(struct tls *t) {
	return t->socket;
} /* tls_peek() */


static ssize_t tls_read_try(struct tls *t, void *buf, size_t bufsiz) {
	ssize_t nbytes;

	t->state	&= ~(TLS_S_NEED_READ | TLS_S_NEED_WRITE);

	ERR_clear_error();

	nbytes	= SSL_read(t->socket, buf, bufsiz);

	if (nbytes >= 0)
		return nbytes;

	switch (SSL_get_error(t->socket, nbytes)) {
	case SSL_ERROR_WANT_READ:
		t->state	|= TLS_S_NEED_READ;
		SetLastError(EAGAIN);

		break;
	case SSL_ERROR_WANT_WRITE:
		t->state	|= TLS_S_NEED_WRITE;
		SetLastError(EAGAIN);

		break;
	case SSL_ERROR_SSL:
		SetLastError(EIO);

		/* FALL THROUGH */
	case SSL_ERROR_SYSCALL:
		/* FALL THROUGH */
	default:
		if (!GetLastError() || GetLastError() == EAGAIN)
			SetLastError(EIO);

		t->state	= TLS_S_DISCONNECTED;
	}

	t->tls_errno	= TLS_ESYSTEM;
	t->sys_errno	= GetLastError();

	return -1;
} /* tls_read_try() */


ssize_t tls_read(struct tls *t, void *buf, size_t bufsiz, tls_read_cb cb, void *arg, struct timeval *timeout) {
	assert(cb == 0);	/* NOT SUPPORTED */

	return tls_read_try(t, buf, bufsiz);
} /* tls_read() */


static ssize_t tls_write_try(struct tls *t, const void *buf, size_t bufsiz) {
	ssize_t nbytes;

	t->state	&= ~(TLS_S_NEED_READ | TLS_S_NEED_WRITE);

	ERR_clear_error();

	nbytes	= SSL_write(t->socket, buf, bufsiz);

	if (nbytes > 0)
		return nbytes;

	switch(SSL_get_error(t->socket, nbytes)) {
	case SSL_ERROR_ZERO_RETURN:
		t->state	= TLS_S_DISCONNECTED;
		SetLastError(EPIPE);

		break;
	case SSL_ERROR_WANT_READ:
		t->state	|= TLS_S_NEED_READ;
		SetLastError(EAGAIN);

		break;
	case SSL_ERROR_WANT_WRITE:
		t->state	|= TLS_S_NEED_WRITE;
		SetLastError(EAGAIN);

		break;
	case SSL_ERROR_SSL:
		SetLastError(EIO);

		/* FALL THROUGH */
	case SSL_ERROR_SYSCALL:

		/* FALL THROUGH */
	default:
		if (!GetLastError() || GetLastError() == EAGAIN)
			SetLastError(EIO);

		t->state	= TLS_S_DISCONNECTED;
	}

	t->tls_errno	= TLS_ESYSTEM;
	t->sys_errno	= GetLastError();

	return -1;
} /* tls_write_try() */


ssize_t tls_write(struct tls *t, const void *buf, size_t bufsiz, tls_read_cb cb, void *arg, struct timeval *timeout) {
	assert(cb == 0);	/* NOT SUPPORTED */

	return tls_write_try(t, buf, bufsiz);
} /* tls_write() */


static enum tls_errno tls_accept_try(struct tls *t) {
	X509 *peer;
	int retval;
	const char *cipher, *version;

	t->state	&= ~(TLS_S_NEED_READ | TLS_S_NEED_WRITE);

	ERR_clear_error();

	retval	= SSL_do_handshake(t->socket);

	switch (retval) {
	case 1:
		t->state	|= TLS_S_CONNECTED;

		if ((peer = SSL_get_peer_certificate(t->socket)))
			X509_free(peer), peer = 0;

		if (X509_V_OK == SSL_get_verify_result(t->socket)
		&&  (peer = SSL_get_peer_certificate(t->socket))) {
			X509_free(peer), peer = 0;

			t->state	|= TLS_S_VERIFIED;
		}

		if ((cipher = SSL_get_cipher(t->socket)))
			(void)snprintf(t->info.cipher.name, sizeof t->info.cipher.name, "%s", cipher);

		t->info.cipher.bits	= MAX(0, SSL_get_cipher_bits(t->socket, 0));

		if ((version = SSL_get_version(t->socket))) {
			(void)snprintf(t->info.version.name, sizeof t->info.version.name, "%s", version);

			if (0 == strcmp(version, "TLSv1"))
				t->info.version.id	= TLS_VERSION_1;
			else if (0 == strcmp(version, "SSLv3"))
				t->info.version.id	= SSL_VERSION_3;
			else if (0 == strcmp(version, "SSLv2"))
				t->info.version.id	= SSL_VERSION_2;

			/* TODO: Enforce specified versions. */
		}

		return 0;
	case 0:
		return t->tls_errno = TLS_EOPENSSL;
	}

	switch (SSL_get_error(t->socket, retval)) {
	case SSL_ERROR_WANT_READ:
		t->state	|= TLS_S_NEED_READ;
		SetLastError(EAGAIN);

		break;
	case SSL_ERROR_WANT_WRITE:
		t->state	|= TLS_S_NEED_WRITE;
		SetLastError(EAGAIN);

		break;
	case SSL_ERROR_SSL:
		SetLastError(EIO);

		/* FALL THROUGH */
	case SSL_ERROR_SYSCALL:

		/* FALL THROUGH */
	default:
		if (!GetLastError() || GetLastError() == EAGAIN)
			SetLastError(EIO);

		t->state	= TLS_S_DISCONNECTED;
	}

	t->tls_errno	= TLS_ESYSTEM;
	t->sys_errno	= GetLastError();

	return t->tls_errno;
} /* tls_accept_try() */


static void tls_accept_catch(int fd, short events, void *arg) {
	struct tls *t	= arg;
	tls_accept_cb cb;

	if (0 == tls_accept_try(t)) {
		t->tls_errno	= TLS_ESUCCESS;

		goto finish;
	}

	if (t->tls_errno == TLS_ESYSTEM && t->sys_errno == EAGAIN) {
		assert(t->state & (TLS_S_NEED_READ | TLS_S_NEED_WRITE));

		if (0 != tls_event_add(t, &tls_accept_catch))
			goto finish;
	} else
		goto finish;

	return /* void */;
finish:
	tls_event_del(t);

	cb	= t->accept.cb;
	arg	= t->accept.arg;

	t->accept.cb	= 0;
	t->accept.arg	= 0;		

	cb(t, t->tls_errno, arg);

	return /* void */;			
} /* tls_accept_catch() */


enum tls_errno tls_accept(struct tls *t, tls_accept_cb cb, void *arg, struct timeval *timeout) {
	struct timeval now;

	assert(!(t->state & TLS_S_MODE_CONNECT));

#if 0
	char *cipher;
	int i	= 0;
	while ((cipher = SSL_get_cipher_list(t->socket, i++)))
		fprintf(stderr, "cipher: %s\n", cipher);
#endif

	if (!(t->state & TLS_S_MODE_ACCEPT)) {
		SSL_set_accept_state(t->socket);

		t->state	|= TLS_S_MODE_ACCEPT;
	}

	if (cb) {
		if (timeout) {
			if (0 != gettimeofday(&now, 0)) {
				t->tls_errno	= TLS_ESYSTEM;
				t->sys_errno	= GetLastError();

				return t->tls_errno;
			}

			timeradd(&now, timeout, &t->event_deadline);
			t->deadline_pending	= 1;
		} else
			t->deadline_pending	= 0;

		t->accept.cb	= cb;
		t->accept.arg	= arg;

		/* Fake callback from libevent. */
		tls_accept_catch(t->fd, 0, t);

		return 0;
	} else 
		return tls_accept_try(t);
} /* tls_accept() */


enum tls_errno tls_connect(struct tls *t, tls_connect_cb cb, void *arg, struct timeval *timeout) {
	struct timeval now;

	assert(!(t->state & TLS_S_MODE_ACCEPT));

	if (!(t->state & TLS_S_MODE_CONNECT)) {
		SSL_set_connect_state(t->socket);

		t->state	|= TLS_S_MODE_CONNECT;
	}

	/*
	 * NOTE: We overload tls_accept(). The accept-mode code does all the
	 * heavy lifting; we just set the connect mode up-front.
	 */

	if (cb) {
		if (timeout) {
			if (0 != gettimeofday(&now, 0)) {
				t->tls_errno	= TLS_ESYSTEM;
				t->sys_errno	= GetLastError();

				return t->tls_errno;
			}

			timeradd(&now, timeout, &t->event_deadline);
			t->deadline_pending	= 1;
		} else
			t->deadline_pending	= 0;

		t->accept.cb	= cb;
		t->accept.arg	= arg;

		tls_accept_catch(t->fd, 0, t);

		return 0;
	} else 
		return tls_accept_try(t);
} /* tls_connect() */


int tls_state(struct tls *t) {
	return t->state;
} /* tls_state() */


struct tls_info *tls_info(struct tls *t) {
	return &t->info;
} /* tls_info() */


enum tls_errno tls_errno(struct tls *t) {
	return t->tls_errno;
} /* tls_errno() */


const char *tls_strerror(struct tls *t) {
	assert(t->tls_errno >= 0 && t->tls_errno < tls_nerr);

	switch (t->tls_errno) {
	case TLS_ESYSTEM:
		return strerror(t->sys_errno);
	default:
		return tls_errlist[t->tls_errno];
	}

	/* NOT REACHED */
} /* tls_strerror() */


#else /* USE_OPENSSL */

#include <stddef.h>	/* size_t */
#include <sys/time.h>	/* struct timeval */

#include "tls.h"


const char *tls_errlist[1] = { 0 };

const int tls_nerr	= 0;

const struct tls_options tls_options_initializer;

const struct tls_options tls_defaults;


struct tls *tls_open(int fd, struct tls_identity *c, struct event_base *evbase, const struct arena_prototype *ap, enum tls_errno *ep) {
	return 0;
} /* tls_open() */


void tls_close(struct tls *t) {
	return /* void */;
} /* tls_close() */


enum tls_errno tls_accept(struct tls *t, tls_accept_cb cb, void *arg, struct timeval *timeout) {
	return TLS_ESYSTEM;
} /* tls_accept() */


enum tls_errno tls_connect(struct tls *t, tls_connect_cb cb, void *arg, struct timeval *timeout) {
	return TLS_ESYSTEM;
} /* tls_connect() */


enum tls_errno tls_reset(void) {
	return TLS_ESYSTEM;
} /* tls_reset() */


enum tls_errno tls_init(void) {
	return TLS_ESYSTEM;
} /* tls_init() */


struct tls_identity *tls_identity_open(const struct tls_options *opts, const struct arena_prototype *ap, enum tls_errno *t_errno) {
	*t_errno	= TLS_ESYSTEM;

	return 0;
} /* tls_identity_open() */


void tls_identity_close(struct tls_identity *t) {
	return /* void */;
} /* tls_identity_close() */


enum tls_errno tls_identity_certify(struct tls_identity *t, const char *a, const char *b) {
	return TLS_ESYSTEM;
} /* tls_identity_certify() */


enum tls_errno tls_identity_trust(struct tls_identity *t, const char *a) {
	return TLS_ESYSTEM;
} /* tls_identity_trust() */


void *tls_identity_peek(struct tls_identity *t) {
	return 0;
} /* tls_identity_peek() */


int tls_pending(struct tls *t) {
	return 0;
} /* tls_pending() */


void tls_cancel(struct tls *t, int a) {
	return /* void */;
} /* tls_cancel() */


SSL *tls_peek(struct tls *t) {
	return 0;
} /* tls_peek() */


#ifdef WIN32
typedef long ssize_t;
#endif

ssize_t tls_read(struct tls *t, void *a, size_t b, tls_read_cb c, void *d, struct timeval *e) {
	return -1;
} /* tls_read() */


ssize_t tls_write(struct tls *t, const void *a, size_t b, tls_read_cb c, void *d, struct timeval *e) {
	return -1;
} /* tls_write() */


int tls_state(struct tls *t) {
	return 0;
} /* tls_state() */


struct tls_info *tls_info(struct tls *t) {
	return 0;
} /* tls_info() */


enum tls_errno tls_errno(struct tls *t) {
	return TLS_ESYSTEM;
} /* tls_errno() */


const char *tls_strerror(struct tls *t) {
	return "Unknown";
} /* tls_strerror() */


#endif /* USE_OPENSSL */



syntax highlighted by Code2HTML, v. 0.9.1