2017-04-19 07:56:45 +00:00
|
|
|
/* 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)
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
/* Include these cipher suite arrays to re-use tstclnt's
|
2017-04-19 07:56:45 +00:00
|
|
|
* cipher selection code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int ssl3CipherSuites[] = {
|
2020-01-24 01:47:36 +00:00
|
|
|
-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 */
|
2017-04-19 07:56:45 +00:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
#define NO_FULLHS_PERCENTAGE -1
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
/* This global string is so that client main can see
|
|
|
|
* which ciphers to use.
|
2017-04-19 07:56:45 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
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 */
|
2020-01-24 01:47:36 +00:00
|
|
|
static PRInt32 globalconid = 0; /* atomically set */
|
|
|
|
static int total_connections; /* total number of connections to perform */
|
2017-04-19 07:56:45 +00:00
|
|
|
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;
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
static PRLock *threadLock; /* protects the global variables below */
|
2017-04-19 07:56:45 +00:00
|
|
|
static PRTime lastConnectFailure;
|
|
|
|
static PRTime lastConnectSuccess;
|
|
|
|
static PRTime lastThrottleUp;
|
2020-01-24 01:47:36 +00:00
|
|
|
static PRInt32 remaining_connections; /* number of connections left */
|
|
|
|
static int active_threads = 8; /* number of threads currently trying to
|
2017-04-19 07:56:45 +00:00
|
|
|
** connect */
|
|
|
|
static PRInt32 numUsed;
|
|
|
|
/* end of variables protected by threadLock */
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
static SSL3Statistics *ssl3stats;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
static int failed_already = 0;
|
|
|
|
static SSLVersionRange enabledVersions;
|
2020-01-24 01:47:36 +00:00
|
|
|
static PRBool disableLocking = PR_FALSE;
|
|
|
|
static PRBool ignoreErrors = PR_FALSE;
|
2017-04-19 07:56:45 +00:00
|
|
|
static PRBool enableSessionTickets = PR_FALSE;
|
2020-01-24 01:47:36 +00:00
|
|
|
static PRBool enableCompression = PR_FALSE;
|
|
|
|
static PRBool enableFalseStart = PR_FALSE;
|
|
|
|
static PRBool enableCertStatus = PR_FALSE;
|
|
|
|
|
|
|
|
PRIntervalTime maxInterval = PR_INTERVAL_NO_TIMEOUT;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
static const SSLSignatureScheme *enabledSigSchemes = NULL;
|
|
|
|
static unsigned int enabledSigSchemeCount = 0;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
char *progName;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
secuPWData pwdata = { PW_NONE, 0 };
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
int stopping;
|
|
|
|
int verbose;
|
|
|
|
SECItem bigBuf;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
#define PRINTF \
|
|
|
|
if (verbose) \
|
|
|
|
printf
|
|
|
|
#define FPRINTF \
|
|
|
|
if (verbose) \
|
|
|
|
fprintf
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
static void
|
2020-01-24 01:47:36 +00:00
|
|
|
Usage(void)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
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);
|
2017-04-19 07:56:45 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-01-24 01:47:36 +00:00
|
|
|
errWarn(char *funcString)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
PRErrorCode perr = PR_GetError();
|
|
|
|
PRInt32 oserr = PR_GetOSError();
|
|
|
|
const char *errString = SECU_Strerror(perr);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
fprintf(stderr, "strsclnt: %s returned error %d, OS error %d: %s\n",
|
|
|
|
funcString, perr, oserr, errString);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-01-24 01:47:36 +00:00
|
|
|
errExit(char *funcString)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
errWarn(funcString);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************
|
2020-01-24 01:47:36 +00:00
|
|
|
**
|
2017-04-19 07:56:45 +00:00
|
|
|
** Routines for disabling SSL ciphers.
|
|
|
|
**
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
void
|
|
|
|
disableAllSSLCiphers(void)
|
|
|
|
{
|
|
|
|
const PRUint16 *cipherSuites = SSL_GetImplementedCiphers();
|
2020-01-24 01:47:36 +00:00
|
|
|
int i = SSL_GetNumImplementedCiphers();
|
|
|
|
SECStatus rv;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
/* disable all the SSL3 cipher suites */
|
|
|
|
while (--i >= 0) {
|
2020-01-24 01:47:36 +00:00
|
|
|
PRUint16 suite = cipherSuites[i];
|
2017-04-19 07:56:45 +00:00
|
|
|
rv = SSL_CipherPrefSetDefault(suite, PR_FALSE);
|
2020-01-24 01:47:36 +00:00
|
|
|
if (rv != SECSuccess) {
|
|
|
|
printf("SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n",
|
|
|
|
suite, i);
|
|
|
|
errWarn("SSL_CipherPrefSetDefault");
|
|
|
|
exit(2);
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This invokes the "default" AuthCert handler in libssl.
|
2020-01-24 01:47:36 +00:00
|
|
|
** The only reason to use this one is that it prints out info as it goes.
|
2017-04-19 07:56:45 +00:00
|
|
|
*/
|
|
|
|
static SECStatus
|
|
|
|
mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig,
|
2020-01-24 01:47:36 +00:00
|
|
|
PRBool isServer)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
SECStatus rv;
|
2020-01-24 01:47:36 +00:00
|
|
|
CERTCertificate *peerCert;
|
2017-04-19 07:56:45 +00:00
|
|
|
const SECItemArray *csa;
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
if (MakeCertOK >= 2) {
|
2017-04-19 07:56:45 +00:00
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
peerCert = SSL_PeerCertificate(fd);
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
PRINTF("strsclnt: Subject: %s\nstrsclnt: Issuer : %s\n",
|
|
|
|
peerCert->subjectName, peerCert->issuerName);
|
2017-04-19 07:56:45 +00:00
|
|
|
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) {
|
2020-01-24 01:47:36 +00:00
|
|
|
fputs("strsclnt: -- SSL: Server Certificate Validated.\n", stderr);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
CERT_DestroyCertificate(peerCert);
|
|
|
|
/* error, if any, will be displayed by the Bad Cert Handler. */
|
2020-01-24 01:47:36 +00:00
|
|
|
return rv;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static SECStatus
|
2020-01-24 01:47:36 +00:00
|
|
|
myBadCertHandler(void *arg, PRFileDesc *fd)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
PRErrorCode err = PR_GetError();
|
|
|
|
if (!MakeCertOK)
|
2020-01-24 01:47:36 +00:00
|
|
|
fprintf(stderr,
|
|
|
|
"strsclnt: -- SSL: Server Certificate Invalid, err %d.\n%s\n",
|
|
|
|
err, SECU_Strerror(err));
|
2017-04-19 07:56:45 +00:00
|
|
|
return (MakeCertOK ? SECSuccess : SECFailure);
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
void
|
2017-04-19 07:56:45 +00:00
|
|
|
printSecurityInfo(PRFileDesc *fd)
|
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
CERTCertificate *cert = NULL;
|
2017-04-19 07:56:45 +00:00
|
|
|
SECStatus result;
|
2020-01-24 01:47:36 +00:00
|
|
|
SSLChannelInfo channel;
|
2017-04-19 07:56:45 +00:00
|
|
|
SSLCipherSuiteInfo suite;
|
|
|
|
|
|
|
|
static int only_once;
|
|
|
|
|
|
|
|
if (only_once && verbose < 2)
|
2020-01-24 01:47:36 +00:00
|
|
|
return;
|
2017-04-19 07:56:45 +00:00
|
|
|
only_once = 1;
|
|
|
|
|
|
|
|
result = SSL_GetChannelInfo(fd, &channel, sizeof channel);
|
2020-01-24 01:47:36 +00:00
|
|
|
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\n",
|
|
|
|
channel.protocolVersion >> 8, channel.protocolVersion & 0xff,
|
|
|
|
suite.effectiveKeyBits, suite.symCipherName,
|
|
|
|
suite.macBits, suite.macAlgorithmName);
|
|
|
|
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);
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cert = SSL_LocalCertificate(fd);
|
|
|
|
if (!cert)
|
2020-01-24 01:47:36 +00:00
|
|
|
cert = SSL_PeerCertificate(fd);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
if (verbose && cert) {
|
2020-01-24 01:47:36 +00:00
|
|
|
char *ip = CERT_NameToAscii(&cert->issuer);
|
|
|
|
char *sp = CERT_NameToAscii(&cert->subject);
|
2017-04-19 07:56:45 +00:00
|
|
|
if (sp) {
|
2020-01-24 01:47:36 +00:00
|
|
|
fprintf(stderr, "strsclnt: subject DN: %s\n", sp);
|
|
|
|
PORT_Free(sp);
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
if (ip) {
|
2020-01-24 01:47:36 +00:00
|
|
|
fprintf(stderr, "strsclnt: issuer DN: %s\n", ip);
|
|
|
|
PORT_Free(ip);
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
if (cert) {
|
2020-01-24 01:47:36 +00:00
|
|
|
CERT_DestroyCertificate(cert);
|
|
|
|
cert = NULL;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
fprintf(stderr,
|
2020-01-24 01:47:36 +00:00
|
|
|
"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);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
** Begin thread management routines and data.
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
#define MAX_THREADS 128
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
typedef SECStatus startFn(void *a, void *b, int c);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
static PRInt32 numConnected;
|
|
|
|
static int max_threads; /* peak threads allowed */
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
typedef struct perThreadStr {
|
2020-01-24 01:47:36 +00:00
|
|
|
void *a;
|
|
|
|
void *b;
|
|
|
|
int tid;
|
|
|
|
int rv;
|
|
|
|
startFn *startFunc;
|
|
|
|
PRThread *prThread;
|
|
|
|
PRBool inUse;
|
2017-04-19 07:56:45 +00:00
|
|
|
} perThread;
|
|
|
|
|
|
|
|
perThread threads[MAX_THREADS];
|
|
|
|
|
|
|
|
void
|
2020-01-24 01:47:36 +00:00
|
|
|
thread_wrapper(void *arg)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
perThread *slot = (perThread *)arg;
|
2017-04-19 07:56:45 +00:00
|
|
|
PRBool done = PR_FALSE;
|
|
|
|
|
|
|
|
do {
|
|
|
|
PRBool doop = PR_FALSE;
|
|
|
|
PRBool dosleep = PR_FALSE;
|
|
|
|
PRTime now = PR_Now();
|
|
|
|
|
|
|
|
PR_Lock(threadLock);
|
2020-01-24 01:47:36 +00:00
|
|
|
if (!(slot->tid < active_threads)) {
|
2017-04-19 07:56:45 +00:00
|
|
|
/* 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
|
|
|
|
*/
|
2020-01-24 01:47:36 +00:00
|
|
|
if ((now - lastConnectFailure > 10 * PR_USEC_PER_SEC) &&
|
|
|
|
((!lastThrottleUp) || ((now - lastThrottleUp) >=
|
|
|
|
(PR_USEC_PER_SEC / 2))) &&
|
|
|
|
(lastConnectSuccess > lastConnectFailure)) {
|
2017-04-19 07:56:45 +00:00
|
|
|
/* try throttling up by one thread */
|
2020-01-24 01:47:36 +00:00
|
|
|
active_threads = PR_MIN(max_threads, active_threads + 1);
|
|
|
|
fprintf(stderr, "active_threads set up to %d\n",
|
2017-04-19 07:56:45 +00:00
|
|
|
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) {
|
2020-01-24 01:47:36 +00:00
|
|
|
slot->rv = (*slot->startFunc)(slot->a, slot->b, slot->tid);
|
|
|
|
PRINTF("strsclnt: Thread in slot %d returned %d\n",
|
2017-04-19 07:56:45 +00:00
|
|
|
slot->tid, slot->rv);
|
|
|
|
}
|
|
|
|
if (dosleep) {
|
|
|
|
PR_Sleep(PR_SecondsToInterval(1));
|
|
|
|
}
|
|
|
|
} while (!done && (!failed_already || ignoreErrors));
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
|
|
|
launch_thread(
|
2020-01-24 01:47:36 +00:00
|
|
|
startFn *startFunc,
|
|
|
|
void *a,
|
|
|
|
void *b,
|
|
|
|
int tid)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
PRUint32 i;
|
2020-01-24 01:47:36 +00:00
|
|
|
perThread *slot;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
PR_Lock(threadLock);
|
|
|
|
|
|
|
|
PORT_Assert(numUsed < MAX_THREADS);
|
2020-01-24 01:47:36 +00:00
|
|
|
if (!(numUsed < MAX_THREADS)) {
|
2017-04-19 07:56:45 +00:00
|
|
|
PR_Unlock(threadLock);
|
|
|
|
return SECFailure;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = numUsed++;
|
|
|
|
slot = &threads[i];
|
|
|
|
slot->a = a;
|
|
|
|
slot->b = b;
|
|
|
|
slot->tid = tid;
|
|
|
|
|
|
|
|
slot->startFunc = startFunc;
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
slot->prThread = PR_CreateThread(PR_USER_THREAD,
|
|
|
|
thread_wrapper, slot,
|
|
|
|
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
|
|
|
|
PR_JOINABLE_THREAD, 0);
|
2017-04-19 07:56:45 +00:00
|
|
|
if (slot->prThread == NULL) {
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_Unlock(threadLock);
|
|
|
|
printf("strsclnt: Failed to launch thread!\n");
|
|
|
|
return SECFailure;
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
slot->inUse = 1;
|
2017-04-19 07:56:45 +00:00
|
|
|
PR_Unlock(threadLock);
|
|
|
|
PRINTF("strsclnt: Launched thread in slot %d \n", i);
|
|
|
|
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* join all the threads */
|
2020-01-24 01:47:36 +00:00
|
|
|
int
|
2017-04-19 07:56:45 +00:00
|
|
|
reap_threads(void)
|
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
int i;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
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) {
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_DestroyLock(threadLock);
|
|
|
|
threadLock = NULL;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2020-01-24 01:47:36 +00:00
|
|
|
PRLock *lock;
|
|
|
|
int count;
|
|
|
|
int waiters;
|
|
|
|
PRCondVar *condVar;
|
2017-04-19 07:56:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct lockedVarsStr lockedVars;
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
void
|
|
|
|
lockedVars_Init(lockedVars *lv)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
lv->count = 0;
|
2017-04-19 07:56:45 +00:00
|
|
|
lv->waiters = 0;
|
2020-01-24 01:47:36 +00:00
|
|
|
lv->lock = PR_NewLock();
|
2017-04-19 07:56:45 +00:00
|
|
|
lv->condVar = PR_NewCondVar(lv->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-01-24 01:47:36 +00:00
|
|
|
lockedVars_Destroy(lockedVars *lv)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
PR_DestroyCondVar(lv->condVar);
|
|
|
|
lv->condVar = NULL;
|
|
|
|
|
|
|
|
PR_DestroyLock(lv->lock);
|
|
|
|
lv->lock = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-01-24 01:47:36 +00:00
|
|
|
lockedVars_WaitForDone(lockedVars *lv)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
PR_Lock(lv->lock);
|
|
|
|
while (lv->count > 0) {
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
PR_Unlock(lv->lock);
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
int /* returns count */
|
|
|
|
lockedVars_AddToCount(lockedVars *lv, int addend)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
PR_Lock(lv->lock);
|
|
|
|
rv = lv->count += addend;
|
|
|
|
if (rv <= 0) {
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_NotifyCondVar(lv->condVar);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
PR_Unlock(lv->lock);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
SECStatus
|
2017-04-19 07:56:45 +00:00
|
|
|
do_writes(
|
2020-01-24 01:47:36 +00:00
|
|
|
void *a,
|
|
|
|
void *b,
|
|
|
|
int c)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
PRFileDesc *ssl_sock = (PRFileDesc *)a;
|
|
|
|
lockedVars *lv = (lockedVars *)b;
|
2017-04-19 07:56:45 +00:00
|
|
|
unsigned int sent = 0;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
while (sent < bigBuf.len) {
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
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;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
2020-01-24 01:47:36 +00:00
|
|
|
if (count >= 0) { /* last write didn't fail. */
|
|
|
|
PR_Shutdown(ssl_sock, PR_SHUTDOWN_SEND);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* notify the reader that we're done. */
|
|
|
|
lockedVars_AddToCount(lv, -1);
|
|
|
|
return (sent < bigBuf.len) ? SECFailure : SECSuccess;
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
int
|
|
|
|
handle_fdx_connection(PRFileDesc *ssl_sock, int connection)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
SECStatus result;
|
|
|
|
int firstTime = 1;
|
|
|
|
int countRead = 0;
|
|
|
|
lockedVars lv;
|
|
|
|
char *buf;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
lockedVars_Init(&lv);
|
|
|
|
lockedVars_AddToCount(&lv, 1);
|
|
|
|
|
|
|
|
/* Attempt to launch the writer thread. */
|
|
|
|
result = launch_thread(do_writes, ssl_sock, &lv, connection);
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
if (result != SECSuccess)
|
|
|
|
goto cleanup;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
buf = PR_Malloc(RD_BUF_SIZE);
|
|
|
|
|
|
|
|
if (buf) {
|
2020-01-24 01:47:36 +00:00
|
|
|
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;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait for writer to finish */
|
|
|
|
lockedVars_WaitForDone(&lv);
|
|
|
|
lockedVars_Destroy(&lv);
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
FPRINTF(stderr,
|
|
|
|
"strsclnt: connection %d read %d bytes total. -----------------------\n",
|
|
|
|
connection, countRead);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
/* Caller closes the socket. */
|
|
|
|
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
const char request[] = { "GET /abc HTTP/1.0\r\n\r\n" };
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
SECStatus
|
2020-01-24 01:47:36 +00:00
|
|
|
handle_connection(PRFileDesc *ssl_sock, int tid)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
int countRead = 0;
|
2017-04-19 07:56:45 +00:00
|
|
|
PRInt32 rv;
|
2020-01-24 01:47:36 +00:00
|
|
|
char *buf;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
buf = PR_Malloc(RD_BUF_SIZE);
|
|
|
|
if (!buf)
|
2020-01-24 01:47:36 +00:00
|
|
|
return SECFailure;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
/* compose the http request here. */
|
|
|
|
|
|
|
|
rv = PR_Send(ssl_sock, request, strlen(request), 0, maxInterval);
|
|
|
|
if (rv <= 0) {
|
2020-01-24 01:47:36 +00:00
|
|
|
errWarn("PR_Send");
|
|
|
|
PR_Free(buf);
|
|
|
|
buf = 0;
|
2017-04-19 07:56:45 +00:00
|
|
|
failed_already = 1;
|
2020-01-24 01:47:36 +00:00
|
|
|
return SECFailure;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
printSecurityInfo(ssl_sock);
|
|
|
|
|
|
|
|
/* read until EOF */
|
|
|
|
while (1) {
|
2020-01-24 01:47:36 +00:00
|
|
|
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,
|
2017-04-19 07:56:45 +00:00
|
|
|
"strsclnt: connection on thread %d read %d bytes (%d total).\n",
|
2020-01-24 01:47:36 +00:00
|
|
|
tid, rv, countRead);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
PR_Free(buf);
|
|
|
|
buf = 0;
|
|
|
|
|
|
|
|
/* Caller closes the socket. */
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
FPRINTF(stderr,
|
|
|
|
"strsclnt: connection on thread %d read %d bytes total. ---------\n",
|
|
|
|
tid, countRead);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
return SECSuccess; /* success */
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define USE_SOCK_PEER_ID 1
|
|
|
|
|
|
|
|
#ifdef USE_SOCK_PEER_ID
|
|
|
|
|
|
|
|
PRInt32 lastFullHandshakePeerID;
|
|
|
|
|
|
|
|
void
|
2020-01-24 01:47:36 +00:00
|
|
|
myHandshakeCallback(PRFileDesc *socket, void *arg)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
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.
|
|
|
|
*/
|
2020-01-24 01:47:36 +00:00
|
|
|
SECStatus
|
2017-04-19 07:56:45 +00:00
|
|
|
do_connects(
|
2020-01-24 01:47:36 +00:00
|
|
|
void *a,
|
|
|
|
void *b,
|
|
|
|
int tid)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
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;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
retry:
|
|
|
|
|
|
|
|
tcp_sock = PR_OpenTCPSocket(addr->raw.family);
|
|
|
|
if (tcp_sock == NULL) {
|
2020-01-24 01:47:36 +00:00
|
|
|
errExit("PR_OpenTCPSocket");
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
opt.option = PR_SockOpt_Nonblocking;
|
2017-04-19 07:56:45 +00:00
|
|
|
opt.value.non_blocking = PR_FALSE;
|
|
|
|
prStatus = PR_SetSocketOption(tcp_sock, &opt);
|
|
|
|
if (prStatus != PR_SUCCESS) {
|
2020-01-24 01:47:36 +00:00
|
|
|
errWarn("PR_SetSocketOption(PR_SockOpt_Nonblocking, PR_FALSE)");
|
|
|
|
PR_Close(tcp_sock);
|
|
|
|
return SECSuccess;
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
if (NoDelay) {
|
2020-01-24 01:47:36 +00:00
|
|
|
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;
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
|
|
|
}
|
2020-01-24 01:47:36 +00:00
|
|
|
if ((err == PR_CONNECT_REFUSED_ERROR) ||
|
|
|
|
(err == PR_CONNECT_RESET_ERROR)) {
|
|
|
|
int connections = numConnected;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_Close(tcp_sock);
|
2017-04-19 07:56:45 +00:00
|
|
|
PR_Lock(threadLock);
|
|
|
|
if (connections > 2 && active_threads >= connections) {
|
|
|
|
active_threads = connections - 1;
|
2020-01-24 01:47:36 +00:00
|
|
|
fprintf(stderr, "active_threads set down to %d\n",
|
2017-04-19 07:56:45 +00:00
|
|
|
active_threads);
|
|
|
|
}
|
|
|
|
PR_Unlock(threadLock);
|
|
|
|
|
|
|
|
if (QuitOnTimeout && sleepInterval > 40000) {
|
|
|
|
fprintf(stderr,
|
2020-01-24 01:47:36 +00:00
|
|
|
"strsclnt: Client timed out waiting for connection to server.\n");
|
2017-04-19 07:56:45 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_Sleep(PR_MillisecondsToInterval(sleepInterval));
|
|
|
|
sleepInterval <<= 1;
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
errWarn("PR_Connect");
|
|
|
|
goto done;
|
2017-04-19 07:56:45 +00:00
|
|
|
} 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) {
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_Close(tcp_sock);
|
|
|
|
return SECSuccess;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
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 */
|
2020-01-24 01:47:36 +00:00
|
|
|
if ((savid != 1) &&
|
|
|
|
(((savid <= total_connections_rounded_down_to_hundreds) &&
|
|
|
|
(conid <= fullhs)) ||
|
|
|
|
(conid * 100 <= total_connections_modulo_100 * fullhs)))
|
2017-04-19 07:56:45 +00:00
|
|
|
#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) {
|
2020-01-24 01:47:36 +00:00
|
|
|
errWarn("SSL_ResetHandshake");
|
|
|
|
goto done;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PR_ATOMIC_INCREMENT(&numConnected);
|
|
|
|
|
|
|
|
if (bigBuf.data != NULL) {
|
2020-01-24 01:47:36 +00:00
|
|
|
(void)handle_fdx_connection(ssl_sock, tid);
|
2017-04-19 07:56:45 +00:00
|
|
|
} else {
|
2020-01-24 01:47:36 +00:00
|
|
|
(void)handle_connection(ssl_sock, tid);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PR_ATOMIC_DECREMENT(&numConnected);
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (ssl_sock) {
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_Close(ssl_sock);
|
2017-04-19 07:56:45 +00:00
|
|
|
} else if (tcp_sock) {
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_Close(tcp_sock);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
2020-01-24 01:47:36 +00:00
|
|
|
return rv;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
2020-01-24 01:47:36 +00:00
|
|
|
PRLock *lock;
|
|
|
|
char *nickname;
|
|
|
|
CERTCertificate *cert;
|
|
|
|
SECKEYPrivateKey *key;
|
|
|
|
void *wincx;
|
2017-04-19 07:56:45 +00:00
|
|
|
} cert_and_key;
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
PRBool
|
|
|
|
FindCertAndKey(cert_and_key *Cert_And_Key)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
if ((NULL == Cert_And_Key->nickname) || (0 == strcmp(Cert_And_Key->nickname, "none"))) {
|
2017-04-19 07:56:45 +00:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
Cert_And_Key->cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(),
|
2020-01-24 01:47:36 +00:00
|
|
|
Cert_And_Key->nickname, certUsageSSLClient,
|
|
|
|
PR_FALSE, Cert_And_Key->wincx);
|
2017-04-19 07:56:45 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
PRBool
|
|
|
|
LoggedIn(CERTCertificate *cert, SECKEYPrivateKey *key)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
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))) {
|
2017-04-19 07:56:45 +00:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
2020-01-24 01:47:36 +00:00
|
|
|
|
2017-04-19 07:56:45 +00:00
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
SECStatus
|
|
|
|
StressClient_GetClientAuthData(void *arg,
|
|
|
|
PRFileDesc *socket,
|
|
|
|
struct CERTDistNamesStr *caNames,
|
|
|
|
struct CERTCertificateStr **pRetCert,
|
|
|
|
struct SECKEYPrivateKeyStr **pRetKey)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
cert_and_key *Cert_And_Key = (cert_and_key *)arg;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
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 */
|
2020-01-24 01:47:36 +00:00
|
|
|
if (PR_FALSE == LoggedIn(*pRetCert, *pRetKey)) {
|
2017-04-19 07:56:45 +00:00
|
|
|
/* 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 */
|
2020-01-24 01:47:36 +00:00
|
|
|
while (PR_FALSE == FindCertAndKey(Cert_And_Key)) {
|
2017-04-19 07:56:45 +00:00
|
|
|
PR_Sleep(PR_SecondsToInterval(1));
|
|
|
|
timeout++;
|
2020-01-24 01:47:36 +00:00
|
|
|
if (timeout >= 60) {
|
2017-04-19 07:56:45 +00:00
|
|
|
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. */
|
2020-01-24 01:47:36 +00:00
|
|
|
CERTCertificate *cert = NULL;
|
|
|
|
SECKEYPrivateKey *privkey = NULL;
|
|
|
|
CERTCertNicknames *names;
|
|
|
|
int i;
|
|
|
|
void *proto_win = NULL;
|
|
|
|
SECStatus rv = SECFailure;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
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(),
|
2020-01-24 01:47:36 +00:00
|
|
|
names->nicknames[i], certUsageSSLClient,
|
|
|
|
PR_FALSE, proto_win);
|
|
|
|
if (!cert)
|
2017-04-19 07:56:45 +00:00
|
|
|
continue;
|
|
|
|
/* Only check unexpired certs */
|
2020-01-24 01:47:36 +00:00
|
|
|
if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_TRUE) !=
|
|
|
|
secCertTimeValid) {
|
2017-04-19 07:56:45 +00:00
|
|
|
CERT_DestroyCertificate(cert);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
rv = NSS_CmpCertChainWCANames(cert, caNames);
|
2020-01-24 01:47:36 +00:00
|
|
|
if (rv == SECSuccess) {
|
2017-04-19 07:56:45 +00:00
|
|
|
privkey = PK11_FindKeyByAnyCert(cert, proto_win);
|
2020-01-24 01:47:36 +00:00
|
|
|
if (privkey)
|
2017-04-19 07:56:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
rv = SECFailure;
|
|
|
|
CERT_DestroyCertificate(cert);
|
|
|
|
}
|
|
|
|
CERT_FreeNicknames(names);
|
|
|
|
}
|
|
|
|
if (rv == SECSuccess) {
|
|
|
|
*pRetCert = cert;
|
2020-01-24 01:47:36 +00:00
|
|
|
*pRetKey = privkey;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
int
|
|
|
|
hexchar_to_int(int c)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
if (((c) >= '0') && ((c) <= '9'))
|
2020-01-24 01:47:36 +00:00
|
|
|
return (c) - '0';
|
2017-04-19 07:56:45 +00:00
|
|
|
if (((c) >= 'a') && ((c) <= 'f'))
|
2020-01-24 01:47:36 +00:00
|
|
|
return (c) - 'a' + 10;
|
2017-04-19 07:56:45 +00:00
|
|
|
if (((c) >= 'A') && ((c) <= 'F'))
|
2020-01-24 01:47:36 +00:00
|
|
|
return (c) - 'A' + 10;
|
2017-04-19 07:56:45 +00:00
|
|
|
failed_already = 1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
client_main(
|
2020-01-24 01:47:36 +00:00
|
|
|
unsigned short port,
|
|
|
|
int connections,
|
|
|
|
cert_and_key *Cert_And_Key,
|
|
|
|
const char *hostName,
|
|
|
|
const char *sniHostName,
|
|
|
|
PRBool allowIPv4,
|
|
|
|
PRBool allowIPv6)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
PRFileDesc *model_sock = NULL;
|
|
|
|
int i;
|
|
|
|
int rv;
|
|
|
|
PRStatus status;
|
|
|
|
PRNetAddr addr;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
status = PR_StringToNetAddr(hostName, &addr);
|
|
|
|
if (status == PR_SUCCESS) {
|
2020-01-24 01:47:36 +00:00
|
|
|
addr.inet.port = PR_htons(port);
|
2017-04-19 07:56:45 +00:00
|
|
|
} else {
|
2020-01-24 01:47:36 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* all suites except RSA_NULL_MD5 are enabled by Domestic Policy */
|
|
|
|
NSS_SetDomesticPolicy();
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
/* all SSL3 cipher suites are enabled by default. */
|
2017-04-19 07:56:45 +00:00
|
|
|
if (cipherString) {
|
|
|
|
int ndx;
|
|
|
|
|
|
|
|
/* disable all the ciphers, then enable the ones we want. */
|
|
|
|
disableAllSSLCiphers();
|
|
|
|
|
|
|
|
while (0 != (ndx = *cipherString)) {
|
2020-01-24 01:47:36 +00:00
|
|
|
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;
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* configure model SSL socket. */
|
|
|
|
|
|
|
|
model_sock = PR_OpenTCPSocket(addr.raw.family);
|
|
|
|
if (model_sock == NULL) {
|
2020-01-24 01:47:36 +00:00
|
|
|
errExit("PR_OpenTCPSocket for model socket");
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
model_sock = SSL_ImportFD(NULL, model_sock);
|
|
|
|
if (model_sock == NULL) {
|
2020-01-24 01:47:36 +00:00
|
|
|
errExit("SSL_ImportFD");
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* do SSL configuration. */
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
rv = SSL_OptionSet(model_sock, SSL_SECURITY, enabledVersions.min != 0);
|
2017-04-19 07:56:45 +00:00
|
|
|
if (rv < 0) {
|
2020-01-24 01:47:36 +00:00
|
|
|
errExit("SSL_OptionSet SSL_SECURITY");
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rv = SSL_VersionRangeSet(model_sock, &enabledVersions);
|
|
|
|
if (rv != SECSuccess) {
|
|
|
|
errExit("error setting SSL/TLS version range ");
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
if (enabledSigSchemes) {
|
|
|
|
rv = SSL_SignatureSchemePrefSet(model_sock, enabledSigSchemes,
|
|
|
|
enabledSigSchemeCount);
|
|
|
|
if (rv < 0) {
|
|
|
|
errExit("SSL_SignatureSchemePrefSet");
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bigBuf.data) { /* doing FDX */
|
2020-01-24 01:47:36 +00:00
|
|
|
rv = SSL_OptionSet(model_sock, SSL_ENABLE_FDX, 1);
|
|
|
|
if (rv < 0) {
|
|
|
|
errExit("SSL_OptionSet SSL_ENABLE_FDX");
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (NoReuse) {
|
2020-01-24 01:47:36 +00:00
|
|
|
rv = SSL_OptionSet(model_sock, SSL_NO_CACHE, 1);
|
|
|
|
if (rv < 0) {
|
|
|
|
errExit("SSL_OptionSet SSL_NO_CACHE");
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (disableLocking) {
|
|
|
|
rv = SSL_OptionSet(model_sock, SSL_NO_LOCKS, 1);
|
2020-01-24 01:47:36 +00:00
|
|
|
if (rv < 0) {
|
|
|
|
errExit("SSL_OptionSet SSL_NO_LOCKS");
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (enableSessionTickets) {
|
2020-01-24 01:47:36 +00:00
|
|
|
rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
|
|
|
|
if (rv != SECSuccess)
|
|
|
|
errExit("SSL_OptionSet SSL_ENABLE_SESSION_TICKETS");
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (enableCompression) {
|
2020-01-24 01:47:36 +00:00
|
|
|
rv = SSL_OptionSet(model_sock, SSL_ENABLE_DEFLATE, PR_TRUE);
|
|
|
|
if (rv != SECSuccess)
|
|
|
|
errExit("SSL_OptionSet SSL_ENABLE_DEFLATE");
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (enableFalseStart) {
|
2020-01-24 01:47:36 +00:00
|
|
|
rv = SSL_OptionSet(model_sock, SSL_ENABLE_FALSE_START, PR_TRUE);
|
|
|
|
if (rv != SECSuccess)
|
|
|
|
errExit("SSL_OptionSet SSL_ENABLE_FALSE_START");
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (enableCertStatus) {
|
2020-01-24 01:47:36 +00:00
|
|
|
rv = SSL_OptionSet(model_sock, SSL_ENABLE_OCSP_STAPLING, PR_TRUE);
|
|
|
|
if (rv != SECSuccess)
|
|
|
|
errExit("SSL_OptionSet SSL_ENABLE_OCSP_STAPLING");
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SSL_SetPKCS11PinArg(model_sock, &pwdata);
|
|
|
|
|
|
|
|
SSL_SetURL(model_sock, hostName);
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate,
|
|
|
|
(void *)CERT_GetDefaultCertDB());
|
2017-04-19 07:56:45 +00:00
|
|
|
SSL_BadCertHook(model_sock, myBadCertHandler, NULL);
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
SSL_GetClientAuthDataHook(model_sock, StressClient_GetClientAuthData, (void *)Cert_And_Key);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
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;
|
2020-01-24 01:47:36 +00:00
|
|
|
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;
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
if (remaining_connections > 0) {
|
2020-01-24 01:47:36 +00:00
|
|
|
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();
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
destroy_thread_data();
|
|
|
|
|
|
|
|
PR_Close(model_sock);
|
|
|
|
}
|
|
|
|
|
|
|
|
SECStatus
|
2020-01-24 01:47:36 +00:00
|
|
|
readBigFile(const char *fileName)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
PRFileInfo info;
|
|
|
|
PRStatus status;
|
|
|
|
SECStatus rv = SECFailure;
|
|
|
|
int count;
|
|
|
|
int hdrLen;
|
2017-04-19 07:56:45 +00:00
|
|
|
PRFileDesc *local_file_fd = NULL;
|
|
|
|
|
|
|
|
status = PR_GetFileInfo(fileName, &info);
|
|
|
|
|
|
|
|
if (status == PR_SUCCESS &&
|
2020-01-24 01:47:36 +00:00
|
|
|
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);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
2020-01-24 01:47:36 +00:00
|
|
|
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;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
/* Call the NSPR initialization routines */
|
2020-01-24 01:47:36 +00:00
|
|
|
PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
|
2017-04-19 07:56:45 +00:00
|
|
|
SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions);
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
tmp = strrchr(argv[0], '/');
|
|
|
|
tmp = tmp ? tmp + 1 : argv[0];
|
2017-04-19 07:56:45 +00:00
|
|
|
progName = strrchr(tmp, '\\');
|
|
|
|
progName = progName ? progName + 1 : tmp;
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
/* XXX: 'B' was used in the past but removed in 3.28,
|
|
|
|
* please leave some time before resuing it. */
|
2017-04-19 07:56:45 +00:00
|
|
|
optstate = PL_CreateOptState(argc, argv,
|
2020-01-24 01:47:36 +00:00
|
|
|
"46C:DJ:NP:TUV:W:a:c:d:f:gin:op:qst:uvw:z");
|
2017-04-19 07:56:45 +00:00
|
|
|
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
|
2020-01-24 01:47:36 +00:00
|
|
|
switch (optstate->option) {
|
|
|
|
case '4':
|
|
|
|
if (!allowIPv4) {
|
|
|
|
fprintf(stderr, "Only one of [-4, -6] can be specified.\n");
|
|
|
|
Usage();
|
|
|
|
}
|
|
|
|
allowIPv6 = PR_FALSE;
|
|
|
|
break;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
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;
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
default:
|
|
|
|
case '?':
|
|
|
|
Usage();
|
|
|
|
break;
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
PL_DestroyOptState(optstate);
|
|
|
|
|
|
|
|
if (!hostName || status == PL_OPT_BAD)
|
2020-01-24 01:47:36 +00:00
|
|
|
Usage();
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
if (fullhs != NO_FULLHS_PERCENTAGE && (fullhs < 0 || fullhs > 100 || NoReuse))
|
|
|
|
Usage();
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
if (port == 0)
|
2020-01-24 01:47:36 +00:00
|
|
|
Usage();
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
if (fileName)
|
2020-01-24 01:47:36 +00:00
|
|
|
readBigFile(fileName);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
PK11_SetPasswordFunc(SECU_GetModulePassword);
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
tmp = PR_GetEnvSecure("NSS_DEBUG_TIMEOUT");
|
2017-04-19 07:56:45 +00:00
|
|
|
if (tmp && tmp[0]) {
|
|
|
|
int sec = PORT_Atoi(tmp);
|
2020-01-24 01:47:36 +00:00
|
|
|
if (sec > 0) {
|
|
|
|
maxInterval = PR_SecondsToInterval(sec);
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Call the NSS initialization routines */
|
|
|
|
rv = NSS_Initialize(dir, "", "", SECMOD_DB, NSS_INIT_READONLY);
|
|
|
|
if (rv != SECSuccess) {
|
2020-01-24 01:47:36 +00:00
|
|
|
fputs("NSS_Init failed.\n", stderr);
|
|
|
|
exit(1);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
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)) {
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
if (Cert_And_Key.cert == NULL) {
|
|
|
|
fprintf(stderr, "strsclnt: Can't find certificate %s\n", Cert_And_Key.nickname);
|
|
|
|
exit(1);
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
if (Cert_And_Key.key == NULL) {
|
|
|
|
fprintf(stderr, "strsclnt: Can't find Private Key for cert %s\n",
|
|
|
|
Cert_And_Key.nickname);
|
|
|
|
exit(1);
|
|
|
|
}
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
client_main(port, connections, &Cert_And_Key, hostName,
|
2020-01-24 01:47:36 +00:00
|
|
|
sniHostName, allowIPv4, allowIPv6);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
/* clean up */
|
|
|
|
if (Cert_And_Key.cert) {
|
2020-01-24 01:47:36 +00:00
|
|
|
CERT_DestroyCertificate(Cert_And_Key.cert);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
if (Cert_And_Key.key) {
|
2020-01-24 01:47:36 +00:00
|
|
|
SECKEY_DestroyPrivateKey(Cert_And_Key.key);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
PORT_Free((SSLSignatureScheme *)enabledSigSchemes);
|
|
|
|
|
2017-04-19 07:56:45 +00:00
|
|
|
/* some final stats. */
|
2020-01-24 01:47:36 +00:00
|
|
|
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);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
if (!NoReuse) {
|
2020-01-24 01:47:36 +00:00
|
|
|
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);
|
2017-04-19 07:56:45 +00:00
|
|
|
} else {
|
2020-01-24 01:47:36 +00:00
|
|
|
printf("strsclnt: NoReuse - %d server certificates tested.\n",
|
2017-04-19 07:56:45 +00:00
|
|
|
certsTested);
|
2020-01-24 01:47:36 +00:00
|
|
|
exitVal = (ssl3stats->hsh_sid_cache_misses != connections) ||
|
|
|
|
(ssl3stats->hsh_sid_stateless_resumes != 0) ||
|
|
|
|
(certsTested != connections);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
2020-01-24 01:47:36 +00:00
|
|
|
exitVal = (exitVal || failed_already);
|
2017-04-19 07:56:45 +00:00
|
|
|
SSL_ClearSessionCache();
|
|
|
|
if (NSS_Shutdown() != SECSuccess) {
|
|
|
|
printf("strsclnt: NSS_Shutdown() failed.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
PR_Cleanup();
|
|
|
|
return exitVal;
|
|
|
|
}
|