/**********************************************************************/
/*                                                                    */
/* Copyright (c) 2001 by Sun Microsystems, Inc.                       */
/* All rights reserved.                                               */
/*                                                                    */
/**********************************************************************/

/*****************************************************************************
*
* Prog Name:    sslsock00.c
*
* Description:
*
*    SSL Socket sample program. This program is partnered with transaction
*    SSL0 (program SSLSOCK0).
*
*    This program opens a socket and connects to the host and port of the
*    SSL server specified on the command line. It invokes transaction
*    SSL0 by sending a message of the form:
*
*    xxxx,12345678901234567890123456789012345
*
*    where,
*
*    "xxxx" is the tran id of 1 to 4 characters (SSL0)
*
*    "12345.......45" is up to 35 characters of optional data  
*    to be placed in the COMMAREA in CLIENT-IN-DATA.
*
*    Having sent its initial message the program must receive a message on
*    the socket. This shows the transaction has been initiated in the MTP
*    region.
*
*    Each message received is sent back until the socket is closed
*    by the transaction.
*
* Usage:
*
*    sslsock00 -h host [-p port] [-d certdir] [-n nickname] [-c cipher]
*
*    where,
*
*    -h host        the socket server host
*    -p port        the socket server port          (default 8002).
*    -d certdir     the client certificate database (default cert_db).
*    -n nickname    the client certificate nickname (default client).
*    -c cipher      the cipher suite to use         (default any)
*
* Change Log:
*
*****************************************************************************/

#include <signal.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>

/* NSPR header files */
#include <prerror.h>
#include <nspr.h>
#include <prnetdb.h>

/* NSS header files */
#include <pk11func.h>
#include <secitem.h>
#include <ssl.h>
#include <certt.h>
#include <nss.h>
#include <secrng.h>
#include <secder.h>
#include <key.h>
#include <sslproto.h>
#include <prerr.h>
#include <secerr.h>
#include <sslerr.h>

#define BUFSIZE  4096  /* message buffer size */

void        usage(char *progname);

PRFileDesc* setup_socket(char *nickname);

void        exitErr(char *function);

SECStatus   ownGetClientAuthData(void                        *arg,
                                 PRFileDesc                  *socket, 
                                 struct CERTDistNamesStr     *caNames,
                                 struct CERTCertificateStr  **pRetCert, 
                                 struct SECKEYPrivateKeyStr **pRetKey);

SECStatus   ownAuthCertificate  (void       *arg, 
                                 PRFileDesc *socket, 
                                 PRBool      checksig, 
                                 PRBool      isServer);

SECStatus   ownBadCertHandler(void *arg, PRFileDesc *socket);

char*       ownPasswd(PK11SlotInfo *slot, PRBool required, void *arg);

SECStatus   ownHandshakeCallback(PRFileDesc *socket, void *arg);

int         readThisMany(PRFileDesc *fd, char *buffer, int thisMany);

int         writeThisMany(PRFileDesc *fd, char *buffer, int thisMany);

/* 
 * Print usage information and exit.
 */
void usage(char *progname)
{
    printf("Usage: %s -h host [-p port] [-d certdir] [-n nickname] [-c cipher]\n",
	    progname);
    exit(1);
}

/*
 * Create an SSL socket.
 */
PRFileDesc* setup_socket(char *nickname) 
{
    PRFileDesc         *socket = NULL;
    PRFileDesc         *sslsocket = NULL;
    PRSocketOptionData  sockdata;
    PRBool on;

    /* Create a new socket */

    socket = PR_NewTCPSocket();
    if (!socket) {
	exitErr("PR_NewTCPSocket");
    }

    /* Make it a blocking socket */

    sockdata.option = PR_SockOpt_Nonblocking;
    sockdata.value.non_blocking = PR_FALSE;

    if (PR_SetSocketOption(socket, &sockdata) != PR_SUCCESS ) {
	exitErr("PR_SetSocketOption");
    }

    /* Import the socket into the SSL layer */

    sslsocket = SSL_ImportFD(NULL, socket);
    if (!sslsocket) {
	exitErr("SSL_ImportFD");
    }

    if (nickname) {
	/*
	 * Define a callback function for SSL to use when a server asks for
	 * this client's certificate.
	 */
	if ((SSL_GetClientAuthDataHook(sslsocket,
			(SSLGetClientAuthData)ownGetClientAuthData,
			(void *)nickname)) != SECSuccess) {
	    exitErr("SSL_GetClientAuthDataHook");
	}
    }

    /*
     * Set up a certificate authentication callback function which will be
     * called to authenticate a server's certificate.
     */
    if ((SSL_AuthCertificateHook(sslsocket,
		    (SSLAuthCertificate)ownAuthCertificate,
		    (void *)CERT_GetDefaultCertDB())) != SECSuccess ) {
	exitErr("SSL_AuthCertificateHook");
    }

    /*
     * Set up a callback function to deal with situations where the
     * SSL_AuthCertificate callback function has failed. This callback
     * function allows the application to override the decision made by
     * the certificate authorization callback and authorize the server
     * certificate.
     */
    if ((SSL_BadCertHook(sslsocket,
		    (SSLBadCertHandler)ownBadCertHandler,
		    NULL)) != SECSuccess ) {
	exitErr("SSL_BadCertHook");
    }

    /*
     * Set up a callback function used by SSL to inform the application when
     * when the handshake is complete.
     */
    if ((SSL_HandshakeCallback(sslsocket,
		    (SSLHandshakeCallback)ownHandshakeCallback,
		    NULL)) != SECSuccess ) {
	exitErr("SSL_HandshakeCallback");
    }

    return sslsocket;
}

/*
 * Display an error message, shutdown NSS, clean up NSPR, then exit.
 */
