#define EAY		/* undefine to build NULL version */
/*
 * SSL server high-level interface.  Applications using this interface
 * supply callback routines that this layer will call to perform raw
 * input and output to the client connection.  The get/put callbacks
 * match the semantics of tserver_tcp's interface.
 *
 * This module (ssl_server_mst.c) exports the callbacks to an internally
 * created MST (message-based service thread), multiplexing 2 bi-directional 
 * data streams over a single MST link.  The service thread, which has a
 * stack size we can govern, calls the SSL engine in a multi-threaded fashion.
 *
 * Logical names:
 *    WWWSSL_MST_THREAD_LIMIT	Maximum threads allowed, default: 10.
 *    WWWSSL_MST_STACK_SIZE	Stack size for server threads, default: 60000.
 *    WWWSSL_MST_QUEUE_FLAG	Indicates whether to wait for available thread.
 *    WWWSSL_MST_CERTIFICATE	PEM file with server's X509 certificate.
 *    WWWSSL_MST_LOGFILE	Log file for errors from MST, default sys$output.
 *    WWWSSL_MST_VERSTION	SSL version to load 2, 23, or 3 (default=2)
 *    WWWSSL_MST_CA_FILE	Cert. authority certificates.
 *
 * Author:	David Jones
 * Date:	24-AUG-1996
 * Revised:	26-SEP-1997	Support version argument to ssl_threads_init
 * Revised:	22-FEB-1998	Enable privileges during MST initialization
 *				so that certificate file may be protected.
 * Revised:	29-OCT-1998	Fix but in 22-FEB-1998 revision.  Don't
 *				disable sysprv.
 * Revised:	28-NOV-2001	Support parameter array in tssl_initialize().
 * Revised:	9-DEC-2001	Add tssl_get_client_cert() function.
 */
#include "pthread_1c_np.h"	/* portability wrapper for <pthread.h> */
#include "tutil.h"		/* thread-safe string functions */
#include "message_service.h"	/* Service threads */

#include <stdio.h>
#include <stdlib.h>
#include <ssdef.h>
#include <prvdef.h>
int SYS$SETPRV();
/*
 * Basic message structure for communicating with DECnet task.
 *   Server sends 'A',G/P,length[,data], then reads and processes responsess 
 *   until a 'A','C' recieved.  Intermediate to the confirm, the remote
 *   may request operations on the 'R' channel (remote client) or 'G' channel
 *   (special global data).
 *   
 */
struct rpc_msg {		/* Should have member alignment inhibited */
   char channel;		/* 'A'-app data. 'R'-remote client 'G'-global */
   char function;		/* 'G'-get, 'P'-put, 'C'-confirm, 'X'-close */
   unsigned short int length;	/* Amount of data returned or max to return */
   char data[4092];		/* variable data */
};
#define RPC_HDR_SIZE (sizeof(struct rpc_msg) - 4092)
#define MAX_HEADROOM 36
/*
 * define primary context structure returned by init_context.
 */
struct ssl_control {
    struct ssl_control *next;
    int ssl_version;		/* sll protocol in use (2, 3, etc) */
    /*
     * Communication stream.
     */
    void *tcp_ctx;		/* Context for TCP transport layer */
    int (*get)();		/* Read from client */
    int (*put)();		/* Write to client */
    int (*error)();		/* Report error */
    int max_io;			/* Largest I/O to perform to network */
    /*
     * RPC parameters.
     */
    mst_link_t *link;			/* Communicaton link to MST thread */
    int out_pos;
    int msg_filled;		/* read data portion */
    struct rpc_msg msg;		/* Data read from channel. */
    struct rpc_msg outmsg;
};
typedef struct ssl_control *ssl_context;
#define SSL_CTX 1		/* Inhibits definition of ssl_context */
#include "ssl_server.h"		/* verify prototypes */
#ifdef EAY
int ssl_threads_init(), ssl_do_thread();
#endif

