summaryrefslogtreecommitdiffstats
path: root/client.c
diff options
context:
space:
mode:
authorSimon Rettberg2014-03-15 01:49:50 +0100
committerSimon Rettberg2014-03-15 01:49:50 +0100
commitbedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad (patch)
treec7d1995a09f6ed0c4e6873252e957d72f5d07d07 /client.c
downloadldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.gz
ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.xz
ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.zip
Lean and mean initial commit
Not much functionality yet
Diffstat (limited to 'client.c')
-rw-r--r--client.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/client.c b/client.c
new file mode 100644
index 0000000..b71b69e
--- /dev/null
+++ b/client.c
@@ -0,0 +1,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;
+}
+