summaryrefslogtreecommitdiffstats
path: root/client.c
blob: b71b69e3b55ac52328448781cba414f0ed2a3fbf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#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;
}