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