#include "client.h" #include "proxy.h" #include "epoll.h" #include "helper.h" #include "asn1.h" #include "openssl.h" #include #include #include #include #include static void client_haveIn(epoll_client_t *client); static BOOL client_haveOut(epoll_client_t *client); 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); } free(client); } void client_callback(void *data, int haveIn, int haveOut, int doCleanup) { epoll_client_t *client = (epoll_client_t*)data; if (doCleanup || client->kill) { //printf("Client gone (1).\n"); client_free(client); return; } if (client->ssl == NULL) { // Plain connection if (haveIn) client_haveIn(client); if (haveOut || client->kill) { if (client_haveOut(client)) return; } if (client->kill) { //printf("Client gone (2).\n"); client_free(client); } return; } // SSL connection if (!client->sslAccepted) { // Still SSL-Connecting if (!ssl_acceptClient(client)) { printf("[Proxy] 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); if (client_haveOut(client)) return; if (client->kill) { //printf("Client gone (3).\n"); client_free(client); } } static void client_haveIn(epoll_client_t *client) { for (;;) { if (client->rbPos >= REQLEN) { printf("[Proxy] Buffer overflow reading from client. 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) { if (client->kill) return; printf("[Proxy] Client gone while reading (ret=%d, errno=%d).\n", (int)ret, errno); client->kill = TRUE; return; } } else { // SSL ret = SSL_read(client->ssl, client->readBuffer + client->rbPos, buflen); if (ret <= 0) { if (client->kill) return; int err = SSL_get_error(client->ssl, ret); if (SSL_BLOCKED(err)) break; if (err == SSL_ERROR_ZERO_RETURN) { SSL_shutdown(client->ssl); // Might have to handle error return codes; need another state in epoll_client_t for this } printf("[Proxy] SSL client gone while reading (ret=%d, err=%d).\n", (int)ret, err); client->kill = TRUE; return; } } // 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 if (!proxy_fromClient(client, len)) { printf("[Proxy] 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 } } /** * Send any queued data. * Returns TRUE if anything has been sent, or the socket * buffer is currently full (EAGAIN, non-blocking mode) */ static BOOL client_haveOut(epoll_client_t *client) { client->writeBlocked = FALSE; if (client->sbPos >= client->sbFill) return FALSE; while (client->sbPos < client->sbFill) { const int tosend = client->sbFill - client->sbPos; 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 TRUE; if (ret <= 0) { printf("[Proxy] Cannot send to client (ret=%d, errno=%d)\n", (int)ret, errno); client->kill = TRUE; return FALSE; } } 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 TRUE; // Blocking } printf("[Proxy] SSL cannot send to client (ret=%d, err=%d)\n", (int)ret, err); ERR_print_errors_fp(stdout); client->kill = TRUE; return FALSE; // Closed } } client->sbPos += ret; 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 TRUE; // Sent less than requested -> buffer full, epoll will trigger us again } client->sbPos = client->sbFill = 0; if (client->ssl == NULL && client->sbLen > MAXMSGLEN / 2) { helper_realloc(&client->sendBuffer, &client->sbLen, 8000, "client_haveOut"); } return TRUE; } BOOL client_send(epoll_client_t *client, const char *buffer, size_t len, const BOOL 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("[Proxy] Client gone when trying to send.\n"); client->kill = TRUE; return FALSE; } if (ret == (int)len) return TRUE; // Couldn't send everything, continue with buffering logic below if (ret > 0) { buffer += ret; len -= (size_t)ret; } } // Buffer... if (client->sbLen - client->sbFill < len) { // Buffer too small? if (client->writeBlocked) { printf("[Proxy] SSL write to client blocked and buffer to small (%d bytes)\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 > MAXMSGLEN) { printf("[Proxy] Dropping client as the send buffer would exceed %d bytes.\n", (int)MAXMSGLEN); 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 + (client->ssl && client->sbLen > 0 ? MAXMSGLEN : 4000), "client_send") == -1) { client->kill = TRUE; return FALSE; } } } // Finally append to buffer memcpy(client->sendBuffer + client->sbFill, buffer, len); client->sbFill += len; if (!cork) client_haveOut(client); return TRUE; } BOOL client_searchResultError(epoll_client_t *client, const unsigned long messageId, const int code, const char *message) { const size_t doneLen = fmt_ldapsearchresultdone(NULL, code, "", message, ""); const size_t doneHeaderLen = fmt_ldapmessage(NULL, messageId, SearchResultDone, doneLen); char buffer[doneLen + doneHeaderLen]; fmt_ldapsearchresultdone(buffer + doneHeaderLen, code, "", message, ""); fmt_ldapmessage(buffer, messageId, SearchResultDone, doneLen); return client_send(client, buffer, doneHeaderLen + doneLen, FALSE); }