#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LINUX_SECCOMP_H #include #include #include #include #include #include #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; } /* 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; } else { 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; }