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

/*****************************************************************************
*
* Prog Name:    sslsock.c
*
* Description:
*
*    SSL Socket Sample Program - send and receive. This TP is partnered
*    with transaction SSL0, initial program SSLSOCK0, source SSLSOCK0.cl2
*
*    This program opens a socket and connects to <host><port>. It
*    invokes the transaction by sending a message to the socket 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 at the Sun MTP system.
*
*    Each message received is sent back until the socket is closed
*    by the remote transaction.
*
* Usage:
*
*       sslsock00 -h host [-p port] -d certdir [-n nickname] [-c cipher]
*
*       where,
*
*       -h host        identifies the socket Server host.
*       -p port        identifies the socket Server port.
*       -d certdir     identifies the client certificate database.
*       -n nickname    identifies the client certificate nickname.
*       -c cipher      identifies the cipher suite to use.
*
* Change Log:
*
*****************************************************************************/

/* Platform specific header files */

#ifdef SOLARIS
#include <signal.h>
#endif

#ifdef XP_UNIX
#include <sys/types.h>
#include <netinet/in.h>
#endif

/* Generic header files */

#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    /* size of message buffer */

/* Function Prototypes */

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);

/* Function implementation */

/* Stubs required for Solaris 2.5 */
#if 0
void mmap64(){}
void stat64(){}
void open64(){}
void lseek64(){}
void fstat64(){}
#endif

/* Function: void usage()
 *
 * Purpose: Upon a bogus invocation of the program, print the usage information
 * and exit with a nonzero status.
 */

void usage(char *progname) {

    printf("Usage: %s -h host [-p port] -d certdir [-n nickname] [-c cipher]\n\n", progname);
    exit(1);
}

/* Function: PRFileDesc * setup_socket()
 *
 * Purpose: setup_socket takes a char *, creates a new socket, configures it
 * for SSL, and returns the address of the new socket. The nickname parameter
 * is used in the setting of callback functions.
 */

PRFileDesc * setup_socket(char *nickname) {

	PRFileDesc *		socket = NULL;
	PRFileDesc *		sslsocket = NULL;
	PRSocketOptionData	sockdata;

	/* Create a new socket */

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

	/* Ensure that the socking is blocking. The default on some platforms
 	 * is non blocking
	 */

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

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

	/* Import this socket into the SSL layer */

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

	/* Set some flags on this new SSL socket */

	if ((SSL_Enable(sslsocket, SSL_SECURITY, 1)) != SECSuccess ) {
		exitErr("SSL_Enable:SSL_SECURITY");
	}

	if ((SSL_Enable(sslsocket, SSL_HANDSHAKE_AS_CLIENT, 1)) != SECSuccess) {
		exitErr("SSL_Enable:SSL_HANDSHAKE_AS_CLIENT");
	}

	/* Set the desired callbacks. */

	if (nickname) {
	    /* Only set the certificate callback if we have a nickname. */
	    if ((SSL_GetClientAuthDataHook(	sslsocket,
		    (SSLGetClientAuthData)	ownGetClientAuthData,
		    (void *)			nickname))
		!= SECSuccess) {

		exitErr("SSL_GetClientAuthDataHook");
	    }
	}

	if (	(SSL_AuthCertificateHook(	sslsocket,
		(SSLAuthCertificate) 		ownAuthCertificate,
		(void *)			CERT_GetDefaultCertDB()))
		!= SECSuccess ) {

		exitErr("SSL_AuthCertificateHook");
	}

	if (	(SSL_BadCertHook(	sslsocket,
		(SSLBadCertHandler) 	ownBadCertHandler,
					NULL))
		!= SECSuccess ) {

		exitErr("SSL_BadCertHook");
	}

	if (	(SSL_HandshakeCallback(	sslsocket,
		(SSLHandshakeCallback) 	ownHandshakeCallback,
					NULL))
		!= SECSuccess ) {

		exitErr("SSL_HandshakeCallback");
	}

	/* Return the address of the configured socket */

	return sslsocket;
}

/* Function void exitErr()
 *
 * Purpose: When a security function returns an error, get the error number
 * using PR_GetError() and find a matching error string using the custom
 * function SSL_Strerror(). Print out all relevant information, shutdown 
 * NSS, clean up NSPR, and then exit with a nonzero status.
 *
 * Note: SSL_Strerror is defined in sslerror.h.
 */

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_RESERVED_ERROR_5935: 				errortext = "PR_RESERVED_ERROR_5935"; 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 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_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; 
    }

    if (errortext == NULL) {
	printf("Error in function %s: %d\n", function, errorcode);
    } else {
	printf("Error in function %s: %s (%d)\n",
	       	function, errortext, errorcode);
    }

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

/* Function: SECStatus ownGetClientAuthData()
 *
 * Purpose: This callback is used by SSL to pull client certificate 
 * information upon server request.
 */

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 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) {
				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);
			} /* for loop */
		}
	}

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

	return rv;
}