static ssl_context free_ctx;
static pthread_mutex_t context_db;   /* Mutex to guard access to global data */
static char ssl_service_name[256];
static int ssl_pool_id;
static int (*ssl_error) ( );
static int flush_outmsg ( ssl_context ctx );	/* forward reference */

int ssl_engine ( mst_link_t link, char *port, char *info, int ndx, int avail );
/*
 * Scan list of keyword=value pairs in penv for first one whose keyword
 * matches var and return value if found.  If not found, return
 * getenv ( "WWWSSL_MST_'var'" );
 */
static char *getpenv ( const char *var, char **penv )
{
    char *result, logical[256];
    int i, j, klen;
    /*
     * Scan the penv.
     */
    klen = tu_strlen ( var );
    for ( i = 0; penv[i]; i++ ) {
	if ( tu_strncmp ( var, penv[i], klen ) == 0 ) {
	    /* Possibile match, next character must be '=' */
	    if ( penv[i][klen] == '=' ) return &penv[i][klen+1];
	}
    }
    /*
     * Fallback to standard environment variable, construct name.
     */
    tu_strcpy ( logical, "WWWSSL_MST_" );
    tu_strnzcpy ( &logical[11], var, sizeof(logical)-12 );
    result = getenv ( logical );
    return result;
}
/***************************************************************************/
/* Function tssl_initalize() should be called once per image invocation.
 * It handles any global configuration needed by the ssl_server module.
 */
int http_log_level;
int tssl_initialize(int (*error) ( int, char *), char **penv )
{
    char *task_name, *envval, *certificate, *slash, version[64], errlog[256];
    char *ca_file;
    int status, tlog_init(), tlog_putlog(), tlog_initlog();
    int thread_limit, stack_size, q_flag, setprv_status;
    pthread_attr_t thread_attr;
    long priv[2], prev_priv[2];
    /*
     * Initialize ssl_context lookaside list and mutex that guards it.
     */
    free_ctx = (ssl_context) 0;
    INITIALIZE_MUTEX ( &context_db );
    ssl_error = error;
    /*
     * Get parameters for MST from environment variables.
     */
    envval = getpenv ( "THREAD_LIMIT", penv );
    thread_limit = envval ? atoi(envval) : 10;

    envval = getpenv ( "STACK_SIZE", penv );
    stack_size = envval ? atoi(envval) : 60000;

    envval = getpenv ( "QUEUE_FLAG", penv );
    q_flag = envval ? atoi(envval) : 1;

    envval = getpenv ( "SERVICE_NAME", penv );
    pthread_mutex_lock ( &context_db );
    tu_strnzcpy ( ssl_service_name, envval ? envval : "SSL",
		sizeof(ssl_service_name)-1 );
    pthread_mutex_unlock ( &context_db );

    envval = getpenv ( "VERSION", penv );
    if ( envval ) tu_strnzcpy ( version, envval, sizeof(version)-1 );
    else tu_strcpy ( version, "2" );

    certificate = getpenv ( "CERTIFICATE", penv );
    if ( !certificate ) certificate = "{WWWSSL_MST_CERTIFICATE undefined}";
    

    envval = getpenv ( "LOGFILE", penv );
    tu_strnzcpy ( errlog, envval ? envval : "sys$output", sizeof(errlog)-1 );

    ca_file = getpenv ( "CA_FILE", penv );
    /*
     * The MST module depends upon tlog_putlog(), so initialize tlogger module.
     * The service threads, call tlog_putlog() instead of ssl_error.
     */
    /* http_log_level = 0; */
    slash = tu_strstr ( errlog, "/" );
    if ( slash ) {
	/* Number after slash is desired logger level */
	*slash++ = '\0';
	http_log_level = atoi ( slash );
    }
    status = tlog_init ( errlog );
    (*ssl_error) ( status, "Error log for SSL engine" );
    if ( http_log_level > 0 ) tlog_initlog ( http_log_level, errlog );
    tlog_putlog ( 0, 
	"SSL-MST service startup at !%D, !SL thread!%S, log level: !SL!/",
		0, thread_limit, http_log_level );
    /*
     * Create MST thread pool and service, assume we are part of a tserver_ssl
     * shareable image so the namespace for pools an threads is indpendant
     * of those defined for the main server.
     */
    INITIALIZE_THREAD_ATTR(&thread_attr);
    pthread_attr_setstacksize ( &thread_attr, stack_size );

    status = mst_create_service_pool 
		( thread_attr, thread_limit, q_flag, &ssl_pool_id );
    if ( (status&1) == 1 ) status = mst_register_service (
		ssl_service_name, ssl_engine, certificate, ssl_pool_id );
    if ( (status&1) == 0 ) {
	char errmsg[256];
	mst_format_error ( status, errmsg, sizeof(errmsg) );
	(*error) ( status, errmsg );
	(*error) ( 1, "SSL-MST initialization failure" );
	return status;
    }
    /*
     * Initialize the SSL engine (read server certifcate, etc).
     */
#ifdef EAY
    priv[0] = PRV$M_SYSPRV;
    priv[1] = 0;
    setprv_status = SYS$SETPRV ( 1, priv, 0, prev_priv );
    status = ssl_threads_init ( certificate, version, ca_file, penv );
    if ( (setprv_status&1) == 1 &&
	(prev_priv[0]&PRV$M_SYSPRV) == 0 ) SYS$SETPRV ( 0, priv, 0, 0 );

    tlog_putlog(0,
     "Loaded server certificate: '!AZ', SSL version: !AZ, status: !SL (!AZ)!/", 
	certificate, version, status, (status&1) ? "success" : "failure" );
#endif
    /*
     * Announce we are loaded.
     */
    (*error) ( 1, "SSL-MST initialized" );
    return SS$_NORMAL;
}