void exitErr(char *function)
{
    const char  *errortext = NULL;
    PRErrorCode  errorcode = PR_GetError();

    switch (errorcode) {
    case PR_OUT_OF_MEMORY_ERROR:                           errortext = "PR_OUT_OF_MEMORY_ERROR"; break; 
    case PR_BAD_DESCRIPTOR_ERROR:                          errortext = "PR_BAD_DESCRIPTOR_ERROR"; break; 
    case PR_WOULD_BLOCK_ERROR:                             errortext = "PR_WOULD_BLOCK_ERROR"; break; 
    case PR_ACCESS_FAULT_ERROR:                            errortext = "PR_ACCESS_FAULT_ERROR"; break; 
    case PR_INVALID_METHOD_ERROR:                          errortext = "PR_INVALID_METHOD_ERROR"; break; 
    case PR_ILLEGAL_ACCESS_ERROR:                          errortext = "PR_ILLEGAL_ACCESS_ERROR"; break; 
    case PR_UNKNOWN_ERROR:                                 errortext = "PR_UNKNOWN_ERROR"; break; 
    case PR_PENDING_INTERRUPT_ERROR:                       errortext = "PR_PENDING_INTERRUPT_ERROR"; break; 
    case PR_NOT_IMPLEMENTED_ERROR:                         errortext = "PR_NOT_IMPLEMENTED_ERROR"; break; 
    case PR_IO_ERROR:                                      errortext = "PR_IO_ERROR"; break; 
    case PR_IO_TIMEOUT_ERROR:                              errortext = "PR_IO_TIMEOUT_ERROR"; break; 
    case PR_IO_PENDING_ERROR:                              errortext = "PR_IO_PENDING_ERROR"; break; 
    case PR_DIRECTORY_OPEN_ERROR:                          errortext = "PR_DIRECTORY_OPEN_ERROR"; break; 
    case PR_INVALID_ARGUMENT_ERROR:                        errortext = "PR_INVALID_ARGUMENT_ERROR"; break; 
    case PR_ADDRESS_NOT_AVAILABLE_ERROR:                   errortext = "PR_ADDRESS_NOT_AVAILABLE_ERROR"; break; 
    case PR_ADDRESS_NOT_SUPPORTED_ERROR:                   errortext = "PR_ADDRESS_NOT_SUPPORTED_ERROR"; break; 
    case PR_IS_CONNECTED_ERROR:                            errortext = "PR_IS_CONNECTED_ERROR"; break; 
    case PR_BAD_ADDRESS_ERROR:                             errortext = "PR_BAD_ADDRESS_ERROR"; break; 
    case PR_ADDRESS_IN_USE_ERROR:                          errortext = "PR_ADDRESS_IN_USE_ERROR"; break; 
    case PR_CONNECT_REFUSED_ERROR:                         errortext = "PR_CONNECT_REFUSED_ERROR"; break; 
    case PR_NETWORK_UNREACHABLE_ERROR:                     errortext = "PR_NETWORK_UNREACHABLE_ERROR"; break; 
    case PR_CONNECT_TIMEOUT_ERROR:                         errortext = "PR_CONNECT_TIMEOUT_ERROR"; break; 
    case PR_NOT_CONNECTED_ERROR:                           errortext = "PR_NOT_CONNECTED_ERROR"; break; 
    case PR_LOAD_LIBRARY_ERROR:                            errortext = "PR_LOAD_LIBRARY_ERROR"; break; 
    case PR_UNLOAD_LIBRARY_ERROR:                          errortext = "PR_UNLOAD_LIBRARY_ERROR"; break; 
    case PR_FIND_SYMBOL_ERROR:                             errortext = "PR_FIND_SYMBOL_ERROR"; break; 
    case PR_INSUFFICIENT_RESOURCES_ERROR:                  errortext = "PR_INSUFFICIENT_RESOURCES_ERROR"; break; 
    case PR_DIRECTORY_LOOKUP_ERROR:                        errortext = "PR_DIRECTORY_LOOKUP_ERROR"; break; 
    case PR_TPD_RANGE_ERROR:                               errortext = "PR_TPD_RANGE_ERROR"; break; 
    case PR_PROC_DESC_TABLE_FULL_ERROR:                    errortext = "PR_PROC_DESC_TABLE_FULL_ERROR"; break; 
    case PR_SYS_DESC_TABLE_FULL_ERROR:                     errortext = "PR_SYS_DESC_TABLE_FULL_ERROR"; break; 
    case PR_NOT_SOCKET_ERROR:                              errortext = "PR_NOT_SOCKET_ERROR"; break; 
    case PR_NOT_TCP_SOCKET_ERROR:                          errortext = "PR_NOT_TCP_SOCKET_ERROR"; break; 
    case PR_SOCKET_ADDRESS_IS_BOUND_ERROR:                 errortext = "PR_SOCKET_ADDRESS_IS_BOUND_ERROR"; break; 
    case PR_NO_ACCESS_RIGHTS_ERROR:                        errortext = "PR_NO_ACCESS_RIGHTS_ERROR"; break; 
    case PR_OPERATION_NOT_SUPPORTED_ERROR:                 errortext = "PR_OPERATION_NOT_SUPPORTED_ERROR"; break; 
    case PR_PROTOCOL_NOT_SUPPORTED_ERROR:                  errortext = "PR_PROTOCOL_NOT_SUPPORTED_ERROR"; break; 
    case PR_REMOTE_FILE_ERROR:                             errortext = "PR_REMOTE_FILE_ERROR"; break; 
    case PR_BUFFER_OVERFLOW_ERROR:                         errortext = "PR_BUFFER_OVERFLOW_ERROR"; break; 
    case PR_CONNECT_RESET_ERROR:                           errortext = "PR_CONNECT_RESET_ERROR"; break; 
    case PR_RANGE_ERROR:                                   errortext = "PR_RANGE_ERROR"; break; 
    case PR_DEADLOCK_ERROR:                                errortext = "PR_DEADLOCK_ERROR"; break; 
    case PR_FILE_IS_LOCKED_ERROR:                          errortext = "PR_FILE_IS_LOCKED_ERROR"; break; 
    case PR_FILE_TOO_BIG_ERROR:                            errortext = "PR_FILE_TOO_BIG_ERROR"; break; 
    case PR_NO_DEVICE_SPACE_ERROR:                         errortext = "PR_NO_DEVICE_SPACE_ERROR"; break; 
    case PR_PIPE_ERROR:                                    errortext = "PR_PIPE_ERROR"; break; 
    case PR_NO_SEEK_DEVICE_ERROR:                          errortext = "PR_NO_SEEK_DEVICE_ERROR"; break; 
    case PR_IS_DIRECTORY_ERROR:                            errortext = "PR_IS_DIRECTORY_ERROR"; break; 
    case PR_LOOP_ERROR:                                    errortext = "PR_LOOP_ERROR"; break; 
    case PR_NAME_TOO_LONG_ERROR:                           errortext = "PR_NAME_TOO_LONG_ERROR"; break; 
    case PR_FILE_NOT_FOUND_ERROR:                          errortext = "PR_FILE_NOT_FOUND_ERROR"; break; 
    case PR_NOT_DIRECTORY_ERROR:                           errortext = "PR_NOT_DIRECTORY_ERROR"; break; 
    case PR_READ_ONLY_FILESYSTEM_ERROR:                    errortext = "PR_READ_ONLY_FILESYSTEM_ERROR"; break; 
    case PR_DIRECTORY_NOT_EMPTY_ERROR:                     errortext = "PR_DIRECTORY_NOT_EMPTY_ERROR"; break; 
    case PR_FILESYSTEM_MOUNTED_ERROR:                      errortext = "PR_FILESYSTEM_MOUNTED_ERROR"; break; 
    case PR_NOT_SAME_DEVICE_ERROR:                         errortext = "PR_NOT_SAME_DEVICE_ERROR"; break; 
    case PR_DIRECTORY_CORRUPTED_ERROR:                     errortext = "PR_DIRECTORY_CORRUPTED_ERROR"; break; 
    case PR_FILE_EXISTS_ERROR:                             errortext = "PR_FILE_EXISTS_ERROR"; break; 
    case PR_MAX_DIRECTORY_ENTRIES_ERROR:                   errortext = "PR_MAX_DIRECTORY_ENTRIES_ERROR"; break; 
    case PR_INVALID_DEVICE_STATE_ERROR:                    errortext = "PR_INVALID_DEVICE_STATE_ERROR"; break; 
    case PR_DEVICE_IS_LOCKED_ERROR:                        errortext = "PR_DEVICE_IS_LOCKED_ERROR"; break; 
    case PR_NO_MORE_FILES_ERROR:                           errortext = "PR_NO_MORE_FILES_ERROR"; break; 
    case PR_END_OF_FILE_ERROR:                             errortext = "PR_END_OF_FILE_ERROR"; break; 
    case PR_FILE_SEEK_ERROR:                               errortext = "PR_FILE_SEEK_ERROR"; break; 
    case PR_FILE_IS_BUSY_ERROR:                            errortext = "PR_FILE_IS_BUSY_ERROR"; break; 
    case PR_OPERATION_ABORTED_ERROR:                       errortext = "PR_OPERATION_ABORTED_ERROR"; break; 
    case PR_IN_PROGRESS_ERROR:                             errortext = "PR_IN_PROGRESS_ERROR"; break; 
    case PR_ALREADY_INITIATED_ERROR:                       errortext = "PR_ALREADY_INITIATED_ERROR"; break; 
    case PR_GROUP_EMPTY_ERROR:                             errortext = "PR_GROUP_EMPTY_ERROR"; break; 
    case PR_INVALID_STATE_ERROR:                           errortext = "PR_INVALID_STATE_ERROR"; break; 
    case PR_NETWORK_DOWN_ERROR:                            errortext = "PR_NETWORK_DOWN_ERROR"; break; 
    case PR_SOCKET_SHUTDOWN_ERROR:                         errortext = "PR_SOCKET_SHUTDOWN_ERROR"; break; 
    case PR_CONNECT_ABORTED_ERROR:                         errortext = "PR_CONNECT_ABORTED_ERROR"; break; 
    case PR_HOST_UNREACHABLE_ERROR:                        errortext = "PR_HOST_UNREACHABLE_ERROR"; break; 
    case PR_MAX_ERROR:                                     errortext = "PR_MAX_ERROR"; break; 
    case SSL_ERROR_EXPORT_ONLY_SERVER:                     errortext = "SSL_ERROR_EXPORT_ONLY_SERVER"; break;        
    case SSL_ERROR_US_ONLY_SERVER:                         errortext = "SSL_ERROR_US_ONLY_SERVER"; break;        
    case SSL_ERROR_NO_CYPHER_OVERLAP:                      errortext = "SSL_ERROR_NO_CYPHER_OVERLAP"; break;        
    case SSL_ERROR_NO_CERTIFICATE:                         errortext = "SSL_ERROR_NO_CERTIFICATE"; break;
    case SSL_ERROR_BAD_CERTIFICATE:                        errortext = "SSL_ERROR_BAD_CERTIFICATE"; break;        
    case SSL_ERROR_BAD_CLIENT:                             errortext = "SSL_ERROR_BAD_CLIENT"; break;        
    case SSL_ERROR_BAD_SERVER:                             errortext = "SSL_ERROR_BAD_SERVER"; break;        
    case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE:           errortext = "SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE"; break;        
    case SSL_ERROR_UNSUPPORTED_VERSION:                    errortext = "SSL_ERROR_UNSUPPORTED_VERSION"; break;        
    case SSL_ERROR_WRONG_CERTIFICATE:                      errortext = "SSL_ERROR_WRONG_CERTIFICATE"; break;        
    case SSL_ERROR_BAD_CERT_DOMAIN:                        errortext = "SSL_ERROR_BAD_CERT_DOMAIN"; break;        
    case SSL_ERROR_POST_WARNING:                           errortext = "SSL_ERROR_POST_WARNING"; break;        
    case SSL_ERROR_SSL2_DISABLED:                          errortext = "SSL_ERROR_SSL2_DISABLED"; break;        
    case SSL_ERROR_BAD_MAC_READ:                           errortext = "SSL_ERROR_BAD_MAC_READ"; break;        
    case SSL_ERROR_BAD_MAC_ALERT:                          errortext = "SSL_ERROR_BAD_MAC_ALERT"; break;        
    case SSL_ERROR_BAD_CERT_ALERT:                         errortext = "SSL_ERROR_BAD_CERT_ALERT"; break; 
    case SSL_ERROR_REVOKED_CERT_ALERT:                     errortext = "SSL_ERROR_REVOKED_CERT_ALERT"; break;        
    case SSL_ERROR_EXPIRED_CERT_ALERT:                     errortext = "SSL_ERROR_EXPIRED_CERT_ALERT"; break;        
    case SSL_ERROR_SSL_DISABLED:                           errortext = "SSL_ERROR_SSL_DISABLED"; break;        
    case SSL_ERROR_FORTEZZA_PQG:                           errortext = "SSL_ERROR_FORTEZZA_PQG"; break;        
    case SSL_ERROR_UNKNOWN_CIPHER_SUITE:                   errortext = "SSL_ERROR_UNKNOWN_CIPHER_SUITE"; break;        
    case SSL_ERROR_NO_CIPHERS_SUPPORTED:                   errortext = "SSL_ERROR_NO_CIPHERS_SUPPORTED"; break;        
    case SSL_ERROR_BAD_BLOCK_PADDING:                      errortext = "SSL_ERROR_BAD_BLOCK_PADDING"; break;        
    case SSL_ERROR_RX_RECORD_TOO_LONG:                     errortext = "SSL_ERROR_RX_RECORD_TOO_LONG"; break;        
    case SSL_ERROR_TX_RECORD_TOO_LONG:                     errortext = "SSL_ERROR_TX_RECORD_TOO_LONG"; break;        
    case SSL_ERROR_RX_MALFORMED_HELLO_REQUEST:             errortext = "SSL_ERROR_RX_MALFORMED_HELLO_REQUEST"; break;        
    case SSL_ERROR_RX_MALFORMED_CLIENT_HELLO:              errortext = "SSL_ERROR_RX_MALFORMED_CLIENT_HELLO"; break;        
    case SSL_ERROR_RX_MALFORMED_SERVER_HELLO:              errortext = "SSL_ERROR_RX_MALFORMED_SERVER_HELLO"; break;        
    case SSL_ERROR_RX_MALFORMED_CERTIFICATE:               errortext = "SSL_ERROR_RX_MALFORMED_CERTIFICATE"; break;        
    case SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH:           errortext = "SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH"; break;        
    case SSL_ERROR_RX_MALFORMED_CERT_REQUEST:              errortext = "SSL_ERROR_RX_MALFORMED_CERT_REQUEST"; break;        
    case SSL_ERROR_RX_MALFORMED_HELLO_DONE:                errortext = "SSL_ERROR_RX_MALFORMED_HELLO_DONE"; break;        
    case SSL_ERROR_RX_MALFORMED_CERT_VERIFY:               errortext = "SSL_ERROR_RX_MALFORMED_CERT_VERIFY"; break;        
    case SSL_ERROR_RX_MALFORMED_CLIENT_KEY_EXCH:           errortext = "SSL_ERROR_RX_MALFORMED_CLIENT_KEY_EXCH"; break;        
    case SSL_ERROR_RX_MALFORMED_FINISHED:                  errortext = "SSL_ERROR_RX_MALFORMED_FINISHED "; break;        
    case SSL_ERROR_RX_MALFORMED_CHANGE_CIPHER:             errortext = "SSL_ERROR_RX_MALFORMED_CHANGE_CIPHER"; break;        
    case SSL_ERROR_RX_MALFORMED_ALERT:                     errortext = "SSL_ERROR_RX_MALFORMED_ALERT"; break;        
    case SSL_ERROR_RX_MALFORMED_HANDSHAKE:                 errortext = "SSL_ERROR_RX_MALFORMED_HANDSHAKE"; break;        
    case SSL_ERROR_RX_MALFORMED_APPLICATION_DATA:          errortext = "SSL_ERROR_RX_MALFORMED_APPLICATION_DATA"; break;        
    case SSL_ERROR_RX_UNEXPECTED_HELLO_REQUEST:            errortext = "SSL_ERROR_RX_UNEXPECTED_HELLO_REQUEST"; break;        
    case SSL_ERROR_RX_UNEXPECTED_CLIENT_HELLO:             errortext = "SSL_ERROR_RX_UNEXPECTED_CLIENT_HELLO"; break;        
    case SSL_ERROR_RX_UNEXPECTED_SERVER_HELLO:             errortext = "SSL_ERROR_RX_UNEXPECTED_SERVER_HELLO"; break;        
    case SSL_ERROR_RX_UNEXPECTED_CERTIFICATE:              errortext = "SSL_ERROR_RX_UNEXPECTED_CERTIFICATE"; break;        
    case SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH:          errortext = "SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH"; break;        
    case SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST:             errortext = "SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST"; break;        
    case SSL_ERROR_RX_UNEXPECTED_HELLO_DONE:               errortext = "SSL_ERROR_RX_UNEXPECTED_HELLO_DONE"; break;        
    case SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY:              errortext = "SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY"; break;        
    case SSL_ERROR_RX_UNEXPECTED_CLIENT_KEY_EXCH:          errortext = "SSL_ERROR_RX_UNEXPECTED_CLIENT_KEY_EXCH"; break;        
    case SSL_ERROR_RX_UNEXPECTED_FINISHED:                 errortext = "SSL_ERROR_RX_UNEXPECTED_FINISHED"; break;        
    case SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER:            errortext = "SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER"; break;        
    case SSL_ERROR_RX_UNEXPECTED_ALERT:                    errortext = "SSL_ERROR_RX_UNEXPECTED_ALERT"; break;        
    case SSL_ERROR_RX_UNEXPECTED_HANDSHAKE:                errortext = "SSL_ERROR_RX_UNEXPECTED_HANDSHAKE"; break;        
    case SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA:         errortext = "SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA"; break;
    case SSL_ERROR_RX_UNKNOWN_RECORD_TYPE:                 errortext = "SSL_ERROR_RX_UNKNOWN_RECORD_TYPE"; break;        
    case SSL_ERROR_RX_UNKNOWN_HANDSHAKE:                   errortext = "SSL_ERROR_RX_UNKNOWN_HANDSHAKE"; break;        
    case SSL_ERROR_RX_UNKNOWN_ALERT:                       errortext = "SSL_ERROR_RX_UNKNOWN_ALERT"; break;        
    case SSL_ERROR_CLOSE_NOTIFY_ALERT:                     errortext = "SSL_ERROR_CLOSE_NOTIFY_ALERT"; break;        
    case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT:             errortext = "SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT"; break;        
    case SSL_ERROR_DECOMPRESSION_FAILURE_ALERT:            errortext = "SSL_ERROR_DECOMPRESSION_FAILURE_ALERT"; break;        
    case SSL_ERROR_HANDSHAKE_FAILURE_ALERT:                errortext = "SSL_ERROR_HANDSHAKE_FAILURE_ALERT"; break;        
    case SSL_ERROR_ILLEGAL_PARAMETER_ALERT:                errortext = "SSL_ERROR_ILLEGAL_PARAMETER_ALERT"; break;        
    case SSL_ERROR_UNSUPPORTED_CERT_ALERT:                 errortext = "SSL_ERROR_UNSUPPORTED_CERT_ALERT"; break;        
    case SSL_ERROR_CERTIFICATE_UNKNOWN_ALERT:              errortext = "SSL_ERROR_CERTIFICATE_UNKNOWN_ALERT"; break;        
    case SSL_ERROR_GENERATE_RANDOM_FAILURE:                errortext = "SSL_ERROR_GENERATE_RANDOM_FAILURE"; break;        
    case SSL_ERROR_SIGN_HASHES_FAILURE:                    errortext = "SSL_ERROR_SIGN_HASHES_FAILURE"; break;        
    case SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE:             errortext = "SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE"; break;        
    case SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE:            errortext = "SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE"; break;        
    case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE:            errortext = "SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE"; break;        
    case SSL_ERROR_ENCRYPTION_FAILURE:                     errortext = "SSL_ERROR_ENCRYPTION_FAILURE        "; break;        
    case SSL_ERROR_DECRYPTION_FAILURE:                     errortext = "SSL_ERROR_DECRYPTION_FAILURE"; break;        
    case SSL_ERROR_SOCKET_WRITE_FAILURE:                   errortext = "SSL_ERROR_SOCKET_WRITE_FAILURE"; break;        
    case SSL_ERROR_MD5_DIGEST_FAILURE:                     errortext = "SSL_ERROR_MD5_DIGEST_FAILURE"; break;        
    case SSL_ERROR_SHA_DIGEST_FAILURE:                     errortext = "SSL_ERROR_SHA_DIGEST_FAILURE"; break;        
    case SSL_ERROR_MAC_COMPUTATION_FAILURE:                errortext = "SSL_ERROR_MAC_COMPUTATION_FAILURE"; break;        
    case SSL_ERROR_SYM_KEY_CONTEXT_FAILURE:                errortext = "SSL_ERROR_SYM_KEY_CONTEXT_FAILURE"; break;        
    case SSL_ERROR_SYM_KEY_UNWRAP_FAILURE:                 errortext = "SSL_ERROR_SYM_KEY_UNWRAP_FAILURE"; break;        
    case SSL_ERROR_PUB_KEY_SIZE_LIMIT_EXCEEDED:            errortext = "SSL_ERROR_PUB_KEY_SIZE_LIMIT_EXCEEDED"; break;        
    case SSL_ERROR_IV_PARAM_FAILURE:                       errortext = "SSL_ERROR_IV_PARAM_FAILURE"; break;        
    case SSL_ERROR_INIT_CIPHER_SUITE_FAILURE:              errortext = "SSL_ERROR_INIT_CIPHER_SUITE_FAILURE"; break;        
    case SSL_ERROR_SESSION_KEY_GEN_FAILURE:                errortext = "SSL_ERROR_SESSION_KEY_GEN_FAILURE"; break;        
    case SSL_ERROR_NO_SERVER_KEY_FOR_ALG:                  errortext = "SSL_ERROR_NO_SERVER_KEY_FOR_ALG"; break;        
    case SSL_ERROR_TOKEN_INSERTION_REMOVAL:                errortext = "SSL_ERROR_TOKEN_INSERTION_REMOVAL"; break;        
    case SSL_ERROR_TOKEN_SLOT_NOT_FOUND:                   errortext = "SSL_ERROR_TOKEN_SLOT_NOT_FOUND"; break;        
    case SSL_ERROR_NO_COMPRESSION_OVERLAP:                 errortext = "SSL_ERROR_NO_COMPRESSION_OVERLAP"; break;        
    case SSL_ERROR_HANDSHAKE_NOT_COMPLETED:                errortext = "SSL_ERROR_HANDSHAKE_NOT_COMPLETED"; break;        
    case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE:               errortext = "SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE"; break;        
    case SSL_ERROR_CERT_KEA_MISMATCH:                      errortext = "SSL_ERROR_CERT_KEA_MISMATCH"; break;        
    case SSL_ERROR_NO_TRUSTED_SSL_CLIENT_CA:               errortext = "SSL_ERROR_NO_TRUSTED_SSL_CLIENT_CA"; break;        
    case SSL_ERROR_SESSION_NOT_FOUND:                      errortext = "SSL_ERROR_SESSION_NOT_FOUND"; break;
    case SSL_ERROR_DECRYPTION_FAILED_ALERT:                errortext = "SSL_ERROR_DECRYPTION_FAILED_ALERT"; break;
    case SSL_ERROR_RECORD_OVERFLOW_ALERT:                  errortext = "SSL_ERROR_RECORD_OVERFLOW_ALERT"; break;
    case SSL_ERROR_UNKNOWN_CA_ALERT:                       errortext = "SSL_ERROR_UNKNOWN_CA_ALERT"; break;
    case SSL_ERROR_ACCESS_DENIED_ALERT:                    errortext = "SSL_ERROR_ACCESS_DENIED_ALERT"; break;
    case SSL_ERROR_DECODE_ERROR_ALERT:                     errortext = "SSL_ERROR_DECODE_ERROR_ALERT"; break;
    case SSL_ERROR_DECRYPT_ERROR_ALERT:                    errortext = "SSL_ERROR_DECRYPT_ERROR_ALERT"; break;
    case SSL_ERROR_EXPORT_RESTRICTION_ALERT:               errortext = "SSL_ERROR_EXPORT_RESTRICTION_ALERT"; break;
    case SSL_ERROR_PROTOCOL_VERSION_ALERT:                 errortext = "SSL_ERROR_PROTOCOL_VERSION_ALERT"; break;
    case SSL_ERROR_INSUFFICIENT_SECURITY_ALERT:            errortext = "SSL_ERROR_INSUFFICIENT_SECURITY_ALERT"; break;
    case SSL_ERROR_INTERNAL_ERROR_ALERT:                   errortext = "SSL_ERROR_INTERNAL_ERROR_ALERT"; break;
    case SSL_ERROR_USER_CANCELED_ALERT:                    errortext = "SSL_ERROR_USER_CANCELED_ALERT"; break;
    case SSL_ERROR_NO_RENEGOTIATION_ALERT:                 errortext = "SSL_ERROR_NO_RENEGOTIATION_ALERT"; break;
    case SEC_ERROR_IO:                                     errortext = "SEC_ERROR_IO"; break; 
    case SEC_ERROR_LIBRARY_FAILURE:                        errortext = "SEC_ERROR_LIBRARY_FAILURE"; break; 
    case SEC_ERROR_BAD_DATA:                               errortext = "SEC_ERROR_BAD_DATA"; break; 
    case SEC_ERROR_OUTPUT_LEN:                             errortext = "SEC_ERROR_OUTPUT_LEN"; break; 
    case SEC_ERROR_INPUT_LEN:                              errortext = "SEC_ERROR_INPUT_LEN"; break; 
    case SEC_ERROR_INVALID_ARGS:                           errortext = "SEC_ERROR_INVALID_ARGS"; break; 
    case SEC_ERROR_INVALID_ALGORITHM:                      errortext = "SEC_ERROR_INVALID_ALGORITHM"; break; 
    case SEC_ERROR_INVALID_AVA:                            errortext = "SEC_ERROR_INVALID_AVA"; break; 
    case SEC_ERROR_INVALID_TIME:                           errortext = "SEC_ERROR_INVALID_TIME"; break; 
    case SEC_ERROR_BAD_DER:                                errortext = "SEC_ERROR_BAD_DER"; break; 
    case SEC_ERROR_BAD_SIGNATURE:                          errortext = "SEC_ERROR_BAD_SIGNATURE"; break; 
    case SEC_ERROR_EXPIRED_CERTIFICATE:                    errortext = "SEC_ERROR_EXPIRED_CERTIFICATE"; break; 
    case SEC_ERROR_REVOKED_CERTIFICATE:                    errortext = "SEC_ERROR_REVOKED_CERTIFICATE"; break; 
    case SEC_ERROR_UNKNOWN_ISSUER:                         errortext = "SEC_ERROR_UNKNOWN_ISSUER"; break; 
    case SEC_ERROR_BAD_KEY:                                errortext = "SEC_ERROR_BAD_KEY"; break; 
    case SEC_ERROR_BAD_PASSWORD:                           errortext = "SEC_ERROR_BAD_PASSWORD"; break; 
    case SEC_ERROR_RETRY_PASSWORD:                         errortext = "SEC_ERROR_RETRY_PASSWORD"; break; 
    case SEC_ERROR_NO_NODELOCK:                            errortext = "SEC_ERROR_NO_NODELOCK"; break; 
    case SEC_ERROR_BAD_DATABASE:                           errortext = "SEC_ERROR_BAD_DATABASE"; break; 
    case SEC_ERROR_NO_MEMORY:                              errortext = "SEC_ERROR_NO_MEMORY"; break; 
    case SEC_ERROR_UNTRUSTED_ISSUER:                       errortext = "SEC_ERROR_UNTRUSTED_ISSUER"; break; 
    case SEC_ERROR_UNTRUSTED_CERT:                         errortext = "SEC_ERROR_UNTRUSTED_CERT"; break; 
    case SEC_ERROR_DUPLICATE_CERT:                         errortext = "SEC_ERROR_DUPLICATE_CERT"; break; 
    case SEC_ERROR_DUPLICATE_CERT_NAME:                    errortext = "SEC_ERROR_DUPLICATE_CERT_NAME"; break; 
    case SEC_ERROR_ADDING_CERT:                            errortext = "SEC_ERROR_ADDING_CERT"; break; 
    case SEC_ERROR_FILING_KEY:                             errortext = "SEC_ERROR_FILING_KEY"; break; 
    case SEC_ERROR_NO_KEY:                                 errortext = "SEC_ERROR_NO_KEY"; break; 
    case SEC_ERROR_CERT_VALID:                             errortext = "SEC_ERROR_CERT_VALID"; break; 
    case SEC_ERROR_CERT_NOT_VALID:                         errortext = "SEC_ERROR_CERT_NOT_VALID"; break; 
    case SEC_ERROR_CERT_NO_RESPONSE:                       errortext = "SEC_ERROR_CERT_NO_RESPONSE"; break; 
    case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:             errortext = "SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE"; break; 
    case SEC_ERROR_CRL_EXPIRED:                            errortext = "SEC_ERROR_CRL_EXPIRED"; break; 
    case SEC_ERROR_CRL_BAD_SIGNATURE:                      errortext = "SEC_ERROR_CRL_BAD_SIGNATURE"; break; 
    case SEC_ERROR_CRL_INVALID:                            errortext = "SEC_ERROR_CRL_INVALID"; break; 
    case SEC_ERROR_EXTENSION_VALUE_INVALID:                errortext = "SEC_ERROR_EXTENSION_VALUE_INVALID"; break; 
    case SEC_ERROR_EXTENSION_NOT_FOUND:                    errortext = "SEC_ERROR_EXTENSION_NOT_FOUND"; break; 
    case SEC_ERROR_CA_CERT_INVALID:                        errortext = "SEC_ERROR_CA_CERT_INVALID"; break; 
    case SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID:            errortext = "SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID"; break; 
    case SEC_ERROR_CERT_USAGES_INVALID:                    errortext = "SEC_ERROR_CERT_USAGES_INVALID"; break; 
    case SEC_INTERNAL_ONLY:                                errortext = "SEC_INTERNAL_ONLY"; break; 
    case SEC_ERROR_INVALID_KEY:                            errortext = "SEC_ERROR_INVALID_KEY"; break; 
    case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:             errortext = "SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION"; break; 
    case SEC_ERROR_OLD_CRL:                                errortext = "SEC_ERROR_OLD_CRL"; break; 
    case SEC_ERROR_NO_EMAIL_CERT:                          errortext = "SEC_ERROR_NO_EMAIL_CERT"; break; 
    case SEC_ERROR_NO_RECIPIENT_CERTS_QUERY:               errortext = "SEC_ERROR_NO_RECIPIENT_CERTS_QUERY"; break; 
    case SEC_ERROR_NOT_A_RECIPIENT:                        errortext = "SEC_ERROR_NOT_A_RECIPIENT"; break; 
    case SEC_ERROR_PKCS7_KEYALG_MISMATCH:                  errortext = "SEC_ERROR_PKCS7_KEYALG_MISMATCH"; break; 
    case SEC_ERROR_PKCS7_BAD_SIGNATURE:                    errortext = "SEC_ERROR_PKCS7_BAD_SIGNATURE"; break; 
    case SEC_ERROR_UNSUPPORTED_KEYALG:                     errortext = "SEC_ERROR_UNSUPPORTED_KEYALG"; break; 
    case SEC_ERROR_DECRYPTION_DISALLOWED:                  errortext = "SEC_ERROR_DECRYPTION_DISALLOWED"; break; 
    case XP_SEC_FORTEZZA_BAD_CARD:                         errortext = "XP_SEC_FORTEZZA_BAD_CARD"; break; 
    case XP_SEC_FORTEZZA_NO_CARD:                          errortext = "XP_SEC_FORTEZZA_NO_CARD"; break; 
    case XP_SEC_FORTEZZA_NONE_SELECTED:                    errortext = "XP_SEC_FORTEZZA_NONE_SELECTED"; break; 
    case XP_SEC_FORTEZZA_MORE_INFO:                        errortext = "XP_SEC_FORTEZZA_MORE_INFO"; break; 
    case XP_SEC_FORTEZZA_PERSON_NOT_FOUND:                 errortext = "XP_SEC_FORTEZZA_PERSON_NOT_FOUND"; break; 
    case XP_SEC_FORTEZZA_NO_MORE_INFO:                     errortext = "XP_SEC_FORTEZZA_NO_MORE_INFO"; break; 
    case XP_SEC_FORTEZZA_BAD_PIN:                          errortext = "XP_SEC_FORTEZZA_BAD_PIN"; break; 
    case XP_SEC_FORTEZZA_PERSON_ERROR:                     errortext = "XP_SEC_FORTEZZA_PERSON_ERROR"; break; 
    case SEC_ERROR_NO_KRL:                                 errortext = "SEC_ERROR_NO_KRL"; break; 
    case SEC_ERROR_KRL_EXPIRED:                            errortext = "SEC_ERROR_KRL_EXPIRED"; break; 
    case SEC_ERROR_KRL_BAD_SIGNATURE:                      errortext = "SEC_ERROR_KRL_BAD_SIGNATURE"; break; 
    case SEC_ERROR_REVOKED_KEY:                            errortext = "SEC_ERROR_REVOKED_KEY"; break; 
    case SEC_ERROR_KRL_INVALID:                            errortext = "SEC_ERROR_KRL_INVALID"; break; 
    case SEC_ERROR_NEED_RANDOM:                            errortext = "SEC_ERROR_NEED_RANDOM"; break; 
    case SEC_ERROR_NO_MODULE:                              errortext = "SEC_ERROR_NO_MODULE"; break; 
    case SEC_ERROR_NO_TOKEN:                               errortext = "SEC_ERROR_NO_TOKEN"; break; 
    case SEC_ERROR_READ_ONLY:                              errortext = "SEC_ERROR_READ_ONLY"; break; 
    case SEC_ERROR_NO_SLOT_SELECTED:                       errortext = "SEC_ERROR_NO_SLOT_SELECTED"; break; 
    case SEC_ERROR_CERT_NICKNAME_COLLISION:                errortext = "SEC_ERROR_CERT_NICKNAME_COLLISION"; break; 
    case SEC_ERROR_KEY_NICKNAME_COLLISION:                 errortext = "SEC_ERROR_KEY_NICKNAME_COLLISION"; break; 
    case SEC_ERROR_SAFE_NOT_CREATED:                       errortext = "SEC_ERROR_SAFE_NOT_CREATED"; break; 
    case SEC_ERROR_BAGGAGE_NOT_CREATED:                    errortext = "SEC_ERROR_BAGGAGE_NOT_CREATED"; break; 
    case XP_JAVA_REMOVE_PRINCIPAL_ERROR:                   errortext = "XP_JAVA_REMOVE_PRINCIPAL_ERROR"; break; 
    case XP_JAVA_DELETE_PRIVILEGE_ERROR:                   errortext = "XP_JAVA_DELETE_PRIVILEGE_ERROR"; break; 
    case XP_JAVA_CERT_NOT_EXISTS_ERROR:                    errortext = "XP_JAVA_CERT_NOT_EXISTS_ERROR"; break; 
    case SEC_ERROR_BAD_EXPORT_ALGORITHM:                   errortext = "SEC_ERROR_BAD_EXPORT_ALGORITHM"; break; 
    case SEC_ERROR_EXPORTING_CERTIFICATES:                 errortext = "SEC_ERROR_EXPORTING_CERTIFICATES"; break; 
    case SEC_ERROR_IMPORTING_CERTIFICATES:                 errortext = "SEC_ERROR_IMPORTING_CERTIFICATES"; break; 
    case SEC_ERROR_PKCS12_DECODING_PFX:                    errortext = "SEC_ERROR_PKCS12_DECODING_PFX"; break; 
    case SEC_ERROR_PKCS12_INVALID_MAC:                     errortext = "SEC_ERROR_PKCS12_INVALID_MAC"; break; 
    case SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM:       errortext = "SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM"; break; 
    case SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE:      errortext = "SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE"; break; 
    case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:           errortext = "SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE"; break; 
    case SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM:       errortext = "SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM"; break; 
    case SEC_ERROR_PKCS12_UNSUPPORTED_VERSION:             errortext = "SEC_ERROR_PKCS12_UNSUPPORTED_VERSION"; break; 
    case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT:      errortext = "SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT"; break; 
    case SEC_ERROR_PKCS12_CERT_COLLISION:                  errortext = "SEC_ERROR_PKCS12_CERT_COLLISION"; break; 
    case SEC_ERROR_USER_CANCELLED:                         errortext = "SEC_ERROR_USER_CANCELLED"; break; 
    case SEC_ERROR_PKCS12_DUPLICATE_DATA:                  errortext = "SEC_ERROR_PKCS12_DUPLICATE_DATA"; break; 
    case SEC_ERROR_MESSAGE_SEND_ABORTED:                   errortext = "SEC_ERROR_MESSAGE_SEND_ABORTED"; break; 
    case SEC_ERROR_INADEQUATE_KEY_USAGE:                   errortext = "SEC_ERROR_INADEQUATE_KEY_USAGE"; break; 
    case SEC_ERROR_INADEQUATE_CERT_TYPE:                   errortext = "SEC_ERROR_INADEQUATE_CERT_TYPE"; break; 
    case SEC_ERROR_CERT_ADDR_MISMATCH:                     errortext = "SEC_ERROR_CERT_ADDR_MISMATCH"; break; 
    case SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY:            errortext = "SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY"; break; 
    case SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN:            errortext = "SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN"; break; 
    case SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME: errortext = "SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME"; break; 
    case SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY:            errortext = "SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY"; break; 
    case SEC_ERROR_PKCS12_UNABLE_TO_WRITE:                 errortext = "SEC_ERROR_PKCS12_UNABLE_TO_WRITE"; break; 
    case SEC_ERROR_PKCS12_UNABLE_TO_READ:                  errortext = "SEC_ERROR_PKCS12_UNABLE_TO_READ"; break; 
    case SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED:    errortext = "SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED"; break;
    case SEC_ERROR_KEYGEN_FAIL:                            errortext = "SEC_ERROR_KEYGEN_FAIL "; break;        
    case SEC_ERROR_INVALID_PASSWORD:                       errortext = "SEC_ERROR_INVALID_PASSWORD"; break;        
    case SEC_ERROR_RETRY_OLD_PASSWORD:                     errortext = "SEC_ERROR_RETRY_OLD_PASSWORD"; break;        
    case SEC_ERROR_BAD_NICKNAME:                           errortext = "SEC_ERROR_BAD_NICKNAME"; break;        
    case SEC_ERROR_NOT_FORTEZZA_ISSUER:                    errortext = "SEC_ERROR_NOT_FORTEZZA_ISSUER"; break;        
    case SEC_ERROR_CANNOT_MOVE_SENSITIVE_KEY:              errortext = "SEC_ERROR_CANNOT_MOVE_SENSITIVE_KEY"; break;
    case SEC_ERROR_JS_INVALID_MODULE_NAME:                 errortext = "SEC_ERROR_JS_INVALID_MODULE_NAME"; break; 
    case SEC_ERROR_JS_INVALID_DLL:                         errortext = "SEC_ERROR_JS_INVALID_DLL "; break;        
    case SEC_ERROR_JS_ADD_MOD_FAILURE:                     errortext = "SEC_ERROR_JS_ADD_MOD_FAILURE"; break;        
    case SEC_ERROR_JS_DEL_MOD_FAILURE:                     errortext = "SEC_ERROR_JS_DEL_MOD_FAILURE"; break;        
    case SEC_ERROR_OLD_KRL:                                errortext = "SEC_ERROR_OLD_KRL"; break;        
    case SEC_ERROR_CKL_CONFLICT:                           errortext = "SEC_ERROR_CKL_CONFLICT"; break;        
    case SEC_ERROR_CERT_NOT_IN_NAME_SPACE:                 errortext = "SEC_ERROR_CERT_NOT_IN_NAME_SPACE"; break; 
    case SEC_ERROR_KRL_NOT_YET_VALID:                      errortext = "SEC_ERROR_KRL_NOT_YET_VALID"; break; 
    case SEC_ERROR_CRL_NOT_YET_VALID:                      errortext = "SEC_ERROR_CRL_NOT_YET_VALID"; break; 
    case SEC_ERROR_UNKNOWN_CERT:                           errortext = "SEC_ERROR_UNKNOWN_CERT"; break;        
    case SEC_ERROR_UNKNOWN_SIGNER:                         errortext = "SEC_ERROR_UNKNOWN_SIGNER"; break;        
    case SEC_ERROR_CERT_BAD_ACCESS_LOCATION:               errortext = "SEC_ERROR_CERT_BAD_ACCESS_LOCATION"; break;        
    case SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE:             errortext = "SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE"; break; 
    case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE:                 errortext = "SEC_ERROR_OCSP_BAD_HTTP_RESPONSE"; break; 
    case SEC_ERROR_OCSP_MALFORMED_REQUEST:                 errortext = "SEC_ERROR_OCSP_MALFORMED_REQUEST"; break; 
    case SEC_ERROR_OCSP_SERVER_ERROR:                      errortext = "SEC_ERROR_OCSP_SERVER_ERROR"; break; 
    case SEC_ERROR_OCSP_TRY_SERVER_LATER:                  errortext = "SEC_ERROR_OCSP_TRY_SERVER_LATER"; break; 
    case SEC_ERROR_OCSP_REQUEST_NEEDS_SIG:                 errortext = "SEC_ERROR_OCSP_REQUEST_NEEDS_SIG "; break;        
    case SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST:              errortext = "SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST"; break; 
    case SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS:           errortext = "SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS"; break; 
    case SEC_ERROR_OCSP_UNKNOWN_CERT:                      errortext = "SEC_ERROR_OCSP_UNKNOWN_CERT"; break; 
    case SEC_ERROR_OCSP_NOT_ENABLED:                       errortext = "SEC_ERROR_OCSP_NOT_ENABLED"; break; 
    case SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER:              errortext = "SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER"; break; 
    case SEC_ERROR_OCSP_MALFORMED_RESPONSE:                errortext = "SEC_ERROR_OCSP_MALFORMED_RESPONSE"; break; 
    case SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE:             errortext = "SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE"; break; 
    case SEC_ERROR_OCSP_FUTURE_RESPONSE:                   errortext = "SEC_ERROR_OCSP_FUTURE_RESPONSE"; break; 
    case SEC_ERROR_OCSP_OLD_RESPONSE:                      errortext = "SEC_ERROR_OCSP_OLD_RESPONSE"; break; 
    case SEC_ERROR_DIGEST_NOT_FOUND:                       errortext = "SEC_ERROR_DIGEST_NOT_FOUND"; break;
    case SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE:               errortext = "SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE"; break;
    }
    
    if (errortext == NULL) {
	printf("sslsock00:error in function %s: %d\n", function, errorcode);
    } else {
	printf("sslsock00:error in function %s: %s (%d)\n", function, errortext,
		errorcode);
    }

    NSS_Shutdown();
    PR_Cleanup();
    exit(EXIT_FAILURE);
}

