Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | #include <stdio.h> |
michael@0 | 5 | #include <string.h> |
michael@0 | 6 | |
michael@0 | 7 | #include "secutil.h" |
michael@0 | 8 | #include "basicutil.h" |
michael@0 | 9 | |
michael@0 | 10 | #if defined(XP_UNIX) |
michael@0 | 11 | #include <unistd.h> |
michael@0 | 12 | #endif |
michael@0 | 13 | #include <stdlib.h> |
michael@0 | 14 | #include <errno.h> |
michael@0 | 15 | #include <fcntl.h> |
michael@0 | 16 | #include <stdarg.h> |
michael@0 | 17 | |
michael@0 | 18 | #include "plgetopt.h" |
michael@0 | 19 | |
michael@0 | 20 | #include "nspr.h" |
michael@0 | 21 | #include "prio.h" |
michael@0 | 22 | #include "prnetdb.h" |
michael@0 | 23 | #include "prerror.h" |
michael@0 | 24 | |
michael@0 | 25 | #include "pk11func.h" |
michael@0 | 26 | #include "secitem.h" |
michael@0 | 27 | #include "sslproto.h" |
michael@0 | 28 | #include "nss.h" |
michael@0 | 29 | #include "ssl.h" |
michael@0 | 30 | |
michael@0 | 31 | #ifndef PORT_Sprintf |
michael@0 | 32 | #define PORT_Sprintf sprintf |
michael@0 | 33 | #endif |
michael@0 | 34 | |
michael@0 | 35 | #ifndef PORT_Strstr |
michael@0 | 36 | #define PORT_Strstr strstr |
michael@0 | 37 | #endif |
michael@0 | 38 | |
michael@0 | 39 | #ifndef PORT_Malloc |
michael@0 | 40 | #define PORT_Malloc PR_Malloc |
michael@0 | 41 | #endif |
michael@0 | 42 | |
michael@0 | 43 | #define RD_BUF_SIZE (60 * 1024) |
michael@0 | 44 | |
michael@0 | 45 | /* Include these cipher suite arrays to re-use tstclnt's |
michael@0 | 46 | * cipher selection code. |
michael@0 | 47 | */ |
michael@0 | 48 | |
michael@0 | 49 | int ssl2CipherSuites[] = { |
michael@0 | 50 | SSL_EN_RC4_128_WITH_MD5, /* A */ |
michael@0 | 51 | SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ |
michael@0 | 52 | SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ |
michael@0 | 53 | SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ |
michael@0 | 54 | SSL_EN_DES_64_CBC_WITH_MD5, /* E */ |
michael@0 | 55 | SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ |
michael@0 | 56 | 0 |
michael@0 | 57 | }; |
michael@0 | 58 | |
michael@0 | 59 | int ssl3CipherSuites[] = { |
michael@0 | 60 | -1, /* SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA* a */ |
michael@0 | 61 | -1, /* SSL_FORTEZZA_DMS_WITH_RC4_128_SHA * b */ |
michael@0 | 62 | TLS_RSA_WITH_RC4_128_MD5, /* c */ |
michael@0 | 63 | TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ |
michael@0 | 64 | TLS_RSA_WITH_DES_CBC_SHA, /* e */ |
michael@0 | 65 | TLS_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ |
michael@0 | 66 | TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ |
michael@0 | 67 | -1, /* SSL_FORTEZZA_DMS_WITH_NULL_SHA * h */ |
michael@0 | 68 | TLS_RSA_WITH_NULL_MD5, /* i */ |
michael@0 | 69 | SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ |
michael@0 | 70 | SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ |
michael@0 | 71 | TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ |
michael@0 | 72 | TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ |
michael@0 | 73 | TLS_RSA_WITH_RC4_128_SHA, /* n */ |
michael@0 | 74 | TLS_DHE_DSS_WITH_RC4_128_SHA, /* o */ |
michael@0 | 75 | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* p */ |
michael@0 | 76 | TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* q */ |
michael@0 | 77 | TLS_DHE_RSA_WITH_DES_CBC_SHA, /* r */ |
michael@0 | 78 | TLS_DHE_DSS_WITH_DES_CBC_SHA, /* s */ |
michael@0 | 79 | TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* t */ |
michael@0 | 80 | TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* u */ |
michael@0 | 81 | TLS_RSA_WITH_AES_128_CBC_SHA, /* v */ |
michael@0 | 82 | TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* w */ |
michael@0 | 83 | TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* x */ |
michael@0 | 84 | TLS_RSA_WITH_AES_256_CBC_SHA, /* y */ |
michael@0 | 85 | TLS_RSA_WITH_NULL_SHA, /* z */ |
michael@0 | 86 | 0 |
michael@0 | 87 | }; |
michael@0 | 88 | |
michael@0 | 89 | #define NO_FULLHS_PERCENTAGE -1 |
michael@0 | 90 | |
michael@0 | 91 | /* This global string is so that client main can see |
michael@0 | 92 | * which ciphers to use. |
michael@0 | 93 | */ |
michael@0 | 94 | |
michael@0 | 95 | static const char *cipherString; |
michael@0 | 96 | |
michael@0 | 97 | static PRInt32 certsTested; |
michael@0 | 98 | static int MakeCertOK; |
michael@0 | 99 | static int NoReuse; |
michael@0 | 100 | static int fullhs = NO_FULLHS_PERCENTAGE; /* percentage of full handshakes to |
michael@0 | 101 | ** perform */ |
michael@0 | 102 | static PRInt32 globalconid = 0; /* atomically set */ |
michael@0 | 103 | static int total_connections; /* total number of connections to perform */ |
michael@0 | 104 | static int total_connections_rounded_down_to_hundreds; |
michael@0 | 105 | static int total_connections_modulo_100; |
michael@0 | 106 | |
michael@0 | 107 | static PRBool NoDelay; |
michael@0 | 108 | static PRBool QuitOnTimeout = PR_FALSE; |
michael@0 | 109 | static PRBool ThrottleUp = PR_FALSE; |
michael@0 | 110 | |
michael@0 | 111 | static PRLock * threadLock; /* protects the global variables below */ |
michael@0 | 112 | static PRTime lastConnectFailure; |
michael@0 | 113 | static PRTime lastConnectSuccess; |
michael@0 | 114 | static PRTime lastThrottleUp; |
michael@0 | 115 | static PRInt32 remaining_connections; /* number of connections left */ |
michael@0 | 116 | static int active_threads = 8; /* number of threads currently trying to |
michael@0 | 117 | ** connect */ |
michael@0 | 118 | static PRInt32 numUsed; |
michael@0 | 119 | /* end of variables protected by threadLock */ |
michael@0 | 120 | |
michael@0 | 121 | static SSL3Statistics * ssl3stats; |
michael@0 | 122 | |
michael@0 | 123 | static int failed_already = 0; |
michael@0 | 124 | static SSLVersionRange enabledVersions; |
michael@0 | 125 | static PRBool enableSSL2 = PR_TRUE; |
michael@0 | 126 | static PRBool bypassPKCS11 = PR_FALSE; |
michael@0 | 127 | static PRBool disableLocking = PR_FALSE; |
michael@0 | 128 | static PRBool ignoreErrors = PR_FALSE; |
michael@0 | 129 | static PRBool enableSessionTickets = PR_FALSE; |
michael@0 | 130 | static PRBool enableCompression = PR_FALSE; |
michael@0 | 131 | static PRBool enableFalseStart = PR_FALSE; |
michael@0 | 132 | static PRBool enableCertStatus = PR_FALSE; |
michael@0 | 133 | |
michael@0 | 134 | PRIntervalTime maxInterval = PR_INTERVAL_NO_TIMEOUT; |
michael@0 | 135 | |
michael@0 | 136 | char * progName; |
michael@0 | 137 | |
michael@0 | 138 | secuPWData pwdata = { PW_NONE, 0 }; |
michael@0 | 139 | |
michael@0 | 140 | int stopping; |
michael@0 | 141 | int verbose; |
michael@0 | 142 | SECItem bigBuf; |
michael@0 | 143 | |
michael@0 | 144 | #define PRINTF if (verbose) printf |
michael@0 | 145 | #define FPRINTF if (verbose) fprintf |
michael@0 | 146 | |
michael@0 | 147 | static void |
michael@0 | 148 | Usage(const char *progName) |
michael@0 | 149 | { |
michael@0 | 150 | fprintf(stderr, |
michael@0 | 151 | "Usage: %s [-n nickname] [-p port] [-d dbdir] [-c connections]\n" |
michael@0 | 152 | " [-BDNovqs] [-f filename] [-N | -P percentage]\n" |
michael@0 | 153 | " [-w dbpasswd] [-C cipher(s)] [-t threads] [-W pwfile]\n" |
michael@0 | 154 | " [-V [min-version]:[max-version]] [-a sniHostName] hostname\n" |
michael@0 | 155 | " where -v means verbose\n" |
michael@0 | 156 | " -o flag is interpreted as follows:\n" |
michael@0 | 157 | " 1 -o means override the result of server certificate validation.\n" |
michael@0 | 158 | " 2 -o's mean skip server certificate validation altogether.\n" |
michael@0 | 159 | " -D means no TCP delays\n" |
michael@0 | 160 | " -q means quit when server gone (timeout rather than retry forever)\n" |
michael@0 | 161 | " -s means disable SSL socket locking\n" |
michael@0 | 162 | " -N means no session reuse\n" |
michael@0 | 163 | " -P means do a specified percentage of full handshakes (0-100)\n" |
michael@0 | 164 | " -V [min]:[max] restricts the set of enabled SSL/TLS protocols versions.\n" |
michael@0 | 165 | " All versions are enabled by default.\n" |
michael@0 | 166 | " Possible values for min/max: ssl2 ssl3 tls1.0 tls1.1 tls1.2\n" |
michael@0 | 167 | " Example: \"-V ssl3:\" enables SSL 3 and newer.\n" |
michael@0 | 168 | " -U means enable throttling up threads\n" |
michael@0 | 169 | " -B bypasses the PKCS11 layer for SSL encryption and MACing\n" |
michael@0 | 170 | " -T enable the cert_status extension (OCSP stapling)\n" |
michael@0 | 171 | " -u enable TLS Session Ticket extension\n" |
michael@0 | 172 | " -z enable compression\n" |
michael@0 | 173 | " -g enable false start\n", |
michael@0 | 174 | progName); |
michael@0 | 175 | exit(1); |
michael@0 | 176 | } |
michael@0 | 177 | |
michael@0 | 178 | |
michael@0 | 179 | static void |
michael@0 | 180 | errWarn(char * funcString) |
michael@0 | 181 | { |
michael@0 | 182 | PRErrorCode perr = PR_GetError(); |
michael@0 | 183 | PRInt32 oserr = PR_GetOSError(); |
michael@0 | 184 | const char * errString = SECU_Strerror(perr); |
michael@0 | 185 | |
michael@0 | 186 | fprintf(stderr, "strsclnt: %s returned error %d, OS error %d: %s\n", |
michael@0 | 187 | funcString, perr, oserr, errString); |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | static void |
michael@0 | 191 | errExit(char * funcString) |
michael@0 | 192 | { |
michael@0 | 193 | errWarn(funcString); |
michael@0 | 194 | exit(1); |
michael@0 | 195 | } |
michael@0 | 196 | |
michael@0 | 197 | /************************************************************************** |
michael@0 | 198 | ** |
michael@0 | 199 | ** Routines for disabling SSL ciphers. |
michael@0 | 200 | ** |
michael@0 | 201 | **************************************************************************/ |
michael@0 | 202 | |
michael@0 | 203 | void |
michael@0 | 204 | disableAllSSLCiphers(void) |
michael@0 | 205 | { |
michael@0 | 206 | const PRUint16 *cipherSuites = SSL_GetImplementedCiphers(); |
michael@0 | 207 | int i = SSL_GetNumImplementedCiphers(); |
michael@0 | 208 | SECStatus rv; |
michael@0 | 209 | |
michael@0 | 210 | /* disable all the SSL3 cipher suites */ |
michael@0 | 211 | while (--i >= 0) { |
michael@0 | 212 | PRUint16 suite = cipherSuites[i]; |
michael@0 | 213 | rv = SSL_CipherPrefSetDefault(suite, PR_FALSE); |
michael@0 | 214 | if (rv != SECSuccess) { |
michael@0 | 215 | printf("SSL_CipherPrefSetDefault didn't like value 0x%04x (i = %d)\n", |
michael@0 | 216 | suite, i); |
michael@0 | 217 | errWarn("SSL_CipherPrefSetDefault"); |
michael@0 | 218 | exit(2); |
michael@0 | 219 | } |
michael@0 | 220 | } |
michael@0 | 221 | } |
michael@0 | 222 | |
michael@0 | 223 | /* This invokes the "default" AuthCert handler in libssl. |
michael@0 | 224 | ** The only reason to use this one is that it prints out info as it goes. |
michael@0 | 225 | */ |
michael@0 | 226 | static SECStatus |
michael@0 | 227 | mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, |
michael@0 | 228 | PRBool isServer) |
michael@0 | 229 | { |
michael@0 | 230 | SECStatus rv; |
michael@0 | 231 | CERTCertificate * peerCert; |
michael@0 | 232 | const SECItemArray *csa; |
michael@0 | 233 | |
michael@0 | 234 | if (MakeCertOK>=2) { |
michael@0 | 235 | return SECSuccess; |
michael@0 | 236 | } |
michael@0 | 237 | peerCert = SSL_PeerCertificate(fd); |
michael@0 | 238 | |
michael@0 | 239 | PRINTF("strsclnt: Subject: %s\nstrsclnt: Issuer : %s\n", |
michael@0 | 240 | peerCert->subjectName, peerCert->issuerName); |
michael@0 | 241 | csa = SSL_PeerStapledOCSPResponses(fd); |
michael@0 | 242 | if (csa) { |
michael@0 | 243 | PRINTF("Received %d Cert Status items (OCSP stapled data)\n", |
michael@0 | 244 | csa->len); |
michael@0 | 245 | } |
michael@0 | 246 | /* invoke the "default" AuthCert handler. */ |
michael@0 | 247 | rv = SSL_AuthCertificate(arg, fd, checkSig, isServer); |
michael@0 | 248 | |
michael@0 | 249 | PR_ATOMIC_INCREMENT(&certsTested); |
michael@0 | 250 | if (rv == SECSuccess) { |
michael@0 | 251 | fputs("strsclnt: -- SSL: Server Certificate Validated.\n", stderr); |
michael@0 | 252 | } |
michael@0 | 253 | CERT_DestroyCertificate(peerCert); |
michael@0 | 254 | /* error, if any, will be displayed by the Bad Cert Handler. */ |
michael@0 | 255 | return rv; |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | static SECStatus |
michael@0 | 259 | myBadCertHandler( void *arg, PRFileDesc *fd) |
michael@0 | 260 | { |
michael@0 | 261 | PRErrorCode err = PR_GetError(); |
michael@0 | 262 | if (!MakeCertOK) |
michael@0 | 263 | fprintf(stderr, |
michael@0 | 264 | "strsclnt: -- SSL: Server Certificate Invalid, err %d.\n%s\n", |
michael@0 | 265 | err, SECU_Strerror(err)); |
michael@0 | 266 | return (MakeCertOK ? SECSuccess : SECFailure); |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | void |
michael@0 | 270 | printSecurityInfo(PRFileDesc *fd) |
michael@0 | 271 | { |
michael@0 | 272 | CERTCertificate * cert = NULL; |
michael@0 | 273 | SSL3Statistics * ssl3stats = SSL_GetStatistics(); |
michael@0 | 274 | SECStatus result; |
michael@0 | 275 | SSLChannelInfo channel; |
michael@0 | 276 | SSLCipherSuiteInfo suite; |
michael@0 | 277 | |
michael@0 | 278 | static int only_once; |
michael@0 | 279 | |
michael@0 | 280 | if (only_once && verbose < 2) |
michael@0 | 281 | return; |
michael@0 | 282 | only_once = 1; |
michael@0 | 283 | |
michael@0 | 284 | result = SSL_GetChannelInfo(fd, &channel, sizeof channel); |
michael@0 | 285 | if (result == SECSuccess && |
michael@0 | 286 | channel.length == sizeof channel && |
michael@0 | 287 | channel.cipherSuite) { |
michael@0 | 288 | result = SSL_GetCipherSuiteInfo(channel.cipherSuite, |
michael@0 | 289 | &suite, sizeof suite); |
michael@0 | 290 | if (result == SECSuccess) { |
michael@0 | 291 | FPRINTF(stderr, |
michael@0 | 292 | "strsclnt: SSL version %d.%d using %d-bit %s with %d-bit %s MAC\n", |
michael@0 | 293 | channel.protocolVersion >> 8, channel.protocolVersion & 0xff, |
michael@0 | 294 | suite.effectiveKeyBits, suite.symCipherName, |
michael@0 | 295 | suite.macBits, suite.macAlgorithmName); |
michael@0 | 296 | FPRINTF(stderr, |
michael@0 | 297 | "strsclnt: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" |
michael@0 | 298 | " Compression: %s\n", |
michael@0 | 299 | channel.authKeyBits, suite.authAlgorithmName, |
michael@0 | 300 | channel.keaKeyBits, suite.keaTypeName, |
michael@0 | 301 | channel.compressionMethodName); |
michael@0 | 302 | } |
michael@0 | 303 | } |
michael@0 | 304 | |
michael@0 | 305 | cert = SSL_LocalCertificate(fd); |
michael@0 | 306 | if (!cert) |
michael@0 | 307 | cert = SSL_PeerCertificate(fd); |
michael@0 | 308 | |
michael@0 | 309 | if (verbose && cert) { |
michael@0 | 310 | char * ip = CERT_NameToAscii(&cert->issuer); |
michael@0 | 311 | char * sp = CERT_NameToAscii(&cert->subject); |
michael@0 | 312 | if (sp) { |
michael@0 | 313 | fprintf(stderr, "strsclnt: subject DN: %s\n", sp); |
michael@0 | 314 | PORT_Free(sp); |
michael@0 | 315 | } |
michael@0 | 316 | if (ip) { |
michael@0 | 317 | fprintf(stderr, "strsclnt: issuer DN: %s\n", ip); |
michael@0 | 318 | PORT_Free(ip); |
michael@0 | 319 | } |
michael@0 | 320 | } |
michael@0 | 321 | if (cert) { |
michael@0 | 322 | CERT_DestroyCertificate(cert); |
michael@0 | 323 | cert = NULL; |
michael@0 | 324 | } |
michael@0 | 325 | fprintf(stderr, |
michael@0 | 326 | "strsclnt: %ld cache hits; %ld cache misses, %ld cache not reusable\n" |
michael@0 | 327 | " %ld stateless resumes\n", |
michael@0 | 328 | ssl3stats->hsh_sid_cache_hits, |
michael@0 | 329 | ssl3stats->hsh_sid_cache_misses, |
michael@0 | 330 | ssl3stats->hsh_sid_cache_not_ok, |
michael@0 | 331 | ssl3stats->hsh_sid_stateless_resumes); |
michael@0 | 332 | |
michael@0 | 333 | } |
michael@0 | 334 | |
michael@0 | 335 | /************************************************************************** |
michael@0 | 336 | ** Begin thread management routines and data. |
michael@0 | 337 | **************************************************************************/ |
michael@0 | 338 | |
michael@0 | 339 | #define MAX_THREADS 128 |
michael@0 | 340 | |
michael@0 | 341 | typedef int startFn(void *a, void *b, int c); |
michael@0 | 342 | |
michael@0 | 343 | |
michael@0 | 344 | static PRInt32 numConnected; |
michael@0 | 345 | static int max_threads; /* peak threads allowed */ |
michael@0 | 346 | |
michael@0 | 347 | typedef struct perThreadStr { |
michael@0 | 348 | void * a; |
michael@0 | 349 | void * b; |
michael@0 | 350 | int tid; |
michael@0 | 351 | int rv; |
michael@0 | 352 | startFn * startFunc; |
michael@0 | 353 | PRThread * prThread; |
michael@0 | 354 | PRBool inUse; |
michael@0 | 355 | } perThread; |
michael@0 | 356 | |
michael@0 | 357 | perThread threads[MAX_THREADS]; |
michael@0 | 358 | |
michael@0 | 359 | void |
michael@0 | 360 | thread_wrapper(void * arg) |
michael@0 | 361 | { |
michael@0 | 362 | perThread * slot = (perThread *)arg; |
michael@0 | 363 | PRBool done = PR_FALSE; |
michael@0 | 364 | |
michael@0 | 365 | do { |
michael@0 | 366 | PRBool doop = PR_FALSE; |
michael@0 | 367 | PRBool dosleep = PR_FALSE; |
michael@0 | 368 | PRTime now = PR_Now(); |
michael@0 | 369 | |
michael@0 | 370 | PR_Lock(threadLock); |
michael@0 | 371 | if (! (slot->tid < active_threads)) { |
michael@0 | 372 | /* this thread isn't supposed to be running */ |
michael@0 | 373 | if (!ThrottleUp) { |
michael@0 | 374 | /* we'll never need this thread again, so abort it */ |
michael@0 | 375 | done = PR_TRUE; |
michael@0 | 376 | } else if (remaining_connections > 0) { |
michael@0 | 377 | /* we may still need this thread, so just sleep for 1s */ |
michael@0 | 378 | dosleep = PR_TRUE; |
michael@0 | 379 | /* the conditions to trigger a throttle up are : |
michael@0 | 380 | ** 1. last PR_Connect failure must have happened more than |
michael@0 | 381 | ** 10s ago |
michael@0 | 382 | ** 2. last throttling up must have happened more than 0.5s ago |
michael@0 | 383 | ** 3. there must be a more recent PR_Connect success than |
michael@0 | 384 | ** failure |
michael@0 | 385 | */ |
michael@0 | 386 | if ( (now - lastConnectFailure > 10 * PR_USEC_PER_SEC) && |
michael@0 | 387 | ( (!lastThrottleUp) || ( (now - lastThrottleUp) >= |
michael@0 | 388 | (PR_USEC_PER_SEC/2)) ) && |
michael@0 | 389 | (lastConnectSuccess > lastConnectFailure) ) { |
michael@0 | 390 | /* try throttling up by one thread */ |
michael@0 | 391 | active_threads = PR_MIN(max_threads, active_threads+1); |
michael@0 | 392 | fprintf(stderr,"active_threads set up to %d\n", |
michael@0 | 393 | active_threads); |
michael@0 | 394 | lastThrottleUp = PR_MAX(now, lastThrottleUp); |
michael@0 | 395 | } |
michael@0 | 396 | } else { |
michael@0 | 397 | /* no more connections left, we are done */ |
michael@0 | 398 | done = PR_TRUE; |
michael@0 | 399 | } |
michael@0 | 400 | } else { |
michael@0 | 401 | /* this thread should run */ |
michael@0 | 402 | if (--remaining_connections >= 0) { /* protected by threadLock */ |
michael@0 | 403 | doop = PR_TRUE; |
michael@0 | 404 | } else { |
michael@0 | 405 | done = PR_TRUE; |
michael@0 | 406 | } |
michael@0 | 407 | } |
michael@0 | 408 | PR_Unlock(threadLock); |
michael@0 | 409 | if (doop) { |
michael@0 | 410 | slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->tid); |
michael@0 | 411 | PRINTF("strsclnt: Thread in slot %d returned %d\n", |
michael@0 | 412 | slot->tid, slot->rv); |
michael@0 | 413 | } |
michael@0 | 414 | if (dosleep) { |
michael@0 | 415 | PR_Sleep(PR_SecondsToInterval(1)); |
michael@0 | 416 | } |
michael@0 | 417 | } while (!done && (!failed_already || ignoreErrors)); |
michael@0 | 418 | } |
michael@0 | 419 | |
michael@0 | 420 | SECStatus |
michael@0 | 421 | launch_thread( |
michael@0 | 422 | startFn * startFunc, |
michael@0 | 423 | void * a, |
michael@0 | 424 | void * b, |
michael@0 | 425 | int tid) |
michael@0 | 426 | { |
michael@0 | 427 | PRUint32 i; |
michael@0 | 428 | perThread * slot; |
michael@0 | 429 | |
michael@0 | 430 | PR_Lock(threadLock); |
michael@0 | 431 | |
michael@0 | 432 | PORT_Assert(numUsed < MAX_THREADS); |
michael@0 | 433 | if (! (numUsed < MAX_THREADS)) { |
michael@0 | 434 | PR_Unlock(threadLock); |
michael@0 | 435 | return SECFailure; |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | i = numUsed++; |
michael@0 | 439 | slot = &threads[i]; |
michael@0 | 440 | slot->a = a; |
michael@0 | 441 | slot->b = b; |
michael@0 | 442 | slot->tid = tid; |
michael@0 | 443 | |
michael@0 | 444 | slot->startFunc = startFunc; |
michael@0 | 445 | |
michael@0 | 446 | slot->prThread = PR_CreateThread(PR_USER_THREAD, |
michael@0 | 447 | thread_wrapper, slot, |
michael@0 | 448 | PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, |
michael@0 | 449 | PR_JOINABLE_THREAD, 0); |
michael@0 | 450 | if (slot->prThread == NULL) { |
michael@0 | 451 | PR_Unlock(threadLock); |
michael@0 | 452 | printf("strsclnt: Failed to launch thread!\n"); |
michael@0 | 453 | return SECFailure; |
michael@0 | 454 | } |
michael@0 | 455 | |
michael@0 | 456 | slot->inUse = 1; |
michael@0 | 457 | PR_Unlock(threadLock); |
michael@0 | 458 | PRINTF("strsclnt: Launched thread in slot %d \n", i); |
michael@0 | 459 | |
michael@0 | 460 | return SECSuccess; |
michael@0 | 461 | } |
michael@0 | 462 | |
michael@0 | 463 | /* join all the threads */ |
michael@0 | 464 | int |
michael@0 | 465 | reap_threads(void) |
michael@0 | 466 | { |
michael@0 | 467 | int i; |
michael@0 | 468 | |
michael@0 | 469 | for (i = 0; i < MAX_THREADS; ++i) { |
michael@0 | 470 | if (threads[i].prThread) { |
michael@0 | 471 | PR_JoinThread(threads[i].prThread); |
michael@0 | 472 | threads[i].prThread = NULL; |
michael@0 | 473 | } |
michael@0 | 474 | } |
michael@0 | 475 | return 0; |
michael@0 | 476 | } |
michael@0 | 477 | |
michael@0 | 478 | void |
michael@0 | 479 | destroy_thread_data(void) |
michael@0 | 480 | { |
michael@0 | 481 | PORT_Memset(threads, 0, sizeof threads); |
michael@0 | 482 | |
michael@0 | 483 | if (threadLock) { |
michael@0 | 484 | PR_DestroyLock(threadLock); |
michael@0 | 485 | threadLock = NULL; |
michael@0 | 486 | } |
michael@0 | 487 | } |
michael@0 | 488 | |
michael@0 | 489 | void |
michael@0 | 490 | init_thread_data(void) |
michael@0 | 491 | { |
michael@0 | 492 | threadLock = PR_NewLock(); |
michael@0 | 493 | } |
michael@0 | 494 | |
michael@0 | 495 | /************************************************************************** |
michael@0 | 496 | ** End thread management routines. |
michael@0 | 497 | **************************************************************************/ |
michael@0 | 498 | |
michael@0 | 499 | PRBool useModelSocket = PR_TRUE; |
michael@0 | 500 | |
michael@0 | 501 | static const char stopCmd[] = { "GET /stop " }; |
michael@0 | 502 | static const char outHeader[] = { |
michael@0 | 503 | "HTTP/1.0 200 OK\r\n" |
michael@0 | 504 | "Server: Netscape-Enterprise/2.0a\r\n" |
michael@0 | 505 | "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" |
michael@0 | 506 | "Content-type: text/plain\r\n" |
michael@0 | 507 | "\r\n" |
michael@0 | 508 | }; |
michael@0 | 509 | |
michael@0 | 510 | struct lockedVarsStr { |
michael@0 | 511 | PRLock * lock; |
michael@0 | 512 | int count; |
michael@0 | 513 | int waiters; |
michael@0 | 514 | PRCondVar * condVar; |
michael@0 | 515 | }; |
michael@0 | 516 | |
michael@0 | 517 | typedef struct lockedVarsStr lockedVars; |
michael@0 | 518 | |
michael@0 | 519 | void |
michael@0 | 520 | lockedVars_Init( lockedVars * lv) |
michael@0 | 521 | { |
michael@0 | 522 | lv->count = 0; |
michael@0 | 523 | lv->waiters = 0; |
michael@0 | 524 | lv->lock = PR_NewLock(); |
michael@0 | 525 | lv->condVar = PR_NewCondVar(lv->lock); |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | void |
michael@0 | 529 | lockedVars_Destroy( lockedVars * lv) |
michael@0 | 530 | { |
michael@0 | 531 | PR_DestroyCondVar(lv->condVar); |
michael@0 | 532 | lv->condVar = NULL; |
michael@0 | 533 | |
michael@0 | 534 | PR_DestroyLock(lv->lock); |
michael@0 | 535 | lv->lock = NULL; |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | void |
michael@0 | 539 | lockedVars_WaitForDone(lockedVars * lv) |
michael@0 | 540 | { |
michael@0 | 541 | PR_Lock(lv->lock); |
michael@0 | 542 | while (lv->count > 0) { |
michael@0 | 543 | PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 544 | } |
michael@0 | 545 | PR_Unlock(lv->lock); |
michael@0 | 546 | } |
michael@0 | 547 | |
michael@0 | 548 | int /* returns count */ |
michael@0 | 549 | lockedVars_AddToCount(lockedVars * lv, int addend) |
michael@0 | 550 | { |
michael@0 | 551 | int rv; |
michael@0 | 552 | |
michael@0 | 553 | PR_Lock(lv->lock); |
michael@0 | 554 | rv = lv->count += addend; |
michael@0 | 555 | if (rv <= 0) { |
michael@0 | 556 | PR_NotifyCondVar(lv->condVar); |
michael@0 | 557 | } |
michael@0 | 558 | PR_Unlock(lv->lock); |
michael@0 | 559 | return rv; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | int |
michael@0 | 563 | do_writes( |
michael@0 | 564 | void * a, |
michael@0 | 565 | void * b, |
michael@0 | 566 | int c) |
michael@0 | 567 | { |
michael@0 | 568 | PRFileDesc * ssl_sock = (PRFileDesc *)a; |
michael@0 | 569 | lockedVars * lv = (lockedVars *)b; |
michael@0 | 570 | int sent = 0; |
michael@0 | 571 | int count = 0; |
michael@0 | 572 | |
michael@0 | 573 | while (sent < bigBuf.len) { |
michael@0 | 574 | |
michael@0 | 575 | count = PR_Send(ssl_sock, bigBuf.data + sent, bigBuf.len - sent, |
michael@0 | 576 | 0, maxInterval); |
michael@0 | 577 | if (count < 0) { |
michael@0 | 578 | errWarn("PR_Send bigBuf"); |
michael@0 | 579 | break; |
michael@0 | 580 | } |
michael@0 | 581 | FPRINTF(stderr, "strsclnt: PR_Send wrote %d bytes from bigBuf\n", |
michael@0 | 582 | count ); |
michael@0 | 583 | sent += count; |
michael@0 | 584 | } |
michael@0 | 585 | if (count >= 0) { /* last write didn't fail. */ |
michael@0 | 586 | PR_Shutdown(ssl_sock, PR_SHUTDOWN_SEND); |
michael@0 | 587 | } |
michael@0 | 588 | |
michael@0 | 589 | /* notify the reader that we're done. */ |
michael@0 | 590 | lockedVars_AddToCount(lv, -1); |
michael@0 | 591 | return (sent < bigBuf.len) ? SECFailure : SECSuccess; |
michael@0 | 592 | } |
michael@0 | 593 | |
michael@0 | 594 | int |
michael@0 | 595 | handle_fdx_connection( PRFileDesc * ssl_sock, int connection) |
michael@0 | 596 | { |
michael@0 | 597 | SECStatus result; |
michael@0 | 598 | int firstTime = 1; |
michael@0 | 599 | int countRead = 0; |
michael@0 | 600 | lockedVars lv; |
michael@0 | 601 | char *buf; |
michael@0 | 602 | |
michael@0 | 603 | |
michael@0 | 604 | lockedVars_Init(&lv); |
michael@0 | 605 | lockedVars_AddToCount(&lv, 1); |
michael@0 | 606 | |
michael@0 | 607 | /* Attempt to launch the writer thread. */ |
michael@0 | 608 | result = launch_thread(do_writes, ssl_sock, &lv, connection); |
michael@0 | 609 | |
michael@0 | 610 | if (result != SECSuccess) |
michael@0 | 611 | goto cleanup; |
michael@0 | 612 | |
michael@0 | 613 | buf = PR_Malloc(RD_BUF_SIZE); |
michael@0 | 614 | |
michael@0 | 615 | if (buf) { |
michael@0 | 616 | do { |
michael@0 | 617 | /* do reads here. */ |
michael@0 | 618 | PRInt32 count; |
michael@0 | 619 | |
michael@0 | 620 | count = PR_Recv(ssl_sock, buf, RD_BUF_SIZE, 0, maxInterval); |
michael@0 | 621 | if (count < 0) { |
michael@0 | 622 | errWarn("PR_Recv"); |
michael@0 | 623 | break; |
michael@0 | 624 | } |
michael@0 | 625 | countRead += count; |
michael@0 | 626 | FPRINTF(stderr, |
michael@0 | 627 | "strsclnt: connection %d read %d bytes (%d total).\n", |
michael@0 | 628 | connection, count, countRead ); |
michael@0 | 629 | if (firstTime) { |
michael@0 | 630 | firstTime = 0; |
michael@0 | 631 | printSecurityInfo(ssl_sock); |
michael@0 | 632 | } |
michael@0 | 633 | } while (lockedVars_AddToCount(&lv, 0) > 0); |
michael@0 | 634 | PR_Free(buf); |
michael@0 | 635 | buf = 0; |
michael@0 | 636 | } |
michael@0 | 637 | |
michael@0 | 638 | /* Wait for writer to finish */ |
michael@0 | 639 | lockedVars_WaitForDone(&lv); |
michael@0 | 640 | lockedVars_Destroy(&lv); |
michael@0 | 641 | |
michael@0 | 642 | FPRINTF(stderr, |
michael@0 | 643 | "strsclnt: connection %d read %d bytes total. -----------------------\n", |
michael@0 | 644 | connection, countRead); |
michael@0 | 645 | |
michael@0 | 646 | cleanup: |
michael@0 | 647 | /* Caller closes the socket. */ |
michael@0 | 648 | |
michael@0 | 649 | return SECSuccess; |
michael@0 | 650 | } |
michael@0 | 651 | |
michael@0 | 652 | const char request[] = {"GET /abc HTTP/1.0\r\n\r\n" }; |
michael@0 | 653 | |
michael@0 | 654 | SECStatus |
michael@0 | 655 | handle_connection( PRFileDesc *ssl_sock, int tid) |
michael@0 | 656 | { |
michael@0 | 657 | int countRead = 0; |
michael@0 | 658 | PRInt32 rv; |
michael@0 | 659 | char *buf; |
michael@0 | 660 | |
michael@0 | 661 | buf = PR_Malloc(RD_BUF_SIZE); |
michael@0 | 662 | if (!buf) |
michael@0 | 663 | return SECFailure; |
michael@0 | 664 | |
michael@0 | 665 | /* compose the http request here. */ |
michael@0 | 666 | |
michael@0 | 667 | rv = PR_Send(ssl_sock, request, strlen(request), 0, maxInterval); |
michael@0 | 668 | if (rv <= 0) { |
michael@0 | 669 | errWarn("PR_Send"); |
michael@0 | 670 | PR_Free(buf); |
michael@0 | 671 | buf = 0; |
michael@0 | 672 | failed_already = 1; |
michael@0 | 673 | return SECFailure; |
michael@0 | 674 | } |
michael@0 | 675 | printSecurityInfo(ssl_sock); |
michael@0 | 676 | |
michael@0 | 677 | /* read until EOF */ |
michael@0 | 678 | while (1) { |
michael@0 | 679 | rv = PR_Recv(ssl_sock, buf, RD_BUF_SIZE, 0, maxInterval); |
michael@0 | 680 | if (rv == 0) { |
michael@0 | 681 | break; /* EOF */ |
michael@0 | 682 | } |
michael@0 | 683 | if (rv < 0) { |
michael@0 | 684 | errWarn("PR_Recv"); |
michael@0 | 685 | failed_already = 1; |
michael@0 | 686 | break; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | countRead += rv; |
michael@0 | 690 | FPRINTF(stderr, |
michael@0 | 691 | "strsclnt: connection on thread %d read %d bytes (%d total).\n", |
michael@0 | 692 | tid, rv, countRead ); |
michael@0 | 693 | } |
michael@0 | 694 | PR_Free(buf); |
michael@0 | 695 | buf = 0; |
michael@0 | 696 | |
michael@0 | 697 | /* Caller closes the socket. */ |
michael@0 | 698 | |
michael@0 | 699 | FPRINTF(stderr, |
michael@0 | 700 | "strsclnt: connection on thread %d read %d bytes total. ---------\n", |
michael@0 | 701 | tid, countRead); |
michael@0 | 702 | |
michael@0 | 703 | return SECSuccess; /* success */ |
michael@0 | 704 | } |
michael@0 | 705 | |
michael@0 | 706 | #define USE_SOCK_PEER_ID 1 |
michael@0 | 707 | |
michael@0 | 708 | #ifdef USE_SOCK_PEER_ID |
michael@0 | 709 | |
michael@0 | 710 | PRInt32 lastFullHandshakePeerID; |
michael@0 | 711 | |
michael@0 | 712 | void |
michael@0 | 713 | myHandshakeCallback(PRFileDesc *socket, void *arg) |
michael@0 | 714 | { |
michael@0 | 715 | PR_ATOMIC_SET(&lastFullHandshakePeerID, (PRInt32) arg); |
michael@0 | 716 | } |
michael@0 | 717 | |
michael@0 | 718 | #endif |
michael@0 | 719 | |
michael@0 | 720 | /* one copy of this function is launched in a separate thread for each |
michael@0 | 721 | ** connection to be made. |
michael@0 | 722 | */ |
michael@0 | 723 | int |
michael@0 | 724 | do_connects( |
michael@0 | 725 | void * a, |
michael@0 | 726 | void * b, |
michael@0 | 727 | int tid) |
michael@0 | 728 | { |
michael@0 | 729 | PRNetAddr * addr = (PRNetAddr *) a; |
michael@0 | 730 | PRFileDesc * model_sock = (PRFileDesc *) b; |
michael@0 | 731 | PRFileDesc * ssl_sock = 0; |
michael@0 | 732 | PRFileDesc * tcp_sock = 0; |
michael@0 | 733 | PRStatus prStatus; |
michael@0 | 734 | PRUint32 sleepInterval = 50; /* milliseconds */ |
michael@0 | 735 | SECStatus result; |
michael@0 | 736 | int rv = SECSuccess; |
michael@0 | 737 | PRSocketOptionData opt; |
michael@0 | 738 | |
michael@0 | 739 | retry: |
michael@0 | 740 | |
michael@0 | 741 | tcp_sock = PR_OpenTCPSocket(addr->raw.family); |
michael@0 | 742 | if (tcp_sock == NULL) { |
michael@0 | 743 | errExit("PR_OpenTCPSocket"); |
michael@0 | 744 | } |
michael@0 | 745 | |
michael@0 | 746 | opt.option = PR_SockOpt_Nonblocking; |
michael@0 | 747 | opt.value.non_blocking = PR_FALSE; |
michael@0 | 748 | prStatus = PR_SetSocketOption(tcp_sock, &opt); |
michael@0 | 749 | if (prStatus != PR_SUCCESS) { |
michael@0 | 750 | errWarn("PR_SetSocketOption(PR_SockOpt_Nonblocking, PR_FALSE)"); |
michael@0 | 751 | PR_Close(tcp_sock); |
michael@0 | 752 | return SECSuccess; |
michael@0 | 753 | } |
michael@0 | 754 | |
michael@0 | 755 | if (NoDelay) { |
michael@0 | 756 | opt.option = PR_SockOpt_NoDelay; |
michael@0 | 757 | opt.value.no_delay = PR_TRUE; |
michael@0 | 758 | prStatus = PR_SetSocketOption(tcp_sock, &opt); |
michael@0 | 759 | if (prStatus != PR_SUCCESS) { |
michael@0 | 760 | errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)"); |
michael@0 | 761 | PR_Close(tcp_sock); |
michael@0 | 762 | return SECSuccess; |
michael@0 | 763 | } |
michael@0 | 764 | } |
michael@0 | 765 | |
michael@0 | 766 | prStatus = PR_Connect(tcp_sock, addr, PR_INTERVAL_NO_TIMEOUT); |
michael@0 | 767 | if (prStatus != PR_SUCCESS) { |
michael@0 | 768 | PRErrorCode err = PR_GetError(); /* save error code */ |
michael@0 | 769 | PRInt32 oserr = PR_GetOSError(); |
michael@0 | 770 | if (ThrottleUp) { |
michael@0 | 771 | PRTime now = PR_Now(); |
michael@0 | 772 | PR_Lock(threadLock); |
michael@0 | 773 | lastConnectFailure = PR_MAX(now, lastConnectFailure); |
michael@0 | 774 | PR_Unlock(threadLock); |
michael@0 | 775 | PR_SetError(err, oserr); /* restore error code */ |
michael@0 | 776 | } |
michael@0 | 777 | if ((err == PR_CONNECT_REFUSED_ERROR) || |
michael@0 | 778 | (err == PR_CONNECT_RESET_ERROR) ) { |
michael@0 | 779 | int connections = numConnected; |
michael@0 | 780 | |
michael@0 | 781 | PR_Close(tcp_sock); |
michael@0 | 782 | PR_Lock(threadLock); |
michael@0 | 783 | if (connections > 2 && active_threads >= connections) { |
michael@0 | 784 | active_threads = connections - 1; |
michael@0 | 785 | fprintf(stderr,"active_threads set down to %d\n", |
michael@0 | 786 | active_threads); |
michael@0 | 787 | } |
michael@0 | 788 | PR_Unlock(threadLock); |
michael@0 | 789 | |
michael@0 | 790 | if (QuitOnTimeout && sleepInterval > 40000) { |
michael@0 | 791 | fprintf(stderr, |
michael@0 | 792 | "strsclnt: Client timed out waiting for connection to server.\n"); |
michael@0 | 793 | exit(1); |
michael@0 | 794 | } |
michael@0 | 795 | PR_Sleep(PR_MillisecondsToInterval(sleepInterval)); |
michael@0 | 796 | sleepInterval <<= 1; |
michael@0 | 797 | goto retry; |
michael@0 | 798 | } |
michael@0 | 799 | errWarn("PR_Connect"); |
michael@0 | 800 | rv = SECFailure; |
michael@0 | 801 | goto done; |
michael@0 | 802 | } else { |
michael@0 | 803 | if (ThrottleUp) { |
michael@0 | 804 | PRTime now = PR_Now(); |
michael@0 | 805 | PR_Lock(threadLock); |
michael@0 | 806 | lastConnectSuccess = PR_MAX(now, lastConnectSuccess); |
michael@0 | 807 | PR_Unlock(threadLock); |
michael@0 | 808 | } |
michael@0 | 809 | } |
michael@0 | 810 | |
michael@0 | 811 | ssl_sock = SSL_ImportFD(model_sock, tcp_sock); |
michael@0 | 812 | /* XXX if this import fails, close tcp_sock and return. */ |
michael@0 | 813 | if (!ssl_sock) { |
michael@0 | 814 | PR_Close(tcp_sock); |
michael@0 | 815 | return SECSuccess; |
michael@0 | 816 | } |
michael@0 | 817 | if (fullhs != NO_FULLHS_PERCENTAGE) { |
michael@0 | 818 | #ifdef USE_SOCK_PEER_ID |
michael@0 | 819 | char sockPeerIDString[512]; |
michael@0 | 820 | static PRInt32 sockPeerID = 0; /* atomically incremented */ |
michael@0 | 821 | PRInt32 thisPeerID; |
michael@0 | 822 | #endif |
michael@0 | 823 | PRInt32 savid = PR_ATOMIC_INCREMENT(&globalconid); |
michael@0 | 824 | PRInt32 conid = 1 + (savid - 1) % 100; |
michael@0 | 825 | /* don't change peer ID on the very first handshake, which is always |
michael@0 | 826 | a full, so the session gets stored into the client cache */ |
michael@0 | 827 | if ( (savid != 1) && |
michael@0 | 828 | ( ( (savid <= total_connections_rounded_down_to_hundreds) && |
michael@0 | 829 | (conid <= fullhs) ) || |
michael@0 | 830 | (conid*100 <= total_connections_modulo_100*fullhs ) ) ) |
michael@0 | 831 | #ifdef USE_SOCK_PEER_ID |
michael@0 | 832 | { |
michael@0 | 833 | /* force a full handshake by changing the socket peer ID */ |
michael@0 | 834 | thisPeerID = PR_ATOMIC_INCREMENT(&sockPeerID); |
michael@0 | 835 | } else { |
michael@0 | 836 | /* reuse previous sockPeerID for restart handhsake */ |
michael@0 | 837 | thisPeerID = lastFullHandshakePeerID; |
michael@0 | 838 | } |
michael@0 | 839 | PR_snprintf(sockPeerIDString, sizeof(sockPeerIDString), "ID%d", |
michael@0 | 840 | thisPeerID); |
michael@0 | 841 | SSL_SetSockPeerID(ssl_sock, sockPeerIDString); |
michael@0 | 842 | SSL_HandshakeCallback(ssl_sock, myHandshakeCallback, (void*)thisPeerID); |
michael@0 | 843 | #else |
michael@0 | 844 | /* force a full handshake by setting the no cache option */ |
michael@0 | 845 | SSL_OptionSet(ssl_sock, SSL_NO_CACHE, 1); |
michael@0 | 846 | #endif |
michael@0 | 847 | } |
michael@0 | 848 | rv = SSL_ResetHandshake(ssl_sock, /* asServer */ 0); |
michael@0 | 849 | if (rv != SECSuccess) { |
michael@0 | 850 | errWarn("SSL_ResetHandshake"); |
michael@0 | 851 | goto done; |
michael@0 | 852 | } |
michael@0 | 853 | |
michael@0 | 854 | PR_ATOMIC_INCREMENT(&numConnected); |
michael@0 | 855 | |
michael@0 | 856 | if (bigBuf.data != NULL) { |
michael@0 | 857 | result = handle_fdx_connection( ssl_sock, tid); |
michael@0 | 858 | } else { |
michael@0 | 859 | result = handle_connection( ssl_sock, tid); |
michael@0 | 860 | } |
michael@0 | 861 | |
michael@0 | 862 | PR_ATOMIC_DECREMENT(&numConnected); |
michael@0 | 863 | |
michael@0 | 864 | done: |
michael@0 | 865 | if (ssl_sock) { |
michael@0 | 866 | PR_Close(ssl_sock); |
michael@0 | 867 | } else if (tcp_sock) { |
michael@0 | 868 | PR_Close(tcp_sock); |
michael@0 | 869 | } |
michael@0 | 870 | return SECSuccess; |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | |
michael@0 | 874 | typedef struct { |
michael@0 | 875 | PRLock* lock; |
michael@0 | 876 | char* nickname; |
michael@0 | 877 | CERTCertificate* cert; |
michael@0 | 878 | SECKEYPrivateKey* key; |
michael@0 | 879 | void* wincx; |
michael@0 | 880 | } cert_and_key; |
michael@0 | 881 | |
michael@0 | 882 | PRBool FindCertAndKey(cert_and_key* Cert_And_Key) |
michael@0 | 883 | { |
michael@0 | 884 | if ( (NULL == Cert_And_Key->nickname) || (0 == strcmp(Cert_And_Key->nickname,"none"))) { |
michael@0 | 885 | return PR_TRUE; |
michael@0 | 886 | } |
michael@0 | 887 | Cert_And_Key->cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), |
michael@0 | 888 | Cert_And_Key->nickname, certUsageSSLClient, |
michael@0 | 889 | PR_FALSE, Cert_And_Key->wincx); |
michael@0 | 890 | if (Cert_And_Key->cert) { |
michael@0 | 891 | Cert_And_Key->key = PK11_FindKeyByAnyCert(Cert_And_Key->cert, Cert_And_Key->wincx); |
michael@0 | 892 | } |
michael@0 | 893 | if (Cert_And_Key->cert && Cert_And_Key->key) { |
michael@0 | 894 | return PR_TRUE; |
michael@0 | 895 | } else { |
michael@0 | 896 | return PR_FALSE; |
michael@0 | 897 | } |
michael@0 | 898 | } |
michael@0 | 899 | |
michael@0 | 900 | PRBool LoggedIn(CERTCertificate* cert, SECKEYPrivateKey* key) |
michael@0 | 901 | { |
michael@0 | 902 | if ( (cert->slot) && (key->pkcs11Slot) && |
michael@0 | 903 | (PR_TRUE == PK11_IsLoggedIn(cert->slot, NULL)) && |
michael@0 | 904 | (PR_TRUE == PK11_IsLoggedIn(key->pkcs11Slot, NULL)) ) { |
michael@0 | 905 | return PR_TRUE; |
michael@0 | 906 | } |
michael@0 | 907 | |
michael@0 | 908 | return PR_FALSE; |
michael@0 | 909 | } |
michael@0 | 910 | |
michael@0 | 911 | SECStatus |
michael@0 | 912 | StressClient_GetClientAuthData(void * arg, |
michael@0 | 913 | PRFileDesc * socket, |
michael@0 | 914 | struct CERTDistNamesStr * caNames, |
michael@0 | 915 | struct CERTCertificateStr ** pRetCert, |
michael@0 | 916 | struct SECKEYPrivateKeyStr **pRetKey) |
michael@0 | 917 | { |
michael@0 | 918 | cert_and_key* Cert_And_Key = (cert_and_key*) arg; |
michael@0 | 919 | |
michael@0 | 920 | if (!pRetCert || !pRetKey) { |
michael@0 | 921 | /* bad pointers, can't return a cert or key */ |
michael@0 | 922 | return SECFailure; |
michael@0 | 923 | } |
michael@0 | 924 | |
michael@0 | 925 | *pRetCert = NULL; |
michael@0 | 926 | *pRetKey = NULL; |
michael@0 | 927 | |
michael@0 | 928 | if (Cert_And_Key && Cert_And_Key->nickname) { |
michael@0 | 929 | while (PR_TRUE) { |
michael@0 | 930 | if (Cert_And_Key && Cert_And_Key->lock) { |
michael@0 | 931 | int timeout = 0; |
michael@0 | 932 | PR_Lock(Cert_And_Key->lock); |
michael@0 | 933 | |
michael@0 | 934 | if (Cert_And_Key->cert) { |
michael@0 | 935 | *pRetCert = CERT_DupCertificate(Cert_And_Key->cert); |
michael@0 | 936 | } |
michael@0 | 937 | |
michael@0 | 938 | if (Cert_And_Key->key) { |
michael@0 | 939 | *pRetKey = SECKEY_CopyPrivateKey(Cert_And_Key->key); |
michael@0 | 940 | } |
michael@0 | 941 | PR_Unlock(Cert_And_Key->lock); |
michael@0 | 942 | if (!*pRetCert || !*pRetKey) { |
michael@0 | 943 | /* one or both of them failed to copy. Either the source was NULL, or there was |
michael@0 | 944 | ** an out of memory condition. Free any allocated copy and fail */ |
michael@0 | 945 | if (*pRetCert) { |
michael@0 | 946 | CERT_DestroyCertificate(*pRetCert); |
michael@0 | 947 | *pRetCert = NULL; |
michael@0 | 948 | } |
michael@0 | 949 | if (*pRetKey) { |
michael@0 | 950 | SECKEY_DestroyPrivateKey(*pRetKey); |
michael@0 | 951 | *pRetKey = NULL; |
michael@0 | 952 | } |
michael@0 | 953 | break; |
michael@0 | 954 | } |
michael@0 | 955 | /* now check if those objects are valid */ |
michael@0 | 956 | if ( PR_FALSE == LoggedIn(*pRetCert, *pRetKey) ) { |
michael@0 | 957 | /* token is no longer logged in, it was removed */ |
michael@0 | 958 | |
michael@0 | 959 | /* first, delete and clear our invalid local objects */ |
michael@0 | 960 | CERT_DestroyCertificate(*pRetCert); |
michael@0 | 961 | SECKEY_DestroyPrivateKey(*pRetKey); |
michael@0 | 962 | *pRetCert = NULL; |
michael@0 | 963 | *pRetKey = NULL; |
michael@0 | 964 | |
michael@0 | 965 | PR_Lock(Cert_And_Key->lock); |
michael@0 | 966 | /* check if another thread already logged back in */ |
michael@0 | 967 | if (PR_TRUE == LoggedIn(Cert_And_Key->cert, Cert_And_Key->key)) { |
michael@0 | 968 | /* yes : try again */ |
michael@0 | 969 | PR_Unlock(Cert_And_Key->lock); |
michael@0 | 970 | continue; |
michael@0 | 971 | } |
michael@0 | 972 | /* this is the thread to retry */ |
michael@0 | 973 | CERT_DestroyCertificate(Cert_And_Key->cert); |
michael@0 | 974 | SECKEY_DestroyPrivateKey(Cert_And_Key->key); |
michael@0 | 975 | Cert_And_Key->cert = NULL; |
michael@0 | 976 | Cert_And_Key->key = NULL; |
michael@0 | 977 | |
michael@0 | 978 | |
michael@0 | 979 | /* now look up the cert and key again */ |
michael@0 | 980 | while (PR_FALSE == FindCertAndKey(Cert_And_Key) ) { |
michael@0 | 981 | PR_Sleep(PR_SecondsToInterval(1)); |
michael@0 | 982 | timeout++; |
michael@0 | 983 | if (timeout>=60) { |
michael@0 | 984 | printf("\nToken pulled and not reinserted early enough : aborting.\n"); |
michael@0 | 985 | exit(1); |
michael@0 | 986 | } |
michael@0 | 987 | } |
michael@0 | 988 | PR_Unlock(Cert_And_Key->lock); |
michael@0 | 989 | continue; |
michael@0 | 990 | /* try again to reduce code size */ |
michael@0 | 991 | } |
michael@0 | 992 | return SECSuccess; |
michael@0 | 993 | } |
michael@0 | 994 | } |
michael@0 | 995 | *pRetCert = NULL; |
michael@0 | 996 | *pRetKey = NULL; |
michael@0 | 997 | return SECFailure; |
michael@0 | 998 | } else { |
michael@0 | 999 | /* no cert configured, automatically find the right cert. */ |
michael@0 | 1000 | CERTCertificate * cert = NULL; |
michael@0 | 1001 | SECKEYPrivateKey * privkey = NULL; |
michael@0 | 1002 | CERTCertNicknames * names; |
michael@0 | 1003 | int i; |
michael@0 | 1004 | void * proto_win = NULL; |
michael@0 | 1005 | SECStatus rv = SECFailure; |
michael@0 | 1006 | |
michael@0 | 1007 | if (Cert_And_Key) { |
michael@0 | 1008 | proto_win = Cert_And_Key->wincx; |
michael@0 | 1009 | } |
michael@0 | 1010 | |
michael@0 | 1011 | names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(), |
michael@0 | 1012 | SEC_CERT_NICKNAMES_USER, proto_win); |
michael@0 | 1013 | if (names != NULL) { |
michael@0 | 1014 | for (i = 0; i < names->numnicknames; i++) { |
michael@0 | 1015 | cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), |
michael@0 | 1016 | names->nicknames[i], certUsageSSLClient, |
michael@0 | 1017 | PR_FALSE, proto_win); |
michael@0 | 1018 | if ( !cert ) |
michael@0 | 1019 | continue; |
michael@0 | 1020 | /* Only check unexpired certs */ |
michael@0 | 1021 | if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_TRUE) != |
michael@0 | 1022 | secCertTimeValid ) { |
michael@0 | 1023 | CERT_DestroyCertificate(cert); |
michael@0 | 1024 | continue; |
michael@0 | 1025 | } |
michael@0 | 1026 | rv = NSS_CmpCertChainWCANames(cert, caNames); |
michael@0 | 1027 | if ( rv == SECSuccess ) { |
michael@0 | 1028 | privkey = PK11_FindKeyByAnyCert(cert, proto_win); |
michael@0 | 1029 | if ( privkey ) |
michael@0 | 1030 | break; |
michael@0 | 1031 | } |
michael@0 | 1032 | rv = SECFailure; |
michael@0 | 1033 | CERT_DestroyCertificate(cert); |
michael@0 | 1034 | } |
michael@0 | 1035 | CERT_FreeNicknames(names); |
michael@0 | 1036 | } |
michael@0 | 1037 | if (rv == SECSuccess) { |
michael@0 | 1038 | *pRetCert = cert; |
michael@0 | 1039 | *pRetKey = privkey; |
michael@0 | 1040 | } |
michael@0 | 1041 | return rv; |
michael@0 | 1042 | } |
michael@0 | 1043 | } |
michael@0 | 1044 | |
michael@0 | 1045 | int |
michael@0 | 1046 | hexchar_to_int(int c) |
michael@0 | 1047 | { |
michael@0 | 1048 | if (((c) >= '0') && ((c) <= '9')) |
michael@0 | 1049 | return (c) - '0'; |
michael@0 | 1050 | if (((c) >= 'a') && ((c) <= 'f')) |
michael@0 | 1051 | return (c) - 'a' + 10; |
michael@0 | 1052 | if (((c) >= 'A') && ((c) <= 'F')) |
michael@0 | 1053 | return (c) - 'A' + 10; |
michael@0 | 1054 | failed_already = 1; |
michael@0 | 1055 | return -1; |
michael@0 | 1056 | } |
michael@0 | 1057 | |
michael@0 | 1058 | void |
michael@0 | 1059 | client_main( |
michael@0 | 1060 | unsigned short port, |
michael@0 | 1061 | int connections, |
michael@0 | 1062 | cert_and_key* Cert_And_Key, |
michael@0 | 1063 | const char * hostName, |
michael@0 | 1064 | const char * sniHostName) |
michael@0 | 1065 | { |
michael@0 | 1066 | PRFileDesc *model_sock = NULL; |
michael@0 | 1067 | int i; |
michael@0 | 1068 | int rv; |
michael@0 | 1069 | PRStatus status; |
michael@0 | 1070 | PRNetAddr addr; |
michael@0 | 1071 | |
michael@0 | 1072 | status = PR_StringToNetAddr(hostName, &addr); |
michael@0 | 1073 | if (status == PR_SUCCESS) { |
michael@0 | 1074 | addr.inet.port = PR_htons(port); |
michael@0 | 1075 | } else { |
michael@0 | 1076 | /* Lookup host */ |
michael@0 | 1077 | PRAddrInfo *addrInfo; |
michael@0 | 1078 | void *enumPtr = NULL; |
michael@0 | 1079 | |
michael@0 | 1080 | addrInfo = PR_GetAddrInfoByName(hostName, PR_AF_UNSPEC, |
michael@0 | 1081 | PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME); |
michael@0 | 1082 | if (!addrInfo) { |
michael@0 | 1083 | SECU_PrintError(progName, "error looking up host"); |
michael@0 | 1084 | return; |
michael@0 | 1085 | } |
michael@0 | 1086 | do { |
michael@0 | 1087 | enumPtr = PR_EnumerateAddrInfo(enumPtr, addrInfo, port, &addr); |
michael@0 | 1088 | } while (enumPtr != NULL && |
michael@0 | 1089 | addr.raw.family != PR_AF_INET && |
michael@0 | 1090 | addr.raw.family != PR_AF_INET6); |
michael@0 | 1091 | PR_FreeAddrInfo(addrInfo); |
michael@0 | 1092 | if (enumPtr == NULL) { |
michael@0 | 1093 | SECU_PrintError(progName, "error looking up host address"); |
michael@0 | 1094 | return; |
michael@0 | 1095 | } |
michael@0 | 1096 | } |
michael@0 | 1097 | |
michael@0 | 1098 | /* all suites except RSA_NULL_MD5 are enabled by Domestic Policy */ |
michael@0 | 1099 | NSS_SetDomesticPolicy(); |
michael@0 | 1100 | |
michael@0 | 1101 | /* all the SSL2 and SSL3 cipher suites are enabled by default. */ |
michael@0 | 1102 | if (cipherString) { |
michael@0 | 1103 | int ndx; |
michael@0 | 1104 | |
michael@0 | 1105 | /* disable all the ciphers, then enable the ones we want. */ |
michael@0 | 1106 | disableAllSSLCiphers(); |
michael@0 | 1107 | |
michael@0 | 1108 | while (0 != (ndx = *cipherString)) { |
michael@0 | 1109 | const char * startCipher = cipherString++; |
michael@0 | 1110 | int cipher = 0; |
michael@0 | 1111 | SECStatus rv; |
michael@0 | 1112 | |
michael@0 | 1113 | if (ndx == ':') { |
michael@0 | 1114 | cipher = hexchar_to_int(*cipherString++); |
michael@0 | 1115 | cipher <<= 4; |
michael@0 | 1116 | cipher |= hexchar_to_int(*cipherString++); |
michael@0 | 1117 | cipher <<= 4; |
michael@0 | 1118 | cipher |= hexchar_to_int(*cipherString++); |
michael@0 | 1119 | cipher <<= 4; |
michael@0 | 1120 | cipher |= hexchar_to_int(*cipherString++); |
michael@0 | 1121 | if (cipher <= 0) { |
michael@0 | 1122 | fprintf(stderr, "strsclnt: Invalid cipher value: %-5.5s\n", |
michael@0 | 1123 | startCipher); |
michael@0 | 1124 | failed_already = 1; |
michael@0 | 1125 | return; |
michael@0 | 1126 | } |
michael@0 | 1127 | } else { |
michael@0 | 1128 | if (isalpha(ndx)) { |
michael@0 | 1129 | const int *cptr; |
michael@0 | 1130 | |
michael@0 | 1131 | cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites; |
michael@0 | 1132 | for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) |
michael@0 | 1133 | /* do nothing */; |
michael@0 | 1134 | } |
michael@0 | 1135 | if (cipher <= 0) { |
michael@0 | 1136 | fprintf(stderr, "strsclnt: Invalid cipher letter: %c\n", |
michael@0 | 1137 | *startCipher); |
michael@0 | 1138 | failed_already = 1; |
michael@0 | 1139 | return; |
michael@0 | 1140 | } |
michael@0 | 1141 | } |
michael@0 | 1142 | rv = SSL_CipherPrefSetDefault(cipher, PR_TRUE); |
michael@0 | 1143 | if (rv != SECSuccess) { |
michael@0 | 1144 | fprintf(stderr, |
michael@0 | 1145 | "strsclnt: SSL_CipherPrefSetDefault(0x%04x) failed\n", |
michael@0 | 1146 | cipher); |
michael@0 | 1147 | failed_already = 1; |
michael@0 | 1148 | return; |
michael@0 | 1149 | } |
michael@0 | 1150 | } |
michael@0 | 1151 | } |
michael@0 | 1152 | |
michael@0 | 1153 | /* configure model SSL socket. */ |
michael@0 | 1154 | |
michael@0 | 1155 | model_sock = PR_OpenTCPSocket(addr.raw.family); |
michael@0 | 1156 | if (model_sock == NULL) { |
michael@0 | 1157 | errExit("PR_OpenTCPSocket for model socket"); |
michael@0 | 1158 | } |
michael@0 | 1159 | |
michael@0 | 1160 | model_sock = SSL_ImportFD(NULL, model_sock); |
michael@0 | 1161 | if (model_sock == NULL) { |
michael@0 | 1162 | errExit("SSL_ImportFD"); |
michael@0 | 1163 | } |
michael@0 | 1164 | |
michael@0 | 1165 | /* do SSL configuration. */ |
michael@0 | 1166 | |
michael@0 | 1167 | rv = SSL_OptionSet(model_sock, SSL_SECURITY, |
michael@0 | 1168 | enableSSL2 || enabledVersions.min != 0); |
michael@0 | 1169 | if (rv < 0) { |
michael@0 | 1170 | errExit("SSL_OptionSet SSL_SECURITY"); |
michael@0 | 1171 | } |
michael@0 | 1172 | |
michael@0 | 1173 | rv = SSL_VersionRangeSet(model_sock, &enabledVersions); |
michael@0 | 1174 | if (rv != SECSuccess) { |
michael@0 | 1175 | errExit("error setting SSL/TLS version range "); |
michael@0 | 1176 | } |
michael@0 | 1177 | |
michael@0 | 1178 | rv = SSL_OptionSet(model_sock, SSL_ENABLE_SSL2, enableSSL2); |
michael@0 | 1179 | if (rv != SECSuccess) { |
michael@0 | 1180 | errExit("error enabling SSLv2 "); |
michael@0 | 1181 | } |
michael@0 | 1182 | |
michael@0 | 1183 | rv = SSL_OptionSet(model_sock, SSL_V2_COMPATIBLE_HELLO, enableSSL2); |
michael@0 | 1184 | if (rv != SECSuccess) { |
michael@0 | 1185 | errExit("error enabling SSLv2 compatible hellos "); |
michael@0 | 1186 | } |
michael@0 | 1187 | |
michael@0 | 1188 | if (bigBuf.data) { /* doing FDX */ |
michael@0 | 1189 | rv = SSL_OptionSet(model_sock, SSL_ENABLE_FDX, 1); |
michael@0 | 1190 | if (rv < 0) { |
michael@0 | 1191 | errExit("SSL_OptionSet SSL_ENABLE_FDX"); |
michael@0 | 1192 | } |
michael@0 | 1193 | } |
michael@0 | 1194 | |
michael@0 | 1195 | if (NoReuse) { |
michael@0 | 1196 | rv = SSL_OptionSet(model_sock, SSL_NO_CACHE, 1); |
michael@0 | 1197 | if (rv < 0) { |
michael@0 | 1198 | errExit("SSL_OptionSet SSL_NO_CACHE"); |
michael@0 | 1199 | } |
michael@0 | 1200 | } |
michael@0 | 1201 | |
michael@0 | 1202 | if (bypassPKCS11) { |
michael@0 | 1203 | rv = SSL_OptionSet(model_sock, SSL_BYPASS_PKCS11, 1); |
michael@0 | 1204 | if (rv < 0) { |
michael@0 | 1205 | errExit("SSL_OptionSet SSL_BYPASS_PKCS11"); |
michael@0 | 1206 | } |
michael@0 | 1207 | } |
michael@0 | 1208 | |
michael@0 | 1209 | if (disableLocking) { |
michael@0 | 1210 | rv = SSL_OptionSet(model_sock, SSL_NO_LOCKS, 1); |
michael@0 | 1211 | if (rv < 0) { |
michael@0 | 1212 | errExit("SSL_OptionSet SSL_NO_LOCKS"); |
michael@0 | 1213 | } |
michael@0 | 1214 | } |
michael@0 | 1215 | |
michael@0 | 1216 | if (enableSessionTickets) { |
michael@0 | 1217 | rv = SSL_OptionSet(model_sock, SSL_ENABLE_SESSION_TICKETS, PR_TRUE); |
michael@0 | 1218 | if (rv != SECSuccess) |
michael@0 | 1219 | errExit("SSL_OptionSet SSL_ENABLE_SESSION_TICKETS"); |
michael@0 | 1220 | } |
michael@0 | 1221 | |
michael@0 | 1222 | if (enableCompression) { |
michael@0 | 1223 | rv = SSL_OptionSet(model_sock, SSL_ENABLE_DEFLATE, PR_TRUE); |
michael@0 | 1224 | if (rv != SECSuccess) |
michael@0 | 1225 | errExit("SSL_OptionSet SSL_ENABLE_DEFLATE"); |
michael@0 | 1226 | } |
michael@0 | 1227 | |
michael@0 | 1228 | if (enableFalseStart) { |
michael@0 | 1229 | rv = SSL_OptionSet(model_sock, SSL_ENABLE_FALSE_START, PR_TRUE); |
michael@0 | 1230 | if (rv != SECSuccess) |
michael@0 | 1231 | errExit("SSL_OptionSet SSL_ENABLE_FALSE_START"); |
michael@0 | 1232 | } |
michael@0 | 1233 | |
michael@0 | 1234 | if (enableCertStatus) { |
michael@0 | 1235 | rv = SSL_OptionSet(model_sock, SSL_ENABLE_OCSP_STAPLING, PR_TRUE); |
michael@0 | 1236 | if (rv != SECSuccess) |
michael@0 | 1237 | errExit("SSL_OptionSet SSL_ENABLE_OCSP_STAPLING"); |
michael@0 | 1238 | } |
michael@0 | 1239 | |
michael@0 | 1240 | SSL_SetPKCS11PinArg(model_sock, &pwdata); |
michael@0 | 1241 | |
michael@0 | 1242 | SSL_SetURL(model_sock, hostName); |
michael@0 | 1243 | |
michael@0 | 1244 | SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate, |
michael@0 | 1245 | (void *)CERT_GetDefaultCertDB()); |
michael@0 | 1246 | SSL_BadCertHook(model_sock, myBadCertHandler, NULL); |
michael@0 | 1247 | |
michael@0 | 1248 | SSL_GetClientAuthDataHook(model_sock, StressClient_GetClientAuthData, (void*)Cert_And_Key); |
michael@0 | 1249 | |
michael@0 | 1250 | if (sniHostName) { |
michael@0 | 1251 | SSL_SetURL(model_sock, sniHostName); |
michael@0 | 1252 | } |
michael@0 | 1253 | /* I'm not going to set the HandshakeCallback function. */ |
michael@0 | 1254 | |
michael@0 | 1255 | /* end of ssl configuration. */ |
michael@0 | 1256 | |
michael@0 | 1257 | init_thread_data(); |
michael@0 | 1258 | |
michael@0 | 1259 | remaining_connections = total_connections = connections; |
michael@0 | 1260 | total_connections_modulo_100 = total_connections % 100; |
michael@0 | 1261 | total_connections_rounded_down_to_hundreds = |
michael@0 | 1262 | total_connections - total_connections_modulo_100; |
michael@0 | 1263 | |
michael@0 | 1264 | if (!NoReuse) { |
michael@0 | 1265 | remaining_connections = 1; |
michael@0 | 1266 | rv = launch_thread(do_connects, &addr, model_sock, 0); |
michael@0 | 1267 | /* wait for the first connection to terminate, then launch the rest. */ |
michael@0 | 1268 | reap_threads(); |
michael@0 | 1269 | remaining_connections = total_connections - 1 ; |
michael@0 | 1270 | } |
michael@0 | 1271 | if (remaining_connections > 0) { |
michael@0 | 1272 | active_threads = PR_MIN(active_threads, remaining_connections); |
michael@0 | 1273 | /* Start up the threads */ |
michael@0 | 1274 | for (i=0;i<active_threads;i++) { |
michael@0 | 1275 | rv = launch_thread(do_connects, &addr, model_sock, i); |
michael@0 | 1276 | } |
michael@0 | 1277 | reap_threads(); |
michael@0 | 1278 | } |
michael@0 | 1279 | destroy_thread_data(); |
michael@0 | 1280 | |
michael@0 | 1281 | PR_Close(model_sock); |
michael@0 | 1282 | } |
michael@0 | 1283 | |
michael@0 | 1284 | SECStatus |
michael@0 | 1285 | readBigFile(const char * fileName) |
michael@0 | 1286 | { |
michael@0 | 1287 | PRFileInfo info; |
michael@0 | 1288 | PRStatus status; |
michael@0 | 1289 | SECStatus rv = SECFailure; |
michael@0 | 1290 | int count; |
michael@0 | 1291 | int hdrLen; |
michael@0 | 1292 | PRFileDesc *local_file_fd = NULL; |
michael@0 | 1293 | |
michael@0 | 1294 | status = PR_GetFileInfo(fileName, &info); |
michael@0 | 1295 | |
michael@0 | 1296 | if (status == PR_SUCCESS && |
michael@0 | 1297 | info.type == PR_FILE_FILE && |
michael@0 | 1298 | info.size > 0 && |
michael@0 | 1299 | NULL != (local_file_fd = PR_Open(fileName, PR_RDONLY, 0))) { |
michael@0 | 1300 | |
michael@0 | 1301 | hdrLen = PORT_Strlen(outHeader); |
michael@0 | 1302 | bigBuf.len = hdrLen + info.size; |
michael@0 | 1303 | bigBuf.data = PORT_Malloc(bigBuf.len + 4095); |
michael@0 | 1304 | if (!bigBuf.data) { |
michael@0 | 1305 | errWarn("PORT_Malloc"); |
michael@0 | 1306 | goto done; |
michael@0 | 1307 | } |
michael@0 | 1308 | |
michael@0 | 1309 | PORT_Memcpy(bigBuf.data, outHeader, hdrLen); |
michael@0 | 1310 | |
michael@0 | 1311 | count = PR_Read(local_file_fd, bigBuf.data + hdrLen, info.size); |
michael@0 | 1312 | if (count != info.size) { |
michael@0 | 1313 | errWarn("PR_Read local file"); |
michael@0 | 1314 | goto done; |
michael@0 | 1315 | } |
michael@0 | 1316 | rv = SECSuccess; |
michael@0 | 1317 | done: |
michael@0 | 1318 | PR_Close(local_file_fd); |
michael@0 | 1319 | } |
michael@0 | 1320 | return rv; |
michael@0 | 1321 | } |
michael@0 | 1322 | |
michael@0 | 1323 | int |
michael@0 | 1324 | main(int argc, char **argv) |
michael@0 | 1325 | { |
michael@0 | 1326 | const char * dir = "."; |
michael@0 | 1327 | const char * fileName = NULL; |
michael@0 | 1328 | char * hostName = NULL; |
michael@0 | 1329 | char * nickName = NULL; |
michael@0 | 1330 | char * tmp = NULL; |
michael@0 | 1331 | int connections = 1; |
michael@0 | 1332 | int exitVal; |
michael@0 | 1333 | int tmpInt; |
michael@0 | 1334 | unsigned short port = 443; |
michael@0 | 1335 | SECStatus rv; |
michael@0 | 1336 | PLOptState * optstate; |
michael@0 | 1337 | PLOptStatus status; |
michael@0 | 1338 | cert_and_key Cert_And_Key; |
michael@0 | 1339 | char * sniHostName = NULL; |
michael@0 | 1340 | |
michael@0 | 1341 | /* Call the NSPR initialization routines */ |
michael@0 | 1342 | PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); |
michael@0 | 1343 | SSL_VersionRangeGetSupported(ssl_variant_stream, &enabledVersions); |
michael@0 | 1344 | |
michael@0 | 1345 | tmp = strrchr(argv[0], '/'); |
michael@0 | 1346 | tmp = tmp ? tmp + 1 : argv[0]; |
michael@0 | 1347 | progName = strrchr(tmp, '\\'); |
michael@0 | 1348 | progName = progName ? progName + 1 : tmp; |
michael@0 | 1349 | |
michael@0 | 1350 | |
michael@0 | 1351 | optstate = PL_CreateOptState(argc, argv, |
michael@0 | 1352 | "BC:DNP:TUV:W:a:c:d:f:gin:op:qst:uvw:z"); |
michael@0 | 1353 | while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { |
michael@0 | 1354 | switch(optstate->option) { |
michael@0 | 1355 | case 'B': bypassPKCS11 = PR_TRUE; break; |
michael@0 | 1356 | |
michael@0 | 1357 | case 'C': cipherString = optstate->value; break; |
michael@0 | 1358 | |
michael@0 | 1359 | case 'D': NoDelay = PR_TRUE; break; |
michael@0 | 1360 | |
michael@0 | 1361 | case 'I': /* reserved for OCSP multi-stapling */ break; |
michael@0 | 1362 | |
michael@0 | 1363 | case 'N': NoReuse = 1; break; |
michael@0 | 1364 | |
michael@0 | 1365 | case 'P': fullhs = PORT_Atoi(optstate->value); break; |
michael@0 | 1366 | |
michael@0 | 1367 | case 'T': enableCertStatus = PR_TRUE; break; |
michael@0 | 1368 | |
michael@0 | 1369 | case 'U': ThrottleUp = PR_TRUE; break; |
michael@0 | 1370 | |
michael@0 | 1371 | case 'V': if (SECU_ParseSSLVersionRangeString(optstate->value, |
michael@0 | 1372 | enabledVersions, enableSSL2, |
michael@0 | 1373 | &enabledVersions, &enableSSL2) != SECSuccess) { |
michael@0 | 1374 | Usage(progName); |
michael@0 | 1375 | } |
michael@0 | 1376 | break; |
michael@0 | 1377 | |
michael@0 | 1378 | case 'a': sniHostName = PL_strdup(optstate->value); break; |
michael@0 | 1379 | |
michael@0 | 1380 | case 'c': connections = PORT_Atoi(optstate->value); break; |
michael@0 | 1381 | |
michael@0 | 1382 | case 'd': dir = optstate->value; break; |
michael@0 | 1383 | |
michael@0 | 1384 | case 'f': fileName = optstate->value; break; |
michael@0 | 1385 | |
michael@0 | 1386 | case 'g': enableFalseStart = PR_TRUE; break; |
michael@0 | 1387 | |
michael@0 | 1388 | case 'i': ignoreErrors = PR_TRUE; break; |
michael@0 | 1389 | |
michael@0 | 1390 | case 'n': nickName = PL_strdup(optstate->value); break; |
michael@0 | 1391 | |
michael@0 | 1392 | case 'o': MakeCertOK++; break; |
michael@0 | 1393 | |
michael@0 | 1394 | case 'p': port = PORT_Atoi(optstate->value); break; |
michael@0 | 1395 | |
michael@0 | 1396 | case 'q': QuitOnTimeout = PR_TRUE; break; |
michael@0 | 1397 | |
michael@0 | 1398 | case 's': disableLocking = PR_TRUE; break; |
michael@0 | 1399 | |
michael@0 | 1400 | case 't': |
michael@0 | 1401 | tmpInt = PORT_Atoi(optstate->value); |
michael@0 | 1402 | if (tmpInt > 0 && tmpInt < MAX_THREADS) |
michael@0 | 1403 | max_threads = active_threads = tmpInt; |
michael@0 | 1404 | break; |
michael@0 | 1405 | |
michael@0 | 1406 | case 'u': enableSessionTickets = PR_TRUE; break; |
michael@0 | 1407 | |
michael@0 | 1408 | case 'v': verbose++; break; |
michael@0 | 1409 | |
michael@0 | 1410 | case 'w': |
michael@0 | 1411 | pwdata.source = PW_PLAINTEXT; |
michael@0 | 1412 | pwdata.data = PL_strdup(optstate->value); |
michael@0 | 1413 | break; |
michael@0 | 1414 | |
michael@0 | 1415 | case 'W': |
michael@0 | 1416 | pwdata.source = PW_FROMFILE; |
michael@0 | 1417 | pwdata.data = PL_strdup(optstate->value); |
michael@0 | 1418 | break; |
michael@0 | 1419 | |
michael@0 | 1420 | case 'z': enableCompression = PR_TRUE; break; |
michael@0 | 1421 | |
michael@0 | 1422 | case 0: /* positional parameter */ |
michael@0 | 1423 | if (hostName) { |
michael@0 | 1424 | Usage(progName); |
michael@0 | 1425 | } |
michael@0 | 1426 | hostName = PL_strdup(optstate->value); |
michael@0 | 1427 | break; |
michael@0 | 1428 | |
michael@0 | 1429 | default: |
michael@0 | 1430 | case '?': |
michael@0 | 1431 | Usage(progName); |
michael@0 | 1432 | break; |
michael@0 | 1433 | |
michael@0 | 1434 | } |
michael@0 | 1435 | } |
michael@0 | 1436 | PL_DestroyOptState(optstate); |
michael@0 | 1437 | |
michael@0 | 1438 | if (!hostName || status == PL_OPT_BAD) |
michael@0 | 1439 | Usage(progName); |
michael@0 | 1440 | |
michael@0 | 1441 | if (fullhs!= NO_FULLHS_PERCENTAGE && (fullhs < 0 || fullhs>100 || NoReuse) ) |
michael@0 | 1442 | Usage(progName); |
michael@0 | 1443 | |
michael@0 | 1444 | if (port == 0) |
michael@0 | 1445 | Usage(progName); |
michael@0 | 1446 | |
michael@0 | 1447 | if (fileName) |
michael@0 | 1448 | readBigFile(fileName); |
michael@0 | 1449 | |
michael@0 | 1450 | PK11_SetPasswordFunc(SECU_GetModulePassword); |
michael@0 | 1451 | |
michael@0 | 1452 | tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT"); |
michael@0 | 1453 | if (tmp && tmp[0]) { |
michael@0 | 1454 | int sec = PORT_Atoi(tmp); |
michael@0 | 1455 | if (sec > 0) { |
michael@0 | 1456 | maxInterval = PR_SecondsToInterval(sec); |
michael@0 | 1457 | } |
michael@0 | 1458 | } |
michael@0 | 1459 | |
michael@0 | 1460 | /* Call the NSS initialization routines */ |
michael@0 | 1461 | rv = NSS_Initialize(dir, "", "", SECMOD_DB, NSS_INIT_READONLY); |
michael@0 | 1462 | if (rv != SECSuccess) { |
michael@0 | 1463 | fputs("NSS_Init failed.\n", stderr); |
michael@0 | 1464 | exit(1); |
michael@0 | 1465 | } |
michael@0 | 1466 | ssl3stats = SSL_GetStatistics(); |
michael@0 | 1467 | Cert_And_Key.lock = PR_NewLock(); |
michael@0 | 1468 | Cert_And_Key.nickname = nickName; |
michael@0 | 1469 | Cert_And_Key.wincx = &pwdata; |
michael@0 | 1470 | Cert_And_Key.cert = NULL; |
michael@0 | 1471 | Cert_And_Key.key = NULL; |
michael@0 | 1472 | |
michael@0 | 1473 | if (PR_FALSE == FindCertAndKey(&Cert_And_Key)) { |
michael@0 | 1474 | |
michael@0 | 1475 | if (Cert_And_Key.cert == NULL) { |
michael@0 | 1476 | fprintf(stderr, "strsclnt: Can't find certificate %s\n", Cert_And_Key.nickname); |
michael@0 | 1477 | exit(1); |
michael@0 | 1478 | } |
michael@0 | 1479 | |
michael@0 | 1480 | if (Cert_And_Key.key == NULL) { |
michael@0 | 1481 | fprintf(stderr, "strsclnt: Can't find Private Key for cert %s\n", |
michael@0 | 1482 | Cert_And_Key.nickname); |
michael@0 | 1483 | exit(1); |
michael@0 | 1484 | } |
michael@0 | 1485 | |
michael@0 | 1486 | } |
michael@0 | 1487 | |
michael@0 | 1488 | client_main(port, connections, &Cert_And_Key, hostName, |
michael@0 | 1489 | sniHostName); |
michael@0 | 1490 | |
michael@0 | 1491 | /* clean up */ |
michael@0 | 1492 | if (Cert_And_Key.cert) { |
michael@0 | 1493 | CERT_DestroyCertificate(Cert_And_Key.cert); |
michael@0 | 1494 | } |
michael@0 | 1495 | if (Cert_And_Key.key) { |
michael@0 | 1496 | SECKEY_DestroyPrivateKey(Cert_And_Key.key); |
michael@0 | 1497 | } |
michael@0 | 1498 | |
michael@0 | 1499 | PR_DestroyLock(Cert_And_Key.lock); |
michael@0 | 1500 | |
michael@0 | 1501 | if (pwdata.data) { |
michael@0 | 1502 | PL_strfree(pwdata.data); |
michael@0 | 1503 | } |
michael@0 | 1504 | if (Cert_And_Key.nickname) { |
michael@0 | 1505 | PL_strfree(Cert_And_Key.nickname); |
michael@0 | 1506 | } |
michael@0 | 1507 | if (sniHostName) { |
michael@0 | 1508 | PL_strfree(sniHostName); |
michael@0 | 1509 | } |
michael@0 | 1510 | |
michael@0 | 1511 | PL_strfree(hostName); |
michael@0 | 1512 | |
michael@0 | 1513 | /* some final stats. */ |
michael@0 | 1514 | if (ssl3stats->hsh_sid_cache_hits + |
michael@0 | 1515 | ssl3stats->hsh_sid_cache_misses + |
michael@0 | 1516 | ssl3stats->hsh_sid_cache_not_ok + |
michael@0 | 1517 | ssl3stats->hsh_sid_stateless_resumes == 0) { |
michael@0 | 1518 | /* presumably we were testing SSL2. */ |
michael@0 | 1519 | printf("strsclnt: SSL2 - %d server certificates tested.\n", |
michael@0 | 1520 | certsTested); |
michael@0 | 1521 | } else { |
michael@0 | 1522 | printf( |
michael@0 | 1523 | "strsclnt: %ld cache hits; %ld cache misses, %ld cache not reusable\n" |
michael@0 | 1524 | " %ld stateless resumes\n", |
michael@0 | 1525 | ssl3stats->hsh_sid_cache_hits, |
michael@0 | 1526 | ssl3stats->hsh_sid_cache_misses, |
michael@0 | 1527 | ssl3stats->hsh_sid_cache_not_ok, |
michael@0 | 1528 | ssl3stats->hsh_sid_stateless_resumes); |
michael@0 | 1529 | } |
michael@0 | 1530 | |
michael@0 | 1531 | if (!NoReuse) { |
michael@0 | 1532 | if (enableSessionTickets) |
michael@0 | 1533 | exitVal = (ssl3stats->hsh_sid_stateless_resumes == 0); |
michael@0 | 1534 | else |
michael@0 | 1535 | exitVal = (ssl3stats->hsh_sid_cache_misses > 1) || |
michael@0 | 1536 | (ssl3stats->hsh_sid_stateless_resumes != 0); |
michael@0 | 1537 | if (!exitVal) |
michael@0 | 1538 | exitVal = (ssl3stats->hsh_sid_cache_not_ok != 0) || |
michael@0 | 1539 | (certsTested > 1); |
michael@0 | 1540 | } else { |
michael@0 | 1541 | printf("strsclnt: NoReuse - %d server certificates tested.\n", |
michael@0 | 1542 | certsTested); |
michael@0 | 1543 | if (ssl3stats->hsh_sid_cache_hits + |
michael@0 | 1544 | ssl3stats->hsh_sid_cache_misses + |
michael@0 | 1545 | ssl3stats->hsh_sid_cache_not_ok + |
michael@0 | 1546 | ssl3stats->hsh_sid_stateless_resumes > 0) { |
michael@0 | 1547 | exitVal = (ssl3stats->hsh_sid_cache_misses != connections) || |
michael@0 | 1548 | (ssl3stats->hsh_sid_stateless_resumes != 0) || |
michael@0 | 1549 | (certsTested != connections); |
michael@0 | 1550 | } else { /* ssl2 connections */ |
michael@0 | 1551 | exitVal = (certsTested != connections); |
michael@0 | 1552 | } |
michael@0 | 1553 | } |
michael@0 | 1554 | |
michael@0 | 1555 | exitVal = ( exitVal || failed_already ); |
michael@0 | 1556 | SSL_ClearSessionCache(); |
michael@0 | 1557 | if (NSS_Shutdown() != SECSuccess) { |
michael@0 | 1558 | printf("strsclnt: NSS_Shutdown() failed.\n"); |
michael@0 | 1559 | exit(1); |
michael@0 | 1560 | } |
michael@0 | 1561 | |
michael@0 | 1562 | PR_Cleanup(); |
michael@0 | 1563 | return exitVal; |
michael@0 | 1564 | } |
michael@0 | 1565 |