diff options
Diffstat (limited to 'buckets')
-rw-r--r-- | buckets/allocator.c | 14 | ||||
-rw-r--r-- | buckets/buckets.c | 84 | ||||
-rw-r--r-- | buckets/dechunk_buckets.c | 10 | ||||
-rw-r--r-- | buckets/headers_buckets.c | 6 | ||||
-rw-r--r-- | buckets/mmap_buckets.c | 22 | ||||
-rw-r--r-- | buckets/response_body_buckets.c | 135 | ||||
-rw-r--r-- | buckets/response_buckets.c | 43 | ||||
-rw-r--r-- | buckets/socket_buckets.c | 13 | ||||
-rw-r--r-- | buckets/ssl_buckets.c | 297 |
9 files changed, 518 insertions, 106 deletions
diff --git a/buckets/allocator.c b/buckets/allocator.c index 857cc74..108167e 100644 --- a/buckets/allocator.c +++ b/buckets/allocator.c @@ -144,14 +144,14 @@ serf_bucket_alloc_t *serf_bucket_allocator_create( } #endif - /* ### this implies buckets cannot cross a fork/exec. desirable? - * - * ### hmm. it probably also means that buckets cannot be AROUND - * ### during a fork/exec. the new process will try to clean them - * ### up and figure out there are unfreed blocks... - */ + /* NOTE: On a fork/exec, the child won't bother cleaning up memory. + This is just fine... the memory will go away at exec. + + NOTE: If the child will NOT perform an exec, then the parent or + the child will need to decide who to clean up any + outstanding connection/buckets (as appropriate). */ apr_pool_cleanup_register(pool, allocator, - allocator_cleanup, allocator_cleanup); + allocator_cleanup, apr_pool_cleanup_null); return allocator; } diff --git a/buckets/buckets.c b/buckets/buckets.c index 29175ff..72b3913 100644 --- a/buckets/buckets.c +++ b/buckets/buckets.c @@ -464,11 +464,10 @@ apr_status_t serf_linebuf_fetch( /* Whatever was read, the line is now ready for use. */ linebuf->state = SERF_LINEBUF_READY; + } else { + /* no data available, try again later. */ + return APR_EAGAIN; } - /* ### we need data. gotta check this char. bail if zero?! */ - /* else len == 0 */ - - /* ### status */ } else { int found; @@ -536,3 +535,80 @@ apr_status_t serf_linebuf_fetch( } /* NOTREACHED */ } + +/* Logging functions. + Use with one of the [COMP]_VERBOSE defines so that the compiler knows to + optimize this code out when no logging is needed. */ +static void log_time() +{ + apr_time_exp_t tm; + + apr_time_exp_lt(&tm, apr_time_now()); + fprintf(stderr, "[%d-%02d-%02dT%02d:%02d:%02d.%06d%+03d] ", + 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_usec, + tm.tm_gmtoff/3600); +} + +void serf__log(int verbose_flag, const char *filename, const char *fmt, ...) +{ + va_list argp; + + if (verbose_flag) { + log_time(); + + if (filename) + fprintf(stderr, "%s: ", filename); + + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + } +} + +void serf__log_nopref(int verbose_flag, const char *fmt, ...) +{ + va_list argp; + + if (verbose_flag) { + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + } +} + +void serf__log_skt(int verbose_flag, const char *filename, apr_socket_t *skt, + const char *fmt, ...) +{ + va_list argp; + + if (verbose_flag) { + apr_sockaddr_t *sa; + log_time(); + + if (skt) { + /* Log local and remote ip address:port */ + fprintf(stderr, "[l:"); + if (apr_socket_addr_get(&sa, APR_LOCAL, skt) == APR_SUCCESS) { + char buf[32]; + apr_sockaddr_ip_getbuf(buf, 32, sa); + fprintf(stderr, "%s:%d", buf, sa->port); + } + fprintf(stderr, " r:"); + if (apr_socket_addr_get(&sa, APR_REMOTE, skt) == APR_SUCCESS) { + char buf[32]; + apr_sockaddr_ip_getbuf(buf, 32, sa); + fprintf(stderr, "%s:%d", buf, sa->port); + } + fprintf(stderr, "] "); + } + + if (filename) + fprintf(stderr, "%s: ", filename); + + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + } +} + diff --git a/buckets/dechunk_buckets.c b/buckets/dechunk_buckets.c index 28dd671..262cffb 100644 --- a/buckets/dechunk_buckets.c +++ b/buckets/dechunk_buckets.c @@ -135,6 +135,11 @@ static apr_status_t serf_dechunk_read(serf_bucket_t *bucket, ctx->body_left = 2; /* CRLF */ } + /* We need more data but there is no more available. */ + if (ctx->body_left && APR_STATUS_IS_EOF(status)) { + return SERF_ERROR_TRUNCATED_HTTP_RESPONSE; + } + /* Return the data we just read. */ return status; @@ -148,6 +153,11 @@ static apr_status_t serf_dechunk_read(serf_bucket_t *bucket, * if we're done reading the chunk terminator. */ ctx->body_left -= *len; + + /* We need more data but there is no more available. */ + if (ctx->body_left && APR_STATUS_IS_EOF(status)) + return SERF_ERROR_TRUNCATED_HTTP_RESPONSE; + if (!ctx->body_left) { ctx->state = STATE_SIZE; } diff --git a/buckets/headers_buckets.c b/buckets/headers_buckets.c index 8bf91b4..1c37ef0 100644 --- a/buckets/headers_buckets.c +++ b/buckets/headers_buckets.c @@ -163,11 +163,11 @@ const char *serf_bucket_headers_get( be comma-separated, that is clearly the correct behavior; for others, the correct behavior is undefined anyway. */ - /* The "+1" is for the comma; serf_bstrmemdup() will also add - one slot for the terminating '\0'. */ + /* The "+1" is for the comma; the +1 in the alloc + call is for the terminating '\0' */ apr_size_t new_size = found->value_size + value_size + 1; char *new_val = serf_bucket_mem_alloc(headers_bucket->allocator, - new_size); + new_size + 1); memcpy(new_val, val, value_size); new_val[value_size] = ','; memcpy(new_val + value_size + 1, found->value, diff --git a/buckets/mmap_buckets.c b/buckets/mmap_buckets.c index f55a76b..c96bce4 100644 --- a/buckets/mmap_buckets.c +++ b/buckets/mmap_buckets.c @@ -19,6 +19,7 @@ #include "serf.h" #include "serf_bucket_util.h" +#if APR_HAS_MMAP typedef struct { apr_mmap_t *mmap; @@ -116,3 +117,24 @@ const serf_bucket_type_t serf_bucket_type_mmap = { serf_mmap_peek, serf_default_destroy_and_data, }; + +#else /* !APR_HAS_MMAP */ + +serf_bucket_t *serf_bucket_mmap_create(apr_mmap_t *file_mmap, + serf_bucket_alloc_t *allocator) +{ + return NULL; +} + +const serf_bucket_type_t serf_bucket_type_mmap = { + "MMAP", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +#endif diff --git a/buckets/response_body_buckets.c b/buckets/response_body_buckets.c new file mode 100644 index 0000000..c9648a6 --- /dev/null +++ b/buckets/response_body_buckets.c @@ -0,0 +1,135 @@ +/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <apr_pools.h> + +#include "serf.h" +#include "serf_bucket_util.h" + +/* Older versions of APR do not have this macro. */ +#ifdef APR_SIZE_MAX +#define REQUESTED_MAX APR_SIZE_MAX +#else +#define REQUESTED_MAX (~((apr_size_t)0)) +#endif + + +typedef struct { + serf_bucket_t *stream; + apr_uint64_t remaining; +} body_context_t; + +serf_bucket_t *serf_bucket_response_body_create( + serf_bucket_t *stream, apr_uint64_t len, serf_bucket_alloc_t *allocator) +{ + body_context_t *ctx; + + ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); + ctx->stream = stream; + ctx->remaining = len; + + return serf_bucket_create(&serf_bucket_type_response_body, allocator, ctx); +} + +static apr_status_t serf_response_body_read(serf_bucket_t *bucket, + apr_size_t requested, + const char **data, + apr_size_t *len) +{ + body_context_t *ctx = bucket->data; + apr_status_t status; + + if (!ctx->remaining) { + *len = 0; + return APR_EOF; + } + + if (requested == SERF_READ_ALL_AVAIL || requested > ctx->remaining) { + if (ctx->remaining <= REQUESTED_MAX) { + requested = (apr_size_t) ctx->remaining; + } else { + requested = REQUESTED_MAX; + } + } + + status = serf_bucket_read(ctx->stream, requested, data, len); + + if (!SERF_BUCKET_READ_ERROR(status)) { + ctx->remaining -= *len; + } + + if (APR_STATUS_IS_EOF(status) && ctx->remaining > 0) { + /* The server sent less data than expected. */ + status = SERF_ERROR_TRUNCATED_HTTP_RESPONSE; + } + + return status; +} + +static apr_status_t serf_response_body_readline(serf_bucket_t *bucket, + int acceptable, int *found, + const char **data, + apr_size_t *len) +{ + body_context_t *ctx = bucket->data; + apr_status_t status; + + if (!ctx->remaining) { + *len = 0; + return APR_EOF; + } + + status = serf_bucket_readline(ctx->stream, acceptable, found, data, len); + + if (!SERF_BUCKET_READ_ERROR(status)) { + ctx->remaining -= *len; + } + + if (APR_STATUS_IS_EOF(status) && ctx->remaining > 0) { + /* The server sent less data than expected. */ + status = SERF_ERROR_TRUNCATED_HTTP_RESPONSE; + } + + return status; +} + +static apr_status_t serf_response_body_peek(serf_bucket_t *bucket, + const char **data, + apr_size_t *len) +{ + body_context_t *ctx = bucket->data; + + return serf_bucket_peek(ctx->stream, data, len); +} + +static void serf_response_body_destroy(serf_bucket_t *bucket) +{ + body_context_t *ctx = bucket->data; + + serf_bucket_destroy(ctx->stream); + + serf_default_destroy_and_data(bucket); +} + +const serf_bucket_type_t serf_bucket_type_response_body = { + "RESPONSE_BODY", + serf_response_body_read, + serf_response_body_readline, + serf_default_read_iovec, + serf_default_read_for_sendfile, + serf_default_read_bucket, + serf_response_body_peek, + serf_response_body_destroy, +}; diff --git a/buckets/response_buckets.c b/buckets/response_buckets.c index 975fedc..cb6a19c 100644 --- a/buckets/response_buckets.c +++ b/buckets/response_buckets.c @@ -249,8 +249,8 @@ static apr_status_t run_machine(serf_bucket_t *bkt, response_context_t *ctx) if (errno == ERANGE) { return APR_FROM_OS_ERROR(ERANGE); } - ctx->body = serf_bucket_limit_create(ctx->body, length, - bkt->allocator); + ctx->body = serf_bucket_response_body_create( + ctx->body, length, bkt->allocator); } else { v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding"); @@ -385,13 +385,15 @@ static apr_status_t serf_response_read(serf_bucket_t *bucket, } rv = serf_bucket_read(ctx->body, requested, data, len); + if (SERF_BUCKET_READ_ERROR(rv)) + return rv; + if (APR_STATUS_IS_EOF(rv)) { if (ctx->chunked) { ctx->state = STATE_TRAILERS; /* Mask the result. */ rv = APR_SUCCESS; - } - else { + } else { ctx->state = STATE_DONE; } } @@ -414,6 +416,39 @@ static apr_status_t serf_response_readline(serf_bucket_t *bucket, return serf_bucket_readline(ctx->body, acceptable, found, data, len); } +apr_status_t serf_response_full_become_aggregate(serf_bucket_t *bucket) +{ + response_context_t *ctx = bucket->data; + serf_bucket_t *bkt; + char buf[256]; + int size; + + serf_bucket_aggregate_become(bucket); + + /* Add reconstructed status line. */ + size = apr_snprintf(buf, 256, "HTTP/%d.%d %d ", + SERF_HTTP_VERSION_MAJOR(ctx->sl.version), + SERF_HTTP_VERSION_MINOR(ctx->sl.version), + ctx->sl.code); + bkt = serf_bucket_simple_copy_create(buf, size, + bucket->allocator); + serf_bucket_aggregate_append(bucket, bkt); + bkt = serf_bucket_simple_copy_create(ctx->sl.reason, strlen(ctx->sl.reason), + bucket->allocator); + serf_bucket_aggregate_append(bucket, bkt); + bkt = SERF_BUCKET_SIMPLE_STRING_LEN("\r\n", 2, + bucket->allocator); + serf_bucket_aggregate_append(bucket, bkt); + + /* Add headers and stream buckets in order. */ + serf_bucket_aggregate_append(bucket, ctx->headers); + serf_bucket_aggregate_append(bucket, ctx->stream); + + serf_bucket_mem_free(bucket->allocator, ctx); + + return APR_SUCCESS; +} + /* ### need to implement */ #define serf_response_peek NULL diff --git a/buckets/socket_buckets.c b/buckets/socket_buckets.c index dd2469a..ef718af 100644 --- a/buckets/socket_buckets.c +++ b/buckets/socket_buckets.c @@ -17,6 +17,7 @@ #include <apr_network_io.h> #include "serf.h" +#include "serf_private.h" #include "serf_bucket_util.h" @@ -40,6 +41,15 @@ static apr_status_t socket_reader(void *baton, apr_size_t bufsize, *len = bufsize; status = apr_socket_recv(ctx->skt, buf, len); + if (status && !APR_STATUS_IS_EAGAIN(status)) + serf__log_skt(SOCK_VERBOSE, __FILE__, ctx->skt, + "socket_recv error %d\n", status); + + if (*len) + serf__log_skt(SOCK_MSG_VERBOSE, __FILE__, ctx->skt, + "--- socket_recv:\n%.*s\n-(%d)-\n", + *len, buf, *len); + if (ctx->progress_func) ctx->progress_func(ctx->progress_baton, *len, 0); @@ -60,7 +70,8 @@ serf_bucket_t *serf_bucket_socket_create( ctx->databuf.read = socket_reader; ctx->databuf.read_baton = ctx; - ctx->progress_func = ctx->progress_baton = NULL; + ctx->progress_func = NULL; + ctx->progress_baton = NULL; return serf_bucket_create(&serf_bucket_type_socket, allocator, ctx); } diff --git a/buckets/ssl_buckets.c b/buckets/ssl_buckets.c index 3f43543..c5a0e60 100644 --- a/buckets/ssl_buckets.c +++ b/buckets/ssl_buckets.c @@ -43,6 +43,7 @@ #include <apr_atomic.h> #include "serf.h" +#include "serf_private.h" #include "serf_bucket_util.h" #include <openssl/bio.h> @@ -64,8 +65,6 @@ #endif -/*#define SSL_VERBOSE*/ - /* * Here's an overview of the SSL bucket's relationship to OpenSSL and serf. * @@ -180,6 +179,10 @@ struct serf_ssl_context_t { EVP_PKEY *cached_cert_pw; apr_status_t pending_err; + + /* Status of a fatal error, returned on subsequent encrypt or decrypt + requests. */ + apr_status_t fatal_err; }; typedef struct { @@ -198,6 +201,47 @@ struct serf_ssl_certificate_t { int depth; }; +static void disable_compression(serf_ssl_context_t *ssl_ctx); + +#if SSL_VERBOSE +/* Log all ssl alerts that we receive from the server. */ +static void +apps_ssl_info_callback(const SSL *s, int where, int ret) +{ + const char *str; + int w; + w = where & ~SSL_ST_MASK; + + if (w & SSL_ST_CONNECT) + str = "SSL_connect"; + else if (w & SSL_ST_ACCEPT) + str = "SSL_accept"; + else + str = "undefined"; + + if (where & SSL_CB_LOOP) { + serf__log(SSL_VERBOSE, __FILE__, "%s:%s\n", str, + SSL_state_string_long(s)); + } + else if (where & SSL_CB_ALERT) { + str = (where & SSL_CB_READ) ? "read" : "write"; + serf__log(SSL_VERBOSE, __FILE__, "SSL3 alert %s:%s:%s\n", + str, + SSL_alert_type_string_long(ret), + SSL_alert_desc_string_long(ret)); + } + else if (where & SSL_CB_EXIT) { + if (ret == 0) + serf__log(SSL_VERBOSE, __FILE__, "%s:failed in %s\n", str, + SSL_state_string_long(s)); + else if (ret < 0) { + serf__log(SSL_VERBOSE, __FILE__, "%s:error in %s\n", str, + SSL_state_string_long(s)); + } + } +} +#endif + /* Returns the amount read. */ static int bio_bucket_read(BIO *bio, char *in, int inlen) { @@ -206,17 +250,14 @@ static int bio_bucket_read(BIO *bio, char *in, int inlen) apr_status_t status; apr_size_t len; -#ifdef SSL_VERBOSE - printf("bio_bucket_read called for %d bytes\n", inlen); -#endif + serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_read called for %d bytes\n", + inlen); if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN && BIO_should_read(ctx->bio)) { -#ifdef SSL_VERBOSE - printf("bio_bucket_read waiting: (%d %d %d)\n", + serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_read waiting: (%d %d %d)\n", BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), BIO_get_retry_flags(ctx->bio)); -#endif /* Falling back... */ ctx->encrypt.exhausted_reset = 1; BIO_clear_retry_flags(bio); @@ -225,9 +266,9 @@ static int bio_bucket_read(BIO *bio, char *in, int inlen) status = serf_bucket_read(ctx->decrypt.pending, inlen, &data, &len); ctx->decrypt.status = status; -#ifdef SSL_VERBOSE - printf("bio_bucket_read received %d bytes (%d)\n", len, status); -#endif + + serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_read received %d bytes (%d)\n", + len, status); if (!SERF_BUCKET_READ_ERROR(status)) { /* Oh suck. */ @@ -250,16 +291,14 @@ static int bio_bucket_write(BIO *bio, const char *in, int inl) serf_ssl_context_t *ctx = bio->ptr; serf_bucket_t *tmp; -#ifdef SSL_VERBOSE - printf("bio_bucket_write called for %d bytes\n", inl); -#endif + serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_write called for %d bytes\n", + inl); + if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN && !BIO_should_read(ctx->bio)) { -#ifdef SSL_VERBOSE - printf("bio_bucket_write waiting: (%d %d %d)\n", + serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_write waiting: (%d %d %d)\n", BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), BIO_get_retry_flags(ctx->bio)); -#endif /* Falling back... */ ctx->encrypt.exhausted_reset = 1; BIO_clear_retry_flags(bio); @@ -424,6 +463,9 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) case X509_V_ERR_INVALID_CA: failures |= SERF_SSL_CERT_UNKNOWNCA; break; + case X509_V_ERR_CERT_REVOKED: + failures |= SERF_SSL_CERT_REVOKED; + break; default: failures |= SERF_SSL_CERT_UNKNOWN_FAILURE; break; @@ -455,9 +497,13 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) failures, cert); if (status == APR_SUCCESS) cert_valid = 1; - else + else { + /* Even if openssl found the certificate valid, the application + told us to reject it. */ + cert_valid = 0; /* Pass the error back to the caller through the context-run. */ ctx->pending_err = status; + } apr_pool_destroy(subpool); } @@ -490,7 +536,7 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) certs_len = 1; } else { int i; - + certs_len = sk_X509_num(chain); /* Room for all the certs and a trailing NULL. */ @@ -514,6 +560,9 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) if (status == APR_SUCCESS) { cert_valid = 1; } else { + /* Even if openssl found the certificate valid, the application + told us to reject it. */ + cert_valid = 0; /* Pass the error back to the caller through the context-run. */ ctx->pending_err = status; } @@ -534,18 +583,18 @@ static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize, const char *data; int ssl_len; -#ifdef SSL_VERBOSE - printf("ssl_decrypt: begin %d\n", bufsize); -#endif + if (ctx->fatal_err) + return ctx->fatal_err; + + serf__log(SSL_VERBOSE, __FILE__, "ssl_decrypt: begin %d\n", bufsize); /* Is there some data waiting to be read? */ ssl_len = SSL_read(ctx->ssl, buf, bufsize); if (ssl_len > 0) { -#ifdef SSL_VERBOSE - printf("ssl_decrypt: %d bytes (%d); status: %d; flags: %d\n", - ssl_len, bufsize, ctx->decrypt.status, - BIO_get_retry_flags(ctx->bio)); -#endif + serf__log(SSL_VERBOSE, __FILE__, + "ssl_decrypt: %d bytes (%d); status: %d; flags: %d\n", + ssl_len, bufsize, ctx->decrypt.status, + BIO_get_retry_flags(ctx->bio)); *len = ssl_len; return APR_SUCCESS; } @@ -555,10 +604,9 @@ static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize, if (!SERF_BUCKET_READ_ERROR(status) && priv_len) { serf_bucket_t *tmp; -#ifdef SSL_VERBOSE - printf("ssl_decrypt: read %d bytes (%d); status: %d\n", priv_len, - bufsize, status); -#endif + serf__log(SSL_VERBOSE, __FILE__, + "ssl_decrypt: read %d bytes (%d); status: %d\n", + priv_len, bufsize, status); tmp = serf_bucket_simple_copy_create(data, priv_len, ctx->decrypt.pending->allocator); @@ -581,29 +629,55 @@ static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize, break; case SSL_ERROR_SSL: *len = 0; - status = ctx->pending_err ? ctx->pending_err : APR_EGENERAL; - ctx->pending_err = 0; + if (ctx->pending_err) { + status = ctx->pending_err; + ctx->pending_err = 0; + } else { + ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED; + } break; default: *len = 0; - status = APR_EGENERAL; + ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED; break; } - } - else { + } else if (ssl_len == 0) { + /* The server shut down the connection. */ + int ssl_err, shutdown; + *len = 0; + + /* Check for SSL_RECEIVED_SHUTDOWN */ + shutdown = SSL_get_shutdown(ctx->ssl); + /* Check for SSL_ERROR_ZERO_RETURN */ + ssl_err = SSL_get_error(ctx->ssl, ssl_len); + + if (shutdown == SSL_RECEIVED_SHUTDOWN && + ssl_err == SSL_ERROR_ZERO_RETURN) { + /* The server closed the SSL session. While this doesn't + necessary mean the connection is closed, let's close + it here anyway. + We can optimize this later. */ + serf__log(SSL_VERBOSE, __FILE__, + "ssl_decrypt: SSL read error: server" + " shut down connection!\n"); + status = APR_EOF; + } else { + /* A fatal error occurred. */ + ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED; + } + } else { *len = ssl_len; -#ifdef SSL_VERBOSE - printf("---\n%s\n-(%d)-\n", buf, *len); -#endif + serf__log(SSL_MSG_VERBOSE, __FILE__, + "---\n%.*s\n-(%d)-\n", *len, buf, *len); } } else { *len = 0; } -#ifdef SSL_VERBOSE - printf("ssl_decrypt: %d %d %d\n", status, *len, - BIO_get_retry_flags(ctx->bio)); -#endif + serf__log(SSL_VERBOSE, __FILE__, + "ssl_decrypt: %d %d %d\n", status, *len, + BIO_get_retry_flags(ctx->bio)); + return status; } @@ -616,9 +690,10 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize, serf_ssl_context_t *ctx = baton; apr_status_t status; -#ifdef SSL_VERBOSE - printf("ssl_encrypt: begin %d\n", bufsize); -#endif + if (ctx->fatal_err) + return ctx->fatal_err; + + serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: begin %d\n", bufsize); /* Try to read already encrypted but unread data first. */ status = serf_bucket_read(ctx->encrypt.pending, bufsize, &data, len); @@ -632,29 +707,28 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize, if (APR_STATUS_IS_EOF(status)) { status = APR_SUCCESS; } -#ifdef SSL_VERBOSE - printf("ssl_encrypt: %d %d %d (quick read)\n", status, *len, - BIO_get_retry_flags(ctx->bio)); -#endif + + serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: %d %d %d (quick read)\n", + status, *len, BIO_get_retry_flags(ctx->bio)); + return status; } if (BIO_should_retry(ctx->bio) && BIO_should_write(ctx->bio)) { -#ifdef SSL_VERBOSE - printf("ssl_encrypt: %d %d %d (should write exit)\n", status, *len, - BIO_get_retry_flags(ctx->bio)); -#endif + serf__log(SSL_VERBOSE, __FILE__, + "ssl_encrypt: %d %d %d (should write exit)\n", + status, *len, BIO_get_retry_flags(ctx->bio)); + return APR_EAGAIN; } /* If we were previously blocked, unblock ourselves now. */ if (BIO_should_read(ctx->bio)) { -#ifdef SSL_VERBOSE - printf("ssl_encrypt: reset %d %d (%d %d %d)\n", status, - ctx->encrypt.status, - BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), - BIO_get_retry_flags(ctx->bio)); -#endif + serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: reset %d %d (%d %d %d)\n", + status, ctx->encrypt.status, + BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), + BIO_get_retry_flags(ctx->bio)); + ctx->encrypt.status = APR_SUCCESS; ctx->encrypt.exhausted_reset = 0; } @@ -696,18 +770,20 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize, interim_bufsize -= vecs_data_len; interim_len = vecs_data_len; -#ifdef SSL_VERBOSE - printf("ssl_encrypt: bucket read %d bytes; status %d\n", - interim_len, status); - printf("---\n%s\n-(%d)-\n", vecs_data, interim_len); -#endif + serf__log(SSL_VERBOSE, __FILE__, + "ssl_encrypt: bucket read %d bytes; "\ + "status %d\n", interim_len, status); + serf__log(SSL_MSG_VERBOSE, __FILE__, "---\n%.*s\n-(%d)-\n", + interim_len, vecs_data, interim_len); + /* Stash our status away. */ ctx->encrypt.status = status; ssl_len = SSL_write(ctx->ssl, vecs_data, interim_len); -#ifdef SSL_VERBOSE - printf("ssl_encrypt: SSL write: %d\n", ssl_len); -#endif + + serf__log(SSL_VERBOSE, __FILE__, + "ssl_encrypt: SSL write: %d\n", ssl_len); + /* We're done. */ serf_bucket_mem_free(ctx->allocator, vecs_data); @@ -720,9 +796,10 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize, vecs, vecs_read); ssl_err = SSL_get_error(ctx->ssl, ssl_len); -#ifdef SSL_VERBOSE - printf("ssl_encrypt: SSL write error: %d\n", ssl_err); -#endif + + serf__log(SSL_VERBOSE, __FILE__, + "ssl_encrypt: SSL write error: %d\n", ssl_err); + if (ssl_err == SSL_ERROR_SYSCALL) { status = ctx->encrypt.status; if (SERF_BUCKET_READ_ERROR(status)) { @@ -735,12 +812,13 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize, status = SERF_ERROR_WAIT_CONN; } else { - status = APR_EGENERAL; + ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED; } } -#ifdef SSL_VERBOSE - printf("ssl_encrypt: SSL write error: %d %d\n", status, *len); -#endif + + serf__log(SSL_VERBOSE, __FILE__, + "ssl_encrypt: SSL write error: %d %d\n", + status, *len); } } } @@ -767,10 +845,9 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize, *len += vecs[i].iov_len; } -#ifdef SSL_VERBOSE - printf("ssl_encrypt read agg: %d %d %d %d\n", status, agg_status, - ctx->encrypt.status, *len); -#endif + serf__log(SSL_VERBOSE, __FILE__, + "ssl_encrypt read agg: %d %d %d %d\n", status, agg_status, + ctx->encrypt.status, *len); if (!agg_status) { status = agg_status; @@ -783,11 +860,11 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize, ctx->encrypt.status = SERF_ERROR_WAIT_CONN; } -#ifdef SSL_VERBOSE - printf("ssl_encrypt finished: %d %d (%d %d %d)\n", status, *len, - BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), - BIO_get_retry_flags(ctx->bio)); -#endif + serf__log(SSL_VERBOSE, __FILE__, + "ssl_encrypt finished: %d %d (%d %d %d)\n", status, *len, + BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), + BIO_get_retry_flags(ctx->bio)); + return status; } @@ -873,6 +950,20 @@ static void init_ssl_libraries(void) #if APR_HAS_THREADS int i, numlocks; #endif + +#ifdef SSL_VERBOSE + /* Warn when compile-time and run-time version of OpenSSL differ in + major/minor version number. */ + long libver = SSLeay(); + + if ((libver ^ OPENSSL_VERSION_NUMBER) & 0xFFF00000) { + serf__log(SSL_VERBOSE, __FILE__, + "Warning: OpenSSL library version mismatch, compile-time " + "was %lx, runtime is %lx.\n", + OPENSSL_VERSION_NUMBER, libver); + } +#endif + CRYPTO_malloc_init(); ERR_load_crypto_strings(); SSL_load_error_strings(); @@ -1107,6 +1198,7 @@ static serf_ssl_context_t *ssl_init_context(void) ssl_ctx->cached_cert = 0; ssl_ctx->cached_cert_pw = 0; ssl_ctx->pending_err = APR_SUCCESS; + ssl_ctx->fatal_err = APR_SUCCESS; ssl_ctx->cert_callback = NULL; ssl_ctx->cert_pw_callback = NULL; @@ -1116,6 +1208,8 @@ static serf_ssl_context_t *ssl_init_context(void) SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER, validate_server_certificate); SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_ALL); + /* Disable SSL compression by default. */ + disable_compression(ssl_ctx); ssl_ctx->ssl = SSL_new(ssl_ctx->ctx); ssl_ctx->bio = BIO_new(&bio_bucket_method); @@ -1127,6 +1221,10 @@ static serf_ssl_context_t *ssl_init_context(void) SSL_set_app_data(ssl_ctx->ssl, ssl_ctx); +#if SSL_VERBOSE + SSL_CTX_set_info_callback(ssl_ctx->ctx, apps_ssl_info_callback); +#endif + ssl_ctx->encrypt.stream = NULL; ssl_ctx->encrypt.stream_next = NULL; ssl_ctx->encrypt.pending = serf_bucket_aggregate_create(allocator); @@ -1206,7 +1304,7 @@ apr_status_t serf_ssl_use_default_certificates(serf_ssl_context_t *ssl_ctx) int result = X509_STORE_set_default_paths(store); - return result ? APR_SUCCESS : APR_EGENERAL; + return result ? APR_SUCCESS : SERF_ERROR_SSL_CERT_FAILED; } apr_status_t serf_ssl_load_cert_file( @@ -1228,7 +1326,7 @@ apr_status_t serf_ssl_load_cert_file( } } - return APR_EGENERAL; + return SERF_ERROR_SSL_CERT_FAILED; } @@ -1240,7 +1338,7 @@ apr_status_t serf_ssl_trust_cert( int result = X509_STORE_add_cert(store, cert->ssl_cert); - return result ? APR_SUCCESS : APR_EGENERAL; + return result ? APR_SUCCESS : SERF_ERROR_SSL_CERT_FAILED; } @@ -1521,6 +1619,31 @@ const char *serf_ssl_cert_export( return encoded_cert; } +/* Disables compression for all SSL sessions. */ +static void disable_compression(serf_ssl_context_t *ssl_ctx) +{ +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_NO_COMPRESSION); +#endif +} + +apr_status_t serf_ssl_use_compression(serf_ssl_context_t *ssl_ctx, int enabled) +{ + if (enabled) { +#ifdef SSL_OP_NO_COMPRESSION + SSL_clear_options(ssl_ctx->ssl, SSL_OP_NO_COMPRESSION); + return APR_SUCCESS; +#endif + } else { +#ifdef SSL_OP_NO_COMPRESSION + SSL_set_options(ssl_ctx->ssl, SSL_OP_NO_COMPRESSION); + return APR_SUCCESS; +#endif + } + + return APR_EGENERAL; +} + static void serf_ssl_destroy_and_data(serf_bucket_t *bucket) { ssl_context_t *ctx = bucket->data; |