/***************************************************************************/
/* Initialize context for new client connect.  Connect to SSL object at
 * this point.
 */
int tssl_init_server_context ( ssl_context *ctx,	/* New context */
    int (*get) ( void *, char *, int, int * ),
    int (*put) ( void *, char *, int ),
    int (*error) ( int code, char *text ),
    void *io_ctx,		/* I/O context for get() and put() calls */
    int max_io_size )		/* Max I/O length per get or put call */
{
    mst_link_t new_link;
    int status;
    char mst_error[300];
    /*
     * Try to connect to SSL engine. Doing it now means we don't have to
     * free any allocated structures if it fails.
     */
    status = mst_connect ( ssl_service_name, "SSL", &new_link );
    if ( (status&1) == 0 ) {
	int length;
	tu_strcpy ( mst_error, "connect failure: " );
	length = tu_strlen ( mst_error );
	mst_format_error ( status, &mst_error[length], 
		sizeof(mst_error)-length );
	(*error) ( status, mst_error );
	return status;
    }
    /*
     * Allocate a structure, either removing from free_ctx lookaside list
     * or mallocing new.
     */
    pthread_mutex_lock ( &context_db );
    if ( free_ctx ) {
	*ctx = free_ctx;
	free_ctx = (*ctx)->next;
	pthread_mutex_unlock ( &context_db );
    } else {
	pthread_mutex_unlock ( &context_db );
	LOCK_C_RTL
	*ctx = (ssl_context) malloc ( sizeof(struct ssl_control) );
	UNLOCK_C_RTL
	if ( !*ctx ) {
	    (*error) ( SS$_INSFMEM, "Failure to allocate ssl_context" );
	    mst_close ( new_link );
	    return SS$_INSFMEM;
	}
    }
    (*ctx)->next = (ssl_context) 0;
    /*
     * Initialize TCP callback interface.
     */
    (*ctx)->ssl_version = 0;
    (*ctx)->tcp_ctx = io_ctx;
    (*ctx)->get = get;
    (*ctx)->put = put;
    (*ctx)->error = error;
    (*ctx)->max_io = max_io_size;
    /*
     * Initialize for RPC loop.
     */
    (*ctx)->link = new_link;
    (*ctx)->out_pos = 0;
    (*ctx)->outmsg.channel = 'A';
    (*ctx)->outmsg.function = 'P';

    return status;
}
/*****************************************************************************/
/* RCP dispatcher.  Read messages from remote task and perform requested
 * actions until connection close or application data operation.
 *
 */
