|
|
|
|
|
|
|
|
| #define _POSIX_C_SOURCE 200809L
|
| #include <sys/socket.h>
|
| #include <sys/types.h>
|
| #include <sys/epoll.h>
|
| #include <netinet/in.h>
|
| #include <netinet/tcp.h>
|
| #include <arpa/inet.h>
|
| #include <unistd.h>
|
| #include <fcntl.h>
|
| #include <errno.h>
|
| #include <stddef.h>
|
| #include <stdio.h>
|
| #include <stdlib.h>
|
| #include <string.h>
|
| #include <time.h>
|
|
|
| #define MARLBORG_PORT 8443
|
| #define BACKLOG 128
|
| #define BUFFER_SIZE 4096
|
| #define MAX_CLIENTS 1024
|
| #define TIMEOUT_SECONDS 300
|
|
|
| typedef struct {
|
| int fd;
|
| struct sockaddr_in addr;
|
| socklen_t addr_len;
|
| time_t last_active;
|
| int is_authenticated;
|
| } marlborg_conn_t;
|
|
|
| static marlborg_conn_t connections[MAX_CLIENTS];
|
| static int listen_fd = -1;
|
| static int epoll_fd = -1;
|
|
|
| static void marlborg_close_conn(int index);
|
|
|
| int marlborg_socket_init(void) {
|
| listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
| if (listen_fd < 0) {
|
| perror("socket");
|
| return -1;
|
| }
|
|
|
| int opt = 1;
|
| setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
| setsockopt(listen_fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
|
|
|
| struct sockaddr_in addr = {
|
| .sin_family = AF_INET,
|
| .sin_addr.s_addr = INADDR_ANY,
|
| .sin_port = htons(MARLBORG_PORT)
|
| };
|
| if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
| perror("bind");
|
| close(listen_fd);
|
| return -1;
|
| }
|
|
|
| if (listen(listen_fd, BACKLOG) < 0) {
|
| perror("listen");
|
| close(listen_fd);
|
| return -1;
|
| }
|
|
|
| epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
| if (epoll_fd < 0) {
|
| perror("epoll_create1");
|
| close(listen_fd);
|
| return -1;
|
| }
|
|
|
| struct epoll_event ev = {
|
| .events = EPOLLIN | EPOLLET,
|
| .data.fd = listen_fd
|
| };
|
| if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) < 0) {
|
| perror("epoll_ctl: listen_fd");
|
| close(listen_fd);
|
| close(epoll_fd);
|
| return -1;
|
| }
|
|
|
| for (int i = 0; i < MAX_CLIENTS; i++) {
|
| connections[i].fd = -1;
|
| }
|
|
|
| return 0;
|
| }
|
|
|
| static int marlborg_accept_conn(void) {
|
| struct sockaddr_in addr;
|
| socklen_t addr_len = sizeof(addr);
|
|
|
| int conn_fd = accept4(listen_fd,
|
| (struct sockaddr*)&addr,
|
| &addr_len,
|
| SOCK_NONBLOCK | SOCK_CLOEXEC);
|
| if (conn_fd < 0) {
|
| if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
| perror("accept4");
|
| }
|
| return -1;
|
| }
|
|
|
| for (int i = 0; i < MAX_CLIENTS; i++) {
|
| if (connections[i].fd == -1) {
|
| connections[i] = (marlborg_conn_t){
|
| .fd = conn_fd,
|
| .addr = addr,
|
| .addr_len = addr_len,
|
| .last_active = time(NULL),
|
| .is_authenticated = 0
|
| };
|
|
|
| struct epoll_event ev = {
|
| .events = EPOLLIN | EPOLLOUT | EPOLLET,
|
| .data.u64 = (uint64_t)i
|
| };
|
| if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev) < 0) {
|
| perror("epoll_ctl: conn");
|
| close(conn_fd);
|
| connections[i].fd = -1;
|
| return -1;
|
| }
|
| return i;
|
| }
|
| }
|
|
|
| close(conn_fd);
|
| return -1;
|
| }
|
|
|
| static void marlborg_handle_client(int index) {
|
| marlborg_conn_t* conn = &connections[index];
|
| char buffer[BUFFER_SIZE];
|
| ssize_t n;
|
|
|
| conn->last_active = time(NULL);
|
|
|
| n = read(conn->fd, buffer, BUFFER_SIZE - 1);
|
| if (n > 0) {
|
| buffer[n] = '\0';
|
| printf("Received from %s:%d: %.*s\n",
|
| inet_ntoa(conn->addr.sin_addr),
|
| ntohs(conn->addr.sin_port),
|
| (int)n, buffer);
|
| write(conn->fd, buffer, n);
|
| } else if (n == 0) {
|
| printf("Client %s:%d disconnected\n",
|
| inet_ntoa(conn->addr.sin_addr),
|
| ntohs(conn->addr.sin_port));
|
| marlborg_close_conn(index);
|
| } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
| perror("read");
|
| marlborg_close_conn(index);
|
| }
|
| }
|
|
|
| static void marlborg_close_conn(int index) {
|
| marlborg_conn_t* conn = &connections[index];
|
| if (conn->fd != -1) {
|
| epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn->fd, NULL);
|
| close(conn->fd);
|
| conn->fd = -1;
|
| }
|
| }
|
|
|
| void marlborg_socket_run(void) {
|
| struct epoll_event events[MAX_CLIENTS];
|
|
|
| while (1) {
|
| int nfds = epoll_wait(epoll_fd, events, MAX_CLIENTS, -1);
|
| if (nfds < 0) {
|
| if (errno == EINTR) continue;
|
| perror("epoll_wait");
|
| break;
|
| }
|
|
|
| for (int i = 0; i < nfds; i++) {
|
| if (events[i].data.fd == listen_fd) {
|
| int idx = marlborg_accept_conn();
|
| if (idx >= 0) {
|
| printf("New connection from %s:%d (slot %d)\n",
|
| inet_ntoa(connections[idx].addr.sin_addr),
|
| ntohs(connections[idx].addr.sin_port),
|
| idx);
|
| }
|
| } else {
|
| marlborg_handle_client((int)events[i].data.u64);
|
| }
|
| }
|
|
|
| time_t now = time(NULL);
|
| for (int i = 0; i < MAX_CLIENTS; i++) {
|
| if (connections[i].fd != -1 &&
|
| (now - connections[i].last_active) > TIMEOUT_SECONDS) {
|
| printf("Timeout: closing connection %d\n", i);
|
| marlborg_close_conn(i);
|
| }
|
| }
|
| }
|
|
|
| close(listen_fd);
|
| close(epoll_fd);
|
| }
|
|
|
| int main(void) {
|
| if (marlborg_socket_init() < 0) {
|
| fprintf(stderr, "Failed to initialize socket layer\n");
|
| return EXIT_FAILURE;
|
| }
|
|
|
| printf("Marlborg-Wormhole socket layer listening on port %d\n", MARLBORG_PORT);
|
| marlborg_socket_run();
|
|
|
| return EXIT_SUCCESS;
|
| }
|
|
|