/*
 * DECnet object for threaded SSL engine.  Run program runs as a permanent
 * process that declares a DECNet taskname.  Remote processes connect
 * to this task and perform an SSL session by multiplexing both the
 * encrypted and un-encrypted data streams over a single DECnet logical link.
 *
 * Command line arguments:
 *    argv[1]			DECnet object name to declare.
 *    argv[2]			Name of log file, default: sys$output.
 *
 * Logicals:
 *    SSL_SERVER_ACCESS		Multi-valued logical name that translates
 *				to the list clients permitted to use the
 *				SSL engine (format: node::user).
 *    SSL_SERVER_CERTIFICATE	Translates to PEM file that encodes the
 *				SSL certificate.
 *    SSL_SERVER_CA_FILE	Translates to PEM file holding list of
 *				trusted CA certificates.
 *    SSL_SERVER_LOG_LEVEL	Sets trace level for debugging (default=0).
 *    SSL_SERVER_THREAD_LIMIT	Max number of connections (default=32).
 *    SSL_SERVER_VERSION	Selects SSL protocol version to support:
 *				  2, 23, or 3 (default=2).
 *
 * Security considerations:
 *   This program need SYSNAM privilege in order to declare the DECnet
 *   task name.
 *
 * Author:	David Jones
 * Date:	26-SEP-1997
 * Revised:	26-FEB-1998	remove needless reference to <impdef.h>
 * Revised:	30-APR-1998	Update for V7 DECthreads compatibily.
 * Revised:	28-NOV-2001	Add ca_file argument to ssl_threads_init call.
 * Revised:	13-DEC-2001	Add penv argument ssl_threads_init call.
 */
#include "pthread_1c_np.h"
#include <stdio.h>
#include <stdlib.h>
#include <descrip.h>
#include "tutil.h"
#include "tserver_decnet.h"
#include "decnet_access.h"
#include "ssl_server.h"
int tlog_putlog(int,char *, ...);
int tlog_init(char *), tlog_initlog ( int, char * );

typedef struct { char *s; int l; } string;

static int ssl_engine
	( ts_client_ctx ctx, struct ncbdef *ncb, int ndx, int available );

static int logger_level;
int ssl_threads_init(), ssl_do_thread();
/******************************************************************************/
/* Main entry point.
 */
int main ( int argc, char **argv )
{
    int client_limit, i, status, *final_status, pthread_create();
    char *object, *log_fname, *arg, *certificate, *ca_file, version[64];
    pthread_attr_t attr;
    pthread_t listener, writer, clock;
    /*
     * Get parameters, command line and logical names.
     */
    if ( argc < 2 ) {
	fprintf(stderr, "Usage: ssl_engine object-name [log-file]\n");
	object = (char *) 0;
	exit(1);
    }
    object = argv[1];
    if ( argc > 3 ) log_fname = argv[2]; else log_fname = "sys$output";

    arg = getenv ( "SSL_SERVER_THREAD_LIMIT" );
    if ( arg ) {
	client_limit = atoi ( arg );
    } else client_limit = 32;

    logger_level = 0;
    arg = getenv ( "SSL_SERVER_LOG_LEVEL" );
    status = tlog_init ( log_fname );
    if ( arg ) {
	logger_level = atoi ( arg );
        if ( logger_level ) tlog_initlog ( logger_level, log_fname );
    }

    status = ts_set_access ( "SSL_SERVER_ACCESS" );
    if ( (status&1) == 0 ) {
	fprintf(stderr,"Error setting access list\n");
	exit ( status );
    }

    arg = getenv ( "SSL_SERVER_VERSION" );
    if ( arg ) tu_strnzcpy ( version, arg, sizeof(version)-1 );
    else tu_strcpy ( version, "2" );

    tlog_putlog(0,
	"SSL server started at !%D, objectname: '!AZ', SSL version: !AZ!/", 
	0, object, version );
    /*
     * INitialize the SSL library.
     */
    certificate = getenv ( "SSL_SERVER_CERTIFICATE" );
    if ( !certificate ) certificate = "sys$disk:[]server.pem";

    ca_file = getenv ( "SSL_SERVE_CA_FILE" );

    status = ssl_threads_init ( certificate, version, ca_file, (char **) 0 );
    tlog_putlog(0,"Status of init: !SL, certificate: !AZ!/", status, certificate );
    /*
     * Create threads:
     *   Listener thread listens for client connects, creating new client
     *   threads.
     */
    INITIALIZE_THREAD_ATTR ( &attr );
    pthread_attr_setstacksize ( &attr, 200000 );

    status = ts_declare_decnet_object 
		( object, client_limit, &attr, &listener, ssl_engine );
    if ( (status&1) == 0 ) {
	tlog_putlog ( 0, "Error declaring DECnet object: !SL!/", status );
	fprintf(stderr, "Error declaring object %s: %d\n", object, status );
	exit(status);
    }
    /*
     * Wait for listener thread exit.
     */
    pthread_join ( listener, (void *)  &final_status );

    exit ( *final_status );
}
/***************************************************************************/
/* Start routine for MST created to handle SSL connection.
 */
int ssl_engine ( ts_client_ctx link, struct ncbdef *ncb,
	int ndx, int avail )
{
    int status, length, id;
    char prefix[32];
    char taskname[256], remote_node[256], remote_user[64], source[400];
    /*
     * Confirm creation.
     */
    ts_decnet_info ( taskname, remote_node, remote_user );

    if ( logger_level > 2 ) tlog_putlog ( 1, 
	"!SL: Connect to !AZ at !%D from !AZ@!AZ!/",
	ndx, taskname, 0, remote_user, remote_node );

    tu_strcpy ( source, remote_node );
    tu_strcpy ( &source[tu_strlen(source)], remote_user );
    tu_strcpy ( prefix, "Service%SSL/" );
    tu_strint ( ndx, &prefix[12] );
    length = tu_strlen ( &prefix[12] ) + 12;
    prefix[length++] = ' ';
    prefix[length++] = '-';
    prefix[length] = '\0';
    if ( logger_level > 0 ) tlog_putlog ( 1,
	"!AZ thread created at !%D, ndx: !SL, available: !SL!/",
	prefix, 0, ndx, avail );
    id = ndx;
    /*
     * call common routine.
     */
    status = ssl_do_thread ( link, 1, &id );

    if ( logger_level > 0 ) tlog_putlog ( 1, "!AZ completed at !%T!/", 
		prefix, 0 );
    return 1;
}
/********************************************************************************/
/* Make substitute definitions for routines in message_service.c referenced
 * by bess_s_mst();
 */
int mst_write ( void *dptr, char *buffer, int bufsize, int *written )
{
    int status;
    status = ts_decnet_write ( (ts_client_ctx) dptr, buffer, bufsize );
    if ( (status&1) == 1 ) *written = bufsize;
    return status;
}
int mst_read ( void *dptr, char *buffer, int bufsize, int *read )
{
    return ts_decnet_read ( (ts_client_ctx) dptr, buffer, bufsize, read );
}