static int confirm_app_data ( ssl_context ctx )
{
    int status, size, length, segment, i, written;

    for ( status = 1; (status&1); ) {
        status = mst_read ( ctx->link, (char *) &ctx->msg, 
			sizeof(ctx->msg), &ctx->msg_filled );
#ifdef DEBUG
	printf ( "/confirm_app/ read status: %d, chan: '%c', func: '%c', len: %d\n",
	status, ctx->msg.channel, ctx->msg.function, ctx->msg_filled );
#endif
	if ( ctx->msg_filled < 4 ) { status = 20; break; }

	/*
	 * response to data in header.
	 */
	if ( ctx->msg.channel == 'A' ) {
	    /*
	     * Only operations we should be getting on the A channel
	     * is a confirm (function=='C');
	     */
	    if ( ctx->msg.function == 'C' ) break;
	    else {
		/* Error */
		status = 20;
	    }
	} else if ( ctx->msg.channel == 'R' ) {
	    /*
	     * relay the request to the remote client.
	     */
	    switch ( ctx->msg.function ) {
		case 'G':		/* get */
		    /*
		     * Read data from remote IP connection.
		     */
		    ctx->msg.function = 'C';
		    size = sizeof(ctx->msg.data);
		    if ( size > ctx->max_io ) size = ctx->max_io;
		    status = (*ctx->get) ( ctx->tcp_ctx, ctx->msg.data, 
			size, &length );
#ifdef DEBUG
		     printf ( 
			"   - read from client connection result: %d %d\n", 
			status, length);
#endif
		    if ( (status&1) == 0 ) {
			ctx->msg.function = 'X';
			length = 0;
		    }
		    /*
		     * Send confirm message with the data we read.
		     */
		    ctx->msg.length = length;
		    status = mst_write ( ctx->link, (char *) &ctx->msg,
			RPC_HDR_SIZE + length, &written );
		    break;


		case 'P':		/* put */
		    /*
		     * Read data from decnet side and write to TCP side.
		     */
		    ctx->msg.function = 'C';
		    segment = ctx->msg_filled - RPC_HDR_SIZE;
		    if ( segment > 0 ) {
			/* Send data included with message */
			status = (*ctx->put) ( ctx->tcp_ctx, 
				ctx->msg.data,	segment );
			if ( (status&1) == 0 ) break;
			
		    }
		    /*
		     * send confirm.
		     */
		    ctx->msg.length = 0;
		    status = mst_write ( ctx->link, (char *) &ctx->msg,
			RPC_HDR_SIZE, &written );
		    break;
		case 'X':		/* Close remote (ignored) */
		    break;
		default:
		    break;
	    }
	} else if ( ctx->msg.channel == 'G' ) {
	    /*
	     * Special channel for allowing remote to make general
	     * queries of server.
	     * Only operations we should be getting on the G channel
	     * is a confirm (function=='C');
	     */
	    if ( ctx->msg.function == 'C' ) break;
	    else {
		/* Error */
		status = 20;
	    }
	} else {
	    /*  Invalid channel identifier. */
	    status = 20;
	}
    }
    if ( (status&1) == 0 ) {
	ctx->msg_filled = 4;
	ctx->msg.channel = 'A';
	ctx->msg.function = 'X';
        ctx->msg.length = status;
    }
    return status;
}
/*****************************************************************************/
/*  The tssl_put_app_data function encrypts the application data and sends
 *  it to the client.  The application data may be buffered prior to
 *  encryption.
 */
