]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: socket syscalls (socket/bind/listen/accept/connect/send/recv/sendto/recvfrom)
authorTulio A M Mendes <[email protected]>
Thu, 12 Feb 2026 02:01:31 +0000 (23:01 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 02:20:50 +0000 (23:20 -0300)
Kernel socket subsystem over lwIP TCP/UDP PCBs with ring-buffer RX,
wait queues for blocking ops, and fd integration via sentinel file
structs (flags=0x534F434B).

Socket dispatch extracted to separate noinline function to prevent
syscall_handler stack overflow that caused heap corruption.

include/errno.h
include/socket.h [new file with mode: 0644]
include/syscall.h
src/kernel/init.c
src/kernel/socket.c [new file with mode: 0644]
src/kernel/syscall.c

index 3e607cebdd60009902762822b6a4c8dc70fcc8e3..c0f3cc365b7a390a753c256ac708290a43ff28ca 100644 (file)
 #define ENAMETOOLONG 36
 #define ENOSYS 38
 #define ENOTEMPTY 39
+#define EAFNOSUPPORT 47
+#define EADDRINUSE 48
+#define ECONNREFUSED 61
+#define ENOTCONN 57
+#define EOPNOTSUPP 95
+#define EPROTONOSUPPORT 93
+#define ECONNRESET 54
+#define ETIMEDOUT 60
 
 #endif
diff --git a/include/socket.h b/include/socket.h
new file mode 100644 (file)
index 0000000..327f910
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Address families */
+#define AF_INET     2
+
+/* Socket types */
+#define SOCK_STREAM 1   /* TCP */
+#define SOCK_DGRAM  2   /* UDP */
+
+/* Protocols */
+#define IPPROTO_TCP 6
+#define IPPROTO_UDP 17
+
+/* Shutdown how */
+#define SHUT_RD     0
+#define SHUT_WR     1
+#define SHUT_RDWR   2
+
+/* Socket options */
+#define SOL_SOCKET      1
+#define SO_REUSEADDR    2
+#define SO_ERROR        4
+#define SO_KEEPALIVE    9
+
+/* sockaddr_in (IPv4) — matches POSIX layout */
+struct sockaddr_in {
+    uint16_t sin_family;
+    uint16_t sin_port;      /* network byte order */
+    uint32_t sin_addr;      /* network byte order */
+    uint8_t  sin_zero[8];
+};
+
+/* Generic sockaddr */
+struct sockaddr {
+    uint16_t sa_family;
+    char     sa_data[14];
+};
+
+typedef uint32_t socklen_t;
+
+/* Max kernel sockets */
+#define KSOCKET_MAX         16
+#define KSOCKET_RX_BUF_SIZE 4096
+#define KSOCKET_ACCEPT_MAX  4
+
+/* Kernel socket states */
+#define KSOCK_CLOSED      0
+#define KSOCK_CREATED     1
+#define KSOCK_BOUND       2
+#define KSOCK_LISTENING   3
+#define KSOCK_CONNECTING  4
+#define KSOCK_CONNECTED   5
+#define KSOCK_PEER_CLOSED 6
+
+/* Byte order helpers (x86 is little-endian) */
+static inline uint16_t htons(uint16_t x) { return (uint16_t)((x >> 8) | (x << 8)); }
+static inline uint16_t ntohs(uint16_t x) { return htons(x); }
+static inline uint32_t htonl(uint32_t x) {
+    return ((x >> 24) & 0xFF) | ((x >> 8) & 0xFF00) |
+           ((x << 8) & 0xFF0000) | ((x << 24) & 0xFF000000U);
+}
+static inline uint32_t ntohl(uint32_t x) { return htonl(x); }
+
+/* Kernel socket API (called from syscall layer) */
+int  ksocket_create(int domain, int type, int protocol);
+int  ksocket_bind(int sid, const struct sockaddr_in* addr);
+int  ksocket_listen(int sid, int backlog);
+int  ksocket_accept(int sid, struct sockaddr_in* addr);
+int  ksocket_connect(int sid, const struct sockaddr_in* addr);
+int  ksocket_send(int sid, const void* buf, size_t len, int flags);
+int  ksocket_recv(int sid, void* buf, size_t len, int flags);
+int  ksocket_sendto(int sid, const void* buf, size_t len, int flags,
+                    const struct sockaddr_in* dest);
+int  ksocket_recvfrom(int sid, void* buf, size_t len, int flags,
+                      struct sockaddr_in* src);
+int  ksocket_close(int sid);
+void ksocket_init(void);
+
+#endif
index 2948163d20e1cd069b8b880ab8fbd28ef726efd4..469e387d8f1f2d3e1d521ad9e8fd387ff032f6ac 100644 (file)
@@ -78,6 +78,16 @@ enum {
     SYSCALL_READLINK = 56,
 
     SYSCALL_SET_THREAD_AREA = 57,
+
+    SYSCALL_SOCKET    = 58,
+    SYSCALL_BIND      = 59,
+    SYSCALL_LISTEN    = 60,
+    SYSCALL_ACCEPT    = 61,
+    SYSCALL_CONNECT   = 62,
+    SYSCALL_SEND      = 63,
+    SYSCALL_RECV      = 64,
+    SYSCALL_SENDTO    = 65,
+    SYSCALL_RECVFROM  = 66,
 };
 
 #endif
index 7547bddf4e260505de3058bf616fc9f80f4dfd38..97a6c7083a646f99ac7b249d84670b9ebe0f6c7a 100644 (file)
@@ -15,6 +15,7 @@
 #include "pci.h"
 #include "e1000.h"
 #include "net.h"
+#include "socket.h"
 #include "vbe.h"
 #include "uart_console.h"
 
@@ -73,6 +74,7 @@ int init_start(const struct boot_info* bi) {
     pci_init();
     e1000_init();
     net_init();
+    ksocket_init();
     vbe_init(bi);
 
     tty_init();
diff --git a/src/kernel/socket.c b/src/kernel/socket.c
new file mode 100644 (file)
index 0000000..ec7baf5
--- /dev/null
@@ -0,0 +1,435 @@
+#include "socket.h"
+#include "net.h"
+#include "errno.h"
+#include "process.h"
+#include "waitqueue.h"
+#include "uart_console.h"
+#include "utils.h"
+
+#include "lwip/tcp.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/pbuf.h"
+
+#include <stddef.h>
+
+/* ------------------------------------------------------------------ */
+/* Kernel socket table                                                */
+/* ------------------------------------------------------------------ */
+
+struct ksocket {
+    int      in_use;
+    int      type;          /* SOCK_STREAM or SOCK_DGRAM */
+    int      state;
+    union {
+        struct tcp_pcb* tcp;
+        struct udp_pcb* udp;
+    } pcb;
+
+    /* Receive ring buffer */
+    uint8_t  rx_buf[KSOCKET_RX_BUF_SIZE];
+    uint16_t rx_head;
+    uint16_t rx_tail;
+    uint16_t rx_count;
+
+    /* Accept queue (TCP listening sockets) */
+    int      accept_queue[KSOCKET_ACCEPT_MAX];
+    int      aq_head;
+    int      aq_tail;
+    int      aq_count;
+
+    /* UDP recvfrom: last received source */
+    uint32_t last_remote_ip;
+    uint16_t last_remote_port;
+
+    /* Wait queues */
+    waitqueue_t rx_wq;
+    waitqueue_t accept_wq;
+    waitqueue_t connect_wq;
+
+    int      error;
+};
+
+static struct ksocket sockets[KSOCKET_MAX];
+
+void ksocket_init(void) {
+    for (int i = 0; i < KSOCKET_MAX; i++) {
+        sockets[i].in_use = 0;
+    }
+}
+
+static int alloc_socket(void) {
+    for (int i = 0; i < KSOCKET_MAX; i++) {
+        if (!sockets[i].in_use) {
+            memset(&sockets[i], 0, sizeof(struct ksocket));
+            sockets[i].in_use = 1;
+            sockets[i].state = KSOCK_CREATED;
+            wq_init(&sockets[i].rx_wq);
+            wq_init(&sockets[i].accept_wq);
+            wq_init(&sockets[i].connect_wq);
+            return i;
+        }
+    }
+    return -ENOMEM;
+}
+
+static struct ksocket* get_socket(int sid) {
+    if (sid < 0 || sid >= KSOCKET_MAX) return NULL;
+    if (!sockets[sid].in_use) return NULL;
+    return &sockets[sid];
+}
+
+/* ------------------------------------------------------------------ */
+/* Ring buffer helpers                                                */
+/* ------------------------------------------------------------------ */
+
+static int rxbuf_write(struct ksocket* s, const void* data, uint16_t len) {
+    const uint8_t* src = data;
+    uint16_t avail = KSOCKET_RX_BUF_SIZE - s->rx_count;
+    if (len > avail) len = avail;
+    for (uint16_t i = 0; i < len; i++) {
+        s->rx_buf[s->rx_tail] = src[i];
+        s->rx_tail = (uint16_t)((s->rx_tail + 1) % KSOCKET_RX_BUF_SIZE);
+        s->rx_count++;
+    }
+    return len;
+}
+
+static int rxbuf_read(struct ksocket* s, void* data, uint16_t len) {
+    uint8_t* dst = data;
+    if (len > s->rx_count) len = s->rx_count;
+    for (uint16_t i = 0; i < len; i++) {
+        dst[i] = s->rx_buf[s->rx_head];
+        s->rx_head = (uint16_t)((s->rx_head + 1) % KSOCKET_RX_BUF_SIZE);
+        s->rx_count--;
+    }
+    return len;
+}
+
+/* ------------------------------------------------------------------ */
+/* lwIP TCP callbacks                                                 */
+/* ------------------------------------------------------------------ */
+
+static err_t tcp_recv_cb(void* arg, struct tcp_pcb* tpcb, struct pbuf* p, err_t err) {
+    int sid = (int)(uintptr_t)arg;
+    struct ksocket* s = get_socket(sid);
+    if (!s) { if (p) pbuf_free(p); return ERR_ABRT; }
+
+    if (!p || err != ERR_OK) {
+        /* Peer closed */
+        s->state = KSOCK_PEER_CLOSED;
+        wq_wake_all(&s->rx_wq);
+        if (p) pbuf_free(p);
+        return ERR_OK;
+    }
+
+    /* Copy data into ring buffer */
+    uint16_t copied = 0;
+    for (struct pbuf* q = p; q != NULL; q = q->next) {
+        copied += (uint16_t)rxbuf_write(s, q->payload, q->len);
+    }
+    tcp_recved(tpcb, copied);
+    pbuf_free(p);
+
+    wq_wake_all(&s->rx_wq);
+    return ERR_OK;
+}
+
+static err_t tcp_connected_cb(void* arg, struct tcp_pcb* tpcb, err_t err) {
+    (void)tpcb;
+    int sid = (int)(uintptr_t)arg;
+    struct ksocket* s = get_socket(sid);
+    if (!s) return ERR_ABRT;
+
+    if (err == ERR_OK) {
+        s->state = KSOCK_CONNECTED;
+    } else {
+        s->error = -ECONNREFUSED;
+        s->state = KSOCK_CLOSED;
+    }
+    wq_wake_all(&s->connect_wq);
+    return ERR_OK;
+}
+
+static err_t tcp_accept_cb(void* arg, struct tcp_pcb* newpcb, err_t err) {
+    int sid = (int)(uintptr_t)arg;
+    struct ksocket* s = get_socket(sid);
+    if (!s || err != ERR_OK) return ERR_ABRT;
+
+    if (s->aq_count >= KSOCKET_ACCEPT_MAX) {
+        return ERR_MEM;  /* Reject — queue full */
+    }
+
+    /* Allocate a new ksocket for the accepted connection */
+    int new_sid = alloc_socket();
+    if (new_sid < 0) return ERR_MEM;
+
+    struct ksocket* ns = &sockets[new_sid];
+    ns->type = SOCK_STREAM;
+    ns->state = KSOCK_CONNECTED;
+    ns->pcb.tcp = newpcb;
+
+    tcp_arg(newpcb, (void*)(uintptr_t)new_sid);
+    tcp_recv(newpcb, tcp_recv_cb);
+
+    /* Enqueue */
+    s->accept_queue[s->aq_tail] = new_sid;
+    s->aq_tail = (s->aq_tail + 1) % KSOCKET_ACCEPT_MAX;
+    s->aq_count++;
+
+    wq_wake_all(&s->accept_wq);
+    return ERR_OK;
+}
+
+/* ------------------------------------------------------------------ */
+/* lwIP UDP callback                                                  */
+/* ------------------------------------------------------------------ */
+
+static void udp_recv_cb(void* arg, struct udp_pcb* upcb, struct pbuf* p,
+                        const ip_addr_t* addr, u16_t port) {
+    (void)upcb;
+    int sid = (int)(uintptr_t)arg;
+    struct ksocket* s = get_socket(sid);
+    if (!s || !p) { if (p) pbuf_free(p); return; }
+
+    s->last_remote_ip = ip_addr_get_ip4_u32(addr);
+    s->last_remote_port = port;
+
+    for (struct pbuf* q = p; q != NULL; q = q->next) {
+        rxbuf_write(s, q->payload, q->len);
+    }
+    pbuf_free(p);
+
+    wq_wake_all(&s->rx_wq);
+}
+
+/* ------------------------------------------------------------------ */
+/* Public API                                                         */
+/* ------------------------------------------------------------------ */
+
+int ksocket_create(int domain, int type, int protocol) {
+    (void)protocol;
+    if (domain != AF_INET) return -EAFNOSUPPORT;
+    if (type != SOCK_STREAM && type != SOCK_DGRAM) return -EPROTONOSUPPORT;
+
+    int sid = alloc_socket();
+    if (sid < 0) return sid;
+
+    struct ksocket* s = &sockets[sid];
+    s->type = type;
+
+    if (type == SOCK_STREAM) {
+        s->pcb.tcp = tcp_new();
+        if (!s->pcb.tcp) { s->in_use = 0; return -ENOMEM; }
+        tcp_arg(s->pcb.tcp, (void*)(uintptr_t)sid);
+        tcp_recv(s->pcb.tcp, tcp_recv_cb);
+    } else {
+        s->pcb.udp = udp_new();
+        if (!s->pcb.udp) { s->in_use = 0; return -ENOMEM; }
+        udp_recv(s->pcb.udp, udp_recv_cb, (void*)(uintptr_t)sid);
+    }
+
+    return sid;
+}
+
+int ksocket_bind(int sid, const struct sockaddr_in* addr) {
+    struct ksocket* s = get_socket(sid);
+    if (!s) return -EBADF;
+
+    ip_addr_t ip;
+    ip_addr_set_ip4_u32(&ip, addr->sin_addr);
+    uint16_t port = ntohs(addr->sin_port);
+
+    err_t err;
+    if (s->type == SOCK_STREAM) {
+        err = tcp_bind(s->pcb.tcp, &ip, port);
+    } else {
+        err = udp_bind(s->pcb.udp, &ip, port);
+    }
+
+    if (err != ERR_OK) return -EADDRINUSE;
+    s->state = KSOCK_BOUND;
+    return 0;
+}
+
+int ksocket_listen(int sid, int backlog) {
+    (void)backlog;
+    struct ksocket* s = get_socket(sid);
+    if (!s) return -EBADF;
+    if (s->type != SOCK_STREAM) return -EOPNOTSUPP;
+
+    struct tcp_pcb* lpcb = tcp_listen(s->pcb.tcp);
+    if (!lpcb) return -ENOMEM;
+
+    s->pcb.tcp = lpcb;
+    s->state = KSOCK_LISTENING;
+    tcp_arg(lpcb, (void*)(uintptr_t)sid);
+    tcp_accept(lpcb, tcp_accept_cb);
+
+    return 0;
+}
+
+int ksocket_accept(int sid, struct sockaddr_in* addr) {
+    struct ksocket* s = get_socket(sid);
+    if (!s) return -EBADF;
+    if (s->state != KSOCK_LISTENING) return -EINVAL;
+
+    /* Block until a connection arrives */
+    while (s->aq_count == 0) {
+        wq_push(&s->accept_wq, current_process);
+        current_process->state = PROCESS_BLOCKED;
+        schedule();
+    }
+
+    int new_sid = s->accept_queue[s->aq_head];
+    s->aq_head = (s->aq_head + 1) % KSOCKET_ACCEPT_MAX;
+    s->aq_count--;
+
+    if (addr) {
+        struct ksocket* ns = get_socket(new_sid);
+        if (ns && ns->pcb.tcp) {
+            addr->sin_family = AF_INET;
+            addr->sin_port = htons(ns->pcb.tcp->remote_port);
+            addr->sin_addr = ip_addr_get_ip4_u32(&ns->pcb.tcp->remote_ip);
+        }
+    }
+
+    return new_sid;
+}
+
+int ksocket_connect(int sid, const struct sockaddr_in* addr) {
+    struct ksocket* s = get_socket(sid);
+    if (!s) return -EBADF;
+
+    ip_addr_t ip;
+    ip_addr_set_ip4_u32(&ip, addr->sin_addr);
+    uint16_t port = ntohs(addr->sin_port);
+
+    if (s->type == SOCK_STREAM) {
+        s->state = KSOCK_CONNECTING;
+        err_t err = tcp_connect(s->pcb.tcp, &ip, port, tcp_connected_cb);
+        if (err != ERR_OK) return -ECONNREFUSED;
+
+        /* Block until connected */
+        while (s->state == KSOCK_CONNECTING) {
+            wq_push(&s->connect_wq, current_process);
+            current_process->state = PROCESS_BLOCKED;
+            schedule();
+        }
+        if (s->state != KSOCK_CONNECTED) return s->error ? s->error : -ECONNREFUSED;
+        return 0;
+    } else {
+        /* UDP "connect" just sets the default destination */
+        udp_connect(s->pcb.udp, &ip, port);
+        s->state = KSOCK_CONNECTED;
+        return 0;
+    }
+}
+
+int ksocket_send(int sid, const void* buf, size_t len, int flags) {
+    (void)flags;
+    struct ksocket* s = get_socket(sid);
+    if (!s) return -EBADF;
+
+    if (s->type == SOCK_STREAM) {
+        if (s->state != KSOCK_CONNECTED) return -ENOTCONN;
+        uint16_t snd_len = (len > 0xFFFF) ? 0xFFFF : (uint16_t)len;
+        uint16_t avail = tcp_sndbuf(s->pcb.tcp);
+        if (snd_len > avail) snd_len = avail;
+        if (snd_len == 0) return -EAGAIN;
+
+        err_t err = tcp_write(s->pcb.tcp, buf, snd_len, TCP_WRITE_FLAG_COPY);
+        if (err != ERR_OK) return -EIO;
+        tcp_output(s->pcb.tcp);
+        return (int)snd_len;
+    } else {
+        /* UDP connected send */
+        if (s->state != KSOCK_CONNECTED) return -ENOTCONN;
+        struct pbuf* p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)len, PBUF_RAM);
+        if (!p) return -ENOMEM;
+        memcpy(p->payload, buf, len);
+        err_t err = udp_send(s->pcb.udp, p);
+        pbuf_free(p);
+        return (err == ERR_OK) ? (int)len : -EIO;
+    }
+}
+
+int ksocket_recv(int sid, void* buf, size_t len, int flags) {
+    (void)flags;
+    struct ksocket* s = get_socket(sid);
+    if (!s) return -EBADF;
+
+    /* Block until data available or peer closed */
+    while (s->rx_count == 0 && s->state != KSOCK_PEER_CLOSED && s->state != KSOCK_CLOSED) {
+        wq_push(&s->rx_wq, current_process);
+        current_process->state = PROCESS_BLOCKED;
+        schedule();
+    }
+
+    if (s->rx_count == 0) return 0;  /* EOF / peer closed */
+
+    uint16_t rlen = (len > 0xFFFF) ? 0xFFFF : (uint16_t)len;
+    return rxbuf_read(s, buf, rlen);
+}
+
+int ksocket_sendto(int sid, const void* buf, size_t len, int flags,
+                   const struct sockaddr_in* dest) {
+    (void)flags;
+    struct ksocket* s = get_socket(sid);
+    if (!s) return -EBADF;
+    if (s->type != SOCK_DGRAM) return -EOPNOTSUPP;
+
+    ip_addr_t ip;
+    ip_addr_set_ip4_u32(&ip, dest->sin_addr);
+    uint16_t port = ntohs(dest->sin_port);
+
+    struct pbuf* p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)len, PBUF_RAM);
+    if (!p) return -ENOMEM;
+    memcpy(p->payload, buf, len);
+    err_t err = udp_sendto(s->pcb.udp, p, &ip, port);
+    pbuf_free(p);
+    return (err == ERR_OK) ? (int)len : -EIO;
+}
+
+int ksocket_recvfrom(int sid, void* buf, size_t len, int flags,
+                     struct sockaddr_in* src) {
+    int ret = ksocket_recv(sid, buf, len, flags);
+    if (ret > 0 && src) {
+        struct ksocket* s = get_socket(sid);
+        if (s) {
+            src->sin_family = AF_INET;
+            src->sin_port = htons(s->last_remote_port);
+            src->sin_addr = s->last_remote_ip;
+        }
+    }
+    return ret;
+}
+
+int ksocket_close(int sid) {
+    struct ksocket* s = get_socket(sid);
+    if (!s) return -EBADF;
+
+    if (s->type == SOCK_STREAM && s->pcb.tcp) {
+        tcp_arg(s->pcb.tcp, NULL);
+        tcp_recv(s->pcb.tcp, NULL);
+        tcp_accept(s->pcb.tcp, NULL);
+        tcp_close(s->pcb.tcp);
+    } else if (s->type == SOCK_DGRAM && s->pcb.udp) {
+        udp_remove(s->pcb.udp);
+    }
+
+    /* Free any pending accepted sockets */
+    while (s->aq_count > 0) {
+        int child = s->accept_queue[s->aq_head];
+        s->aq_head = (s->aq_head + 1) % KSOCKET_ACCEPT_MAX;
+        s->aq_count--;
+        ksocket_close(child);
+    }
+
+    wq_wake_all(&s->rx_wq);
+    wq_wake_all(&s->accept_wq);
+    wq_wake_all(&s->connect_wq);
+
+    s->in_use = 0;
+    return 0;
+}
index 999115e5bd2744a634512ba87d7933fa83c14056..68644a133cb04278455e50857869c2ed8af7b17d 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "errno.h"
 #include "shm.h"
