/*
 * $Header: /san01/cvs/ashpool/csrc/modssl/Attic/ssl.c,v 1.7 2004/04/21 21:47:05 aleigh Exp $
 */

/*
 * Copyright (c) 1994, 2001 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Use, disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

#include <continuity.h>
#include <openssl/ssl.h>
#include "ssl.h"

#define KEYFILE "server.pem"
#define PASSWORD "password"

static SSL_CTX *ctx;
static const char *pass;
static int (*sslGhandler) (netTconn * conn, lstTset * o) = NULL;

struct sslScontext {
    SSL *ssl;
    BIO *bio;
};
typedef struct sslScontext sslTcontext;

static int password_cb(char *buf, int num, int rwflag, void *userdata)
{
    if (num < strlen(pass) + 1)
        return (0);

    strcpy(buf, pass);
    return (strlen(pass));
}

static SSL_CTX *initialize_ctx(const char *keyfile, const char *password)
{
    SSL_METHOD *meth;
    SSL_CTX *ctx;
    BIO *bio_err = 0;

    if (!bio_err) {
        /* Global system initialization */
        SSL_library_init();
        SSL_load_error_strings();

        /* An error write context */
        bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
    }

    /* Create our context */
    meth = SSLv23_server_method();
    ctx = SSL_CTX_new(meth);

    /* Load our keys and certificates */
    if (!(SSL_CTX_use_certificate_chain_file(ctx, keyfile))) {
        logFmsg(2, "Can't read certificate file.");
        exit(1);
    }
    pass = password;
    SSL_CTX_set_default_passwd_cb(ctx, password_cb);
    if (!(SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))) {
        logFmsg(2, "Can't read key file.");
        exit(1);
    }
#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
    SSL_CTX_set_verify_depth(ctx, 1);
#endif

    return ctx;
}

int sslFinit(void *p, lstTset * o)
{
    const char *password, *keyfile, *handler;

    logFmsg(0, "mod/ssl: Secure Sockets Layer (SSL) Engine");
    logFmsg(0, "Copyright (c) 2004, Alex Leigh.");

    password = lstFset_get(o, "cert_password");
    keyfile = lstFset_get(o, "cert_file");
    handler = lstFset_get(o, "handler");

    if (!keyfile) {
        logFmsg(2, "cert_file not specified!");
        exit(1);
    }

    if (!password) {
        logFmsg(2, "cert_password not specified!");
        exit(1);
    }

    if (!handler) {
        logFmsg(2, "handler not specified!");
        exit(1);
    }

    sslGhandler = dlFget_addr(handler);
    if (sslGhandler == NULL) {
        logFmsg(2, "Could not map handler: %s", handler);
        exit(1);
    }

    /* Build our SSL context */
    ctx = initialize_ctx(keyfile, password);
}

ssize_t sslFio_write(netTconn * conn, const void *buf, size_t len)
{
    sslTcontext *c = (sslTcontext *) conn->data;
    ssize_t ret;

    ret = SSL_write(c->ssl, buf, len);
    return ret;
}

ssize_t sslFio_read(netTconn * conn, void *buf, size_t len)
{
    sslTcontext *c = (sslTcontext *) conn->data;
    ssize_t ret;

    ret = SSL_read(c->ssl, buf, len);
    return ret;
}

int sslFio_close(netTconn * conn)
{
    sslTcontext *c = (sslTcontext *) conn->data;
    int r;

    r = SSL_shutdown(c->ssl);
    if (!r) {
        shutdown(conn->sd, 1);
        r = SSL_shutdown(c->ssl);
    }
    return close(conn->sd);
}

ssize_t sslFio_writev(struct netSconn * conn, const struct iovec * iov,
                      int iovcnt)
{
    int i, r;
    ssize_t written = 0;
    sslTcontext *c = (sslTcontext *) conn->data;

    for (i = 0; i < iovcnt; i++) {
        r = SSL_write(c->ssl, iov[i].iov_base, iov[i].iov_len);
        if (r < 1) {
            return written;
        }
        written += r;
    }
}

void sslFprivate_free(sslTcontext * c)
{
    free(c);
}

int sslFhandler(void *p, const lstTset * arg)
{
    netTconn *conn = (netTconn *) p;
    sslTcontext *private = (sslTcontext *) malloc(sizeof(sslTcontext));
    SSL *ssl;
    BIO *sbio;
    int r;

    sbio = BIO_new_socket(conn->sd, BIO_NOCLOSE);
    ssl = SSL_new(ctx);
    SSL_set_bio(ssl, sbio, sbio);

    SSL_set_accept_state(ssl);

    if (SSL_accept(ssl) < 1) {
        conn->io_close(conn);
        return STATUS_ERROR;
    }

    private->ssl = ssl;
    private->bio = sbio;

    conn->io_close = sslFio_close;
    conn->io_write = sslFio_write;
    conn->io_read = sslFio_read;
    conn->data = private;
    conn->data_free = sslFprivate_free;

    return (sslGhandler) (p, arg);
}