int tssl_put_app_data ( ssl_context ctx,  unsigned char *data, int length )
{
    int status, i, j;
    /*
     * Buffer output data.  MAX_HEADROOM prevents loss of efficiency that
     * would occur from the encrypted data being fragmented into multiple
     * puts to the 'R' channel.
     */
    j = ctx->out_pos;
    for ( i = 0; i < length; i++ ) {
	if ( j >= sizeof ( ctx->outmsg.data ) - MAX_HEADROOM ) {
	    ctx->out_pos = j;
	    status = flush_outmsg ( ctx );
	    if ( (status&1) == 0 ) return status;
	    j = 0;
	}
	ctx->outmsg.data[j++] = data[i];
    }
    ctx->out_pos = j;

    return SS$_NORMAL;
}

/*****************************************************************************/
/* Tssl_get_app_data flushes any pending output data and reads sll records
 * until more application data arrives.  The function returns a pointer to
 * the decrypted application data.
 */
int tssl_get_app_data ( ssl_context ctx, int *length, unsigned char **data )
{
    int status, io_length, written;
    /*
     * Flush any pending outbound data we've buffered.
     */
    if ( ctx->out_pos > 0 ) {
	status = flush_outmsg ( ctx );
	if ( (status&1) == 0 ) return status;
    }
    /*
     * Request more application data from the SSL engine.
     */
    *data = (unsigned char *) ctx->msg.data;
    io_length = ctx->max_io;
    if ( io_length > sizeof(ctx->msg.data) ) io_length = sizeof(ctx->msg.data);
    ctx->msg.channel = 'A';
    ctx->msg.function = 'G';
    ctx->msg.length = io_length;
    status = mst_write ( ctx->link, (char *) &ctx->msg, RPC_HDR_SIZE, 
		&written );
    if ( (status&1) == 1 ) {
	/*
	 * Wait for SSL to confirm request, confirm message will include
	 * the more data.
	 */
	status = confirm_app_data ( ctx );
	*length = ctx->msg.length;
    }

    return status;
}

/*****************************************************************************/
/* Tssl_get_client_cert flushes any pending output data and reads a 'G' channel
 * message from the SSL engine, returning a message to the data buffer.
 * The data returned is an encoded form of the client certificate and
 * related information.
 */
int tssl_get_client_cert ( ssl_context ctx, int *length, unsigned char **data )
{
    int status, io_length, written;
    /*
     * Flush any pending outbound data we've buffered.
     */
    if ( ctx->out_pos > 0 ) {
	status = flush_outmsg ( ctx );
	if ( (status&1) == 0 ) return status;
    }
    /*
     * Request data from special 'G' channel.
     */
    *data = (unsigned char *) ctx->msg.data;
    io_length = ctx->max_io;
    if ( io_length > sizeof(ctx->msg.data) ) io_length = sizeof(ctx->msg.data);
    ctx->msg.channel = 'G';
    ctx->msg.function = 'G';
    ctx->msg.length = io_length;
    status = mst_write ( ctx->link, (char *) &ctx->msg, RPC_HDR_SIZE, 
		&written );
    if ( (status&1) == 1 ) {
	/*
	 * Wait for SSL to confirm request, confirm message will include
	 * the more data.
	 */
	status = confirm_app_data ( ctx );
	*length = ctx->msg.length;
    }

    return status;
}

/*****************************************************************************/
/* Cleanup context (session IDs for context may still be cached).  If flags
 * bit 0 is set, rundown will flush pending output data before performing
 * rundown.
 */
int tssl_rundown_context ( ssl_context ctx, int flags )
{
    int status;
    /*
     * Flush remaining outbound data if requested.
     */
    if ( (flags&1) == 1 && ctx->out_pos > 0 ) status = flush_outmsg ( ctx );
    else status = SS$_NORMAL;
    /*
     * Terminate decnet.
     */
    if ( ctx->link ) {
	status = mst_close ( ctx->link );
    }
    /*
     * Put structure back on free list.
     */
    pthread_mutex_lock ( &context_db );
    ctx->next = free_ctx;
    free_ctx = ctx;
    pthread_mutex_unlock ( &context_db );
    return status;
}

/*****************************************************************************/
/* Flush pending outbound application data to the SSL engine.
 */
