This patch modifies the support/ab.c to handle SSL/TLS properly.
The current implementation in 2.0.54 seems to be broken because it dumps core when I compile it with "-DUSE_SSL". Even when I go back to 2.0.39, it still has many problems.
i.e. - Asynchronous I/O does not work with SSL, which means only one slow connection prevents all other concurrent connections from proceeding. - It sleeps one second for each SSL connections during SSL handshake. - SIGSEGV occurs with "-v 4" because of the buffer overflow inside ssl_print_cert_info(). - Cannot specify either protocol version or cipher suites.
This patch makes ab work with asynchronous I/O even in SSL/TLS, while it introduces 2 more options for SSL/TLS.
-Z ciphersuite Specify SSL/TLS cipher suite (See openssl ciphers) -f protocol Specify SSL/TLS protocol (SSL2, SSL3, TLS1, or ALL)
Example: ab -f SSL3 -Z DES-CBC3-SHA -n 1000 -c 100 https://server/
This patch also includes the modification for configure.in to set "-DAB_USE_SSL" automatically when you configure apache with "--enable-ssl". You have to run buildconf after applying this patch, of course.
-- Masaoki Kobayashi <[EMAIL PROTECTED]>
diff -urN httpd-2.0.54/configure.in httpd-2.0.54-ab-ssl-patch/configure.in --- httpd-2.0.54/configure.in 2005-03-30 20:00:06.000000000 +0900 +++ httpd-2.0.54-ab-ssl-patch/configure.in 2005-05-08 23:36:00.000000000 +0900 @@ -395,6 +395,10 @@ APACHE_SUBST(SHLIBPATH_VAR) APACHE_SUBST(OS_SPECIFIC_VARS) +if test "$enable_ssl" != "no"; then + APR_ADDTO(DEFS, "-DAB_USE_SSL") +fi + PRE_SHARED_CMDS='echo ""' POST_SHARED_CMDS='echo ""' diff -urN httpd-2.0.54/support/ab.c httpd-2.0.54-ab-ssl-patch/support/ab.c --- httpd-2.0.54/support/ab.c 2005-02-05 05:21:18.000000000 +0900 +++ httpd-2.0.54-ab-ssl-patch/support/ab.c 2005-05-08 23:36:00.000000000 +0900 @@ -91,7 +91,7 @@ * ab - or to due to a change in the distribution it is compiled with * (such as an APR change in for example blocking). */ -#define AP_AB_BASEREVISION "2.0.41-dev" +#define AP_AB_BASEREVISION "2.0.41-dev-ssl-patch" /* * BUGS: @@ -144,7 +144,7 @@ #if APR_HAVE_STDLIB_H #include <stdlib.h> -#ifdef USE_SSL +#ifdef AB_USE_SSL #if ((!(RSAREF)) && (!(SYSSSL))) /* Libraries on most systems.. */ #include <openssl/rsa.h> @@ -154,6 +154,15 @@ #include <openssl/err.h> #include <openssl/ssl.h> #include <openssl/rand.h> +#ifdef RSAREF + typedef STACK X509_STACK_TYPE; +# define SK_NUM(x) sk_num(x) +# define SK_VALUE(x,y) sk_value(x,y) +#else + typedef STACK_OF(X509) X509_STACK_TYPE; +# define SK_NUM(x) sk_X509_num(x) +# define SK_VALUE(x,y) sk_X509_value(x,y) +#endif #else /* Libraries for RSAref and SYSSSL */ #include <rsa.h> @@ -214,15 +223,12 @@ done; /* Connection closed */ int socknum; -#ifdef USE_SSL +#ifdef AB_USE_SSL SSL *ssl; #endif }; struct data { -#ifdef USE_SSL - /* XXXX insert SSL timings */ -#endif int read; /* number of bytes read */ apr_time_t starttime; /* start time of connection in seconds since * Jan. 1, 1970 */ @@ -288,10 +294,12 @@ long good = 0, bad = 0; /* number of good and bad requests */ long epipe = 0; /* number of broken pipe writes */ -#ifdef USE_SSL -int ssl = 0; -SSL_CTX *ctx; -BIO *bio_out,*bio_err; +#ifdef AB_USE_SSL +int is_ssl; +SSL_CTX *ssl_ctx; +char *ssl_cipher = NULL; +char *ssl_info = NULL; +BIO *bio_out, *bio_err; static void write_request(struct connection * c); #endif @@ -351,81 +359,13 @@ exit(rv); } -#if defined(USE_SSL) && USE_THREADS -/* - * To ensure thread-safetyness in OpenSSL - work in progress - */ - -static apr_thread_mutex_t **lock_cs; -static int lock_num_locks; - -static void ssl_util_thr_lock(int mode, int type, - const char *file, int line) -{ - if (type < lock_num_locks) { - if (mode & CRYPTO_LOCK) { - apr_thread_mutex_lock(lock_cs[type]); - } - else { - apr_thread_mutex_unlock(lock_cs[type]); - } - } -} - -static unsigned long ssl_util_thr_id(void) -{ - /* OpenSSL needs this to return an unsigned long. On OS/390, the pthread - * id is a structure twice that big. Use the TCB pointer instead as a - * unique unsigned long. - */ -#ifdef __MVS__ - struct PSA { - char unmapped[540]; - unsigned long PSATOLD; - } *psaptr = 0; - - return psaptr->PSATOLD; -#else - return (unsigned long) apr_os_thread_current(); -#endif -} - -static apr_status_t ssl_util_thread_cleanup(void *data) -{ - CRYPTO_set_locking_callback(NULL); - - /* Let the registered mutex cleanups do their own thing - */ - return APR_SUCCESS; -} - -void ssl_util_thread_setup(apr_pool_t *p) -{ - int i; - - lock_num_locks = CRYPTO_num_locks(); - lock_cs = apr_palloc(p, lock_num_locks * sizeof(*lock_cs)); - - for (i = 0; i < lock_num_locks; i++) { - apr_thread_mutex_create(&(lock_cs[i]), APR_THREAD_MUTEX_DEFAULT, p); - } - - CRYPTO_set_id_callback(ssl_util_thr_id); - - CRYPTO_set_locking_callback(ssl_util_thr_lock); - - apr_pool_cleanup_register(p, NULL, ssl_util_thread_cleanup, - apr_pool_cleanup_null); -} -#endif - /* --------------------------------------------------------- */ /* write out request to a connection - assumes we can write * (small) request out in one go into our new socket buffer * */ -#ifdef USE_SSL -long ssl_print_cb(BIO *bio,int cmd,const char *argp,int argi,long argl,long ret) +#ifdef AB_USE_SSL +static long ssl_print_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret) { BIO *out; @@ -434,20 +374,38 @@ if (cmd == (BIO_CB_READ|BIO_CB_RETURN)) { - BIO_printf(out,"read from %08X [%08lX] (%d bytes => %ld (0x%X))\n", - bio,argp,argi,ret,ret); - BIO_dump(out,(char *)argp,(int)ret); + BIO_printf(out, "read from %08X [%08lX] (%d bytes => %ld (0x%X))\n", + bio, argp, argi, ret, ret); + BIO_dump(out, (char *)argp, (int)ret); return(ret); } else if (cmd == (BIO_CB_WRITE|BIO_CB_RETURN)) { - BIO_printf(out,"write to %08X [%08lX] (%d bytes => %ld (0x%X))\n", - bio,argp,argi,ret,ret); - BIO_dump(out,(char *)argp,(int)ret); + BIO_printf(out, "write to %08X [%08lX] (%d bytes => %ld (0x%X))\n", + bio, argp, argi, ret, ret); + BIO_dump(out, (char *)argp, (int)ret); } return(ret); } +static void ssl_state_cb(const SSL *s, int w, int r) +{ + if (w & SSL_CB_ALERT) { + BIO_printf(bio_err, "SSL/TLS Alert [%s] %s:%s\n", + (w & SSL_CB_READ ? "read" : "write"), + SSL_alert_type_string_long(r), + SSL_alert_desc_string_long(r)); + } else if (w & SSL_CB_LOOP) { + BIO_printf(bio_err, "SSL/TLS State [%s] %s\n", + (SSL_in_connect_init(s) ? "connect" : "-"), + SSL_state_string_long(s)); + } else if (w & (SSL_CB_HANDSHAKE_START|SSL_CB_HANDSHAKE_DONE)) { + BIO_printf(bio_err, "SSL/TLS Handshake [%s] %s\n", + (w & SSL_CB_HANDSHAKE_START ? "Start" : "Done"), + SSL_state_string_long(s)); + } +} + #ifndef RAND_MAX #include <limits.h> #define RAND_MAX INT_MAX @@ -499,14 +457,12 @@ nDone += 128; } -int ssl_print_connection_info(bio,ssl) -BIO *bio; -SSL *ssl; +int ssl_print_connection_info(BIO *bio, SSL *ssl) { SSL_CIPHER *c; int alg_bits,bits; - c=SSL_get_current_cipher(ssl); + c = SSL_get_current_cipher(ssl); BIO_printf(bio,"Cipher Suite Protocol :%s\n", SSL_CIPHER_get_version(c)); BIO_printf(bio,"Cipher Suite Name :%s\n",SSL_CIPHER_get_name(c)); @@ -516,170 +472,125 @@ return(1); } -int ssl_print_cert_info(bio,x509cert) -BIO *bio; -X509 *x509cert; +void ssl_print_cert_info(BIO *bio, X509 *cert) { X509_NAME *dn; - char buf[64]; + char buf[1024]; - BIO_printf(bio,"Certificate version: %d\n",X509_get_version(x509cert)+1); + BIO_printf(bio, "Certificate version: %d\n", X509_get_version(cert)+1); BIO_printf(bio,"Valid from: "); - ASN1_UTCTIME_print(bio, X509_get_notBefore(x509cert)); + ASN1_UTCTIME_print(bio, X509_get_notBefore(cert)); BIO_printf(bio,"\n"); BIO_printf(bio,"Valid to : "); - ASN1_UTCTIME_print(bio, X509_get_notAfter(x509cert)); + ASN1_UTCTIME_print(bio, X509_get_notAfter(cert)); BIO_printf(bio,"\n"); BIO_printf(bio,"Public key is %d bits\n", - EVP_PKEY_bits(X509_get_pubkey(x509cert))); + EVP_PKEY_bits(X509_get_pubkey(cert))); - dn=X509_get_issuer_name(x509cert); - X509_NAME_oneline(dn, buf, BUFSIZ); - BIO_printf(bio,"The issuer name is %s\n", buf); - - dn=X509_get_subject_name(x509cert); - X509_NAME_oneline(dn, buf, BUFSIZ); - BIO_printf(bio,"The subject name is %s\n", buf); + dn = X509_get_issuer_name(cert); + X509_NAME_oneline(dn, buf, sizeof(buf)); + BIO_printf(bio, "The issuer name is %s\n", buf); + + dn=X509_get_subject_name(cert); + X509_NAME_oneline(dn, buf, sizeof(buf)); + BIO_printf(bio, "The subject name is %s\n", buf); /* dump the extension list too */ - BIO_printf(bio,"Extension Count: %d\n",X509_get_ext_count(x509cert)); - - return(1); + BIO_printf(bio, "Extension Count: %d\n", X509_get_ext_count(cert)); } -void ssl_start_connect(struct connection * c) +void ssl_print_info(struct connection *c) { - BIO *bio; - X509 *x509cert; -#ifdef RSAREF - STACK *sk; -#else - STACK_OF(X509) *sk; -#endif - int i, count, hdone = 0; - char ssl_hostname[80]; - - /* XXX - Verify if it's okay - TBD */ - if (requests < concurrency) - requests = concurrency; - - if (!(started < requests)) - return; - - c->read = 0; - c->bread = 0; - c->keepalive = 0; - c->cbx = 0; - c->gotheader = 0; - c->rwrite = 0; - if (c->ctx) - apr_pool_destroy(c->ctx); - apr_pool_create(&c->ctx, cntxt); - - if ((c->ssl=SSL_new(ctx)) == NULL) - { - BIO_printf(bio_err,"SSL_new failed\n"); - exit(1); - } - - ssl_rand_seed(); - - c->start = apr_time_now(); - memset(ssl_hostname, 0, 80); - sprintf(ssl_hostname, "%s:%d", hostname, port); - - if ((bio = BIO_new_connect(ssl_hostname)) == NULL) - { - BIO_printf(bio_err,"BIO_new_connect failed\n"); - exit(1); - } - SSL_set_bio(c->ssl,bio,bio); - SSL_set_connect_state(c->ssl); - - if (verbosity >= 4) - { - BIO_set_callback(bio,ssl_print_cb); - BIO_set_callback_arg(bio,(void*)bio_err); - } - - while (!hdone) - { - i = SSL_do_handshake(c->ssl); - - switch (SSL_get_error(c->ssl,i)) - { - case SSL_ERROR_NONE: - hdone=1; - break; - case SSL_ERROR_SSL: - case SSL_ERROR_SYSCALL: - BIO_printf(bio_err,"SSL connection failed\n"); - err_conn++; - c->state = STATE_UNCONNECTED; - if (bad++ > 10) { - SSL_free (c->ssl); - BIO_printf(bio_err,"\nTest aborted after 10 failures\n\n"); - exit (1); - } - break; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_CONNECT: - BIO_printf(bio_err, "Waiting .. sleep(1)\n"); - apr_sleep(apr_time_from_sec(1)); - c->state = STATE_CONNECTED; - c->rwrite = 0; - break; - case SSL_ERROR_ZERO_RETURN: - BIO_printf(bio_err,"socket closed\n"); - break; - } - } - - if (verbosity >= 2) - { - BIO_printf(bio_err, "\n"); - sk = SSL_get_peer_cert_chain(c->ssl); -#ifdef RSAREF - if ((count = sk_num(sk)) > 0) -#else - if ((count = sk_X509_num(sk)) > 0) -#endif - { - for (i=1; i<count; i++) - { -#ifdef RSAREF - x509cert = (X509 *)sk_value(sk,i); -#else - x509cert = (X509 *)sk_X509_value(sk,i); -#endif - ssl_print_cert_info(bio_out,x509cert); - X509_free(x509cert); - } - } - - x509cert = SSL_get_peer_certificate(c->ssl); - if (x509cert == NULL) - BIO_printf(bio_out, "Anon DH\n"); - else - { - BIO_printf(bio_out, "Peer certificate\n"); - ssl_print_cert_info(bio_out,x509cert); - X509_free(x509cert); - } + X509_STACK_TYPE *sk; + X509 *cert; + int count; + + BIO_printf(bio_err, "\n"); + sk = SSL_get_peer_cert_chain(c->ssl); + if ((count = SK_NUM(sk)) > 0) { + int i; + for (i=1; i<count; i++) { + cert = (X509 *)SK_VALUE(sk, i); + ssl_print_cert_info(bio_out, cert); + X509_free(cert); + } + } + cert = SSL_get_peer_certificate(c->ssl); + if (cert == NULL) { + BIO_printf(bio_out, "Anon DH\n"); + } else { + BIO_printf(bio_out, "Peer certificate\n"); + ssl_print_cert_info(bio_out, cert); + X509_free(cert); + } + ssl_print_connection_info(bio_err,c->ssl); + SSL_SESSION_print(bio_err, SSL_get_session(c->ssl)); +} + +void ssl_proceed_handshake(struct connection *c) +{ + int do_next = 1; + + while(do_next) { + int ret, ecode; + apr_pollfd_t new_pollfd; + + ret = SSL_do_handshake(c->ssl); + ecode = SSL_get_error(c->ssl, ret); + + switch(ecode) { + case SSL_ERROR_NONE: + if (verbosity >= 2) + ssl_print_info(c); + if (ssl_info == NULL) { + SSL_CIPHER *ci; + X509 *cert; + int sk_bits, pk_bits; + + ci = SSL_get_current_cipher(c->ssl); + SSL_CIPHER_get_bits(ci, &sk_bits); + cert = SSL_get_peer_certificate(c->ssl); + if (cert) + pk_bits = EVP_PKEY_bits(X509_get_pubkey(cert)); + else + pk_bits = 0; /* Anon DH */ - ssl_print_connection_info(bio_err,c->ssl); - SSL_SESSION_print(bio_err,SSL_get_session(c->ssl)); + ssl_info = malloc(128); + apr_snprintf(ssl_info, 128, "%s,%s,%d,%d", + SSL_CIPHER_get_version(ci), + SSL_CIPHER_get_name(ci), + pk_bits, sk_bits); + } + write_request(c); + do_next = 0; + break; + case SSL_ERROR_WANT_READ: + new_pollfd.desc_type = APR_POLL_SOCKET; + new_pollfd.reqevents = APR_POLLIN; + new_pollfd.desc.s = c->aprsock; + new_pollfd.client_data = c; + apr_pollset_add(readbits, &new_pollfd); + do_next = 0; + break; + case SSL_ERROR_WANT_WRITE: + /* Try again */ + do_next = 1; + break; + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_SSL: + case SSL_ERROR_SYSCALL: + /* Unexpected result */ + BIO_printf(bio_err, "SSL handshake failed (%d).\n", ecode); + ERR_print_errors(bio_err); + close_connection(c); + break; + } } - - /* connected first time */ - started++; - write_request(c); } -#endif /* USE_SSL */ +#endif /* AB_USE_SSL */ static void write_request(struct connection * c) { @@ -692,9 +603,6 @@ * First time round ? */ if (c->rwrite == 0) { -#ifdef USE_SSL - if (ssl != 1) -#endif apr_socket_timeout_set(c->aprsock, 0); c->connect = tnow; c->rwrite = reqlen; @@ -708,19 +616,19 @@ return; } -#ifdef USE_SSL - if (ssl == 1) { +#ifdef AB_USE_SSL + if (c->ssl) { apr_size_t e_ssl; - e_ssl = SSL_write(c->ssl,request + c->rwrote, l); - if (e_ssl != l) - { + e_ssl = SSL_write(c->ssl, request + c->rwrote, l); + if (e_ssl != l) { printf("SSL write failed - closing connection\n"); + ERR_print_errors(bio_err); close_connection (c); return; } l = e_ssl; - } - else + e = APR_SUCCESS; + } else #endif e = apr_send(c->aprsock, request + c->rwrote, &l); @@ -730,9 +638,6 @@ if (l == c->rwrite) break; -#ifdef USE_SSL - if (ssl != 1) -#endif if (e != APR_SUCCESS) { /* * Let's hope this traps EWOULDBLOCK too ! @@ -751,9 +656,6 @@ totalposted += c->rwrite; c->state = STATE_READ; c->endwrite = apr_time_now(); -#ifdef USE_SSL - if (ssl != 1) -#endif { apr_pollfd_t new_pollfd; new_pollfd.desc_type = APR_POLL_SOCKET; @@ -820,6 +722,10 @@ printf("Server Software: %s\n", servername); printf("Server Hostname: %s\n", hostname); printf("Server Port: %hd\n", port); +#ifdef AB_USE_SSL + if (is_ssl) + printf("SSL/TLS Protocol: %s\n", ssl_info); +#endif printf("\n"); printf("Document Path: %s\n", path); printf("Document Length: %" APR_SIZE_T_FMT " bytes\n", doclen); @@ -1184,13 +1090,6 @@ { apr_status_t rv; -#ifdef USE_SSL - if (ssl == 1) { - ssl_start_connect(c); - return; - } -#endif - if (!(started < requests)) return; @@ -1213,6 +1112,28 @@ apr_err("socket nonblock", rv); } c->start = apr_time_now(); +#ifdef AB_USE_SSL + if (is_ssl) { + BIO *bio; + apr_os_sock_t fd; + if ((c->ssl = SSL_new(ssl_ctx)) == NULL) { + BIO_printf(bio_err, "SSL_new failed.\n"); + ERR_print_errors(bio_err); + exit(1); + } + ssl_rand_seed(); + apr_os_sock_get(&fd, c->aprsock); + bio = BIO_new_socket(fd, BIO_NOCLOSE); + SSL_set_bio(c->ssl, bio, bio); + SSL_set_connect_state(c->ssl); + if (verbosity >= 4) { + BIO_set_callback(bio, ssl_print_cb); + BIO_set_callback_arg(bio, bio_err); + } + } else { + c->ssl = NULL; + } +#endif if ((rv = apr_connect(c->aprsock, destsa)) != APR_SUCCESS) { if (APR_STATUS_IS_EINPROGRESS(rv)) { apr_pollfd_t new_pollfd; @@ -1246,6 +1167,11 @@ /* connected first time */ c->state = STATE_CONNECTED; started++; +#ifdef AB_USE_SSL + if (c->ssl) + ssl_proceed_handshake(c); + else +#endif write_request(c); } @@ -1288,18 +1214,18 @@ } } -#ifdef USE_SSL - if (ssl == 1) { - SSL_shutdown(c->ssl); - SSL_free(c->ssl); - } - else -#endif { apr_pollfd_t remove_pollfd; remove_pollfd.desc_type = APR_POLL_SOCKET; remove_pollfd.desc.s = c->aprsock; apr_pollset_remove(readbits, &remove_pollfd); +#ifdef AB_USE_SSL + if (c->ssl) { + SSL_shutdown(c->ssl); + SSL_free(c->ssl); + c->ssl = NULL; + } +#endif apr_socket_close(c->aprsock); } c->state = STATE_UNCONNECTED; @@ -1321,17 +1247,17 @@ char respcode[4]; /* 3 digits and null */ r = sizeof(buffer); -#ifdef USE_SSL - if (ssl == 1) - { +#ifdef AB_USE_SSL + if (c->ssl) { status = SSL_read (c->ssl, buffer, r); if (status <= 0) { good++; c->read = 0; if (status < 0) printf("SSL read failed - closing connection\n"); + ERR_print_errors(bio_err); close_connection(c); return; } - r = status; + r = status; } else { #endif @@ -1350,7 +1276,7 @@ * certain number of them before completely failing? -aaron */ apr_err("apr_recv", status); } -#ifdef USE_SSL +#ifdef AB_USE_SSL } #endif @@ -1622,9 +1548,6 @@ #endif /* NOT_ASCII */ /* This only needs to be done once */ -#ifdef USE_SSL - if (ssl != 1) -#endif if ((rv = apr_sockaddr_info_get(&destsa, connecthost, APR_UNSPEC, connectport, 0, cntxt)) != APR_SUCCESS) { char buf[120]; @@ -1656,11 +1579,6 @@ } n = concurrency; -#ifdef USE_SSL - if (ssl == 1) - status = APR_SUCCESS; - else -#endif status = apr_pollset_poll(readbits, aprtimeout, &n, &pollresults); if (status != APR_SUCCESS) apr_err("apr_poll", status); @@ -1676,16 +1594,18 @@ /* * If the connection isn't connected how can we check it? */ - if (c->state == STATE_UNCONNECTED) + if (c->state == STATE_UNCONNECTED) { continue; + } -#ifdef USE_SSL - if (ssl == 1) - rv = APR_POLLIN; - else -#endif rv = next_fd->rtnevents; +#ifdef AB_USE_SSL + if (c->state == STATE_CONNECTED && c->ssl && SSL_in_init(c->ssl)) { + ssl_proceed_handshake(c); + continue; + } +#endif /* * Notes: APR_POLLHUP is set after FIN is received on some * systems, so treat that like APR_POLLIN so that we try to read @@ -1727,6 +1647,11 @@ } else { c->state = STATE_CONNECTED; +#ifdef AB_USE_SSL + if (c->ssl) + ssl_proceed_handshake(c); + else +#endif write_request(c); } } @@ -1742,17 +1667,14 @@ * connection is in STATE_READ or STATE_CONNECTING we'll add the * socket back in as APR_POLLIN. */ -#ifdef USE_SSL - if (ssl != 1) -#endif - if (c->state == STATE_READ) { - apr_pollfd_t new_pollfd; - new_pollfd.desc_type = APR_POLL_SOCKET; - new_pollfd.reqevents = APR_POLLIN; - new_pollfd.desc.s = c->aprsock; - new_pollfd.client_data = c; - apr_pollset_add(readbits, &new_pollfd); - } + if (c->state == STATE_READ) { + apr_pollfd_t new_pollfd; + new_pollfd.desc_type = APR_POLL_SOCKET; + new_pollfd.reqevents = APR_POLLIN; + new_pollfd.desc.s = c->aprsock; + new_pollfd.client_data = c; + apr_pollset_add(readbits, &new_pollfd); + } } } @@ -1791,7 +1713,7 @@ static void usage(const char *progname) { fprintf(stderr, "Usage: %s [options] [http" -#ifdef USE_SSL +#ifdef AB_USE_SSL "[s]" #endif "://]hostname[:port]/path\n", progname); @@ -1821,10 +1743,11 @@ fprintf(stderr, " -S Do not show confidence estimators and warnings.\n"); fprintf(stderr, " -g filename Output collected data to gnuplot format file.\n"); fprintf(stderr, " -e filename Output CSV file with percentages served\n"); -#ifdef USE_SSL - fprintf(stderr, " -s Use httpS instead of HTTP (SSL)\n"); -#endif fprintf(stderr, " -h Display usage information (this message)\n"); +#ifdef AB_USE_SSL + fprintf(stderr, " -Z ciphersuite Specify SSL/TLS cipher suite (See openssl ciphers)\n"); + fprintf(stderr, " -f protocol Specify SSL/TLS protocol (SSL2, SSL3, TLS1, or ALL)\n"); +#endif exit(EINVAL); } @@ -1844,22 +1767,20 @@ if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0) { url += 7; -#ifdef USE_SSL - ssl = 0; +#ifdef AB_USE_SSL + is_ssl = 0; #endif } else -#ifdef USE_SSL if (strlen(url) > 8 && strncmp(url, "https://", 8) == 0) { +#ifdef AB_USE_SSL url += 8; - ssl = 1; - } + is_ssl = 1; #else - if (strlen(url) > 8 && strncmp(url, "https://", 8) == 0) { fprintf(stderr, "SSL not compiled in; no https support\n"); exit(1); - } #endif + } if ((cp = strchr(url, '/')) == NULL) return 1; @@ -1880,8 +1801,8 @@ } if (port == 0) { /* no port specified */ -#ifdef USE_SSL - if (ssl == 1) +#ifdef AB_USE_SSL + if (is_ssl) port = 443; else #endif @@ -1889,8 +1810,8 @@ } if (( -#ifdef USE_SSL - (ssl == 1) && (port != 443)) || (( ssl == 0 ) && +#ifdef AB_USE_SSL + is_ssl && (port != 443)) || (!is_ssl && #endif (port != 80))) { @@ -1955,6 +1876,9 @@ apr_getopt_t *opt; const char *optarg; char c; +#ifdef AB_USE_SSL + SSL_METHOD *meth = SSLv23_client_method(); +#endif /* table defaults */ tablestring = ""; @@ -1989,19 +1913,11 @@ apr_getopt_init(&opt, cntxt, argc, argv); while ((status = apr_getopt(opt, "n:c:t:T:p:v:kVhwix:y:z:C:H:P:A:g:X:de:Sq" -#ifdef USE_SSL - "s" +#ifdef AB_USE_SSL + "Z:f:" #endif ,&c, &optarg)) == APR_SUCCESS) { switch (c) { - case 's': -#ifdef USE_SSL - ssl = 1; - break; -#else - fprintf(stderr, "SSL not compiled in; no https support\n"); - exit(1); -#endif case 'n': requests = atoi(optarg); if (!requests) { @@ -2133,6 +2049,22 @@ case 'V': copyright(); return 0; +#ifdef AB_USE_SSL + case 'Z': + ssl_cipher = strdup(optarg); + break; + case 'f': + if (strncasecmp(optarg, "ALL", 3) == 0) { + meth = SSLv23_client_method(); + } else if (strncasecmp(optarg, "SSL2", 4) == 0) { + meth = SSLv2_client_method(); + } else if (strncasecmp(optarg, "SSL3", 4) == 0) { + meth = SSLv3_client_method(); + } else if (strncasecmp(optarg, "TLS1", 4) == 0) { + meth = TLSv1_client_method(); + } + break; +#endif } } @@ -2161,7 +2093,7 @@ else heartbeatres = 0; -#ifdef USE_SSL +#ifdef AB_USE_SSL #ifdef RSAREF R_malloc_init(); #else @@ -2169,19 +2101,25 @@ #endif SSL_load_error_strings(); SSL_library_init(); - bio_out=BIO_new_fp(stdout,BIO_NOCLOSE); - bio_err=BIO_new_fp(stderr,BIO_NOCLOSE); + bio_out = BIO_new_fp(stdout, BIO_NOCLOSE); + bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); - /* TODO: Allow force SSLv2_client_method() (TLSv1?) */ - if (!(ctx = SSL_CTX_new(SSLv23_client_method()))) { - fprintf(stderr, "Could not init SSL CTX"); + if (!(ssl_ctx = SSL_CTX_new(meth))) { + fprintf(stderr, "Could not initialize an SSL context.\n"); ERR_print_errors_fp(stderr); exit(1); } - SSL_CTX_set_options(ctx, SSL_OP_ALL); -#ifdef USE_THREADS - ssl_util_thread_setup(cntxt); -#endif + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL); + if (ssl_cipher != NULL) { + if (!SSL_CTX_set_cipher_list(ssl_ctx, ssl_cipher)) { + fprintf(stderr, "error setting cipher list [%s]\n", ssl_cipher); + ERR_print_errors_fp(stderr); + exit(1); + } + } + if (verbosity >= 3) { + SSL_CTX_set_info_callback(ssl_ctx, ssl_state_cb); + } #endif #ifdef SIGPIPE apr_signal(SIGPIPE, SIG_IGN); /* Ignore writes to connections that