commit 9776672e5e7d3c3a8d7a34d7d47cb78a172a7fb0 Author: Daniele Lacamera Date: Thu Nov 28 10:04:26 2024 +0100 Initial import diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c332190 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +LDFLAGS=-lpthread + +all: server ftserver + +ftserver:LDFLAGS+=-lfemtotcp + +ftserver:server.o + gcc -o $@ $^ $(LDFLAGS) + +server: server.o + gcc -o $@ $^ $(LDFLAGS) + + +clean: + rm -f ftserver server server.o diff --git a/server.c b/server.c new file mode 100644 index 0000000..ade5067 --- /dev/null +++ b/server.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 31337 +#define BUFFER_SIZE (32 * 1024) +#define MAX_CLIENTS 10 +#define UID 1000 +#define SLIDES_PATH "femtotcp.gitslides" + +/* Telnet negotiation commands */ +#define IAC 255 +#define DO 253 +#define WILL 251 +#define SUPPRESS_GO_AHEAD 3 +#define ECHO 1 +#define LINEMODE 34 +#define CMD_SIZE 256 +#define PATH_SIZE 256 + +struct Client { + int fd; + char repo_path[PATH_SIZE]; + char slides_path[PATH_SIZE]; + char current_slide[BUFFER_SIZE]; +}; + +/* Telnet negotiation to enable non-canonical mode */ +void send_telnet_negotiation(int client_fd) { + unsigned char suppress_go_ahead[] = {IAC, WILL, SUPPRESS_GO_AHEAD}; + unsigned char disable_echo[] = {IAC, DO, ECHO}; + unsigned char enable_linemode[] = {IAC, DO, LINEMODE}; + + write(client_fd, suppress_go_ahead, sizeof(suppress_go_ahead)); + write(client_fd, disable_echo, sizeof(disable_echo)); + write(client_fd, enable_linemode, sizeof(enable_linemode)); +} + +/* Clear the screen */ +void clear_screen(int client_fd) { + const char *ESC_CLEAR = "\x1b[2J\x1b[H"; + write(client_fd, ESC_CLEAR, strlen(ESC_CLEAR)); +} + +/* Execute a git-slides command */ +void execute_git_slides(const char *repo_path, const char *slide_command) { + char command[CMD_SIZE]; + snprintf(command, sizeof(command), "cd %s && git-slides %s", repo_path, slide_command); + system(command); +} + +/* Load slide content from file */ +void load_slide_content(const char *slides_path, char *output) { + int fd = open(slides_path, O_RDONLY); + if (fd < 0) { + snprintf(output, BUFFER_SIZE, "Error: Could not open slides file: %s\n", strerror(errno)); + return; + } + + ssize_t n = read(fd, output, BUFFER_SIZE - 1); + if (n < 0) { + snprintf(output, BUFFER_SIZE, "Error: Could not read slides file: %s\n", strerror(errno)); + } else { + output[n] = '\0'; /* Null-terminate the buffer */ + } + + close(fd); +} + +/* Handle client input and update slides */ +void handle_client_input(struct Client *client, char command) { + if (command == ' ') { + execute_git_slides(client->repo_path, "next"); + } else if (command == '\x08' || command == '\x7f') { /* Backspace or DEL */ + execute_git_slides(client->repo_path, "prev"); + } else if (command == 'q') { + clear_screen(client->fd); + close(client->fd); + client->fd = -1; /* Mark as disconnected */ + return; + } else { + snprintf(client->current_slide, BUFFER_SIZE, "Unknown command '%c'. Use SPACE, BACKSPACE, or Q.\n", command); + } + + load_slide_content(client->slides_path, client->current_slide); +} + +/* Send welcome message to the client */ +void send_welcome_message(struct Client *client) { + clear_screen(client->fd); + const char *welcome_msg = + "Welcome to the git-slides presentation!\n" + "Use SPACE (forward), BACKSPACE (back), or Q (quit).\n\n"; + write(client->fd, welcome_msg, strlen(welcome_msg)); + write(client->fd, client->current_slide, strlen(client->current_slide)); +} + +/* Clean up client resources */ +void cleanup_client(struct Client *client) { + if (client->fd != -1) { + close(client->fd); + client->fd = -1; + } + if (strlen(client->repo_path) > 0) { + char rm_command[CMD_SIZE]; + snprintf(rm_command, sizeof(rm_command), "rm -rf %s", client->repo_path); + system(rm_command); + } +} + +int main() { + int server_fd; + struct sockaddr_in server_addr; + setuid(UID); /* Drop privileges */ + printf("User ID: %d\n", getuid()); + + + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(PORT); + server_addr.sin_addr.s_addr = INADDR_ANY; + + if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + perror("bind"); + close(server_fd); + exit(EXIT_FAILURE); + } + + if (listen(server_fd, MAX_CLIENTS) < 0) { + perror("listen"); + close(server_fd); + exit(EXIT_FAILURE); + } + + struct pollfd fds[MAX_CLIENTS + 1]; + struct Client clients[MAX_CLIENTS] = {0}; + int i; + + for (i = 0; i < MAX_CLIENTS; i++) { + clients[i].fd = -1; + } + + fds[0].fd = server_fd; + fds[0].events = POLLIN; + + printf("Server running on port %d\n", PORT); + + while (1) { + int num_fds = 1; + + /* Prepare the pollfd array */ + for (i = 0; i < MAX_CLIENTS; i++) { + if (clients[i].fd != -1) { + fds[num_fds].fd = clients[i].fd; + fds[num_fds].events = POLLIN; + num_fds++; + } + } + + int ret = poll(fds, num_fds, -1); + if (ret < 0) { + perror("poll"); + break; + } + + /* Handle new connections */ + if (fds[0].revents & POLLIN) { + int client_fd = accept(server_fd, NULL, NULL); + if (client_fd < 0) { + perror("accept"); + continue; + } + + /* Find an empty slot for the client */ + int slot = -1; + for (i = 0; i < MAX_CLIENTS; i++) { + if (clients[i].fd == -1) { + slot = i; + break; + } + } + + if (slot == -1) { + const char *msg = "Server full. Try again later.\n"; + write(client_fd, msg, strlen(msg)); + close(client_fd); + } else { + struct Client *client = &clients[slot]; + char cp_command[CMD_SIZE]; + client->fd = client_fd; + snprintf(client->repo_path, PATH_SIZE, "/tmp/git_repo_%d", client_fd); + snprintf(client->slides_path, PATH_SIZE, "%s/%s", client->repo_path, SLIDES_PATH); + + /* Create a temporary copy of the repo */ + snprintf(cp_command, sizeof(cp_command), "cp -a . %s", client->repo_path); + system(cp_command); + + execute_git_slides(client->repo_path, "prev"); + load_slide_content(client->slides_path, client->current_slide); + + send_telnet_negotiation(client_fd); + send_welcome_message(client); + + printf("Client connected: %d\n", client_fd); + } + } + + /* Handle client events */ + for (i = 1; i < num_fds; i++) { + if (fds[i].revents & POLLIN) { + char command; + int client_idx = i - 1; + struct Client *client = &clients[client_idx]; + + ssize_t n = read(client->fd, &command, 1); + if (n <= 0) { + cleanup_client(client); + continue; + } + + handle_client_input(client, command); + + clear_screen(client->fd); + write(client->fd, client->current_slide, strlen(client->current_slide)); + } + } + } + + /* Cleanup */ + for (i = 0; i < MAX_CLIENTS; i++) { + cleanup_client(&clients[i]); + } + close(server_fd); + return 0; +} +