/*
 * This callback is used by SSL to get the client certificate when requested
 * by a server.
 */
SECStatus ownGetClientAuthData(void                        *arg,
                               PRFileDesc                  *socket,
                               struct CERTDistNamesStr     *caNames,
                               struct CERTCertificateStr  **pRetCert,
                               struct SECKEYPrivateKeyStr **pRetKey)
{
    CERTCertificate  *cert;
    SECKEYPrivateKey *privKey;
    char             *chosenNickName = (char *)arg;
    void             *proto_win = NULL;
    SECStatus         rv = SECFailure;

    proto_win = SSL_RevealPinArg(socket);

    if (chosenNickName) {
	cert = PK11_FindCertFromNickname(chosenNickName, proto_win);
	if (cert) {
	    privKey = PK11_FindKeyByAnyCert(cert, proto_win);
	    if (privKey) {
		rv = SECSuccess;
	    } else {
		CERT_DestroyCertificate(cert);
	    }
	}
    } else {

       	/* No nickname was given, automatically find the right cert */

	CERTCertNicknames *names;
	int i;

	names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(), 
		SEC_CERT_NICKNAMES_USER, proto_win);

	if (names != NULL) {
	    for (i=0; i < names->numnicknames; i++) {

		cert = PK11_FindCertFromNickname(names->nicknames[i],
			proto_win);

		if (cert == NULL) {
		    continue;
		}

		/* Only check unexpired certs */

		if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE)
			!= secCertTimeValid ) {
		    CERT_DestroyCertificate(cert);
		    continue;
		}

