// laim (0.2.5): a lame mail server // connect to a --boot laim with `ncat --ssl
` (requires nmap) // or `nc
` if not running under SSL (requires netcat or a busybox with the nc applet built) // // originally created by Moon Flower Fields (https://coffin.ir/) or @moonflowerfields on Telegram // very funny. #define COMEDIAN false #define SZ 128 #define EOF -1 #include #include #include #include #include typedef uint64_t u64; typedef uint16_t u16; typedef uint8_t u8; typedef const char *cstr; typedef u8 byte; struct linux_dirent64 { u64 d_ino; u64 d_off; u16 d_reclen; u8 d_type; char d_name[]; }; #define UCHAR_MAX (127 * 2 + 1) #define ALIGN (sizeof(size_t)) #define ONES ((size_t) -1 / UCHAR_MAX) #define HIGHS (ONES * (UCHAR_MAX / 2 + 1)) #define HASZERO(x) ((x) - ONES & ~(x) & HIGHS) size_t lenstr(cstr s) { const char *a = s; const size_t *w; for (; (uintptr_t) s % ALIGN; s++) if (!*s) return s - a; for (w = (const void *) s; !HASZERO(*w); w++); for (s = (const void *) w; *s; s++); return s - a; } char out_buf[ 4096 ]; int out_pos = 0; void flush() { if (out_pos == 0) return; // a branch instead of a syscall? in this economy?! write(STDOUT_FILENO, out_buf, out_pos); out_pos = 0; } void memcopy(char *s, cstr d, u64 len) { for (u64 i = 0; i < len; i++) s[ i ] = d[ i ]; } void writeb(cstr s, u64 len) { if (out_pos + len > (u64) sizeof(out_buf)) flush(); if (len > (u64) sizeof(out_buf)) { write(STDOUT_FILENO, s, len); return; } memcopy(out_buf + out_pos, s, len); out_pos += len; } void writes(cstr s) { writeb(s, lenstr(s)); } char in_buf[ 4096 ]; u64 in_pos = 0; u64 in_len = 0; int readn(char *buf, int n) { if (out_pos != 0) flush(); // a branch over a syscall? in this economy?! int avail = in_len - in_pos; if (avail > 0) { if (avail > n) avail = n; memcopy(buf, in_buf + in_pos, avail); in_pos += avail; return avail; } return read(STDIN_FILENO, buf, n); } int get_char() { if (in_pos == in_len) { in_len = readn(in_buf, sizeof(in_buf)); in_pos = 0; if (in_len <= 0) return EOF; } else if (out_pos != 0) flush(); int c = (unsigned char) in_buf[ in_pos++ ]; if (c == '\r') return get_char(); return c; } // burst into flames if uninitialized char *glob_buf = NULL; u64 glob_sz = 0; // clang-format off void sadd(cstr s) { char *prev = glob_buf; while (*s) *glob_buf++ = *s++; glob_sz += glob_buf - prev; } #define over __attribute__((overloadable)) void over sit(char *p, cstr s) { glob_buf = p; glob_sz = 0; sadd(s); } over sit(char *p, cstr s, cstr s2) { sit(p, s); sadd(s2); } over sit(char *p, cstr s, cstr s2, cstr s3) { sit(p, s); sadd(s2); sadd(s3); } void memzero(char *s, u64 len) { for (u64 i = 0; i < len; i++) s[ i ] = 0; } void nadd(u64 n) { if (!n) { *glob_buf++ = '0'; glob_sz++; return; } char tmp[ 20 ] = { 0 }; u8 i = 20; while (n) { tmp[ --i ] = '0' + (n % 10); n /= 10; } u8 len = 20 - i; memcopy(glob_buf, tmp + i, len); glob_buf += len; glob_sz += len; } void nlod(char *p, u64 n) { glob_buf = p; glob_sz = 0; nadd(n); } // clang-format on void print_u64(u64 n) { static char tmp[ 21 ] = { 0 }; nlod(tmp, n); writeb(tmp, glob_sz); } char *str_u64(u64 n) { static char s[ 21 ]; nlod(s, n); *glob_buf = 0; return s; } u64 scan_u64() { u64 n = 0; char c; while ((c = get_char()) != EOF) { if (c < '0' || c > '9') break; n = n * 10 + (c - '0'); } return n; } long syscall(long number, ...); u64 now() { struct timespec ts; syscall(228, 0, &ts); return ts.tv_sec; } u64 room_user_count(char *room_path) { static char members_path[ SZ ]; sit(members_path, room_path, ".members/"); *glob_buf = 0; int dir_fd = open(members_path, O_RDONLY | O_DIRECTORY); if (dir_fd < 0) return 0; static char buf[ 1024 ]; u64 count = 0; int n; while ((n = syscall(217, dir_fd, buf, sizeof(buf))) > 0) { for (int off = 0; off < n;) { struct linux_dirent64 *d = (struct linux_dirent64 *) (buf + off); if (d->d_name[ 0 ] != '.') { int fd = openat(dir_fd, d->d_name, O_RDONLY); if (fd >= 0) { if (flock(fd, LOCK_EX | LOCK_NB) == 0) { // dead owner flock(fd, LOCK_UN); unlinkat(dir_fd, d->d_name, 0); } else { count++; } close(fd); } } off += d->d_reclen; } } close(dir_fd); return count; } void list_users(int dir_fd, bool rooms) { static char buf[ 1024 ]; u64 n; while ((n = syscall(217, dir_fd, buf, 1024)) > 0) { for (u64 off = 0; off < n;) { struct linux_dirent64 *d; d = (struct linux_dirent64 *) (buf + off); cstr name = d->d_name; if (*name != '.' && rooms ^ (*name != '~')) { writeb(name, lenstr(name)); writes(" "); if (rooms) { static char rp[ SZ ]; sit(rp, "mail/", name, "/"); *glob_buf = 0; print_u64(room_user_count(rp)); } else { char lastlogin[ SZ ] = { 0 }; sit(lastlogin, "mail/", name, "/.login"); int login_fd = open(lastlogin, O_RDONLY); if (login_fd >= 0) { u64 ts; read(login_fd, &ts, 8); print_u64(ts); close(login_fd); } else { writes(" 0\n"); } } writes("\n"); } off += d->d_reclen; } } } int smake(char *name) { return open(name, O_RDWR | O_CREAT, 0600); } void slock(int fd) { flock(fd, LOCK_EX); } void sopen(int fd) { flock(fd, LOCK_UN); } #define err(a) \ ({ \ writes(a); \ continue; \ }) void printmail(char path[ SZ ], u64 after) { mkdir(path, 0700); // make it if it doesnt exist int dir_fd = open(path, O_RDONLY | O_DIRECTORY); if (dir_fd < 0) return; int cfd = openat(dir_fd, ".count", O_RDONLY); if (cfd < 0) { writes(";"); close(dir_fd); return; } u64 total = 0; read(cfd, &total, sizeof(u64)); close(cfd); for (u64 i = 0; i < total; i++) { char *name = str_u64(i); int mail_fd = openat(dir_fd, name, O_RDONLY); if (mail_fd < 0) continue; flock(mail_fd, LOCK_SH); struct stat st; fstat(mail_fd, &st); if (st.st_size == 0) { close(mail_fd); continue; } u64 ts = 0; char tsbuf[ 21 ]; u64 n = read(mail_fd, tsbuf, sizeof(tsbuf) - 1); if (n <= 0) { close(mail_fd); continue; } tsbuf[ n ] = 0; for (u64 j = 0; j < n; j++) { if (tsbuf[ j ] >= '0' && tsbuf[ j ] <= '9') ts = ts * 10 + (tsbuf[ j ] - '0'); else { lseek(mail_fd, j - n, SEEK_CUR); break; } } if (ts < after) { close(mail_fd); continue; } static char header[ SZ ] = { 0 }; nlod(header, i); *glob_buf++ = '\n'; writeb(header, ++glob_sz); nlod(header, ts); writeb(header, glob_sz); static char buf[ 256 ]; int r; while ((r = read(mail_fd, buf, sizeof(buf))) > 0) writeb(buf, r); writes("\n\neof\n"); close(mail_fd); } close(dir_fd); } u64 create_index(int dirfd) { int cfd = openat(dirfd, ".count", O_RDWR | O_CREAT, 0600); if (cfd < 0) return -1; u64 count = 0; read(cfd, &count, sizeof(u64)); count++; lseek(cfd, 0, SEEK_SET); if (write(cfd, &count, sizeof(u64)) != sizeof(u64)) { close(cfd); return -1; } ftruncate(cfd, sizeof(u64)); close(cfd); return count - 1; } const char motd[] = "a laim server!\n"; const char docs[] = { #embed "docs.txt" , 0 }; const char source[] = { #embed "laim.c" , 0 }; const char makefile[] = { #embed "Makefile" , 0 }; // used for both passwords and usernames. // not my fault if you wanna use a weird pass. bool is_path_char(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == ' ' || c == '$' || c == ':'; } #define read_field(buf, err, fail_action, len_buf) \ ({ \ bool _term = false; \ for (u8 _i = 0; _i < 64; _i++) { \ int _ch = get_char(); \ if (_ch == '\n' || _ch == EOF) { \ (buf)[ _i ] = 0; \ _term = true; \ if (len_buf != NULL) { \ u8 *ptr = len_buf; \ *ptr = _i; \ } \ break; \ } \ if (!is_path_char((char) _ch)) { \ writes(err); \ fail_action; \ } \ (buf)[ _i ] = (char) _ch; \ } \ if (!_term) { \ writes(err); \ fail_action; \ } \ }) // timing safe or whatever. bool safe_cmp(char *left, char *right, u64 size) { bool nonmatch = false; bool ended = false; for (u64 i = 0; i < size; i++) { nonmatch = ((*left != *right) && !ended) | nonmatch; ended = (*left == 0 || *right == 0) | ended; left++, right++; } return !nonmatch; } void openwrite(char *filename, cstr content) { int fd = open(filename, O_RDWR | O_CREAT, 0600); write(fd, content, lenstr(content)); close(fd); } char *filename; char *count_filename; int member_fd = -1; bool ephemeral = false; static char member_file[ SZ ] = { 0 }; void leave_room() { if (member_fd < 0) return; flock(member_fd, LOCK_UN); close(member_fd); member_fd = -1; unlink(member_file); if (ephemeral && room_user_count((char *) filename) == 0) { static char mp2[ SZ ]; sit(mp2, filename, ".members/"); *glob_buf = 0; rmdir(mp2); int cfd2 = open(count_filename, O_RDONLY); u64 total2 = 0; if (cfd2 >= 0) { read(cfd2, &total2, 8); close(cfd2); } for (u64 i = 0; i < total2; i++) { static char mp[ SZ ]; sit(mp, filename, str_u64(i)); *glob_buf = 0; unlink(mp); } unlink(count_filename); rmdir(filename); } ephemeral = false; } int main(int argc, char **argv) { char path[ 4096 ] = { 0 }; { u64 bye = readlink("/proc/self/exe", path, sizeof(path) - 1); } if (argc > 1) { if (safe_cmp(argv[ 1 ], "--help", 6) || safe_cmp(argv[ 1 ], "-h", 2)) { writes( "laim: lame mail server\n" "created by: moon (coffin.ir)\n" "\n" "laim is a mail server that operates on STDIO.\n" "it is meant to be run with a networking program, such as socat, or busybox's tcpsvd.\n" "it can be served over SSL using socat or busybox's ssl_server, which pipes TLS to I/O.\n" "\n" "usage:\n" " "); writes(argv[ 0 ]); writes( " --help\n" " shows this message (non-zero exit)\n" " "); writes(argv[ 0 ]); writes( " --open\n" " extracts the program's source code and build files (non-zero exit)\n" " "); writes(argv[ 0 ]); writes( " --boot \n" " re-runs laim, wrapped with ssl_server and tcpsvd, provided a busybox with those applets is " "installed.\n" "\n"); return 1; } else if (safe_cmp(argv[ 1 ], "--open", 6)) { openwrite("laim.c", source); openwrite("docs.txt", docs); openwrite("Makefile", makefile); writes("done\n"); return 1; } else if (safe_cmp(argv[ 1 ], "--boot", 6)) { if (argc != 5) { writes("too many / too little args, read --help\n"); return 1; } char *new_argv[] = { "tcpsvd", "-v", argv[ 2 ], argv[ 3 ], "ssl_server", "-f", argv[ 4 ], path }; execvp("busybox", new_argv); } else { writes("unknown arg\n"); return 1; } } char user[ 65 ] = { 0 }, pass[ 65 ] = { 0 }; u8 userlen = 0; writes("laim\2\n"); // manipulates: // users/ - user db, manually add users // mail/ - mails directory, each filename is a username mkdir("users", 0700); mkdir("mail", 0700); laim_start:; memzero(user, 65); memzero(pass, 65); int action = get_char(); if (action == 'M') { writes(motd); goto laim_start; } else if (action == 'L') { // username read_field(user, "4", goto laim_start, &userlen); // password read_field(pass, "5", goto laim_start, NULL); char user_path[ SZ ] = "users/"; memcopy(user_path + 6, user, userlen); user_path[ 6 + userlen ] = 0; if (access(user_path, F_OK) != 0) { writes("N"); goto laim_start; } int fd = open(user_path, O_RDONLY); if (fd < 0) { writes("err:fopen\n"); return 1; } char actual_pass[ 65 ] = { 0 }; int pass_size = read(fd, actual_pass, 64); actual_pass[ pass_size ] = 0; if (!safe_cmp(pass, actual_pass, 64)) { writes("I"); sleep(2); // punish user for null goto laim_start; } memzero(actual_pass, 65); close(fd); writes("O"); // only fallthrough } else if (action == EOF) { return 1; } else { writes("?"); goto laim_start; } // -- authorized from here on out -- { // write last login char last_login[ SZ ] = { 0 }; sit(last_login, "mail/", user, "/.login"); int login_fd = open(last_login, O_RDWR | O_CREAT, 0600); if (login_fd >= 0) { ftruncate(login_fd, 0); u64 ts = now(); write(login_fd, &ts, 8); close(login_fd); } } // remove password from memory memzero(pass, 65); // make sure user has a mail dir char user_filename[ SZ ] = { 0 }; sit(user_filename, "mail/", user, "/"); mkdir(user_filename, 0700); char user_count_filename[ SZ ] = { 0 }; sit(user_count_filename, user_filename, ".count"); static char room_path[ SZ ] = { 0 }; static char room_count_path[ SZ ] = { 0 }; filename = user_filename; count_filename = user_count_filename; int _c; while ((_c = get_char()) != EOF) { if (_c == '\n') continue; char c = _c; if (c == 'G') { // get all mail printmail(filename, 0); writes("O"); } else if (c == 'W') { // whoami if (COMEDIAN) writes("you're... you!"); else { writes(user); } writes("\n"); } else if (c == 'E') { // count inbox size, includes deleted mails int count_fd = open(count_filename, O_RDONLY); if (count_fd < 0) err("!"); u64 result = 0; u64 size = read(count_fd, &result, 8); if (size < 8) { close(count_fd); err("!"); } close(count_fd); print_u64(result); writes("\n"); } else if (c == 'B') { leave_room(); char room[ 64 + 1 ]; u8 len; read_field(room, "7", goto while_end, &len); if (len == 0) sit(room_path, "mail/~hub/"); else { sit(room_path, "mail/~", room, "/"); } ephemeral = (len > 0 && room[ 0 ] == ':'); *glob_buf = 0; mkdir(room_path, 0700); sit(room_count_path, room_path, ".count"); *glob_buf = 0; int cfd = open(room_count_path, O_RDWR | O_CREAT | O_EXCL, 0600); if (cfd >= 0) { u64 zero = 0; write(cfd, &zero, 8); close(cfd); } filename = room_path; count_filename = room_count_path; static char members_path[ SZ ]; sit(members_path, room_path, ".members/"); *glob_buf = 0; mkdir(members_path, 0700); u64 pid = syscall(39); // getpid sit(member_file, members_path, str_u64(pid)); *glob_buf = 0; member_fd = open(member_file, O_RDWR | O_CREAT, 0600); flock(member_fd, LOCK_EX); writes("O"); } else if (c == 'Q') { // turn off broadcast mode leave_room(); filename = user_filename; count_filename = user_count_filename; writes("O"); } else if (c == 'U' || c == 'R') { // unified listing int dir_fd = open(c == 'R' ? "mail/" : "users/", O_RDONLY | O_DIRECTORY); if (dir_fd < 0) { writes("err:dir\n"); continue; } list_users(dir_fd, c == 'R'); close(dir_fd); } else if (c == 'T') { // get mail after X printmail(filename, scan_u64()); } else if (c == 'S') { // send mail char target[ SZ ]; read_field(target, "7", goto while_end, NULL); char dest_path[ SZ ] = { 0 }; sit(dest_path, "mail/", target, "/"); mkdir(dest_path, 0700); int dir_fd = open(dest_path, O_RDONLY | O_DIRECTORY); if (dir_fd < 0) { writes("err:dir\n"); continue; } slock(dir_fd); u64 index = create_index(dir_fd); if (index == -1) { writes("!"); sopen(dir_fd); close(dir_fd); continue; } sit(dest_path, "mail/", target, "/"); nadd(index); int user_fd = smake(dest_path); if (user_fd < 0) { sopen(dir_fd); close(dir_fd); writes("err:smake\n"); continue; } slock(user_fd); sopen(dir_fd); close(dir_fd); char *ts = str_u64(now()); write(user_fd, ts, lenstr(ts)); write(user_fd, "\n", 1); write(user_fd, user, userlen); write(user_fd, "\n", 1); bool hit_limit = false; u64 total_size = 0; char buf[ 256 ] = { 0 }; int read_size; while ((read_size = readn(buf, sizeof(buf))) > 0) { write(user_fd, buf, read_size); total_size += read_size; if (total_size > 65536) { hit_limit = true; writes("."); break; } } if (!hit_limit) writes("O"); sopen(user_fd); close(user_fd); } else if (c == 'D') { // delete mail u64 index = scan_u64(); char dest_path[ SZ ] = { 0 }; sit(dest_path, filename); nadd(index); if (unlink(dest_path) < 0) err("8"); else { err("O"); } } else if (c == 'C') { // change password char newpass[ 65 ] = { 0 }, newpass2[ 65 ] = { 0 }; u8 passlen, passlen2; read_field(newpass, "6", goto while_end, &passlen); read_field(newpass2, "7", goto while_end, &passlen2); if (passlen != passlen2) err("K"); if (safe_cmp(newpass, newpass2, passlen)) err("K"); char user_path[ SZ ] = "users/"; memcopy(user_path + 6, user, userlen); user_path[ 6 + userlen ] = 0; int ufd = smake(user_path); if (ufd < 0) err("8"); slock(ufd); u8 newpass_len = lenstr(newpass); write(ufd, newpass, newpass_len); ftruncate(ufd, newpass_len); sopen(ufd); close(ufd); writes("O"); } else if (c == 'K') { // exit client break; } else { writes("?"); } while_end:; } return 0; }