#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/tcp.h>

#define LISTEN_PORT 65000
#define CF_IP "185.185.185.185"
#define CF_PORT 443
#define BUFFER_SIZE 32768
#define MAX_CONNECTIONS 200
#define LISTEN_BACKLOG 256
#define IDLE_TIMEOUT 300  // seconds
#define CONNECT_TIMEOUT 15 // seconds

typedef struct {
    unsigned char data[BUFFER_SIZE];
    size_t len;  // end of valid data
    size_t off;  // start offset of pending data
} buffer_t;

typedef struct {
    int in_use;
    int client_fd;
    int remote_fd;
    int remote_connected;
    time_t connect_started;

    int client_rd_closed;
    int remote_rd_closed;
    int client_wr_shutdown;
    int remote_wr_shutdown;

    int client_err;
    int remote_err;

    buffer_t c2r;
    buffer_t r2c;
    time_t last_active;
} conn_t;

static conn_t g_conns[MAX_CONNECTIONS];

static int set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0) return -1;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

static void buf_reset(buffer_t *b) {
    b->len = 0;
    b->off = 0;
}

static void buf_compact(buffer_t *b) {
    if (b->off == 0) return;
    if (b->off == b->len) {
        b->off = 0;
        b->len = 0;
        return;
    }
    memmove(b->data, b->data + b->off, b->len - b->off);
    b->len -= b->off;
    b->off = 0;
}

static size_t buf_avail(const buffer_t *b) {
    return BUFFER_SIZE - b->len;
}

static size_t buf_pending(const buffer_t *b) {
    return b->len - b->off;
}

static void close_conn(conn_t *c) {
    if (!c->in_use) return;
    if (c->client_fd >= 0) close(c->client_fd);
    if (c->remote_fd >= 0) close(c->remote_fd);
    c->client_fd = -1;
    c->remote_fd = -1;
    c->in_use = 0;
}

static void set_keepalive(int fd) {
    int on = 1;
    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
}

static void read_into(int fd, buffer_t *b, int *rd_closed, time_t *last_active) {
    if (*rd_closed) return;

    if (buf_avail(b) == 0) {
        buf_compact(b);
        if (buf_avail(b) == 0) return;
    }

    ssize_t n = recv(fd, b->data + b->len, buf_avail(b), 0);
    if (n > 0) {
        b->len += (size_t)n;
        *last_active = time(NULL);
    } else if (n == 0) {
        *rd_closed = 1;
    } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
        *rd_closed = 1;
    }
}

static void write_from(int fd, buffer_t *b, int *err_flag, time_t *last_active) {
    size_t pending = buf_pending(b);
    if (pending == 0) return;

    ssize_t n = send(fd, b->data + b->off, pending, 0);
    if (n > 0) {
        b->off += (size_t)n;
        if (b->off == b->len) {
            b->off = 0;
            b->len = 0;
        }
        *last_active = time(NULL);
    } else if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
        *err_flag = 1;
    }
}

static int find_free_slot(conn_t *conns) {
    for (int i = 0; i < MAX_CONNECTIONS; i++) {
        if (!conns[i].in_use) return i;
    }
    return -1;
}

static void try_shutdown_peer(conn_t *c) {
    if (!c->remote_wr_shutdown && c->remote_connected &&
        c->client_rd_closed && buf_pending(&c->c2r) == 0) {
        shutdown(c->remote_fd, SHUT_WR);
        c->remote_wr_shutdown = 1;
    }

    if (!c->client_wr_shutdown &&
        c->remote_rd_closed && buf_pending(&c->r2c) == 0) {
        shutdown(c->client_fd, SHUT_WR);
        c->client_wr_shutdown = 1;
    }
}

