254 lines
6.1 KiB
C
254 lines
6.1 KiB
C
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <linux/fb.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/file.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#ifdef HAVE_LINUX_SECCOMP_H
|
|
#include <linux/audit.h>
|
|
#include <linux/filter.h>
|
|
#include <linux/seccomp.h>
|
|
#include <stddef.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/syscall.h>
|
|
|
|
#define ALLOW(syscall) \
|
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SYS_##syscall, 0, 1), \
|
|
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
|
|
|
|
static int setup_seccomp() {
|
|
struct sock_filter filter[] = {
|
|
/* check if architecture is the same as what we
|
|
* were compiled with */
|
|
BPF_STMT(BPF_LD + BPF_W + BPF_ABS,
|
|
offsetof(struct seccomp_data, arch)),
|
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
|
|
#if defined(__x86_64__)
|
|
AUDIT_ARCH_X86_64,
|
|
#elif defined(__i386__)
|
|
AUDIT_ARCH_I386,
|
|
#elif defined(__riscv) && __riscv_xlen == 64
|
|
AUDIT_ARCH_RISCV64,
|
|
#elif defined(__riscv) && __riscv_xlen == 32
|
|
AUDIT_ARCH_RISCV32,
|
|
#elif defined(__arm__)
|
|
AUDIT_ARCH_ARM,
|
|
#elif defined(__aarch64__)
|
|
AUDIT_ARCH_AARCH64,
|
|
#else
|
|
#error unknown architecture, file a bug or turn off seccomp
|
|
#endif
|
|
1, 0),
|
|
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS),
|
|
/* check syscalls */
|
|
BPF_STMT(BPF_LD + BPF_W + BPF_ABS,
|
|
offsetof(struct seccomp_data, nr)),
|
|
|
|
ALLOW(close),
|
|
ALLOW(exit),
|
|
ALLOW(madvise),
|
|
ALLOW(munmap),
|
|
#ifdef __NR_recv
|
|
ALLOW(recv),
|
|
#endif
|
|
ALLOW(recvfrom),
|
|
ALLOW(rt_sigprocmask),
|
|
ALLOW(write),
|
|
ALLOW(writev),
|
|
/* otherwise kill the process */
|
|
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL_PROCESS),
|
|
};
|
|
struct sock_fprog prog = {.len = sizeof(filter) / sizeof(*filter),
|
|
.filter = filter};
|
|
|
|
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
|
|
perror("error enabling seccomp");
|
|
return 1;
|
|
}
|
|
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
|
|
perror("error enabling seccomp");
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int fbfd, fb_width, fb_height, fb_length, fb_bytes, fb_hexbytes;
|
|
uint32_t *fbdata;
|
|
|
|
char *safestrtok(char *str, const char *delim, char **strtokptr) {
|
|
char *result = strtok_r(str, delim, strtokptr);
|
|
if (result == NULL)
|
|
result = "";
|
|
return result;
|
|
}
|
|
|
|
void *handle_connection(void *socket_desc) {
|
|
#ifdef HAVE_LINUX_SECCOMP_H
|
|
setup_seccomp();
|
|
#endif
|
|
pthread_detach(pthread_self());
|
|
|
|
int sock = *(int *)socket_desc;
|
|
int read_size, message_len;
|
|
char message[37], client_message[45];
|
|
|
|
while ((read_size = recv(sock, client_message, 36, MSG_PEEK)) > 0) {
|
|
uintptr_t read_to;
|
|
char *command, *strtokptr;
|
|
|
|
client_message[read_size] = '\0';
|
|
read_to = (uintptr_t)strchr(client_message, '\n');
|
|
if (read_to == 0) {
|
|
if (read_size == 36) {
|
|
/* line too long, attempt parsing anyways */
|
|
read_to = (uintptr_t)client_message + read_size;
|
|
} else {
|
|
/* line partially read, try reading more */
|
|
read_size = recv(sock, client_message, 36,
|
|
MSG_PEEK
|
|
#ifndef LOSSY
|
|
| MSG_WAITALL
|
|
#endif
|
|
);
|
|
client_message[read_size] = '\0';
|
|
read_to =
|
|
(uintptr_t)strchrnul(client_message, '\n');
|
|
}
|
|
}
|
|
read_to = read_to - (uintptr_t)client_message;
|
|
read_size = recv(sock, client_message, read_to + 1, 0);
|
|
client_message[read_size] = '\0';
|
|
|
|
command = safestrtok(client_message, " \n", &strtokptr);
|
|
|
|
if (!strcmp("PX", command)) {
|
|
int xpos = atoi(safestrtok(NULL, " \n", &strtokptr));
|
|
int ypos = atoi(safestrtok(NULL, " \n", &strtokptr));
|
|
char *colorcode = strtok_r(NULL, " \n", &strtokptr);
|
|
|
|
if (xpos >= 0 && ypos >= 0 && xpos < fb_width &&
|
|
ypos < fb_height) {
|
|
if (colorcode == NULL) {
|
|
message_len = sprintf(
|
|
message, "PX %i %i %06X\n", xpos,
|
|
ypos,
|
|
fbdata[ypos * fb_length + xpos]);
|
|
write(sock, message, message_len);
|
|
continue;
|
|
}
|
|
colorcode[fb_hexbytes] = '\0';
|
|
#ifdef ALPHA_AT_END
|
|
if (strlen(colorcode) < fb_hexbytes)
|
|
strcat(colorcode, "00");
|
|
#endif
|
|
int color = (int)strtol(colorcode, NULL, 16);
|
|
fbdata[ypos * fb_length + xpos] = color;
|
|
}
|
|
continue;
|
|
}
|
|
if (!strcmp("SIZE", command)) {
|
|
message_len = sprintf(message, "SIZE %i %i\n", fb_width,
|
|
fb_height);
|
|
write(sock, message, message_len);
|
|
continue;
|
|
}
|
|
if (!strcmp("HELP", command)) {
|
|
write(sock, "HELP\nSIZE\nPX x y [color]\n", 25);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
close(sock);
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, const char *argv[]) {
|
|
int port;
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
port = argc > 1 ? atoi(argv[1]) : 0;
|
|
if (!port)
|
|
port = 1234;
|
|
|
|
/* FIXME: probably should not hard-code this,
|
|
* there may be more than one framebuffer */
|
|
fbfd = open("/dev/fb0", O_RDWR);
|
|
|
|
if (fbfd < 0) {
|
|
perror("failed to open framebuffer");
|
|
return 2;
|
|
}
|
|
|
|
struct fb_var_screeninfo vinfo;
|
|
struct fb_fix_screeninfo finfo;
|
|
|
|
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);
|
|
ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo);
|
|
|
|
fb_width = vinfo.xres;
|
|
fb_height = vinfo.yres;
|
|
fb_bytes = vinfo.bits_per_pixel / 8;
|
|
fb_length = finfo.line_length / fb_bytes;
|
|
fb_hexbytes = fb_bytes * 2;
|
|
|
|
/* turn off cursor blinking in the tty */
|
|
printf("\033[?17;127cwidth: %i, height: %i, bpp: %i\n", fb_width,
|
|
fb_height, fb_bytes);
|
|
|
|
int fb_data_size = fb_length * fb_height * fb_bytes;
|
|
|
|
fbdata = mmap(0, fb_data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd,
|
|
(off_t)0);
|
|
|
|
/* clear the screen */
|
|
memset(fbdata, 0, fb_data_size);
|
|
|
|
int socket_desc, client_sock, c;
|
|
struct sockaddr_in6 server, client;
|
|
|
|
socket_desc = socket(AF_INET6, SOCK_STREAM, 0);
|
|
|
|
server.sin6_family = AF_INET6;
|
|
server.sin6_addr = in6addr_any;
|
|
server.sin6_port = htons(port);
|
|
|
|
{
|
|
/* disconnect idle clients */
|
|
struct timeval tv;
|
|
tv.tv_sec = 60;
|
|
tv.tv_usec = 0;
|
|
setsockopt(socket_desc, SOL_SOCKET, SO_RCVTIMEO,
|
|
(const char *)&tv, sizeof tv);
|
|
}
|
|
|
|
if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0) {
|
|
perror("failed to bind");
|
|
return 15;
|
|
}
|
|
|
|
listen(socket_desc, 100);
|
|
c = sizeof(struct sockaddr_in);
|
|
|
|
while ((client_sock = accept(socket_desc, (struct sockaddr *)&client,
|
|
(socklen_t *)&c))) {
|
|
pthread_t thread_id;
|
|
|
|
pthread_create(&thread_id, NULL, handle_connection,
|
|
(void *)&client_sock);
|
|
}
|
|
|
|
return 0;
|
|
}
|