summaryrefslogblamecommitdiffstats
path: root/client.c
blob: b71b69e3b55ac52328448781cba414f0ed2a3fbf (plain) (tree)







































































































































                                                                                                                                                                              
#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;
}