#define _GNU_SOURCE
#include "hooks.h"
#include "ipc.h"

#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/random.h>
#include <sys/types.h>
#include <sys/utsname.h>

// These functions will hook the regular libc functions called by the sandboxed process
// The sandbox supervisor will intercept these calls and log / handle them safely

int system(const char *command) {
    if (getenv("SANDBOX_ACTIVE")) {
        ipc_message_t msg = { .command = CMD_SYSTEM };

        if (command) 
            strncpy(msg.data.system.path, command, sizeof(msg.data.system.path) - 1);
        
        sandbox_send(&msg, sizeof(msg));
        
        errno = EPERM;
        return -1;
    }

    system_func_t real_system = (system_func_t)dlsym(RTLD_NEXT, "system");
    return real_system(command);
}

int execve(const char *pathname, char *const argv[], char *const envp[]) {
    if (getenv("SANDBOX_ACTIVE")) {
        ipc_message_t msg = { .command = CMD_EXECVE };
        
        if (pathname)
            strncpy(msg.data.execve.path, pathname, sizeof(msg.data.execve.path) - 1);

        sandbox_send(&msg, sizeof(msg));
        
        errno = EPERM;
        return -1;
    }

    execve_func_t real_execve = (execve_func_t)dlsym(RTLD_NEXT, "execve");
    return real_execve(pathname, argv, envp);
}

int open(const char *pathname, int flags, ...) {
    if (getenv("SANDBOX_ACTIVE")) {
        ipc_message_t msg = { .command = CMD_OPEN };

        if (pathname) 
            strncpy(msg.data.open.path, pathname, sizeof(msg.data.open.path) - 1);
        
        msg.data.open.flags = flags;
        
        sandbox_send(&msg, sizeof(msg));
        
        errno = EPERM;
        return -1;
    }
    
    open_func_t real_open = (open_func_t)dlsym(RTLD_NEXT, "open");
    return real_open(pathname, flags);
}

FILE *popen(const char *command, const char *type) {
    if (getenv("SANDBOX_ACTIVE")) {
        ipc_message_t msg = { .command = CMD_POPEN };

        if (command) 
            strncpy(msg.data.popen.path, command, sizeof(msg.data.popen.path) - 1);

        sandbox_send(&msg, sizeof(msg));

        errno = EPERM;
        return NULL;
    }
    
    popen_func_t real_popen = (popen_func_t)dlsym(RTLD_NEXT, "popen");
    return real_popen(command, type);
}

int chmod(const char *pathname, mode_t mode) {
    if (getenv("SANDBOX_ACTIVE")) {
        ipc_message_t msg = { .command = CMD_CHMOD };

        if (pathname) 
            strncpy(msg.data.chmod.path, pathname, sizeof(msg.data.chmod.path) - 1);
        
        msg.data.chmod.mode = mode;
        
        sandbox_send(&msg, sizeof(msg));
        
        errno = EPERM;
        return -1;
    }
    
    chmod_func_t real_chmod = (chmod_func_t)dlsym(RTLD_NEXT, "chmod");
    return real_chmod(pathname, mode);
}

int uname(struct utsname *buf) {
    if (getenv("SANDBOX_ACTIVE")) {
        ipc_message_t msg = { .command = CMD_UNAME };
        
        sandbox_send(&msg, sizeof(msg));
        
        if (buf) memcpy(buf, &msg.data.uname.buf, sizeof(struct utsname));
        return 0;
    }
    
    uname_func_t real_uname = (uname_func_t)dlsym(RTLD_NEXT, "uname");
    return real_uname(buf);
}

ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) {
    if (getenv("SANDBOX_ACTIVE")) {
        ipc_message_t msg = { .command = CMD_GETRANDOM };
        size_t max_len = sizeof(msg.data.getrandom.buflen) - sizeof(size_t);
        size_t request_len = (buflen > max_len) ? max_len : buflen;
        
        msg.data.getrandom.buflen = request_len;
        
        sandbox_send(&msg, sizeof(msg));
        
        size_t provided_len = msg.data.getrandom.buflen;
        
        if (buf && provided_len > 0)
            memcpy(buf, msg.data.getrandom.buf, provided_len);

        return provided_len;
    }
    
    getrandom_func_t real_getrandom = (getrandom_func_t)dlsym(RTLD_NEXT, "getrandom");
    return real_getrandom(buf, buflen, flags);
}

int unlink(const char *pathname) {
    if (getenv("SANDBOX_ACTIVE")) {
        ipc_message_t msg = { .command = CMD_UNLINK };

        if (pathname) 
            strncpy(msg.data.unlink.path, pathname, sizeof(msg.data.unlink.path) - 1);
        
        sandbox_send(&msg, sizeof(msg));
        
        errno = EPERM;
        return -1;
    }
    
    unlink_func_t real_unlink = (unlink_func_t)dlsym(RTLD_NEXT, "unlink");
    return real_unlink(pathname);
}