		rv = NSS_CmpCertChainWCANames(cert, caNames);

		if (rv == SECSuccess) {
		    privKey = PK11_FindKeyByAnyCert(cert, proto_win);
		    if (privKey) {
			break;
		    }
		    rv = SECFailure;
		    break;
		}

		CERT_FreeNicknames(names);
	    }
	}
    }

    if (rv == SECSuccess) {
	*pRetCert = cert;
	*pRetKey  = privKey;
    }

    return rv;
}

/*
 * Check the validity of a server certificate.
 */
SECStatus ownAuthCertificate(void       *arg,
                             PRFileDesc *socket,
                             PRBool      checksig,
                             PRBool      isServer)
{
    CERTCertificate *cert;
    void            *pinArg;
    char            *hostname;

    if (arg == NULL || socket == NULL) {
	return SECFailure;
    }

    if (isServer == PR_TRUE) {
        printf("sslsock00:isServer=PR_TRUE\n");
	return SECFailure;
    }

    cert   = SSL_PeerCertificate(socket);
    pinArg = SSL_RevealPinArg(socket);

    if (CERT_VerifyCertNow((CERTCertDBHandle *)arg, cert, checksig,
		certUsageSSLClient, pinArg) != SECSuccess) {
	return SECFailure;
    }

    /*
     * Certificate is OK. Since this is the client side of an SSL
     * connection, we need to verify that the name field in the cert
     * matches the desired hostname. This is our defense against
     * man-in-the-middle attacks.
     */

    /* SSL_RevealURL returns a hostname, not a URL. */

    hostname = SSL_RevealURL(socket);

    if (hostname == NULL) {
	return SECFailure;
    }

    return CERT_VerifyCertName(cert, hostname);
}

