Skip to content

Instantly share code, notes, and snippets.

@rlapz
Last active January 30, 2023 16:09
Show Gist options
  • Save rlapz/e116e88dbc604c20d002b5bdeab0530a to your computer and use it in GitHub Desktop.
Save rlapz/e116e88dbc604c20d002b5bdeab0530a to your computer and use it in GitHub Desktop.
An --ex-- extreme mail server without including any external library (for linux x86_64 only).
// SPDX-License-Identifier: GPL-2.0-only
/* ExMail (WIP)
* An --ex-- extreme mail server without including any external library
* (for linux x86_64 only).
*
* This simple mail-server aims to implements some of these RFCs:
* SMTP: https://datatracker.ietf.org/doc/html/rfc2821
* POP3: https://datatracker.ietf.org/doc/html/rfc1939
*
* ----------------------------------------------------------------------
* Compile:
* cc -std=c11 -static -ffreestanding -nostdlib -nostartfiles -O3 \
* exmail.c -o exmail;
*
* Run: [TODO]
* SMTP:
* ./exmail smtp [listen addr] [listen port]
*
* POP3
* ./exmail pop3 [listen addr] [listen port]
* ----------------------------------------------------------------------
*
* Copyright (C) 2022 Ammar Faizi <[email protected]>
* Copyright (C) 2023 Arthur Lapz <[email protected]>
*/
/*
* Bear with me, I want to do some experiments, expect stupid mistake(s).
* I can't guarantee this project will be finished. But I will do my best. Yeah!
*/
/* Mail dir scheme:
*
* mailbox/
* +rlapz/ <---------------------------------[ username ]
* | +inbox/ <-----------------------------[ received mail dir ]
* | | [email protected]/ <----------------[ src username@domain ]
* | | | +yyyymmddhhmmss.txt <----------[ mail content ]
* | | | +yyyymmddhhmmss.txt
* | | [email protected]/
* | | | +yyyymmddhhmmss.txt
* | +outbox/ <----------------------------[ sent mail dir ]
* | | [email protected]/ <------------[ target username@domain ]
* | | | +yyyymmddhhmmss.txt
* | +spam/
* | | [email protected]/
* | | | +yyyymmddhhmmss.txt
* | +trash/
* | | +stupid_user@spam_enjoyer.com/
* | | | +yyyymmddhhmmss.txt
*/
/* Sections:
* Entry point
* Config
* Compiler attributes
* Helpers
* Constants
* Types
* Syscall wrappers
* Misc.
* os
* mem
* Str
* cstr
* ascii
* fmt
* slots
* queue
* address
* socket
* DateTime
* mysql
* ---------------------
* TLS
* FSDNS
* FSCONF
* MailBox
* POP3
* SMTP
* ---------------------
* main
*/
/*
* Entry point:
*/
__asm__(
".global _start\n"
".section .text\n"
"_start:\n\t"
/* calling stupid_main function */
"popq %rdi\n\t"
"movq %rsp, %rsi\n\t"
"xorl %ebp, %ebp\n\t"
"andq $-16, %rsp\n\t"
"movq 0(%rsp), %rdi\n\t" /* argc */
"leaq 8(%rsp), %rsi\n\t" /* argv */
"callq stupid_main\n\t"
);
/****************************************************************************
* Config *
***************************************************************************/
#define MAIL_QUEUE_MAX 1024
#define SMTP_CLIENTS_SIZE 1024
#define POP3_CLIENTS_SIZE 1024
#define SMTP_IO_QUEUE_DEPTH 32
#define POP3_IO_QUEUE_DEPTH 32
#define DEFAULT_CONFIG_FILE "./exmail.fsconf"
#define DEFAULT_MAILBOX_DIR "./mailbox"
#define DEFAULT_HOST_NAME "localhost"
#define DEFAULT_MYSQL_HOST_PORT "127.0.0.1:3306"
#define DEFAULT_MYSQL_DB_NAME "exmail"
/*
* SMTP
*/
#define DEFAULT_SMTP_HOST_PORT "127.0.0.1:9002"
#define DEFAULT_SMTP_AUTH "PLAIN"
/*
* POP3
*/
#define DEFAULT_POP3_HOST_PORT "127.0.0.1:9003"
/* fsconf file, see: "FSCONF" section */
#define DEFAULT_CONFIG \
"mailbox_dir(" DEFAULT_MAILBOX_DIR ")\n" \
"host_name(" DEFAULT_HOST_NAME ")\n\n" \
"smtp_host_port(" DEFAULT_SMTP_HOST_PORT ")\n" \
"smtp_auth(" DEFAULT_SMTP_AUTH ")\n\n" \
"pop3_host_port(" DEFAULT_POP3_HOST_PORT ")\n\n" \
"mysql_db_name(" DEFAULT_MYSQL_DB_NAME ")\n\n" \
"mysql_host_port(" DEFAULT_MYSQL_HOST_PORT ")\n"
/****************************************************************************
* Compiler attributes *
***************************************************************************/
#define INLINE __attribute__((__always_inline__)) inline
#define NOINLINE __attribute__((__noinline__))
#define COLD __attribute__((__cold__))
#define HOT __attribute__((__hot__))
#define USED __attribute__((__used__))
#define PACKED __attribute__((__packed__))
#define ALIGN(X) __attribute__((__aligned__(X)))
#define UNREACHABLE __builtin_unreachable()
#define LIKELY(X) __builtin_expect(!!(X), 1)
#define UNLIKELY(X) __builtin_expect(!!(X), 0)
/****************************************************************************
* Helpers *
***************************************************************************/
#define va_start(VA, args) __builtin_va_start(VA, args)
#define va_arg(VA, type) __builtin_va_arg(VA, type)
#define va_end(VA) __builtin_va_end(VA)
#define os_error1(NUM) UNLIKELY(os_error(NUM))
#ifdef NDEBUG
#define DPRINT(...)
#define PERROR(ERRNO, MSG) \
fmt_printfd(print_buffer_g, PRINT_BUFFER_SIZE, \
STDERR_FILENO, \
"Error: {s}: " MSG ": {s}({u})\n", \
__func__, cstr_error((int)ERRNO), ERRNO)
#else
#define DPRINT(...) \
fmt_printfd(print_buffer_g, PRINT_BUFFER_SIZE, \
STDOUT_FILENO, __VA_ARGS__)
#define PERROR(ERRNO, MSG) \
fmt_printfd(print_buffer_g, PRINT_BUFFER_SIZE, \
STDERR_FILENO, \
"Error: {s}({u}): " MSG ": {s}({u})\n", \
__func__, __LINE__, cstr_error((int)ERRNO), \
ERRNO)
#endif
/*
* Syscalls (linux x86_64)
*/
#define SYS0(NUM)({ \
isize rax; \
__asm__ volatile( \
"syscall" \
: "=a"(rax) /* %rax */ \
: "a"((NUM)) /* %rax */ \
: "rcx", "r11", "memory" \
); \
rax; \
})
#define SYS1(NUM, A)({ \
isize rax; \
__asm__ volatile( \
"syscall" \
: "=a"(rax) /* %rax */ \
: "a"((NUM)), /* %rax */ \
"D"((A)) /* %rdi */ \
: "rcx", "r11", "memory" \
); \
rax; \
})
#define SYS2(NUM, A, B)({ \
isize rax; \
__asm__ volatile( \
"syscall" \
: "=a"(rax) /* %rax */ \
: "a"((NUM)), /* %rax */ \
"D"((A)), /* %rdi */ \
"S"((B)) /* %rsi */ \
: "rcx", "r11", "memory" \
); \
rax; \
})
#define SYS3(NUM, A, B, C)({ \
isize rax; \
__asm__ volatile( \
"syscall" \
: "=a"(rax) /* %rax */ \
: "a"((NUM)), /* %rax */ \
"D"((A)), /* %rdi */ \
"S"((B)), /* %rsi */ \
"d"((C)) /* %rdx */ \
: "rcx", "r11", "memory" \
); \
rax; \
})
#define SYS4(NUM, A, B, C, D)({ \
isize rax; \
register __typeof__(D) r10 __asm__("r10") = (D); \
\
__asm__ volatile( \
"syscall" \
: "=a"(rax) /* %rax */ \
: "a"((NUM)), /* %rax */ \
"D"((A)), /* %rdi */ \
"S"((B)), /* %rsi */ \
"d"((C)), /* %rdx */ \
"r"(r10) /* %r10 */ \
: "rcx", "r11", "memory" \
); \
rax; \
})
#define SYS5(NUM, A, B, C, D, E)({ \
isize rax; \
register __typeof__(D) r10 __asm__("r10") = (D); \
register __typeof__(E) r8 __asm__("r8") = (E); \
\
__asm__ volatile( \
"syscall" \
: "=a"(rax) /* %rax */ \
: "a"((NUM)), /* %rax */ \
"D"((A)), /* %rdi */ \
"S"((B)), /* %rsi */ \
"d"((C)), /* %rdx */ \
"r"(r10), /* %r10 */ \
"r"(r8) /* %r8 */ \
: "rcx", "r11", "memory" \
); \
rax; \
})
#define SYS6(NUM, A, B, C, D, E, F)({ \
isize rax; \
register __typeof__(D) r10 __asm__("r10") = (D); \
register __typeof__(E) r8 __asm__("r8") = (E); \
register __typeof__(F) r9 __asm__("r9") = (F); \
\
__asm__ volatile( \
"syscall" \
: "=a"(rax) /* %rax */ \
: "a"((NUM)), /* %rax */ \
"D"((A)), /* %rdi */ \
"S"((B)), /* %rsi */ \
"d"((C)), /* %rdx */ \
"r"(r10), /* %r10 */ \
"r"(r8), /* %r8 */ \
"r"(r9) /* %r9 */ \
: "rcx", "r11", "memory" \
); \
rax; \
})
/****************************************************************************
* Constants *
***************************************************************************/
enum {
AF_UNSPECT,
AF_LOCAL,
AF_INET4,
AF_INET6 = 10,
};
enum {
/* TODO */
F_SETF,
};
#define INADDR_ANY 0x0
#define INET4_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
enum {
IPPROTO_TCP = 6,
IPPROTO_UDP = 17,
};
enum {
MAP_SHARED = 0x1,
MAP_PRIVATE = 0x2,
MAP_FIXED = 0x10,
MAP_ANONYMOUS = 0x20,
};
enum {
MSG_PEEK = 0x2,
MSG_DONTWAIT = 0x40,
};
enum {
O_RDONLY,
O_WRONLY = 01,
O_RDWR = 02,
O_CREAT = 0100,
O_TRUNC = 0200,
O_DIRECTORY = 0200000,
O_CLOEXEC = 02000000,
};
enum {
POLL_IN = 0x01,
POLL_PRI = 0x02,
POLL_OUT = 0x04,
POLL_ERR = 0x08,
POLL_HUP = 0x10,
};
enum {
PROT_NONE,
PROT_READ,
PROT_WRITE,
PROT_EXEC = 0x4,
};
/* TODO */
enum {
SEEK_SET,
SEEK_CUR,
SEEK_END,
};
enum {
SHUT_RD,
SHUT_WR,
SHUT_RDWR,
};
enum {
SO_REUSEADDR = 2,
};
enum {
SOCK_STREAM = 1,
SOCK_DGRAM,
SOCK_RAW,
SOCK_NONBLOCK = 04000,
};
enum {
SOL_SOCKET = 1,
};
enum {
STDIN_FILENO,
STDOUT_FILENO,
STDERR_FILENO,
};
enum {
TCP_NODELAY = 1,
TCP_CORK,
TCP_QUICKACK = 12,
};
/*
* Error numbers
*/
enum {
SUCCESS = 0,
EPERM = 1,
ENOENT = 2,
EINTR = 4,
EBADF = 9,
EAGAIN = 11,
ENOMEM = 12,
EACCES = 13,
EFAULT = 14,
EINVAL = 22,
ENOTSOCK = 88,
ENOPROTOOPT = 92,
EADDRNOTAVAIL = 99,
};
/*
* Syscall numbers (linux x86_64)
* Ref: https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl
*/
enum {
SYS_READ = 0,
SYS_WRITE,
SYS_OPEN,
SYS_CLOSE,
SYS_STAT = 4,
SYS_FSTAT = 5,
SYS_POLL = 7,
SYS_LSEEK,
SYS_MMAP,
SYS_MUNMAP = 11,
SYS_RT_SIGACTION = 13,
SYS_IOCTL = 16,
SYS_MSYNC = 26,
SYS_SOCKET = 41,
SYS_CONNECT,
SYS_ACCEPT,
SYS_SHUTDOWN = 48,
SYS_BIND,
SYS_LISTEN,
SYS_SETSOCKOPT = 54,
SYS_FORK = 57,
SYS_EXECVE = 59,
SYS_EXIT = 60,
SYS_MSGSND = 69, /* Nice! */
SYS_FCNTL = 72,
SYS_TIME = 201,
SYS_GETRANDOM = 318,
};
/****************************************************************************
* Types *
***************************************************************************/
typedef unsigned long usize;
typedef unsigned long long u64;
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
typedef signed long isize;
typedef signed long long i64;
typedef signed int i32;
typedef signed short i16;
typedef signed char i8;
typedef u32 bool;
#define false 0
#define true 1
#define NULL ((void *)0)
#define noreturn _Noreturn
typedef __builtin_va_list va_list;
typedef isize Time; /* time_t */
typedef struct {
u64 __val[1024 / (8 * sizeof(u64))];
} SigSet; /* sigset_t */
typedef struct {
union {
void (*handler)(int);
};
SigSet mask;
int flags;
void (*restorer)(void);
} SigAction; /* struct sigaction */
typedef struct {
i64 sec;
i64 nsec;
} TimeSpec; /* timespec */
typedef struct {
void *base;
usize len;
} IOVec; /* struct iovec */
typedef struct {
void *name;
u32 name_len;
IOVec *iov;
usize iov_len;
void *control;
usize control_len;
int flags;
} MsgHdr; /* struct msghdr */
typedef struct {
u32 addr;
} Address4;
typedef struct {
u8 addr[16];
} Address6;
typedef struct {
u16 family;
u8 data[14];
} SockAddress; /* struct sockaddr */
#define SS_MAX_SIZE 128
typedef struct {
u16 family ALIGN(8);
u8 padding[SS_MAX_SIZE - sizeof(u16)];
} SockAddressStorage; /* struct sockaddr_storage */
typedef struct {
u16 family;
u16 port;
Address4 addr;
u8 zero[8];
} SockAddressIn4; /* struct sockaddr_in */
typedef struct {
u16 family;
u16 port;
u32 flow_info;
Address6 addr;
u32 scope_id;
} SockAddressIn6; /* struct sockaddr_in6 */
typedef union {
SockAddress any;
SockAddressIn4 in4;
SockAddressIn6 in6;
/* TODO: unix sockaddr */
} Address; /* struct sockaddr */
typedef struct {
u64 dev;
u64 ino;
usize nlink;
u32 mode;
u32 uid;
u32 gid;
u32 __pad0;
u64 rdev;
i64 size;
isize blksize;
i64 blocks;
TimeSpec atime;
TimeSpec mtime;
TimeSpec ctime;
isize __dont_use[3];
} Stat; /* struct stat */
typedef struct {
int fd;
i16 events;
i16 revents;
} PollFd; /* struct pollfd */
/***************************************************************************
* Syscall wrappers *
**************************************************************************/
static inline isize sys_read(int fd, void *buffer, usize count);
static inline isize sys_write(int fd, const void *buffer, usize count);
static inline isize sys_open(const char pathname[], int flags, u32 mode);
static inline isize sys_close(int fd);
static inline isize sys_stat(const char pathname[], Stat *stat);
static inline isize sys_fstat(int fd, Stat *stat);
static inline isize sys_poll(PollFd *pfds, usize size, i32 timeout);
static inline isize sys_lseek(int fd, i64 offset, int whence);
static inline isize sys_mmap(void *addr, usize len, int prot, int flags,
int fd, i64 offt);
static inline isize sys_munmap(void *addr, usize len);
static inline isize sys_sigaction(int num, const SigAction *act,
SigAction *old);
static inline isize sys_ioctl(int fd, u32 request, usize arg);
static inline isize sys_msync(void *addr, usize len, int flags);
static inline isize sys_socket(int domain, int type, int proto);
static inline isize sys_connect(int fd, const SockAddress *addr, u32 len);
static inline isize sys_accept(int fd, SockAddress *restrict addr,
u32 *restrict len);
static inline isize sys_shutdown(int fd, u32 how);
static inline isize sys_bind(int fd, const SockAddress *addr, u32 len);
static inline isize sys_listen(int fd, int backlog);
static inline isize sys_setsockopt(int fd, int level, int optname,
const void *optval, u32 len);
static inline isize sys_fork(void);
static inline isize sys_execve(const char pathname[], char *const argv[],
char *const envp[]);
static inline isize sys_exit(int ret);
static inline isize sys_fcntl(int fd, int cmd, usize arg);
static inline isize sys_time(Time *time);
static inline isize sys_getrandom(void *buffer, usize len, u32 flags);
/***************************************************************************
* Misc. *
***************************************************************************/
/*
* os
*/
/* only for sys_* function */
static inline bool os_error(isize num);
/*
* mem
*/
typedef struct {
usize size;
u8 mem[];
} MemHeap;
static int mem_alloc(void **dest, usize size);
static void mem_free(void *mem);
static void mem_copy(void *dest, const void *src, usize size);
static void mem_set(void *dest, u8 c, usize size);
static inline u16 mem_beton16(u16 num);
static inline u16 mem_ntobe16(u16 num);
static inline u32 mem_beton32(u32 num);
static inline u32 mem_ntobe32(u32 num);
/*
* Str
*
* size: buffer size including '\0' byte
*/
typedef struct {
bool is_heap;
usize len;
usize size;
char *cstr;
} Str;
static void str_init(Str *self, char buffer[], usize size);
static int str_init_alloc(Str *self, usize size);
static void str_deinit(Str *self);
static const char *str_append(Str *self, const char src[]);
static const char *str_fmt_append(Str *self, const char fmt[], ...);
static int str_shrink(Str *self, usize size);
/*
* cstr
*
* NOTE:
* @size: size of `buffer`
* @*size: size of `buffer`, and will returns the length of "written" bytes
* excluding '\0'
* ret: int: error: returns <0 otherwise success
* ptr: error: returns NULL otherwise success
*/
static void cstr_copy(char dest[], usize size, const char src[]);
static int cstr_copy1(char dest[], usize *size, const char src[]);
static usize cstr_len(const char cstr[]);
static inline void cstr_printc(char c);
static void cstr_print(const char cstr[]);
static const char *cstr_find_char(const char cstr[], u8 c);
static const char *cstr_find(const char hy[], const char nd[]);
static bool cstr_eql(const char cstr1[], const char cstr2[]);
static bool cstr_eql1(const char cstr1[], const char cstr2[],
usize len);
static void cstr_reverse(char cstr[]);
static int cstr_to_u64(const char cstr[], u64 *ret);
static int cstr_to_i64(const char cstr[], i64 *ret);
static int cstr_from_u64(char buffer[], usize *size, u64 num);
static int cstr_from_i64(char buffer[], usize *size, i64 num);
COLD static const char *cstr_error(int errnum);
/*
* ascii
*/
static inline bool ascii_chk(u8 c);
static inline u8 ascii_from(u8 c);
static inline bool ascii_isspace(u8 c);
static inline bool ascii_isdigit(u8 c);
static inline bool ascii_isxdigit(u8 c);
static inline bool ascii_islower(u8 c);
static inline bool ascii_isupper(u8 c);
static inline u8 ascii_tolower(u8 c);
static inline u8 ascii_toupper(u8 c);
/*
* fmt: a simple and footgunny string formatter
* (these simple implementations does not perform strict exceptions nor
* support escape character)
*
* format arguments:
* {i} => signed number (int)
* {u} => unsigned number (unsigned int)
* {s} => string
* {c} => char
*
* example:
* fmt_printfd(buffer, 120, STDOUT_FILENO, "{s}: {u}\n", "num", 123);
* result:
* "num: 123\n"
*
* NOTE:
* size:
* input : buffer length including '\0'
* output: returns "written" bytes excluding '\0'
* ret: returns 0 on sucess, <0 on error
*/
static int fmt_formatv(char buffer[], usize *size, const char fmt[],
va_list va);
static void fmt_printfd(char buffer[], usize size, int fd,
const char fmt[], ...);
static int fmt_format(char buffer[], usize *size, const char fmt[], ...);
/*
* slots
*/
typedef struct {
u32 count;
u32 size;
u32 *array;
} Slots;
static int slots_init(Slots *self, u32 size);
static void slots_deinit(Slots *self);
static inline int slots_push(Slots *self, u32 udata);
static inline i64 slots_pop(Slots *self);
/*
* queue; ring
*/
typedef struct {
u32 head;
u32 tail;
u32 count;
u32 nmemb;
u32 size;
void *array;
} Queue;
static int queue_init(Queue *self, u32 nmemb, u32 size);
static void queue_deinit(Queue *self);
static inline void *queue_sqe(Queue *self);
static inline void *queue_peek(Queue *self);
static inline void queue_seen(Queue *self);
/*
* address
*/
static int address4_from(Address4 *self, const char addr[]);
static int address6_from(Address6 *self, const char addr[]);
static int address4_to(const Address4 *self, char buf[INET4_ADDRSTRLEN]);
static int address6_to(const Address6 *self, char buf[INET6_ADDRSTRLEN]);
/*
* socket
*/
static int socket_listener(const char addr[], u16 port);
/*
* DateTime
*/
typedef struct {
u8 month;
u8 day;
u8 hour;
u8 minute;
u8 second;
u16 year;
} DateTime;
static int datetime_get(DateTime *self);
/*
* mysql
*/
#define MYSQL_ARGS_COUNT 32
typedef struct {
char host[64];
char port[6];
char name[256];
char uname[256];
char passwd[256];
u32 args_len;
char *args[MYSQL_ARGS_COUNT];
} MySQL;
static void mysql_init(MySQL *self, const char host[], const char port[],
const char name[], const char uname[], const char passwd[]);
static int mysql_prep(MySQL *self, u32 count, ...);
static int mysql_exec(MySQL *self);
/****************************************************************************
* TLS *
***************************************************************************/
/****************************************************************************
* FSDNS *
***************************************************************************/
/* A f*cking simple DNS resolver */
typedef struct {
} FsDns;
static int fsdns_resolve(const char addr[], const char port[]);
/***************************************************************************
* FSCONF *
**************************************************************************/
/* A f*cking simple, error prone, and slow key-value-based file configuration
*
* File name: exmail.fsconf
* ------------------------
* key0(value)\n
* key1(value)\n
* ------------------------
*
* Example:
* ------------------------
* mailbox_dir(mailbox)\n
* smtp_host_port(127.0.0.1:9002)\n
* pop3_host_port(127.0.0.1:9003)\n
* mysql_host_port(127.0.0.1:9004)\n
* ------------------------
* NOTE: '\n' is a must!
*/
static int fsconf_get(char dest[], usize size, const char src[],
const char key[]);
/****************************************************************************
* MailBox *
***************************************************************************/
typedef enum {
MAILBOX_INBOX,
MAILBOX_OUTBOX,
MAILBOX_SPAM,
MAILBOX_TRASH,
} MailBox;
/*
* ud: username@domain
*/
static int mailbox_add(MailBox self, const char ud[], const char mail[],
usize len);
static int mailbox_get(MailBox self, const char ud[], char mail[], usize size);
/****************************************************************************
* POP3 *
***************************************************************************/
typedef struct {
} Pop3Packet;
typedef struct {
Pop3Packet packet;
} Pop3Client;
typedef struct {
bool is_alive;
int sock_fd;
Slots client_slots;
usize clients_count;
Pop3Client clients[POP3_CLIENTS_SIZE];
} Pop3;
static int pop3_init(Pop3 *self, const char addr[], const char port[]);
static void pop3_deinit(Pop3 *self);
static int pop3_run(Pop3 *self);
static void pop3_stop(Pop3 *self);
/****************************************************************************
* SMTP *
***************************************************************************/
typedef struct {
} SmtpPacket;
typedef struct {
SmtpPacket packet;
} SmtpClient;
typedef struct {
bool is_alive;
int dns_fd;
int sock_fd;
Slots client_slots;
usize clients_count;
SmtpClient clients[SMTP_CLIENTS_SIZE];
} Smtp;
static int smtp_init(Smtp *self, const char addr[], const char port[]);
static void smtp_deinit(Smtp *self);
static int smtp_run(Smtp *self);
static void smtp_stop(Smtp *self);
/***************************************************************************
* Global variables *
**************************************************************************/
#define PRINT_BUFFER_SIZE 8192
static char print_buffer_g[PRINT_BUFFER_SIZE];
static const char *mailbox_dir_g = DEFAULT_MAILBOX_DIR;
/*
############################################################################
############################################################################
# IMPLEMENTATIONS #
############################################################################
############################################################################
*/
/***************************************************************************
* Syscall wrappers *
**************************************************************************/
static inline isize
sys_read(int fd, void *buffer, usize count)
{
return SYS3(SYS_READ, fd, buffer, count);
}
static inline isize
sys_write(int fd, const void *buffer, usize count)
{
return SYS3(SYS_WRITE, fd, buffer, count);
}
static inline isize
sys_open(const char pathname[], int flags, u32 mode)
{
return SYS3(SYS_OPEN, pathname, flags, mode);
}
static inline isize
sys_close(int fd)
{
return SYS1(SYS_CLOSE, fd);
}
static inline isize
sys_stat(const char pathname[], Stat *stat)
{
return SYS2(SYS_STAT, pathname, stat);
}
static inline isize
sys_fstat(int fd, Stat *stat)
{
return SYS2(SYS_FSTAT, fd, stat);
}
static inline isize
sys_poll(PollFd *pfds, usize size, i32 timeout)
{
return SYS3(SYS_POLL, pfds, size, timeout);
}
static inline isize
sys_lseek(int fd, i64 offset, int whence)
{
return SYS3(SYS_LSEEK, fd, offset, whence);
}
static inline isize
sys_mmap(void *addr, usize len, int prot, int flags, int fd, i64 offt)
{
return SYS6(SYS_MMAP, addr, len, prot, flags, fd, offt);
}
static inline isize
sys_munmap(void *addr, usize len)
{
return SYS2(SYS_MUNMAP, addr, len);
}
static inline isize
sys_sigaction(int num, const SigAction *act, SigAction *old)
{
return SYS3(SYS_RT_SIGACTION, num, act, old);
}
static inline isize
sys_ioctl(int fd, u32 request, usize arg)
{
return SYS3(SYS_IOCTL, fd, request, arg);
}
static inline isize
sys_msync(void *addr, usize len, int flags)
{
return SYS3(SYS_MSYNC, addr, len, flags);
}
static inline isize
sys_socket(int domain, int type, int proto)
{
return SYS3(SYS_SOCKET, domain, type, proto);
}
static inline isize
sys_connect(int fd, const SockAddress *addr, u32 len)
{
return SYS3(SYS_CONNECT, fd, addr, len);
}
static inline isize
sys_accept(int fd, SockAddress *restrict addr, u32 *restrict len)
{
return SYS3(SYS_ACCEPT, fd, addr, len);
}
static inline isize
sys_shutdown(int fd, u32 how)
{
return SYS2(SYS_SHUTDOWN, fd, how);
}
static inline isize
sys_bind(int fd, const SockAddress *addr, u32 len)
{
return SYS3(SYS_BIND, fd, addr, len);
}
static inline isize
sys_listen(int fd, int backlog)
{
return SYS2(SYS_LISTEN, fd, backlog);
}
static inline isize
sys_setsockopt(int fd, int level, int optname, const void *optval, u32 len)
{
return SYS5(SYS_SETSOCKOPT, fd, level, optname, optval, len);
}
static inline isize
sys_fork(void)
{
return SYS0(SYS_FORK);
}
static inline isize
sys_execve(const char pathname[], char *const argv[], char *const envp[])
{
return SYS3(SYS_EXECVE, pathname, argv, envp);
}
static inline isize
sys_exit(int ret)
{
return SYS1(SYS_EXIT, ret);
}
static inline isize
sys_fcntl(int fd, int cmd, usize arg)
{
return SYS3(SYS_FCNTL, fd, cmd, arg);
}
static inline isize
sys_time(Time *time)
{
return SYS1(SYS_TIME, time);
}
static inline isize
sys_getrandom(void *buffer, usize len, u32 flags)
{
return SYS3(SYS_GETRANDOM, buffer, len, flags);
}
/***************************************************************************
* Misc. *
***************************************************************************/
/*
* os
*/
static INLINE bool
os_error(isize num)
{
return (num > -4096 && num < 0);
}
/*
* mem
*/
static int
mem_alloc(void **dest, usize size)
{
const usize new_size = sizeof(MemHeap) + size;
const isize mem = sys_mmap(NULL, new_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (os_error1(mem))
return (int)mem;
MemHeap *const heap = (MemHeap *)mem;
heap->size = new_size;
*dest = heap->mem;
return 0;
}
#define mem_alloc1(dest, size) mem_alloc((void **)dest, size)
static void
mem_free(void *mem)
{
MemHeap *const heap = (MemHeap *)(((u8 *)mem) - sizeof(MemHeap));
const usize size = heap->size;
(void)sys_munmap(heap, size);
}
static void
mem_copy(void *dest, const void *src, usize size)
{
u8 *const dest_ = (u8 *)dest;
const u8 *const src_ = (const u8 *)src;
for (usize i = 0; i < size; i++)
dest_[i] = src_[i];
}
static void
mem_set(void *dest, u8 c, usize size)
{
u8 *const dest_ = (u8 *)dest;
while (size--)
dest_[size] = c;
}
static inline u16
mem_beton16(u16 num)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return __builtin_bswap16(num);
#else
return num;
#endif
}
static inline u16
mem_ntobe16(u16 num)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return __builtin_bswap16(num);
#else
return num;
#endif
}
static inline u32
mem_beton32(u32 num)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return __builtin_bswap32(num);
#else
return num;
#endif
}
static inline u32
mem_ntobe32(u32 num)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return __builtin_bswap32(num);
#else
return num;
#endif
}
/*
* Str
*/
static void
str_init(Str *self, char buffer[], usize size)
{
self->len = 0;
self->size = size;
self->cstr = buffer;
self->is_heap = false;
}
static int
str_init_alloc(Str *self, usize size)
{
str_init(self, NULL, size);
const int ret = mem_alloc1(&self->cstr, size);
if (ret < 0)
return ret;
self->is_heap = true;
return 0;
}
static void
str_deinit(Str *self)
{
if (self->is_heap)
mem_free(self->cstr);
self->len = 0;
}
static const char *
str_append(Str *self, const char src[])
{
char *const cstr = self->cstr;
const usize size = self->size;
const usize tmp_len = self->len;
usize len = tmp_len;
while ((len < size) && (*src != '\0'))
cstr[len++] = *src++;
if (*src != '\0') {
cstr[tmp_len] = '\0';
return NULL;
}
cstr[len] = '\0';
self->len = len;
return cstr;
}
static const char *
str_fmt_append(Str *self, const char fmt[], ...)
{
int ret = -1;
va_list va;
const usize size = self->size;
usize len = self->len;
usize w_len = size - len;
if (len < size) {
va_start(va, fmt);
ret = fmt_formatv(self->cstr + len, &w_len, fmt, va);
va_end(va);
}
if (ret < 0)
return NULL;
self->len += w_len;
return self->cstr;
}
static int
str_shrink(Str *self, usize size)
{
usize len = self->len;
if (size <= len) {
len -= size;
self->len = len;
self->cstr[len] = '\0';
return 0;
}
return -EINVAL;
}
/*
* cstr
*/
static void
cstr_copy(char dest[], usize size, const char src[])
{
usize i = 0;
for (; (i < size && *src != '\0'); i++, src++)
dest[i] = *src;
dest[i] = '\0';
}
static int
cstr_copy1(char dest[], usize *size, const char src[])
{
usize size_ = *size;
if (size_ == 0)
return -ENOMEM;
size_--;
usize i = 0;
for (; (i < size_ && *src != '\0'); i++, src++)
dest[i] = *src;
dest[i] = '\0';
*size = i;
if (*src == '\0')
return 0;
return -ENOMEM;
}
static usize
cstr_len(const char cstr[])
{
usize len = 0;
while (cstr[len] != '\0')
len++;
return len;
}
static inline void
cstr_printc(char c)
{
(void)sys_write(STDOUT_FILENO, &c, 1);
}
static void
cstr_print(const char cstr[])
{
const usize len = cstr_len(cstr);
usize wrt = 0;
while (wrt < len) {
isize wr = sys_write(STDOUT_FILENO, cstr + wrt, len - wrt);
if (os_error1(wr))
break;
wrt += (usize)wr;
}
}
static const char *
cstr_find_char(const char cstr[], u8 c)
{
for (; *cstr != '\0'; cstr++) {
if (*cstr == c)
return cstr;
}
return NULL;
}
static const char *
cstr_find(const char hy[], const char nd[])
{
const char *ret = NULL;
const char *tmp = nd;
usize found = 0;
for (; (*hy != '\0') && (*tmp != '\0'); hy++) {
if (*hy != *tmp) {
tmp = nd;
found = 0;
continue;
}
found++;
if (found == 1)
ret = hy;
tmp++;
}
if (((tmp - nd) == found) && (*tmp == '\0'))
return ret;
return NULL;
}
static bool
cstr_eql(const char cstr1[], const char cstr2[])
{
while ((*cstr1 != '\0') && (*cstr2 != '\0')) {
if (*cstr1 != *cstr2)
return false;
cstr1++;
cstr2++;
}
/* both should have the same length */
if ((*cstr1 == '\0') && (*cstr2 == '\0'))
return true;
return false;
}
static bool
cstr_eql1(const char cstr1[], const char cstr2[], usize len)
{
while ((len != 0) && (*cstr1 != '\0') && (*cstr2 != '\0')) {
if (*cstr1 != *cstr2)
return false;
cstr1++;
cstr2++;
len--;
}
if (len != 0)
return false;
return true;
}
static void
cstr_reverse(char cstr[])
{
if (*cstr == '\0')
return;
char tmp;
usize len = cstr_len(cstr) -1;
for (usize i = 0; i < len; i++, len--) {
tmp = cstr[i];
cstr[i] = cstr[len];
cstr[len] = tmp;
}
}
static int
cstr_to_u64(const char cstr[], u64 *ret)
{
if (*cstr == '\0')
return -EINVAL;
u64 tmp = 0;
for (; *cstr != '\0'; cstr++) {
if (ascii_isdigit(*cstr)) {
tmp = (10 * tmp) + (*cstr - '0');
continue;
}
break;
}
if (*cstr != '\0')
return -EINVAL;
*ret = tmp;
return 0;
}
static int
cstr_to_i64(const char cstr[], i64 *ret)
{
if (*cstr == '\0')
return -EINVAL;
u64 tmp;
bool has_sign = false;
if (*cstr == '-') {
has_sign = true;
cstr++;
if (*cstr == '\0')
return -EINVAL;
}
const int ret_ = cstr_to_u64(cstr, &tmp);
if (ret_ < 0)
return ret_;
i64 tmp_ = (i64)tmp;
if (has_sign)
tmp_ = -tmp_;
*ret = tmp_;
return 0;
}
static int
cstr_from_u64(char buffer[], usize *size, u64 num)
{
usize size_ = *size;
if (size_ < 2)
return -ENOMEM;
size_--;
u64 res;
usize i = 0;
do {
res = num % 10;
buffer[i++] = '0' + res;
num /= 10;
} while ((i < size_) && (num != 0));
if (num != 0)
return -ENOMEM;
buffer[i] = '\0';
cstr_reverse(buffer);
*size = i;
return 0;
}
static int
cstr_from_i64(char buffer[], usize *size, i64 num)
{
usize size_ = *size;
if (size_ < 2)
return -ENOMEM;
u64 tmp;
bool has_sign = false;
if (num < 0) {
*buffer = '-';
buffer++;
size_--;
tmp = (u64)-num;
has_sign = true;
} else {
tmp = (u64)num;
}
const int ret = cstr_from_u64(buffer, &size_, tmp);
if (ret < 0)
return ret;
if (has_sign)
size_++;
*size = size_;
return 0;
}
COLD static const char *
cstr_error(int errnum)
{
switch (errnum) {
case SUCCESS: return "Success";
case EPERM: return "Operation not permitted";
case ENOENT: return "No such file or directory";
case EINTR: return "Interrupted system call";
case EBADF: return "Bad file descriptor";
case EAGAIN: return "Resource temporarily unavailable";
case ENOMEM: return "Cannot allocate memory";
case EACCES: return "Permission denied";
case EFAULT: return "Bad request";
case EINVAL: return "Invalid argument";
case ENOTSOCK: return "Socket operation on non-socket";
case ENOPROTOOPT: return "Protocol not available";
case EADDRNOTAVAIL: return "Cannot assign requested address";
}
return "Unknown error";
}
/*
* ascii
*/
static inline bool
ascii_chk(u8 c)
{
return (c < 128);
}
static inline u8
ascii_from(u8 c)
{
return (c & 127);
}
static inline bool
ascii_isspace(u8 c)
{
return (c == ' ');
}
static inline bool
ascii_isdigit(u8 c)
{
return (c >= '0' && c <= '9');
}
static inline bool
ascii_isxdigit(u8 c)
{
return (ascii_isdigit(c) || (c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'));
}
static inline bool
ascii_islower(u8 c)
{
return (c >= 'a' && c <= 'z');
}
static inline bool
ascii_isupper(u8 c)
{
return (c >= 'A' && c <= 'Z');
}
static inline u8
ascii_tolower(u8 c)
{
if (ascii_isupper(c))
return (c | 32); /* 0b00100000 */
return c;
}
static inline u8
ascii_toupper(u8 c)
{
if (ascii_islower(c))
return (c & 223); /* 0b11011111 */
return c;
}
/*
* fmt
*/
/* TODO: simplify and hardening */
static int
fmt_formatv(char buffer[], usize *size, const char fmt[], va_list va)
{
bool has_open_tok = false;
bool find_close_tok = false;
char tok_type = 0;
usize i = 0;
usize size_ = *size;
if (size_ < 2)
return -ENOMEM;
/* reserving 1 byte for '\0' */
size_--;
for (; (*fmt != '\0') && (i < size_); fmt++) {
if (*fmt == '{' && (!has_open_tok)) {
has_open_tok = true;
continue;
}
if (has_open_tok && (!find_close_tok)) {
tok_type = *fmt;
find_close_tok = true;
continue;
}
if (find_close_tok) {
has_open_tok = false;
find_close_tok = false;
/* size + '\0' */
usize w_size = (size_ +1) - i;
/* avoid nested hell */
if (*fmt != '}')
goto out0;
switch (tok_type) {
case 'i':
if (cstr_from_i64(buffer + i, &w_size,
va_arg(va, i32)) < 0) {
return -ENOMEM;
}
i += w_size;
continue;
case 'u':
if (cstr_from_u64(buffer + i, &w_size,
va_arg(va, u32)) < 0) {
return -ENOMEM;
}
i += w_size;
continue;
case 's':
if (cstr_copy1(buffer + i, &w_size,
va_arg(va, const char *)) < 0) {
return -ENOMEM;
}
i += w_size;
continue;
case 'c':
buffer[i++] = (char)va_arg(va, int);
continue;
}
out0:
if ((i + 3) < size_) {
buffer[i++] = '{';
buffer[i++] = tok_type;
} else {
goto out1;
}
}
buffer[i++] = *fmt;
}
if (*fmt != '\0')
return -ENOMEM;
if (has_open_tok && ((i + 1) < size_))
buffer[i++] = '{';
if (find_close_tok && ((i + 1) < size_))
buffer[i++] = tok_type;
out1:
buffer[i] = '\0';
*size = i;
return 0;
}
static void
fmt_printfd(char buffer[], usize size, int fd, const char fmt[], ...)
{
va_list va;
va_start(va, fmt);
(void)fmt_formatv(buffer, &size, fmt, va);
va_end(va);
cstr_print(buffer);
}
static int
fmt_format(char buffer[], usize *size, const char fmt[], ...)
{
int ret;
va_list va;
va_start(va, fmt);
ret = fmt_formatv(buffer, size, fmt, va);
va_end(va);
return ret;
}
/*
* slots
*/
static int
slots_init(Slots *self, u32 size)
{
const int ret = mem_alloc1(&self->array, sizeof(u32) * size);
if (UNLIKELY(ret < 0))
return ret;
self->count = 0;
self->size = size;
return 0;
}
static void
slots_deinit(Slots *self)
{
mem_free(self->array);
}
static inline int
slots_push(Slots *self, u32 udata)
{
const u32 count = self->count;
if (UNLIKELY(count == self->size))
return -EAGAIN;
self->array[count] = udata;
self->count = count +1;
return 0;
}
static inline i64
slots_pop(Slots *self)
{
u32 count = self->count;
if (UNLIKELY(count == 0))
return -EAGAIN;
count--;
self->count = count;
return (i64)self->array[count];
}
/*
* queue
*/
static int
queue_init(Queue *self, u32 nmemb, u32 size)
{
if (mem_alloc1(&self->array, nmemb * size) < 0)
return 0;
self->count = 0;
self->tail = 0;
self->head = 0;
self->size = size;
self->nmemb = nmemb;
return 0;
}
static void
queue_deinit(Queue *self)
{
mem_free(self->array);
}
static inline void *
queue_sqe(Queue *self)
{
const u32 size = self->size;
const u32 count = self->count;
if (count == size)
return NULL;
u32 head = self->head;
void *const ret = &((u8 *)self->array)[self->nmemb * head];
head++;
if (head >= size)
head = 0;
self->head = head;
self->count = count +1;
return ret;
}
static inline void *
queue_peek(Queue *self)
{
if (self->count == 0)
return NULL;
return &((u8 *)self->array)[self->nmemb * self->tail];
}
static inline void
queue_seen(Queue *self)
{
u32 tail = self->tail +1;
if (tail >= (self->size))
tail = 0;
self->tail = tail;
self->count--;
}
/*
* address
*/
static int
address4_from(Address4 *self, const char addr[])
{
const usize size = sizeof(Address4);
u8 *p = (u8 *)self;
u8 dots = 0;
u16 sum = 0;
for (u8 i = 0; (*addr != '\0') && (i < size) && (dots <= 3); addr++) {
if (ascii_isdigit(*addr)) {
sum = (10 * sum) + (*addr - '0');
if (sum > 255)
return -EINVAL;
p[i] = (u8)sum;
continue;
}
if (*addr == '.') {
sum = 0;
i++;
dots++;
continue;
}
break;
}
if ((dots != 3) || (*addr != '\0'))
return -EINVAL;
return 0;
}
static int
address6_from(Address6 *self, const char addr[])
{
return 0;
}
static int
address4_to(const Address4 *self, char buf[INET4_ADDRSTRLEN])
{
int ret;
const u8 *p = (const u8 *)self;
usize len = INET4_ADDRSTRLEN;
ret = fmt_format(buf, &len, "{u}.{u}.{u}.{u}", p[0], p[1], p[2], p[3]);
if (ret < 0)
return ret;
return 0;
}
static int
address6_to(const Address6 *self, char buf[INET6_ADDRSTRLEN])
{
return 0;
}
/*
* socket
*/
static int
socket_listener(const char addr[], u16 port)
{
int sfd;
isize ret;
SockAddressIn4 addri = {
.family = AF_INET4,
.port = mem_ntobe16(port),
};
ret = address4_from(&addri.addr, addr);
if (ret < 0) {
PERROR(-ret, "address4_from");
return -1;
}
ret = sys_socket(AF_INET4, SOCK_STREAM, IPPROTO_TCP);
if (os_error1(ret)) {
PERROR(-ret, "sys_socket");
return -1;
}
sfd = (int)ret;
int y = 1;
ret = sys_setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y));
if (os_error1(ret)) {
PERROR(-ret, "sys_setsockopt");
goto err0;
}
ret = sys_bind(sfd, (SockAddress *)&addri, sizeof(addri));
if (os_error1(ret)) {
PERROR(-ret, "sys_bind");
goto err0;
}
ret = sys_listen(sfd, 30);
if (os_error1(ret)) {
PERROR(-ret, "sys_listen");
goto err0;
}
/* Success */
return sfd;
err0:
(void)sys_close(sfd);
return -1;
}
/*
* DateTime
*/
static int
datetime_get(DateTime *self)
{
const isize ret = sys_time(NULL);
if (os_error(ret))
return (int)ret;
const Time time = (Time)ret;
/* TODO: parse unix time */
self->year = 0;
self->month = 0;
self->day = 0;
self->hour = 0;
self->minute = 0;
self->second = 0;
return 0;
}
/*
* mysql
* maybe unsafe; leaking secret data
*/
static void
mysql_init(MySQL *self, const char host[], const char port[], const char name[],
const char uname[], const char passwd[])
{
cstr_copy(self->host, sizeof(self->host), host);
cstr_copy(self->port, sizeof(self->port), port);
cstr_copy(self->name, sizeof(self->name), name);
cstr_copy(self->uname, sizeof(self->uname), uname);
cstr_copy(self->passwd, sizeof(self->passwd), passwd);
u32 len = 0;
self->args[len++] = "mysql";
self->args[len++] = self->name;
self->args[len++] = "-u";
self->args[len++] = self->uname;
self->args[len++] = "-p";
self->args[len++] = self->passwd;
self->args_len = len;
}
static int
mysql_prep(MySQL *self, u32 count, ...)
{
const u32 len = self->args_len -1;
if (UNLIKELY((count + len) >= MYSQL_ARGS_COUNT)) {
PERROR(EINVAL, "~count");
return -EINVAL;
}
va_list va;
va_start(va, count);
usize i = 0;
for (; i < count; i++)
self->args[i + len] = va_arg(va, char *);
va_end(va);
self->args[i + len] = NULL;
return 0;
}
static int
mysql_exec(MySQL *self)
{
isize ret = sys_fork();
if (os_error1(ret)) {
PERROR(-ret, "sys_fork");
return (int)ret;
}
if (ret == 0) {
ret = sys_execve("mysql", self->args, NULL);
if (os_error1(ret)) {
PERROR(-ret, "sys_execve");
return (int)ret;
}
} else {
/* parent
* TODO: waitpid()
*/
}
return 0;
}
/****************************************************************************
* TLS *
***************************************************************************/
/****************************************************************************
* FSDNS *
***************************************************************************/
static int
fsdns_resolve(const char addr[], const char port[])
{
return 0;
}
/***************************************************************************
* FSCONF *
**************************************************************************/
static int
fsconf_get(char dest[], usize size, const char src[], const char key[])
{
const char *start = cstr_find(src, key);
if (start == NULL)
return -EINVAL;
start += cstr_len(key);
if (*start != '(')
return -EINVAL;
/* skips '(' */
start++;
const char *end = cstr_find(start, ")\n");
if (end == NULL)
return -EINVAL;
const usize out_len = (usize)(end - start);
if (size <= out_len)
return -ENOMEM;
mem_copy(dest, start, out_len);
dest[out_len] = '\0';
return 0;
}
/****************************************************************************
* MailBox *
***************************************************************************/
/****************************************************************************
* POP3 *
***************************************************************************/
/****************************************************************************
* SMTP *
***************************************************************************/
/****************************************************************************
* main *
***************************************************************************/
/*
* Global variables
*/
Smtp *smtp_g;
Pop3 *pop3_g;
COLD static char *
init_config_file(usize *size)
{
u8 try = 0;
u32 flags = O_RDONLY;
u32 mode = 0;
bool need_write = false;
char *fmap = NULL;
int fd;
Stat stat;
isize ret;
while (true) {
ret = sys_open(DEFAULT_CONFIG_FILE, flags, mode);
if (os_error(ret)) {
PERROR(-ret, "sys_open");
cstr_print("Creating default config file...\n");
need_write = true;
if ((-(int)ret == ENOENT) && (try < 3)) {
DPRINT("..\n");
flags = O_RDWR | O_CREAT | O_TRUNC;
mode = 0644;
try++;
continue;
}
return NULL;
}
fd = (int)ret;
if (need_write)
break;
else
goto out0;
}
const usize cfg_len = sizeof(DEFAULT_CONFIG) -1; /* excluding '\0' */
const char *cfg = DEFAULT_CONFIG;
usize wrt = 0;
while (wrt < cfg_len) {
ret = sys_write(fd, cfg + wrt, cfg_len - wrt);
if (os_error(ret)) {
PERROR(-ret, "sys_write");
goto out1;
}
if (ret == 0)
break;
wrt += (usize)ret;
}
if (wrt != cfg_len)
goto out1;
/* do we need `lseek()` after `write()` ? */
ret = sys_lseek(fd, 0, SEEK_SET);
if (os_error(ret)) {
PERROR(-ret, "sys_lseek");
goto out1;
}
out0:
ret = sys_fstat(fd, &stat);
if (os_error(ret)) {
PERROR(-ret, "sys_fstat");
goto out1;
}
ret = sys_mmap(NULL, (usize)stat.size, PROT_READ, MAP_PRIVATE, fd, 0);
if (os_error(ret)) {
PERROR(-ret, "sys_mmap");
goto out1;
}
fmap = (char *)ret;
*size = (usize)stat.size;
out1:
(void)sys_close(fd);
return fmap;
}
static int
run_smtp(char *argv[])
{
Smtp smtp;
smtp_g = &smtp;
return 0;
}
static int
run_pop3(char *argv[])
{
Pop3 pop3;
pop3_g = &pop3;
return 0;
}
/*
* test functions
*/
static void
test_num(void)
{
char buf[100] = { 'a' };
usize size = sizeof(buf);
if (cstr_find("jkdfjldfjdklf jd2i34uijlfjo", "2i34uijl") == NULL)
return;
if (cstr_from_i64(buf, &size, -11) < 0)
return;
if (!cstr_eql(buf, "-11"))
return;
if (size != 3)
return;
size = 20; /* expected length + '\0' */
if (fmt_format(buf, &size, "ID: {i}: Num: {u}\n", -12, 1230) < 0)
return;
if (!cstr_eql(buf, "ID: -12: Num: 1230\n"))
return;
if (size != 19)
return;
if (!cstr_eql1("aaaa", "aabc", 2))
return;
u64 uret;
if (cstr_to_u64("0000000000000001234567890", &uret) < 0)
return;
if (uret != 1234567890)
return;
i64 iret;
if (cstr_to_i64("-00001234567890", &iret) < 0)
return;
if (iret != -1234567890)
return;
cstr_print("passed\n");
}
/*
* stupid_main
*/
USED static noreturn void
stupid_main(int argc, char *argv[])
{
test_num();
if (argc != 3)
goto out1;
int ret;
u64 port;
ret = cstr_to_u64(argv[2], &port);
if (ret < 0) {
PERROR(-ret, "cstr_to_u64");
goto out1;
}
const int fd = socket_listener(argv[1], (u16)port);
if (fd < 0)
goto out1;
(void)sys_close(fd);
DPRINT("Ok!\n");
out1:
(void)sys_exit(0);
UNREACHABLE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment