diff --git a/src/connections-glue.c b/src/connections-glue.c index 0263183c..bc67b9fd 100644 --- a/src/connections-glue.c +++ b/src/connections-glue.c @@ -2,6 +2,13 @@ #include "base.h" #include "connections.h" +#include "joblist.h" +#include "log.h" + +#ifdef USE_OPENSSL +# include +# include +#endif const char *connection_get_state(connection_state_t state) { switch (state) { @@ -45,3 +52,318 @@ int connection_set_state(server *srv, connection *con, connection_state_t state) return 0; } +#if 0 +static void dump_packet(const unsigned char *data, size_t len) { + size_t i, j; + + if (len == 0) return; + + for (i = 0; i < len; i++) { + if (i % 16 == 0) fprintf(stderr, " "); + + fprintf(stderr, "%02x ", data[i]); + + if ((i + 1) % 16 == 0) { + fprintf(stderr, " "); + for (j = 0; j <= i % 16; j++) { + unsigned char c; + + if (i-15+j >= len) break; + + c = data[i-15+j]; + + fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); + } + + fprintf(stderr, "\n"); + } + } + + if (len % 16 != 0) { + for (j = i % 16; j < 16; j++) { + fprintf(stderr, " "); + } + + fprintf(stderr, " "); + for (j = i & ~0xf; j < len; j++) { + unsigned char c; + + c = data[j]; + fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); + } + fprintf(stderr, "\n"); + } +} +#endif + +static int connection_handle_read_ssl(server *srv, connection *con) { +#ifdef USE_OPENSSL + int r, ssl_err, len, count = 0; + char *mem = NULL; + size_t mem_len = 0; + + if (!con->srv_socket->is_ssl) return -1; + + ERR_clear_error(); + do { + chunkqueue_get_memory(con->read_queue, &mem, &mem_len, 0, SSL_pending(con->ssl)); +#if 0 + /* overwrite everything with 0 */ + memset(mem, 0, mem_len); +#endif + + len = SSL_read(con->ssl, mem, mem_len); + chunkqueue_use_memory(con->read_queue, len > 0 ? len : 0); + + if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { + log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection"); + connection_set_state(srv, con, CON_STATE_ERROR); + return -1; + } + + if (len > 0) { + con->bytes_read += len; + count += len; + } + } while (len == (ssize_t) mem_len && count < MAX_READ_LIMIT); + + + if (len < 0) { + int oerrno = errno; + switch ((r = SSL_get_error(con->ssl, len))) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + con->is_readable = 0; + + /* the manual says we have to call SSL_read with the same arguments next time. + * we ignore this restriction; no one has complained about it in 1.5 yet, so it probably works anyway. + */ + + return 0; + case SSL_ERROR_SYSCALL: + /** + * man SSL_get_error() + * + * SSL_ERROR_SYSCALL + * Some I/O error occurred. The OpenSSL error queue may contain more + * information on the error. If the error queue is empty (i.e. + * ERR_get_error() returns 0), ret can be used to find out more about + * the error: If ret == 0, an EOF was observed that violates the + * protocol. If ret == -1, the underlying BIO reported an I/O error + * (for socket I/O on Unix systems, consult errno for details). + * + */ + while((ssl_err = ERR_get_error())) { + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); + } + + switch(oerrno) { + default: + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", + len, r, oerrno, + strerror(oerrno)); + break; + } + + break; + case SSL_ERROR_ZERO_RETURN: + /* clean shutdown on the remote side */ + + if (r == 0) { + /* FIXME: later */ + } + + /* fall thourgh */ + default: + while((ssl_err = ERR_get_error())) { + switch (ERR_GET_REASON(ssl_err)) { + case SSL_R_SSL_HANDSHAKE_FAILURE: + case SSL_R_TLSV1_ALERT_UNKNOWN_CA: + case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN: + case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE: + if (!con->conf.log_ssl_noise) continue; + break; + default: + break; + } + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); + } + break; + } + + connection_set_state(srv, con, CON_STATE_ERROR); + + return -1; + } else if (len == 0) { + con->is_readable = 0; + /* the other end close the connection -> KEEP-ALIVE */ + + return -2; + } else { + joblist_append(srv, con); + } + + return 0; +#else + UNUSED(srv); + UNUSED(con); + return -1; +#endif +} + +/* 0: everything ok, -1: error, -2: con closed */ +int connection_handle_read(server *srv, connection *con) { + int len; + char *mem = NULL; + size_t mem_len = 0; + int toread; + + if (con->srv_socket->is_ssl) { + return connection_handle_read_ssl(srv, con); + } + + /* default size for chunks is 4kb; only use bigger chunks if FIONREAD tells + * us more than 4kb is available + * if FIONREAD doesn't signal a big chunk we fill the previous buffer + * if it has >= 1kb free + */ +#if defined(__WIN32) + chunkqueue_get_memory(con->read_queue, &mem, &mem_len, 0, 4096); + + len = recv(con->fd, mem, mem_len, 0); +#else /* __WIN32 */ + if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { + toread = 4096; + } + else if (toread > MAX_READ_LIMIT) { + toread = MAX_READ_LIMIT; + } + chunkqueue_get_memory(con->read_queue, &mem, &mem_len, 0, toread); + + len = read(con->fd, mem, mem_len); +#endif /* __WIN32 */ + + chunkqueue_use_memory(con->read_queue, len > 0 ? len : 0); + + if (len < 0) { + con->is_readable = 0; + +#if defined(__WIN32) + { + int lastError = WSAGetLastError(); + switch (lastError) { + case EAGAIN: + return 0; + case EINTR: + /* we have been interrupted before we could read */ + con->is_readable = 1; + return 0; + case ECONNRESET: + /* suppress logging for this error, expected for keep-alive */ + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - recv failed: ", lastError); + break; + } + } +#else /* __WIN32 */ + switch (errno) { + case EAGAIN: + return 0; + case EINTR: + /* we have been interrupted before we could read */ + con->is_readable = 1; + return 0; + case ECONNRESET: + /* suppress logging for this error, expected for keep-alive */ + break; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); + break; + } +#endif /* __WIN32 */ + + connection_set_state(srv, con, CON_STATE_ERROR); + + return -1; + } else if (len == 0) { + con->is_readable = 0; + /* the other end close the connection -> KEEP-ALIVE */ + + /* pipelining */ + + return -2; + } else if (len != (ssize_t) mem_len) { + /* we got less then expected, wait for the next fd-event */ + + con->is_readable = 0; + } + + con->bytes_read += len; +#if 0 + dump_packet(b->ptr, len); +#endif + + return 0; +} + +handler_t connection_handle_read_post_state(server *srv, connection *con) { + chunkqueue *cq = con->read_queue; + chunkqueue *dst_cq = con->request_content_queue; + + int is_closed = 0; + + if (con->is_readable) { + con->read_idle_ts = srv->cur_ts; + + switch(connection_handle_read(srv, con)) { + case -1: + return HANDLER_ERROR; + case -2: + is_closed = 1; + break; + default: + break; + } + } + + chunkqueue_remove_finished_chunks(cq); + + if (con->request.content_length <= 64*1024) { + /* don't buffer request bodies <= 64k on disk */ + chunkqueue_steal(dst_cq, cq, (off_t)con->request.content_length - dst_cq->bytes_in); + } + else if (0 != chunkqueue_steal_with_tempfiles(srv, dst_cq, cq, (off_t)con->request.content_length - dst_cq->bytes_in)) { + /* writing to temp file failed */ + con->http_status = 500; /* Internal Server Error */ + con->keep_alive = 0; + con->mode = DIRECT; + chunkqueue_reset(con->write_queue); + + return HANDLER_FINISHED; + } + + chunkqueue_remove_finished_chunks(cq); + + if (dst_cq->bytes_in == (off_t)con->request.content_length) { + /* Content is ready */ + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + return HANDLER_GO_ON; + } else if (is_closed) { + #if 0 + con->http_status = 400; /* Bad Request */ + con->keep_alive = 0; + con->mode = DIRECT; + chunkqueue_reset(con->write_queue); + + return HANDLER_FINISHED; + #endif + return HANDLER_ERROR; + } else { + return HANDLER_WAIT_FOR_EVENT; + } +} diff --git a/src/connections.c b/src/connections.c index 7d777eec..4c860b56 100644 --- a/src/connections.c +++ b/src/connections.c @@ -156,322 +156,6 @@ static int connection_close(server *srv, connection *con) { return 0; } -#if 0 -static void dump_packet(const unsigned char *data, size_t len) { - size_t i, j; - - if (len == 0) return; - - for (i = 0; i < len; i++) { - if (i % 16 == 0) fprintf(stderr, " "); - - fprintf(stderr, "%02x ", data[i]); - - if ((i + 1) % 16 == 0) { - fprintf(stderr, " "); - for (j = 0; j <= i % 16; j++) { - unsigned char c; - - if (i-15+j >= len) break; - - c = data[i-15+j]; - - fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); - } - - fprintf(stderr, "\n"); - } - } - - if (len % 16 != 0) { - for (j = i % 16; j < 16; j++) { - fprintf(stderr, " "); - } - - fprintf(stderr, " "); - for (j = i & ~0xf; j < len; j++) { - unsigned char c; - - c = data[j]; - fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); - } - fprintf(stderr, "\n"); - } -} -#endif - -static int connection_handle_read_ssl(server *srv, connection *con) { -#ifdef USE_OPENSSL - int r, ssl_err, len, count = 0; - char *mem = NULL; - size_t mem_len = 0; - - if (!con->srv_socket->is_ssl) return -1; - - ERR_clear_error(); - do { - chunkqueue_get_memory(con->read_queue, &mem, &mem_len, 0, SSL_pending(con->ssl)); -#if 0 - /* overwrite everything with 0 */ - memset(mem, 0, mem_len); -#endif - - len = SSL_read(con->ssl, mem, mem_len); - chunkqueue_use_memory(con->read_queue, len > 0 ? len : 0); - - if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) { - log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection"); - connection_set_state(srv, con, CON_STATE_ERROR); - return -1; - } - - if (len > 0) { - con->bytes_read += len; - count += len; - } - } while (len == (ssize_t) mem_len && count < MAX_READ_LIMIT); - - - if (len < 0) { - int oerrno = errno; - switch ((r = SSL_get_error(con->ssl, len))) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - con->is_readable = 0; - - /* the manual says we have to call SSL_read with the same arguments next time. - * we ignore this restriction; no one has complained about it in 1.5 yet, so it probably works anyway. - */ - - return 0; - case SSL_ERROR_SYSCALL: - /** - * man SSL_get_error() - * - * SSL_ERROR_SYSCALL - * Some I/O error occurred. The OpenSSL error queue may contain more - * information on the error. If the error queue is empty (i.e. - * ERR_get_error() returns 0), ret can be used to find out more about - * the error: If ret == 0, an EOF was observed that violates the - * protocol. If ret == -1, the underlying BIO reported an I/O error - * (for socket I/O on Unix systems, consult errno for details). - * - */ - while((ssl_err = ERR_get_error())) { - /* get all errors from the error-queue */ - log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", - r, ERR_error_string(ssl_err, NULL)); - } - - switch(oerrno) { - default: - log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", - len, r, oerrno, - strerror(oerrno)); - break; - } - - break; - case SSL_ERROR_ZERO_RETURN: - /* clean shutdown on the remote side */ - - if (r == 0) { - /* FIXME: later */ - } - - /* fall thourgh */ - default: - while((ssl_err = ERR_get_error())) { - switch (ERR_GET_REASON(ssl_err)) { - case SSL_R_SSL_HANDSHAKE_FAILURE: - case SSL_R_TLSV1_ALERT_UNKNOWN_CA: - case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN: - case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE: - if (!con->conf.log_ssl_noise) continue; - break; - default: - break; - } - /* get all errors from the error-queue */ - log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", - r, ERR_error_string(ssl_err, NULL)); - } - break; - } - - connection_set_state(srv, con, CON_STATE_ERROR); - - return -1; - } else if (len == 0) { - con->is_readable = 0; - /* the other end close the connection -> KEEP-ALIVE */ - - return -2; - } else { - joblist_append(srv, con); - } - - return 0; -#else - UNUSED(srv); - UNUSED(con); - return -1; -#endif -} - -/* 0: everything ok, -1: error, -2: con closed */ -static int connection_handle_read(server *srv, connection *con) { - int len; - char *mem = NULL; - size_t mem_len = 0; - int toread; - - if (con->srv_socket->is_ssl) { - return connection_handle_read_ssl(srv, con); - } - - /* default size for chunks is 4kb; only use bigger chunks if FIONREAD tells - * us more than 4kb is available - * if FIONREAD doesn't signal a big chunk we fill the previous buffer - * if it has >= 1kb free - */ -#if defined(__WIN32) - chunkqueue_get_memory(con->read_queue, &mem, &mem_len, 0, 4096); - - len = recv(con->fd, mem, mem_len, 0); -#else /* __WIN32 */ - if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { - toread = 4096; - } - else if (toread > MAX_READ_LIMIT) { - toread = MAX_READ_LIMIT; - } - chunkqueue_get_memory(con->read_queue, &mem, &mem_len, 0, toread); - - len = read(con->fd, mem, mem_len); -#endif /* __WIN32 */ - - chunkqueue_use_memory(con->read_queue, len > 0 ? len : 0); - - if (len < 0) { - con->is_readable = 0; - -#if defined(__WIN32) - { - int lastError = WSAGetLastError(); - switch (lastError) { - case EAGAIN: - return 0; - case EINTR: - /* we have been interrupted before we could read */ - con->is_readable = 1; - return 0; - case ECONNRESET: - /* suppress logging for this error, expected for keep-alive */ - break; - default: - log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - recv failed: ", lastError); - break; - } - } -#else /* __WIN32 */ - switch (errno) { - case EAGAIN: - return 0; - case EINTR: - /* we have been interrupted before we could read */ - con->is_readable = 1; - return 0; - case ECONNRESET: - /* suppress logging for this error, expected for keep-alive */ - break; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); - break; - } -#endif /* __WIN32 */ - - connection_set_state(srv, con, CON_STATE_ERROR); - - return -1; - } else if (len == 0) { - con->is_readable = 0; - /* the other end close the connection -> KEEP-ALIVE */ - - /* pipelining */ - - return -2; - } else if (len != (ssize_t) mem_len) { - /* we got less then expected, wait for the next fd-event */ - - con->is_readable = 0; - } - - con->bytes_read += len; -#if 0 - dump_packet(b->ptr, len); -#endif - - return 0; -} - -handler_t connection_handle_read_post_state(server *srv, connection *con) { - chunkqueue *cq = con->read_queue; - chunkqueue *dst_cq = con->request_content_queue; - - int is_closed = 0; - - if (con->is_readable) { - con->read_idle_ts = srv->cur_ts; - - switch(connection_handle_read(srv, con)) { - case -1: - return HANDLER_ERROR; - case -2: - is_closed = 1; - break; - default: - break; - } - } - - chunkqueue_remove_finished_chunks(cq); - - if (con->request.content_length <= 64*1024) { - /* don't buffer request bodies <= 64k on disk */ - chunkqueue_steal(dst_cq, cq, (off_t)con->request.content_length - dst_cq->bytes_in); - } - else if (0 != chunkqueue_steal_with_tempfiles(srv, dst_cq, cq, (off_t)con->request.content_length - dst_cq->bytes_in)) { - /* writing to temp file failed */ - con->http_status = 500; /* Internal Server Error */ - con->keep_alive = 0; - con->mode = DIRECT; - chunkqueue_reset(con->write_queue); - - return HANDLER_FINISHED; - } - - chunkqueue_remove_finished_chunks(cq); - - if (dst_cq->bytes_in == (off_t)con->request.content_length) { - /* Content is ready */ - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); - return HANDLER_GO_ON; - } else if (is_closed) { - #if 0 - con->http_status = 400; /* Bad Request */ - con->keep_alive = 0; - con->mode = DIRECT; - chunkqueue_reset(con->write_queue); - - return HANDLER_FINISHED; - #endif - return HANDLER_ERROR; - } else { - return HANDLER_WAIT_FOR_EVENT; - } -} - static void connection_handle_errdoc_init(connection *con) { /* reset caching response headers potentially added by mod_expire */ data_string *ds; diff --git a/src/connections.h b/src/connections.h index daf8eb51..92cdc618 100644 --- a/src/connections.h +++ b/src/connections.h @@ -15,6 +15,7 @@ int connection_set_state(server *srv, connection *con, connection_state_t state) const char * connection_get_state(connection_state_t state); const char * connection_get_short_state(connection_state_t state); int connection_state_machine(server *srv, connection *con); +int connection_handle_read(server *srv, connection *con); handler_t connection_handle_read_post_state(server *srv, connection *con); #endif