#include "first.h" #include "network_backends.h" #if defined(USE_OPENSSL) #include "network.h" #include "log.h" #include #include #include #include # include # include 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 */