/* Function: SECStatus ownAuthCertificate()
 *
 * Purpose: This function checks the validity of an incoming certificate.
 */

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

	SECCertUsage		certUsage;
	CERTCertificate *	cert;
	void *			pinArg;
	char *			hostname;
	SECStatus		rv = SECFailure;

	if (!arg || !socket) {
		return rv;
	}

	certUsage = isServer ? certUsageSSLClient : certUsageSSLServer;

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

	rv = CERT_VerifyCertNow((CERTCertDBHandle*)arg,
				 cert,
				 checksig,
				 certUsage,
				 pinArg);

	if ( (rv != SECSuccess) || isServer ) {
		return rv;
	}
	
	/* 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 && hostname[0]) {
		rv = CERT_VerifyCertName(cert, hostname);
	} else {
		rv = SECFailure;
	}

	return rv;
}

/* Function: SECStatus ownBadCertHandler()
 *
 * Purpose: This callback is used when the incoming cert is not valid. 
 * It should return SECSuccess to accept the cert anyway, SECFailure
 * to reject.
 */

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

	/* Can log invalid cert here. */
	printf("Bad server certificate: %d\n", PR_GetError());

	/* Note here that this particular callback implementation always
	 * rejects a bad cert.
	 */
	return SECFailure;
}

/* Function: char * ownPasswd()
 *
 * Purpose: Called by SSL when retrieving own cert from database. Returns
 * pointer to string with a password for the database.
 */

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 = "clientdb";
		}
	
		passwd = PL_strdup(tmp);
		memset(tmp, 0, strlen(tmp));
	}

	return passwd;
}

/* Function: SECStatus ownHandshakeCallback()
 *
 * Purpose: 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) {

	printf("Handshake has completed, ready to send data securely.\n");
	return SECSuccess;
}

/* Function: int readThisMany()
 *
 * Purpose: 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;

	while ( total < thisMany ) {

		int	got;	

		got = PR_Read(fd, buffer+total, thisMany-total);
		if ( got <= 0 ) {
		    return got;
		}

		total += got;
	}

	return total;
}

/* Function: int writeThisMany()
 *
 * Purpose: 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;

	while ( total < thisMany ) {

		int	got;	

		got = PR_Write(fd, buffer+total, thisMany-total);
		if ( got < 0 ) {
			if ( PR_GetError() != PR_WOULD_BLOCK_ERROR ) {
				break;
			}
		continue;
		}

		total += got;
	}

	return total;
}

/* Function: int main()
 *
 * Purpose: This is the client's main function.
 */

int main (int argc, char **argv) {

	char * 		host = NULL;
	int 		port = 8002;
	char * 		certdir = NULL;
	char * 		nickname = NULL;
	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"};

	signal(SIGPIPE, SIG_IGN);

	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 || certdir == NULL || errflg) {
		usage(argv[0]);
	}
  
	PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1);      

	 /* Do the libsec initialization */
	PK11_SetPasswordFunc(ownPasswd);  /* set 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 (cipher) {
	    /*
	     * If a cipher suite was specified use it exclusively.
	     */
	    SSL_EnableCipher(SSL_EN_RC4_128_WITH_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_EN_RC4_128_EXPORT40_WITH_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_EN_RC2_128_CBC_WITH_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_EN_DES_64_CBC_WITH_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_EN_DES_192_EDE3_CBC_WITH_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_RSA_WITH_NULL_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_RSA_EXPORT_WITH_RC4_40_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_RSA_WITH_RC4_128_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, PR_FALSE);
	    SSL_EnableCipher(SSL_RSA_WITH_DES_CBC_SHA, PR_FALSE);
	    SSL_EnableCipher(SSL_RSA_WITH_3DES_EDE_CBC_SHA, PR_FALSE);
	    SSL_EnableCipher(SSL_FORTEZZA_DMS_WITH_NULL_SHA, PR_FALSE);
	    SSL_EnableCipher(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, PR_FALSE);
	    SSL_EnableCipher(SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, PR_FALSE);
	    SSL_EnableCipher(SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, PR_FALSE);
	    SSL_EnableCipher(SSL_RSA_FIPS_WITH_DES_CBC_SHA, PR_FALSE);
	    SSL_EnableCipher(TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, PR_FALSE);
	    SSL_EnableCipher(TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, PR_FALSE);

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

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

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

	/* 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");  
	}
  
	/* established SSL connection, ready to send data */
	secstatus = SSL_ForceHandshake(sock); 
	if (secstatus != SECSuccess) {
		exitErr("SSL_ForceHandshake");
	}

	/* 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");
	}

	while (1) {

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

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

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

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

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

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

	printf("sslsock00:done, exiting...\n");
	/* now, close down SSL */
	PR_Close(sock);
	NSS_Shutdown();
	PR_Cleanup();
	return EXIT_SUCCESS;
}
