michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include michael@0: #include michael@0: michael@0: #include "secutil.h" michael@0: #include "basicutil.h" michael@0: michael@0: #if defined(XP_UNIX) michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "plgetopt.h" michael@0: michael@0: #include "nspr.h" michael@0: #include "prio.h" michael@0: #include "prnetdb.h" michael@0: #include "prerror.h" michael@0: michael@0: #include "pk11func.h" michael@0: #include "secitem.h" michael@0: #include "sslproto.h" michael@0: #include "nss.h" michael@0: #include "ssl.h" michael@0: michael@0: #ifndef PORT_Sprintf michael@0: #define PORT_Sprintf sprintf michael@0: #endif michael@0: michael@0: #ifndef PORT_Strstr michael@0: #define PORT_Strstr strstr michael@0: #endif michael@0: michael@0: #ifndef PORT_Malloc michael@0: #define PORT_Malloc PR_Malloc michael@0: #endif michael@0: michael@0: #define RD_BUF_SIZE (60 * 1024) michael@0: michael@0: /* Include these cipher suite arrays to re-use tstclnt's michael@0: * cipher selection code. michael@0: */ michael@0: michael@0: int ssl2CipherSuites[] = { michael@0: SSL_EN_RC4_128_WITH_MD5, /* A */ michael@0: SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ michael@0: SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ michael@0: SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ michael@0: SSL_EN_DES_64_CBC_WITH_MD5, /* E */ michael@0: SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ michael@0: 0 michael@0: }; michael@0: michael@0: int ssl3CipherSuites[] = { michael@0: -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */ michael@0: -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA * b */ michael@0: TLS_RSA_WITH_RC4_128_MD5, /* c */ michael@0: TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ michael@0: TLS_RSA_WITH_DES_CBC_SHA, /* e */ michael@0: TLS_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ michael@0: TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ michael@0: -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA * h */ michael@0: TLS_RSA_WITH_NULL_MD5, /* i */ michael@0: SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ michael@0: SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ michael@0: TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ michael@0: TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ michael@0: TLS_RSA_WITH_RC4_128_SHA, /* n */ michael@0: TLS_DHE_DSS_WITH_RC4_128_SHA, /* o */ michael@0: TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* p */ michael@0: TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* q */ michael@0: TLS_DHE_RSA_WITH_DES_CBC_SHA, /* r */ michael@0: TLS_DHE_DSS_WITH_DES_CBC_SHA, /* s */ michael@0: TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* t */ michael@0: TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* u */ michael@0: TLS_RSA_WITH_AES_128_CBC_SHA, /* v */ michael@0: TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* w */ michael@0: TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* x */ michael@0: TLS_RSA_WITH_AES_256_CBC_SHA, /* y */ michael@0: TLS_RSA_WITH_NULL_SHA, /* z */ michael@0: 0 michael@0: }; michael@0: michael@0: #define NO_FULLHS_PERCENTAGE -1 michael@0: michael@0: /* This global string is so that client main can see michael@0: * which ciphers to use. michael@0: */ michael@0: michael@0: static const char *cipherString; michael@0: michael@0: static PRInt32 certsTested; michael@0: static int MakeCertOK; michael@0: static int NoReuse; michael@0: static int fullhs = NO_FULLHS_PERCENTAGE; /* percentage of full handshakes to michael@0: ** perform */ michael@0: static PRInt32 globalconid = 0; /* atomically set */ michael@0: static int total_connections; /* total number of connections to perform */ michael@0: static int total_connections_rounded_down_to_hundreds; michael@0: static int total_connections_modulo_100; michael@0: michael@0: static PRBool NoDelay; michael@0: static PRBool QuitOnTimeout = PR_FALSE; michael@0: static PRBool ThrottleUp = PR_FALSE; michael@0: michael@0: static PRLock * threadLock; /* protects the global variables below */ michael@0: static PRTime lastConnectFailure; michael@0: static PRTime lastConnectSuccess; michael@0: static PRTime lastThrottleUp; michael@0: static PRInt32 remaining_connections; /* number of connections left */ michael@0: static int active_threads = 8; /* number of threads currently trying to michael@0: ** connect */ michael@0: static PRInt32 numUsed; michael@0: /* end of variables protected by threadLock */ michael@0: michael@0: static SSL3Statistics * ssl3stats; michael@0: michael@0: static int failed_already = 0; michael@0: static SSLVersionRange enabledVersions; michael@0: static PRBool enableSSL2 = PR_TRUE; michael@0: static PRBool bypassPKCS11 = PR_FALSE; michael@0: static PRBool disableLocking = PR_FALSE; michael@0: static PRBool ignoreErrors = PR_FALSE; michael@0: static PRBool enableSessionTickets = PR_FALSE; michael@0: static PRBool enableCompression = PR_FALSE; michael@0: static PRBool enableFalseStart = PR_FALSE; michael@0: static PRBool enableCertStatus = PR_FALSE; michael@0: michael@0: PRIntervalTime maxInterval = PR_INTERVAL_NO_TIMEOUT; michael@0: michael@0: char * progName; michael@0: michael@0: secuPWData pwdata = { PW_NONE, 0 }; michael@0: michael@0: int stopping; michael@0: int verbose; michael@0: SECItem bigBuf; michael@0: michael@0: #define PRINTF if (verbose) printf michael@0: #define FPRINTF if (verbose) fprintf michael@0: michael@0: static void michael@0: Usage(const char *progName) michael@0: { michael@0: fprintf(stderr, michael@0: "Usage: %s [-n nickname] [-p port] [-d dbdir] [-c connections]\n" michael@0: " [-BDNovqs] [-f filename] [-N | -P percentage]\n" michael@0: " [-w dbpasswd] [-C cipher(s)] [-t threads] [-W pwfile]\n" michael@0: " [-V [min-version]:[max-version]] [-a sniHostName] hostname\n" michael@0: " where -v means verbose\n" michael@0: " -o flag is interpreted as follows:\n" michael@0: " 1 -o means override the result of server certificate validation.\n" michael@0: " 2 -o's mean skip server certificate validation altogether.\n" michael@0: " -D means no TCP delays\n" michael@0: " -q means quit when server gone (timeout rather than retry forever)\n" michael@0: " -s means disable SSL socket locking\n" michael@0: " -N means no session reuse\n" michael@0: " -P means do a specified percentage of full handshakes (0-100)\n" michael@0: " -V [min]:[max] restricts the set of enabled SSL/TLS protocols versions.\n" michael@0: " All versions are enabled by default.\n" michael@0: " Possible values for min/max: ssl2 ssl3 tls1.0 tls1.1 tls1.2\n" michael@0: " Example: \"-V ssl3:\" enables SSL 3 and newer.\n" michael@0: " -U means enable throttling up threads\n" michael@0: " -B bypasses the PKCS11 layer for SSL encryption and MACing\n" michael@0: " -T enable the cert_status extension (OCSP stapling)\n" michael@0: " -u enable TLS Session Ticket extension\n" michael@0: " -z enable compression\n" michael@0: " -g enable false start\n", michael@0: progName); michael@0: exit(1); michael@0: } michael@0: michael@0: michael@0: static void michael@0: errWarn(char * funcString) michael@0: { michael@0: PRErrorCode perr = PR_GetError(); michael@0: PRInt32 oserr = PR_GetOSError(); michael@0: const char * errString = SECU_Strerror(perr); michael@0: michael@0: fprintf(stderr, "strsclnt: %s returned error %d, OS error %d: %s\n", michael@0: funcString, perr, oserr, errString); michael@0: } michael@0: michael@0: static void michael@0: errExit(char * funcString) michael@0: { michael@0: errWarn(funcString); michael@0: exit(1); michael@0: } michael@0: michael@0: /************************************************************************** michael@0: ** michael@0: ** Routines for disabling SSL ciphers. michael@0: ** michael@0: **************************************************************************/ michael@0: michael@0: void michael@0: disableAllSSLCiphers(void) michael@0: { michael@0: const PRUint16 *cipherSuites = SSL_GetImplementedCiphers(); michael@0: int i = SSL_GetNumImplementedCiphers(); michael@0: SECStatus rv; michael@0: michael@0: /* disable all the SSL3 cipher suites */ michael@0: while (--i >= 0) { michael@0: PRUint16 suite = cipherSuites[i]; michael@0: rv = SSL_CipherPrefSetDefault(suite, PR_FALSE); michael@0: if (rv != SECSuccess) { michael@0: printf("SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n", michael@0: suite, i); michael@0: errWarn("SSL_CipherPrefSetDefault"); michael@0: exit(2); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* This invokes the "default" AuthCert handler in libssl. michael@0: ** The only reason to use this one is that it prints out info as it goes. michael@0: */ michael@0: static SECStatus michael@0: mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, michael@0: PRBool isServer) michael@0: { michael@0: SECStatus rv; michael@0: CERTCertificate * peerCert; michael@0: const SECItemArray *csa; michael@0: michael@0: if (MakeCertOK>=2) { michael@0: return SECSuccess; michael@0: } michael@0: peerCert = SSL_PeerCertificate(fd); michael@0: michael@0: PRINTF("strsclnt: Subject: %s\nstrsclnt: Issuer : %s\n", michael@0: peerCert->subjectName, peerCert->issuerName); michael@0: csa = SSL_PeerStapledOCSPResponses(fd); michael@0: if (csa) { michael@0: PRINTF("Received %d Cert Status items (OCSP stapled data)\n", michael@0: csa->len); michael@0: } michael@0: /* invoke the "default" AuthCert handler. */ michael@0: rv = SSL_AuthCertificate(arg, fd, checkSig, isServer); michael@0: michael@0: PR_ATOMIC_INCREMENT(&certsTested); michael@0: if (rv == SECSuccess) { michael@0: fputs("strsclnt: -- SSL: Server Certificate Validated.\n", stderr); michael@0: } michael@0: CERT_DestroyCertificate(peerCert); michael@0: /* error, if any, will be displayed by the Bad Cert Handler. */ michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: myBadCertHandler( void *arg, PRFileDesc *fd) michael@0: { michael@0: PRErrorCode err = PR_GetError(); michael@0: if (!MakeCertOK) michael@0: fprintf(stderr, michael@0: "strsclnt: -- SSL: Server Certificate Invalid, err %d.\n%s\n", michael@0: err, SECU_Strerror(err)); michael@0: return (MakeCertOK ? SECSuccess : SECFailure); michael@0: } michael@0: michael@0: void michael@0: printSecurityInfo(PRFileDesc *fd) michael@0: { michael@0: CERTCertificate * cert = NULL; michael@0: SSL3Statistics * ssl3stats = SSL_GetStatistics(); michael@0: SECStatus result; michael@0: SSLChannelInfo channel; michael@0: SSLCipherSuiteInfo suite; michael@0: michael@0: static int only_once; michael@0: michael@0: if (only_once && verbose < 2) michael@0: return; michael@0: only_once = 1; michael@0: michael@0: result = SSL_GetChannelInfo(fd, &channel, sizeof channel); michael@0: if (result == SECSuccess && michael@0: channel.length == sizeof channel && michael@0: channel.cipherSuite) { michael@0: result = SSL_GetCipherSuiteInfo(channel.cipherSuite, michael@0: &suite, sizeof suite); michael@0: if (result == SECSuccess) { michael@0: FPRINTF(stderr, michael@0: "strsclnt: SSL version %d.%d using %d-bit %s with %d-bit %s MAC\n", michael@0: channel.protocolVersion >> 8, channel.protocolVersion & 0xff, michael@0: suite.effectiveKeyBits, suite.symCipherName, michael@0: suite.macBits, suite.macAlgorithmName); michael@0: FPRINTF(stderr, michael@0: "strsclnt: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" michael@0: " Compression: %s\n", michael@0: channel.authKeyBits, suite.authAlgorithmName, michael@0: channel.keaKeyBits, suite.keaTypeName, michael@0: channel.compressionMethodName); michael@0: } michael@0: } michael@0: michael@0: cert = SSL_LocalCertificate(fd); michael@0: if (!cert) michael@0: cert = SSL_PeerCertificate(fd); michael@0: michael@0: if (verbose && cert) { michael@0: char * ip = CERT_NameToAscii(&cert->issuer); michael@0: char * sp = CERT_NameToAscii(&cert->subject); michael@0: if (sp) { michael@0: fprintf(stderr, "strsclnt: subject DN: %s\n", sp); michael@0: PORT_Free(sp); michael@0: } michael@0: if (ip) { michael@0: fprintf(stderr, "strsclnt: issuer DN: %s\n", ip); michael@0: PORT_Free(ip); michael@0: } michael@0: } michael@0: if (cert) { michael@0: CERT_DestroyCertificate(cert); michael@0: cert = NULL; michael@0: } michael@0: fprintf(stderr, michael@0: "strsclnt: %ld cache hits; %ld cache misses, %ld cache not reusable\n" michael@0: " %ld stateless resumes\n", michael@0: ssl3stats->hsh_sid_cache_hits, michael@0: ssl3stats->hsh_sid_cache_misses, michael@0: ssl3stats->hsh_sid_cache_not_ok, michael@0: ssl3stats->hsh_sid_stateless_resumes); michael@0: michael@0: } michael@0: michael@0: /************************************************************************** michael@0: ** Begin thread management routines and data. michael@0: **************************************************************************/ michael@0: michael@0: #define MAX_THREADS 128 michael@0: michael@0: typedef int startFn(void *a, void *b, int c); michael@0: michael@0: michael@0: static PRInt32 numConnected; michael@0: static int max_threads; /* peak threads allowed */ michael@0: michael@0: typedef struct perThreadStr { michael@0: void * a; michael@0: void * b; michael@0: int tid; michael@0: int rv; michael@0: startFn * startFunc; michael@0: PRThread * prThread; michael@0: PRBool inUse; michael@0: } perThread; michael@0: michael@0: perThread threads[MAX_THREADS]; michael@0: michael@0: void michael@0: thread_wrapper(void * arg) michael@0: { michael@0: perThread * slot = (perThread *)arg; michael@0: PRBool done = PR_FALSE; michael@0: michael@0: do { michael@0: PRBool doop = PR_FALSE; michael@0: PRBool dosleep = PR_FALSE; michael@0: PRTime now = PR_Now(); michael@0: michael@0: PR_Lock(threadLock); michael@0: if (! (slot->tid < active_threads)) { michael@0: /* this thread isn't supposed to be running */ michael@0: if (!ThrottleUp) { michael@0: /* we'll never need this thread again, so abort it */ michael@0: done = PR_TRUE; michael@0: } else if (remaining_connections > 0) { michael@0: /* we may still need this thread, so just sleep for 1s */ michael@0: dosleep = PR_TRUE; michael@0: /* the conditions to trigger a throttle up are : michael@0: ** 1. last PR_Connect failure must have happened more than michael@0: ** 10s ago michael@0: ** 2. last throttling up must have happened more than 0.5s ago michael@0: ** 3. there must be a more recent PR_Connect success than michael@0: ** failure michael@0: */ michael@0: if ( (now - lastConnectFailure > 10 * PR_USEC_PER_SEC) && michael@0: ( (!lastThrottleUp) || ( (now - lastThrottleUp) >= michael@0: (PR_USEC_PER_SEC/2)) ) && michael@0: (lastConnectSuccess > lastConnectFailure) ) { michael@0: /* try throttling up by one thread */ michael@0: active_threads = PR_MIN(max_threads, active_threads+1); michael@0: fprintf(stderr,"active_threads set up to %d\n", michael@0: active_threads); michael@0: lastThrottleUp = PR_MAX(now, lastThrottleUp); michael@0: } michael@0: } else { michael@0: /* no more connections left, we are done */ michael@0: done = PR_TRUE; michael@0: } michael@0: } else { michael@0: /* this thread should run */ michael@0: if (--remaining_connections >= 0) { /* protected by threadLock */ michael@0: doop = PR_TRUE; michael@0: } else { michael@0: done = PR_TRUE; michael@0: } michael@0: } michael@0: PR_Unlock(threadLock); michael@0: if (doop) { michael@0: slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->tid); michael@0: PRINTF("strsclnt: Thread in slot %d returned %d\n", michael@0: slot->tid, slot->rv); michael@0: } michael@0: if (dosleep) { michael@0: PR_Sleep(PR_SecondsToInterval(1)); michael@0: } michael@0: } while (!done && (!failed_already || ignoreErrors)); michael@0: } michael@0: michael@0: SECStatus michael@0: launch_thread( michael@0: startFn * startFunc, michael@0: void * a, michael@0: void * b, michael@0: int tid) michael@0: { michael@0: PRUint32 i; michael@0: perThread * slot; michael@0: michael@0: PR_Lock(threadLock); michael@0: michael@0: PORT_Assert(numUsed < MAX_THREADS); michael@0: if (! (numUsed < MAX_THREADS)) { michael@0: PR_Unlock(threadLock); michael@0: return SECFailure; michael@0: } michael@0: michael@0: i = numUsed++; michael@0: slot = &threads[i]; michael@0: slot->a = a; michael@0: slot->b = b; michael@0: slot->tid = tid; michael@0: michael@0: slot->startFunc = startFunc; michael@0: michael@0: slot->prThread = PR_CreateThread(PR_USER_THREAD, michael@0: thread_wrapper, slot, michael@0: PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, michael@0: PR_JOINABLE_THREAD, 0); michael@0: if (slot->prThread == NULL) { michael@0: PR_Unlock(threadLock); michael@0: printf("strsclnt: Failed to launch thread!\n"); michael@0: return SECFailure; michael@0: } michael@0: michael@0: slot->inUse = 1; michael@0: PR_Unlock(threadLock); michael@0: PRINTF("strsclnt: Launched thread in slot %d \n", i); michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* join all the threads */ michael@0: int michael@0: reap_threads(void) michael@0: { michael@0: int i; michael@0: michael@0: for (i = 0; i < MAX_THREADS; ++i) { michael@0: if (threads[i].prThread) { michael@0: PR_JoinThread(threads[i].prThread); michael@0: threads[i].prThread = NULL; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: void michael@0: destroy_thread_data(void) michael@0: { michael@0: PORT_Memset(threads, 0, sizeof threads); michael@0: michael@0: if (threadLock) { michael@0: PR_DestroyLock(threadLock); michael@0: threadLock = NULL; michael@0: } michael@0: } michael@0: michael@0: void michael@0: init_thread_data(void) michael@0: { michael@0: threadLock = PR_NewLock(); michael@0: } michael@0: michael@0: /************************************************************************** michael@0: ** End thread management routines. michael@0: **************************************************************************/ michael@0: michael@0: PRBool useModelSocket = PR_TRUE; michael@0: michael@0: static const char stopCmd[] = { "GET /stop " }; michael@0: static const char outHeader[] = { michael@0: "HTTP/1.0 200 OK\r\n" michael@0: "Server: Netscape-Enterprise/2.0a\r\n" michael@0: "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" michael@0: "Content-type: text/plain\r\n" michael@0: "\r\n" michael@0: }; michael@0: michael@0: struct lockedVarsStr { michael@0: PRLock * lock; michael@0: int count; michael@0: int waiters; michael@0: PRCondVar * condVar; michael@0: }; michael@0: michael@0: typedef struct lockedVarsStr lockedVars; michael@0: michael@0: void michael@0: lockedVars_Init( lockedVars * lv) michael@0: { michael@0: lv->count = 0; michael@0: lv->waiters = 0; michael@0: lv->lock = PR_NewLock(); michael@0: lv->condVar = PR_NewCondVar(lv->lock); michael@0: } michael@0: michael@0: void michael@0: lockedVars_Destroy( lockedVars * lv) michael@0: { michael@0: PR_DestroyCondVar(lv->condVar); michael@0: lv->condVar = NULL; michael@0: michael@0: PR_DestroyLock(lv->lock); michael@0: lv->lock = NULL; michael@0: } michael@0: michael@0: void michael@0: lockedVars_WaitForDone(lockedVars * lv) michael@0: { michael@0: PR_Lock(lv->lock); michael@0: while (lv->count > 0) { michael@0: PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT); michael@0: } michael@0: PR_Unlock(lv->lock); michael@0: } michael@0: michael@0: int /* returns count */ michael@0: lockedVars_AddToCount(lockedVars * lv, int addend) michael@0: { michael@0: int rv; michael@0: michael@0: PR_Lock(lv->lock); michael@0: rv = lv->count += addend; michael@0: if (rv <= 0) { michael@0: PR_NotifyCondVar(lv->condVar); michael@0: } michael@0: PR_Unlock(lv->lock); michael@0: return rv; michael@0: } michael@0: michael@0: int michael@0: do_writes( michael@0: void * a, michael@0: void * b, michael@0: int c) michael@0: { michael@0: PRFileDesc * ssl_sock = (PRFileDesc *)a; michael@0: lockedVars * lv = (lockedVars *)b; michael@0: int sent = 0; michael@0: int count = 0; michael@0: michael@0: while (sent < bigBuf.len) { michael@0: michael@0: count = PR_Send(ssl_sock, bigBuf.data + sent, bigBuf.len - sent, michael@0: 0, maxInterval); michael@0: if (count < 0) { michael@0: errWarn("PR_Send bigBuf"); michael@0: break; michael@0: } michael@0: FPRINTF(stderr, "strsclnt: PR_Send wrote %d bytes from bigBuf\n", michael@0: count ); michael@0: sent += count; michael@0: } michael@0: if (count >= 0) { /* last write didn't fail. */ michael@0: PR_Shutdown(ssl_sock, PR_SHUTDOWN_SEND); michael@0: } michael@0: michael@0: /* notify the reader that we're done. */ michael@0: lockedVars_AddToCount(lv, -1); michael@0: return (sent < bigBuf.len) ? SECFailure : SECSuccess; michael@0: } michael@0: michael@0: int michael@0: handle_fdx_connection( PRFileDesc * ssl_sock, int connection) michael@0: { michael@0: SECStatus result; michael@0: int firstTime = 1; michael@0: int countRead = 0; michael@0: lockedVars lv; michael@0: char *buf; michael@0: michael@0: michael@0: lockedVars_Init(&lv); michael@0: lockedVars_AddToCount(&lv, 1); michael@0: michael@0: /* Attempt to launch the writer thread. */ michael@0: result = launch_thread(do_writes, ssl_sock, &lv, connection); michael@0: michael@0: if (result != SECSuccess) michael@0: goto cleanup; michael@0: michael@0: buf = PR_Malloc(RD_BUF_SIZE); michael@0: michael@0: if (buf) { michael@0: do { michael@0: /* do reads here. */ michael@0: PRInt32 count; michael@0: michael@0: count = PR_Recv(ssl_sock, buf, RD_BUF_SIZE, 0, maxInterval); michael@0: if (count < 0) { michael@0: errWarn("PR_Recv"); michael@0: break; michael@0: } michael@0: countRead += count; michael@0: FPRINTF(stderr, michael@0: "strsclnt: connection %d read %d bytes (%d total).\n", michael@0: connection, count, countRead ); michael@0: if (firstTime) { michael@0: firstTime = 0; michael@0: printSecurityInfo(ssl_sock); michael@0: } michael@0: } while (lockedVars_AddToCount(&lv, 0) > 0); michael@0: PR_Free(buf); michael@0: buf = 0; michael@0: } michael@0: michael@0: /* Wait for writer to finish */ michael@0: lockedVars_WaitForDone(&lv); michael@0: lockedVars_Destroy(&lv); michael@0: michael@0: FPRINTF(stderr, michael@0: "strsclnt: connection %d read %d bytes total. -----------------------\n", michael@0: connection, countRead); michael@0: michael@0: cleanup: michael@0: /* Caller closes the socket. */ michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: const char request[] = {"GET /abc HTTP/1.0\r\n\r\n" }; michael@0: michael@0: SECStatus michael@0: handle_connection( PRFileDesc *ssl_sock, int tid) michael@0: { michael@0: int countRead = 0; michael@0: PRInt32 rv; michael@0: char *buf; michael@0: michael@0: buf = PR_Malloc(RD_BUF_SIZE); michael@0: if (!buf) michael@0: return SECFailure; michael@0: michael@0: /* compose the http request here. */ michael@0: michael@0: rv = PR_Send(ssl_sock, request, strlen(request), 0, maxInterval); michael@0: if (rv <= 0) { michael@0: errWarn("PR_Send"); michael@0: PR_Free(buf); michael@0: buf = 0; michael@0: failed_already = 1; michael@0: return SECFailure; michael@0: } michael@0: printSecurityInfo(ssl_sock); michael@0: michael@0: /* read until EOF */ michael@0: while (1) { michael@0: rv = PR_Recv(ssl_sock, buf, RD_BUF_SIZE, 0, maxInterval); michael@0: if (rv == 0) { michael@0: break; /* EOF */ michael@0: } michael@0: if (rv < 0) { michael@0: errWarn("PR_Recv"); michael@0: failed_already = 1; michael@0: break; michael@0: } michael@0: michael@0: countRead += rv; michael@0: FPRINTF(stderr, michael@0: "strsclnt: connection on thread %d read %d bytes (%d total).\n", michael@0: tid, rv, countRead ); michael@0: } michael@0: PR_Free(buf); michael@0: buf = 0; michael@0: michael@0: /* Caller closes the socket. */ michael@0: michael@0: FPRINTF(stderr, michael@0: "strsclnt: connection on thread %d read %d bytes total. ---------\n", michael@0: tid, countRead); michael@0: michael@0: return SECSuccess; /* success */ michael@0: } michael@0: michael@0: #define USE_SOCK_PEER_ID 1 michael@0: michael@0: #ifdef USE_SOCK_PEER_ID michael@0: michael@0: PRInt32 lastFullHandshakePeerID; michael@0: michael@0: void michael@0: myHandshakeCallback(PRFileDesc *socket, void *arg) michael@0: { michael@0: PR_ATOMIC_SET(&lastFullHandshakePeerID, (PRInt32) arg); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: /* one copy of this function is launched in a separate thread for each michael@0: ** connection to be made. michael@0: */ michael@0: int michael@0: do_connects( michael@0: void * a, michael@0: void * b, michael@0: int tid) michael@0: { michael@0: PRNetAddr * addr = (PRNetAddr *) a; michael@0: PRFileDesc * model_sock = (PRFileDesc *) b; michael@0: PRFileDesc * ssl_sock = 0; michael@0: PRFileDesc * tcp_sock = 0; michael@0: PRStatus prStatus; michael@0: PRUint32 sleepInterval = 50; /* milliseconds */ michael@0: SECStatus result; michael@0: int rv = SECSuccess; michael@0: PRSocketOptionData opt; michael@0: michael@0: retry: michael@0: michael@0: tcp_sock = PR_OpenTCPSocket(addr->raw.family); michael@0: if (tcp_sock == NULL) { michael@0: errExit("PR_OpenTCPSocket"); michael@0: } michael@0: michael@0: opt.option = PR_SockOpt_Nonblocking; michael@0: opt.value.non_blocking = PR_FALSE; michael@0: prStatus = PR_SetSocketOption(tcp_sock, &opt); michael@0: if (prStatus != PR_SUCCESS) { michael@0: errWarn("PR_SetSocketOption(PR_SockOpt_Nonblocking, PR_FALSE)"); michael@0: PR_Close(tcp_sock); michael@0: return SECSuccess; michael@0: } michael@0: michael@0: if (NoDelay) { michael@0: opt.option = PR_SockOpt_NoDelay; michael@0: opt.value.no_delay = PR_TRUE; michael@0: prStatus = PR_SetSocketOption(tcp_sock, &opt); michael@0: if (prStatus != PR_SUCCESS) { michael@0: errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)"); michael@0: PR_Close(tcp_sock); michael@0: return SECSuccess; michael@0: } michael@0: } michael@0: michael@0: prStatus = PR_Connect(tcp_sock, addr, PR_INTERVAL_NO_TIMEOUT); michael@0: if (prStatus != PR_SUCCESS) { michael@0: PRErrorCode err = PR_GetError(); /* save error code */ michael@0: PRInt32 oserr = PR_GetOSError(); michael@0: if (ThrottleUp) { michael@0: PRTime now = PR_Now(); michael@0: PR_Lock(threadLock); michael@0: lastConnectFailure = PR_MAX(now, lastConnectFailure); michael@0: PR_Unlock(threadLock); michael@0: PR_SetError(err, oserr); /* restore error code */ michael@0: } michael@0: if ((err == PR_CONNECT_REFUSED_ERROR) || michael@0: (err == PR_CONNECT_RESET_ERROR) ) { michael@0: int connections = numConnected; michael@0: michael@0: PR_Close(tcp_sock); michael@0: PR_Lock(threadLock); michael@0: if (connections > 2 && active_threads >= connections) { michael@0: active_threads = connections - 1; michael@0: fprintf(stderr,"active_threads set down to %d\n", michael@0: active_threads); michael@0: } michael@0: PR_Unlock(threadLock); michael@0: michael@0: if (QuitOnTimeout && sleepInterval > 40000) { michael@0: fprintf(stderr, michael@0: "strsclnt: Client timed out waiting for connection to server.\n"); michael@0: exit(1); michael@0: } michael@0: PR_Sleep(PR_MillisecondsToInterval(sleepInterval)); michael@0: sleepInterval <<= 1; michael@0: goto retry; michael@0: } michael@0: errWarn("PR_Connect"); michael@0: rv = SECFailure; michael@0: goto done; michael@0: } else { michael@0: if (ThrottleUp) { michael@0: PRTime now = PR_Now(); michael@0: PR_Lock(threadLock); michael@0: lastConnectSuccess = PR_MAX(now, lastConnectSuccess); michael@0: PR_Unlock(threadLock); michael@0: } michael@0: } michael@0: michael@0: ssl_sock = SSL_ImportFD(model_sock, tcp_sock); michael@0: /* XXX if this import fails, close tcp_sock and return. */ michael@0: if (!ssl_sock) { michael@0: PR_Close(tcp_sock); michael@0: return SECSuccess; michael@0: } michael@0: if (fullhs != NO_FULLHS_PERCENTAGE) { michael@0: #ifdef USE_SOCK_PEER_ID michael@0: char sockPeerIDString[512]; michael@0: static PRInt32 sockPeerID = 0; /* atomically incremented */ michael@0: PRInt32 thisPeerID; michael@0: #endif michael@0: PRInt32 savid = PR_ATOMIC_INCREMENT(&globalconid); michael@0: PRInt32 conid = 1 + (savid - 1) % 100; michael@0: /* don't change peer ID on the very first handshake, which is always michael@0: a full, so the session gets stored into the client cache */ michael@0: if ( (savid != 1) && michael@0: ( ( (savid <= total_connections_rounded_down_to_hundreds) && michael@0: (conid <= fullhs) ) || michael@0: (conid*100 <= total_connections_modulo_100*fullhs ) ) ) michael@0: #ifdef USE_SOCK_PEER_ID michael@0: { michael@0: /* force a full handshake by changing the socket peer ID */ michael@0: thisPeerID = PR_ATOMIC_INCREMENT(&sockPeerID); michael@0: } else { michael@0: /* reuse previous sockPeerID for restart handhsake */ michael@0: thisPeerID = lastFullHandshakePeerID; michael@0: } michael@0: PR_snprintf(sockPeerIDString, sizeof(sockPeerIDString), "ID%d", michael@0: thisPeerID); michael@0: SSL_SetSockPeerID(ssl_sock, sockPeerIDString); michael@0: SSL_HandshakeCallback(ssl_sock, myHandshakeCallback, (void*)thisPeerID); michael@0: #else michael@0: /* force a full handshake by setting the no cache option */ michael@0: SSL_OptionSet(ssl_sock, SSL_NO_CACHE, 1); michael@0: #endif michael@0: } michael@0: rv = SSL_ResetHandshake(ssl_sock, /* asServer */ 0); michael@0: if (rv != SECSuccess) { michael@0: errWarn("SSL_ResetHandshake"); michael@0: goto done; michael@0: } michael@0: michael@0: PR_ATOMIC_INCREMENT(&numConnected); michael@0: michael@0: if (bigBuf.data != NULL) { michael@0: result = handle_fdx_connection( ssl_sock, tid); michael@0: } else { michael@0: result = handle_connection( ssl_sock, tid); michael@0: } michael@0: michael@0: PR_ATOMIC_DECREMENT(&numConnected); michael@0: michael@0: done: michael@0: if (ssl_sock) { michael@0: PR_Close(ssl_sock); michael@0: } else if (tcp_sock) { michael@0: PR_Close(tcp_sock); michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: michael@0: typedef struct { michael@0: PRLock* lock; michael@0: char* nickname; michael@0: CERTCertificate* cert; michael@0: SECKEYPrivateKey* key; michael@0: void* wincx; michael@0: } cert_and_key; michael@0: michael@0: PRBool FindCertAndKey(cert_and_key* Cert_And_Key) michael@0: { michael@0: if ( (NULL == Cert_And_Key->nickname) || (0 == strcmp(Cert_And_Key->nickname,"none"))) { michael@0: return PR_TRUE; michael@0: } michael@0: Cert_And_Key->cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), michael@0: Cert_And_Key->nickname, certUsageSSLClient, michael@0: PR_FALSE, Cert_And_Key->wincx); michael@0: if (Cert_And_Key->cert) { michael@0: Cert_And_Key->key = PK11_FindKeyByAnyCert(Cert_And_Key->cert, Cert_And_Key->wincx); michael@0: } michael@0: if (Cert_And_Key->cert && Cert_And_Key->key) { michael@0: return PR_TRUE; michael@0: } else { michael@0: return PR_FALSE; michael@0: } michael@0: } michael@0: michael@0: PRBool LoggedIn(CERTCertificate* cert, SECKEYPrivateKey* key) michael@0: { michael@0: if ( (cert->slot) && (key->pkcs11Slot) && michael@0: (PR_TRUE == PK11_IsLoggedIn(cert->slot, NULL)) && michael@0: (PR_TRUE == PK11_IsLoggedIn(key->pkcs11Slot, NULL)) ) { michael@0: return PR_TRUE; michael@0: } michael@0: michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: SECStatus michael@0: StressClient_GetClientAuthData(void * arg, michael@0: PRFileDesc * socket, michael@0: struct CERTDistNamesStr * caNames, michael@0: struct CERTCertificateStr ** pRetCert, michael@0: struct SECKEYPrivateKeyStr **pRetKey) michael@0: { michael@0: cert_and_key* Cert_And_Key = (cert_and_key*) arg; michael@0: michael@0: if (!pRetCert || !pRetKey) { michael@0: /* bad pointers, can't return a cert or key */ michael@0: return SECFailure; michael@0: } michael@0: michael@0: *pRetCert = NULL; michael@0: *pRetKey = NULL; michael@0: michael@0: if (Cert_And_Key && Cert_And_Key->nickname) { michael@0: while (PR_TRUE) { michael@0: if (Cert_And_Key && Cert_And_Key->lock) { michael@0: int timeout = 0; michael@0: PR_Lock(Cert_And_Key->lock); michael@0: michael@0: if (Cert_And_Key->cert) { michael@0: *pRetCert = CERT_DupCertificate(Cert_And_Key->cert); michael@0: } michael@0: michael@0: if (Cert_And_Key->key) { michael@0: *pRetKey = SECKEY_CopyPrivateKey(Cert_And_Key->key); michael@0: } michael@0: PR_Unlock(Cert_And_Key->lock); michael@0: if (!*pRetCert || !*pRetKey) { michael@0: /* one or both of them failed to copy. Either the source was NULL, or there was michael@0: ** an out of memory condition. Free any allocated copy and fail */ michael@0: if (*pRetCert) { michael@0: CERT_DestroyCertificate(*pRetCert); michael@0: *pRetCert = NULL; michael@0: } michael@0: if (*pRetKey) { michael@0: SECKEY_DestroyPrivateKey(*pRetKey); michael@0: *pRetKey = NULL; michael@0: } michael@0: break; michael@0: } michael@0: /* now check if those objects are valid */ michael@0: if ( PR_FALSE == LoggedIn(*pRetCert, *pRetKey) ) { michael@0: /* token is no longer logged in, it was removed */ michael@0: michael@0: /* first, delete and clear our invalid local objects */ michael@0: CERT_DestroyCertificate(*pRetCert); michael@0: SECKEY_DestroyPrivateKey(*pRetKey); michael@0: *pRetCert = NULL; michael@0: *pRetKey = NULL; michael@0: michael@0: PR_Lock(Cert_And_Key->lock); michael@0: /* check if another thread already logged back in */ michael@0: if (PR_TRUE == LoggedIn(Cert_And_Key->cert, Cert_And_Key->key)) { michael@0: /* yes : try again */ michael@0: PR_Unlock(Cert_And_Key->lock); michael@0: continue; michael@0: } michael@0: /* this is the thread to retry */ michael@0: CERT_DestroyCertificate(Cert_And_Key->cert); michael@0: SECKEY_DestroyPrivateKey(Cert_And_Key->key); michael@0: Cert_And_Key->cert = NULL; michael@0: Cert_And_Key->key = NULL; michael@0: michael@0: michael@0: /* now look up the cert and key again */ michael@0: while (PR_FALSE == FindCertAndKey(Cert_And_Key) ) { michael@0: PR_Sleep(PR_SecondsToInterval(1)); michael@0: timeout++; michael@0: if (timeout>=60) { michael@0: printf("\nToken pulled and not reinserted early enough : aborting.\n"); michael@0: exit(1); michael@0: } michael@0: } michael@0: PR_Unlock(Cert_And_Key->lock); michael@0: continue; michael@0: /* try again to reduce code size */ michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: } michael@0: *pRetCert = NULL; michael@0: *pRetKey = NULL; michael@0: return SECFailure; michael@0: } else { michael@0: /* no cert configured, automatically find the right cert. */ michael@0: CERTCertificate * cert = NULL; michael@0: SECKEYPrivateKey * privkey = NULL; michael@0: CERTCertNicknames * names; michael@0: int i; michael@0: void * proto_win = NULL; michael@0: SECStatus rv = SECFailure; michael@0: michael@0: if (Cert_And_Key) { michael@0: proto_win = Cert_And_Key->wincx; michael@0: } michael@0: michael@0: names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(), michael@0: SEC_CERT_NICKNAMES_USER, proto_win); michael@0: if (names != NULL) { michael@0: for (i = 0; i < names->numnicknames; i++) { michael@0: cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), michael@0: names->nicknames[i], certUsageSSLClient, michael@0: PR_FALSE, proto_win); michael@0: if ( !cert ) michael@0: continue; michael@0: /* Only check unexpired certs */ michael@0: if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_TRUE) != michael@0: secCertTimeValid ) { michael@0: CERT_DestroyCertificate(cert); michael@0: continue; michael@0: } michael@0: rv = NSS_CmpCertChainWCANames(cert, caNames); michael@0: if ( rv == SECSuccess ) { michael@0: privkey = PK11_FindKeyByAnyCert(cert, proto_win); michael@0: if ( privkey ) michael@0: break; michael@0: } michael@0: rv = SECFailure; michael@0: CERT_DestroyCertificate(cert); michael@0: } michael@0: CERT_FreeNicknames(names); michael@0: } michael@0: if (rv == SECSuccess) { michael@0: *pRetCert = cert; michael@0: *pRetKey = privkey; michael@0: } michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: int michael@0: hexchar_to_int(int c) michael@0: { michael@0: if (((c) >= '0') && ((c) <= '9')) michael@0: return (c) - '0'; michael@0: if (((c) >= 'a') && ((c) <= 'f')) michael@0: return (c) - 'a' + 10; michael@0: if (((c) >= 'A') && ((c) <= 'F')) michael@0: return (c) - 'A' + 10; michael@0: failed_already = 1; michael@0: return -1; michael@0: } michael@0: michael@0: void michael@0: client_main( michael@0: unsigned short port, michael@0: int connections, michael@0: cert_and_key* Cert_And_Key, michael@0: const char * hostName, michael@0: const char * sniHostName) michael@0: { michael@0: PRFileDesc *model_sock = NULL; michael@0: int i; michael@0: int rv; michael@0: PRStatus status; michael@0: PRNetAddr addr; michael@0: michael@0: status = PR_StringToNetAddr(hostName, &addr); michael@0: if (status == PR_SUCCESS) { michael@0: addr.inet.port = PR_htons(port); michael@0: } else { michael@0: /* Lookup host */ michael@0: PRAddrInfo *addrInfo; michael@0: void *enumPtr = NULL; michael@0: michael@0: addrInfo = PR_GetAddrInfoByName(hostName, PR_AF_UNSPEC, michael@0: PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME); michael@0: if (!addrInfo) { michael@0: SECU_PrintError(progName, "error looking up host"); michael@0: return; michael@0: } michael@0: do { michael@0: enumPtr = PR_EnumerateAddrInfo(enumPtr, addrInfo, port, &addr); michael@0: } while (enumPtr != NULL && michael@0: addr.raw.family != PR_AF_INET && michael@0: addr.raw.family != PR_AF_INET6); michael@0: PR_FreeAddrInfo(addrInfo); michael@0: if (enumPtr == NULL) { michael@0: SECU_PrintError(progName, "error looking up host address"); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: /* all suites except RSA_NULL_MD5 are enabled by Domestic Policy */ michael@0: NSS_SetDomesticPolicy(); michael@0: michael@0: /* all the SSL2 and SSL3 cipher suites are enabled by default. */ michael@0: if (cipherString) { michael@0: int ndx; michael@0: michael@0: /* disable all the ciphers, then enable the ones we want. */ michael@0: disableAllSSLCiphers(); michael@0: michael@0: while (0 != (ndx = *cipherString)) { michael@0: const char * startCipher = cipherString++; michael@0: int cipher = 0; michael@0: SECStatus rv; michael@0: michael@0: if (ndx == ':') { michael@0: cipher = hexchar_to_int(*cipherString++); michael@0: cipher <<= 4; michael@0: cipher |= hexchar_to_int(*cipherString++); michael@0: cipher <<= 4; michael@0: cipher |= hexchar_to_int(*cipherString++); michael@0: cipher <<= 4; michael@0: cipher |= hexchar_to_int(*cipherString++); michael@0: if (cipher <= 0) { michael@0: fprintf(stderr, "strsclnt: Invalid cipher value: %-5.5s\n", michael@0: startCipher); michael@0: failed_already = 1; michael@0: return; michael@0: } michael@0: } else { michael@0: if (isalpha(ndx)) { michael@0: const int *cptr; michael@0: michael@0: cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites; michael@0: for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) michael@0: /* do nothing */; michael@0: } michael@0: if (cipher <= 0) { michael@0: fprintf(stderr, "strsclnt: Invalid cipher letter: %c\n", michael@0: *startCipher); michael@0: failed_already = 1; michael@0: return; michael@0: } michael@0: } michael@0: rv = SSL_CipherPrefSetDefault(cipher, PR_TRUE); michael@0: if (rv != SECSuccess) { michael@0: fprintf(stderr, michael@0: "strsclnt: SSL_CipherPrefSetDefault(0x%04x) failed\n", michael@0: cipher); michael@0: failed_already = 1; michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* configure model SSL socket. */ michael@0: michael@0: model_sock = PR_OpenTCPSocket(addr.raw.family); michael@0: if (model_sock == NULL) { michael@0: errExit("PR_OpenTCPSocket for model socket"); michael@0: } michael@0: michael@0: model_sock = SSL_ImportFD(NULL, model_sock); michael@0: if (model_sock == NULL) { michael@0: errExit("SSL_ImportFD"); michael@0: } michael@0: michael@0: /* do SSL configuration. */ michael@0: michael@0: rv = SSL_OptionSet(model_sock, SSL_SECURITY, michael@0: enableSSL2 || enabledVersions.min != 0); michael@0: if (rv < 0) { michael@0: errExit("SSL_OptionSet SSL_SECURITY"); michael@0: } michael@0: michael@0: rv = SSL_VersionRangeSet(model_sock, &enabledVersions); michael@0: if (rv != SECSuccess) { michael@0: errExit("error setting SSL/TLS version range "); michael@0: } michael@0: michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL2, enableSSL2); michael@0: if (rv != SECSuccess) { michael@0: errExit("error enabling SSLv2 "); michael@0: } michael@0: michael@0: rv = SSL_OptionSet(model_sock, SSL_V2_COMPATIBLE_HELLO, enableSSL2); michael@0: if (rv != SECSuccess) { michael@0: errExit("error enabling SSLv2 compatible hellos "); michael@0: } michael@0: michael@0: if (bigBuf.data) { /* doing FDX */ michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_FDX, 1); michael@0: if (rv < 0) { michael@0: errExit("SSL_OptionSet SSL_ENABLE_FDX"); michael@0: } michael@0: } michael@0: michael@0: if (NoReuse) { michael@0: rv = SSL_OptionSet(model_sock, SSL_NO_CACHE, 1); michael@0: if (rv < 0) { michael@0: errExit("SSL_OptionSet SSL_NO_CACHE"); michael@0: } michael@0: } michael@0: michael@0: if (bypassPKCS11) { michael@0: rv = SSL_OptionSet(model_sock, SSL_BYPASS_PKCS11, 1); michael@0: if (rv < 0) { michael@0: errExit("SSL_OptionSet SSL_BYPASS_PKCS11"); michael@0: } michael@0: } michael@0: michael@0: if (disableLocking) { michael@0: rv = SSL_OptionSet(model_sock, SSL_NO_LOCKS, 1); michael@0: if (rv < 0) { michael@0: errExit("SSL_OptionSet SSL_NO_LOCKS"); michael@0: } michael@0: } michael@0: michael@0: if (enableSessionTickets) { michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE); michael@0: if (rv != SECSuccess) michael@0: errExit("SSL_OptionSet SSL_ENABLE_SESSION_TICKETS"); michael@0: } michael@0: michael@0: if (enableCompression) { michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_DEFLATE, PR_TRUE); michael@0: if (rv != SECSuccess) michael@0: errExit("SSL_OptionSet SSL_ENABLE_DEFLATE"); michael@0: } michael@0: michael@0: if (enableFalseStart) { michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_FALSE_START, PR_TRUE); michael@0: if (rv != SECSuccess) michael@0: errExit("SSL_OptionSet SSL_ENABLE_FALSE_START"); michael@0: } michael@0: michael@0: if (enableCertStatus) { michael@0: rv = SSL_OptionSet(model_sock, SSL_ENABLE_OCSP_STAPLING, PR_TRUE); michael@0: if (rv != SECSuccess) michael@0: errExit("SSL_OptionSet SSL_ENABLE_OCSP_STAPLING"); michael@0: } michael@0: michael@0: SSL_SetPKCS11PinArg(model_sock, &pwdata); michael@0: michael@0: SSL_SetURL(model_sock, hostName); michael@0: michael@0: SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate, michael@0: (void *)CERT_GetDefaultCertDB()); michael@0: SSL_BadCertHook(model_sock, myBadCertHandler, NULL); michael@0: michael@0: SSL_GetClientAuthDataHook(model_sock, StressClient_GetClientAuthData, (void*)Cert_And_Key); michael@0: michael@0: if (sniHostName) { michael@0: SSL_SetURL(model_sock, sniHostName); michael@0: } michael@0: /* I'm not going to set the HandshakeCallback function. */ michael@0: michael@0: /* end of ssl configuration. */ michael@0: michael@0: init_thread_data(); michael@0: michael@0: remaining_connections = total_connections = connections; michael@0: total_connections_modulo_100 = total_connections % 100; michael@0: total_connections_rounded_down_to_hundreds = michael@0: total_connections - total_connections_modulo_100; michael@0: michael@0: if (!NoReuse) { michael@0: remaining_connections = 1; michael@0: rv = launch_thread(do_connects, &addr, model_sock, 0); michael@0: /* wait for the first connection to terminate, then launch the rest. */ michael@0: reap_threads(); michael@0: remaining_connections = total_connections - 1 ; michael@0: } michael@0: if (remaining_connections > 0) { michael@0: active_threads = PR_MIN(active_threads, remaining_connections); michael@0: /* Start up the threads */ michael@0: for (i=0;i 0 && michael@0: NULL != (local_file_fd = PR_Open(fileName, PR_RDONLY, 0))) { michael@0: michael@0: hdrLen = PORT_Strlen(outHeader); michael@0: bigBuf.len = hdrLen + info.size; michael@0: bigBuf.data = PORT_Malloc(bigBuf.len + 4095); michael@0: if (!bigBuf.data) { michael@0: errWarn("PORT_Malloc"); michael@0: goto done; michael@0: } michael@0: michael@0: PORT_Memcpy(bigBuf.data, outHeader, hdrLen); michael@0: michael@0: count = PR_Read(local_file_fd, bigBuf.data + hdrLen, info.size); michael@0: if (count != info.size) { michael@0: errWarn("PR_Read local file"); michael@0: goto done; michael@0: } michael@0: rv = SECSuccess; michael@0: done: michael@0: PR_Close(local_file_fd); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: const char * dir = "."; michael@0: const char * fileName = NULL; michael@0: char * hostName = NULL; michael@0: char * nickName = NULL; michael@0: char * tmp = NULL; michael@0: int connections = 1; michael@0: int exitVal; michael@0: int tmpInt; michael@0: unsigned short port = 443; michael@0: SECStatus rv; michael@0: PLOptState * optstate; michael@0: PLOptStatus status; michael@0: cert_and_key Cert_And_Key; michael@0: char * sniHostName = NULL; michael@0: michael@0: /* Call the NSPR initialization routines */ michael@0: PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); michael@0: SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions); michael@0: michael@0: tmp = strrchr(argv[0], '/'); michael@0: tmp = tmp ? tmp + 1 : argv[0]; michael@0: progName = strrchr(tmp, '\\'); michael@0: progName = progName ? progName + 1 : tmp; michael@0: michael@0: michael@0: optstate = PL_CreateOptState(argc, argv, michael@0: "BC:DNP:TUV:W:a:c:d:f:gin:op:qst:uvw:z"); michael@0: while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { michael@0: switch(optstate->option) { michael@0: case 'B': bypassPKCS11 = PR_TRUE; break; michael@0: michael@0: case 'C': cipherString = optstate->value; break; michael@0: michael@0: case 'D': NoDelay = PR_TRUE; break; michael@0: michael@0: case 'I': /* reserved for OCSP multi-stapling */ break; michael@0: michael@0: case 'N': NoReuse = 1; break; michael@0: michael@0: case 'P': fullhs = PORT_Atoi(optstate->value); break; michael@0: michael@0: case 'T': enableCertStatus = PR_TRUE; break; michael@0: michael@0: case 'U': ThrottleUp = PR_TRUE; break; michael@0: michael@0: case 'V': if (SECU_ParseSSLVersionRangeString(optstate->value, michael@0: enabledVersions, enableSSL2, michael@0: &enabledVersions, &enableSSL2) != SECSuccess) { michael@0: Usage(progName); michael@0: } michael@0: break; michael@0: michael@0: case 'a': sniHostName = PL_strdup(optstate->value); break; michael@0: michael@0: case 'c': connections = PORT_Atoi(optstate->value); break; michael@0: michael@0: case 'd': dir = optstate->value; break; michael@0: michael@0: case 'f': fileName = optstate->value; break; michael@0: michael@0: case 'g': enableFalseStart = PR_TRUE; break; michael@0: michael@0: case 'i': ignoreErrors = PR_TRUE; break; michael@0: michael@0: case 'n': nickName = PL_strdup(optstate->value); break; michael@0: michael@0: case 'o': MakeCertOK++; break; michael@0: michael@0: case 'p': port = PORT_Atoi(optstate->value); break; michael@0: michael@0: case 'q': QuitOnTimeout = PR_TRUE; break; michael@0: michael@0: case 's': disableLocking = PR_TRUE; break; michael@0: michael@0: case 't': michael@0: tmpInt = PORT_Atoi(optstate->value); michael@0: if (tmpInt > 0 && tmpInt < MAX_THREADS) michael@0: max_threads = active_threads = tmpInt; michael@0: break; michael@0: michael@0: case 'u': enableSessionTickets = PR_TRUE; break; michael@0: michael@0: case 'v': verbose++; break; michael@0: michael@0: case 'w': michael@0: pwdata.source = PW_PLAINTEXT; michael@0: pwdata.data = PL_strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'W': michael@0: pwdata.source = PW_FROMFILE; michael@0: pwdata.data = PL_strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'z': enableCompression = PR_TRUE; break; michael@0: michael@0: case 0: /* positional parameter */ michael@0: if (hostName) { michael@0: Usage(progName); michael@0: } michael@0: hostName = PL_strdup(optstate->value); michael@0: break; michael@0: michael@0: default: michael@0: case '?': michael@0: Usage(progName); michael@0: break; michael@0: michael@0: } michael@0: } michael@0: PL_DestroyOptState(optstate); michael@0: michael@0: if (!hostName || status == PL_OPT_BAD) michael@0: Usage(progName); michael@0: michael@0: if (fullhs!= NO_FULLHS_PERCENTAGE && (fullhs < 0 || fullhs>100 || NoReuse) ) michael@0: Usage(progName); michael@0: michael@0: if (port == 0) michael@0: Usage(progName); michael@0: michael@0: if (fileName) michael@0: readBigFile(fileName); michael@0: michael@0: PK11_SetPasswordFunc(SECU_GetModulePassword); michael@0: michael@0: tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT"); michael@0: if (tmp && tmp[0]) { michael@0: int sec = PORT_Atoi(tmp); michael@0: if (sec > 0) { michael@0: maxInterval = PR_SecondsToInterval(sec); michael@0: } michael@0: } michael@0: michael@0: /* Call the NSS initialization routines */ michael@0: rv = NSS_Initialize(dir, "", "", SECMOD_DB, NSS_INIT_READONLY); michael@0: if (rv != SECSuccess) { michael@0: fputs("NSS_Init failed.\n", stderr); michael@0: exit(1); michael@0: } michael@0: ssl3stats = SSL_GetStatistics(); michael@0: Cert_And_Key.lock = PR_NewLock(); michael@0: Cert_And_Key.nickname = nickName; michael@0: Cert_And_Key.wincx = &pwdata; michael@0: Cert_And_Key.cert = NULL; michael@0: Cert_And_Key.key = NULL; michael@0: michael@0: if (PR_FALSE == FindCertAndKey(&Cert_And_Key)) { michael@0: michael@0: if (Cert_And_Key.cert == NULL) { michael@0: fprintf(stderr, "strsclnt: Can't find certificate %s\n", Cert_And_Key.nickname); michael@0: exit(1); michael@0: } michael@0: michael@0: if (Cert_And_Key.key == NULL) { michael@0: fprintf(stderr, "strsclnt: Can't find Private Key for cert %s\n", michael@0: Cert_And_Key.nickname); michael@0: exit(1); michael@0: } michael@0: michael@0: } michael@0: michael@0: client_main(port, connections, &Cert_And_Key, hostName, michael@0: sniHostName); michael@0: michael@0: /* clean up */ michael@0: if (Cert_And_Key.cert) { michael@0: CERT_DestroyCertificate(Cert_And_Key.cert); michael@0: } michael@0: if (Cert_And_Key.key) { michael@0: SECKEY_DestroyPrivateKey(Cert_And_Key.key); michael@0: } michael@0: michael@0: PR_DestroyLock(Cert_And_Key.lock); michael@0: michael@0: if (pwdata.data) { michael@0: PL_strfree(pwdata.data); michael@0: } michael@0: if (Cert_And_Key.nickname) { michael@0: PL_strfree(Cert_And_Key.nickname); michael@0: } michael@0: if (sniHostName) { michael@0: PL_strfree(sniHostName); michael@0: } michael@0: michael@0: PL_strfree(hostName); michael@0: michael@0: /* some final stats. */ michael@0: if (ssl3stats->hsh_sid_cache_hits + michael@0: ssl3stats->hsh_sid_cache_misses + michael@0: ssl3stats->hsh_sid_cache_not_ok + michael@0: ssl3stats->hsh_sid_stateless_resumes == 0) { michael@0: /* presumably we were testing SSL2. */ michael@0: printf("strsclnt: SSL2 - %d server certificates tested.\n", michael@0: certsTested); michael@0: } else { michael@0: printf( michael@0: "strsclnt: %ld cache hits; %ld cache misses, %ld cache not reusable\n" michael@0: " %ld stateless resumes\n", michael@0: ssl3stats->hsh_sid_cache_hits, michael@0: ssl3stats->hsh_sid_cache_misses, michael@0: ssl3stats->hsh_sid_cache_not_ok, michael@0: ssl3stats->hsh_sid_stateless_resumes); michael@0: } michael@0: michael@0: if (!NoReuse) { michael@0: if (enableSessionTickets) michael@0: exitVal = (ssl3stats->hsh_sid_stateless_resumes == 0); michael@0: else michael@0: exitVal = (ssl3stats->hsh_sid_cache_misses > 1) || michael@0: (ssl3stats->hsh_sid_stateless_resumes != 0); michael@0: if (!exitVal) michael@0: exitVal = (ssl3stats->hsh_sid_cache_not_ok != 0) || michael@0: (certsTested > 1); michael@0: } else { michael@0: printf("strsclnt: NoReuse - %d server certificates tested.\n", michael@0: certsTested); michael@0: if (ssl3stats->hsh_sid_cache_hits + michael@0: ssl3stats->hsh_sid_cache_misses + michael@0: ssl3stats->hsh_sid_cache_not_ok + michael@0: ssl3stats->hsh_sid_stateless_resumes > 0) { michael@0: exitVal = (ssl3stats->hsh_sid_cache_misses != connections) || michael@0: (ssl3stats->hsh_sid_stateless_resumes != 0) || michael@0: (certsTested != connections); michael@0: } else { /* ssl2 connections */ michael@0: exitVal = (certsTested != connections); michael@0: } michael@0: } michael@0: michael@0: exitVal = ( exitVal || failed_already ); michael@0: SSL_ClearSessionCache(); michael@0: if (NSS_Shutdown() != SECSuccess) { michael@0: printf("strsclnt: NSS_Shutdown() failed.\n"); michael@0: exit(1); michael@0: } michael@0: michael@0: PR_Cleanup(); michael@0: return exitVal; michael@0: } michael@0: