Initial import

This commit is contained in:
Daniele Lacamera 2024-11-28 10:04:26 +01:00
commit 9776672e5e
2 changed files with 262 additions and 0 deletions

15
Makefile Normal file
View file

@ -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

247
server.c Normal file
View file

@ -0,0 +1,247 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#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;
}