lighttpd 1.4.x
https://www.lighttpd.net/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
4.6 KiB
187 lines
4.6 KiB
#include "first.h" |
|
|
|
#include "network_backends.h" |
|
|
|
#if defined(USE_OPENSSL) |
|
|
|
#include "network.h" |
|
#include "log.h" |
|
|
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
|
|
#include <errno.h> |
|
#include <string.h> |
|
|
|
# include <openssl/ssl.h> |
|
# include <openssl/err.h> |
|
|
|
static int load_next_chunk(server *srv, connection *con, chunkqueue *cq, off_t max_bytes, const char **data, size_t *data_len) { |
|
chunk * const c = cq->first; |
|
|
|
#define LOCAL_SEND_BUFSIZE (64 * 1024) |
|
/* this is a 64k sendbuffer |
|
* |
|
* it has to stay at the same location all the time to satisfy the needs |
|
* of SSL_write to pass the SAME parameter in case of a _WANT_WRITE |
|
* |
|
* the buffer is allocated once, is NOT realloced and is NOT freed at shutdown |
|
* -> we expect a 64k block to 'leak' in valgrind |
|
* */ |
|
static char *local_send_buffer = NULL; |
|
|
|
force_assert(NULL != c); |
|
|
|
switch (c->type) { |
|
case MEM_CHUNK: |
|
{ |
|
size_t have; |
|
|
|
force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem)); |
|
|
|
have = buffer_string_length(c->mem) - c->offset; |
|
if ((off_t) have > max_bytes) have = max_bytes; |
|
|
|
*data = c->mem->ptr + c->offset; |
|
*data_len = have; |
|
} |
|
return 0; |
|
|
|
case FILE_CHUNK: |
|
if (NULL == local_send_buffer) { |
|
local_send_buffer = malloc(LOCAL_SEND_BUFSIZE); |
|
force_assert(NULL != local_send_buffer); |
|
} |
|
|
|
if (0 != network_open_file_chunk(srv, con, cq)) return -1; |
|
|
|
{ |
|
off_t offset, toSend; |
|
|
|
force_assert(c->offset >= 0 && c->offset <= c->file.length); |
|
offset = c->file.start + c->offset; |
|
toSend = c->file.length - c->offset; |
|
|
|
if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE; |
|
if (toSend > max_bytes) toSend = max_bytes; |
|
|
|
if (-1 == lseek(c->file.fd, offset, SEEK_SET)) { |
|
log_error_write(srv, __FILE__, __LINE__, "ss", "lseek: ", strerror(errno)); |
|
return -1; |
|
} |
|
if (-1 == (toSend = read(c->file.fd, local_send_buffer, toSend))) { |
|
log_error_write(srv, __FILE__, __LINE__, "ss", "read: ", strerror(errno)); |
|
return -1; |
|
} |
|
|
|
*data = local_send_buffer; |
|
*data_len = toSend; |
|
} |
|
return 0; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
|
|
int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes) { |
|
/* the remote side closed the connection before without shutdown request |
|
* - IE |
|
* - wget |
|
* if keep-alive is disabled */ |
|
|
|
if (con->keep_alive == 0) { |
|
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); |
|
} |
|
|
|
chunkqueue_remove_finished_chunks(cq); |
|
|
|
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; |
|
|
|
/** |
|
* 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, data, data_len); |
|
|
|
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; |
|
} |
|
|
|
if (r <= 0) { |
|
int ssl_r; |
|
unsigned long err; |
|
|
|
switch ((ssl_r = SSL_get_error(ssl, r))) { |
|
case SSL_ERROR_WANT_READ: |
|
con->is_readable = -1; |
|
return 0; /* try again later */ |
|
case SSL_ERROR_WANT_WRITE: |
|
con->is_writable = -1; |
|
return 0; /* try again later */ |
|
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)); |
|
} |
|
break; |
|
|
|
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)); |
|
} |
|
break; |
|
} |
|
return -1; |
|
} |
|
|
|
chunkqueue_mark_written(cq, r); |
|
max_bytes -= r; |
|
|
|
if ((size_t) r < data_len) break; /* try again later */ |
|
} |
|
|
|
return 0; |
|
} |
|
#endif /* USE_OPENSSL */
|
|
|