/*
 * This callback is used when the server certificate is not valid. 
 * It should return SECSuccess to accept the certificate anyway, SECFailure
 * to reject.
 */
SECStatus ownBadCertHandler(void *arg, PRFileDesc *socket) 
{
    /* Can log invalid certificate here. */
    printf("sslsock00:bad server certificate: %d\n", PR_GetError());

    /* This implementation always rejects a bad certificate. */
    return SECFailure;
}

/*
 * Called by SSL when retrieving own certificate from database. 
 * Returns the database password.
 */
char* ownPasswd(PK11SlotInfo *slot, PRBool retry, void *arg) 
{
    char       *passwd = NULL;
    static int  tries  = 0;

    if (!retry) tries = 0; /* starting over */

    if (++tries < 3) {
	char *tmp = NULL;
	while (!tmp && arg) {
	    /*tmp = getpass((char *)arg);*/
	    tmp = "certdb";
	}

	passwd = PL_strdup(tmp);
	memset(tmp, 0, strlen(tmp));
    }

    return passwd;
}

/*
 * Called by SSL to inform application that the handshake is complete.
 * This function is mostly used on the server side of an SSL connection,
 * although it is provided for a client as well.
 */
SECStatus ownHandshakeCallback(PRFileDesc *socket, void *arg) 
{
    return SECSuccess;
}

