#include "client.h"
#include "proxy.h"
#include "epoll.h"
#include "helper.h"
#include "asn1.h"
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#define MAX_SEND_BUFFER 100000
static void client_flush(epoll_client_t *client);
static void client_free(epoll_client_t *client)
{
proxy_removeClient(client);
if (client->sendBuffer != NULL) free(client->sendBuffer);
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) {
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 (ret < 0 && errno == EINTR) continue;
if (ret < 0 && errno == EAGAIN) break;
if (ret <= 0) {
printf("Client gone while reading.\n");
client_free(client);
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;
}
if ((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)
{
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;
}
client->sbPos += ret;
if (ret != tosend) return; // Sent less than requested -> buffer full, epoll will trigger us again
}
client->sbPos = client->sbFill = 0;
}
int client_send(epoll_client_t *client, const char *buffer, size_t len, const BOOL cork)
{
if (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;
}
if (ret == (int)len) return 0;
// 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->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;
}
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;
}
}
// Finally append to buffer
memcpy(client->sendBuffer + client->sbFill, buffer, len);
client->sbFill += len;
if (!cork) client_flush(client);
return 0;
}