int main() {
    signal(SIGPIPE, SIG_IGN);

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) return 1;

    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    set_keepalive(server_fd);

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(LISTEN_PORT);

    if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) return 1;
    if (listen(server_fd, LISTEN_BACKLOG) < 0) return 1;
    if (set_nonblocking(server_fd) < 0) return 1;

    struct sockaddr_in remote_addr;
    memset(&remote_addr, 0, sizeof(remote_addr));
    remote_addr.sin_family = AF_INET;
    remote_addr.sin_port = htons(CF_PORT);
    if (inet_pton(AF_INET, CF_IP, &remote_addr.sin_addr) != 1) return 1;

    conn_t *conns = g_conns;
    memset(conns, 0, sizeof(g_conns));
    for (int i = 0; i < MAX_CONNECTIONS; i++) {
        conns[i].client_fd = -1;
        conns[i].remote_fd = -1;
    }

    struct pollfd pfds[1 + MAX_CONNECTIONS * 2];
    conn_t *refs[1 + MAX_CONNECTIONS * 2];
    int side[1 + MAX_CONNECTIONS * 2]; // 0=client, 1=remote, -1=listener

    while (1) {
        int nfds = 0;
        pfds[nfds].fd = server_fd;
        pfds[nfds].events = POLLIN;
        refs[nfds] = NULL;
        side[nfds] = -1;
        nfds++;

        for (int i = 0; i < MAX_CONNECTIONS; i++) {
            if (!conns[i].in_use) continue;
            conn_t *c = &conns[i];

            short ev_client = 0;
            if (!c->client_rd_closed && buf_avail(&c->c2r) > 0) ev_client |= POLLIN;
            if (buf_pending(&c->r2c) > 0) ev_client |= POLLOUT;

            pfds[nfds].fd = c->client_fd;
            pfds[nfds].events = ev_client;
            refs[nfds] = c;
            side[nfds] = 0;
            nfds++;

            short ev_remote = 0;
            if (!c->remote_connected) {
                ev_remote |= POLLOUT;
            } else {
                if (!c->remote_rd_closed && buf_avail(&c->r2c) > 0) ev_remote |= POLLIN;
                if (buf_pending(&c->c2r) > 0) ev_remote |= POLLOUT;
            }

            pfds[nfds].fd = c->remote_fd;
            pfds[nfds].events = ev_remote;
            refs[nfds] = c;
            side[nfds] = 1;
            nfds++;
        }

        int ret = poll(pfds, nfds, 1000);
        if (ret < 0) {
            if (errno == EINTR) continue;
            break;
        }

        if (pfds[0].revents & POLLIN) {
            while (1) {
                struct sockaddr_in client_addr;
                socklen_t client_len = sizeof(client_addr);
                int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
                if (client_fd < 0) {
                    if (errno == EAGAIN || errno == EWOULDBLOCK) break;
                    break;
                }

                if (set_nonblocking(client_fd) < 0) {
                    close(client_fd);
                    continue;
                }
                set_keepalive(client_fd);

                int slot = find_free_slot(conns);
                if (slot < 0) {
                    close(client_fd);
                    continue;
                }

                int remote_fd = socket(AF_INET, SOCK_STREAM, 0);
                if (remote_fd < 0) {
                    close(client_fd);
                    continue;
                }

                if (set_nonblocking(remote_fd) < 0) {
                    close(client_fd);
                    close(remote_fd);
                    continue;
                }
                set_keepalive(remote_fd);

                int connected = 0;
                int rc = connect(remote_fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr));
                if (rc == 0) {
                    connected = 1;
                } else if (errno != EINPROGRESS) {
                    close(client_fd);
                    close(remote_fd);
                    continue;
                }

                conn_t *c = &conns[slot];
                memset(c, 0, sizeof(*c));
                c->in_use = 1;
                c->client_fd = client_fd;
                c->remote_fd = remote_fd;
                c->remote_connected = connected;
                c->connect_started = time(NULL);
                buf_reset(&c->c2r);
                buf_reset(&c->r2c);
                c->last_active = time(NULL);
            }
        }

        for (int i = 1; i < nfds; i++) {
            conn_t *c = refs[i];
            if (!c || !c->in_use) continue;

            short re = pfds[i].revents;
            if (re == 0) continue;

            if (side[i] == 0) {
                if (re & (POLLERR | POLLNVAL)) c->client_err = 1;
                if (re & POLLIN) read_into(c->client_fd, &c->c2r, &c->client_rd_closed, &c->last_active);
                if (re & POLLOUT) write_from(c->client_fd, &c->r2c, &c->client_err, &c->last_active);
                if (re & POLLHUP) c->client_rd_closed = 1;
            } else if (side[i] == 1) {
                if (!c->remote_connected) {
                    if (re & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) {
                        int err = 0;
                        socklen_t len = sizeof(err);
                        if (getsockopt(c->remote_fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0 || err != 0) {
                            c->remote_err = 1;
                        } else {
                            c->remote_connected = 1;
                            c->last_active = time(NULL);
                        }
                    }
                    continue;
                }

                if (re & (POLLERR | POLLNVAL)) c->remote_err = 1;
                if (re & POLLIN) read_into(c->remote_fd, &c->r2c, &c->remote_rd_closed, &c->last_active);
                if (re & POLLOUT) write_from(c->remote_fd, &c->c2r, &c->remote_err, &c->last_active);
                if (re & POLLHUP) c->remote_rd_closed = 1;
            }
        }

        time_t now = time(NULL);
        for (int i = 0; i < MAX_CONNECTIONS; i++) {
            if (!conns[i].in_use) continue;
            conn_t *c = &conns[i];

            if (c->client_err || c->remote_err) {
                close_conn(c);
                continue;
            }

            if (!c->remote_connected && now - c->connect_started >= CONNECT_TIMEOUT) {
                close_conn(c);
                continue;
            }

            try_shutdown_peer(c);

            if (now - c->last_active >= IDLE_TIMEOUT) {
                close_conn(c);
                continue;
            }

            if (c->client_rd_closed && c->remote_rd_closed &&
                buf_pending(&c->c2r) == 0 && buf_pending(&c->r2c) == 0) {
                close_conn(c);
                continue;
            }
        }
    }

    close(server_fd);
    return 0;
}