feat: Sockets, user-mode networking, IRC client application

This commit is contained in:
2026-02-18 18:35:34 +01:00
parent dfa86b272c
commit 1a5d943649
15 changed files with 1635 additions and 14 deletions
Binary file not shown.
+10
View File
@@ -40,6 +40,16 @@ namespace Zenith {
static constexpr uint64_t SYS_RESET = 26;
static constexpr uint64_t SYS_SHUTDOWN = 27;
static constexpr uint64_t SYS_GETTIME = 28;
static constexpr uint64_t SYS_SOCKET = 29;
static constexpr uint64_t SYS_CONNECT = 30;
static constexpr uint64_t SYS_BIND = 31;
static constexpr uint64_t SYS_LISTEN = 32;
static constexpr uint64_t SYS_ACCEPT = 33;
static constexpr uint64_t SYS_SEND = 34;
static constexpr uint64_t SYS_RECV = 35;
static constexpr uint64_t SYS_CLOSESOCK = 36;
static constexpr int SOCK_TCP = 1;
struct DateTime {
uint16_t Year;
+26
View File
@@ -157,6 +157,32 @@ namespace zenith {
return (int32_t)syscall2(Zenith::SYS_PING, (uint64_t)ip, (uint64_t)timeoutMs);
}
// Sockets
inline int socket(int type) {
return (int)syscall1(Zenith::SYS_SOCKET, (uint64_t)type);
}
inline int connect(int fd, uint32_t ip, uint16_t port) {
return (int)syscall3(Zenith::SYS_CONNECT, (uint64_t)fd, (uint64_t)ip, (uint64_t)port);
}
inline int bind(int fd, uint16_t port) {
return (int)syscall2(Zenith::SYS_BIND, (uint64_t)fd, (uint64_t)port);
}
inline int listen(int fd) {
return (int)syscall1(Zenith::SYS_LISTEN, (uint64_t)fd);
}
inline int accept(int fd) {
return (int)syscall1(Zenith::SYS_ACCEPT, (uint64_t)fd);
}
inline int send(int fd, const void* data, uint32_t len) {
return (int)syscall3(Zenith::SYS_SEND, (uint64_t)fd, (uint64_t)data, (uint64_t)len);
}
inline int recv(int fd, void* buf, uint32_t maxLen) {
return (int)syscall3(Zenith::SYS_RECV, (uint64_t)fd, (uint64_t)buf, (uint64_t)maxLen);
}
inline int closesocket(int fd) {
return (int)syscall1(Zenith::SYS_CLOSESOCK, (uint64_t)fd);
}
// Process management
inline void waitpid(int pid) { syscall1(Zenith::SYS_WAITPID, (uint64_t)pid); }
+35 -1
View File
@@ -3,7 +3,7 @@
syscalls - overview of ZenithOS system calls
.SH DESCRIPTION
ZenithOS provides 29 system calls (numbers 0-28) for userspace
ZenithOS provides 37 system calls (numbers 0-36) for userspace
programs. Syscalls use the x86-64 SYSCALL instruction with the
following register convention:
@@ -120,6 +120,40 @@
Send an ICMP echo request and wait for reply.
int32_t zenith::ping(uint32_t ip, uint32_t timeoutMs);
.B SYS_SOCKET (29)
Create a socket. type=SOCK_TCP (1). Returns fd or -1.
int zenith::socket(int type);
.B SYS_CONNECT (30)
Connect a TCP socket to a remote host.
int zenith::connect(int fd, uint32_t ip, uint16_t port);
.B SYS_BIND (31)
Bind a socket to a local port for listening.
int zenith::bind(int fd, uint16_t port);
.B SYS_LISTEN (32)
Start listening for incoming TCP connections.
int zenith::listen(int fd);
.B SYS_ACCEPT (33)
Accept an incoming connection on a listening socket.
Returns a new socket fd for the client connection.
int zenith::accept(int fd);
.B SYS_SEND (34)
Send data on a connected socket. Returns bytes sent.
int zenith::send(int fd, const void* data, uint32_t len);
.B SYS_RECV (35)
Receive data from a connected socket. Returns bytes
received, 0 on connection close, or -1 on error.
int zenith::recv(int fd, void* buf, uint32_t maxLen);
.B SYS_CLOSESOCK (36)
Close a socket and release its resources.
int zenith::closesocket(int fd);
.SH FRAMEBUFFER
.B SYS_FBINFO (21)
Get framebuffer dimensions and format.
Binary file not shown.
File diff suppressed because it is too large Load Diff
+153 -6
View File
@@ -94,6 +94,7 @@ static void cmd_help() {
zenith::print(" cat <file> Display file contents\n");
zenith::print(" run <file> Spawn a new process from an ELF file\n");
zenith::print(" ping <ip> Send ICMP echo requests\n");
zenith::print(" tcpconnect <ip> <port> Connect to a TCP server\n");
zenith::print(" date Show current date and time\n");
zenith::print(" uptime Show uptime in milliseconds\n");
zenith::print(" clear Clear the screen\n");
@@ -297,20 +298,162 @@ static void cmd_ping(const char* arg) {
}
}
static void cmd_run(const char* arg) {
static bool parse_uint16(const char* s, uint16_t* out) {
uint32_t val = 0;
if (*s == '\0') return false;
while (*s) {
if (*s < '0' || *s > '9') return false;
val = val * 10 + (*s - '0');
if (val > 65535) return false;
s++;
}
*out = (uint16_t)val;
return true;
}
static void cmd_tcpconnect(const char* arg) {
arg = skip_spaces(arg);
if (*arg == '\0') {
zenith::print("Usage: run <filename>\n");
zenith::print("Usage: tcpconnect <ip> <port>\n");
return;
}
char path[128];
resolve_path(arg, path, sizeof(path));
// Parse IP address (up to first space)
char ipStr[32];
int i = 0;
while (arg[i] && arg[i] != ' ' && i < 31) {
ipStr[i] = arg[i];
i++;
}
ipStr[i] = '\0';
int pid = zenith::spawn(path);
uint32_t ip;
if (!parse_ip(ipStr, &ip)) {
zenith::print("Invalid IP address: ");
zenith::print(ipStr);
zenith::putchar('\n');
return;
}
// Parse port
const char* portStr = skip_spaces(arg + i);
if (*portStr == '\0') {
zenith::print("Usage: tcpconnect <ip> <port>\n");
return;
}
uint16_t port;
if (!parse_uint16(portStr, &port)) {
zenith::print("Invalid port: ");
zenith::print(portStr);
zenith::putchar('\n');
return;
}
// Create socket
int fd = zenith::socket(Zenith::SOCK_TCP);
if (fd < 0) {
zenith::print("Error: failed to create socket\n");
return;
}
zenith::print("Connecting to ");
print_ip(ip);
zenith::putchar(':');
print_int(port);
zenith::print("...\n");
if (zenith::connect(fd, ip, port) < 0) {
zenith::print("Error: connection failed\n");
zenith::closesocket(fd);
return;
}
zenith::print("Connected! Type to send, Ctrl+Q to disconnect.\n");
// Interactive send/receive loop
char sendBuf[256];
int sendPos = 0;
uint8_t recvBuf[512];
while (true) {
// Poll for received data (non-blocking)
int r = zenith::recv(fd, recvBuf, sizeof(recvBuf) - 1);
if (r < 0) {
zenith::print("\nConnection closed by remote.\n");
break;
}
if (r > 0) {
recvBuf[r] = '\0';
zenith::print((const char*)recvBuf);
}
// Poll keyboard
if (zenith::is_key_available()) {
Zenith::KeyEvent ev;
zenith::getkey(&ev);
if (!ev.pressed) continue;
// Ctrl+Q to quit
if (ev.ctrl && (ev.ascii == 'q' || ev.ascii == 'Q')) {
zenith::print("\nDisconnecting...\n");
break;
}
if (ev.ascii == '\n') {
sendBuf[sendPos++] = '\n';
zenith::putchar('\n');
zenith::send(fd, sendBuf, sendPos);
sendPos = 0;
} else if (ev.ascii == '\b') {
if (sendPos > 0) {
sendPos--;
zenith::putchar('\b');
zenith::putchar(' ');
zenith::putchar('\b');
}
} else if (ev.ascii >= ' ' && sendPos < 254) {
sendBuf[sendPos++] = ev.ascii;
zenith::putchar(ev.ascii);
}
} else {
// No key and no data — yield to avoid busy-spinning
zenith::yield();
}
}
zenith::closesocket(fd);
}
static void cmd_run(const char* arg) {
arg = skip_spaces(arg);
if (*arg == '\0') {
zenith::print("Usage: run <filename> [args...]\n");
return;
}
// Split filename from arguments at first space
char filename[128];
int i = 0;
while (arg[i] && arg[i] != ' ' && i < 127) {
filename[i] = arg[i];
i++;
}
filename[i] = '\0';
const char* args = nullptr;
if (arg[i] == ' ') {
args = skip_spaces(arg + i);
if (*args == '\0') args = nullptr;
}
char path[128];
resolve_path(filename, path, sizeof(path));
int pid = zenith::spawn(path, args);
if (pid < 0) {
zenith::print("Error: failed to spawn '");
zenith::print(arg);
zenith::print(filename);
zenith::print("'\n");
} else {
zenith::waitpid(pid);
@@ -461,6 +604,10 @@ static void process_command(const char* line) {
cmd_ping(line + 5);
} else if (streq(line, "ping")) {
cmd_ping("");
} else if (starts_with(line, "tcpconnect ")) {
cmd_tcpconnect(line + 11);
} else if (streq(line, "tcpconnect")) {
cmd_tcpconnect("");
} else if (streq(line, "date")) {
cmd_date();
} else if (streq(line, "uptime")) {