summaryrefslogtreecommitdiff
path: root/buckets
diff options
context:
space:
mode:
Diffstat (limited to 'buckets')
-rw-r--r--buckets/allocator.c14
-rw-r--r--buckets/buckets.c84
-rw-r--r--buckets/dechunk_buckets.c10
-rw-r--r--buckets/headers_buckets.c6
-rw-r--r--buckets/mmap_buckets.c22
-rw-r--r--buckets/response_body_buckets.c135
-rw-r--r--buckets/response_buckets.c43
-rw-r--r--buckets/socket_buckets.c13
-rw-r--r--buckets/ssl_buckets.c297
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;