static int flush_outmsg ( ssl_context ctx )
{
    int status, segment, i;
    /*
     * Write data to decnet task and await confirm.
     */
    status = SS$_NORMAL;
    (*ctx->error) ( 3, "Flushing output data" );
    ctx->outmsg.channel = 'A';
    ctx->outmsg.function = 'P';
    ctx->outmsg.length = ctx->out_pos;

    status = mst_write ( ctx->link, (char *) &ctx->outmsg, 
	ctx->outmsg.length + RPC_HDR_SIZE, &segment );

    if ( (status&1) == 1 ) status = confirm_app_data ( ctx );
    if ( (status&1) == 0 ) {
	(*ctx->error) ( status, "failure in outbound flush" );
    }
    ctx->out_pos = 0;
    return status;
}
/***********************************************************************/
/*
 * MST for servicing SSL.  We accept the inbound and speak a
 * simple protocol for multiplexing the 2 data streams (application and
 * ssl data) over this logical link.
 *
 * Each message sent over the MST link has the following structure:
 *    struct rpc_msg { 
 *      char channel;
 *      char function;
 *      short length;
 *      char data[MAX_DATA];
 *    } msg;
 *
 * The channel field designates the virtual data stream this message applies
 * to and is one of:
 *   A - Application data (payload).
 *   R - Remote client connection that initiated the SSL connection.  Encrypted
 *       data is sent over this connection.
 *   G - General data, reserved for future use.
 *
 * The data streams are half-duplex read/write and have following functions:
 *   G - Get, requests that up to msg.length bytes of data be returned.  The
 *       data is returned in the next 'C' function response that matches the
 *       requesting channel.
 *   P - Put, requests that the first msg.length bytes of msg.data be appended
 *       to the designated stream.
 *   C - Confirms a get or put.  Every get and put will get a confirm response,
 *       you cannot initiate another function on a channel until the previous
 *       operation has been confirmed.
 *
 *  The 2 channels may interleave their operations, for example:
 *        Server msg           Client msg
 *         A, Get, 4092          ---->
 *                               <----  R, get, 4092
 *         R, Confirm, {hello}   ---->
 *                               <----  R, put, {srv hello}
 *         R, Confirm, 0         ---->
 *                               .		(SSL handshake completed)
 *                               .              (read first app data).
 *                               <----  A, confirm, {http data}
 *         A, Put, {http data}   ---->
 *                               <----  A, confirm, 0
 *
 *  The length field is not permitted to be larger that 4092 bytes.
 *
 * Author: Dave Jones
 * Date:   22-JUL-1996
 * Revised: 24-AUG-1996		convert to MST
 */

#define RPC_HDR_SIZE (sizeof(struct rpc_msg) - 4092)

/***************************************************************************/
/* Handle operations on the 'A' channel.
 */
static int app_request ( mst_link_t link, char *log_prefix,
	struct rpc_msg *msg, int length )
{
    int status, segment, written, tlog_putlog();
    status = 1;
    switch ( msg->function ) {
	case 'G':
	    /*
	     * Request remote data (channel R).
	     */
	    msg->channel = 'R';
	    status = mst_write ( link, (char *) msg, length, &written );
	    if ( (status&1) == 0 ) break;
	    /*
	     * Read response, verify answer is remote confirm.
	     */
	    status = mst_read ( link, (char *) msg, sizeof(*msg), &segment );
	    if ( (status&1) == 0 ) break;
	    if ( (length < RPC_HDR_SIZE) || (msg->channel != 'R') ||
		(msg->function != 'C') ) {
		printf("Unexpected response to get: '%c' '%c' (%d)\n",
			msg->channel, msg->function, length );
		status = 20;
		break;
	    }
	    /*
	     * Send confirm (and data) back to channel A.
	     */
	    msg->channel = 'A';
	    msg->function = 'C';
	    status = mst_write ( link, (char *) msg, segment, &written );
	    if ( (status&1) == 0 ) break;
	    break;

	case 'P':
	    /*
	     * Put data to remote.
	     */
	    msg->channel = 'R';
	    status = mst_write ( link, (char *) msg, length, &written );
	    if ( (status&1) == 0 ) break;
	    /*
	     * Read confirm message.
	     */
	    status = mst_read ( link, (char *) msg, sizeof(*msg), &length );
	    if ( (status&1) == 0 ) break;
	    if ( (length < RPC_HDR_SIZE) || (msg->channel != 'R') ||
		(msg->function != 'C') ) {
		tlog_putlog(0,"!AZ Unexpected response to put: '%c' '%c' (%d)\n",
			log_prefix, msg->channel, msg->function, length );
		status = 20;
		break;
	    }
	    /*
	     * Confirm the send.
	     */
	    msg->channel = 'A';
	    msg->function = 'C';
	    msg->length = 0;
	    status = mst_write ( link, (char *) msg, RPC_HDR_SIZE, &written );
	    break;
	case 'X':
	    /*
	     * Close down connection, error status will force exit.
	     */
	    status = 2160;
	    break;
	default:
		tlog_putlog(0,"!AZ Unexpected app request: '!AF'!/", 1,
			log_prefix, &msg->function );
	    status = 20;
	    break;
    }
    return status;
}
/***************************************************************************/
/* Handle operations on the 'G' channel.
 */