+#include "socket.h"
 
 #if defined(__i386__)
 extern void x86_sysenter_init(void);
@@ -64,6 +65,7 @@ struct sigframe {
 static int fd_alloc(struct file* f);
 static int fd_close(int fd);
 static struct file* fd_get(int fd);
+static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no);
 
 struct pollfd {
     int fd;
@@ -582,7 +584,9 @@ static int fd_close(int fd) {
     current_process->files[fd] = NULL;
 
     if (__sync_sub_and_fetch(&f->refcount, 1) == 0) {
-        if (f->node) {
+        if (f->flags == 0x534F434BU && f->node == NULL) {
+            ksocket_close((int)f->offset);
+        } else if (f->node) {
             vfs_close(f->node);
         }
         kfree(f);
@@ -2154,6 +2158,180 @@ void syscall_handler(struct registers* regs) {
         return;
     }
 
+    /* ---- Socket syscalls ---- */
+    socket_syscall_dispatch(regs, syscall_no);
+    /* If socket dispatch handled it, eax is set and we return.
+       If not, it sets ENOSYS. Either way, return. */
+    return;
+}
+
+/* Separate function to keep socket locals off syscall_handler's stack frame */
+__attribute__((noinline))
+static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) {
+    if (syscall_no == SYSCALL_SOCKET) {
+        int domain   = (int)regs->ebx;
+        int type     = (int)regs->ecx;
+        int protocol = (int)regs->edx;
+        int sid = ksocket_create(domain, type, protocol);
+        if (sid < 0) { regs->eax = (uint32_t)sid; return; }
+        int fd = -1;
+        for (int i = 0; i < PROCESS_MAX_FILES; i++) {
+            if (!current_process->files[i]) { fd = i; break; }
+        }
+        if (fd < 0) { ksocket_close(sid); regs->eax = (uint32_t)-EMFILE; return; }
+        struct file* f = (struct file*)kmalloc(sizeof(struct file));
+        if (!f) { ksocket_close(sid); regs->eax = (uint32_t)-ENOMEM; return; }
+        f->node = NULL;
+        f->offset = (uint32_t)sid;
+        f->flags = 0x534F434BU;     /* magic 'SOCK' */
+        f->refcount = 1;
+        current_process->files[fd] = f;
+        regs->eax = (uint32_t)fd;
+        return;
+    }
+
+    if (syscall_no == SYSCALL_BIND) {
+        int fd = (int)regs->ebx;
+        if (fd < 0 || fd >= PROCESS_MAX_FILES || !current_process->files[fd] ||
+            current_process->files[fd]->flags != 0x534F434BU) {
+            regs->eax = (uint32_t)-EBADF; return;
+        }
+        struct sockaddr_in sa;
+        if (copy_from_user(&sa, (const void*)regs->ecx, sizeof(sa)) < 0) {
+            regs->eax = (uint32_t)-EFAULT; return;
+        }
+        int sid = (int)current_process->files[fd]->offset;
+        regs->eax = (uint32_t)ksocket_bind(sid, &sa);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_LISTEN) {
+        int fd = (int)regs->ebx;
+        if (fd < 0 || fd >= PROCESS_MAX_FILES || !current_process->files[fd] ||
+            current_process->files[fd]->flags != 0x534F434BU) {
+            regs->eax = (uint32_t)-EBADF; return;
+        }
+        int sid = (int)current_process->files[fd]->offset;
+        regs->eax = (uint32_t)ksocket_listen(sid, (int)regs->ecx);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_ACCEPT) {
+        int fd = (int)regs->ebx;
+        if (fd < 0 || fd >= PROCESS_MAX_FILES || !current_process->files[fd] ||
+            current_process->files[fd]->flags != 0x534F434BU) {
+            regs->eax = (uint32_t)-EBADF; return;
+        }
+        int sid = (int)current_process->files[fd]->offset;
+        struct sockaddr_in sa;
+        memset(&sa, 0, sizeof(sa));
+        int new_sid = ksocket_accept(sid, &sa);
+        if (new_sid < 0) { regs->eax = (uint32_t)new_sid; return; }
+        int new_fd = -1;
+        for (int i = 0; i < PROCESS_MAX_FILES; i++) {
+            if (!current_process->files[i]) { new_fd = i; break; }
+        }
+        if (new_fd < 0) { ksocket_close(new_sid); regs->eax = (uint32_t)-EMFILE; return; }
+        struct file* f = (struct file*)kmalloc(sizeof(struct file));
+        if (!f) { ksocket_close(new_sid); regs->eax = (uint32_t)-ENOMEM; return; }
+        f->node = NULL;
+        f->offset = (uint32_t)new_sid;
+        f->flags = 0x534F434BU;
+        f->refcount = 1;
+        current_process->files[new_fd] = f;
+        if (regs->ecx) {
+            (void)copy_to_user((void*)regs->ecx, &sa, sizeof(sa));
+        }
+        regs->eax = (uint32_t)new_fd;
+        return;
+    }
+
+    if (syscall_no == SYSCALL_CONNECT) {
+        int fd = (int)regs->ebx;
+        if (fd < 0 || fd >= PROCESS_MAX_FILES || !current_process->files[fd] ||
+            current_process->files[fd]->flags != 0x534F434BU) {
+            regs->eax = (uint32_t)-EBADF; return;
+        }
+        struct sockaddr_in sa;
+        if (copy_from_user(&sa, (const void*)regs->ecx, sizeof(sa)) < 0) {
+            regs->eax = (uint32_t)-EFAULT; return;
+        }
+        int sid = (int)current_process->files[fd]->offset;
+        regs->eax = (uint32_t)ksocket_connect(sid, &sa);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_SEND) {
+        int fd = (int)regs->ebx;
+        if (fd < 0 || fd >= PROCESS_MAX_FILES || !current_process->files[fd] ||
+            current_process->files[fd]->flags != 0x534F434BU) {
+            regs->eax = (uint32_t)-EBADF; return;
+        }
+        size_t len = (size_t)regs->edx;
+        if (!user_range_ok((const void*)regs->ecx, len)) {
+            regs->eax = (uint32_t)-EFAULT; return;
+        }
+        int sid = (int)current_process->files[fd]->offset;
+        regs->eax = (uint32_t)ksocket_send(sid, (const void*)regs->ecx, len, (int)regs->esi);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_RECV) {
+        int fd = (int)regs->ebx;
+        if (fd < 0 || fd >= PROCESS_MAX_FILES || !current_process->files[fd] ||
+            current_process->files[fd]->flags != 0x534F434BU) {
+            regs->eax = (uint32_t)-EBADF; return;
+        }
+        size_t len = (size_t)regs->edx;
+        if (!user_range_ok((void*)regs->ecx, len)) {
+            regs->eax = (uint32_t)-EFAULT; return;
+        }
+        int sid = (int)current_process->files[fd]->offset;
+        regs->eax = (uint32_t)ksocket_recv(sid, (void*)regs->ecx, len, (int)regs->esi);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_SENDTO) {
+        int fd = (int)regs->ebx;
+        if (fd < 0 || fd >= PROCESS_MAX_FILES || !current_process->files[fd] ||
+            current_process->files[fd]->flags != 0x534F434BU) {
+            regs->eax = (uint32_t)-EBADF; return;
+        }
+        size_t len = (size_t)regs->edx;
+        if (!user_range_ok((const void*)regs->ecx, len)) {
+            regs->eax = (uint32_t)-EFAULT; return;
+        }
+        struct sockaddr_in dest;
+        if (copy_from_user(&dest, (const void*)regs->edi, sizeof(dest)) < 0) {
+            regs->eax = (uint32_t)-EFAULT; return;
+        }
+        int sid = (int)current_process->files[fd]->offset;
+        regs->eax = (uint32_t)ksocket_sendto(sid, (const void*)regs->ecx, len,
+                                              (int)regs->esi, &dest);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_RECVFROM) {
+        int fd = (int)regs->ebx;
+        if (fd < 0 || fd >= PROCESS_MAX_FILES || !current_process->files[fd] ||
+            current_process->files[fd]->flags != 0x534F434BU) {
+            regs->eax = (uint32_t)-EBADF; return;
+        }
+        size_t len = (size_t)regs->edx;
+        if (!user_range_ok((void*)regs->ecx, len)) {
+            regs->eax = (uint32_t)-EFAULT; return;
+        }
+        struct sockaddr_in src;
+        memset(&src, 0, sizeof(src));
+        int sid = (int)current_process->files[fd]->offset;
+        int ret = ksocket_recvfrom(sid, (void*)regs->ecx, len, (int)regs->esi, &src);
+        if (ret > 0 && regs->edi) {
+            (void)copy_to_user((void*)regs->edi, &src, sizeof(src));
+        }
+        regs->eax = (uint32_t)ret;
+        return;
+    }
+
     regs->eax = (uint32_t)-ENOSYS;
 }