/*
 * This is a wrapper function around PR_Read that will read exactly
 * the number of bytes requested and return the number of bytes read.
 */
int readThisMany(PRFileDesc *fd, char *buffer, int thisMany) 
{
    int total = 0;
    int n;

    while (total < thisMany) {

	n = PR_Read(fd, buffer+total, thisMany-total);

	if (n <= 0) {
	    return n;
	}

	total += n;
    }

    return total;
}

/*
 * This is a wrapper function around PR_Write that will write exactly
 * the number of bytes requested and return the number of bytes written.
 */
int writeThisMany(PRFileDesc *fd, char *buffer, int thisMany) 
{
    int total = 0;
    int n;

    while (total < thisMany) {

	n = PR_Write(fd, buffer+total, thisMany-total);

	if (n < 0) {
	    if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
		break;
	    }
	    continue;
	}

	total += n;
    }

    return total;
}

/*
 * Client's main function.
 */
int main (int argc, char **argv) 
{
    char       *host = NULL;
    int         port = 8002;
    char       *certdir = "cert_db";
    char       *nickname = "client";
    char       *cipher = NULL;
    char       *trans = "SSL0";
    PRFileDesc *sock;
    PRNetAddr   addr;
    PRHostEnt   hostentry;
    char        hostbuf[256];
    int         n;
    int         c, errflg = 0;
    SECStatus   secstatus = SECFailure;
    PRStatus    prstatus = PR_FAILURE;
    char        ibuffer[4+1+35];  /* transid  4 bytes
                                     ','      1 byte
                                     data    35 bytes */
    char        buffer[BUFSIZE];

    char * data = {"12345678901234567890123456789012345678901234567890"};

    while ((c=getopt(argc, argv, "h:p:d:n:c:")) != -1) {
	switch (c) {
	    case 'h':
		host = optarg;
		break;
	    case 'p':
		port = atoi(optarg);
		break;
	    case 'd':
		certdir = optarg;
		break;
	    case 'n':
		nickname = optarg;
		break;
	    case 'c':
		cipher = optarg;
		break;
	    default:
		errflg++;
		break;        
	}
    }

    if (host == NULL || port == 0 || certdir == NULL || errflg) {
	usage(argv[0]);
    }

    /* ignore SIGPIPE signals */
    signal(SIGPIPE, SIG_IGN);

    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1);      

    /* Do libsec initialization */

    PK11_SetPasswordFunc(ownPasswd);  /* Set the passwd callback */

    secstatus = NSS_Init(certdir);
    if (secstatus != SECSuccess) {
	exitErr("NSS_Init");
    }

    /* All the ciphers except SSL_RSA_WITH_NULL_MD5 are on by default. */

    secstatus = NSS_SetDomesticPolicy();
    if (secstatus != SECSuccess ) {
	exitErr("NSS_SetDomesticPolicy");
    }

    /* If a cipher suite was specified use it exclusively. */

    if (cipher) {
	SSL_CipherPrefSetDefault(SSL_EN_RC4_128_WITH_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_EN_RC4_128_EXPORT40_WITH_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_EN_RC2_128_CBC_WITH_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_EN_DES_64_CBC_WITH_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_EN_DES_192_EDE3_CBC_WITH_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_RSA_EXPORT_WITH_RC4_40_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_RSA_WITH_RC4_128_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_RSA_WITH_DES_CBC_SHA, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_RSA_WITH_3DES_EDE_CBC_SHA, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_FORTEZZA_DMS_WITH_NULL_SHA, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, PR_FALSE);
	SSL_CipherPrefSetDefault(SSL_RSA_FIPS_WITH_DES_CBC_SHA, PR_FALSE);
	SSL_CipherPrefSetDefault(TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, PR_FALSE);
	SSL_CipherPrefSetDefault(TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, PR_FALSE);

	if (strcmp(cipher, "SSL_EN_RC4_128_WITH_MD5") == 0) {
	    printf("sslsock00:using SSL_EN_RC4_128_WITH_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_EN_RC4_128_WITH_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_EN_RC4_128_EXPORT40_WITH_MD5") == 0) {
	    printf("sslsock00:using SSL_EN_RC4_128_EXPORT40_WITH_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_EN_RC4_128_EXPORT40_WITH_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_EN_RC2_128_CBC_WITH_MD5") == 0) {
	    printf("sslsock00:using SSL_EN_RC2_128_CBC_WITH_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_EN_RC2_128_CBC_WITH_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5") == 0) {
	    printf("sslsock00:using SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_EN_DES_64_CBC_WITH_MD5") == 0) {
	    printf("sslsock00:using SSL_EN_DES_64_CBC_WITH_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_EN_DES_64_CBC_WITH_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_EN_DES_192_EDE3_CBC_WITH_MD5") == 0) {
	    printf("sslsock00:using SSL_EN_DES_192_EDE3_CBC_WITH_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_EN_DES_192_EDE3_CBC_WITH_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_RSA_WITH_NULL_MD5") == 0) {
	    printf("sslsock00:using SSL_RSA_WITH_NULL_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_RSA_WITH_NULL_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_RSA_EXPORT_WITH_RC4_40_MD5") == 0) {
	    printf("sslsock00:using SSL_RSA_EXPORT_WITH_RC4_40_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_RSA_EXPORT_WITH_RC4_40_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_RSA_WITH_RC4_128_MD5") == 0) {
	    printf("sslsock00:using SSL_RSA_WITH_RC4_128_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_RSA_WITH_RC4_128_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5") == 0) {
	    printf("sslsock00:using SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5\n");
	    SSL_CipherPrefSetDefault(SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_RSA_WITH_DES_CBC_SHA") == 0) {
	    printf("sslsock00:using SSL_RSA_WITH_DES_CBC_SHA\n");
	    SSL_CipherPrefSetDefault(SSL_RSA_WITH_DES_CBC_SHA, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_RSA_WITH_3DES_EDE_CBC_SHA") == 0) {
	    printf("sslsock00:using SSL_RSA_WITH_3DES_EDE_CBC_SHA\n");
	    SSL_CipherPrefSetDefault(SSL_RSA_WITH_3DES_EDE_CBC_SHA, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_FORTEZZA_DMS_WITH_NULL_SHA") == 0) {
	    printf("sslsock00:using SSL_FORTEZZA_DMS_WITH_NULL_SHA\n");
	    SSL_CipherPrefSetDefault(SSL_FORTEZZA_DMS_WITH_NULL_SHA, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA") == 0) {
	    printf("sslsock00:using SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA\n");
	    SSL_CipherPrefSetDefault(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_FORTEZZA_DMS_WITH_RC4_128_SHA") == 0) {
	    printf("sslsock00:using SSL_FORTEZZA_DMS_WITH_RC4_128_SHA\n");
	    SSL_CipherPrefSetDefault(SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA") == 0) {
	    printf("sslsock00:using SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA\n");
	    SSL_CipherPrefSetDefault(SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, PR_TRUE);
	} else
	if (strcmp(cipher, "SSL_RSA_FIPS_WITH_DES_CBC_SHA") == 0) {
	    printf("sslsock00:using SSL_RSA_FIPS_WITH_DES_CBC_SHA\n");
	    SSL_CipherPrefSetDefault(SSL_RSA_FIPS_WITH_DES_CBC_SHA, PR_TRUE);
	} else
	if (strcmp(cipher, "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA") == 0) {
	    printf("sslsock00:using TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA\n");
	    SSL_CipherPrefSetDefault(TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, PR_TRUE);
	} else
	if (strcmp(cipher, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA") == 0) {
	    printf("sslsock00:using TLS_RSA_EXPORT1024_WITH_RC4_56_SHA\n");
	    SSL_CipherPrefSetDefault(TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, PR_TRUE);
	}
    }

    /* set up SSL secure socket */
    sock = setup_socket(nickname);
    if (sock == NULL ) {
	exitErr("setup_socket");
    }

    printf("sslsock00:socket opened\n");

    secstatus = SSL_SetPKCS11PinArg(sock, "Enter database password: ");
    if (secstatus != SECSuccess) {
	exitErr("SSL_SetPKCS11PinArg");
    }

    secstatus = SSL_SetURL(sock, host);
    if (secstatus != SECSuccess) {
	exitErr("SSL_SetURL");
    }

    if (host == NULL || strcmp(host, "any") == 0) {
	addr.inet.ip     = htonl(INADDR_ANY);
	addr.inet.family = AF_INET;
	addr.inet.port   = htons(port);
    } else if (strcmp(host, "loopback") == 0) {
	addr.inet.ip     = htonl(INADDR_LOOPBACK);
	addr.inet.family = AF_INET;
	addr.inet.port   = htons(port);
    } else {
	/* prepare and setup network connection */
	prstatus = PR_GetHostByName(host, hostbuf, sizeof(hostbuf), &hostentry);
	if (prstatus != PR_SUCCESS) {
	    exitErr("PR_GetHostByName");
	}

	if (PR_EnumerateHostEnt(0, &hostentry, port, &addr)<0) {
	    exitErr("PR_EnumerateHostEnt");
	}
    }

    prstatus = PR_Connect(sock, &addr, PR_INTERVAL_NO_TIMEOUT);
    if (prstatus == PR_FAILURE) {
	exitErr("PR_Connect");  
    }

    printf("sslsock00:socket connected\n");

    /* established SSL connection, ready to send data */
    secstatus = SSL_ForceHandshake(sock); 
    if (secstatus != SECSuccess) {
	exitErr("SSL_ForceHandshake");
    }

    printf("sslsock00:handshake complete\n");

    /* build initial socket message */
    memset(ibuffer, 0, sizeof(ibuffer));
    memcpy(ibuffer,   trans,  4);
    memcpy(ibuffer+4, ",",    1);
    memcpy(ibuffer+5, data,  35);

    /* Initial send to invoke transaction */
    if (PR_Write(sock, ibuffer, sizeof(ibuffer)) <= 0) {
	exitErr("PR_Write");
    }

    printf("sslsock00:transaction %4.4s\n", trans);

    while (1) {

	memset(buffer, 0, sizeof(buffer));

	if ((n=readThisMany(sock, buffer, sizeof(buffer))) < 0) {
	    exitErr("PR_Read");
	}

	if (n == 0) {
	    printf("sslsock00:socket disconnected\n");
	    break;
	}

	printf("sslsock00:read %d chars\n", n);

	if ((n=writeThisMany(sock, (char *)buffer, n)) <= 0) {
	    exitErr("PR_Write");
	}

	printf("sslsock00:sent %d chars\n", n);
    }

    PR_Close(sock);

    printf("sslsock00:socket closed\n");

    /* now, close down SSL */
    NSS_Shutdown();
    PR_Cleanup();

    printf("sslsock00:done\n");

    return EXIT_SUCCESS;
}