static int general_request ( mst_link_t link, char *log_prefix,
	struct rpc_msg *msg, int length )
{
    return 48;
}
/***************************************************************************/
/* Start routine for MST created to handle SSL connection.
 */
int ssl_engine ( mst_link_t link, char *service_name,
	char *certificate, int ndx, int avail )
{
    int status, length, tlog_putlog(), id;
    struct rpc_msg msg;
    char prefix[32], temp[80];
    /*
     * Confirm creation.
     */
    tu_strcpy ( prefix, "Service%SSL/" );
    tu_strint ( ndx, &prefix[12] );
    length = tu_strlen ( &prefix[12] ) + 12;
    prefix[length++] = ' ';
    prefix[length++] = '-';
    prefix[length] = '\0';
    if ( http_log_level > 0 ) tlog_putlog ( 1,
	"!AZ thread created at !%D, ndx: !SL, available: !SL!/",
	prefix, 0, ndx, avail );
#ifdef EAY
    id = ndx;
    status = ssl_do_thread ( link, 0, &id );
#else
    /*
     * Take commands from client until bad status.
     */
    while ( (status&1) == 1 ) {
	/*
	 * Read message.
	 */
	status = mst_read ( link, (char *) &msg, sizeof(msg), &length );
	if ( (status&1) == 0 ) {
	    if ( status != SS$_LINKDISCON ) tlog_putlog ( 0,
		"!AZ Error in main loop get: !SL!/", prefix, status );
	    break;
	}
	if ( length < RPC_HDR_SIZE ) {
	    tlog_putlog(0,"!AZ Error in main loop get size: %!SL!/", 
		prefix, length );
	    break;
	}
	/*
	 * Take action depending upon which data stream selected.
	 */
	if ( http_log_level > 4 ) tlog_putlog ( 5,
		"!AZ Client command: chan='!AF' func='!AF' len=!SL!/",
		prefix, 1, &msg.channel, 1, &msg.function, length );

	switch ( msg.channel ) {
	    case 'A':
		status = app_request ( link, prefix, &msg, length );
		break;
	    case 'R':
		/*
		 * The SSL server, can't respond to requests for the
		 * the remote channel (we make requests for the R channel).
		 */
		tlog_putlog(0, "!AZ Invalid channel: '!AF'!/", prefix,
			1, &msg.channel );
		status = 20;
		break;
	    case 'G':
		status = general_request ( link, prefix, &msg, length );
		break;
	    default:
		tlog_putlog(0, "!AZ Unknown channel code: '!AF'!/", 
			prefix, 1, &msg.channel );
		status = 20;
		break;
	}
    }
#endif
    if ( http_log_level > 0 ) tlog_putlog ( 1, "!AZ completed at !%T!/", 
		prefix, 0 );
    return 1;
}
