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