version 1.1.1.2, 2014/06/15 20:20:06
|
version 1.1.1.3, 2016/11/02 10:35:00
|
Line 1
|
Line 1
|
|
#include "first.h" |
|
|
#include "network_backends.h" |
#include "network_backends.h" |
|
|
#ifdef USE_OPENSSL | #if defined(USE_OPENSSL) |
|
|
#include "network.h" |
#include "network.h" |
#include "fdevent.h" |
|
#include "log.h" |
#include "log.h" |
#include "stat_cache.h" |
|
|
|
#include <sys/types.h> | #include <unistd.h> |
#include <sys/socket.h> | #include <stdlib.h> |
#include <sys/stat.h> | |
#include <sys/time.h> | |
#include <sys/resource.h> | |
|
|
#include <netinet/in.h> |
|
#include <netinet/tcp.h> |
|
|
|
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <netdb.h> |
|
#include <string.h> |
#include <string.h> |
#include <stdlib.h> |
|
#include <assert.h> |
|
|
|
# include <openssl/ssl.h> |
# include <openssl/ssl.h> |
# include <openssl/err.h> |
# include <openssl/err.h> |
|
|
int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes) { | static int load_next_chunk(server *srv, connection *con, chunkqueue *cq, off_t max_bytes, const char **data, size_t *data_len) { |
int ssl_r; | chunk * const c = cq->first; |
chunk *c; | |
|
|
|
#define LOCAL_SEND_BUFSIZE (64 * 1024) |
/* this is a 64k sendbuffer |
/* this is a 64k sendbuffer |
* |
* |
* it has to stay at the same location all the time to satisfy the needs |
* it has to stay at the same location all the time to satisfy the needs |
Line 38 int network_write_chunkqueue_openssl(server *srv, conn
|
Line 27 int network_write_chunkqueue_openssl(server *srv, conn
|
* |
* |
* the buffer is allocated once, is NOT realloced and is NOT freed at shutdown |
* the buffer is allocated once, is NOT realloced and is NOT freed at shutdown |
* -> we expect a 64k block to 'leak' in valgrind |
* -> we expect a 64k block to 'leak' in valgrind |
* |
|
* |
|
* In reality we would like to use mmap() but we don't have a guarantee that |
|
* we get the same mmap() address for each call. On openbsd the mmap() address |
|
* even randomized. |
|
* That means either we keep the mmap() open or we do a read() into a |
|
* constant buffer |
|
* */ |
* */ |
#define LOCAL_SEND_BUFSIZE (64 * 1024) |
|
static char *local_send_buffer = NULL; |
static char *local_send_buffer = NULL; |
|
|
/* the remote side closed the connection before without shutdown request | force_assert(NULL != c); |
* - IE | |
* - wget | |
* if keep-alive is disabled */ | |
|
|
if (con->keep_alive == 0) { | switch (c->type) { |
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); | case MEM_CHUNK: |
} | { |
| size_t have; |
|
|
for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { | force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem)); |
int chunk_finished = 0; | |
|
|
switch(c->type) { | have = buffer_string_length(c->mem) - c->offset; |
case MEM_CHUNK: { | if ((off_t) have > max_bytes) have = max_bytes; |
char * offset; | |
off_t toSend; | |
ssize_t r; | |
|
|
if (c->mem->used == 0 || c->mem->used == 1) { | *data = c->mem->ptr + c->offset; |
chunk_finished = 1; | *data_len = have; |
break; | } |
} | return 0; |
|
|
offset = c->mem->ptr + c->offset; | case FILE_CHUNK: |
toSend = c->mem->used - 1 - c->offset; | if (NULL == local_send_buffer) { |
if (toSend > max_bytes) toSend = max_bytes; | local_send_buffer = malloc(LOCAL_SEND_BUFSIZE); |
| force_assert(NULL != local_send_buffer); |
| } |
|
|
/** | if (0 != network_open_file_chunk(srv, con, cq)) return -1; |
* SSL_write man-page | |
* | |
* WARNING | |
* When an SSL_write() operation has to be repeated because of | |
* SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be | |
* repeated with the same arguments. | |
* | |
*/ | |
|
|
ERR_clear_error(); | { |
r = SSL_write(ssl, offset, toSend); | off_t offset, toSend; |
|
|
if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { | force_assert(c->offset >= 0 && c->offset <= c->file.length); |
log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection"); | offset = c->file.start + c->offset; |
return -1; | toSend = c->file.length - c->offset; |
} | |
|
|
if (r <= 0) { | if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE; |
unsigned long err; | if (toSend > max_bytes) toSend = max_bytes; |
|
|
switch ((ssl_r = SSL_get_error(ssl, r))) { | if (-1 == lseek(c->file.fd, offset, SEEK_SET)) { |
case SSL_ERROR_WANT_WRITE: | log_error_write(srv, __FILE__, __LINE__, "ss", "lseek: ", strerror(errno)); |
break; | return -1; |
case SSL_ERROR_SYSCALL: | |
/* perhaps we have error waiting in our error-queue */ | |
if (0 != (err = ERR_get_error())) { | |
do { | |
log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", | |
ssl_r, r, | |
ERR_error_string(err, NULL)); | |
} while((err = ERR_get_error())); | |
} else if (r == -1) { | |
/* no, but we have errno */ | |
switch(errno) { | |
case EPIPE: | |
case ECONNRESET: | |
return -2; | |
default: | |
log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", | |
ssl_r, r, errno, | |
strerror(errno)); | |
break; | |
} | |
} else { | |
/* neither error-queue nor errno ? */ | |
log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", | |
ssl_r, r, errno, | |
strerror(errno)); | |
} | |
| |
return -1; | |
case SSL_ERROR_ZERO_RETURN: | |
/* clean shutdown on the remote side */ | |
| |
if (r == 0) return -2; | |
| |
/* fall through */ | |
default: | |
while((err = ERR_get_error())) { | |
log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", | |
ssl_r, r, | |
ERR_error_string(err, NULL)); | |
} | |
| |
return -1; | |
} | |
} else { | |
c->offset += r; | |
cq->bytes_out += r; | |
max_bytes -= r; | |
} |
} |
| if (-1 == (toSend = read(c->file.fd, local_send_buffer, toSend))) { |
if (c->offset == (off_t)c->mem->used - 1) { | log_error_write(srv, __FILE__, __LINE__, "ss", "read: ", strerror(errno)); |
chunk_finished = 1; | return -1; |
} |
} |
|
|
break; | *data = local_send_buffer; |
| *data_len = toSend; |
} |
} |
case FILE_CHUNK: { | return 0; |
char *s; | } |
ssize_t r; | |
stat_cache_entry *sce = NULL; | |
int ifd; | |
int write_wait = 0; | |
|
|
if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { | return -1; |
log_error_write(srv, __FILE__, __LINE__, "sb", | } |
strerror(errno), c->file.name); | |
return -1; | |
} | |
|
|
if (NULL == local_send_buffer) { |
|
local_send_buffer = malloc(LOCAL_SEND_BUFSIZE); |
|
force_assert(local_send_buffer); |
|
} |
|
|
|
do { | int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes) { |
off_t offset = c->file.start + c->offset; | /* the remote side closed the connection before without shutdown request |
off_t toSend = c->file.length - c->offset; | * - IE |
if (toSend > max_bytes) toSend = max_bytes; | * - wget |
| * if keep-alive is disabled */ |
|
|
if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE; | if (con->keep_alive == 0) { |
| SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); |
| } |
|
|
if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { | chunkqueue_remove_finished_chunks(cq); |
log_error_write(srv, __FILE__, __LINE__, "ss", "open failed:", strerror(errno)); | |
|
|
return -1; | while (max_bytes > 0 && NULL != cq->first) { |
} | const char *data; |
| size_t data_len; |
| int r; |
|
|
|
if (0 != load_next_chunk(srv, con, cq, max_bytes, &data, &data_len)) return -1; |
|
|
if (-1 == lseek(ifd, offset, SEEK_SET)) { | /** |
log_error_write(srv, __FILE__, __LINE__, "ss", "lseek failed:", strerror(errno)); | * SSL_write man-page |
close(ifd); | * |
return -1; | * WARNING |
} | * When an SSL_write() operation has to be repeated because of |
if (-1 == (toSend = read(ifd, local_send_buffer, toSend))) { | * SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be |
log_error_write(srv, __FILE__, __LINE__, "ss", "read failed:", strerror(errno)); | * repeated with the same arguments. |
close(ifd); | */ |
return -1; | |
} | |
|
|
s = local_send_buffer; | ERR_clear_error(); |
| r = SSL_write(ssl, data, data_len); |
|
|
close(ifd); | if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { |
| log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection"); |
| return -1; |
| } |
|
|
ERR_clear_error(); | if (r <= 0) { |
r = SSL_write(ssl, s, toSend); | int ssl_r; |
| unsigned long err; |
|
|
if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { | switch ((ssl_r = SSL_get_error(ssl, r))) { |
log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection"); | case SSL_ERROR_WANT_READ: |
return -1; | con->is_readable = -1; |
} | return 0; /* try again later */ |
| case SSL_ERROR_WANT_WRITE: |
if (r <= 0) { | con->is_writable = -1; |
unsigned long err; | return 0; /* try again later */ |
| case SSL_ERROR_SYSCALL: |
switch ((ssl_r = SSL_get_error(ssl, r))) { | /* perhaps we have error waiting in our error-queue */ |
case SSL_ERROR_WANT_WRITE: | if (0 != (err = ERR_get_error())) { |
write_wait = 1; | do { |
break; | log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", |
case SSL_ERROR_SYSCALL: | ssl_r, r, |
/* perhaps we have error waiting in our error-queue */ | ERR_error_string(err, NULL)); |
if (0 != (err = ERR_get_error())) { | } while((err = ERR_get_error())); |
do { | } else if (r == -1) { |
log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", | /* no, but we have errno */ |
ssl_r, r, | switch(errno) { |
ERR_error_string(err, NULL)); | case EPIPE: |
} while((err = ERR_get_error())); | case ECONNRESET: |
} else if (r == -1) { | return -2; |
/* no, but we have errno */ | |
switch(errno) { | |
case EPIPE: | |
case ECONNRESET: | |
return -2; | |
default: | |
log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", | |
ssl_r, r, errno, | |
strerror(errno)); | |
break; | |
} | |
} else { | |
/* neither error-queue nor errno ? */ | |
log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", | |
ssl_r, r, errno, | |
strerror(errno)); | |
} | |
| |
return -1; | |
case SSL_ERROR_ZERO_RETURN: | |
/* clean shutdown on the remote side */ | |
| |
if (r == 0) return -2; | |
| |
/* fall thourgh */ | |
default: |
default: |
while((err = ERR_get_error())) { | log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", |
log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", | ssl_r, r, errno, |
ssl_r, r, | strerror(errno)); |
ERR_error_string(err, NULL)); | break; |
} | |
| |
return -1; | |
} |
} |
} else { |
} else { |
c->offset += r; | /* neither error-queue nor errno ? */ |
cq->bytes_out += r; | log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):", |
max_bytes -= r; | ssl_r, r, errno, |
| strerror(errno)); |
} |
} |
|
break; |
|
|
if (c->offset == c->file.length) { | case SSL_ERROR_ZERO_RETURN: |
chunk_finished = 1; | /* clean shutdown on the remote side */ |
} | |
} while (!chunk_finished && !write_wait && max_bytes > 0); | |
|
|
break; | if (r == 0) return -2; |
} | |
default: | |
log_error_write(srv, __FILE__, __LINE__, "s", "type not known"); | |
|
|
|
/* fall through */ |
|
default: |
|
while((err = ERR_get_error())) { |
|
log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:", |
|
ssl_r, r, |
|
ERR_error_string(err, NULL)); |
|
} |
|
break; |
|
} |
return -1; |
return -1; |
} |
} |
|
|
if (!chunk_finished) { | chunkqueue_mark_written(cq, r); |
/* not finished yet */ | max_bytes -= r; |
|
|
break; | if ((size_t) r < data_len) break; /* try again later */ |
} | |
} |
} |
|
|
return 0; |
return 0; |
} |
} |
#endif | #endif /* USE_OPENSSL */ |
| |
#if 0 | |
network_openssl_init(void) { | |
p->write_ssl = network_openssl_write_chunkset; | |
} | |
#endif | |