From bbdf2fba7b9ae0fa97aa164bcf84c1b88df38f32 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 9 Sep 2014 18:07:48 +0200 Subject: Add OpenSSL-Support (Client<->Proxy) --- client.c | 196 ++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 138 insertions(+), 58 deletions(-) (limited to 'client.c') diff --git a/client.c b/client.c index b71b69e..e9e9453 100644 --- a/client.c +++ b/client.c @@ -3,20 +3,23 @@ #include "epoll.h" #include "helper.h" #include "asn1.h" +#include "openssl.h" #include #include #include #include #include -#define MAX_SEND_BUFFER 100000 +#define MAX_SEND_BUFFER 150000 -static void client_flush(epoll_client_t *client); +static void client_haveIn(epoll_client_t *client); +static void client_haveOut(epoll_client_t *client); static void client_free(epoll_client_t *client) { proxy_removeClient(client); if (client->sendBuffer != NULL) free(client->sendBuffer); + if (client->ssl != NULL) SSL_free(client->ssl); if (client->fd != -1) { //ePoll_remove(client->fd); // epoll would remove closed descriptors automatically, but just to be sure we don't cause use after free, we remove it explicitly close(client->fd); @@ -27,110 +30,187 @@ static void client_free(epoll_client_t *client) void client_callback(void *data, int haveIn, int haveOut, int doCleanup) { epoll_client_t *client = (epoll_client_t*)data; - if (doCleanup) { + if (doCleanup || client->kill) { printf("Client gone.\n"); client_free(client); return; } - // Anything to read? - if (haveIn) { - for (;;) { - if (client->rbPos >= REQLEN) { - printf("[C->Proxy] Read buffer overflow. Disconnecting.\n"); - client_free(client); - return; - } - const size_t buflen = REQLEN - client->rbPos; - const ssize_t ret = read(client->fd, client->readBuffer + client->rbPos, buflen); + if (client->ssl == NULL) { + // Plain connection + if (haveIn) client_haveIn(client); + if (haveOut) client_haveOut(client); + if (client->kill) client_free(client); + return; + } + // SSL connection + if (!client->sslAccepted) { + // Still SSL-Connecting + if (!ssl_acceptClient(client)) { + printf("SSL Client accept failed.\n"); + client_free(client); + return; + } + if (!client->sslAccepted) return; + } + // Since we don't know if the incoming data is just wrapped application data or ssl protocol stuff, we always call both + client_haveIn(client); + client_haveOut(client); + if (client->kill) { + printf("Client killed.\n"); + client_free(client); + } +} + +static void client_haveIn(epoll_client_t *client) +{ + for (;;) { + if (client->rbPos >= REQLEN) { + printf("[C->Proxy] Read buffer overflow. Disconnecting.\n"); + client->kill = TRUE; + return; + } + const size_t buflen = REQLEN - client->rbPos; + ssize_t ret; + if (client->ssl == NULL) { + // Plain + ret = read(client->fd, client->readBuffer + client->rbPos, buflen); if (ret < 0 && errno == EINTR) continue; if (ret < 0 && errno == EAGAIN) break; if (ret <= 0) { printf("Client gone while reading.\n"); - client_free(client); + client->kill = TRUE; return; } - client->rbPos += ret; - // Request complete? - for (;;) { - size_t consumed, len; - consumed = scan_asn1SEQUENCE(client->readBuffer, client->readBuffer + client->rbPos, &len); - if (consumed == 0) break; // Length-Header not complete - len += consumed; - if (len > client->rbPos) break; // Body not complete yet - printf("Received complete requrest...\n"); - if (proxy_fromClient(client, len) == -1) { - printf("Error parsing request from client.\n"); - client_free(client); - return; - } - // Shift remaining buffer contents - if (len == client->rbPos) { - client->rbPos = 0; - break; - } - memmove(client->readBuffer, client->readBuffer + len, client->rbPos - len); - client->rbPos -= len; + } else { + // SSL + ret = SSL_read(client->ssl, client->readBuffer + client->rbPos, buflen); + if (ret <= 0) { + int err = SSL_get_error(client->ssl, ret); + if (SSL_BLOCKED(err)) break; + printf("Client gone while reading (%d, %d).\n", (int)ret, err); + client->kill = TRUE; + return; } - if ((ssize_t)buflen > ret) break; // Read less than buffer len, epoll will fire again } + // We reach here if something was read (so ret > 0) + client->rbPos += ret; + // Request complete? + for (;;) { + size_t consumed, len; + consumed = scan_asn1SEQUENCE(client->readBuffer, client->readBuffer + client->rbPos, &len); + if (consumed == 0) break; // Length-Header not complete + len += consumed; + if (len > client->rbPos) break; // Body not complete yet + printf("Received complete requrest...\n"); + if (!proxy_fromClient(client, len)) { + printf("Error parsing request from client.\n"); + client->kill = TRUE; + return; + } + // Shift remaining buffer contents + if (len == client->rbPos) { + client->rbPos = 0; + break; + } + memmove(client->readBuffer, client->readBuffer + len, client->rbPos - len); + client->rbPos -= len; + } + if (client->ssl == NULL && (ssize_t)buflen > ret) break; // Read less than buffer len, epoll will fire again } - // Anything to send? - if (haveOut) client_flush(client); } -static void client_flush(epoll_client_t *client) +static void client_haveOut(epoll_client_t *client) { + client->writeBlocked = FALSE; while (client->sbPos < client->sbFill) { const int tosend = client->sbFill - client->sbPos; - const int ret = write(client->fd, client->sendBuffer + client->sbPos, tosend); - if (ret < 0 && errno == EINTR) continue; - if (ret < 0 && errno == EAGAIN) return; - if (ret <= 0) { - printf("Cannot send to client (ret: %d, errno: %d)\n", ret, errno); - return; + ssize_t ret; + if (client->ssl == NULL) { + // Plain + ret = write(client->fd, client->sendBuffer + client->sbPos, tosend); + if (ret < 0 && errno == EINTR) continue; + if (ret < 0 && errno == EAGAIN) return; + if (ret <= 0) { + printf("Cannot send to client (ret: %d, errno: %d)\n", (int)ret, errno); + client->kill = TRUE; + return; + } + } else { + // SSL + ret = SSL_write(client->ssl, client->sendBuffer + client->sbPos, tosend); + if (ret <= 0) { + int err = SSL_get_error(client->ssl, ret); + if (SSL_BLOCKED(err)) { + client->writeBlocked = TRUE; + return; // Blocking + } + printf("SSL client gone while sending (%d)\n", err); + ERR_print_errors_fp(stdout); + client->kill = TRUE; + return; // Closed + } } client->sbPos += ret; - if (ret != tosend) return; // Sent less than requested -> buffer full, epoll will trigger us again + if (client->ssl != NULL) { + memmove(client->sendBuffer, client->sendBuffer + client->sbPos, client->sbFill - client->sbPos); + client->sbFill -= client->sbPos; + client->sbPos = 0; + } + if (client->ssl == NULL && ret != tosend) return; // Sent less than requested -> buffer full, epoll will trigger us again } client->sbPos = client->sbFill = 0; + if (client->ssl == NULL && client->sbLen > MAX_SEND_BUFFER / 2) { + helper_realloc(&client->sendBuffer, &client->sbLen, 8000, "client_haveOut"); + } } -int client_send(epoll_client_t *client, const char *buffer, size_t len, const BOOL cork) +BOOL client_send(epoll_client_t *client, const char *buffer, size_t len, const BOOL cork) { - if (client->sbFill == 0 && !cork) { + if (client->ssl == NULL && client->sbFill == 0 && !cork) { // Nothing in send buffer, fire away const int ret = write(client->fd, buffer, len); if (ret == 0 || (ret < 0 && errno != EINTR && errno != EAGAIN)) { printf("Client gone when trying to send.\n"); - return -1; + client->kill = TRUE; + return FALSE; } - if (ret == (int)len) return 0; + if (ret == (int)len) return TRUE; // Couldn't send everything, continue with buffering logic below if (ret > 0) { buffer += ret; len -= (size_t)ret; } } - // Sanity - if ((client->sbLen - client->sbPos) + len > MAX_SEND_BUFFER) { - printf("Dropping client as the send buffer would exceed %d bytes.\n", (int)MAX_SEND_BUFFER); - return -1; - } // Buffer... if (client->sbLen - client->sbFill < len) { // Buffer too small? + if (client->writeBlocked) { + printf("SSL Write blocked and buffer to small (%d)\n", (int)client->sbLen); + client->kill = TRUE; + return FALSE; + } if (client->sbPos != 0) { // See if we can compact it memmove(client->sendBuffer, client->sendBuffer + client->sbPos, client->sbFill - client->sbPos); client->sbFill -= client->sbPos; client->sbPos = 0; } + // Sanity + if (client->sbFill + len > MAX_SEND_BUFFER) { + printf("Dropping client as the send buffer would exceed %d bytes.\n", (int)MAX_SEND_BUFFER); + client->kill = TRUE; + return FALSE; + } if (client->sbLen - client->sbFill < len) { // Still too small after compacting? realloc - if (helper_realloc(&client->sendBuffer, &client->sbLen, client->sbLen + len + 1500, "client_send") == -1) return -1; + if (helper_realloc(&client->sendBuffer, &client->sbLen, client->sbLen + len + (client->ssl && client->sbLen > 0 ? MAX_SEND_BUFFER : 4000), "client_send") == -1) { + client->kill = TRUE; + return FALSE; + } + printf("Send Buffer now %d\n", (int)client->sbLen); } } // Finally append to buffer memcpy(client->sendBuffer + client->sbFill, buffer, len); client->sbFill += len; - if (!cork) client_flush(client); - return 0; + if (!cork) client_haveOut(client); + return TRUE; } -- cgit v1.2.3-55-g7522